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