xref: /openbsd-src/usr.bin/cvs/rcs.c (revision d874cce4b1d9fe6b41c9e4f2117a77d8a4a37b92)
1 /*	$OpenBSD: rcs.c,v 1.277 2008/06/15 04:44:06 joris 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 
1213 	if (rev == RCS_HEAD_REV) {
1214 		if (rf->rf_flags & RCS_CREATE) {
1215 			if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1216 				return (-1);
1217 			if (rf->rf_head != NULL)
1218 				xfree(rf->rf_head);
1219 			rf->rf_head = rcsnum_alloc();
1220 			rcsnum_cpy(rev, rf->rf_head, 0);
1221 		} else if (rf->rf_head == NULL) {
1222 			return (-1);
1223 		} else {
1224 			rev = rcsnum_inc(rf->rf_head);
1225 		}
1226 	} else {
1227 		if ((rdp = rcs_findrev(rf, rev)) != NULL)
1228 			return (-1);
1229 	}
1230 
1231 	if ((pw = getpwuid(getuid())) == NULL)
1232 		fatal("getpwuid failed");
1233 
1234 	rdp = xcalloc(1, sizeof(*rdp));
1235 
1236 	TAILQ_INIT(&(rdp->rd_branches));
1237 
1238 	rdp->rd_num = rcsnum_alloc();
1239 	rcsnum_cpy(rev, rdp->rd_num, 0);
1240 
1241 	rdp->rd_next = rcsnum_alloc();
1242 
1243 	if (username == NULL)
1244 		username = pw->pw_name;
1245 
1246 	rdp->rd_author = xstrdup(username);
1247 	rdp->rd_state = xstrdup(RCS_STATE_EXP);
1248 	rdp->rd_log = xstrdup(msg);
1249 
1250 	if (date != (time_t)(-1))
1251 		now = date;
1252 	else
1253 		time(&now);
1254 	gmtime_r(&now, &(rdp->rd_date));
1255 
1256 	if (RCSNUM_ISBRANCHREV(rev))
1257 		TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1258 	else
1259 		TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1260 	rf->rf_ndelta++;
1261 
1262 	if (!(rf->rf_flags & RCS_CREATE)) {
1263 		if (RCSNUM_ISBRANCHREV(rev)) {
1264 			if (rev->rn_id[rev->rn_len - 1] == 1) {
1265 				/* a new branch */
1266 				root = rcsnum_branch_root(rev);
1267 				brp = xmalloc(sizeof(*brp));
1268 				brp->rb_num = rcsnum_alloc();
1269 				rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1270 
1271 				if ((ordp = rcs_findrev(rf, root)) == NULL)
1272 					fatal("root node not found");
1273 
1274 				TAILQ_FOREACH(obrp, &(ordp->rd_branches),
1275 				    rb_list) {
1276 					if (!rcsnum_cmp(obrp->rb_num,
1277 					    brp->rb_num,
1278 					    brp->rb_num->rn_len - 1))
1279 						break;
1280 				}
1281 
1282 				if (obrp == NULL) {
1283 					TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1284 					    brp, rb_list);
1285 				}
1286 			} else {
1287 				root = rcsnum_alloc();
1288 				rcsnum_cpy(rev, root, 0);
1289 				rcsnum_dec(root);
1290 				if ((ordp = rcs_findrev(rf, root)) == NULL)
1291 					fatal("previous revision not found");
1292 				rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1293 			}
1294 		} else {
1295 			ordp = TAILQ_NEXT(rdp, rd_list);
1296 			rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1297 		}
1298 	}
1299 
1300 	if (root != NULL)
1301 		rcsnum_free(root);
1302 
1303 	/* not synced anymore */
1304 	rf->rf_flags &= ~RCS_SYNCED;
1305 
1306 	return (0);
1307 }
1308 
1309 /*
1310  * rcs_rev_remove()
1311  *
1312  * Remove the revision whose number is <rev> from the RCS file <rf>.
1313  */
1314 int
1315 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1316 {
1317 	int fd1, fd2;
1318 	char *path_tmp1, *path_tmp2;
1319 	struct rcs_delta *rdp, *prevrdp, *nextrdp;
1320 	BUF *prevbuf, *newdiff, *newdeltatext;
1321 
1322 	if (rev == RCS_HEAD_REV)
1323 		rev = rf->rf_head;
1324 
1325 	if (rev == NULL)
1326 		return (-1);
1327 
1328 	/* do we actually have that revision? */
1329 	if ((rdp = rcs_findrev(rf, rev)) == NULL)
1330 		return (-1);
1331 
1332 	/*
1333 	 * This is confusing, the previous delta is next in the TAILQ list.
1334 	 * the next delta is the previous one in the TAILQ list.
1335 	 *
1336 	 * When the HEAD revision got specified, nextrdp will be NULL.
1337 	 * When the first revision got specified, prevrdp will be NULL.
1338 	 */
1339 	prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
1340 	nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list);
1341 
1342 	newdeltatext = NULL;
1343 	prevbuf = NULL;
1344 	path_tmp1 = path_tmp2 = NULL;
1345 
1346 	if (prevrdp != NULL && nextrdp != NULL) {
1347 		newdiff = cvs_buf_alloc(64);
1348 
1349 		/* calculate new diff */
1350 		(void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1351 		fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1352 
1353 		(void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1354 		fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1355 
1356 		diff_format = D_RCSDIFF;
1357 		if (cvs_diffreg(path_tmp1, path_tmp2,
1358 		    fd1, fd2, newdiff) == D_ERROR)
1359 			fatal("rcs_diffreg failed");
1360 
1361 		close(fd1);
1362 		close(fd2);
1363 
1364 		newdeltatext = newdiff;
1365 	} else if (nextrdp == NULL && prevrdp != NULL) {
1366 		newdeltatext = prevbuf;
1367 	}
1368 
1369 	if (newdeltatext != NULL) {
1370 		if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1371 			fatal("error setting new deltatext");
1372 	}
1373 
1374 	TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1375 
1376 	/* update pointers */
1377 	if (prevrdp != NULL && nextrdp != NULL) {
1378 		rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1379 	} else if (prevrdp != NULL) {
1380 		if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1381 			fatal("rcs_head_set failed");
1382 	} else if (nextrdp != NULL) {
1383 		rcsnum_free(nextrdp->rd_next);
1384 		nextrdp->rd_next = rcsnum_alloc();
1385 	} else {
1386 		rcsnum_free(rf->rf_head);
1387 		rf->rf_head = NULL;
1388 	}
1389 
1390 	rf->rf_ndelta--;
1391 	rf->rf_flags &= ~RCS_SYNCED;
1392 
1393 	rcs_freedelta(rdp);
1394 
1395 	if (newdeltatext != NULL)
1396 		xfree(newdeltatext);
1397 
1398 	if (path_tmp1 != NULL)
1399 		xfree(path_tmp1);
1400 	if (path_tmp2 != NULL)
1401 		xfree(path_tmp2);
1402 
1403 	return (0);
1404 }
1405 
1406 /*
1407  * rcs_findrev()
1408  *
1409  * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1410  * The revision number is given in <rev>.
1411  *
1412  * Returns a pointer to the delta on success, or NULL on failure.
1413  */
1414 struct rcs_delta *
1415 rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1416 {
1417 	int isbrev;
1418 	struct rcs_delta *rdp;
1419 
1420 	if (rev == NULL)
1421 		return NULL;
1422 
1423 	isbrev = RCSNUM_ISBRANCHREV(rev);
1424 
1425 	/*
1426 	 * We need to do more parsing if the last revision in the linked list
1427 	 * is greater than the requested revision.
1428 	 */
1429 	rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1430 	if (rdp == NULL ||
1431 	    (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1432 	    ((isbrev && rdp->rd_num->rn_len < 4) ||
1433 	    (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1434 		rcs_parse_deltas(rfp, rev);
1435 	}
1436 
1437 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1438 		if (rcsnum_differ(rdp->rd_num, rev))
1439 			continue;
1440 		else
1441 			return (rdp);
1442 	}
1443 
1444 	return (NULL);
1445 }
1446 
1447 /*
1448  * rcs_kwexp_set()
1449  *
1450  * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1451  */
1452 void
1453 rcs_kwexp_set(RCSFILE *file, int mode)
1454 {
1455 	int i;
1456 	char *tmp, buf[8] = "";
1457 
1458 	if (RCS_KWEXP_INVAL(mode))
1459 		return;
1460 
1461 	i = 0;
1462 	if (mode == RCS_KWEXP_NONE)
1463 		buf[0] = 'b';
1464 	else if (mode == RCS_KWEXP_OLD)
1465 		buf[0] = 'o';
1466 	else {
1467 		if (mode & RCS_KWEXP_NAME)
1468 			buf[i++] = 'k';
1469 		if (mode & RCS_KWEXP_VAL)
1470 			buf[i++] = 'v';
1471 		if (mode & RCS_KWEXP_LKR)
1472 			buf[i++] = 'l';
1473 	}
1474 
1475 	tmp = xstrdup(buf);
1476 	if (file->rf_expand != NULL)
1477 		xfree(file->rf_expand);
1478 	file->rf_expand = tmp;
1479 	/* not synced anymore */
1480 	file->rf_flags &= ~RCS_SYNCED;
1481 }
1482 
1483 /*
1484  * rcs_kwexp_get()
1485  *
1486  * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1487  */
1488 int
1489 rcs_kwexp_get(RCSFILE *file)
1490 {
1491 	return rcs_kflag_get(file->rf_expand);
1492 }
1493 
1494 /*
1495  * rcs_kflag_get()
1496  *
1497  * Get the keyword expansion mode from a set of character flags given in
1498  * <flags> and return the appropriate flag mask.  In case of an error, the
1499  * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1500  */
1501 int
1502 rcs_kflag_get(const char *flags)
1503 {
1504 	int fl;
1505 	size_t len;
1506 	const char *fp;
1507 
1508 	if (flags == NULL)
1509 		return 0;
1510 
1511 	fl = 0;
1512 	if (!(len = strlen(flags)))
1513 		return RCS_KWEXP_ERR;
1514 
1515 	for (fp = flags; *fp != '\0'; fp++) {
1516 		if (*fp == 'k')
1517 			fl |= RCS_KWEXP_NAME;
1518 		else if (*fp == 'v')
1519 			fl |= RCS_KWEXP_VAL;
1520 		else if (*fp == 'l')
1521 			fl |= RCS_KWEXP_LKR;
1522 		else if (*fp == 'o') {
1523 			if (len != 1)
1524 				fl |= RCS_KWEXP_ERR;
1525 			fl |= RCS_KWEXP_OLD;
1526 		} else if (*fp == 'b') {
1527 			if (len != 1)
1528 				fl |= RCS_KWEXP_ERR;
1529 			fl |= RCS_KWEXP_NONE;
1530 		} else	/* unknown letter */
1531 			fl |= RCS_KWEXP_ERR;
1532 	}
1533 
1534 	return (fl);
1535 }
1536 
1537 /* rcs_parse_deltas()
1538  *
1539  * Parse deltas. If <rev> is not NULL, parse only as far as that
1540  * revision. If <rev> is NULL, parse all deltas.
1541  */
1542 static void
1543 rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev)
1544 {
1545 	int ret;
1546 	struct rcs_delta *enddelta;
1547 
1548 	if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
1549 		return;
1550 
1551 	for (;;) {
1552 		ret = rcs_parse_delta(rfp);
1553 		if (rev != NULL) {
1554 			enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1555 			if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
1556 				break;
1557 		}
1558 
1559 		if (ret == 0) {
1560 			rfp->rf_flags |= PARSED_DELTAS;
1561 			break;
1562 		}
1563 		else if (ret == -1)
1564 			fatal("error parsing deltas");
1565 	}
1566 }
1567 
1568 /* rcs_parse_deltatexts()
1569  *
1570  * Parse deltatexts. If <rev> is not NULL, parse only as far as that
1571  * revision. If <rev> is NULL, parse everything.
1572  */
1573 static void
1574 rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
1575 {
1576 	int ret;
1577 	struct rcs_delta *rdp;
1578 
1579 	if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
1580 	    (rfp->rf_flags & RCS_CREATE))
1581 		return;
1582 
1583 	if (!(rfp->rf_flags & PARSED_DESC))
1584 		rcs_parse_desc(rfp, rev);
1585 	for (;;) {
1586 		if (rev != NULL) {
1587 			rdp = rcs_findrev(rfp, rev);
1588 			if (rdp->rd_text != NULL)
1589 				break;
1590 			else
1591 				ret = rcs_parse_deltatext(rfp);
1592 		} else
1593 			ret = rcs_parse_deltatext(rfp);
1594 		if (ret == 0) {
1595 			rfp->rf_flags |= PARSED_DELTATEXTS;
1596 			break;
1597 		}
1598 		else if (ret == -1)
1599 			fatal("problem parsing deltatexts");
1600 	}
1601 }
1602 
1603 /* rcs_parse_desc()
1604  *
1605  * Parse RCS description.
1606  */
1607 static void
1608 rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev)
1609 {
1610 	int ret = 0;
1611 
1612 	if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE))
1613 		return;
1614 	if (!(rfp->rf_flags & PARSED_DELTAS))
1615 		rcs_parse_deltas(rfp, rev);
1616 	/* do parsing */
1617 	ret = rcs_gettok(rfp);
1618 	if (ret != RCS_TOK_DESC)
1619 		fatal("token `%s' found where RCS desc expected",
1620 		    RCS_TOKSTR(rfp));
1621 
1622 	ret = rcs_gettok(rfp);
1623 	if (ret != RCS_TOK_STRING)
1624 		fatal("token `%s' found where RCS desc expected",
1625 		    RCS_TOKSTR(rfp));
1626 
1627 	rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp));
1628 	rfp->rf_flags |= PARSED_DESC;
1629 }
1630 
1631 /*
1632  * rcs_parse_init()
1633  *
1634  * Initial parsing of file <path>, which are in the RCS format.
1635  * Just does admin section.
1636  */
1637 static void
1638 rcs_parse_init(RCSFILE *rfp)
1639 {
1640 	struct rcs_pdata *pdp;
1641 
1642 	if (rfp->rf_flags & RCS_PARSED)
1643 		return;
1644 
1645 	pdp = xcalloc(1, sizeof(*pdp));
1646 
1647 	pdp->rp_lines = 0;
1648 	pdp->rp_pttype = RCS_TOK_ERR;
1649 
1650 	if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL)
1651 		fatal("fdopen: `%s'", rfp->rf_path);
1652 
1653 	pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE);
1654 	pdp->rp_blen = RCS_BUFSIZE;
1655 	pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1656 
1657 	/* ditch the strict lock */
1658 	rfp->rf_flags &= ~RCS_SLOCK;
1659 	rfp->rf_pdata = pdp;
1660 
1661 	if (rcs_parse_admin(rfp) < 0) {
1662 		rcs_freepdata(pdp);
1663 		fatal("could not parse admin data");
1664 	}
1665 
1666 	if (rfp->rf_flags & RCS_PARSE_FULLY) {
1667 		rcs_parse_deltatexts(rfp, NULL);
1668 		(void)close(rfp->fd);
1669 		rfp->fd = -1;
1670 	}
1671 
1672 	rfp->rf_flags |= RCS_SYNCED;
1673 }
1674 
1675 /*
1676  * rcs_parse_admin()
1677  *
1678  * Parse the administrative portion of an RCS file.
1679  * Returns the type of the first token found after the admin section on
1680  * success, or -1 on failure.
1681  */
1682 static int
1683 rcs_parse_admin(RCSFILE *rfp)
1684 {
1685 	u_int i;
1686 	int tok, ntok, hmask;
1687 	struct rcs_key *rk;
1688 
1689 	rfp->rf_head = NULL;
1690 	rfp->rf_branch = NULL;
1691 
1692 	/* hmask is a mask of the headers already encountered */
1693 	hmask = 0;
1694 	for (;;) {
1695 		tok = rcs_gettok(rfp);
1696 		if (tok == RCS_TOK_ERR) {
1697 			cvs_log(LP_ERR, "parse error in RCS admin section");
1698 			goto fail;
1699 		} else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1700 			/*
1701 			 * Assume this is the start of the first delta or
1702 			 * that we are dealing with an empty RCS file and
1703 			 * we just found the description.
1704 			 */
1705 			if (!(hmask & (1 << RCS_TOK_HEAD))) {
1706 				cvs_log(LP_ERR, "head missing");
1707 				goto fail;
1708 			}
1709 			rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1710 			return (tok);
1711 		}
1712 
1713 		rk = NULL;
1714 		for (i = 0; i < RCS_NKEYS; i++)
1715 			if (rcs_keys[i].rk_id == tok)
1716 				rk = &(rcs_keys[i]);
1717 
1718 		if (hmask & (1 << tok)) {
1719 			cvs_log(LP_ERR, "duplicate RCS key");
1720 			goto fail;
1721 		}
1722 		hmask |= (1 << tok);
1723 
1724 		switch (tok) {
1725 		case RCS_TOK_HEAD:
1726 		case RCS_TOK_BRANCH:
1727 		case RCS_TOK_COMMENT:
1728 		case RCS_TOK_EXPAND:
1729 			ntok = rcs_gettok(rfp);
1730 			if (ntok == RCS_TOK_SCOLON)
1731 				break;
1732 			if (ntok != rk->rk_val) {
1733 				cvs_log(LP_ERR,
1734 				    "invalid value type for RCS key `%s'",
1735 				    rk->rk_str);
1736 			}
1737 
1738 			if (tok == RCS_TOK_HEAD) {
1739 				if (rfp->rf_head == NULL)
1740 					rfp->rf_head = rcsnum_alloc();
1741 				if (RCS_TOKSTR(rfp)[0] == '\0' ||
1742 				    rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1743 				    rfp->rf_head) < 0) {
1744 					rcsnum_free(rfp->rf_head);
1745 					rfp->rf_head = NULL;
1746 				}
1747 			} else if (tok == RCS_TOK_BRANCH) {
1748 				if (rfp->rf_branch == NULL)
1749 					rfp->rf_branch = rcsnum_alloc();
1750 				if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1751 				    rfp->rf_branch) < 0)
1752 					goto fail;
1753 			} else if (tok == RCS_TOK_COMMENT) {
1754 				rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp));
1755 			} else if (tok == RCS_TOK_EXPAND) {
1756 				rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp));
1757 			}
1758 
1759 			/* now get the expected semi-colon */
1760 			ntok = rcs_gettok(rfp);
1761 			if (ntok != RCS_TOK_SCOLON) {
1762 				cvs_log(LP_ERR,
1763 				    "missing semi-colon after RCS `%s' key",
1764 				    rk->rk_str);
1765 				goto fail;
1766 			}
1767 			break;
1768 		case RCS_TOK_ACCESS:
1769 			if (rcs_parse_access(rfp) < 0)
1770 				goto fail;
1771 			break;
1772 		case RCS_TOK_SYMBOLS:
1773 			rcs_ignore_keys = 1;
1774 			if (rcs_parse_symbols(rfp) < 0)
1775 				goto fail;
1776 			break;
1777 		case RCS_TOK_LOCKS:
1778 			if (rcs_parse_locks(rfp) < 0)
1779 				goto fail;
1780 			break;
1781 		default:
1782 			cvs_log(LP_ERR,
1783 			    "unexpected token `%s' in RCS admin section",
1784 			    RCS_TOKSTR(rfp));
1785 			goto fail;
1786 		}
1787 
1788 		rcs_ignore_keys = 0;
1789 
1790 	}
1791 
1792 fail:
1793 	return (-1);
1794 }
1795 
1796 /*
1797  * rcs_parse_delta()
1798  *
1799  * Parse an RCS delta section and allocate the structure to store that delta's
1800  * information in the <rfp> delta list.
1801  * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1802  * -1 on error.
1803  */
1804 static int
1805 rcs_parse_delta(RCSFILE *rfp)
1806 {
1807 	int ret, tok, ntok, hmask;
1808 	u_int i;
1809 	char *tokstr;
1810 	RCSNUM *datenum;
1811 	struct rcs_delta *rdp;
1812 	struct rcs_key *rk;
1813 
1814 	tok = rcs_gettok(rfp);
1815 	if (tok == RCS_TOK_DESC) {
1816 		rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1817 		return (0);
1818 	} else if (tok != RCS_TOK_NUM) {
1819 		cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1820 		    RCS_TOKSTR(rfp));
1821 		return (-1);
1822 	}
1823 
1824 	rdp = xcalloc(1, sizeof(*rdp));
1825 
1826 	rdp->rd_num = rcsnum_alloc();
1827 	rdp->rd_next = rcsnum_alloc();
1828 
1829 	TAILQ_INIT(&(rdp->rd_branches));
1830 
1831 	rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1832 
1833 	hmask = 0;
1834 	ret = 0;
1835 	tokstr = NULL;
1836 
1837 	for (;;) {
1838 		tok = rcs_gettok(rfp);
1839 		if (tok == RCS_TOK_ERR) {
1840 			cvs_log(LP_ERR, "parse error in RCS delta section");
1841 			rcs_freedelta(rdp);
1842 			return (-1);
1843 		} else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1844 			rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1845 			ret = (tok == RCS_TOK_NUM ? 1 : 0);
1846 			break;
1847 		}
1848 
1849 		rk = NULL;
1850 		for (i = 0; i < RCS_NKEYS; i++)
1851 			if (rcs_keys[i].rk_id == tok)
1852 				rk = &(rcs_keys[i]);
1853 
1854 		if (hmask & (1 << tok)) {
1855 			cvs_log(LP_ERR, "duplicate RCS key");
1856 			rcs_freedelta(rdp);
1857 			return (-1);
1858 		}
1859 		hmask |= (1 << tok);
1860 
1861 		switch (tok) {
1862 		case RCS_TOK_DATE:
1863 		case RCS_TOK_AUTHOR:
1864 		case RCS_TOK_STATE:
1865 		case RCS_TOK_NEXT:
1866 			ntok = rcs_gettok(rfp);
1867 			if (ntok == RCS_TOK_SCOLON) {
1868 				if (rk->rk_flags & RCS_VOPT)
1869 					break;
1870 				else {
1871 					cvs_log(LP_ERR, "missing mandatory "
1872 					    "value to RCS key `%s'",
1873 					    rk->rk_str);
1874 					rcs_freedelta(rdp);
1875 					return (-1);
1876 				}
1877 			}
1878 
1879 			if (ntok != rk->rk_val) {
1880 				cvs_log(LP_ERR,
1881 				    "invalid value type for RCS key `%s'",
1882 				    rk->rk_str);
1883 				rcs_freedelta(rdp);
1884 				return (-1);
1885 			}
1886 
1887 			if (tokstr != NULL)
1888 				xfree(tokstr);
1889 			tokstr = xstrdup(RCS_TOKSTR(rfp));
1890 			/* now get the expected semi-colon */
1891 			ntok = rcs_gettok(rfp);
1892 			if (ntok != RCS_TOK_SCOLON) {
1893 				cvs_log(LP_ERR,
1894 				    "missing semi-colon after RCS `%s' key",
1895 				    rk->rk_str);
1896 				xfree(tokstr);
1897 				rcs_freedelta(rdp);
1898 				return (-1);
1899 			}
1900 
1901 			if (tok == RCS_TOK_DATE) {
1902 				if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1903 					xfree(tokstr);
1904 					rcs_freedelta(rdp);
1905 					return (-1);
1906 				}
1907 				if (datenum->rn_len != 6) {
1908 					cvs_log(LP_ERR,
1909 					    "RCS date specification has %s "
1910 					    "fields",
1911 					    (datenum->rn_len > 6) ? "too many" :
1912 					    "missing");
1913 					xfree(tokstr);
1914 					rcs_freedelta(rdp);
1915 					rcsnum_free(datenum);
1916 					return (-1);
1917 				}
1918 				rdp->rd_date.tm_year = datenum->rn_id[0];
1919 				if (rdp->rd_date.tm_year >= 1900)
1920 					rdp->rd_date.tm_year -= 1900;
1921 				rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1922 				rdp->rd_date.tm_mday = datenum->rn_id[2];
1923 				rdp->rd_date.tm_hour = datenum->rn_id[3];
1924 				rdp->rd_date.tm_min = datenum->rn_id[4];
1925 				rdp->rd_date.tm_sec = datenum->rn_id[5];
1926 				rcsnum_free(datenum);
1927 			} else if (tok == RCS_TOK_AUTHOR) {
1928 				rdp->rd_author = tokstr;
1929 				tokstr = NULL;
1930 			} else if (tok == RCS_TOK_STATE) {
1931 				rdp->rd_state = tokstr;
1932 				tokstr = NULL;
1933 			} else if (tok == RCS_TOK_NEXT) {
1934 				rcsnum_aton(tokstr, NULL, rdp->rd_next);
1935 			}
1936 			break;
1937 		case RCS_TOK_BRANCHES:
1938 			if (rcs_parse_branches(rfp, rdp) < 0) {
1939 				rcs_freedelta(rdp);
1940 				return (-1);
1941 			}
1942 			break;
1943 		default:
1944 			cvs_log(LP_ERR, "unexpected token `%s' in RCS delta",
1945 			    RCS_TOKSTR(rfp));
1946 			rcs_freedelta(rdp);
1947 			return (-1);
1948 		}
1949 	}
1950 
1951 	if (tokstr != NULL)
1952 		xfree(tokstr);
1953 
1954 	TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1955 	rfp->rf_ndelta++;
1956 
1957 	return (ret);
1958 }
1959 
1960 /*
1961  * rcs_parse_deltatext()
1962  *
1963  * Parse an RCS delta text section and fill in the log and text field of the
1964  * appropriate delta section.
1965  * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1966  * -1 on error.
1967  */
1968 static int
1969 rcs_parse_deltatext(RCSFILE *rfp)
1970 {
1971 	int tok;
1972 	RCSNUM *tnum;
1973 	struct rcs_delta *rdp;
1974 
1975 	tok = rcs_gettok(rfp);
1976 	if (tok == RCS_TOK_EOF)
1977 		return (0);
1978 
1979 	if (tok != RCS_TOK_NUM) {
1980 		cvs_log(LP_ERR,
1981 		    "unexpected token `%s' at start of RCS delta text",
1982 		    RCS_TOKSTR(rfp));
1983 		return (-1);
1984 	}
1985 
1986 	tnum = rcsnum_alloc();
1987 	rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1988 
1989 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1990 		if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1991 			break;
1992 	}
1993 	rcsnum_free(tnum);
1994 
1995 	if (rdp == NULL) {
1996 		cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1997 		    RCS_TOKSTR(rfp));
1998 		return (-1);
1999 	}
2000 
2001 	tok = rcs_gettok(rfp);
2002 	if (tok != RCS_TOK_LOG) {
2003 		cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2004 		    RCS_TOKSTR(rfp));
2005 		return (-1);
2006 	}
2007 
2008 	tok = rcs_gettok(rfp);
2009 	if (tok != RCS_TOK_STRING) {
2010 		cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2011 		    RCS_TOKSTR(rfp));
2012 		return (-1);
2013 	}
2014 	rdp->rd_log = xstrdup(RCS_TOKSTR(rfp));
2015 	tok = rcs_gettok(rfp);
2016 	if (tok != RCS_TOK_TEXT) {
2017 		cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2018 		    RCS_TOKSTR(rfp));
2019 		return (-1);
2020 	}
2021 
2022 	tok = rcs_gettok(rfp);
2023 	if (tok != RCS_TOK_STRING) {
2024 		cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2025 		    RCS_TOKSTR(rfp));
2026 		return (-1);
2027 	}
2028 
2029 	if (RCS_TOKLEN(rfp) == 0) {
2030 		rdp->rd_text = xmalloc(1);
2031 		rdp->rd_text[0] = '\0';
2032 		rdp->rd_tlen = 0;
2033 	} else {
2034 		rdp->rd_text = xmalloc(RCS_TOKLEN(rfp));
2035 		memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp));
2036 		rdp->rd_tlen = RCS_TOKLEN(rfp);
2037 	}
2038 
2039 	return (1);
2040 }
2041 
2042 /*
2043  * rcs_parse_access()
2044  *
2045  * Parse the access list given as value to the `access' keyword.
2046  * Returns 0 on success, or -1 on failure.
2047  */
2048 static int
2049 rcs_parse_access(RCSFILE *rfp)
2050 {
2051 	int type;
2052 
2053 	while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
2054 		if (type != RCS_TOK_ID) {
2055 			cvs_log(LP_ERR, "unexpected token `%s' in access list",
2056 			    RCS_TOKSTR(rfp));
2057 			return (-1);
2058 		}
2059 
2060 		if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
2061 			return (-1);
2062 	}
2063 
2064 	return (0);
2065 }
2066 
2067 /*
2068  * rcs_parse_symbols()
2069  *
2070  * Parse the symbol list given as value to the `symbols' keyword.
2071  * Returns 0 on success, or -1 on failure.
2072  */
2073 static int
2074 rcs_parse_symbols(RCSFILE *rfp)
2075 {
2076 	int type;
2077 	struct rcs_sym *symp;
2078 
2079 	for (;;) {
2080 		type = rcs_gettok(rfp);
2081 		if (type == RCS_TOK_SCOLON)
2082 			break;
2083 
2084 		if (type != RCS_TOK_ID) {
2085 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2086 			    RCS_TOKSTR(rfp));
2087 			return (-1);
2088 		}
2089 
2090 		symp = xmalloc(sizeof(*symp));
2091 		symp->rs_name = xstrdup(RCS_TOKSTR(rfp));
2092 		symp->rs_num = rcsnum_alloc();
2093 
2094 		type = rcs_gettok(rfp);
2095 		if (type != RCS_TOK_COLON) {
2096 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2097 			    RCS_TOKSTR(rfp));
2098 			rcsnum_free(symp->rs_num);
2099 			xfree(symp->rs_name);
2100 			xfree(symp);
2101 			return (-1);
2102 		}
2103 
2104 		type = rcs_gettok(rfp);
2105 		if (type != RCS_TOK_NUM) {
2106 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2107 			    RCS_TOKSTR(rfp));
2108 			rcsnum_free(symp->rs_num);
2109 			xfree(symp->rs_name);
2110 			xfree(symp);
2111 			return (-1);
2112 		}
2113 
2114 		if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
2115 			cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2116 			    RCS_TOKSTR(rfp));
2117 			rcsnum_free(symp->rs_num);
2118 			xfree(symp->rs_name);
2119 			xfree(symp);
2120 			return (-1);
2121 		}
2122 
2123 		TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
2124 	}
2125 
2126 	return (0);
2127 }
2128 
2129 /*
2130  * rcs_parse_locks()
2131  *
2132  * Parse the lock list given as value to the `locks' keyword.
2133  * Returns 0 on success, or -1 on failure.
2134  */
2135 static int
2136 rcs_parse_locks(RCSFILE *rfp)
2137 {
2138 	int type;
2139 	struct rcs_lock *lkp;
2140 
2141 	for (;;) {
2142 		type = rcs_gettok(rfp);
2143 		if (type == RCS_TOK_SCOLON)
2144 			break;
2145 
2146 		if (type != RCS_TOK_ID) {
2147 			cvs_log(LP_ERR, "unexpected token `%s' in lock list",
2148 			    RCS_TOKSTR(rfp));
2149 			return (-1);
2150 		}
2151 
2152 		lkp = xmalloc(sizeof(*lkp));
2153 		lkp->rl_name = xstrdup(RCS_TOKSTR(rfp));
2154 		lkp->rl_num = rcsnum_alloc();
2155 
2156 		type = rcs_gettok(rfp);
2157 		if (type != RCS_TOK_COLON) {
2158 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2159 			    RCS_TOKSTR(rfp));
2160 			rcsnum_free(lkp->rl_num);
2161 			xfree(lkp->rl_name);
2162 			xfree(lkp);
2163 			return (-1);
2164 		}
2165 
2166 		type = rcs_gettok(rfp);
2167 		if (type != RCS_TOK_NUM) {
2168 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2169 			    RCS_TOKSTR(rfp));
2170 			rcsnum_free(lkp->rl_num);
2171 			xfree(lkp->rl_name);
2172 			xfree(lkp);
2173 			return (-1);
2174 		}
2175 
2176 		if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
2177 			cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2178 			    RCS_TOKSTR(rfp));
2179 			rcsnum_free(lkp->rl_num);
2180 			xfree(lkp->rl_name);
2181 			xfree(lkp);
2182 			return (-1);
2183 		}
2184 
2185 		TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
2186 	}
2187 
2188 	/* check if we have a `strict' */
2189 	type = rcs_gettok(rfp);
2190 	if (type != RCS_TOK_STRICT) {
2191 		rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
2192 	} else {
2193 		rfp->rf_flags |= RCS_SLOCK;
2194 
2195 		type = rcs_gettok(rfp);
2196 		if (type != RCS_TOK_SCOLON) {
2197 			cvs_log(LP_ERR,
2198 			    "missing semi-colon after `strict' keyword");
2199 			return (-1);
2200 		}
2201 	}
2202 
2203 	return (0);
2204 }
2205 
2206 /*
2207  * rcs_parse_branches()
2208  *
2209  * Parse the list of branches following a `branches' keyword in a delta.
2210  * Returns 0 on success, or -1 on failure.
2211  */
2212 static int
2213 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
2214 {
2215 	int type;
2216 	struct rcs_branch *brp;
2217 
2218 	for (;;) {
2219 		type = rcs_gettok(rfp);
2220 		if (type == RCS_TOK_SCOLON)
2221 			break;
2222 
2223 		if (type != RCS_TOK_NUM) {
2224 			cvs_log(LP_ERR,
2225 			    "unexpected token `%s' in list of branches",
2226 			    RCS_TOKSTR(rfp));
2227 			return (-1);
2228 		}
2229 
2230 		brp = xmalloc(sizeof(*brp));
2231 		brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
2232 		if (brp->rb_num == NULL) {
2233 			xfree(brp);
2234 			return (-1);
2235 		}
2236 
2237 		TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
2238 	}
2239 
2240 	return (0);
2241 }
2242 
2243 /*
2244  * rcs_freedelta()
2245  *
2246  * Free the contents of a delta structure.
2247  */
2248 static void
2249 rcs_freedelta(struct rcs_delta *rdp)
2250 {
2251 	struct rcs_branch *rb;
2252 
2253 	if (rdp->rd_num != NULL)
2254 		rcsnum_free(rdp->rd_num);
2255 	if (rdp->rd_next != NULL)
2256 		rcsnum_free(rdp->rd_next);
2257 
2258 	if (rdp->rd_author != NULL)
2259 		xfree(rdp->rd_author);
2260 	if (rdp->rd_locker != NULL)
2261 		xfree(rdp->rd_locker);
2262 	if (rdp->rd_state != NULL)
2263 		xfree(rdp->rd_state);
2264 	if (rdp->rd_log != NULL)
2265 		xfree(rdp->rd_log);
2266 	if (rdp->rd_text != NULL)
2267 		xfree(rdp->rd_text);
2268 
2269 	while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
2270 		TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
2271 		rcsnum_free(rb->rb_num);
2272 		xfree(rb);
2273 	}
2274 
2275 	xfree(rdp);
2276 }
2277 
2278 /*
2279  * rcs_freepdata()
2280  *
2281  * Free the contents of the parser data structure.
2282  */
2283 static void
2284 rcs_freepdata(struct rcs_pdata *pd)
2285 {
2286 	if (pd->rp_file != NULL)
2287 		(void)fclose(pd->rp_file);
2288 	if (pd->rp_buf != NULL)
2289 		xfree(pd->rp_buf);
2290 	xfree(pd);
2291 }
2292 
2293 /*
2294  * rcs_gettok()
2295  *
2296  * Get the next RCS token from the string <str>.
2297  */
2298 static int
2299 rcs_gettok(RCSFILE *rfp)
2300 {
2301 	u_int i;
2302 	int ch, last, type;
2303 	size_t len;
2304 	char *bp;
2305 	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2306 
2307 	type = RCS_TOK_ERR;
2308 	bp = pdp->rp_buf;
2309 	pdp->rp_tlen = 0;
2310 	*bp = '\0';
2311 
2312 	if (pdp->rp_pttype != RCS_TOK_ERR) {
2313 		type = pdp->rp_pttype;
2314 		if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >=
2315 		    pdp->rp_blen)
2316 			fatal("rcs_gettok: strlcpy");
2317 		pdp->rp_pttype = RCS_TOK_ERR;
2318 		return (type);
2319 	}
2320 
2321 	/* skip leading whitespace */
2322 	/* XXX we must skip backspace too for compatibility, should we? */
2323 	do {
2324 		ch = getc(pdp->rp_file);
2325 		if (ch == '\n')
2326 			pdp->rp_lines++;
2327 	} while (isspace(ch));
2328 
2329 	if (ch == EOF) {
2330 		type = RCS_TOK_EOF;
2331 	} else if (ch == ';') {
2332 		type = RCS_TOK_SCOLON;
2333 	} else if (ch == ':') {
2334 		type = RCS_TOK_COLON;
2335 	} else if (isalpha(ch)) {
2336 		type = RCS_TOK_ID;
2337 		*(bp++) = ch;
2338 		for (;;) {
2339 			ch = getc(pdp->rp_file);
2340 			if (ch == EOF) {
2341 				type = RCS_TOK_EOF;
2342 				break;
2343 			} else if (!isalnum(ch) && ch != '_' && ch != '-' &&
2344 			    ch != '/' && ch != '+' && ch != '|') {
2345 				ungetc(ch, pdp->rp_file);
2346 				break;
2347 			}
2348 			*(bp++) = ch;
2349 			pdp->rp_tlen++;
2350 			if (bp == pdp->rp_bufend - 1) {
2351 				len = bp - pdp->rp_buf;
2352 				rcs_growbuf(rfp);
2353 				bp = pdp->rp_buf + len;
2354 			}
2355 		}
2356 		*bp = '\0';
2357 
2358 		if (type != RCS_TOK_ERR && rcs_ignore_keys != 1) {
2359 			for (i = 0; i < RCS_NKEYS; i++) {
2360 				if (strcmp(rcs_keys[i].rk_str,
2361 				    pdp->rp_buf) == 0) {
2362 					type = rcs_keys[i].rk_id;
2363 					break;
2364 				}
2365 			}
2366 		}
2367 	} else if (ch == '@') {
2368 		/* we have a string */
2369 		type = RCS_TOK_STRING;
2370 		for (;;) {
2371 			ch = getc(pdp->rp_file);
2372 			if (ch == EOF) {
2373 				type = RCS_TOK_EOF;
2374 				break;
2375 			} else if (ch == '@') {
2376 				ch = getc(pdp->rp_file);
2377 				if (ch != '@') {
2378 					ungetc(ch, pdp->rp_file);
2379 					break;
2380 				}
2381 			} else if (ch == '\n')
2382 				pdp->rp_lines++;
2383 
2384 			*(bp++) = ch;
2385 			pdp->rp_tlen++;
2386 			if (bp == pdp->rp_bufend - 1) {
2387 				len = bp - pdp->rp_buf;
2388 				rcs_growbuf(rfp);
2389 				bp = pdp->rp_buf + len;
2390 			}
2391 		}
2392 
2393 		*bp = '\0';
2394 	} else if (isdigit(ch)) {
2395 		*(bp++) = ch;
2396 		last = ch;
2397 		type = RCS_TOK_NUM;
2398 
2399 		for (;;) {
2400 			ch = getc(pdp->rp_file);
2401 			if (ch == EOF) {
2402 				type = RCS_TOK_EOF;
2403 				break;
2404 			}
2405 			if (bp == pdp->rp_bufend)
2406 				break;
2407 			if (!isdigit(ch) && ch != '.') {
2408 				ungetc(ch, pdp->rp_file);
2409 				break;
2410 			}
2411 
2412 			if (last == '.' && ch == '.') {
2413 				type = RCS_TOK_ERR;
2414 				break;
2415 			}
2416 			last = ch;
2417 			*(bp++) = ch;
2418 			pdp->rp_tlen++;
2419 		}
2420 		*bp = '\0';
2421 	}
2422 
2423 	return (type);
2424 }
2425 
2426 /*
2427  * rcs_pushtok()
2428  *
2429  * Push a token back in the parser's token buffer.
2430  */
2431 static int
2432 rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
2433 {
2434 	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2435 
2436 	if (pdp->rp_pttype != RCS_TOK_ERR)
2437 		return (-1);
2438 
2439 	pdp->rp_pttype = type;
2440 	if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >=
2441 	    sizeof(pdp->rp_ptok))
2442 		fatal("rcs_pushtok: strlcpy");
2443 	return (0);
2444 }
2445 
2446 
2447 /*
2448  * rcs_growbuf()
2449  *
2450  * Attempt to grow the internal parse buffer for the RCS file <rf> by
2451  * RCS_BUFEXTSIZE.
2452  * In case of failure, the original buffer is left unmodified.
2453  */
2454 static void
2455 rcs_growbuf(RCSFILE *rf)
2456 {
2457 	struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2458 
2459 	pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
2460 	    pdp->rp_blen + RCS_BUFEXTSIZE);
2461 	pdp->rp_blen += RCS_BUFEXTSIZE;
2462 	pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
2463 }
2464 
2465 /*
2466  * rcs_strprint()
2467  *
2468  * Output an RCS string <str> of size <slen> to the stream <stream>.  Any
2469  * '@' characters are escaped.  Otherwise, the string can contain arbitrary
2470  * binary data.
2471  */
2472 static void
2473 rcs_strprint(const u_char *str, size_t slen, FILE *stream)
2474 {
2475 	const u_char *ap, *ep, *sp;
2476 
2477 	if (slen == 0)
2478 		return;
2479 
2480 	ep = str + slen - 1;
2481 
2482 	for (sp = str; sp <= ep;)  {
2483 		ap = memchr(sp, '@', ep - sp);
2484 		if (ap == NULL)
2485 			ap = ep;
2486 		(void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
2487 
2488 		if (*ap == '@')
2489 			putc('@', stream);
2490 		sp = ap + 1;
2491 	}
2492 }
2493 
2494 /*
2495  * rcs_deltatext_set()
2496  *
2497  * Set deltatext for <rev> in RCS file <rfp> to <dtext>
2498  * Returns -1 on error, 0 on success.
2499  */
2500 int
2501 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
2502 {
2503 	size_t len;
2504 	u_char *dtext;
2505 	struct rcs_delta *rdp;
2506 
2507 	/* Write operations require full parsing */
2508 	rcs_parse_deltatexts(rfp, NULL);
2509 
2510 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2511 		return (-1);
2512 
2513 	if (rdp->rd_text != NULL)
2514 		xfree(rdp->rd_text);
2515 
2516 	len = cvs_buf_len(bp);
2517 	dtext = cvs_buf_release(bp);
2518 	bp = NULL;
2519 
2520 	if (len != 0) {
2521 		rdp->rd_text = xmalloc(len);
2522 		rdp->rd_tlen = len;
2523 		(void)memcpy(rdp->rd_text, dtext, len);
2524 	} else {
2525 		rdp->rd_text = NULL;
2526 		rdp->rd_tlen = 0;
2527 	}
2528 
2529 	if (dtext != NULL)
2530 		xfree(dtext);
2531 
2532 	return (0);
2533 }
2534 
2535 /*
2536  * rcs_rev_setlog()
2537  *
2538  * Sets the log message of revision <rev> to <logtext>
2539  */
2540 int
2541 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
2542 {
2543 	struct rcs_delta *rdp;
2544 	char buf[CVS_REV_BUFSZ];
2545 
2546 	rcsnum_tostr(rev, buf, sizeof(buf));
2547 
2548 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2549 		return (-1);
2550 
2551 	if (rdp->rd_log != NULL)
2552 		xfree(rdp->rd_log);
2553 
2554 	rdp->rd_log = xstrdup(logtext);
2555 	rfp->rf_flags &= ~RCS_SYNCED;
2556 	return (0);
2557 }
2558 /*
2559  * rcs_rev_getdate()
2560  *
2561  * Get the date corresponding to a given revision.
2562  * Returns the date on success, -1 on failure.
2563  */
2564 time_t
2565 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
2566 {
2567 	struct rcs_delta *rdp;
2568 
2569 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2570 		return (-1);
2571 
2572 	return (timegm(&rdp->rd_date));
2573 }
2574 
2575 /*
2576  * rcs_state_set()
2577  *
2578  * Sets the state of revision <rev> to <state>
2579  * NOTE: default state is 'Exp'. States may not contain spaces.
2580  *
2581  * Returns -1 on failure, 0 on success.
2582  */
2583 int
2584 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
2585 {
2586 	struct rcs_delta *rdp;
2587 
2588 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2589 		return (-1);
2590 
2591 	if (rdp->rd_state != NULL)
2592 		xfree(rdp->rd_state);
2593 
2594 	rdp->rd_state = xstrdup(state);
2595 
2596 	rfp->rf_flags &= ~RCS_SYNCED;
2597 
2598 	return (0);
2599 }
2600 
2601 /*
2602  * rcs_state_check()
2603  *
2604  * Check if string <state> is valid.
2605  *
2606  * Returns 0 if the string is valid, -1 otherwise.
2607  */
2608 int
2609 rcs_state_check(const char *state)
2610 {
2611 	if (strchr(state, ' ') != NULL)
2612 		return (-1);
2613 
2614 	return (0);
2615 }
2616 
2617 /*
2618  * rcs_state_get()
2619  *
2620  * Get the state for a given revision of a specified RCSFILE.
2621  *
2622  * Returns NULL on failure.
2623  */
2624 const char *
2625 rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
2626 {
2627 	struct rcs_delta *rdp;
2628 
2629 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2630 		return (NULL);
2631 
2632 	return (rdp->rd_state);
2633 }
2634 
2635 /* rcs_get_revision() */
2636 static RCSNUM *
2637 rcs_get_revision(const char *revstr, RCSFILE *rfp)
2638 {
2639 	RCSNUM *rev, *brev, *frev;
2640 	struct rcs_branch *brp;
2641 	struct rcs_delta *rdp;
2642 	size_t i;
2643 
2644 	rdp = NULL;
2645 
2646 	if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
2647 		if (rfp->rf_head == NULL)
2648 			return NULL;
2649 
2650 		frev = rcsnum_alloc();
2651 		rcsnum_cpy(rfp->rf_head, frev, 0);
2652 		return (frev);
2653 	}
2654 
2655 	/* Possibly we could be passed a version number */
2656 	if ((rev = rcsnum_parse(revstr)) != NULL) {
2657 		/* Do not return if it is not in RCS file */
2658 		if ((rdp = rcs_findrev(rfp, rev)) != NULL) {
2659 			frev = rcsnum_alloc();
2660 			rcsnum_cpy(rev, frev, 0);
2661 			return (frev);
2662 		}
2663 	} else {
2664 		/* More likely we will be passed a symbol */
2665 		rev = rcs_sym_getrev(rfp, revstr);
2666 	}
2667 
2668 	if (rev == NULL)
2669 		return (NULL);
2670 
2671 	/*
2672 	 * If it was not a branch, thats ok the symbolic
2673 	 * name refered to a revision, so return the resolved
2674 	 * revision for the given name. */
2675 	if (!RCSNUM_ISBRANCH(rev)) {
2676 		/* Sanity check: The first two elements of any
2677 		 * revision (be it on a branch or on trunk) cannot
2678 		 * be greater than HEAD.
2679 		 *
2680 		 * XXX: To avoid comparing to uninitialized memory,
2681 		 * the minimum of both revision lengths is taken
2682 		 * instead of just 2.
2683 		 */
2684 		if (rfp->rf_head == NULL)
2685 			return NULL;
2686 
2687 		if (rcsnum_cmp(rev, rfp->rf_head,
2688 		    MIN(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
2689 			rcsnum_free(rev);
2690 			return NULL;
2691 		}
2692 		return (rev);
2693 	}
2694 
2695 	brev = rcsnum_alloc();
2696 	rcsnum_cpy(rev, brev, rev->rn_len - 1);
2697 
2698 	if ((rdp = rcs_findrev(rfp, brev)) == NULL)
2699 		fatal("rcs_get_revision: tag `%s' does not exist", revstr);
2700 	rcsnum_free(brev);
2701 
2702 	TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2703 		for (i = 0; i < rev->rn_len; i++)
2704 			if (brp->rb_num->rn_id[i] != rev->rn_id[i])
2705 				break;
2706 		if (i != rev->rn_len)
2707 			continue;
2708 		break;
2709 	}
2710 
2711 	rcsnum_free(rev);
2712 	frev = rcsnum_alloc();
2713 	if (brp == NULL) {
2714 		rcsnum_cpy(rdp->rd_num, frev, 0);
2715 		return (frev);
2716 	} else {
2717 		/* Fetch the delta with the correct branch num */
2718 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2719 			fatal("rcs_get_revision: could not fetch branch "
2720 			    "delta");
2721 		rcsnum_cpy(rdp->rd_num, frev, 0);
2722 		return (frev);
2723 	}
2724 }
2725 
2726 /*
2727  * rcs_rev_getlines()
2728  *
2729  * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
2730  * return it as a pointer to a struct cvs_lines.
2731  */
2732 struct cvs_lines *
2733 rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2734 {
2735 	size_t plen;
2736 	int annotate, done, i, nextroot;
2737 	RCSNUM *tnum, *bnum;
2738 	struct rcs_branch *brp;
2739 	struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
2740 	u_char *patch;
2741 	struct cvs_line *line, *nline;
2742 	struct cvs_lines *dlines, *plines;
2743 
2744 	if (rfp->rf_head == NULL ||
2745 	    (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
2746 		fatal("rcs_rev_getlines: no HEAD revision");
2747 
2748 	tnum = frev;
2749 	rcs_parse_deltatexts(rfp, hrdp->rd_num);
2750 
2751 	/* revision on branch, get the branch root */
2752 	nextroot = 2;
2753 	if (RCSNUM_ISBRANCHREV(tnum)) {
2754 		bnum = rcsnum_alloc();
2755 		rcsnum_cpy(tnum, bnum, nextroot);
2756 	} else {
2757 		bnum = tnum;
2758 	}
2759 
2760 	if (alines != NULL) {
2761 		/* start with annotate first at requested revision */
2762 		annotate = ANNOTATE_LATER;
2763 		*alines = NULL;
2764 	} else
2765 		annotate = ANNOTATE_NEVER;
2766 
2767 	dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
2768 
2769 	done = 0;
2770 
2771 	rdp = hrdp;
2772 	if (!rcsnum_differ(rdp->rd_num, bnum)) {
2773 		if (annotate == ANNOTATE_LATER) {
2774 			/* found requested revision for annotate */
2775 			i = 0;
2776 			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2777 				line->l_lineno_orig = line->l_lineno;
2778 				i++;
2779 			}
2780 
2781 			*alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2782 			(*alines)[i] = NULL;
2783 			annotate = ANNOTATE_NOW;
2784 
2785 			/* annotate down to 1.1 from where we are */
2786 			if (bnum == tnum)
2787 				bnum = rcsnum_alloc();
2788 			bnum = rcsnum_parse("1.1");
2789 			if (!rcsnum_differ(rdp->rd_num, bnum)) {
2790 				goto next;
2791 			}
2792 		} else
2793 			goto next;
2794 	}
2795 
2796 	prdp = hrdp;
2797 	if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
2798 		goto done;
2799 
2800 again:
2801 	for (;;) {
2802 		if (rdp->rd_next->rn_len != 0) {
2803 			trdp = rcs_findrev(rfp, rdp->rd_next);
2804 			if (trdp == NULL)
2805 				fatal("failed to grab next revision");
2806 		}
2807 
2808 		if (rdp->rd_tlen == 0) {
2809 			rcs_parse_deltatexts(rfp, rdp->rd_num);
2810 			if (rdp->rd_tlen == 0) {
2811 				if (!rcsnum_differ(rdp->rd_num, bnum))
2812 					break;
2813 				rdp = trdp;
2814 				continue;
2815 			}
2816 		}
2817 
2818 		plen = rdp->rd_tlen;
2819 		patch = rdp->rd_text;
2820 		plines = cvs_splitlines(patch, plen);
2821 		if (annotate == ANNOTATE_NOW)
2822 			rcs_patch_lines(dlines, plines, *alines, prdp);
2823 		else
2824 			rcs_patch_lines(dlines, plines, NULL, NULL);
2825 		cvs_freelines(plines);
2826 
2827 		if (!rcsnum_differ(rdp->rd_num, bnum)) {
2828 			if (annotate != ANNOTATE_LATER)
2829 				break;
2830 
2831 			/* found requested revision for annotate */
2832 			i = 0;
2833 			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2834 				line->l_lineno_orig = line->l_lineno;
2835 				i++;
2836 			}
2837 
2838 			*alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2839 			(*alines)[i] = NULL;
2840 			annotate = ANNOTATE_NOW;
2841 
2842 			/* annotate down to 1.1 from where we are */
2843 			if (bnum == tnum)
2844 				bnum = rcsnum_alloc();
2845 			bnum = rcsnum_parse("1.1");
2846 
2847 			if (!rcsnum_differ(rdp->rd_num, bnum))
2848 				break;
2849 		}
2850 
2851 		prdp = rdp;
2852 		rdp = trdp;
2853 	}
2854 
2855 next:
2856 	if (!rcsnum_differ(rdp->rd_num, frev))
2857 		done = 1;
2858 
2859 	if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
2860 		nextroot += 2;
2861 		rcsnum_cpy(frev, bnum, nextroot);
2862 
2863 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2864 			for (i = 0; i < nextroot - 1; i++)
2865 				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2866 					break;
2867 			if (i == nextroot - 1)
2868 				break;
2869 		}
2870 
2871 		if (brp == NULL) {
2872 			if (annotate != ANNOTATE_NEVER) {
2873 				if (*alines != NULL)
2874 					xfree(*alines);
2875 				*alines = NULL;
2876 				cvs_freelines(dlines);
2877 				if (bnum != tnum)
2878 					rcsnum_free(bnum);
2879 				return (NULL);
2880 			}
2881 			fatal("expected branch not found on branch list");
2882 		}
2883 
2884 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2885 			fatal("rcs_rev_getlines: failed to get delta for target rev");
2886 
2887 		goto again;
2888 	}
2889 done:
2890 	/* put remaining lines into annotate buffer */
2891 	if (annotate == ANNOTATE_NOW) {
2892 		for (line = TAILQ_FIRST(&(dlines->l_lines));
2893 		    line != NULL; line = nline) {
2894 			nline = TAILQ_NEXT(line, l_list);
2895 			TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
2896 			if (line->l_line == NULL) {
2897 				xfree(line);
2898 				continue;
2899 			}
2900 
2901 			line->l_delta = rdp;
2902 			(*alines)[line->l_lineno_orig - 1] = line;
2903 		}
2904 
2905 		cvs_freelines(dlines);
2906 		dlines = NULL;
2907 	}
2908 
2909 	if (bnum != tnum)
2910 		rcsnum_free(bnum);
2911 
2912 	return (dlines);
2913 }
2914 
2915 void
2916 rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2917 {
2918 	size_t plen;
2919 	int i, nextroot;
2920 	RCSNUM *bnum;
2921 	struct rcs_branch *brp;
2922 	struct rcs_delta *rdp, *trdp;
2923 	u_char *patch;
2924 	struct cvs_line *line;
2925 	struct cvs_lines *dlines, *plines;
2926 
2927 	if (!RCSNUM_ISBRANCHREV(frev))
2928 		fatal("rcs_annotate_getlines: branch revision expected");
2929 
2930 	/* revision on branch, get the branch root */
2931 	nextroot = 2;
2932 	bnum = rcsnum_alloc();
2933 	rcsnum_cpy(frev, bnum, nextroot);
2934 
2935 	/*
2936 	 * Going from HEAD to 1.1 enables the use of an array, which is
2937 	 * much faster. Unfortunately this is not possible with branch
2938 	 * revisions, so copy over our alines (array) into dlines (tailq).
2939 	 */
2940 	dlines = xcalloc(1, sizeof(*dlines));
2941 	TAILQ_INIT(&(dlines->l_lines));
2942 	line = xcalloc(1, sizeof(*line));
2943 	TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2944 
2945 	for (i = 0; (*alines)[i] != NULL; i++) {
2946 		line = (*alines)[i];
2947 		line->l_lineno = i + 1;
2948 		TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2949 	}
2950 
2951 	rdp = rcs_findrev(rfp, bnum);
2952 	if (rdp == NULL)
2953 		fatal("failed to grab branch root revision");
2954 
2955 	do {
2956 		nextroot += 2;
2957 		rcsnum_cpy(frev, bnum, nextroot);
2958 
2959 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2960 			for (i = 0; i < nextroot - 1; i++)
2961 				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2962 					break;
2963 			if (i == nextroot - 1)
2964 				break;
2965 		}
2966 
2967 		if (brp == NULL)
2968 			fatal("expected branch not found on branch list");
2969 
2970 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2971 			fatal("failed to get delta for target rev");
2972 
2973 		for (;;) {
2974 			if (rdp->rd_next->rn_len != 0) {
2975 				trdp = rcs_findrev(rfp, rdp->rd_next);
2976 				if (trdp == NULL)
2977 					fatal("failed to grab next revision");
2978 			}
2979 
2980 			if (rdp->rd_tlen == 0) {
2981 				rcs_parse_deltatexts(rfp, rdp->rd_num);
2982 				if (rdp->rd_tlen == 0) {
2983 					if (!rcsnum_differ(rdp->rd_num, bnum))
2984 						break;
2985 					rdp = trdp;
2986 					continue;
2987 				}
2988 			}
2989 
2990 			plen = rdp->rd_tlen;
2991 			patch = rdp->rd_text;
2992 			plines = cvs_splitlines(patch, plen);
2993 			rcs_patch_lines(dlines, plines, NULL, rdp);
2994 			cvs_freelines(plines);
2995 
2996 			if (!rcsnum_differ(rdp->rd_num, bnum))
2997 				break;
2998 
2999 			rdp = trdp;
3000 		}
3001 	} while (rcsnum_differ(rdp->rd_num, frev));
3002 
3003 	if (bnum != frev)
3004 		rcsnum_free(bnum);
3005 
3006 	/*
3007 	 * All lines have been parsed, now they must be copied over
3008 	 * into alines (array) again.
3009 	 */
3010 	xfree(*alines);
3011 
3012 	i = 0;
3013 	TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3014 		if (line->l_line != NULL)
3015 			i++;
3016 	}
3017 	*alines = xcalloc(i + 1, sizeof(struct cvs_line *));
3018 	(*alines)[i] = NULL;
3019 
3020 	i = 0;
3021 	TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3022 		if (line->l_line != NULL)
3023 			(*alines)[i++] = line;
3024 	}
3025 }
3026 
3027 /*
3028  * rcs_rev_getbuf()
3029  *
3030  * XXX: This is really really slow and should be avoided if at all possible!
3031  *
3032  * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
3033  * return it as a BUF pointer.
3034  */
3035 BUF *
3036 rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
3037 {
3038 	int expmode, expand;
3039 	struct rcs_delta *rdp;
3040 	struct cvs_lines *lines;
3041 	struct cvs_line *lp, *nlp;
3042 	BUF *bp;
3043 
3044 	expand = 0;
3045 	lines = rcs_rev_getlines(rfp, rev, NULL);
3046 	bp = cvs_buf_alloc(1024 * 16);
3047 
3048 	if (!(mode & RCS_KWEXP_NONE)) {
3049 		if (rfp->rf_expand != NULL)
3050 			expmode = rcs_kwexp_get(rfp);
3051 		else
3052 			expmode = RCS_KWEXP_DEFAULT;
3053 
3054 		if (!(expmode & RCS_KWEXP_NONE)) {
3055 			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3056 				fatal("could not fetch revision");
3057 			expand = 1;
3058 		}
3059 	}
3060 
3061 	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
3062 		nlp = TAILQ_NEXT(lp, l_list);
3063 
3064 		if (lp->l_line == NULL) {
3065 			lp = nlp;
3066 			continue;
3067 		}
3068 
3069 		if (expand)
3070 			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
3071 
3072 		do {
3073 			cvs_buf_append(bp, lp->l_line, lp->l_len);
3074 		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
3075 	}
3076 
3077 	cvs_freelines(lines);
3078 
3079 	return (bp);
3080 }
3081 
3082 /*
3083  * rcs_rev_write_fd()
3084  *
3085  * Write the entire contents of revision <frev> from the rcsfile <rfp> to
3086  * file descriptor <fd>.
3087  */
3088 void
3089 rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
3090 {
3091 	int fd;
3092 	FILE *fp;
3093 	size_t ret;
3094 	int expmode, expand;
3095 	struct rcs_delta *rdp;
3096 	struct cvs_lines *lines;
3097 	struct cvs_line *lp, *nlp;
3098 	extern int print_stdout;
3099 
3100 	expand = 0;
3101 	lines = rcs_rev_getlines(rfp, rev, NULL);
3102 
3103 	if (!(mode & RCS_KWEXP_NONE)) {
3104 		if (rfp->rf_expand != NULL)
3105 			expmode = rcs_kwexp_get(rfp);
3106 		else
3107 			expmode = RCS_KWEXP_DEFAULT;
3108 
3109 		if (!(expmode & RCS_KWEXP_NONE)) {
3110 			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3111 				fatal("could not fetch revision");
3112 			expand = 1;
3113 		}
3114 	}
3115 
3116 	fd = dup(_fd);
3117 	if (fd == -1)
3118 		fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
3119 
3120 	if ((fp = fdopen(fd, "w")) == NULL)
3121 		fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
3122 
3123 	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
3124 		nlp = TAILQ_NEXT(lp, l_list);
3125 
3126 		if (lp->l_line == NULL) {
3127 			lp = nlp;
3128 			continue;
3129 		}
3130 
3131 		if (expand)
3132 			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
3133 
3134 		do {
3135 			/*
3136 			 * Solely for the checkout and update -p options.
3137 			 */
3138 			if (cvs_server_active == 1 &&
3139 			    (cvs_cmdop == CVS_OP_CHECKOUT ||
3140 			    cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
3141 				ret = fwrite("M ", 1, 2, fp);
3142 				if (ret != 2)
3143 					fatal("rcs_rev_write_fd: %s",
3144 					    strerror(errno));
3145 			}
3146 
3147 			ret = fwrite(lp->l_line, 1, lp->l_len, fp);
3148 			if (ret != lp->l_len)
3149 				fatal("rcs_rev_write_fd: %s", strerror(errno));
3150 		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
3151 	}
3152 
3153 	cvs_freelines(lines);
3154 	(void)fclose(fp);
3155 }
3156 
3157 /*
3158  * rcs_rev_write_stmp()
3159  *
3160  * Write the contents of the rev <rev> to a temporary file whose path is
3161  * specified using <template> (see mkstemp(3)). NB. This function will modify
3162  * <template>, as per mkstemp.
3163  */
3164 int
3165 rcs_rev_write_stmp(RCSFILE *rfp,  RCSNUM *rev, char *template, int mode)
3166 {
3167 	int fd;
3168 
3169 	if ((fd = mkstemp(template)) == -1)
3170 		fatal("mkstemp: `%s': %s", template, strerror(errno));
3171 
3172 	cvs_worklist_add(template, &temp_files);
3173 	rcs_rev_write_fd(rfp, rev, fd, mode);
3174 
3175 	if (lseek(fd, 0, SEEK_SET) < 0)
3176 		fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
3177 
3178 	return (fd);
3179 }
3180 
3181 static void
3182 rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines,
3183     struct cvs_line *line, int mode)
3184 {
3185 	BUF *tmpbuf;
3186 	int kwtype;
3187 	u_int j, found;
3188 	const u_char *c, *start, *fin, *end;
3189 	char *kwstr;
3190 	char expbuf[256], buf[256];
3191 	char *fmt;
3192 	size_t clen, kwlen, len, tlen;
3193 
3194 	kwtype = 0;
3195 	kwstr = NULL;
3196 
3197 	if (mode & RCS_KWEXP_OLD)
3198 		return;
3199 
3200 	len = line->l_len;
3201 	if (len == 0)
3202 		return;
3203 
3204 	c = line->l_line;
3205 	found = 0;
3206 	/* Final character in buffer. */
3207 	fin = c + len - 1;
3208 
3209 	/*
3210 	 * Keyword formats:
3211 	 * $Keyword$
3212 	 * $Keyword: value$
3213 	 */
3214 	for (; c < fin; c++) {
3215 		if (*c != '$')
3216 			continue;
3217 
3218 		/* remember start of this possible keyword */
3219 		start = c;
3220 
3221 		/* first following character has to be alphanumeric */
3222 		c++;
3223 		if (!isalpha(*c)) {
3224 			c = start;
3225 			continue;
3226 		}
3227 
3228 		/* Number of characters between c and fin, inclusive. */
3229 		clen = fin - c + 1;
3230 
3231 		/* look for any matching keywords */
3232 		found = 0;
3233 		for (j = 0; j < RCS_NKWORDS; j++) {
3234 			kwlen = strlen(rcs_expkw[j].kw_str);
3235 			/*
3236 			 * kwlen must be less than clen since clen
3237 			 * includes either a terminating `$' or a `:'.
3238 			 */
3239 			if (kwlen < clen &&
3240 			    memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
3241 			    (c[kwlen] == '$' || c[kwlen] == ':')) {
3242 				found = 1;
3243 				kwstr = rcs_expkw[j].kw_str;
3244 				kwtype = rcs_expkw[j].kw_type;
3245 				c += kwlen;
3246 				break;
3247 			}
3248 		}
3249 
3250 		if (found == 0 && cvs_tagname != NULL) {
3251 			kwlen = strlen(cvs_tagname);
3252 			if (kwlen < clen &&
3253 			    memcmp(c, cvs_tagname, kwlen) == 0 &&
3254 			    (c[kwlen] == '$' || c[kwlen] == ':')) {
3255 				found = 1;
3256 				kwstr = cvs_tagname;
3257 				kwtype = RCS_KW_ID;
3258 				c += kwlen;
3259 			}
3260 		}
3261 
3262 		/* unknown keyword, continue looking */
3263 		if (found == 0) {
3264 			c = start;
3265 			continue;
3266 		}
3267 
3268 		/*
3269 		 * if the next character was ':' we need to look for
3270 		 * an '$' before the end of the line to be sure it is
3271 		 * in fact a keyword.
3272 		 */
3273 		if (*c == ':') {
3274 			for (; c <= fin; ++c) {
3275 				if (*c == '$' || *c == '\n')
3276 					break;
3277 			}
3278 
3279 			if (*c != '$') {
3280 				c = start;
3281 				continue;
3282 			}
3283 		}
3284 		end = c + 1;
3285 
3286 		/* start constructing the expansion */
3287 		expbuf[0] = '\0';
3288 
3289 		if (mode & RCS_KWEXP_NAME) {
3290 			if (strlcat(expbuf, "$", sizeof(expbuf)) >=
3291 			    sizeof(expbuf) || strlcat(expbuf, kwstr,
3292 			    sizeof(expbuf)) >= sizeof(expbuf))
3293 				fatal("rcs_kwexp_line: truncated");
3294 			if ((mode & RCS_KWEXP_VAL) &&
3295 			    strlcat(expbuf, ": ", sizeof(expbuf)) >=
3296 			    sizeof(expbuf))
3297 				fatal("rcs_kwexp_line: truncated");
3298 		}
3299 
3300 		/*
3301 		 * order matters because of RCS_KW_ID and
3302 		 * RCS_KW_HEADER here
3303 		 */
3304 		if (mode & RCS_KWEXP_VAL) {
3305 			if (kwtype & RCS_KW_RCSFILE) {
3306 				if (!(kwtype & RCS_KW_FULLPATH))
3307 					(void)strlcat(expbuf, basename(rcsfile),
3308 					    sizeof(expbuf));
3309 				else
3310 					(void)strlcat(expbuf, rcsfile,
3311 					    sizeof(expbuf));
3312 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3313 				    sizeof(expbuf))
3314 					fatal("rcs_kwexp_line: truncated");
3315 			}
3316 
3317 			if (kwtype & RCS_KW_REVISION) {
3318 				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3319 				if (strlcat(buf, " ", sizeof(buf)) >=
3320 				    sizeof(buf) || strlcat(expbuf, buf,
3321 				    sizeof(expbuf)) >= sizeof(buf))
3322 					fatal("rcs_kwexp_line: truncated");
3323 			}
3324 
3325 			if (kwtype & RCS_KW_DATE) {
3326 				fmt = "%Y/%m/%d %H:%M:%S ";
3327 
3328 				if (strftime(buf, sizeof(buf), fmt,
3329 				    &rdp->rd_date) == 0)
3330 					fatal("rcs_kwexp_line: strftime "
3331 					    "failure");
3332 				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3333 				    sizeof(expbuf))
3334 					fatal("rcs_kwexp_line: string "
3335 					    "truncated");
3336 			}
3337 
3338 			if (kwtype & RCS_KW_MDOCDATE) {
3339 				/*
3340 				 * Do not prepend ' ' for a single
3341 				 * digit, %e would do so and there is
3342 				 * no better format for strftime().
3343 				 */
3344 				if (rdp->rd_date.tm_mday < 10)
3345 					fmt = "%B%e %Y ";
3346 				else
3347 					fmt = "%B %e %Y ";
3348 
3349 				if (strftime(buf, sizeof(buf), fmt,
3350 				    &rdp->rd_date) == 0)
3351 					fatal("rcs_kwexp_line: strftime "
3352 					    "failure");
3353 				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3354 				    sizeof(expbuf))
3355 					fatal("rcs_kwexp_line: string "
3356 					    "truncated");
3357 			}
3358 
3359 			if (kwtype & RCS_KW_AUTHOR) {
3360 				if (strlcat(expbuf, rdp->rd_author,
3361 				    sizeof(expbuf)) >= sizeof(expbuf) ||
3362 				    strlcat(expbuf, " ", sizeof(expbuf)) >=
3363 				    sizeof(expbuf))
3364 					fatal("rcs_kwexp_line: string "
3365 					    "truncated");
3366 			}
3367 
3368 			if (kwtype & RCS_KW_STATE) {
3369 				if (strlcat(expbuf, rdp->rd_state,
3370 				    sizeof(expbuf)) >= sizeof(expbuf) ||
3371 				    strlcat(expbuf, " ", sizeof(expbuf)) >=
3372 				    sizeof(expbuf))
3373 					fatal("rcs_kwexp_line: string "
3374 					    "truncated");
3375 			}
3376 
3377 			/* order does not matter anymore below */
3378 			if (kwtype & RCS_KW_LOG) {
3379 				char linebuf[256];
3380 				struct cvs_line *cur, *lp;
3381 				char *logp, *l_line, *prefix, *q, *sprefix;
3382 				size_t i;
3383 
3384 				/* Log line */
3385 				if (!(kwtype & RCS_KW_FULLPATH))
3386 					(void)strlcat(expbuf,
3387 					    basename(rcsfile), sizeof(expbuf));
3388 				else
3389 					(void)strlcat(expbuf, rcsfile,
3390 					    sizeof(expbuf));
3391 
3392 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3393 				    sizeof(expbuf))
3394 					fatal("rcs_kwexp_line: string "
3395 					    "truncated");
3396 
3397 				cur = line;
3398 
3399 				/* copy rdp->rd_log for strsep */
3400 				logp = xstrdup(rdp->rd_log);
3401 
3402 				/* copy our prefix for later processing */
3403 				prefix = xmalloc(start - line->l_line + 1);
3404 				memcpy(prefix, line->l_line,
3405 				    start - line->l_line);
3406 				prefix[start - line->l_line] = '\0';
3407 
3408 				/* copy also prefix without trailing blanks. */
3409 				sprefix = xstrdup(prefix);
3410 				for (i = strlen(sprefix); i > 0 &&
3411 				    sprefix[i - 1] == ' '; i--)
3412 					sprefix[i - 1] = '\0';
3413 
3414 				/* new line: revision + date + author */
3415 				linebuf[0] = '\0';
3416 				if (strlcat(linebuf, "Revision ",
3417 				    sizeof(linebuf)) >= sizeof(linebuf))
3418 					fatal("rcs_kwexp_line: truncated");
3419 				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3420 				if (strlcat(linebuf, buf, sizeof(linebuf))
3421 				    >= sizeof(buf))
3422 					fatal("rcs_kwexp_line: truncated");
3423 				fmt = "  %Y/%m/%d %H:%M:%S  ";
3424 				if (strftime(buf, sizeof(buf), fmt,
3425 				    &rdp->rd_date) == 0)
3426 					fatal("rcs_kwexp_line: strftime "
3427 					    "failure");
3428 				if (strlcat(linebuf, buf, sizeof(linebuf))
3429 				    >= sizeof(linebuf))
3430 					fatal("rcs_kwexp_line: string "
3431 					    "truncated");
3432 				if (strlcat(linebuf, rdp->rd_author,
3433 				    sizeof(linebuf)) >= sizeof(linebuf))
3434 					fatal("rcs_kwexp_line: string "
3435 					    "truncated");
3436 
3437 				lp = xcalloc(1, sizeof(*lp));
3438 				xasprintf(&(lp->l_line), "%s%s\n",
3439 				    prefix, linebuf);
3440 				lp->l_len = strlen(lp->l_line);
3441 				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3442 				    l_list);
3443 				cur = lp;
3444 
3445 				/* Log message */
3446 				q = logp;
3447 				while ((l_line = strsep(&q, "\n")) != NULL &&
3448 				    q != NULL) {
3449 					lp = xcalloc(1, sizeof(*lp));
3450 
3451 					if (l_line[0] == '\0') {
3452 						xasprintf(&(lp->l_line), "%s\n",
3453 						    sprefix);
3454 					} else {
3455 						xasprintf(&(lp->l_line),
3456 						    "%s%s\n", prefix, l_line);
3457 					}
3458 
3459 					lp->l_len = strlen(lp->l_line);
3460 					TAILQ_INSERT_AFTER(&(lines->l_lines),
3461 					    cur, lp, l_list);
3462 					cur = lp;
3463 				}
3464 				xfree(logp);
3465 
3466 				/*
3467 				 * This is just another hairy mess, but it must
3468 				 * be done: All characters behind Log will be
3469 				 * written in a new line next to log messages.
3470 				 * But that's not enough, we have to strip all
3471 				 * trailing whitespaces of our prefix.
3472 				 */
3473 				lp = xcalloc(1, sizeof(*lp));
3474 				lp->l_line = xcalloc(strlen(sprefix) +
3475 				    line->l_line + line->l_len - end + 1, 1);
3476 				strlcpy(lp->l_line, sprefix,
3477 				    strlen(sprefix) + 1);
3478 				memcpy(lp->l_line + strlen(sprefix),
3479 				    end, line->l_line + line->l_len - end);
3480 				lp->l_len = strlen(lp->l_line);
3481 				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3482 				    l_list);
3483 				cur = lp;
3484 
3485 				end = line->l_line + line->l_len - 1;
3486 
3487 				xfree(prefix);
3488 				xfree(sprefix);
3489 
3490 			}
3491 
3492 			if (kwtype & RCS_KW_SOURCE) {
3493 				if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
3494 				    sizeof(expbuf) || strlcat(expbuf, " ",
3495 				    sizeof(expbuf)) >= sizeof(expbuf))
3496 					fatal("rcs_kwexp_line: string "
3497 					    "truncated");
3498 			}
3499 
3500 			if (kwtype & RCS_KW_NAME)
3501 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3502 				    sizeof(expbuf))
3503 					fatal("rcs_kwexp_line: string "
3504 					    "truncated");
3505 
3506 			if (kwtype & RCS_KW_LOCKER)
3507 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3508 				    sizeof(expbuf))
3509 					fatal("rcs_kwexp_line: string "
3510 					    "truncated");
3511 		}
3512 
3513 		/* end the expansion */
3514 		if (mode & RCS_KWEXP_NAME)
3515 			if (strlcat(expbuf, "$",
3516 			    sizeof(expbuf)) >= sizeof(expbuf))
3517 				fatal("rcs_kwexp_line: truncated");
3518 
3519 		/* Concatenate everything together. */
3520 		tmpbuf = cvs_buf_alloc(len + strlen(expbuf));
3521 		/* Append everything before keyword. */
3522 		cvs_buf_append(tmpbuf, line->l_line,
3523 		    start - line->l_line);
3524 		/* Append keyword. */
3525 		cvs_buf_puts(tmpbuf, expbuf);
3526 		/* Point c to end of keyword. */
3527 		tlen = cvs_buf_len(tmpbuf) - 1;
3528 		/* Append everything after keyword. */
3529 		cvs_buf_append(tmpbuf, end,
3530 		    line->l_line + line->l_len - end);
3531 		c = cvs_buf_get(tmpbuf) + tlen;
3532 		/* Point fin to end of data. */
3533 		fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1;
3534 		/* Recalculate new length. */
3535 		len = cvs_buf_len(tmpbuf);
3536 
3537 		/* tmpbuf is now ready, convert to string */
3538 		if (line->l_needsfree)
3539 			xfree(line->l_line);
3540 		line->l_len = len;
3541 		line->l_line = cvs_buf_release(tmpbuf);
3542 		line->l_needsfree = 1;
3543 	}
3544 }
3545 
3546 /* rcs_translate_tag() */
3547 RCSNUM *
3548 rcs_translate_tag(const char *revstr, RCSFILE *rfp)
3549 {
3550 	int follow;
3551 	time_t deltatime;
3552 	char branch[CVS_REV_BUFSZ];
3553 	RCSNUM *brev, *frev, *rev, *rrev;
3554 	struct rcs_delta *rdp, *trdp;
3555 	time_t cdate;
3556 
3557 	brev = frev = rrev = NULL;
3558 
3559 	if (revstr == NULL) {
3560 		if (rfp->rf_branch != NULL) {
3561 			rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
3562 			revstr = branch;
3563 		} else {
3564 			revstr = RCS_HEAD_BRANCH;
3565 		}
3566 	}
3567 
3568 	if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
3569 		return (NULL);
3570 
3571 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3572 		return (NULL);
3573 
3574 	/* let's see if we must follow a branch */
3575 	if (!strcmp(revstr, RCS_HEAD_BRANCH))
3576 		follow = 1;
3577 	else {
3578 		frev = rcs_sym_getrev(rfp, revstr);
3579 		if (frev == NULL)
3580 			frev = rrev = rcsnum_parse(revstr);
3581 
3582 		brev = rcsnum_alloc();
3583 		rcsnum_cpy(rev, brev, rev->rn_len - 1);
3584 
3585 		if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
3586 		    !rcsnum_cmp(frev, brev, 0)) {
3587 			follow = 1;
3588 		} else
3589 			follow = 0;
3590 
3591 		rcsnum_free(brev);
3592 		if (rrev != NULL)
3593 			rcsnum_free(rrev);
3594 	}
3595 
3596 	if (cvs_specified_date != -1)
3597 		cdate = cvs_specified_date;
3598 	else
3599 		cdate = cvs_directory_date;
3600 
3601 	if (cdate == -1) {
3602 		/* XXX */
3603 		if (rev->rn_len < 4 || !follow) {
3604 			return (rev);
3605 		}
3606 
3607 		/* Find the latest delta on that branch */
3608 		rcsnum_free(rev);
3609 		for (;;) {
3610 			if (rdp->rd_next->rn_len == 0)
3611 				break;
3612 			if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
3613 				fatal("rcs_translate_tag: could not fetch "
3614 				    "branch delta");
3615 		}
3616 
3617 		rev = rcsnum_alloc();
3618 		rcsnum_cpy(rdp->rd_num, rev, 0);
3619 		return (rev);
3620 	}
3621 
3622 	if (frev != NULL)
3623 		rcsnum_tostr(frev, branch, sizeof(branch));
3624 
3625 	if (frev != NULL) {
3626 		brev = rcsnum_revtobr(frev);
3627 		brev->rn_len = rev->rn_len - 1;
3628 	}
3629 
3630 	rcsnum_free(rev);
3631 
3632 	do {
3633 		deltatime = timelocal(&(rdp->rd_date));
3634 
3635 		if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
3636 			if (deltatime > cdate) {
3637 				trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
3638 				if (trdp == NULL)
3639 					trdp = rdp;
3640 
3641 				if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
3642 					return (NULL);
3643 
3644 				rev = rcsnum_alloc();
3645 				rcsnum_cpy(trdp->rd_num, rev, 0);
3646 				return (rev);
3647 			}
3648 
3649 			if (rdp->rd_next->rn_len == 0) {
3650 				rev = rcsnum_alloc();
3651 				rcsnum_cpy(rdp->rd_num, rev, 0);
3652 				return (rev);
3653 			}
3654 		} else {
3655 			if (deltatime < cdate) {
3656 				rev = rcsnum_alloc();
3657 				rcsnum_cpy(rdp->rd_num, rev, 0);
3658 				return (rev);
3659 			}
3660 		}
3661 
3662 		if (follow && rdp->rd_next->rn_len != 0) {
3663 			if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
3664 				break;
3665 
3666 			trdp = rcs_findrev(rfp, rdp->rd_next);
3667 			if (trdp == NULL)
3668 				fatal("failed to grab next revision");
3669 			rdp = trdp;
3670 		} else
3671 			follow = 0;
3672 	} while (follow);
3673 
3674 	return (NULL);
3675 }
3676