xref: /openbsd-src/usr.bin/cvs/rcs.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: rcs.c,v 1.281 2008/09/17 06:47:57 reyk Exp $	*/
2 /*
3  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/stat.h>
28 
29 #include <ctype.h>
30 #include <errno.h>
31 #include <libgen.h>
32 #include <pwd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 
37 #include "atomicio.h"
38 #include "cvs.h"
39 #include "diff.h"
40 #include "rcs.h"
41 
42 #define RCS_BUFSIZE	16384
43 #define RCS_BUFEXTSIZE	8192
44 #define RCS_KWEXP_SIZE  1024
45 
46 /* RCS token types */
47 #define RCS_TOK_ERR	-1
48 #define RCS_TOK_EOF	0
49 #define RCS_TOK_NUM	1
50 #define RCS_TOK_ID	2
51 #define RCS_TOK_STRING	3
52 #define RCS_TOK_SCOLON	4
53 #define RCS_TOK_COLON	5
54 
55 #define RCS_TOK_HEAD		8
56 #define RCS_TOK_BRANCH		9
57 #define RCS_TOK_ACCESS		10
58 #define RCS_TOK_SYMBOLS		11
59 #define RCS_TOK_LOCKS		12
60 #define RCS_TOK_COMMENT		13
61 #define RCS_TOK_EXPAND		14
62 #define RCS_TOK_DATE		15
63 #define RCS_TOK_AUTHOR		16
64 #define RCS_TOK_STATE		17
65 #define RCS_TOK_NEXT		18
66 #define RCS_TOK_BRANCHES	19
67 #define RCS_TOK_DESC		20
68 #define RCS_TOK_LOG		21
69 #define RCS_TOK_TEXT		22
70 #define RCS_TOK_STRICT		23
71 
72 #define RCS_ISKEY(t)	(((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
73 
74 #define RCS_NOSCOL	0x01	/* no terminating semi-colon */
75 #define RCS_VOPT	0x02	/* value is optional */
76 
77 #define ANNOTATE_NEVER	0
78 #define ANNOTATE_NOW	1
79 #define ANNOTATE_LATER	2
80 
81 /* opaque parse data */
82 struct rcs_pdata {
83 	u_int	rp_lines;
84 
85 	char	*rp_buf;
86 	size_t	 rp_blen;
87 	char	*rp_bufend;
88 	size_t	 rp_tlen;
89 
90 	/* pushback token buffer */
91 	char	rp_ptok[128];
92 	int	rp_pttype;	/* token type, RCS_TOK_ERR if no token */
93 
94 	FILE	*rp_file;
95 };
96 
97 #define RCS_TOKSTR(rfp)	((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
98 #define RCS_TOKLEN(rfp)	((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen
99 
100 /* invalid characters in RCS symbol names */
101 static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
102 
103 /* comment leaders, depending on the file's suffix */
104 static const struct rcs_comment {
105 	const char	*rc_suffix;
106 	const char	*rc_cstr;
107 } rcs_comments[] = {
108 	{ "1",    ".\\\" " },
109 	{ "2",    ".\\\" " },
110 	{ "3",    ".\\\" " },
111 	{ "4",    ".\\\" " },
112 	{ "5",    ".\\\" " },
113 	{ "6",    ".\\\" " },
114 	{ "7",    ".\\\" " },
115 	{ "8",    ".\\\" " },
116 	{ "9",    ".\\\" " },
117 	{ "a",    "-- "    },	/* Ada		 */
118 	{ "ada",  "-- "    },
119 	{ "adb",  "-- "    },
120 	{ "asm",  ";; "    },	/* assembler (MS-DOS) */
121 	{ "ads",  "-- "    },	/* Ada */
122 	{ "bat",  ":: "    },	/* batch (MS-DOS) */
123 	{ "body", "-- "    },	/* Ada */
124 	{ "c",    " * "    },	/* C */
125 	{ "c++",  "// "    },	/* C++ */
126 	{ "cc",   "// "    },
127 	{ "cpp",  "// "    },
128 	{ "cxx",  "// "    },
129 	{ "m",    "// "    },	/* Objective-C */
130 	{ "cl",   ";;; "   },	/* Common Lisp	 */
131 	{ "cmd",  ":: "    },	/* command (OS/2) */
132 	{ "cmf",  "c "     },	/* CM Fortran	 */
133 	{ "csh",  "# "     },	/* shell	 */
134 	{ "e",    "# "     },	/* efl		 */
135 	{ "epsf", "% "     },	/* encapsulated postscript */
136 	{ "epsi", "% "     },	/* encapsulated postscript */
137 	{ "el",   "; "     },	/* Emacs Lisp	 */
138 	{ "f",    "c "     },	/* Fortran	 */
139 	{ "for",  "c "     },
140 	{ "h",    " * "    },	/* C-header	 */
141 	{ "hh",   "// "    },	/* C++ header	 */
142 	{ "hpp",  "// "    },
143 	{ "hxx",  "// "    },
144 	{ "in",   "# "     },	/* for Makefile.in */
145 	{ "l",    " * "    },	/* lex */
146 	{ "mac",  ";; "    },	/* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
147 	{ "mak",  "# "     },	/* makefile, e.g. Visual C++ */
148 	{ "me",   ".\\\" " },	/* me-macros	t/nroff	 */
149 	{ "ml",   "; "     },	/* mocklisp	 */
150 	{ "mm",   ".\\\" " },	/* mm-macros	t/nroff	 */
151 	{ "ms",   ".\\\" " },	/* ms-macros	t/nroff	 */
152 	{ "man",  ".\\\" " },	/* man-macros	t/nroff	 */
153 	{ "p",    " * "    },	/* pascal	 */
154 	{ "pas",  " * "    },
155 	{ "pl",   "# "     },	/* Perl	(conflict with Prolog) */
156 	{ "pm",   "# "     },	/* Perl	module */
157 	{ "ps",   "% "     },	/* postscript */
158 	{ "psw",  "% "     },	/* postscript wrap */
159 	{ "pswm", "% "     },	/* postscript wrap */
160 	{ "r",    "# "     },	/* ratfor	 */
161 	{ "rc",   " * "    },	/* Microsoft Windows resource file */
162 	{ "red",  "% "     },	/* psl/rlisp	 */
163 	{ "sh",   "# "     },	/* shell	 */
164 	{ "sl",   "% "     },	/* psl		 */
165 	{ "spec", "-- "    },	/* Ada		 */
166 	{ "tex",  "% "     },	/* tex		 */
167 	{ "y",    " * "    },	/* yacc		 */
168 	{ "ye",   " * "    },	/* yacc-efl	 */
169 	{ "yr",   " * "    },	/* yacc-ratfor	 */
170 };
171 
172 struct rcs_kw rcs_expkw[] =  {
173 	{ "Author",	RCS_KW_AUTHOR   },
174 	{ "Date",	RCS_KW_DATE     },
175 	{ "Header",	RCS_KW_HEADER   },
176 	{ "Id",		RCS_KW_ID       },
177 	{ "Locker",	RCS_KW_LOCKER	},
178 	{ "Log",	RCS_KW_LOG      },
179 	{ "Name",	RCS_KW_NAME     },
180 	{ "RCSfile",	RCS_KW_RCSFILE  },
181 	{ "Revision",	RCS_KW_REVISION },
182 	{ "Source",	RCS_KW_SOURCE   },
183 	{ "State",	RCS_KW_STATE    },
184 	{ "Mdocdate",	RCS_KW_MDOCDATE },
185 };
186 
187 #define NB_COMTYPES	(sizeof(rcs_comments)/sizeof(rcs_comments[0]))
188 
189 static struct rcs_key {
190 	char	rk_str[16];
191 	int	rk_id;
192 	int	rk_val;
193 	int	rk_flags;
194 } rcs_keys[] = {
195 	{ "access",   RCS_TOK_ACCESS,   RCS_TOK_ID,     RCS_VOPT     },
196 	{ "author",   RCS_TOK_AUTHOR,   RCS_TOK_ID,     0            },
197 	{ "branch",   RCS_TOK_BRANCH,   RCS_TOK_NUM,    RCS_VOPT     },
198 	{ "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM,    RCS_VOPT     },
199 	{ "comment",  RCS_TOK_COMMENT,  RCS_TOK_STRING, RCS_VOPT     },
200 	{ "date",     RCS_TOK_DATE,     RCS_TOK_NUM,    0            },
201 	{ "desc",     RCS_TOK_DESC,     RCS_TOK_STRING, RCS_NOSCOL   },
202 	{ "expand",   RCS_TOK_EXPAND,   RCS_TOK_STRING, RCS_VOPT     },
203 	{ "head",     RCS_TOK_HEAD,     RCS_TOK_NUM,    RCS_VOPT     },
204 	{ "locks",    RCS_TOK_LOCKS,    RCS_TOK_ID,     0            },
205 	{ "log",      RCS_TOK_LOG,      RCS_TOK_STRING, RCS_NOSCOL   },
206 	{ "next",     RCS_TOK_NEXT,     RCS_TOK_NUM,    RCS_VOPT     },
207 	{ "state",    RCS_TOK_STATE,    RCS_TOK_ID,     RCS_VOPT     },
208 	{ "strict",   RCS_TOK_STRICT,   0,              0,           },
209 	{ "symbols",  RCS_TOK_SYMBOLS,  0,              0            },
210 	{ "text",     RCS_TOK_TEXT,     RCS_TOK_STRING, RCS_NOSCOL   },
211 };
212 
213 #define RCS_NKEYS	(sizeof(rcs_keys)/sizeof(rcs_keys[0]))
214 
215 static RCSNUM	*rcs_get_revision(const char *, RCSFILE *);
216 int		rcs_patch_lines(struct cvs_lines *, struct cvs_lines *,
217 		    struct cvs_line **, struct rcs_delta *);
218 static void	rcs_parse_init(RCSFILE *);
219 static int	rcs_parse_admin(RCSFILE *);
220 static int	rcs_parse_delta(RCSFILE *);
221 static void	rcs_parse_deltas(RCSFILE *, RCSNUM *);
222 static int	rcs_parse_deltatext(RCSFILE *);
223 static void	rcs_parse_deltatexts(RCSFILE *, RCSNUM *);
224 static void	rcs_parse_desc(RCSFILE *, RCSNUM *);
225 
226 static int	rcs_parse_access(RCSFILE *);
227 static int	rcs_parse_symbols(RCSFILE *);
228 static int	rcs_parse_locks(RCSFILE *);
229 static int	rcs_parse_branches(RCSFILE *, struct rcs_delta *);
230 static void	rcs_freedelta(struct rcs_delta *);
231 static void	rcs_freepdata(struct rcs_pdata *);
232 static int	rcs_gettok(RCSFILE *);
233 static int	rcs_pushtok(RCSFILE *, const char *, int);
234 static void	rcs_growbuf(RCSFILE *);
235 static void	rcs_strprint(const u_char *, size_t, FILE *);
236 
237 static void	rcs_kwexp_line(char *, struct rcs_delta *, struct cvs_lines *,
238 		    struct cvs_line *, int mode);
239 
240 static int rcs_ignore_keys = 0;
241 
242 RCSFILE *
243 rcs_open(const char *path, int fd, int flags, ...)
244 {
245 	int mode;
246 	mode_t fmode;
247 	RCSFILE *rfp;
248 	va_list vap;
249 	struct stat st;
250 	struct rcs_delta *rdp;
251 	struct rcs_lock *lkr;
252 
253 	fmode = S_IRUSR|S_IRGRP|S_IROTH;
254 	flags &= 0xffff;	/* ditch any internal flags */
255 
256 	if (flags & RCS_CREATE) {
257 		va_start(vap, flags);
258 		mode = va_arg(vap, int);
259 		va_end(vap);
260 		fmode = (mode_t)mode;
261 	} else {
262 		if (fstat(fd, &st) == -1)
263 			fatal("rcs_open: %s: fstat: %s", path, strerror(errno));
264 		fmode = st.st_mode;
265 	}
266 
267 	fmode &= ~cvs_umask;
268 
269 	rfp = xcalloc(1, sizeof(*rfp));
270 
271 	rfp->rf_path = xstrdup(path);
272 	rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
273 	rfp->rf_mode = fmode;
274 	rfp->fd = fd;
275 	rfp->rf_dead = 0;
276 
277 	TAILQ_INIT(&(rfp->rf_delta));
278 	TAILQ_INIT(&(rfp->rf_access));
279 	TAILQ_INIT(&(rfp->rf_symbols));
280 	TAILQ_INIT(&(rfp->rf_locks));
281 
282 	if (!(rfp->rf_flags & RCS_CREATE))
283 		rcs_parse_init(rfp);
284 
285 	/* fill in rd_locker */
286 	TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
287 		if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
288 			rcs_close(rfp);
289 			return (NULL);
290 		}
291 
292 		rdp->rd_locker = xstrdup(lkr->rl_name);
293 	}
294 
295 	return (rfp);
296 }
297 
298 /*
299  * rcs_close()
300  *
301  * Close an RCS file handle.
302  */
303 void
304 rcs_close(RCSFILE *rfp)
305 {
306 	struct rcs_delta *rdp;
307 	struct rcs_access *rap;
308 	struct rcs_lock *rlp;
309 	struct rcs_sym *rsp;
310 
311 	if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
312 		rcs_write(rfp);
313 
314 	while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
315 		rdp = TAILQ_FIRST(&(rfp->rf_delta));
316 		TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
317 		rcs_freedelta(rdp);
318 	}
319 
320 	while (!TAILQ_EMPTY(&(rfp->rf_access))) {
321 		rap = TAILQ_FIRST(&(rfp->rf_access));
322 		TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
323 		xfree(rap->ra_name);
324 		xfree(rap);
325 	}
326 
327 	while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
328 		rsp = TAILQ_FIRST(&(rfp->rf_symbols));
329 		TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
330 		rcsnum_free(rsp->rs_num);
331 		xfree(rsp->rs_name);
332 		xfree(rsp);
333 	}
334 
335 	while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
336 		rlp = TAILQ_FIRST(&(rfp->rf_locks));
337 		TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
338 		rcsnum_free(rlp->rl_num);
339 		xfree(rlp->rl_name);
340 		xfree(rlp);
341 	}
342 
343 	if (rfp->rf_head != NULL)
344 		rcsnum_free(rfp->rf_head);
345 	if (rfp->rf_branch != NULL)
346 		rcsnum_free(rfp->rf_branch);
347 
348 	if (rfp->rf_path != NULL)
349 		xfree(rfp->rf_path);
350 	if (rfp->rf_comment != NULL)
351 		xfree(rfp->rf_comment);
352 	if (rfp->rf_expand != NULL)
353 		xfree(rfp->rf_expand);
354 	if (rfp->rf_desc != NULL)
355 		xfree(rfp->rf_desc);
356 	if (rfp->rf_pdata != NULL)
357 		rcs_freepdata(rfp->rf_pdata);
358 	xfree(rfp);
359 }
360 
361 /*
362  * rcs_write()
363  *
364  * Write the contents of the RCS file handle <rfp> to disk in the file whose
365  * path is in <rf_path>.
366  */
367 void
368 rcs_write(RCSFILE *rfp)
369 {
370 	FILE *fp;
371 	char buf[1024], numbuf[CVS_REV_BUFSZ], *fn, tmpdir[MAXPATHLEN];
372 	struct rcs_access *ap;
373 	struct rcs_sym *symp;
374 	struct rcs_branch *brp;
375 	struct rcs_delta *rdp;
376 	struct rcs_lock *lkp;
377 	size_t len;
378 	int fd, saved_errno;
379 
380 	fd = -1;
381 
382 	if (rfp->rf_flags & RCS_SYNCED)
383 		return;
384 
385 	if (cvs_noexec == 1)
386 		return;
387 
388 	/* Write operations need the whole file parsed */
389 	rcs_parse_deltatexts(rfp, NULL);
390 
391 	if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
392 		fatal("rcs_write: truncation");
393 	(void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
394 
395 	if ((fd = mkstemp(fn)) == -1)
396 		fatal("%s", fn);
397 
398 	if ((fp = fdopen(fd, "w")) == NULL) {
399 		saved_errno = errno;
400 		(void)unlink(fn);
401 		fatal("fdopen %s: %s", fn, strerror(saved_errno));
402 	}
403 
404 	if (rfp->rf_head != NULL)
405 		rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
406 	else
407 		numbuf[0] = '\0';
408 
409 	fprintf(fp, "head\t%s;\n", numbuf);
410 
411 	if (rfp->rf_branch != NULL) {
412 		rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
413 		fprintf(fp, "branch\t%s;\n", numbuf);
414 	}
415 
416 	fputs("access", fp);
417 	TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
418 		fprintf(fp, "\n\t%s", ap->ra_name);
419 	}
420 	fputs(";\n", fp);
421 
422 	fprintf(fp, "symbols");
423 	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
424 		if (RCSNUM_ISBRANCH(symp->rs_num))
425 			rcsnum_addmagic(symp->rs_num);
426 		rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
427 		if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) ||
428 		    strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) ||
429 		    strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf))
430 			fatal("rcs_write: string overflow");
431 		fprintf(fp, "\n\t%s", buf);
432 	}
433 	fprintf(fp, ";\n");
434 
435 	fprintf(fp, "locks");
436 	TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
437 		rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
438 		fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
439 	}
440 
441 	fprintf(fp, ";");
442 
443 	if (rfp->rf_flags & RCS_SLOCK)
444 		fprintf(fp, " strict;");
445 	fputc('\n', fp);
446 
447 	fputs("comment\t@", fp);
448 	if (rfp->rf_comment != NULL) {
449 		rcs_strprint((const u_char *)rfp->rf_comment,
450 		    strlen(rfp->rf_comment), fp);
451 		fputs("@;\n", fp);
452 	} else
453 		fputs("# @;\n", fp);
454 
455 	if (rfp->rf_expand != NULL) {
456 		fputs("expand @", fp);
457 		rcs_strprint((const u_char *)rfp->rf_expand,
458 		    strlen(rfp->rf_expand), fp);
459 		fputs("@;\n", fp);
460 	}
461 
462 	fputs("\n\n", fp);
463 
464 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
465 		fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
466 		    sizeof(numbuf)));
467 		fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
468 		    rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
469 		    rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
470 		    rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
471 		fprintf(fp, "\tauthor %s;\tstate %s;\n",
472 		    rdp->rd_author, rdp->rd_state);
473 		fputs("branches", fp);
474 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
475 			fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
476 			    sizeof(numbuf)));
477 		}
478 		fputs(";\n", fp);
479 		fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
480 		    numbuf, sizeof(numbuf)));
481 	}
482 
483 	fputs("\ndesc\n@", fp);
484 	if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
485 		rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
486 		if (rfp->rf_desc[len-1] != '\n')
487 			fputc('\n', fp);
488 	}
489 	fputs("@\n", fp);
490 
491 	/* deltatexts */
492 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
493 		fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
494 		    sizeof(numbuf)));
495 		fputs("log\n@", fp);
496 		if (rdp->rd_log != NULL) {
497 			len = strlen(rdp->rd_log);
498 			rcs_strprint((const u_char *)rdp->rd_log, len, fp);
499 			if (rdp->rd_log[len-1] != '\n')
500 				fputc('\n', fp);
501 		}
502 		fputs("@\ntext\n@", fp);
503 		if (rdp->rd_text != NULL) {
504 			rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
505 		}
506 		fputs("@\n", fp);
507 	}
508 
509 	if (fchmod(fd, rfp->rf_mode) == -1) {
510 		saved_errno = errno;
511 		(void)unlink(fn);
512 		fatal("fchmod %s: %s", fn, strerror(saved_errno));
513 	}
514 
515 	(void)fclose(fp);
516 
517 	if (rename(fn, rfp->rf_path) == -1) {
518 		saved_errno = errno;
519 		(void)unlink(fn);
520 		fatal("rename(%s, %s): %s", fn, rfp->rf_path,
521 		    strerror(saved_errno));
522 	}
523 
524 	rfp->rf_flags |= RCS_SYNCED;
525 
526 	if (fn != NULL)
527 		xfree(fn);
528 }
529 
530 /*
531  * rcs_head_get()
532  *
533  * Retrieve the revision number of the head revision for the RCS file <file>.
534  */
535 RCSNUM *
536 rcs_head_get(RCSFILE *file)
537 {
538 	struct rcs_branch *brp;
539 	struct rcs_delta *rdp;
540 	RCSNUM *rev, *rootrev;
541 
542 	if (file->rf_head == NULL)
543 		return NULL;
544 
545 	rev = rcsnum_alloc();
546 	if (file->rf_branch != NULL) {
547 		/* we have a default branch, use that to calculate the
548 		 * real HEAD*/
549 		rootrev = rcsnum_alloc();
550 		rcsnum_cpy(file->rf_branch, rootrev, 2);
551 		if ((rdp = rcs_findrev(file, rootrev)) == NULL)
552 			fatal("rcs_head_get: could not find root revision");
553 
554 		/* HEAD should be the last revision on the default branch */
555 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
556 			if (TAILQ_NEXT(brp, rb_list) == NULL)
557 				break;
558 		}
559 		rcsnum_free(rootrev);
560 
561 		if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
562 			fatal("rcs_head_get: could not find branch revision");
563 		while (rdp->rd_next->rn_len != 0)
564 			if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
565 				fatal("rcs_head_get: could not find "
566 				    "next branch revision");
567 
568 		rcsnum_cpy(rdp->rd_num, rev, 0);
569 	} else {
570 		rcsnum_cpy(file->rf_head, rev, 0);
571 	}
572 
573 	return (rev);
574 }
575 
576 /*
577  * rcs_head_set()
578  *
579  * Set the revision number of the head revision for the RCS file <file> to
580  * <rev>, which must reference a valid revision within the file.
581  */
582 int
583 rcs_head_set(RCSFILE *file, RCSNUM *rev)
584 {
585 	if (rcs_findrev(file, rev) == NULL)
586 		return (-1);
587 
588 	if (file->rf_head == NULL)
589 		file->rf_head = rcsnum_alloc();
590 
591 	rcsnum_cpy(rev, file->rf_head, 0);
592 	file->rf_flags &= ~RCS_SYNCED;
593 	return (0);
594 }
595 
596 /*
597  * rcs_branch_new()
598  *
599  * Create a new branch out of supplied revision for the RCS file <file>.
600  */
601 RCSNUM *
602 rcs_branch_new(RCSFILE *file, RCSNUM *rev)
603 {
604 	RCSNUM *brev;
605 	struct rcs_sym *sym;
606 
607 	if ((brev = rcsnum_new_branch(rev)) == NULL)
608 		return (NULL);
609 
610 	for (;;) {
611 		TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)
612 			if (!rcsnum_cmp(sym->rs_num, brev, 0))
613 				break;
614 
615 		if (sym != NULL) {
616 			if (rcsnum_inc(brev) == NULL ||
617 			    rcsnum_inc(brev) == NULL)
618 				return (NULL);
619 		} else
620 			break;
621 	}
622 
623 	return (brev);
624 }
625 
626 /*
627  * rcs_branch_get()
628  *
629  * Retrieve the default branch number for the RCS file <file>.
630  * Returns the number on success.  If NULL is returned, then there is no
631  * default branch for this file.
632  */
633 const RCSNUM *
634 rcs_branch_get(RCSFILE *file)
635 {
636 	return (file->rf_branch);
637 }
638 
639 /*
640  * rcs_branch_set()
641  *
642  * Set the default branch for the RCS file <file> to <bnum>.
643  * Returns 0 on success, -1 on failure.
644  */
645 int
646 rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
647 {
648 	if (file->rf_branch == NULL)
649 		file->rf_branch = rcsnum_alloc();
650 
651 	rcsnum_cpy(bnum, file->rf_branch, 0);
652 	file->rf_flags &= ~RCS_SYNCED;
653 	return (0);
654 }
655 
656 /*
657  * rcs_access_add()
658  *
659  * Add the login name <login> to the access list for the RCS file <file>.
660  * Returns 0 on success, or -1 on failure.
661  */
662 int
663 rcs_access_add(RCSFILE *file, const char *login)
664 {
665 	struct rcs_access *ap;
666 
667 	/* first look for duplication */
668 	TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
669 		if (strcmp(ap->ra_name, login) == 0)
670 			return (-1);
671 	}
672 
673 	ap = xmalloc(sizeof(*ap));
674 	ap->ra_name = xstrdup(login);
675 	TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
676 
677 	/* not synced anymore */
678 	file->rf_flags &= ~RCS_SYNCED;
679 	return (0);
680 }
681 
682 /*
683  * rcs_access_remove()
684  *
685  * Remove an entry with login name <login> from the access list of the RCS
686  * file <file>.
687  * Returns 0 on success, or -1 on failure.
688  */
689 int
690 rcs_access_remove(RCSFILE *file, const char *login)
691 {
692 	struct rcs_access *ap;
693 
694 	TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
695 		if (strcmp(ap->ra_name, login) == 0)
696 			break;
697 
698 	if (ap == NULL)
699 		return (-1);
700 
701 	TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
702 	xfree(ap->ra_name);
703 	xfree(ap);
704 
705 	/* not synced anymore */
706 	file->rf_flags &= ~RCS_SYNCED;
707 	return (0);
708 }
709 
710 /*
711  * rcs_sym_add()
712  *
713  * Add a symbol to the list of symbols for the RCS file <rfp>.  The new symbol
714  * is named <sym> and is bound to the RCS revision <snum>.
715  */
716 int
717 rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
718 {
719 	struct rcs_sym *symp;
720 
721 	if (!rcs_sym_check(sym))
722 		return (-1);
723 
724 	/* first look for duplication */
725 	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
726 		if (strcmp(symp->rs_name, sym) == 0)
727 			return (1);
728 	}
729 
730 	symp = xmalloc(sizeof(*symp));
731 	symp->rs_name = xstrdup(sym);
732 	symp->rs_num = rcsnum_alloc();
733 	rcsnum_cpy(snum, symp->rs_num, 0);
734 
735 	TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
736 
737 	/* not synced anymore */
738 	rfp->rf_flags &= ~RCS_SYNCED;
739 	return (0);
740 }
741 
742 /*
743  * rcs_sym_remove()
744  *
745  * Remove the symbol with name <sym> from the symbol list for the RCS file
746  * <file>.  If no such symbol is found, the call fails and returns with an
747  * error.
748  * Returns 0 on success, or -1 on failure.
749  */
750 int
751 rcs_sym_remove(RCSFILE *file, const char *sym)
752 {
753 	struct rcs_sym *symp;
754 
755 	if (!rcs_sym_check(sym))
756 		return (-1);
757 
758 	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
759 		if (strcmp(symp->rs_name, sym) == 0)
760 			break;
761 
762 	if (symp == NULL)
763 		return (-1);
764 
765 	TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
766 	xfree(symp->rs_name);
767 	rcsnum_free(symp->rs_num);
768 	xfree(symp);
769 
770 	/* not synced anymore */
771 	file->rf_flags &= ~RCS_SYNCED;
772 	return (0);
773 }
774 
775 /*
776  * rcs_sym_get()
777  *
778  * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
779  *
780  * Returns a pointer to the symbol on success, or NULL on failure.
781  */
782 struct rcs_sym *
783 rcs_sym_get(RCSFILE *file, const char *sym)
784 {
785 	struct rcs_sym *symp;
786 
787 	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
788 		if (strcmp(symp->rs_name, sym) == 0)
789 			return (symp);
790 
791 	return (NULL);
792 }
793 
794 /*
795  * rcs_sym_getrev()
796  *
797  * Retrieve the RCS revision number associated with the symbol <sym> for the
798  * RCS file <file>.  The returned value is a dynamically-allocated copy and
799  * should be freed by the caller once they are done with it.
800  * Returns the RCSNUM on success, or NULL on failure.
801  */
802 RCSNUM *
803 rcs_sym_getrev(RCSFILE *file, const char *sym)
804 {
805 	RCSNUM *num;
806 	struct rcs_sym *symp;
807 
808 	if (!rcs_sym_check(sym) || file->rf_head == NULL)
809 		return (NULL);
810 
811 	if (!strcmp(sym, RCS_HEAD_BRANCH)) {
812 		num = rcsnum_alloc();
813 		rcsnum_cpy(file->rf_head, num, 0);
814 		return (num);
815 	}
816 
817 	num = NULL;
818 	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
819 		if (strcmp(symp->rs_name, sym) == 0)
820 			break;
821 
822 	if (symp != NULL) {
823 		num = rcsnum_alloc();
824 		rcsnum_cpy(symp->rs_num, num, 0);
825 	}
826 
827 	return (num);
828 }
829 
830 /*
831  * rcs_sym_check()
832  *
833  * Check the RCS symbol name <sym> for any unsupported characters.
834  * Returns 1 if the tag is correct, 0 if it isn't valid.
835  */
836 int
837 rcs_sym_check(const char *sym)
838 {
839 	int ret;
840 	const char *cp;
841 
842 	ret = 1;
843 	cp = sym;
844 	if (!isalpha(*cp++))
845 		return (0);
846 
847 	for (; *cp != '\0'; cp++)
848 		if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
849 			ret = 0;
850 			break;
851 		}
852 
853 	return (ret);
854 }
855 
856 /*
857  * rcs_lock_getmode()
858  *
859  * Retrieve the locking mode of the RCS file <file>.
860  */
861 int
862 rcs_lock_getmode(RCSFILE *file)
863 {
864 	return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
865 }
866 
867 /*
868  * rcs_lock_setmode()
869  *
870  * Set the locking mode of the RCS file <file> to <mode>, which must either
871  * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
872  * Returns the previous mode on success, or -1 on failure.
873  */
874 int
875 rcs_lock_setmode(RCSFILE *file, int mode)
876 {
877 	int pmode;
878 	pmode = rcs_lock_getmode(file);
879 
880 	if (mode == RCS_LOCK_STRICT)
881 		file->rf_flags |= RCS_SLOCK;
882 	else if (mode == RCS_LOCK_LOOSE)
883 		file->rf_flags &= ~RCS_SLOCK;
884 	else
885 		fatal("rcs_lock_setmode: invalid mode `%d'", mode);
886 
887 	file->rf_flags &= ~RCS_SYNCED;
888 	return (pmode);
889 }
890 
891 /*
892  * rcs_lock_add()
893  *
894  * Add an RCS lock for the user <user> on revision <rev>.
895  * Returns 0 on success, or -1 on failure.
896  */
897 int
898 rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
899 {
900 	struct rcs_lock *lkp;
901 
902 	/* first look for duplication */
903 	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
904 		if (strcmp(lkp->rl_name, user) == 0 &&
905 		    rcsnum_cmp(rev, lkp->rl_num, 0) == 0)
906 			return (-1);
907 	}
908 
909 	lkp = xmalloc(sizeof(*lkp));
910 	lkp->rl_name = xstrdup(user);
911 	lkp->rl_num = rcsnum_alloc();
912 	rcsnum_cpy(rev, lkp->rl_num, 0);
913 
914 	TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list);
915 
916 	/* not synced anymore */
917 	file->rf_flags &= ~RCS_SYNCED;
918 	return (0);
919 }
920 
921 
922 /*
923  * rcs_lock_remove()
924  *
925  * Remove the RCS lock on revision <rev>.
926  * Returns 0 on success, or -1 on failure.
927  */
928 int
929 rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
930 {
931 	struct rcs_lock *lkp;
932 
933 	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
934 		if (strcmp(lkp->rl_name, user) == 0 &&
935 		    rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
936 			break;
937 	}
938 
939 	if (lkp == NULL)
940 		return (-1);
941 
942 	TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list);
943 	rcsnum_free(lkp->rl_num);
944 	xfree(lkp->rl_name);
945 	xfree(lkp);
946 
947 	/* not synced anymore */
948 	file->rf_flags &= ~RCS_SYNCED;
949 	return (0);
950 }
951 
952 /*
953  * rcs_desc_get()
954  *
955  * Retrieve the description for the RCS file <file>.
956  */
957 const char *
958 rcs_desc_get(RCSFILE *file)
959 {
960 	return (file->rf_desc);
961 }
962 
963 /*
964  * rcs_desc_set()
965  *
966  * Set the description for the RCS file <file>.
967  */
968 void
969 rcs_desc_set(RCSFILE *file, const char *desc)
970 {
971 	char *tmp;
972 
973 	tmp = xstrdup(desc);
974 	if (file->rf_desc != NULL)
975 		xfree(file->rf_desc);
976 	file->rf_desc = tmp;
977 	file->rf_flags &= ~RCS_SYNCED;
978 }
979 
980 /*
981  * rcs_comment_lookup()
982  *
983  * Lookup the assumed comment leader based on a file's suffix.
984  * Returns a pointer to the string on success, or NULL on failure.
985  */
986 const char *
987 rcs_comment_lookup(const char *filename)
988 {
989 	int i;
990 	const char *sp;
991 
992 	if ((sp = strrchr(filename, '.')) == NULL)
993 		return (NULL);
994 	sp++;
995 
996 	for (i = 0; i < (int)NB_COMTYPES; i++)
997 		if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
998 			return (rcs_comments[i].rc_cstr);
999 	return (NULL);
1000 }
1001 
1002 /*
1003  * rcs_comment_get()
1004  *
1005  * Retrieve the comment leader for the RCS file <file>.
1006  */
1007 const char *
1008 rcs_comment_get(RCSFILE *file)
1009 {
1010 	return (file->rf_comment);
1011 }
1012 
1013 /*
1014  * rcs_comment_set()
1015  *
1016  * Set the comment leader for the RCS file <file>.
1017  */
1018 void
1019 rcs_comment_set(RCSFILE *file, const char *comment)
1020 {
1021 	char *tmp;
1022 
1023 	tmp = xstrdup(comment);
1024 	if (file->rf_comment != NULL)
1025 		xfree(file->rf_comment);
1026 	file->rf_comment = tmp;
1027 	file->rf_flags &= ~RCS_SYNCED;
1028 }
1029 
1030 int
1031 rcs_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines,
1032     struct cvs_line **alines, struct rcs_delta *rdp)
1033 {
1034 	u_char op;
1035 	char *ep;
1036 	struct cvs_line *lp, *dlp, *ndlp;
1037 	int i, lineno, nbln;
1038 	u_char tmp;
1039 
1040 	dlp = TAILQ_FIRST(&(dlines->l_lines));
1041 	lp = TAILQ_FIRST(&(plines->l_lines));
1042 
1043 	/* skip first bogus line */
1044 	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1045 	    lp = TAILQ_NEXT(lp, l_list)) {
1046 		if (lp->l_len < 2)
1047 			fatal("line too short, RCS patch seems broken");
1048 		op = *(lp->l_line);
1049 		/* NUL-terminate line buffer for strtol() safety. */
1050 		tmp = lp->l_line[lp->l_len - 1];
1051 		lp->l_line[lp->l_len - 1] = '\0';
1052 		lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
1053 		if (lineno - 1 > dlines->l_nblines || lineno < 0) {
1054 			fatal("invalid line specification in RCS patch");
1055 		}
1056 		ep++;
1057 		nbln = (int)strtol(ep, &ep, 10);
1058 		/* Restore the last byte of the buffer */
1059 		lp->l_line[lp->l_len - 1] = tmp;
1060 		if (nbln < 0)
1061 			fatal("invalid line number specification in RCS patch");
1062 
1063 		/* find the appropriate line */
1064 		for (;;) {
1065 			if (dlp == NULL)
1066 				break;
1067 			if (dlp->l_lineno == lineno)
1068 				break;
1069 			if (dlp->l_lineno > lineno) {
1070 				dlp = TAILQ_PREV(dlp, cvs_tqh, l_list);
1071 			} else if (dlp->l_lineno < lineno) {
1072 				if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
1073 				    ndlp->l_lineno > lineno)
1074 					break;
1075 				dlp = ndlp;
1076 			}
1077 		}
1078 		if (dlp == NULL)
1079 			fatal("can't find referenced line in RCS patch");
1080 
1081 		if (op == 'd') {
1082 			for (i = 0; (i < nbln) && (dlp != NULL); i++) {
1083 				ndlp = TAILQ_NEXT(dlp, l_list);
1084 				TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
1085 				if (alines != NULL && dlp->l_line != NULL) {
1086 					dlp->l_delta = rdp;
1087 					alines[dlp->l_lineno_orig - 1] =
1088 						dlp;
1089 				} else
1090 					xfree(dlp);
1091 				dlp = ndlp;
1092 				/* last line is gone - reset dlp */
1093 				if (dlp == NULL) {
1094 					ndlp = TAILQ_LAST(&(dlines->l_lines),
1095 					    cvs_tqh);
1096 					dlp = ndlp;
1097 				}
1098 			}
1099 		} else if (op == 'a') {
1100 			for (i = 0; i < nbln; i++) {
1101 				ndlp = lp;
1102 				lp = TAILQ_NEXT(lp, l_list);
1103 				if (lp == NULL)
1104 					fatal("truncated RCS patch");
1105 				TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
1106 				if (alines != NULL) {
1107 					if (lp->l_needsfree == 1)
1108 						xfree(lp->l_line);
1109 					lp->l_line = NULL;
1110 					lp->l_needsfree = 0;
1111 				}
1112 				lp->l_delta = rdp;
1113 				TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
1114 				    lp, l_list);
1115 				dlp = lp;
1116 
1117 				/* we don't want lookup to block on those */
1118 				lp->l_lineno = lineno;
1119 
1120 				lp = ndlp;
1121 			}
1122 		} else
1123 			fatal("unknown RCS patch operation `%c'", op);
1124 
1125 		/* last line of the patch, done */
1126 		if (lp->l_lineno == plines->l_nblines)
1127 			break;
1128 	}
1129 
1130 	/* once we're done patching, rebuild the line numbers */
1131 	lineno = 0;
1132 	TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
1133 		lp->l_lineno = lineno++;
1134 	dlines->l_nblines = lineno - 1;
1135 
1136 	return (0);
1137 }
1138 
1139 void
1140 rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
1141 {
1142 	struct cvs_lines *plines;
1143 	struct cvs_line *lp;
1144 	int added, i, lineno, nbln, removed;
1145 	char op, *ep;
1146 	u_char tmp;
1147 
1148 	added = removed = 0;
1149 
1150 	plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
1151 	lp = TAILQ_FIRST(&(plines->l_lines));
1152 
1153 	/* skip first bogus line */
1154 	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1155 	    lp = TAILQ_NEXT(lp, l_list)) {
1156 		if (lp->l_len < 2)
1157 			fatal("line too short, RCS patch seems broken");
1158 		op = *(lp->l_line);
1159 		/* NUL-terminate line buffer for strtol() safety. */
1160 		tmp = lp->l_line[lp->l_len - 1];
1161 		lp->l_line[lp->l_len - 1] = '\0';
1162 		lineno = (int)strtol((lp->l_line + 1), &ep, 10);
1163 		ep++;
1164 		nbln = (int)strtol(ep, &ep, 10);
1165 		/* Restore the last byte of the buffer */
1166 		lp->l_line[lp->l_len - 1] = tmp;
1167 		if (nbln < 0)
1168 			fatal("invalid line number specification in RCS patch");
1169 
1170 		if (op == 'a') {
1171 			added += nbln;
1172 			for (i = 0; i < nbln; i++) {
1173 				lp = TAILQ_NEXT(lp, l_list);
1174 				if (lp == NULL)
1175 					fatal("truncated RCS patch");
1176 			}
1177 		}
1178 		else if (op == 'd')
1179 			removed += nbln;
1180 		else
1181 			fatal("unknown RCS patch operation '%c'", op);
1182 	}
1183 
1184 	cvs_freelines(plines);
1185 
1186 	*ladded = added;
1187 	*lremoved = removed;
1188 }
1189 
1190 /*
1191  * rcs_rev_add()
1192  *
1193  * Add a revision to the RCS file <rf>.  The new revision's number can be
1194  * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1195  * new revision will have a number equal to the previous head revision plus
1196  * one).  The <msg> argument specifies the log message for that revision, and
1197  * <date> specifies the revision's date (a value of -1 is
1198  * equivalent to using the current time).
1199  * If <username> is NULL, set the author for this revision to the current user.
1200  * Otherwise, set it to <username>.
1201  * Returns 0 on success, or -1 on failure.
1202  */
1203 int
1204 rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1205     const char *username)
1206 {
1207 	time_t now;
1208 	RCSNUM *root = NULL;
1209 	struct passwd *pw;
1210 	struct rcs_branch *brp, *obrp;
1211 	struct rcs_delta *ordp, *rdp;
1212 	uid_t uid;
1213 
1214 	if (rev == RCS_HEAD_REV) {
1215 		if (rf->rf_flags & RCS_CREATE) {
1216 			if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1217 				return (-1);
1218 			if (rf->rf_head != NULL)
1219 				xfree(rf->rf_head);
1220 			rf->rf_head = rcsnum_alloc();
1221 			rcsnum_cpy(rev, rf->rf_head, 0);
1222 		} else if (rf->rf_head == NULL) {
1223 			return (-1);
1224 		} else {
1225 			rev = rcsnum_inc(rf->rf_head);
1226 		}
1227 	} else {
1228 		if ((rdp = rcs_findrev(rf, rev)) != NULL)
1229 			return (-1);
1230 	}
1231 
1232 	uid = getuid();
1233 	if ((pw = getpwuid(uid)) == NULL)
1234 		fatal("getpwuid failed");
1235 
1236 	rdp = xcalloc(1, sizeof(*rdp));
1237 
1238 	TAILQ_INIT(&(rdp->rd_branches));
1239 
1240 	rdp->rd_num = rcsnum_alloc();
1241 	rcsnum_cpy(rev, rdp->rd_num, 0);
1242 
1243 	rdp->rd_next = rcsnum_alloc();
1244 
1245 	if (uid == 0)
1246 		username = getlogin();
1247 	if (username == NULL || *username == '\0')
1248 		username = pw->pw_name;
1249 
1250 	rdp->rd_author = xstrdup(username);
1251 	rdp->rd_state = xstrdup(RCS_STATE_EXP);
1252 	rdp->rd_log = xstrdup(msg);
1253 
1254 	if (date != (time_t)(-1))
1255 		now = date;
1256 	else
1257 		time(&now);
1258 	gmtime_r(&now, &(rdp->rd_date));
1259 
1260 	if (RCSNUM_ISBRANCHREV(rev))
1261 		TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1262 	else
1263 		TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1264 	rf->rf_ndelta++;
1265 
1266 	if (!(rf->rf_flags & RCS_CREATE)) {
1267 		if (RCSNUM_ISBRANCHREV(rev)) {
1268 			if (rev->rn_id[rev->rn_len - 1] == 1) {
1269 				/* a new branch */
1270 				root = rcsnum_branch_root(rev);
1271 				brp = xmalloc(sizeof(*brp));
1272 				brp->rb_num = rcsnum_alloc();
1273 				rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1274 
1275 				if ((ordp = rcs_findrev(rf, root)) == NULL)
1276 					fatal("root node not found");
1277 
1278 				TAILQ_FOREACH(obrp, &(ordp->rd_branches),
1279 				    rb_list) {
1280 					if (!rcsnum_cmp(obrp->rb_num,
1281 					    brp->rb_num,
1282 					    brp->rb_num->rn_len - 1))
1283 						break;
1284 				}
1285 
1286 				if (obrp == NULL) {
1287 					TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1288 					    brp, rb_list);
1289 				}
1290 			} else {
1291 				root = rcsnum_alloc();
1292 				rcsnum_cpy(rev, root, 0);
1293 				rcsnum_dec(root);
1294 				if ((ordp = rcs_findrev(rf, root)) == NULL)
1295 					fatal("previous revision not found");
1296 				rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1297 			}
1298 		} else {
1299 			ordp = TAILQ_NEXT(rdp, rd_list);
1300 			rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1301 		}
1302 	}
1303 
1304 	if (root != NULL)
1305 		rcsnum_free(root);
1306 
1307 	/* not synced anymore */
1308 	rf->rf_flags &= ~RCS_SYNCED;
1309 
1310 	return (0);
1311 }
1312 
1313 /*
1314  * rcs_rev_remove()
1315  *
1316  * Remove the revision whose number is <rev> from the RCS file <rf>.
1317  */
1318 int
1319 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1320 {
1321 	int fd1, fd2;
1322 	char *path_tmp1, *path_tmp2;
1323 	struct rcs_delta *rdp, *prevrdp, *nextrdp;
1324 	BUF *prevbuf, *newdiff, *newdeltatext;
1325 
1326 	if (rev == RCS_HEAD_REV)
1327 		rev = rf->rf_head;
1328 
1329 	if (rev == NULL)
1330 		return (-1);
1331 
1332 	/* do we actually have that revision? */
1333 	if ((rdp = rcs_findrev(rf, rev)) == NULL)
1334 		return (-1);
1335 
1336 	/*
1337 	 * This is confusing, the previous delta is next in the TAILQ list.
1338 	 * the next delta is the previous one in the TAILQ list.
1339 	 *
1340 	 * When the HEAD revision got specified, nextrdp will be NULL.
1341 	 * When the first revision got specified, prevrdp will be NULL.
1342 	 */
1343 	prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
1344 	nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list);
1345 
1346 	newdeltatext = NULL;
1347 	prevbuf = NULL;
1348 	path_tmp1 = path_tmp2 = NULL;
1349 
1350 	if (prevrdp != NULL && nextrdp != NULL) {
1351 		newdiff = cvs_buf_alloc(64);
1352 
1353 		/* calculate new diff */
1354 		(void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1355 		fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1356 
1357 		(void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1358 		fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1359 
1360 		diff_format = D_RCSDIFF;
1361 		if (cvs_diffreg(path_tmp1, path_tmp2,
1362 		    fd1, fd2, newdiff) == D_ERROR)
1363 			fatal("rcs_diffreg failed");
1364 
1365 		close(fd1);
1366 		close(fd2);
1367 
1368 		newdeltatext = newdiff;
1369 	} else if (nextrdp == NULL && prevrdp != NULL) {
1370 		newdeltatext = prevbuf;
1371 	}
1372 
1373 	if (newdeltatext != NULL) {
1374 		if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1375 			fatal("error setting new deltatext");
1376 	}
1377 
1378 	TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1379 
1380 	/* update pointers */
1381 	if (prevrdp != NULL && nextrdp != NULL) {
1382 		rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1383 	} else if (prevrdp != NULL) {
1384 		if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1385 			fatal("rcs_head_set failed");
1386 	} else if (nextrdp != NULL) {
1387 		rcsnum_free(nextrdp->rd_next);
1388 		nextrdp->rd_next = rcsnum_alloc();
1389 	} else {
1390 		rcsnum_free(rf->rf_head);
1391 		rf->rf_head = NULL;
1392 	}
1393 
1394 	rf->rf_ndelta--;
1395 	rf->rf_flags &= ~RCS_SYNCED;
1396 
1397 	rcs_freedelta(rdp);
1398 
1399 	if (newdeltatext != NULL)
1400 		xfree(newdeltatext);
1401 
1402 	if (path_tmp1 != NULL)
1403 		xfree(path_tmp1);
1404 	if (path_tmp2 != NULL)
1405 		xfree(path_tmp2);
1406 
1407 	return (0);
1408 }
1409 
1410 /*
1411  * rcs_findrev()
1412  *
1413  * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1414  * The revision number is given in <rev>.
1415  *
1416  * Returns a pointer to the delta on success, or NULL on failure.
1417  */
1418 struct rcs_delta *
1419 rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1420 {
1421 	int isbrev;
1422 	struct rcs_delta *rdp;
1423 
1424 	if (rev == NULL)
1425 		return NULL;
1426 
1427 	isbrev = RCSNUM_ISBRANCHREV(rev);
1428 
1429 	/*
1430 	 * We need to do more parsing if the last revision in the linked list
1431 	 * is greater than the requested revision.
1432 	 */
1433 	rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1434 	if (rdp == NULL ||
1435 	    (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1436 	    ((isbrev && rdp->rd_num->rn_len < 4) ||
1437 	    (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1438 		rcs_parse_deltas(rfp, rev);
1439 	}
1440 
1441 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1442 		if (rcsnum_differ(rdp->rd_num, rev))
1443 			continue;
1444 		else
1445 			return (rdp);
1446 	}
1447 
1448 	return (NULL);
1449 }
1450 
1451 /*
1452  * rcs_kwexp_set()
1453  *
1454  * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1455  */
1456 void
1457 rcs_kwexp_set(RCSFILE *file, int mode)
1458 {
1459 	int i;
1460 	char *tmp, buf[8] = "";
1461 
1462 	if (RCS_KWEXP_INVAL(mode))
1463 		return;
1464 
1465 	i = 0;
1466 	if (mode == RCS_KWEXP_NONE)
1467 		buf[0] = 'b';
1468 	else if (mode == RCS_KWEXP_OLD)
1469 		buf[0] = 'o';
1470 	else {
1471 		if (mode & RCS_KWEXP_NAME)
1472 			buf[i++] = 'k';
1473 		if (mode & RCS_KWEXP_VAL)
1474 			buf[i++] = 'v';
1475 		if (mode & RCS_KWEXP_LKR)
1476 			buf[i++] = 'l';
1477 	}
1478 
1479 	tmp = xstrdup(buf);
1480 	if (file->rf_expand != NULL)
1481 		xfree(file->rf_expand);
1482 	file->rf_expand = tmp;
1483 	/* not synced anymore */
1484 	file->rf_flags &= ~RCS_SYNCED;
1485 }
1486 
1487 /*
1488  * rcs_kwexp_get()
1489  *
1490  * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1491  */
1492 int
1493 rcs_kwexp_get(RCSFILE *file)
1494 {
1495 	return rcs_kflag_get(file->rf_expand);
1496 }
1497 
1498 /*
1499  * rcs_kflag_get()
1500  *
1501  * Get the keyword expansion mode from a set of character flags given in
1502  * <flags> and return the appropriate flag mask.  In case of an error, the
1503  * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1504  */
1505 int
1506 rcs_kflag_get(const char *flags)
1507 {
1508 	int fl;
1509 	size_t len;
1510 	const char *fp;
1511 
1512 	if (flags == NULL)
1513 		return 0;
1514 
1515 	fl = 0;
1516 	if (!(len = strlen(flags)))
1517 		return RCS_KWEXP_ERR;
1518 
1519 	for (fp = flags; *fp != '\0'; fp++) {
1520 		if (*fp == 'k')
1521 			fl |= RCS_KWEXP_NAME;
1522 		else if (*fp == 'v')
1523 			fl |= RCS_KWEXP_VAL;
1524 		else if (*fp == 'l')
1525 			fl |= RCS_KWEXP_LKR;
1526 		else if (*fp == 'o') {
1527 			if (len != 1)
1528 				fl |= RCS_KWEXP_ERR;
1529 			fl |= RCS_KWEXP_OLD;
1530 		} else if (*fp == 'b') {
1531 			if (len != 1)
1532 				fl |= RCS_KWEXP_ERR;
1533 			fl |= RCS_KWEXP_NONE;
1534 		} else	/* unknown letter */
1535 			fl |= RCS_KWEXP_ERR;
1536 	}
1537 
1538 	return (fl);
1539 }
1540 
1541 /* rcs_parse_deltas()
1542  *
1543  * Parse deltas. If <rev> is not NULL, parse only as far as that
1544  * revision. If <rev> is NULL, parse all deltas.
1545  */
1546 static void
1547 rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev)
1548 {
1549 	int ret;
1550 	struct rcs_delta *enddelta;
1551 
1552 	if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
1553 		return;
1554 
1555 	for (;;) {
1556 		ret = rcs_parse_delta(rfp);
1557 		if (rev != NULL) {
1558 			enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1559 			if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
1560 				break;
1561 		}
1562 
1563 		if (ret == 0) {
1564 			rfp->rf_flags |= PARSED_DELTAS;
1565 			break;
1566 		}
1567 		else if (ret == -1)
1568 			fatal("error parsing deltas");
1569 	}
1570 }
1571 
1572 /* rcs_parse_deltatexts()
1573  *
1574  * Parse deltatexts. If <rev> is not NULL, parse only as far as that
1575  * revision. If <rev> is NULL, parse everything.
1576  */
1577 static void
1578 rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
1579 {
1580 	int ret;
1581 	struct rcs_delta *rdp;
1582 
1583 	if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
1584 	    (rfp->rf_flags & RCS_CREATE))
1585 		return;
1586 
1587 	if (!(rfp->rf_flags & PARSED_DESC))
1588 		rcs_parse_desc(rfp, rev);
1589 	for (;;) {
1590 		if (rev != NULL) {
1591 			rdp = rcs_findrev(rfp, rev);
1592 			if (rdp->rd_text != NULL)
1593 				break;
1594 			else
1595 				ret = rcs_parse_deltatext(rfp);
1596 		} else
1597 			ret = rcs_parse_deltatext(rfp);
1598 		if (ret == 0) {
1599 			rfp->rf_flags |= PARSED_DELTATEXTS;
1600 			break;
1601 		}
1602 		else if (ret == -1)
1603 			fatal("problem parsing deltatexts");
1604 	}
1605 }
1606 
1607 /* rcs_parse_desc()
1608  *
1609  * Parse RCS description.
1610  */
1611 static void
1612 rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev)
1613 {
1614 	int ret = 0;
1615 
1616 	if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE))
1617 		return;
1618 	if (!(rfp->rf_flags & PARSED_DELTAS))
1619 		rcs_parse_deltas(rfp, rev);
1620 	/* do parsing */
1621 	ret = rcs_gettok(rfp);
1622 	if (ret != RCS_TOK_DESC)
1623 		fatal("token `%s' found where RCS desc expected",
1624 		    RCS_TOKSTR(rfp));
1625 
1626 	ret = rcs_gettok(rfp);
1627 	if (ret != RCS_TOK_STRING)
1628 		fatal("token `%s' found where RCS desc expected",
1629 		    RCS_TOKSTR(rfp));
1630 
1631 	rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp));
1632 	rfp->rf_flags |= PARSED_DESC;
1633 }
1634 
1635 /*
1636  * rcs_parse_init()
1637  *
1638  * Initial parsing of file <path>, which are in the RCS format.
1639  * Just does admin section.
1640  */
1641 static void
1642 rcs_parse_init(RCSFILE *rfp)
1643 {
1644 	struct rcs_pdata *pdp;
1645 
1646 	if (rfp->rf_flags & RCS_PARSED)
1647 		return;
1648 
1649 	pdp = xcalloc(1, sizeof(*pdp));
1650 
1651 	pdp->rp_lines = 0;
1652 	pdp->rp_pttype = RCS_TOK_ERR;
1653 
1654 	if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL)
1655 		fatal("fdopen: `%s'", rfp->rf_path);
1656 
1657 	pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE);
1658 	pdp->rp_blen = RCS_BUFSIZE;
1659 	pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1660 
1661 	/* ditch the strict lock */
1662 	rfp->rf_flags &= ~RCS_SLOCK;
1663 	rfp->rf_pdata = pdp;
1664 
1665 	if (rcs_parse_admin(rfp) < 0) {
1666 		rcs_freepdata(pdp);
1667 		fatal("could not parse admin data");
1668 	}
1669 
1670 	if (rfp->rf_flags & RCS_PARSE_FULLY) {
1671 		rcs_parse_deltatexts(rfp, NULL);
1672 		(void)close(rfp->fd);
1673 		rfp->fd = -1;
1674 	}
1675 
1676 	rfp->rf_flags |= RCS_SYNCED;
1677 }
1678 
1679 /*
1680  * rcs_parse_admin()
1681  *
1682  * Parse the administrative portion of an RCS file.
1683  * Returns the type of the first token found after the admin section on
1684  * success, or -1 on failure.
1685  */
1686 static int
1687 rcs_parse_admin(RCSFILE *rfp)
1688 {
1689 	u_int i;
1690 	int tok, ntok, hmask;
1691 	struct rcs_key *rk;
1692 
1693 	rfp->rf_head = NULL;
1694 	rfp->rf_branch = NULL;
1695 
1696 	/* hmask is a mask of the headers already encountered */
1697 	hmask = 0;
1698 	for (;;) {
1699 		tok = rcs_gettok(rfp);
1700 		if (tok == RCS_TOK_ERR) {
1701 			cvs_log(LP_ERR, "parse error in RCS admin section");
1702 			goto fail;
1703 		} else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1704 			/*
1705 			 * Assume this is the start of the first delta or
1706 			 * that we are dealing with an empty RCS file and
1707 			 * we just found the description.
1708 			 */
1709 			if (!(hmask & (1 << RCS_TOK_HEAD))) {
1710 				cvs_log(LP_ERR, "head missing");
1711 				goto fail;
1712 			}
1713 			rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1714 			return (tok);
1715 		}
1716 
1717 		rk = NULL;
1718 		for (i = 0; i < RCS_NKEYS; i++)
1719 			if (rcs_keys[i].rk_id == tok)
1720 				rk = &(rcs_keys[i]);
1721 
1722 		if (hmask & (1 << tok)) {
1723 			cvs_log(LP_ERR, "duplicate RCS key");
1724 			goto fail;
1725 		}
1726 		hmask |= (1 << tok);
1727 
1728 		switch (tok) {
1729 		case RCS_TOK_HEAD:
1730 		case RCS_TOK_BRANCH:
1731 		case RCS_TOK_COMMENT:
1732 		case RCS_TOK_EXPAND:
1733 			ntok = rcs_gettok(rfp);
1734 			if (ntok == RCS_TOK_SCOLON)
1735 				break;
1736 			if (ntok != rk->rk_val) {
1737 				cvs_log(LP_ERR,
1738 				    "invalid value type for RCS key `%s'",
1739 				    rk->rk_str);
1740 			}
1741 
1742 			if (tok == RCS_TOK_HEAD) {
1743 				if (rfp->rf_head == NULL)
1744 					rfp->rf_head = rcsnum_alloc();
1745 				if (RCS_TOKSTR(rfp)[0] == '\0' ||
1746 				    rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1747 				    rfp->rf_head) < 0) {
1748 					rcsnum_free(rfp->rf_head);
1749 					rfp->rf_head = NULL;
1750 				}
1751 			} else if (tok == RCS_TOK_BRANCH) {
1752 				if (rfp->rf_branch == NULL)
1753 					rfp->rf_branch = rcsnum_alloc();
1754 				if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1755 				    rfp->rf_branch) < 0)
1756 					goto fail;
1757 			} else if (tok == RCS_TOK_COMMENT) {
1758 				rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp));
1759 			} else if (tok == RCS_TOK_EXPAND) {
1760 				rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp));
1761 			}
1762 
1763 			/* now get the expected semi-colon */
1764 			ntok = rcs_gettok(rfp);
1765 			if (ntok != RCS_TOK_SCOLON) {
1766 				cvs_log(LP_ERR,
1767 				    "missing semi-colon after RCS `%s' key",
1768 				    rk->rk_str);
1769 				goto fail;
1770 			}
1771 			break;
1772 		case RCS_TOK_ACCESS:
1773 			if (rcs_parse_access(rfp) < 0)
1774 				goto fail;
1775 			break;
1776 		case RCS_TOK_SYMBOLS:
1777 			rcs_ignore_keys = 1;
1778 			if (rcs_parse_symbols(rfp) < 0)
1779 				goto fail;
1780 			break;
1781 		case RCS_TOK_LOCKS:
1782 			if (rcs_parse_locks(rfp) < 0)
1783 				goto fail;
1784 			break;
1785 		default:
1786 			cvs_log(LP_ERR,
1787 			    "unexpected token `%s' in RCS admin section",
1788 			    RCS_TOKSTR(rfp));
1789 			goto fail;
1790 		}
1791 
1792 		rcs_ignore_keys = 0;
1793 
1794 	}
1795 
1796 fail:
1797 	return (-1);
1798 }
1799 
1800 /*
1801  * rcs_parse_delta()
1802  *
1803  * Parse an RCS delta section and allocate the structure to store that delta's
1804  * information in the <rfp> delta list.
1805  * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1806  * -1 on error.
1807  */
1808 static int
1809 rcs_parse_delta(RCSFILE *rfp)
1810 {
1811 	int ret, tok, ntok, hmask;
1812 	u_int i;
1813 	char *tokstr;
1814 	RCSNUM *datenum;
1815 	struct rcs_delta *rdp;
1816 	struct rcs_key *rk;
1817 
1818 	tok = rcs_gettok(rfp);
1819 	if (tok == RCS_TOK_DESC) {
1820 		rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1821 		return (0);
1822 	} else if (tok != RCS_TOK_NUM) {
1823 		cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1824 		    RCS_TOKSTR(rfp));
1825 		return (-1);
1826 	}
1827 
1828 	rdp = xcalloc(1, sizeof(*rdp));
1829 
1830 	rdp->rd_num = rcsnum_alloc();
1831 	rdp->rd_next = rcsnum_alloc();
1832 
1833 	TAILQ_INIT(&(rdp->rd_branches));
1834 
1835 	rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1836 
1837 	hmask = 0;
1838 	ret = 0;
1839 	tokstr = NULL;
1840 
1841 	for (;;) {
1842 		tok = rcs_gettok(rfp);
1843 		if (tok == RCS_TOK_ERR) {
1844 			cvs_log(LP_ERR, "parse error in RCS delta section");
1845 			rcs_freedelta(rdp);
1846 			return (-1);
1847 		} else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1848 			rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1849 			ret = (tok == RCS_TOK_NUM ? 1 : 0);
1850 			break;
1851 		}
1852 
1853 		rk = NULL;
1854 		for (i = 0; i < RCS_NKEYS; i++)
1855 			if (rcs_keys[i].rk_id == tok)
1856 				rk = &(rcs_keys[i]);
1857 
1858 		if (hmask & (1 << tok)) {
1859 			cvs_log(LP_ERR, "duplicate RCS key");
1860 			rcs_freedelta(rdp);
1861 			return (-1);
1862 		}
1863 		hmask |= (1 << tok);
1864 
1865 		switch (tok) {
1866 		case RCS_TOK_DATE:
1867 		case RCS_TOK_AUTHOR:
1868 		case RCS_TOK_STATE:
1869 		case RCS_TOK_NEXT:
1870 			ntok = rcs_gettok(rfp);
1871 			if (ntok == RCS_TOK_SCOLON) {
1872 				if (rk->rk_flags & RCS_VOPT)
1873 					break;
1874 				else {
1875 					cvs_log(LP_ERR, "missing mandatory "
1876 					    "value to RCS key `%s'",
1877 					    rk->rk_str);
1878 					rcs_freedelta(rdp);
1879 					return (-1);
1880 				}
1881 			}
1882 
1883 			if (ntok != rk->rk_val) {
1884 				cvs_log(LP_ERR,
1885 				    "invalid value type for RCS key `%s'",
1886 				    rk->rk_str);
1887 				rcs_freedelta(rdp);
1888 				return (-1);
1889 			}
1890 
1891 			if (tokstr != NULL)
1892 				xfree(tokstr);
1893 			tokstr = xstrdup(RCS_TOKSTR(rfp));
1894 			/* now get the expected semi-colon */
1895 			ntok = rcs_gettok(rfp);
1896 			if (ntok != RCS_TOK_SCOLON) {
1897 				cvs_log(LP_ERR,
1898 				    "missing semi-colon after RCS `%s' key",
1899 				    rk->rk_str);
1900 				xfree(tokstr);
1901 				rcs_freedelta(rdp);
1902 				return (-1);
1903 			}
1904 
1905 			if (tok == RCS_TOK_DATE) {
1906 				if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1907 					xfree(tokstr);
1908 					rcs_freedelta(rdp);
1909 					return (-1);
1910 				}
1911 				if (datenum->rn_len != 6) {
1912 					cvs_log(LP_ERR,
1913 					    "RCS date specification has %s "
1914 					    "fields",
1915 					    (datenum->rn_len > 6) ? "too many" :
1916 					    "missing");
1917 					xfree(tokstr);
1918 					rcs_freedelta(rdp);
1919 					rcsnum_free(datenum);
1920 					return (-1);
1921 				}
1922 				rdp->rd_date.tm_year = datenum->rn_id[0];
1923 				if (rdp->rd_date.tm_year >= 1900)
1924 					rdp->rd_date.tm_year -= 1900;
1925 				rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1926 				rdp->rd_date.tm_mday = datenum->rn_id[2];
1927 				rdp->rd_date.tm_hour = datenum->rn_id[3];
1928 				rdp->rd_date.tm_min = datenum->rn_id[4];
1929 				rdp->rd_date.tm_sec = datenum->rn_id[5];
1930 				rcsnum_free(datenum);
1931 			} else if (tok == RCS_TOK_AUTHOR) {
1932 				rdp->rd_author = tokstr;
1933 				tokstr = NULL;
1934 			} else if (tok == RCS_TOK_STATE) {
1935 				rdp->rd_state = tokstr;
1936 				tokstr = NULL;
1937 			} else if (tok == RCS_TOK_NEXT) {
1938 				rcsnum_aton(tokstr, NULL, rdp->rd_next);
1939 			}
1940 			break;
1941 		case RCS_TOK_BRANCHES:
1942 			if (rcs_parse_branches(rfp, rdp) < 0) {
1943 				rcs_freedelta(rdp);
1944 				return (-1);
1945 			}
1946 			break;
1947 		default:
1948 			cvs_log(LP_ERR, "unexpected token `%s' in RCS delta",
1949 			    RCS_TOKSTR(rfp));
1950 			rcs_freedelta(rdp);
1951 			return (-1);
1952 		}
1953 	}
1954 
1955 	if (tokstr != NULL)
1956 		xfree(tokstr);
1957 
1958 	TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1959 	rfp->rf_ndelta++;
1960 
1961 	return (ret);
1962 }
1963 
1964 /*
1965  * rcs_parse_deltatext()
1966  *
1967  * Parse an RCS delta text section and fill in the log and text field of the
1968  * appropriate delta section.
1969  * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1970  * -1 on error.
1971  */
1972 static int
1973 rcs_parse_deltatext(RCSFILE *rfp)
1974 {
1975 	int tok;
1976 	RCSNUM *tnum;
1977 	struct rcs_delta *rdp;
1978 
1979 	tok = rcs_gettok(rfp);
1980 	if (tok == RCS_TOK_EOF)
1981 		return (0);
1982 
1983 	if (tok != RCS_TOK_NUM) {
1984 		cvs_log(LP_ERR,
1985 		    "unexpected token `%s' at start of RCS delta text",
1986 		    RCS_TOKSTR(rfp));
1987 		return (-1);
1988 	}
1989 
1990 	tnum = rcsnum_alloc();
1991 	rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1992 
1993 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1994 		if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1995 			break;
1996 	}
1997 	rcsnum_free(tnum);
1998 
1999 	if (rdp == NULL) {
2000 		cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
2001 		    RCS_TOKSTR(rfp));
2002 		return (-1);
2003 	}
2004 
2005 	tok = rcs_gettok(rfp);
2006 	if (tok != RCS_TOK_LOG) {
2007 		cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2008 		    RCS_TOKSTR(rfp));
2009 		return (-1);
2010 	}
2011 
2012 	tok = rcs_gettok(rfp);
2013 	if (tok != RCS_TOK_STRING) {
2014 		cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2015 		    RCS_TOKSTR(rfp));
2016 		return (-1);
2017 	}
2018 	rdp->rd_log = xstrdup(RCS_TOKSTR(rfp));
2019 	tok = rcs_gettok(rfp);
2020 	if (tok != RCS_TOK_TEXT) {
2021 		cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2022 		    RCS_TOKSTR(rfp));
2023 		return (-1);
2024 	}
2025 
2026 	tok = rcs_gettok(rfp);
2027 	if (tok != RCS_TOK_STRING) {
2028 		cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2029 		    RCS_TOKSTR(rfp));
2030 		return (-1);
2031 	}
2032 
2033 	if (RCS_TOKLEN(rfp) == 0) {
2034 		rdp->rd_text = xmalloc(1);
2035 		rdp->rd_text[0] = '\0';
2036 		rdp->rd_tlen = 0;
2037 	} else {
2038 		rdp->rd_text = xmalloc(RCS_TOKLEN(rfp));
2039 		memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp));
2040 		rdp->rd_tlen = RCS_TOKLEN(rfp);
2041 	}
2042 
2043 	return (1);
2044 }
2045 
2046 /*
2047  * rcs_parse_access()
2048  *
2049  * Parse the access list given as value to the `access' keyword.
2050  * Returns 0 on success, or -1 on failure.
2051  */
2052 static int
2053 rcs_parse_access(RCSFILE *rfp)
2054 {
2055 	int type;
2056 
2057 	while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
2058 		if (type != RCS_TOK_ID) {
2059 			cvs_log(LP_ERR, "unexpected token `%s' in access list",
2060 			    RCS_TOKSTR(rfp));
2061 			return (-1);
2062 		}
2063 
2064 		if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
2065 			return (-1);
2066 	}
2067 
2068 	return (0);
2069 }
2070 
2071 /*
2072  * rcs_parse_symbols()
2073  *
2074  * Parse the symbol list given as value to the `symbols' keyword.
2075  * Returns 0 on success, or -1 on failure.
2076  */
2077 static int
2078 rcs_parse_symbols(RCSFILE *rfp)
2079 {
2080 	int type;
2081 	struct rcs_sym *symp;
2082 
2083 	for (;;) {
2084 		type = rcs_gettok(rfp);
2085 		if (type == RCS_TOK_SCOLON)
2086 			break;
2087 
2088 		if (type != RCS_TOK_ID) {
2089 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2090 			    RCS_TOKSTR(rfp));
2091 			return (-1);
2092 		}
2093 
2094 		symp = xmalloc(sizeof(*symp));
2095 		symp->rs_name = xstrdup(RCS_TOKSTR(rfp));
2096 		symp->rs_num = rcsnum_alloc();
2097 
2098 		type = rcs_gettok(rfp);
2099 		if (type != RCS_TOK_COLON) {
2100 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2101 			    RCS_TOKSTR(rfp));
2102 			rcsnum_free(symp->rs_num);
2103 			xfree(symp->rs_name);
2104 			xfree(symp);
2105 			return (-1);
2106 		}
2107 
2108 		type = rcs_gettok(rfp);
2109 		if (type != RCS_TOK_NUM) {
2110 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2111 			    RCS_TOKSTR(rfp));
2112 			rcsnum_free(symp->rs_num);
2113 			xfree(symp->rs_name);
2114 			xfree(symp);
2115 			return (-1);
2116 		}
2117 
2118 		if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
2119 			cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2120 			    RCS_TOKSTR(rfp));
2121 			rcsnum_free(symp->rs_num);
2122 			xfree(symp->rs_name);
2123 			xfree(symp);
2124 			return (-1);
2125 		}
2126 
2127 		TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
2128 	}
2129 
2130 	return (0);
2131 }
2132 
2133 /*
2134  * rcs_parse_locks()
2135  *
2136  * Parse the lock list given as value to the `locks' keyword.
2137  * Returns 0 on success, or -1 on failure.
2138  */
2139 static int
2140 rcs_parse_locks(RCSFILE *rfp)
2141 {
2142 	int type;
2143 	struct rcs_lock *lkp;
2144 
2145 	for (;;) {
2146 		type = rcs_gettok(rfp);
2147 		if (type == RCS_TOK_SCOLON)
2148 			break;
2149 
2150 		if (type != RCS_TOK_ID) {
2151 			cvs_log(LP_ERR, "unexpected token `%s' in lock list",
2152 			    RCS_TOKSTR(rfp));
2153 			return (-1);
2154 		}
2155 
2156 		lkp = xmalloc(sizeof(*lkp));
2157 		lkp->rl_name = xstrdup(RCS_TOKSTR(rfp));
2158 		lkp->rl_num = rcsnum_alloc();
2159 
2160 		type = rcs_gettok(rfp);
2161 		if (type != RCS_TOK_COLON) {
2162 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2163 			    RCS_TOKSTR(rfp));
2164 			rcsnum_free(lkp->rl_num);
2165 			xfree(lkp->rl_name);
2166 			xfree(lkp);
2167 			return (-1);
2168 		}
2169 
2170 		type = rcs_gettok(rfp);
2171 		if (type != RCS_TOK_NUM) {
2172 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2173 			    RCS_TOKSTR(rfp));
2174 			rcsnum_free(lkp->rl_num);
2175 			xfree(lkp->rl_name);
2176 			xfree(lkp);
2177 			return (-1);
2178 		}
2179 
2180 		if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
2181 			cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2182 			    RCS_TOKSTR(rfp));
2183 			rcsnum_free(lkp->rl_num);
2184 			xfree(lkp->rl_name);
2185 			xfree(lkp);
2186 			return (-1);
2187 		}
2188 
2189 		TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
2190 	}
2191 
2192 	/* check if we have a `strict' */
2193 	type = rcs_gettok(rfp);
2194 	if (type != RCS_TOK_STRICT) {
2195 		rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
2196 	} else {
2197 		rfp->rf_flags |= RCS_SLOCK;
2198 
2199 		type = rcs_gettok(rfp);
2200 		if (type != RCS_TOK_SCOLON) {
2201 			cvs_log(LP_ERR,
2202 			    "missing semi-colon after `strict' keyword");
2203 			return (-1);
2204 		}
2205 	}
2206 
2207 	return (0);
2208 }
2209 
2210 /*
2211  * rcs_parse_branches()
2212  *
2213  * Parse the list of branches following a `branches' keyword in a delta.
2214  * Returns 0 on success, or -1 on failure.
2215  */
2216 static int
2217 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
2218 {
2219 	int type;
2220 	struct rcs_branch *brp;
2221 
2222 	for (;;) {
2223 		type = rcs_gettok(rfp);
2224 		if (type == RCS_TOK_SCOLON)
2225 			break;
2226 
2227 		if (type != RCS_TOK_NUM) {
2228 			cvs_log(LP_ERR,
2229 			    "unexpected token `%s' in list of branches",
2230 			    RCS_TOKSTR(rfp));
2231 			return (-1);
2232 		}
2233 
2234 		brp = xmalloc(sizeof(*brp));
2235 		brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
2236 		if (brp->rb_num == NULL) {
2237 			xfree(brp);
2238 			return (-1);
2239 		}
2240 
2241 		TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
2242 	}
2243 
2244 	return (0);
2245 }
2246 
2247 /*
2248  * rcs_freedelta()
2249  *
2250  * Free the contents of a delta structure.
2251  */
2252 static void
2253 rcs_freedelta(struct rcs_delta *rdp)
2254 {
2255 	struct rcs_branch *rb;
2256 
2257 	if (rdp->rd_num != NULL)
2258 		rcsnum_free(rdp->rd_num);
2259 	if (rdp->rd_next != NULL)
2260 		rcsnum_free(rdp->rd_next);
2261 
2262 	if (rdp->rd_author != NULL)
2263 		xfree(rdp->rd_author);
2264 	if (rdp->rd_locker != NULL)
2265 		xfree(rdp->rd_locker);
2266 	if (rdp->rd_state != NULL)
2267 		xfree(rdp->rd_state);
2268 	if (rdp->rd_log != NULL)
2269 		xfree(rdp->rd_log);
2270 	if (rdp->rd_text != NULL)
2271 		xfree(rdp->rd_text);
2272 
2273 	while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
2274 		TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
2275 		rcsnum_free(rb->rb_num);
2276 		xfree(rb);
2277 	}
2278 
2279 	xfree(rdp);
2280 }
2281 
2282 /*
2283  * rcs_freepdata()
2284  *
2285  * Free the contents of the parser data structure.
2286  */
2287 static void
2288 rcs_freepdata(struct rcs_pdata *pd)
2289 {
2290 	if (pd->rp_file != NULL)
2291 		(void)fclose(pd->rp_file);
2292 	if (pd->rp_buf != NULL)
2293 		xfree(pd->rp_buf);
2294 	xfree(pd);
2295 }
2296 
2297 /*
2298  * rcs_gettok()
2299  *
2300  * Get the next RCS token from the string <str>.
2301  */
2302 static int
2303 rcs_gettok(RCSFILE *rfp)
2304 {
2305 	u_int i;
2306 	int ch, last, type;
2307 	size_t len;
2308 	char *bp;
2309 	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2310 
2311 	type = RCS_TOK_ERR;
2312 	bp = pdp->rp_buf;
2313 	pdp->rp_tlen = 0;
2314 	*bp = '\0';
2315 
2316 	if (pdp->rp_pttype != RCS_TOK_ERR) {
2317 		type = pdp->rp_pttype;
2318 		if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >=
2319 		    pdp->rp_blen)
2320 			fatal("rcs_gettok: strlcpy");
2321 		pdp->rp_pttype = RCS_TOK_ERR;
2322 		return (type);
2323 	}
2324 
2325 	/* skip leading whitespace */
2326 	/* XXX we must skip backspace too for compatibility, should we? */
2327 	do {
2328 		ch = getc(pdp->rp_file);
2329 		if (ch == '\n')
2330 			pdp->rp_lines++;
2331 	} while (isspace(ch));
2332 
2333 	if (ch == EOF) {
2334 		type = RCS_TOK_EOF;
2335 	} else if (ch == ';') {
2336 		type = RCS_TOK_SCOLON;
2337 	} else if (ch == ':') {
2338 		type = RCS_TOK_COLON;
2339 	} else if (isalpha(ch)) {
2340 		type = RCS_TOK_ID;
2341 		*(bp++) = ch;
2342 		for (;;) {
2343 			ch = getc(pdp->rp_file);
2344 			if (ch == EOF) {
2345 				type = RCS_TOK_EOF;
2346 				break;
2347 			} else if (!isalnum(ch) && ch != '_' && ch != '-' &&
2348 			    ch != '/' && ch != '+' && ch != '|') {
2349 				ungetc(ch, pdp->rp_file);
2350 				break;
2351 			}
2352 			*(bp++) = ch;
2353 			pdp->rp_tlen++;
2354 			if (bp == pdp->rp_bufend - 1) {
2355 				len = bp - pdp->rp_buf;
2356 				rcs_growbuf(rfp);
2357 				bp = pdp->rp_buf + len;
2358 			}
2359 		}
2360 		*bp = '\0';
2361 
2362 		if (type != RCS_TOK_ERR && rcs_ignore_keys != 1) {
2363 			for (i = 0; i < RCS_NKEYS; i++) {
2364 				if (strcmp(rcs_keys[i].rk_str,
2365 				    pdp->rp_buf) == 0) {
2366 					type = rcs_keys[i].rk_id;
2367 					break;
2368 				}
2369 			}
2370 		}
2371 	} else if (ch == '@') {
2372 		/* we have a string */
2373 		type = RCS_TOK_STRING;
2374 		for (;;) {
2375 			ch = getc(pdp->rp_file);
2376 			if (ch == EOF) {
2377 				type = RCS_TOK_EOF;
2378 				break;
2379 			} else if (ch == '@') {
2380 				ch = getc(pdp->rp_file);
2381 				if (ch != '@') {
2382 					ungetc(ch, pdp->rp_file);
2383 					break;
2384 				}
2385 			} else if (ch == '\n')
2386 				pdp->rp_lines++;
2387 
2388 			*(bp++) = ch;
2389 			pdp->rp_tlen++;
2390 			if (bp == pdp->rp_bufend - 1) {
2391 				len = bp - pdp->rp_buf;
2392 				rcs_growbuf(rfp);
2393 				bp = pdp->rp_buf + len;
2394 			}
2395 		}
2396 
2397 		*bp = '\0';
2398 	} else if (isdigit(ch)) {
2399 		*(bp++) = ch;
2400 		last = ch;
2401 		type = RCS_TOK_NUM;
2402 
2403 		for (;;) {
2404 			ch = getc(pdp->rp_file);
2405 			if (ch == EOF) {
2406 				type = RCS_TOK_EOF;
2407 				break;
2408 			}
2409 			if (bp == pdp->rp_bufend)
2410 				break;
2411 			if (!isdigit(ch) && ch != '.') {
2412 				ungetc(ch, pdp->rp_file);
2413 				break;
2414 			}
2415 
2416 			if (last == '.' && ch == '.') {
2417 				type = RCS_TOK_ERR;
2418 				break;
2419 			}
2420 			last = ch;
2421 			*(bp++) = ch;
2422 			pdp->rp_tlen++;
2423 		}
2424 		*bp = '\0';
2425 	}
2426 
2427 	return (type);
2428 }
2429 
2430 /*
2431  * rcs_pushtok()
2432  *
2433  * Push a token back in the parser's token buffer.
2434  */
2435 static int
2436 rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
2437 {
2438 	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2439 
2440 	if (pdp->rp_pttype != RCS_TOK_ERR)
2441 		return (-1);
2442 
2443 	pdp->rp_pttype = type;
2444 	if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >=
2445 	    sizeof(pdp->rp_ptok))
2446 		fatal("rcs_pushtok: strlcpy");
2447 	return (0);
2448 }
2449 
2450 
2451 /*
2452  * rcs_growbuf()
2453  *
2454  * Attempt to grow the internal parse buffer for the RCS file <rf> by
2455  * RCS_BUFEXTSIZE.
2456  * In case of failure, the original buffer is left unmodified.
2457  */
2458 static void
2459 rcs_growbuf(RCSFILE *rf)
2460 {
2461 	struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2462 
2463 	pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
2464 	    pdp->rp_blen + RCS_BUFEXTSIZE);
2465 	pdp->rp_blen += RCS_BUFEXTSIZE;
2466 	pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
2467 }
2468 
2469 /*
2470  * rcs_strprint()
2471  *
2472  * Output an RCS string <str> of size <slen> to the stream <stream>.  Any
2473  * '@' characters are escaped.  Otherwise, the string can contain arbitrary
2474  * binary data.
2475  */
2476 static void
2477 rcs_strprint(const u_char *str, size_t slen, FILE *stream)
2478 {
2479 	const u_char *ap, *ep, *sp;
2480 
2481 	if (slen == 0)
2482 		return;
2483 
2484 	ep = str + slen - 1;
2485 
2486 	for (sp = str; sp <= ep;)  {
2487 		ap = memchr(sp, '@', ep - sp);
2488 		if (ap == NULL)
2489 			ap = ep;
2490 		(void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
2491 
2492 		if (*ap == '@')
2493 			putc('@', stream);
2494 		sp = ap + 1;
2495 	}
2496 }
2497 
2498 /*
2499  * rcs_deltatext_set()
2500  *
2501  * Set deltatext for <rev> in RCS file <rfp> to <dtext>
2502  * Returns -1 on error, 0 on success.
2503  */
2504 int
2505 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
2506 {
2507 	size_t len;
2508 	u_char *dtext;
2509 	struct rcs_delta *rdp;
2510 
2511 	/* Write operations require full parsing */
2512 	rcs_parse_deltatexts(rfp, NULL);
2513 
2514 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2515 		return (-1);
2516 
2517 	if (rdp->rd_text != NULL)
2518 		xfree(rdp->rd_text);
2519 
2520 	len = cvs_buf_len(bp);
2521 	dtext = cvs_buf_release(bp);
2522 	bp = NULL;
2523 
2524 	if (len != 0) {
2525 		rdp->rd_text = xmalloc(len);
2526 		rdp->rd_tlen = len;
2527 		(void)memcpy(rdp->rd_text, dtext, len);
2528 	} else {
2529 		rdp->rd_text = NULL;
2530 		rdp->rd_tlen = 0;
2531 	}
2532 
2533 	if (dtext != NULL)
2534 		xfree(dtext);
2535 
2536 	return (0);
2537 }
2538 
2539 /*
2540  * rcs_rev_setlog()
2541  *
2542  * Sets the log message of revision <rev> to <logtext>
2543  */
2544 int
2545 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
2546 {
2547 	struct rcs_delta *rdp;
2548 	char buf[CVS_REV_BUFSZ];
2549 
2550 	rcsnum_tostr(rev, buf, sizeof(buf));
2551 
2552 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2553 		return (-1);
2554 
2555 	if (rdp->rd_log != NULL)
2556 		xfree(rdp->rd_log);
2557 
2558 	rdp->rd_log = xstrdup(logtext);
2559 	rfp->rf_flags &= ~RCS_SYNCED;
2560 	return (0);
2561 }
2562 /*
2563  * rcs_rev_getdate()
2564  *
2565  * Get the date corresponding to a given revision.
2566  * Returns the date on success, -1 on failure.
2567  */
2568 time_t
2569 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
2570 {
2571 	struct rcs_delta *rdp;
2572 
2573 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2574 		return (-1);
2575 
2576 	return (timegm(&rdp->rd_date));
2577 }
2578 
2579 /*
2580  * rcs_state_set()
2581  *
2582  * Sets the state of revision <rev> to <state>
2583  * NOTE: default state is 'Exp'. States may not contain spaces.
2584  *
2585  * Returns -1 on failure, 0 on success.
2586  */
2587 int
2588 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
2589 {
2590 	struct rcs_delta *rdp;
2591 
2592 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2593 		return (-1);
2594 
2595 	if (rdp->rd_state != NULL)
2596 		xfree(rdp->rd_state);
2597 
2598 	rdp->rd_state = xstrdup(state);
2599 
2600 	rfp->rf_flags &= ~RCS_SYNCED;
2601 
2602 	return (0);
2603 }
2604 
2605 /*
2606  * rcs_state_check()
2607  *
2608  * Check if string <state> is valid.
2609  *
2610  * Returns 0 if the string is valid, -1 otherwise.
2611  */
2612 int
2613 rcs_state_check(const char *state)
2614 {
2615 	if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP))
2616 		return (-1);
2617 
2618 	return (0);
2619 }
2620 
2621 /*
2622  * rcs_state_get()
2623  *
2624  * Get the state for a given revision of a specified RCSFILE.
2625  *
2626  * Returns NULL on failure.
2627  */
2628 const char *
2629 rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
2630 {
2631 	struct rcs_delta *rdp;
2632 
2633 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2634 		return (NULL);
2635 
2636 	return (rdp->rd_state);
2637 }
2638 
2639 /* rcs_get_revision() */
2640 static RCSNUM *
2641 rcs_get_revision(const char *revstr, RCSFILE *rfp)
2642 {
2643 	RCSNUM *rev, *brev, *frev;
2644 	struct rcs_branch *brp;
2645 	struct rcs_delta *rdp;
2646 	size_t i;
2647 
2648 	rdp = NULL;
2649 
2650 	if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
2651 		if (rfp->rf_head == NULL)
2652 			return (NULL);
2653 
2654 		frev = rcsnum_alloc();
2655 		rcsnum_cpy(rfp->rf_head, frev, 0);
2656 		return (frev);
2657 	}
2658 
2659 	/* Possibly we could be passed a version number */
2660 	if ((rev = rcsnum_parse(revstr)) != NULL) {
2661 		/* Do not return if it is not in RCS file */
2662 		if ((rdp = rcs_findrev(rfp, rev)) != NULL)
2663 			return (rev);
2664 	} else {
2665 		/* More likely we will be passed a symbol */
2666 		rev = rcs_sym_getrev(rfp, revstr);
2667 	}
2668 
2669 	if (rev == NULL)
2670 		return (NULL);
2671 
2672 	/*
2673 	 * If it was not a branch, thats ok the symbolic
2674 	 * name refered to a revision, so return the resolved
2675 	 * revision for the given name. */
2676 	if (!RCSNUM_ISBRANCH(rev)) {
2677 		/* Sanity check: The first two elements of any
2678 		 * revision (be it on a branch or on trunk) cannot
2679 		 * be greater than HEAD.
2680 		 *
2681 		 * XXX: To avoid comparing to uninitialized memory,
2682 		 * the minimum of both revision lengths is taken
2683 		 * instead of just 2.
2684 		 */
2685 		if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head,
2686 		    MIN(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
2687 			rcsnum_free(rev);
2688 			return (NULL);
2689 		}
2690 		return (rev);
2691 	}
2692 
2693 	brev = rcsnum_alloc();
2694 	rcsnum_cpy(rev, brev, rev->rn_len - 1);
2695 
2696 	if ((rdp = rcs_findrev(rfp, brev)) == NULL)
2697 		fatal("rcs_get_revision: tag `%s' does not exist", revstr);
2698 	rcsnum_free(brev);
2699 
2700 	TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2701 		for (i = 0; i < rev->rn_len; i++)
2702 			if (brp->rb_num->rn_id[i] != rev->rn_id[i])
2703 				break;
2704 		if (i != rev->rn_len)
2705 			continue;
2706 		break;
2707 	}
2708 
2709 	rcsnum_free(rev);
2710 	frev = rcsnum_alloc();
2711 	if (brp == NULL) {
2712 		rcsnum_cpy(rdp->rd_num, frev, 0);
2713 		return (frev);
2714 	} else {
2715 		/* Fetch the delta with the correct branch num */
2716 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2717 			fatal("rcs_get_revision: could not fetch branch "
2718 			    "delta");
2719 		rcsnum_cpy(rdp->rd_num, frev, 0);
2720 		return (frev);
2721 	}
2722 }
2723 
2724 /*
2725  * rcs_rev_getlines()
2726  *
2727  * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
2728  * return it as a pointer to a struct cvs_lines.
2729  */
2730 struct cvs_lines *
2731 rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2732 {
2733 	size_t plen;
2734 	int annotate, done, i, nextroot;
2735 	RCSNUM *tnum, *bnum;
2736 	struct rcs_branch *brp;
2737 	struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
2738 	u_char *patch;
2739 	struct cvs_line *line, *nline;
2740 	struct cvs_lines *dlines, *plines;
2741 
2742 	if (rfp->rf_head == NULL ||
2743 	    (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
2744 		fatal("rcs_rev_getlines: no HEAD revision");
2745 
2746 	tnum = frev;
2747 	rcs_parse_deltatexts(rfp, hrdp->rd_num);
2748 
2749 	/* revision on branch, get the branch root */
2750 	nextroot = 2;
2751 	bnum = rcsnum_alloc();
2752 	if (RCSNUM_ISBRANCHREV(tnum))
2753 		rcsnum_cpy(tnum, bnum, nextroot);
2754 	else
2755 		rcsnum_cpy(tnum, bnum, tnum->rn_len);
2756 
2757 	if (alines != NULL) {
2758 		/* start with annotate first at requested revision */
2759 		annotate = ANNOTATE_LATER;
2760 		*alines = NULL;
2761 	} else
2762 		annotate = ANNOTATE_NEVER;
2763 
2764 	dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
2765 
2766 	done = 0;
2767 
2768 	rdp = hrdp;
2769 	if (!rcsnum_differ(rdp->rd_num, bnum)) {
2770 		if (annotate == ANNOTATE_LATER) {
2771 			/* found requested revision for annotate */
2772 			i = 0;
2773 			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2774 				line->l_lineno_orig = line->l_lineno;
2775 				i++;
2776 			}
2777 
2778 			*alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2779 			(*alines)[i] = NULL;
2780 			annotate = ANNOTATE_NOW;
2781 
2782 			/* annotate down to 1.1 from where we are */
2783 			rcsnum_free(bnum);
2784 			bnum = rcsnum_parse("1.1");
2785 			if (!rcsnum_differ(rdp->rd_num, bnum)) {
2786 				goto next;
2787 			}
2788 		} else
2789 			goto next;
2790 	}
2791 
2792 	prdp = hrdp;
2793 	if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
2794 		goto done;
2795 
2796 again:
2797 	for (;;) {
2798 		if (rdp->rd_next->rn_len != 0) {
2799 			trdp = rcs_findrev(rfp, rdp->rd_next);
2800 			if (trdp == NULL)
2801 				fatal("failed to grab next revision");
2802 		}
2803 
2804 		if (rdp->rd_tlen == 0) {
2805 			rcs_parse_deltatexts(rfp, rdp->rd_num);
2806 			if (rdp->rd_tlen == 0) {
2807 				if (!rcsnum_differ(rdp->rd_num, bnum))
2808 					break;
2809 				rdp = trdp;
2810 				continue;
2811 			}
2812 		}
2813 
2814 		plen = rdp->rd_tlen;
2815 		patch = rdp->rd_text;
2816 		plines = cvs_splitlines(patch, plen);
2817 		if (annotate == ANNOTATE_NOW)
2818 			rcs_patch_lines(dlines, plines, *alines, prdp);
2819 		else
2820 			rcs_patch_lines(dlines, plines, NULL, NULL);
2821 		cvs_freelines(plines);
2822 
2823 		if (!rcsnum_differ(rdp->rd_num, bnum)) {
2824 			if (annotate != ANNOTATE_LATER)
2825 				break;
2826 
2827 			/* found requested revision for annotate */
2828 			i = 0;
2829 			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2830 				line->l_lineno_orig = line->l_lineno;
2831 				i++;
2832 			}
2833 
2834 			*alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2835 			(*alines)[i] = NULL;
2836 			annotate = ANNOTATE_NOW;
2837 
2838 			/* annotate down to 1.1 from where we are */
2839 			rcsnum_free(bnum);
2840 			bnum = rcsnum_parse("1.1");
2841 
2842 			if (!rcsnum_differ(rdp->rd_num, bnum))
2843 				break;
2844 		}
2845 
2846 		prdp = rdp;
2847 		rdp = trdp;
2848 	}
2849 
2850 next:
2851 	if (!rcsnum_differ(rdp->rd_num, frev))
2852 		done = 1;
2853 
2854 	if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
2855 		nextroot += 2;
2856 		rcsnum_cpy(frev, bnum, nextroot);
2857 
2858 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2859 			for (i = 0; i < nextroot - 1; i++)
2860 				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2861 					break;
2862 			if (i == nextroot - 1)
2863 				break;
2864 		}
2865 
2866 		if (brp == NULL) {
2867 			if (annotate != ANNOTATE_NEVER) {
2868 				if (*alines != NULL)
2869 					xfree(*alines);
2870 				*alines = NULL;
2871 				cvs_freelines(dlines);
2872 				rcsnum_free(bnum);
2873 				return (NULL);
2874 			}
2875 			fatal("expected branch not found on branch list");
2876 		}
2877 
2878 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2879 			fatal("rcs_rev_getlines: failed to get delta for target rev");
2880 
2881 		goto again;
2882 	}
2883 done:
2884 	/* put remaining lines into annotate buffer */
2885 	if (annotate == ANNOTATE_NOW) {
2886 		for (line = TAILQ_FIRST(&(dlines->l_lines));
2887 		    line != NULL; line = nline) {
2888 			nline = TAILQ_NEXT(line, l_list);
2889 			TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
2890 			if (line->l_line == NULL) {
2891 				xfree(line);
2892 				continue;
2893 			}
2894 
2895 			line->l_delta = rdp;
2896 			(*alines)[line->l_lineno_orig - 1] = line;
2897 		}
2898 
2899 		cvs_freelines(dlines);
2900 		dlines = NULL;
2901 	}
2902 
2903 	if (bnum != tnum)
2904 		rcsnum_free(bnum);
2905 
2906 	return (dlines);
2907 }
2908 
2909 void
2910 rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2911 {
2912 	size_t plen;
2913 	int i, nextroot;
2914 	RCSNUM *bnum;
2915 	struct rcs_branch *brp;
2916 	struct rcs_delta *rdp, *trdp;
2917 	u_char *patch;
2918 	struct cvs_line *line;
2919 	struct cvs_lines *dlines, *plines;
2920 
2921 	if (!RCSNUM_ISBRANCHREV(frev))
2922 		fatal("rcs_annotate_getlines: branch revision expected");
2923 
2924 	/* revision on branch, get the branch root */
2925 	nextroot = 2;
2926 	bnum = rcsnum_alloc();
2927 	rcsnum_cpy(frev, bnum, nextroot);
2928 
2929 	/*
2930 	 * Going from HEAD to 1.1 enables the use of an array, which is
2931 	 * much faster. Unfortunately this is not possible with branch
2932 	 * revisions, so copy over our alines (array) into dlines (tailq).
2933 	 */
2934 	dlines = xcalloc(1, sizeof(*dlines));
2935 	TAILQ_INIT(&(dlines->l_lines));
2936 	line = xcalloc(1, sizeof(*line));
2937 	TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2938 
2939 	for (i = 0; (*alines)[i] != NULL; i++) {
2940 		line = (*alines)[i];
2941 		line->l_lineno = i + 1;
2942 		TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2943 	}
2944 
2945 	rdp = rcs_findrev(rfp, bnum);
2946 	if (rdp == NULL)
2947 		fatal("failed to grab branch root revision");
2948 
2949 	do {
2950 		nextroot += 2;
2951 		rcsnum_cpy(frev, bnum, nextroot);
2952 
2953 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2954 			for (i = 0; i < nextroot - 1; i++)
2955 				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2956 					break;
2957 			if (i == nextroot - 1)
2958 				break;
2959 		}
2960 
2961 		if (brp == NULL)
2962 			fatal("expected branch not found on branch list");
2963 
2964 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2965 			fatal("failed to get delta for target rev");
2966 
2967 		for (;;) {
2968 			if (rdp->rd_next->rn_len != 0) {
2969 				trdp = rcs_findrev(rfp, rdp->rd_next);
2970 				if (trdp == NULL)
2971 					fatal("failed to grab next revision");
2972 			}
2973 
2974 			if (rdp->rd_tlen == 0) {
2975 				rcs_parse_deltatexts(rfp, rdp->rd_num);
2976 				if (rdp->rd_tlen == 0) {
2977 					if (!rcsnum_differ(rdp->rd_num, bnum))
2978 						break;
2979 					rdp = trdp;
2980 					continue;
2981 				}
2982 			}
2983 
2984 			plen = rdp->rd_tlen;
2985 			patch = rdp->rd_text;
2986 			plines = cvs_splitlines(patch, plen);
2987 			rcs_patch_lines(dlines, plines, NULL, rdp);
2988 			cvs_freelines(plines);
2989 
2990 			if (!rcsnum_differ(rdp->rd_num, bnum))
2991 				break;
2992 
2993 			rdp = trdp;
2994 		}
2995 	} while (rcsnum_differ(rdp->rd_num, frev));
2996 
2997 	if (bnum != frev)
2998 		rcsnum_free(bnum);
2999 
3000 	/*
3001 	 * All lines have been parsed, now they must be copied over
3002 	 * into alines (array) again.
3003 	 */
3004 	xfree(*alines);
3005 
3006 	i = 0;
3007 	TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3008 		if (line->l_line != NULL)
3009 			i++;
3010 	}
3011 	*alines = xcalloc(i + 1, sizeof(struct cvs_line *));
3012 	(*alines)[i] = NULL;
3013 
3014 	i = 0;
3015 	TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3016 		if (line->l_line != NULL)
3017 			(*alines)[i++] = line;
3018 	}
3019 }
3020 
3021 /*
3022  * rcs_rev_getbuf()
3023  *
3024  * XXX: This is really really slow and should be avoided if at all possible!
3025  *
3026  * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
3027  * return it as a BUF pointer.
3028  */
3029 BUF *
3030 rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
3031 {
3032 	int expmode, expand;
3033 	struct rcs_delta *rdp;
3034 	struct cvs_lines *lines;
3035 	struct cvs_line *lp, *nlp;
3036 	BUF *bp;
3037 
3038 	expand = 0;
3039 	lines = rcs_rev_getlines(rfp, rev, NULL);
3040 	bp = cvs_buf_alloc(1024 * 16);
3041 
3042 	if (!(mode & RCS_KWEXP_NONE)) {
3043 		if (rfp->rf_expand != NULL)
3044 			expmode = rcs_kwexp_get(rfp);
3045 		else
3046 			expmode = RCS_KWEXP_DEFAULT;
3047 
3048 		if (!(expmode & RCS_KWEXP_NONE)) {
3049 			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3050 				fatal("could not fetch revision");
3051 			expand = 1;
3052 		}
3053 	}
3054 
3055 	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
3056 		nlp = TAILQ_NEXT(lp, l_list);
3057 
3058 		if (lp->l_line == NULL) {
3059 			lp = nlp;
3060 			continue;
3061 		}
3062 
3063 		if (expand)
3064 			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
3065 
3066 		do {
3067 			cvs_buf_append(bp, lp->l_line, lp->l_len);
3068 		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
3069 	}
3070 
3071 	cvs_freelines(lines);
3072 
3073 	return (bp);
3074 }
3075 
3076 /*
3077  * rcs_rev_write_fd()
3078  *
3079  * Write the entire contents of revision <frev> from the rcsfile <rfp> to
3080  * file descriptor <fd>.
3081  */
3082 void
3083 rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
3084 {
3085 	int fd;
3086 	FILE *fp;
3087 	size_t ret;
3088 	int expmode, expand;
3089 	struct rcs_delta *rdp;
3090 	struct cvs_lines *lines;
3091 	struct cvs_line *lp, *nlp;
3092 	extern int print_stdout;
3093 
3094 	expand = 0;
3095 	lines = rcs_rev_getlines(rfp, rev, NULL);
3096 
3097 	if (!(mode & RCS_KWEXP_NONE)) {
3098 		if (rfp->rf_expand != NULL)
3099 			expmode = rcs_kwexp_get(rfp);
3100 		else
3101 			expmode = RCS_KWEXP_DEFAULT;
3102 
3103 		if (!(expmode & RCS_KWEXP_NONE)) {
3104 			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3105 				fatal("could not fetch revision");
3106 			expand = 1;
3107 		}
3108 	}
3109 
3110 	fd = dup(_fd);
3111 	if (fd == -1)
3112 		fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
3113 
3114 	if ((fp = fdopen(fd, "w")) == NULL)
3115 		fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
3116 
3117 	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
3118 		nlp = TAILQ_NEXT(lp, l_list);
3119 
3120 		if (lp->l_line == NULL) {
3121 			lp = nlp;
3122 			continue;
3123 		}
3124 
3125 		if (expand)
3126 			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
3127 
3128 		do {
3129 			/*
3130 			 * Solely for the checkout and update -p options.
3131 			 */
3132 			if (cvs_server_active == 1 &&
3133 			    (cvs_cmdop == CVS_OP_CHECKOUT ||
3134 			    cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
3135 				ret = fwrite("M ", 1, 2, fp);
3136 				if (ret != 2)
3137 					fatal("rcs_rev_write_fd: %s",
3138 					    strerror(errno));
3139 			}
3140 
3141 			ret = fwrite(lp->l_line, 1, lp->l_len, fp);
3142 			if (ret != lp->l_len)
3143 				fatal("rcs_rev_write_fd: %s", strerror(errno));
3144 		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
3145 	}
3146 
3147 	cvs_freelines(lines);
3148 	(void)fclose(fp);
3149 }
3150 
3151 /*
3152  * rcs_rev_write_stmp()
3153  *
3154  * Write the contents of the rev <rev> to a temporary file whose path is
3155  * specified using <template> (see mkstemp(3)). NB. This function will modify
3156  * <template>, as per mkstemp.
3157  */
3158 int
3159 rcs_rev_write_stmp(RCSFILE *rfp,  RCSNUM *rev, char *template, int mode)
3160 {
3161 	int fd;
3162 
3163 	if ((fd = mkstemp(template)) == -1)
3164 		fatal("mkstemp: `%s': %s", template, strerror(errno));
3165 
3166 	cvs_worklist_add(template, &temp_files);
3167 	rcs_rev_write_fd(rfp, rev, fd, mode);
3168 
3169 	if (lseek(fd, 0, SEEK_SET) < 0)
3170 		fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
3171 
3172 	return (fd);
3173 }
3174 
3175 static void
3176 rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines,
3177     struct cvs_line *line, int mode)
3178 {
3179 	BUF *tmpbuf;
3180 	int kwtype;
3181 	u_int j, found;
3182 	const u_char *c, *start, *fin, *end;
3183 	char *kwstr;
3184 	char expbuf[256], buf[256];
3185 	char *fmt;
3186 	size_t clen, kwlen, len, tlen;
3187 
3188 	kwtype = 0;
3189 	kwstr = NULL;
3190 
3191 	if (mode & RCS_KWEXP_OLD)
3192 		return;
3193 
3194 	len = line->l_len;
3195 	if (len == 0)
3196 		return;
3197 
3198 	c = line->l_line;
3199 	found = 0;
3200 	/* Final character in buffer. */
3201 	fin = c + len - 1;
3202 
3203 	/*
3204 	 * Keyword formats:
3205 	 * $Keyword$
3206 	 * $Keyword: value$
3207 	 */
3208 	for (; c < fin; c++) {
3209 		if (*c != '$')
3210 			continue;
3211 
3212 		/* remember start of this possible keyword */
3213 		start = c;
3214 
3215 		/* first following character has to be alphanumeric */
3216 		c++;
3217 		if (!isalpha(*c)) {
3218 			c = start;
3219 			continue;
3220 		}
3221 
3222 		/* Number of characters between c and fin, inclusive. */
3223 		clen = fin - c + 1;
3224 
3225 		/* look for any matching keywords */
3226 		found = 0;
3227 		for (j = 0; j < RCS_NKWORDS; j++) {
3228 			kwlen = strlen(rcs_expkw[j].kw_str);
3229 			/*
3230 			 * kwlen must be less than clen since clen
3231 			 * includes either a terminating `$' or a `:'.
3232 			 */
3233 			if (kwlen < clen &&
3234 			    memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
3235 			    (c[kwlen] == '$' || c[kwlen] == ':')) {
3236 				found = 1;
3237 				kwstr = rcs_expkw[j].kw_str;
3238 				kwtype = rcs_expkw[j].kw_type;
3239 				c += kwlen;
3240 				break;
3241 			}
3242 		}
3243 
3244 		if (found == 0 && cvs_tagname != NULL) {
3245 			kwlen = strlen(cvs_tagname);
3246 			if (kwlen < clen &&
3247 			    memcmp(c, cvs_tagname, kwlen) == 0 &&
3248 			    (c[kwlen] == '$' || c[kwlen] == ':')) {
3249 				found = 1;
3250 				kwstr = cvs_tagname;
3251 				kwtype = RCS_KW_ID;
3252 				c += kwlen;
3253 			}
3254 		}
3255 
3256 		/* unknown keyword, continue looking */
3257 		if (found == 0) {
3258 			c = start;
3259 			continue;
3260 		}
3261 
3262 		/*
3263 		 * if the next character was ':' we need to look for
3264 		 * an '$' before the end of the line to be sure it is
3265 		 * in fact a keyword.
3266 		 */
3267 		if (*c == ':') {
3268 			for (; c <= fin; ++c) {
3269 				if (*c == '$' || *c == '\n')
3270 					break;
3271 			}
3272 
3273 			if (*c != '$') {
3274 				c = start;
3275 				continue;
3276 			}
3277 		}
3278 		end = c + 1;
3279 
3280 		/* start constructing the expansion */
3281 		expbuf[0] = '\0';
3282 
3283 		if (mode & RCS_KWEXP_NAME) {
3284 			if (strlcat(expbuf, "$", sizeof(expbuf)) >=
3285 			    sizeof(expbuf) || strlcat(expbuf, kwstr,
3286 			    sizeof(expbuf)) >= sizeof(expbuf))
3287 				fatal("rcs_kwexp_line: truncated");
3288 			if ((mode & RCS_KWEXP_VAL) &&
3289 			    strlcat(expbuf, ": ", sizeof(expbuf)) >=
3290 			    sizeof(expbuf))
3291 				fatal("rcs_kwexp_line: truncated");
3292 		}
3293 
3294 		/*
3295 		 * order matters because of RCS_KW_ID and
3296 		 * RCS_KW_HEADER here
3297 		 */
3298 		if (mode & RCS_KWEXP_VAL) {
3299 			if (kwtype & RCS_KW_RCSFILE) {
3300 				if (!(kwtype & RCS_KW_FULLPATH))
3301 					(void)strlcat(expbuf, basename(rcsfile),
3302 					    sizeof(expbuf));
3303 				else
3304 					(void)strlcat(expbuf, rcsfile,
3305 					    sizeof(expbuf));
3306 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3307 				    sizeof(expbuf))
3308 					fatal("rcs_kwexp_line: truncated");
3309 			}
3310 
3311 			if (kwtype & RCS_KW_REVISION) {
3312 				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3313 				if (strlcat(buf, " ", sizeof(buf)) >=
3314 				    sizeof(buf) || strlcat(expbuf, buf,
3315 				    sizeof(expbuf)) >= sizeof(buf))
3316 					fatal("rcs_kwexp_line: truncated");
3317 			}
3318 
3319 			if (kwtype & RCS_KW_DATE) {
3320 				fmt = "%Y/%m/%d %H:%M:%S ";
3321 
3322 				if (strftime(buf, sizeof(buf), fmt,
3323 				    &rdp->rd_date) == 0)
3324 					fatal("rcs_kwexp_line: strftime "
3325 					    "failure");
3326 				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3327 				    sizeof(expbuf))
3328 					fatal("rcs_kwexp_line: string "
3329 					    "truncated");
3330 			}
3331 
3332 			if (kwtype & RCS_KW_MDOCDATE) {
3333 				/*
3334 				 * Do not prepend ' ' for a single
3335 				 * digit, %e would do so and there is
3336 				 * no better format for strftime().
3337 				 */
3338 				if (rdp->rd_date.tm_mday < 10)
3339 					fmt = "%B%e %Y ";
3340 				else
3341 					fmt = "%B %e %Y ";
3342 
3343 				if (strftime(buf, sizeof(buf), fmt,
3344 				    &rdp->rd_date) == 0)
3345 					fatal("rcs_kwexp_line: strftime "
3346 					    "failure");
3347 				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3348 				    sizeof(expbuf))
3349 					fatal("rcs_kwexp_line: string "
3350 					    "truncated");
3351 			}
3352 
3353 			if (kwtype & RCS_KW_AUTHOR) {
3354 				if (strlcat(expbuf, rdp->rd_author,
3355 				    sizeof(expbuf)) >= sizeof(expbuf) ||
3356 				    strlcat(expbuf, " ", sizeof(expbuf)) >=
3357 				    sizeof(expbuf))
3358 					fatal("rcs_kwexp_line: string "
3359 					    "truncated");
3360 			}
3361 
3362 			if (kwtype & RCS_KW_STATE) {
3363 				if (strlcat(expbuf, rdp->rd_state,
3364 				    sizeof(expbuf)) >= sizeof(expbuf) ||
3365 				    strlcat(expbuf, " ", sizeof(expbuf)) >=
3366 				    sizeof(expbuf))
3367 					fatal("rcs_kwexp_line: string "
3368 					    "truncated");
3369 			}
3370 
3371 			/* order does not matter anymore below */
3372 			if (kwtype & RCS_KW_LOG) {
3373 				char linebuf[256];
3374 				struct cvs_line *cur, *lp;
3375 				char *logp, *l_line, *prefix, *q, *sprefix;
3376 				size_t i;
3377 
3378 				/* Log line */
3379 				if (!(kwtype & RCS_KW_FULLPATH))
3380 					(void)strlcat(expbuf,
3381 					    basename(rcsfile), sizeof(expbuf));
3382 				else
3383 					(void)strlcat(expbuf, rcsfile,
3384 					    sizeof(expbuf));
3385 
3386 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3387 				    sizeof(expbuf))
3388 					fatal("rcs_kwexp_line: string "
3389 					    "truncated");
3390 
3391 				cur = line;
3392 
3393 				/* copy rdp->rd_log for strsep */
3394 				logp = xstrdup(rdp->rd_log);
3395 
3396 				/* copy our prefix for later processing */
3397 				prefix = xmalloc(start - line->l_line + 1);
3398 				memcpy(prefix, line->l_line,
3399 				    start - line->l_line);
3400 				prefix[start - line->l_line] = '\0';
3401 
3402 				/* copy also prefix without trailing blanks. */
3403 				sprefix = xstrdup(prefix);
3404 				for (i = strlen(sprefix); i > 0 &&
3405 				    sprefix[i - 1] == ' '; i--)
3406 					sprefix[i - 1] = '\0';
3407 
3408 				/* new line: revision + date + author */
3409 				linebuf[0] = '\0';
3410 				if (strlcat(linebuf, "Revision ",
3411 				    sizeof(linebuf)) >= sizeof(linebuf))
3412 					fatal("rcs_kwexp_line: truncated");
3413 				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3414 				if (strlcat(linebuf, buf, sizeof(linebuf))
3415 				    >= sizeof(buf))
3416 					fatal("rcs_kwexp_line: truncated");
3417 				fmt = "  %Y/%m/%d %H:%M:%S  ";
3418 				if (strftime(buf, sizeof(buf), fmt,
3419 				    &rdp->rd_date) == 0)
3420 					fatal("rcs_kwexp_line: strftime "
3421 					    "failure");
3422 				if (strlcat(linebuf, buf, sizeof(linebuf))
3423 				    >= sizeof(linebuf))
3424 					fatal("rcs_kwexp_line: string "
3425 					    "truncated");
3426 				if (strlcat(linebuf, rdp->rd_author,
3427 				    sizeof(linebuf)) >= sizeof(linebuf))
3428 					fatal("rcs_kwexp_line: string "
3429 					    "truncated");
3430 
3431 				lp = xcalloc(1, sizeof(*lp));
3432 				xasprintf(&(lp->l_line), "%s%s\n",
3433 				    prefix, linebuf);
3434 				lp->l_len = strlen(lp->l_line);
3435 				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3436 				    l_list);
3437 				cur = lp;
3438 
3439 				/* Log message */
3440 				q = logp;
3441 				while ((l_line = strsep(&q, "\n")) != NULL &&
3442 				    q != NULL) {
3443 					lp = xcalloc(1, sizeof(*lp));
3444 
3445 					if (l_line[0] == '\0') {
3446 						xasprintf(&(lp->l_line), "%s\n",
3447 						    sprefix);
3448 					} else {
3449 						xasprintf(&(lp->l_line),
3450 						    "%s%s\n", prefix, l_line);
3451 					}
3452 
3453 					lp->l_len = strlen(lp->l_line);
3454 					TAILQ_INSERT_AFTER(&(lines->l_lines),
3455 					    cur, lp, l_list);
3456 					cur = lp;
3457 				}
3458 				xfree(logp);
3459 
3460 				/*
3461 				 * This is just another hairy mess, but it must
3462 				 * be done: All characters behind Log will be
3463 				 * written in a new line next to log messages.
3464 				 * But that's not enough, we have to strip all
3465 				 * trailing whitespaces of our prefix.
3466 				 */
3467 				lp = xcalloc(1, sizeof(*lp));
3468 				lp->l_line = xcalloc(strlen(sprefix) +
3469 				    line->l_line + line->l_len - end + 1, 1);
3470 				strlcpy(lp->l_line, sprefix,
3471 				    strlen(sprefix) + 1);
3472 				memcpy(lp->l_line + strlen(sprefix),
3473 				    end, line->l_line + line->l_len - end);
3474 				lp->l_len = strlen(lp->l_line);
3475 				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3476 				    l_list);
3477 				cur = lp;
3478 
3479 				end = line->l_line + line->l_len - 1;
3480 
3481 				xfree(prefix);
3482 				xfree(sprefix);
3483 
3484 			}
3485 
3486 			if (kwtype & RCS_KW_SOURCE) {
3487 				if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
3488 				    sizeof(expbuf) || strlcat(expbuf, " ",
3489 				    sizeof(expbuf)) >= sizeof(expbuf))
3490 					fatal("rcs_kwexp_line: string "
3491 					    "truncated");
3492 			}
3493 
3494 			if (kwtype & RCS_KW_NAME)
3495 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3496 				    sizeof(expbuf))
3497 					fatal("rcs_kwexp_line: string "
3498 					    "truncated");
3499 
3500 			if (kwtype & RCS_KW_LOCKER)
3501 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3502 				    sizeof(expbuf))
3503 					fatal("rcs_kwexp_line: string "
3504 					    "truncated");
3505 		}
3506 
3507 		/* end the expansion */
3508 		if (mode & RCS_KWEXP_NAME)
3509 			if (strlcat(expbuf, "$",
3510 			    sizeof(expbuf)) >= sizeof(expbuf))
3511 				fatal("rcs_kwexp_line: truncated");
3512 
3513 		/* Concatenate everything together. */
3514 		tmpbuf = cvs_buf_alloc(len + strlen(expbuf));
3515 		/* Append everything before keyword. */
3516 		cvs_buf_append(tmpbuf, line->l_line,
3517 		    start - line->l_line);
3518 		/* Append keyword. */
3519 		cvs_buf_puts(tmpbuf, expbuf);
3520 		/* Point c to end of keyword. */
3521 		tlen = cvs_buf_len(tmpbuf) - 1;
3522 		/* Append everything after keyword. */
3523 		cvs_buf_append(tmpbuf, end,
3524 		    line->l_line + line->l_len - end);
3525 		c = cvs_buf_get(tmpbuf) + tlen;
3526 		/* Point fin to end of data. */
3527 		fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1;
3528 		/* Recalculate new length. */
3529 		len = cvs_buf_len(tmpbuf);
3530 
3531 		/* tmpbuf is now ready, convert to string */
3532 		if (line->l_needsfree)
3533 			xfree(line->l_line);
3534 		line->l_len = len;
3535 		line->l_line = cvs_buf_release(tmpbuf);
3536 		line->l_needsfree = 1;
3537 	}
3538 }
3539 
3540 /* rcs_translate_tag() */
3541 RCSNUM *
3542 rcs_translate_tag(const char *revstr, RCSFILE *rfp)
3543 {
3544 	int follow;
3545 	time_t deltatime;
3546 	char branch[CVS_REV_BUFSZ];
3547 	RCSNUM *brev, *frev, *rev, *rrev;
3548 	struct rcs_delta *rdp, *trdp;
3549 	time_t cdate;
3550 
3551 	brev = frev = rrev = NULL;
3552 
3553 	if (revstr == NULL) {
3554 		if (rfp->rf_branch != NULL) {
3555 			rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
3556 			revstr = branch;
3557 		} else {
3558 			revstr = RCS_HEAD_BRANCH;
3559 		}
3560 	}
3561 
3562 	if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
3563 		return (NULL);
3564 
3565 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3566 		return (NULL);
3567 
3568 	/* let's see if we must follow a branch */
3569 	if (!strcmp(revstr, RCS_HEAD_BRANCH))
3570 		follow = 1;
3571 	else {
3572 		frev = rcs_sym_getrev(rfp, revstr);
3573 		if (frev == NULL)
3574 			frev = rrev = rcsnum_parse(revstr);
3575 
3576 		brev = rcsnum_alloc();
3577 		rcsnum_cpy(rev, brev, rev->rn_len - 1);
3578 
3579 		if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
3580 		    !rcsnum_cmp(frev, brev, 0)) {
3581 			follow = 1;
3582 		} else
3583 			follow = 0;
3584 
3585 		rcsnum_free(brev);
3586 		if (rrev != NULL)
3587 			rcsnum_free(rrev);
3588 	}
3589 
3590 	if (cvs_specified_date != -1)
3591 		cdate = cvs_specified_date;
3592 	else
3593 		cdate = cvs_directory_date;
3594 
3595 	if (cdate == -1) {
3596 		/* XXX */
3597 		if (rev->rn_len < 4 || !follow) {
3598 			return (rev);
3599 		}
3600 
3601 		/* Find the latest delta on that branch */
3602 		rcsnum_free(rev);
3603 		for (;;) {
3604 			if (rdp->rd_next->rn_len == 0)
3605 				break;
3606 			if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
3607 				fatal("rcs_translate_tag: could not fetch "
3608 				    "branch delta");
3609 		}
3610 
3611 		rev = rcsnum_alloc();
3612 		rcsnum_cpy(rdp->rd_num, rev, 0);
3613 		return (rev);
3614 	}
3615 
3616 	if (frev != NULL)
3617 		rcsnum_tostr(frev, branch, sizeof(branch));
3618 
3619 	if (frev != NULL) {
3620 		brev = rcsnum_revtobr(frev);
3621 		brev->rn_len = rev->rn_len - 1;
3622 	}
3623 
3624 	rcsnum_free(rev);
3625 
3626 	do {
3627 		deltatime = timelocal(&(rdp->rd_date));
3628 
3629 		if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
3630 			if (deltatime > cdate) {
3631 				trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
3632 				if (trdp == NULL)
3633 					trdp = rdp;
3634 
3635 				if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
3636 					return (NULL);
3637 
3638 				rev = rcsnum_alloc();
3639 				rcsnum_cpy(trdp->rd_num, rev, 0);
3640 				return (rev);
3641 			}
3642 
3643 			if (rdp->rd_next->rn_len == 0) {
3644 				rev = rcsnum_alloc();
3645 				rcsnum_cpy(rdp->rd_num, rev, 0);
3646 				return (rev);
3647 			}
3648 		} else {
3649 			if (deltatime < cdate) {
3650 				rev = rcsnum_alloc();
3651 				rcsnum_cpy(rdp->rd_num, rev, 0);
3652 				return (rev);
3653 			}
3654 		}
3655 
3656 		if (follow && rdp->rd_next->rn_len != 0) {
3657 			if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
3658 				break;
3659 
3660 			trdp = rcs_findrev(rfp, rdp->rd_next);
3661 			if (trdp == NULL)
3662 				fatal("failed to grab next revision");
3663 			rdp = trdp;
3664 		} else
3665 			follow = 0;
3666 	} while (follow);
3667 
3668 	return (NULL);
3669 }
3670