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