xref: /openbsd-src/usr.bin/cvs/rcs.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: rcs.c,v 1.313 2015/11/05 09:48:21 nicm 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 #include "rcsparse.h"
42 
43 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
44 
45 #define RCS_KWEXP_SIZE  1024
46 
47 #define ANNOTATE_NEVER	0
48 #define ANNOTATE_NOW	1
49 #define ANNOTATE_LATER	2
50 
51 /* invalid characters in RCS symbol names */
52 static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
53 
54 /* comment leaders, depending on the file's suffix */
55 static const struct rcs_comment {
56 	const char	*rc_suffix;
57 	const char	*rc_cstr;
58 } rcs_comments[] = {
59 	{ "1",    ".\\\" " },
60 	{ "2",    ".\\\" " },
61 	{ "3",    ".\\\" " },
62 	{ "4",    ".\\\" " },
63 	{ "5",    ".\\\" " },
64 	{ "6",    ".\\\" " },
65 	{ "7",    ".\\\" " },
66 	{ "8",    ".\\\" " },
67 	{ "9",    ".\\\" " },
68 	{ "a",    "-- "    },	/* Ada		 */
69 	{ "ada",  "-- "    },
70 	{ "adb",  "-- "    },
71 	{ "asm",  ";; "    },	/* assembler (MS-DOS) */
72 	{ "ads",  "-- "    },	/* Ada */
73 	{ "bat",  ":: "    },	/* batch (MS-DOS) */
74 	{ "body", "-- "    },	/* Ada */
75 	{ "c",    " * "    },	/* C */
76 	{ "c++",  "// "    },	/* C++ */
77 	{ "cc",   "// "    },
78 	{ "cpp",  "// "    },
79 	{ "cxx",  "// "    },
80 	{ "m",    "// "    },	/* Objective-C */
81 	{ "cl",   ";;; "   },	/* Common Lisp	 */
82 	{ "cmd",  ":: "    },	/* command (OS/2) */
83 	{ "cmf",  "c "     },	/* CM Fortran	 */
84 	{ "csh",  "# "     },	/* shell	 */
85 	{ "e",    "# "     },	/* efl		 */
86 	{ "epsf", "% "     },	/* encapsulated postscript */
87 	{ "epsi", "% "     },	/* encapsulated postscript */
88 	{ "el",   "; "     },	/* Emacs Lisp	 */
89 	{ "f",    "c "     },	/* Fortran	 */
90 	{ "for",  "c "     },
91 	{ "h",    " * "    },	/* C-header	 */
92 	{ "hh",   "// "    },	/* C++ header	 */
93 	{ "hpp",  "// "    },
94 	{ "hxx",  "// "    },
95 	{ "in",   "# "     },	/* for Makefile.in */
96 	{ "l",    " * "    },	/* lex */
97 	{ "mac",  ";; "    },	/* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
98 	{ "mak",  "# "     },	/* makefile, e.g. Visual C++ */
99 	{ "me",   ".\\\" " },	/* me-macros	t/nroff	 */
100 	{ "ml",   "; "     },	/* mocklisp	 */
101 	{ "mm",   ".\\\" " },	/* mm-macros	t/nroff	 */
102 	{ "ms",   ".\\\" " },	/* ms-macros	t/nroff	 */
103 	{ "man",  ".\\\" " },	/* man-macros	t/nroff	 */
104 	{ "p",    " * "    },	/* pascal	 */
105 	{ "pas",  " * "    },
106 	{ "pl",   "# "     },	/* Perl	(conflict with Prolog) */
107 	{ "pm",   "# "     },	/* Perl	module */
108 	{ "ps",   "% "     },	/* postscript */
109 	{ "psw",  "% "     },	/* postscript wrap */
110 	{ "pswm", "% "     },	/* postscript wrap */
111 	{ "r",    "# "     },	/* ratfor	 */
112 	{ "rc",   " * "    },	/* Microsoft Windows resource file */
113 	{ "red",  "% "     },	/* psl/rlisp	 */
114 	{ "sh",   "# "     },	/* shell	 */
115 	{ "sl",   "% "     },	/* psl		 */
116 	{ "spec", "-- "    },	/* Ada		 */
117 	{ "tex",  "% "     },	/* tex		 */
118 	{ "y",    " * "    },	/* yacc		 */
119 	{ "ye",   " * "    },	/* yacc-efl	 */
120 	{ "yr",   " * "    },	/* yacc-ratfor	 */
121 };
122 
123 struct rcs_kw rcs_expkw[] =  {
124 	{ "Author",	RCS_KW_AUTHOR   },
125 	{ "Date",	RCS_KW_DATE     },
126 	{ "Header",	RCS_KW_HEADER   },
127 	{ "Id",		RCS_KW_ID       },
128 	{ "Locker",	RCS_KW_LOCKER	},
129 	{ "Log",	RCS_KW_LOG      },
130 	{ "Name",	RCS_KW_NAME     },
131 	{ "RCSfile",	RCS_KW_RCSFILE  },
132 	{ "Revision",	RCS_KW_REVISION },
133 	{ "Source",	RCS_KW_SOURCE   },
134 	{ "State",	RCS_KW_STATE    },
135 	{ "Mdocdate",	RCS_KW_MDOCDATE },
136 };
137 
138 #define NB_COMTYPES	(sizeof(rcs_comments)/sizeof(rcs_comments[0]))
139 
140 static RCSNUM	*rcs_get_revision(const char *, RCSFILE *);
141 int		rcs_patch_lines(struct rcs_lines *, struct rcs_lines *,
142 		    struct rcs_line **, struct rcs_delta *);
143 static void	rcs_freedelta(struct rcs_delta *);
144 static void	rcs_strprint(const u_char *, size_t, FILE *);
145 
146 static void	rcs_kwexp_line(char *, struct rcs_delta *, struct rcs_lines *,
147 		    struct rcs_line *, int mode);
148 
149 /*
150  * Prepare RCSFILE for parsing. The given file descriptor (if any) must be
151  * read-only and is closed on rcs_close().
152  */
153 RCSFILE *
154 rcs_open(const char *path, int fd, int flags, ...)
155 {
156 	int mode;
157 	mode_t fmode;
158 	RCSFILE *rfp;
159 	va_list vap;
160 	struct stat st;
161 	struct rcs_delta *rdp;
162 	struct rcs_lock *lkr;
163 
164 	fmode = S_IRUSR|S_IRGRP|S_IROTH;
165 	flags &= 0xffff;	/* ditch any internal flags */
166 
167 	if (flags & RCS_CREATE) {
168 		va_start(vap, flags);
169 		mode = va_arg(vap, int);
170 		va_end(vap);
171 		fmode = (mode_t)mode;
172 	} else {
173 		if (fstat(fd, &st) == -1)
174 			fatal("rcs_open: %s: fstat: %s", path, strerror(errno));
175 		fmode = st.st_mode;
176 	}
177 
178 	fmode &= ~cvs_umask;
179 
180 	rfp = xcalloc(1, sizeof(*rfp));
181 
182 	rfp->rf_path = xstrdup(path);
183 	rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
184 	rfp->rf_mode = fmode;
185 	if (fd == -1)
186 		rfp->rf_file = NULL;
187 	else if ((rfp->rf_file = fdopen(fd, "r")) == NULL)
188 		fatal("rcs_open: %s: fdopen: %s", path, strerror(errno));
189 	rfp->rf_dead = 0;
190 
191 	TAILQ_INIT(&(rfp->rf_delta));
192 	TAILQ_INIT(&(rfp->rf_access));
193 	TAILQ_INIT(&(rfp->rf_symbols));
194 	TAILQ_INIT(&(rfp->rf_locks));
195 
196 	if (!(rfp->rf_flags & RCS_CREATE)) {
197 		if (rcsparse_init(rfp))
198 			fatal("could not parse admin data");
199 	}
200 
201 	/* fill in rd_locker */
202 	TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
203 		if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
204 			rcs_close(rfp);
205 			return (NULL);
206 		}
207 
208 		rdp->rd_locker = xstrdup(lkr->rl_name);
209 	}
210 
211 	return (rfp);
212 }
213 
214 /*
215  * rcs_close()
216  *
217  * Close an RCS file handle.
218  */
219 void
220 rcs_close(RCSFILE *rfp)
221 {
222 	struct rcs_delta *rdp;
223 	struct rcs_access *rap;
224 	struct rcs_lock *rlp;
225 	struct rcs_sym *rsp;
226 
227 	if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
228 		rcs_write(rfp);
229 
230 	while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
231 		rdp = TAILQ_FIRST(&(rfp->rf_delta));
232 		TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
233 		rcs_freedelta(rdp);
234 	}
235 
236 	while (!TAILQ_EMPTY(&(rfp->rf_access))) {
237 		rap = TAILQ_FIRST(&(rfp->rf_access));
238 		TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
239 		free(rap->ra_name);
240 		free(rap);
241 	}
242 
243 	while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
244 		rsp = TAILQ_FIRST(&(rfp->rf_symbols));
245 		TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
246 		rcsnum_free(rsp->rs_num);
247 		free(rsp->rs_name);
248 		free(rsp);
249 	}
250 
251 	while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
252 		rlp = TAILQ_FIRST(&(rfp->rf_locks));
253 		TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
254 		rcsnum_free(rlp->rl_num);
255 		free(rlp->rl_name);
256 		free(rlp);
257 	}
258 
259 	if (rfp->rf_head != NULL)
260 		rcsnum_free(rfp->rf_head);
261 	if (rfp->rf_branch != NULL)
262 		rcsnum_free(rfp->rf_branch);
263 
264 	if (rfp->rf_file != NULL)
265 		fclose(rfp->rf_file);
266 	free(rfp->rf_path);
267 	free(rfp->rf_comment);
268 	free(rfp->rf_expand);
269 	free(rfp->rf_desc);
270 	if (rfp->rf_pdata != NULL)
271 		rcsparse_free(rfp);
272 	free(rfp);
273 }
274 
275 /*
276  * rcs_write()
277  *
278  * Write the contents of the RCS file handle <rfp> to disk in the file whose
279  * path is in <rf_path>.
280  */
281 void
282 rcs_write(RCSFILE *rfp)
283 {
284 	FILE *fp;
285 	char   numbuf[CVS_REV_BUFSZ], *fn, tmpdir[PATH_MAX];
286 	struct rcs_access *ap;
287 	struct rcs_sym *symp;
288 	struct rcs_branch *brp;
289 	struct rcs_delta *rdp;
290 	struct rcs_lock *lkp;
291 	size_t len;
292 	int fd, saved_errno;
293 
294 	fd = -1;
295 
296 	if (rfp->rf_flags & RCS_SYNCED)
297 		return;
298 
299 	if (cvs_noexec == 1)
300 		return;
301 
302 	/* Write operations need the whole file parsed */
303 	if (rcsparse_deltatexts(rfp, NULL))
304 		fatal("rcs_write: rcsparse_deltatexts");
305 
306 	if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
307 		fatal("rcs_write: truncation");
308 	(void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
309 
310 	if ((fd = mkstemp(fn)) == -1)
311 		fatal("%s", fn);
312 
313 	if ((fp = fdopen(fd, "w")) == NULL) {
314 		saved_errno = errno;
315 		(void)unlink(fn);
316 		fatal("fdopen %s: %s", fn, strerror(saved_errno));
317 	}
318 
319 	worklist_add(fn, &temp_files);
320 
321 	if (rfp->rf_head != NULL)
322 		rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
323 	else
324 		numbuf[0] = '\0';
325 
326 	fprintf(fp, "head\t%s;\n", numbuf);
327 
328 	if (rfp->rf_branch != NULL) {
329 		rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
330 		fprintf(fp, "branch\t%s;\n", numbuf);
331 	}
332 
333 	fputs("access", fp);
334 	TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
335 		fprintf(fp, "\n\t%s", ap->ra_name);
336 	}
337 	fputs(";\n", fp);
338 
339 	fprintf(fp, "symbols");
340 	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
341 		if (RCSNUM_ISBRANCH(symp->rs_num))
342 			rcsnum_addmagic(symp->rs_num);
343 		rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
344 		fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf);
345 	}
346 	fprintf(fp, ";\n");
347 
348 	fprintf(fp, "locks");
349 	TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
350 		rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
351 		fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
352 	}
353 
354 	fprintf(fp, ";");
355 
356 	if (rfp->rf_flags & RCS_SLOCK)
357 		fprintf(fp, " strict;");
358 	fputc('\n', fp);
359 
360 	fputs("comment\t@", fp);
361 	if (rfp->rf_comment != NULL) {
362 		rcs_strprint((const u_char *)rfp->rf_comment,
363 		    strlen(rfp->rf_comment), fp);
364 		fputs("@;\n", fp);
365 	} else
366 		fputs("# @;\n", fp);
367 
368 	if (rfp->rf_expand != NULL) {
369 		fputs("expand @", fp);
370 		rcs_strprint((const u_char *)rfp->rf_expand,
371 		    strlen(rfp->rf_expand), fp);
372 		fputs("@;\n", fp);
373 	}
374 
375 	fputs("\n\n", fp);
376 
377 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
378 		fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
379 		    sizeof(numbuf)));
380 		fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
381 		    rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
382 		    rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
383 		    rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
384 		fprintf(fp, "\tauthor %s;\tstate %s;\n",
385 		    rdp->rd_author, rdp->rd_state);
386 		fputs("branches", fp);
387 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
388 			fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
389 			    sizeof(numbuf)));
390 		}
391 		fputs(";\n", fp);
392 		fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
393 		    numbuf, sizeof(numbuf)));
394 	}
395 
396 	fputs("\ndesc\n@", fp);
397 	if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
398 		rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
399 		if (rfp->rf_desc[len-1] != '\n')
400 			fputc('\n', fp);
401 	}
402 	fputs("@\n", fp);
403 
404 	/* deltatexts */
405 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
406 		fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
407 		    sizeof(numbuf)));
408 		fputs("log\n@", fp);
409 		if (rdp->rd_log != NULL) {
410 			len = strlen(rdp->rd_log);
411 			rcs_strprint((const u_char *)rdp->rd_log, len, fp);
412 			if (len == 0 || rdp->rd_log[len-1] != '\n')
413 				fputc('\n', fp);
414 		}
415 		fputs("@\ntext\n@", fp);
416 		if (rdp->rd_text != NULL)
417 			rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
418 		fputs("@\n", fp);
419 	}
420 
421 	if (fchmod(fd, rfp->rf_mode) == -1) {
422 		saved_errno = errno;
423 		(void)unlink(fn);
424 		fatal("fchmod %s: %s", fn, strerror(saved_errno));
425 	}
426 
427 	(void)fclose(fp);
428 
429 	if (rename(fn, rfp->rf_path) == -1) {
430 		saved_errno = errno;
431 		(void)unlink(fn);
432 		fatal("rename(%s, %s): %s", fn, rfp->rf_path,
433 		    strerror(saved_errno));
434 	}
435 
436 	rfp->rf_flags |= RCS_SYNCED;
437 	free(fn);
438 }
439 
440 /*
441  * rcs_head_get()
442  *
443  * Retrieve the revision number of the head revision for the RCS file <file>.
444  */
445 RCSNUM *
446 rcs_head_get(RCSFILE *file)
447 {
448 	struct rcs_branch *brp;
449 	struct rcs_delta *rdp;
450 	RCSNUM *rev, *rootrev;
451 
452 	if (file->rf_head == NULL)
453 		return NULL;
454 
455 	rev = rcsnum_alloc();
456 	if (file->rf_branch != NULL) {
457 		/* we have a default branch, use that to calculate the
458 		 * real HEAD*/
459 		rootrev = rcsnum_alloc();
460 		rcsnum_cpy(file->rf_branch, rootrev,
461 		    file->rf_branch->rn_len - 1);
462 		if ((rdp = rcs_findrev(file, rootrev)) == NULL)
463 			fatal("rcs_head_get: could not find root revision");
464 
465 		/* HEAD should be the last revision on the default branch */
466 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
467 			if (rcsnum_cmp(brp->rb_num, file->rf_branch,
468 			    file->rf_branch->rn_len) == 0)
469 				break;
470 		}
471 		rcsnum_free(rootrev);
472 
473 		if (brp == NULL)
474 			fatal("rcs_head_get: could not find first default "
475 			    "branch revision");
476 
477 		if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
478 			fatal("rcs_head_get: could not find branch revision");
479 		while (rdp->rd_next->rn_len != 0)
480 			if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
481 				fatal("rcs_head_get: could not find "
482 				    "next branch revision");
483 
484 		rcsnum_cpy(rdp->rd_num, rev, 0);
485 	} else {
486 		rcsnum_cpy(file->rf_head, rev, 0);
487 	}
488 
489 	return (rev);
490 }
491 
492 /*
493  * rcs_head_set()
494  *
495  * Set the revision number of the head revision for the RCS file <file> to
496  * <rev>, which must reference a valid revision within the file.
497  */
498 int
499 rcs_head_set(RCSFILE *file, RCSNUM *rev)
500 {
501 	if (rcs_findrev(file, rev) == NULL)
502 		return (-1);
503 
504 	if (file->rf_head == NULL)
505 		file->rf_head = rcsnum_alloc();
506 
507 	rcsnum_cpy(rev, file->rf_head, 0);
508 	file->rf_flags &= ~RCS_SYNCED;
509 	return (0);
510 }
511 
512 /*
513  * rcs_branch_new()
514  *
515  * Create a new branch out of supplied revision for the RCS file <file>.
516  */
517 RCSNUM *
518 rcs_branch_new(RCSFILE *file, RCSNUM *rev)
519 {
520 	RCSNUM *brev;
521 	struct rcs_sym *sym;
522 
523 	if ((brev = rcsnum_new_branch(rev)) == NULL)
524 		return (NULL);
525 
526 	for (;;) {
527 		TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)
528 			if (!rcsnum_cmp(sym->rs_num, brev, 0))
529 				break;
530 
531 		if (sym == NULL)
532 			break;
533 
534 		if (rcsnum_inc(brev) == NULL ||
535 		    rcsnum_inc(brev) == NULL) {
536 			rcsnum_free(brev);
537 			return (NULL);
538 		}
539 	}
540 
541 	return (brev);
542 }
543 
544 /*
545  * rcs_branch_get()
546  *
547  * Retrieve the default branch number for the RCS file <file>.
548  * Returns the number on success.  If NULL is returned, then there is no
549  * default branch for this file.
550  */
551 const RCSNUM *
552 rcs_branch_get(RCSFILE *file)
553 {
554 	return (file->rf_branch);
555 }
556 
557 /*
558  * rcs_branch_set()
559  *
560  * Set the default branch for the RCS file <file> to <bnum>.
561  * Returns 0 on success, -1 on failure.
562  */
563 int
564 rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
565 {
566 	if (file->rf_branch == NULL)
567 		file->rf_branch = rcsnum_alloc();
568 
569 	rcsnum_cpy(bnum, file->rf_branch, 0);
570 	file->rf_flags &= ~RCS_SYNCED;
571 	return (0);
572 }
573 
574 /*
575  * rcs_access_add()
576  *
577  * Add the login name <login> to the access list for the RCS file <file>.
578  * Returns 0 on success, or -1 on failure.
579  */
580 int
581 rcs_access_add(RCSFILE *file, const char *login)
582 {
583 	struct rcs_access *ap;
584 
585 	/* first look for duplication */
586 	TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
587 		if (strcmp(ap->ra_name, login) == 0)
588 			return (-1);
589 	}
590 
591 	ap = xmalloc(sizeof(*ap));
592 	ap->ra_name = xstrdup(login);
593 	TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
594 
595 	/* not synced anymore */
596 	file->rf_flags &= ~RCS_SYNCED;
597 	return (0);
598 }
599 
600 /*
601  * rcs_access_remove()
602  *
603  * Remove an entry with login name <login> from the access list of the RCS
604  * file <file>.
605  * Returns 0 on success, or -1 on failure.
606  */
607 int
608 rcs_access_remove(RCSFILE *file, const char *login)
609 {
610 	struct rcs_access *ap;
611 
612 	TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
613 		if (strcmp(ap->ra_name, login) == 0)
614 			break;
615 
616 	if (ap == NULL)
617 		return (-1);
618 
619 	TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
620 	free(ap->ra_name);
621 	free(ap);
622 
623 	/* not synced anymore */
624 	file->rf_flags &= ~RCS_SYNCED;
625 	return (0);
626 }
627 
628 /*
629  * rcs_sym_add()
630  *
631  * Add a symbol to the list of symbols for the RCS file <rfp>.  The new symbol
632  * is named <sym> and is bound to the RCS revision <snum>.
633  */
634 int
635 rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
636 {
637 	struct rcs_sym *symp;
638 
639 	if (!rcs_sym_check(sym))
640 		return (-1);
641 
642 	/* first look for duplication */
643 	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
644 		if (strcmp(symp->rs_name, sym) == 0)
645 			return (1);
646 	}
647 
648 	symp = xmalloc(sizeof(*symp));
649 	symp->rs_name = xstrdup(sym);
650 	symp->rs_num = rcsnum_alloc();
651 	rcsnum_cpy(snum, symp->rs_num, 0);
652 
653 	TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
654 
655 	/* not synced anymore */
656 	rfp->rf_flags &= ~RCS_SYNCED;
657 	return (0);
658 }
659 
660 /*
661  * rcs_sym_remove()
662  *
663  * Remove the symbol with name <sym> from the symbol list for the RCS file
664  * <file>.  If no such symbol is found, the call fails and returns with an
665  * error.
666  * Returns 0 on success, or -1 on failure.
667  */
668 int
669 rcs_sym_remove(RCSFILE *file, const char *sym)
670 {
671 	struct rcs_sym *symp;
672 
673 	if (!rcs_sym_check(sym))
674 		return (-1);
675 
676 	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
677 		if (strcmp(symp->rs_name, sym) == 0)
678 			break;
679 
680 	if (symp == NULL)
681 		return (-1);
682 
683 	TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
684 	free(symp->rs_name);
685 	rcsnum_free(symp->rs_num);
686 	free(symp);
687 
688 	/* not synced anymore */
689 	file->rf_flags &= ~RCS_SYNCED;
690 	return (0);
691 }
692 
693 /*
694  * rcs_sym_get()
695  *
696  * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
697  *
698  * Returns a pointer to the symbol on success, or NULL on failure.
699  */
700 struct rcs_sym *
701 rcs_sym_get(RCSFILE *file, const char *sym)
702 {
703 	struct rcs_sym *symp;
704 
705 	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
706 		if (strcmp(symp->rs_name, sym) == 0)
707 			return (symp);
708 
709 	return (NULL);
710 }
711 
712 /*
713  * rcs_sym_getrev()
714  *
715  * Retrieve the RCS revision number associated with the symbol <sym> for the
716  * RCS file <file>.  The returned value is a dynamically-allocated copy and
717  * should be freed by the caller once they are done with it.
718  * Returns the RCSNUM on success, or NULL on failure.
719  */
720 RCSNUM *
721 rcs_sym_getrev(RCSFILE *file, const char *sym)
722 {
723 	RCSNUM *num;
724 	struct rcs_sym *symp;
725 
726 	if (!rcs_sym_check(sym) || file->rf_head == NULL)
727 		return (NULL);
728 
729 	if (!strcmp(sym, RCS_HEAD_BRANCH)) {
730 		num = rcsnum_alloc();
731 		rcsnum_cpy(file->rf_head, num, 0);
732 		return (num);
733 	}
734 
735 	num = NULL;
736 	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
737 		if (strcmp(symp->rs_name, sym) == 0)
738 			break;
739 
740 	if (symp != NULL) {
741 		num = rcsnum_alloc();
742 		rcsnum_cpy(symp->rs_num, num, 0);
743 	}
744 
745 	return (num);
746 }
747 
748 /*
749  * rcs_sym_check()
750  *
751  * Check the RCS symbol name <sym> for any unsupported characters.
752  * Returns 1 if the tag is correct, 0 if it isn't valid.
753  */
754 int
755 rcs_sym_check(const char *sym)
756 {
757 	int ret;
758 	const unsigned char *cp;
759 
760 	ret = 1;
761 	cp = sym;
762 	if (!isalpha(*cp++))
763 		return (0);
764 
765 	for (; *cp != '\0'; cp++)
766 		if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
767 			ret = 0;
768 			break;
769 		}
770 
771 	return (ret);
772 }
773 
774 /*
775  * rcs_lock_getmode()
776  *
777  * Retrieve the locking mode of the RCS file <file>.
778  */
779 int
780 rcs_lock_getmode(RCSFILE *file)
781 {
782 	return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
783 }
784 
785 /*
786  * rcs_lock_setmode()
787  *
788  * Set the locking mode of the RCS file <file> to <mode>, which must either
789  * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
790  * Returns the previous mode on success, or -1 on failure.
791  */
792 int
793 rcs_lock_setmode(RCSFILE *file, int mode)
794 {
795 	int pmode;
796 	pmode = rcs_lock_getmode(file);
797 
798 	if (mode == RCS_LOCK_STRICT)
799 		file->rf_flags |= RCS_SLOCK;
800 	else if (mode == RCS_LOCK_LOOSE)
801 		file->rf_flags &= ~RCS_SLOCK;
802 	else
803 		fatal("rcs_lock_setmode: invalid mode `%d'", mode);
804 
805 	file->rf_flags &= ~RCS_SYNCED;
806 	return (pmode);
807 }
808 
809 /*
810  * rcs_lock_add()
811  *
812  * Add an RCS lock for the user <user> on revision <rev>.
813  * Returns 0 on success, or -1 on failure.
814  */
815 int
816 rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
817 {
818 	struct rcs_lock *lkp;
819 
820 	/* first look for duplication */
821 	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
822 		if (strcmp(lkp->rl_name, user) == 0 &&
823 		    rcsnum_cmp(rev, lkp->rl_num, 0) == 0)
824 			return (-1);
825 	}
826 
827 	lkp = xmalloc(sizeof(*lkp));
828 	lkp->rl_name = xstrdup(user);
829 	lkp->rl_num = rcsnum_alloc();
830 	rcsnum_cpy(rev, lkp->rl_num, 0);
831 
832 	TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list);
833 
834 	/* not synced anymore */
835 	file->rf_flags &= ~RCS_SYNCED;
836 	return (0);
837 }
838 
839 
840 /*
841  * rcs_lock_remove()
842  *
843  * Remove the RCS lock on revision <rev>.
844  * Returns 0 on success, or -1 on failure.
845  */
846 int
847 rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
848 {
849 	struct rcs_lock *lkp;
850 
851 	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
852 		if (strcmp(lkp->rl_name, user) == 0 &&
853 		    rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
854 			break;
855 	}
856 
857 	if (lkp == NULL)
858 		return (-1);
859 
860 	TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list);
861 	rcsnum_free(lkp->rl_num);
862 	free(lkp->rl_name);
863 	free(lkp);
864 
865 	/* not synced anymore */
866 	file->rf_flags &= ~RCS_SYNCED;
867 	return (0);
868 }
869 
870 /*
871  * rcs_desc_get()
872  *
873  * Retrieve the description for the RCS file <file>.
874  */
875 const char *
876 rcs_desc_get(RCSFILE *file)
877 {
878 	return (file->rf_desc);
879 }
880 
881 /*
882  * rcs_desc_set()
883  *
884  * Set the description for the RCS file <file>.
885  */
886 void
887 rcs_desc_set(RCSFILE *file, const char *desc)
888 {
889 	char *tmp;
890 
891 	tmp = xstrdup(desc);
892 	free(file->rf_desc);
893 	file->rf_desc = tmp;
894 	file->rf_flags &= ~RCS_SYNCED;
895 }
896 
897 /*
898  * rcs_comment_lookup()
899  *
900  * Lookup the assumed comment leader based on a file's suffix.
901  * Returns a pointer to the string on success, or NULL on failure.
902  */
903 const char *
904 rcs_comment_lookup(const char *filename)
905 {
906 	int i;
907 	const char *sp;
908 
909 	if ((sp = strrchr(filename, '.')) == NULL)
910 		return (NULL);
911 	sp++;
912 
913 	for (i = 0; i < (int)NB_COMTYPES; i++)
914 		if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
915 			return (rcs_comments[i].rc_cstr);
916 	return (NULL);
917 }
918 
919 /*
920  * rcs_comment_get()
921  *
922  * Retrieve the comment leader for the RCS file <file>.
923  */
924 const char *
925 rcs_comment_get(RCSFILE *file)
926 {
927 	return (file->rf_comment);
928 }
929 
930 /*
931  * rcs_comment_set()
932  *
933  * Set the comment leader for the RCS file <file>.
934  */
935 void
936 rcs_comment_set(RCSFILE *file, const char *comment)
937 {
938 	char *tmp;
939 
940 	tmp = xstrdup(comment);
941 	free(file->rf_comment);
942 	file->rf_comment = tmp;
943 	file->rf_flags &= ~RCS_SYNCED;
944 }
945 
946 int
947 rcs_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines,
948     struct rcs_line **alines, struct rcs_delta *rdp)
949 {
950 	u_char op;
951 	char *ep;
952 	struct rcs_line *lp, *dlp, *ndlp;
953 	int i, lineno, nbln;
954 	u_char tmp;
955 
956 	dlp = TAILQ_FIRST(&(dlines->l_lines));
957 	lp = TAILQ_FIRST(&(plines->l_lines));
958 
959 	/* skip first bogus line */
960 	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
961 	    lp = TAILQ_NEXT(lp, l_list)) {
962 		if (lp->l_len < 2)
963 			fatal("line too short, RCS patch seems broken");
964 		op = *(lp->l_line);
965 		/* NUL-terminate line buffer for strtol() safety. */
966 		tmp = lp->l_line[lp->l_len - 1];
967 		lp->l_line[lp->l_len - 1] = '\0';
968 		lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
969 		if (lineno - 1 > dlines->l_nblines || lineno < 0) {
970 			fatal("invalid line specification in RCS patch");
971 		}
972 		ep++;
973 		nbln = (int)strtol(ep, &ep, 10);
974 		/* Restore the last byte of the buffer */
975 		lp->l_line[lp->l_len - 1] = tmp;
976 		if (nbln < 0)
977 			fatal("invalid line number specification in RCS patch");
978 
979 		/* find the appropriate line */
980 		for (;;) {
981 			if (dlp == NULL)
982 				break;
983 			if (dlp->l_lineno == lineno)
984 				break;
985 			if (dlp->l_lineno > lineno) {
986 				dlp = TAILQ_PREV(dlp, tqh, l_list);
987 			} else if (dlp->l_lineno < lineno) {
988 				if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
989 				    ndlp->l_lineno > lineno)
990 					break;
991 				dlp = ndlp;
992 			}
993 		}
994 		if (dlp == NULL)
995 			fatal("can't find referenced line in RCS patch");
996 
997 		if (op == 'd') {
998 			for (i = 0; (i < nbln) && (dlp != NULL); i++) {
999 				ndlp = TAILQ_NEXT(dlp, l_list);
1000 				TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
1001 				if (alines != NULL && dlp->l_line != NULL) {
1002 					dlp->l_delta = rdp;
1003 					alines[dlp->l_lineno_orig - 1] =
1004 						dlp;
1005 				} else
1006 					free(dlp);
1007 				dlp = ndlp;
1008 				/* last line is gone - reset dlp */
1009 				if (dlp == NULL) {
1010 					ndlp = TAILQ_LAST(&(dlines->l_lines),
1011 					    tqh);
1012 					dlp = ndlp;
1013 				}
1014 			}
1015 		} else if (op == 'a') {
1016 			for (i = 0; i < nbln; i++) {
1017 				ndlp = lp;
1018 				lp = TAILQ_NEXT(lp, l_list);
1019 				if (lp == NULL)
1020 					fatal("truncated RCS patch");
1021 				TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
1022 				if (alines != NULL) {
1023 					if (lp->l_needsfree == 1)
1024 						free(lp->l_line);
1025 					lp->l_line = NULL;
1026 					lp->l_needsfree = 0;
1027 				}
1028 				lp->l_delta = rdp;
1029 				TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
1030 				    lp, l_list);
1031 				dlp = lp;
1032 
1033 				/* we don't want lookup to block on those */
1034 				lp->l_lineno = lineno;
1035 
1036 				lp = ndlp;
1037 			}
1038 		} else
1039 			fatal("unknown RCS patch operation `%c'", op);
1040 
1041 		/* last line of the patch, done */
1042 		if (lp->l_lineno == plines->l_nblines)
1043 			break;
1044 	}
1045 
1046 	/* once we're done patching, rebuild the line numbers */
1047 	lineno = 0;
1048 	TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
1049 		lp->l_lineno = lineno++;
1050 	dlines->l_nblines = lineno - 1;
1051 
1052 	return (0);
1053 }
1054 
1055 void
1056 rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
1057 {
1058 	struct rcs_lines *plines;
1059 	struct rcs_line *lp;
1060 	int added, i, nbln, removed;
1061 	char op, *ep;
1062 	u_char tmp;
1063 
1064 	added = removed = 0;
1065 
1066 	plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
1067 	lp = TAILQ_FIRST(&(plines->l_lines));
1068 
1069 	/* skip first bogus line */
1070 	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1071 	    lp = TAILQ_NEXT(lp, l_list)) {
1072 		if (lp->l_len < 2)
1073 			fatal("line too short, RCS patch seems broken");
1074 		op = *(lp->l_line);
1075 		/* NUL-terminate line buffer for strtol() safety. */
1076 		tmp = lp->l_line[lp->l_len - 1];
1077 		lp->l_line[lp->l_len - 1] = '\0';
1078 		(void)strtol((lp->l_line + 1), &ep, 10);
1079 		ep++;
1080 		nbln = (int)strtol(ep, &ep, 10);
1081 		/* Restore the last byte of the buffer */
1082 		lp->l_line[lp->l_len - 1] = tmp;
1083 		if (nbln < 0)
1084 			fatal("invalid line number specification in RCS patch");
1085 
1086 		if (op == 'a') {
1087 			added += nbln;
1088 			for (i = 0; i < nbln; i++) {
1089 				lp = TAILQ_NEXT(lp, l_list);
1090 				if (lp == NULL)
1091 					fatal("truncated RCS patch");
1092 			}
1093 		}
1094 		else if (op == 'd')
1095 			removed += nbln;
1096 		else
1097 			fatal("unknown RCS patch operation '%c'", op);
1098 	}
1099 
1100 	cvs_freelines(plines);
1101 
1102 	*ladded = added;
1103 	*lremoved = removed;
1104 }
1105 
1106 /*
1107  * rcs_rev_add()
1108  *
1109  * Add a revision to the RCS file <rf>.  The new revision's number can be
1110  * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1111  * new revision will have a number equal to the previous head revision plus
1112  * one).  The <msg> argument specifies the log message for that revision, and
1113  * <date> specifies the revision's date (a value of -1 is
1114  * equivalent to using the current time).
1115  * If <author> is NULL, set the author for this revision to the current user.
1116  * Returns 0 on success, or -1 on failure.
1117  */
1118 int
1119 rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1120     const char *author)
1121 {
1122 	time_t now;
1123 	RCSNUM *root = NULL;
1124 	struct passwd *pw;
1125 	struct rcs_branch *brp, *obrp;
1126 	struct rcs_delta *ordp, *rdp;
1127 
1128 	if (rev == RCS_HEAD_REV) {
1129 		if (rf->rf_flags & RCS_CREATE) {
1130 			if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1131 				return (-1);
1132 			free(rf->rf_head);
1133 			rf->rf_head = rev;
1134 		} else if (rf->rf_head == NULL) {
1135 			return (-1);
1136 		} else {
1137 			rev = rcsnum_inc(rf->rf_head);
1138 		}
1139 	} else {
1140 		if ((rdp = rcs_findrev(rf, rev)) != NULL)
1141 			return (-1);
1142 	}
1143 
1144 	rdp = xcalloc(1, sizeof(*rdp));
1145 
1146 	TAILQ_INIT(&(rdp->rd_branches));
1147 
1148 	rdp->rd_num = rcsnum_alloc();
1149 	rcsnum_cpy(rev, rdp->rd_num, 0);
1150 
1151 	rdp->rd_next = rcsnum_alloc();
1152 
1153 	if (!author && !(author = getlogin())) {
1154 		if (!(pw = getpwuid(getuid())))
1155 			fatal("getpwuid failed");
1156 		author = pw->pw_name;
1157 	}
1158 	rdp->rd_author = xstrdup(author);
1159 	rdp->rd_state = xstrdup(RCS_STATE_EXP);
1160 	rdp->rd_log = xstrdup(msg);
1161 
1162 	if (date != (time_t)(-1))
1163 		now = date;
1164 	else
1165 		time(&now);
1166 	gmtime_r(&now, &(rdp->rd_date));
1167 
1168 	if (RCSNUM_ISBRANCHREV(rev))
1169 		TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1170 	else
1171 		TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1172 	rf->rf_ndelta++;
1173 
1174 	if (!(rf->rf_flags & RCS_CREATE)) {
1175 		if (RCSNUM_ISBRANCHREV(rev)) {
1176 			if (rev->rn_id[rev->rn_len - 1] == 1) {
1177 				/* a new branch */
1178 				root = rcsnum_branch_root(rev);
1179 				brp = xmalloc(sizeof(*brp));
1180 				brp->rb_num = rcsnum_alloc();
1181 				rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1182 
1183 				if ((ordp = rcs_findrev(rf, root)) == NULL)
1184 					fatal("root node not found");
1185 
1186 				TAILQ_FOREACH(obrp, &(ordp->rd_branches),
1187 				    rb_list) {
1188 					if (!rcsnum_cmp(obrp->rb_num,
1189 					    brp->rb_num,
1190 					    brp->rb_num->rn_len - 1))
1191 						break;
1192 				}
1193 
1194 				if (obrp == NULL) {
1195 					TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1196 					    brp, rb_list);
1197 				}
1198 			} else {
1199 				root = rcsnum_alloc();
1200 				rcsnum_cpy(rev, root, 0);
1201 				rcsnum_dec(root);
1202 				if ((ordp = rcs_findrev(rf, root)) == NULL)
1203 					fatal("previous revision not found");
1204 				rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1205 			}
1206 		} else {
1207 			ordp = TAILQ_NEXT(rdp, rd_list);
1208 			rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1209 		}
1210 	}
1211 
1212 	if (root != NULL)
1213 		rcsnum_free(root);
1214 
1215 	/* not synced anymore */
1216 	rf->rf_flags &= ~RCS_SYNCED;
1217 
1218 	return (0);
1219 }
1220 
1221 /*
1222  * rcs_rev_remove()
1223  *
1224  * Remove the revision whose number is <rev> from the RCS file <rf>.
1225  */
1226 int
1227 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1228 {
1229 	int fd1, fd2;
1230 	char *path_tmp1, *path_tmp2;
1231 	struct rcs_delta *rdp, *prevrdp, *nextrdp;
1232 	BUF *prevbuf, *newdiff, *newdeltatext;
1233 
1234 	if (rev == RCS_HEAD_REV)
1235 		rev = rf->rf_head;
1236 
1237 	if (rev == NULL)
1238 		return (-1);
1239 
1240 	/* do we actually have that revision? */
1241 	if ((rdp = rcs_findrev(rf, rev)) == NULL)
1242 		return (-1);
1243 
1244 	/*
1245 	 * This is confusing, the previous delta is next in the TAILQ list.
1246 	 * the next delta is the previous one in the TAILQ list.
1247 	 *
1248 	 * When the HEAD revision got specified, nextrdp will be NULL.
1249 	 * When the first revision got specified, prevrdp will be NULL.
1250 	 */
1251 	prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
1252 	nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, tqh, rd_list);
1253 
1254 	newdeltatext = NULL;
1255 	prevbuf = NULL;
1256 	path_tmp1 = path_tmp2 = NULL;
1257 
1258 	if (prevrdp != NULL && nextrdp != NULL) {
1259 		newdiff = buf_alloc(64);
1260 
1261 		/* calculate new diff */
1262 		(void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1263 		fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1264 
1265 		(void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1266 		fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1267 
1268 		diff_format = D_RCSDIFF;
1269 		if (diffreg(path_tmp1, path_tmp2,
1270 		    fd1, fd2, newdiff, D_FORCEASCII) == D_ERROR)
1271 			fatal("rcs_diffreg failed");
1272 
1273 		close(fd1);
1274 		close(fd2);
1275 
1276 		newdeltatext = newdiff;
1277 	} else if (nextrdp == NULL && prevrdp != NULL) {
1278 		newdeltatext = prevbuf;
1279 	}
1280 
1281 	if (newdeltatext != NULL) {
1282 		if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1283 			fatal("error setting new deltatext");
1284 	}
1285 
1286 	TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1287 
1288 	/* update pointers */
1289 	if (prevrdp != NULL && nextrdp != NULL) {
1290 		rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1291 	} else if (prevrdp != NULL) {
1292 		if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1293 			fatal("rcs_head_set failed");
1294 	} else if (nextrdp != NULL) {
1295 		rcsnum_free(nextrdp->rd_next);
1296 		nextrdp->rd_next = rcsnum_alloc();
1297 	} else {
1298 		rcsnum_free(rf->rf_head);
1299 		rf->rf_head = NULL;
1300 	}
1301 
1302 	rf->rf_ndelta--;
1303 	rf->rf_flags &= ~RCS_SYNCED;
1304 
1305 	rcs_freedelta(rdp);
1306 	free(newdeltatext);
1307 	free(path_tmp1);
1308 	free(path_tmp2);
1309 
1310 	return (0);
1311 }
1312 
1313 /*
1314  * rcs_findrev()
1315  *
1316  * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1317  * The revision number is given in <rev>.
1318  *
1319  * Returns a pointer to the delta on success, or NULL on failure.
1320  */
1321 struct rcs_delta *
1322 rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1323 {
1324 	int isbrev;
1325 	struct rcs_delta *rdp;
1326 
1327 	if (rev == NULL)
1328 		return NULL;
1329 
1330 	isbrev = RCSNUM_ISBRANCHREV(rev);
1331 
1332 	/*
1333 	 * We need to do more parsing if the last revision in the linked list
1334 	 * is greater than the requested revision.
1335 	 */
1336 	rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1337 	if (rdp == NULL ||
1338 	    (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1339 	    ((isbrev && rdp->rd_num->rn_len < 4) ||
1340 	    (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1341 		if (rcsparse_deltas(rfp, rev))
1342 			fatal("error parsing deltas");
1343 	}
1344 
1345 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1346 		if (rcsnum_differ(rdp->rd_num, rev))
1347 			continue;
1348 		else
1349 			return (rdp);
1350 	}
1351 
1352 	return (NULL);
1353 }
1354 
1355 /*
1356  * rcs_kwexp_set()
1357  *
1358  * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1359  */
1360 void
1361 rcs_kwexp_set(RCSFILE *file, int mode)
1362 {
1363 	int i;
1364 	char *tmp, buf[8] = "";
1365 
1366 	if (RCS_KWEXP_INVAL(mode))
1367 		return;
1368 
1369 	i = 0;
1370 	if (mode == RCS_KWEXP_NONE)
1371 		buf[0] = 'b';
1372 	else if (mode == RCS_KWEXP_OLD)
1373 		buf[0] = 'o';
1374 	else {
1375 		if (mode & RCS_KWEXP_NAME)
1376 			buf[i++] = 'k';
1377 		if (mode & RCS_KWEXP_VAL)
1378 			buf[i++] = 'v';
1379 		if (mode & RCS_KWEXP_LKR)
1380 			buf[i++] = 'l';
1381 	}
1382 
1383 	tmp = xstrdup(buf);
1384 	free(file->rf_expand);
1385 	file->rf_expand = tmp;
1386 	/* not synced anymore */
1387 	file->rf_flags &= ~RCS_SYNCED;
1388 }
1389 
1390 /*
1391  * rcs_kwexp_get()
1392  *
1393  * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1394  */
1395 int
1396 rcs_kwexp_get(RCSFILE *file)
1397 {
1398 	if (file->rf_expand == NULL)
1399 		return (RCS_KWEXP_DEFAULT);
1400 
1401 	return (rcs_kflag_get(file->rf_expand));
1402 }
1403 
1404 /*
1405  * rcs_kflag_get()
1406  *
1407  * Get the keyword expansion mode from a set of character flags given in
1408  * <flags> and return the appropriate flag mask.  In case of an error, the
1409  * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1410  */
1411 int
1412 rcs_kflag_get(const char *flags)
1413 {
1414 	int fl;
1415 	size_t len;
1416 	const char *fp;
1417 
1418 	if (flags == NULL || !(len = strlen(flags)))
1419 		return (RCS_KWEXP_ERR);
1420 
1421 	fl = 0;
1422 	for (fp = flags; *fp != '\0'; fp++) {
1423 		if (*fp == 'k')
1424 			fl |= RCS_KWEXP_NAME;
1425 		else if (*fp == 'v')
1426 			fl |= RCS_KWEXP_VAL;
1427 		else if (*fp == 'l')
1428 			fl |= RCS_KWEXP_LKR;
1429 		else if (*fp == 'o') {
1430 			if (len != 1)
1431 				fl |= RCS_KWEXP_ERR;
1432 			fl |= RCS_KWEXP_OLD;
1433 		} else if (*fp == 'b') {
1434 			if (len != 1)
1435 				fl |= RCS_KWEXP_ERR;
1436 			fl |= RCS_KWEXP_NONE;
1437 		} else	/* unknown letter */
1438 			fl |= RCS_KWEXP_ERR;
1439 	}
1440 
1441 	return (fl);
1442 }
1443 
1444 /*
1445  * rcs_freedelta()
1446  *
1447  * Free the contents of a delta structure.
1448  */
1449 static void
1450 rcs_freedelta(struct rcs_delta *rdp)
1451 {
1452 	struct rcs_branch *rb;
1453 
1454 	if (rdp->rd_num != NULL)
1455 		rcsnum_free(rdp->rd_num);
1456 	if (rdp->rd_next != NULL)
1457 		rcsnum_free(rdp->rd_next);
1458 
1459 	free(rdp->rd_author);
1460 	free(rdp->rd_locker);
1461 	free(rdp->rd_state);
1462 	free(rdp->rd_log);
1463 	free(rdp->rd_text);
1464 
1465 	while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1466 		TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
1467 		rcsnum_free(rb->rb_num);
1468 		free(rb);
1469 	}
1470 
1471 	free(rdp);
1472 }
1473 
1474 /*
1475  * rcs_strprint()
1476  *
1477  * Output an RCS string <str> of size <slen> to the stream <stream>.  Any
1478  * '@' characters are escaped.  Otherwise, the string can contain arbitrary
1479  * binary data.
1480  */
1481 static void
1482 rcs_strprint(const u_char *str, size_t slen, FILE *stream)
1483 {
1484 	const u_char *ap, *ep, *sp;
1485 
1486 	if (slen == 0)
1487 		return;
1488 
1489 	ep = str + slen - 1;
1490 
1491 	for (sp = str; sp <= ep;)  {
1492 		ap = memchr(sp, '@', ep - sp);
1493 		if (ap == NULL)
1494 			ap = ep;
1495 		(void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1496 
1497 		if (*ap == '@')
1498 			putc('@', stream);
1499 		sp = ap + 1;
1500 	}
1501 }
1502 
1503 /*
1504  * rcs_deltatext_set()
1505  *
1506  * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1507  * Returns -1 on error, 0 on success.
1508  */
1509 int
1510 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1511 {
1512 	size_t len;
1513 	u_char *dtext;
1514 	struct rcs_delta *rdp;
1515 
1516 	/* Write operations require full parsing */
1517 	if (rcsparse_deltatexts(rfp, NULL))
1518 		return (-1);
1519 
1520 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1521 		return (-1);
1522 
1523 	free(rdp->rd_text);
1524 
1525 	len = buf_len(bp);
1526 	dtext = buf_release(bp);
1527 	bp = NULL;
1528 
1529 	if (len != 0) {
1530 		rdp->rd_text = xmalloc(len);
1531 		rdp->rd_tlen = len;
1532 		(void)memcpy(rdp->rd_text, dtext, len);
1533 	} else {
1534 		rdp->rd_text = NULL;
1535 		rdp->rd_tlen = 0;
1536 	}
1537 
1538 	free(dtext);
1539 	return (0);
1540 }
1541 
1542 /*
1543  * rcs_rev_setlog()
1544  *
1545  * Sets the log message of revision <rev> to <logtext>.
1546  */
1547 int
1548 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
1549 {
1550 	struct rcs_delta *rdp;
1551 
1552 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1553 		return (-1);
1554 
1555 	free(rdp->rd_log);
1556 
1557 	rdp->rd_log = xstrdup(logtext);
1558 	rfp->rf_flags &= ~RCS_SYNCED;
1559 	return (0);
1560 }
1561 /*
1562  * rcs_rev_getdate()
1563  *
1564  * Get the date corresponding to a given revision.
1565  * Returns the date on success, -1 on failure.
1566  */
1567 time_t
1568 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
1569 {
1570 	struct rcs_delta *rdp;
1571 
1572 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1573 		return (-1);
1574 
1575 	return (timegm(&rdp->rd_date));
1576 }
1577 
1578 /*
1579  * rcs_state_set()
1580  *
1581  * Sets the state of revision <rev> to <state>
1582  * NOTE: default state is 'Exp'. States may not contain spaces.
1583  *
1584  * Returns -1 on failure, 0 on success.
1585  */
1586 int
1587 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
1588 {
1589 	struct rcs_delta *rdp;
1590 
1591 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1592 		return (-1);
1593 
1594 	free(rdp->rd_state);
1595 
1596 	rdp->rd_state = xstrdup(state);
1597 
1598 	rfp->rf_flags &= ~RCS_SYNCED;
1599 
1600 	return (0);
1601 }
1602 
1603 /*
1604  * rcs_state_check()
1605  *
1606  * Check if string <state> is valid.
1607  *
1608  * Returns 0 if the string is valid, -1 otherwise.
1609  */
1610 int
1611 rcs_state_check(const char *state)
1612 {
1613 	if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP))
1614 		return (-1);
1615 
1616 	return (0);
1617 }
1618 
1619 /*
1620  * rcs_state_get()
1621  *
1622  * Get the state for a given revision of a specified RCSFILE.
1623  *
1624  * Returns NULL on failure.
1625  */
1626 const char *
1627 rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
1628 {
1629 	struct rcs_delta *rdp;
1630 
1631 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1632 		return (NULL);
1633 
1634 	return (rdp->rd_state);
1635 }
1636 
1637 /* rcs_get_revision() */
1638 static RCSNUM *
1639 rcs_get_revision(const char *revstr, RCSFILE *rfp)
1640 {
1641 	RCSNUM *rev, *brev, *frev;
1642 	struct rcs_branch *brp;
1643 	struct rcs_delta *rdp;
1644 	size_t i;
1645 
1646 	rdp = NULL;
1647 
1648 	if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
1649 		if (rfp->rf_head == NULL)
1650 			return (NULL);
1651 
1652 		frev = rcsnum_alloc();
1653 		rcsnum_cpy(rfp->rf_head, frev, 0);
1654 		return (frev);
1655 	}
1656 
1657 	/* Possibly we could be passed a version number */
1658 	if ((rev = rcsnum_parse(revstr)) != NULL) {
1659 		/* Do not return if it is not in RCS file */
1660 		if ((rdp = rcs_findrev(rfp, rev)) != NULL)
1661 			return (rev);
1662 	} else {
1663 		/* More likely we will be passed a symbol */
1664 		rev = rcs_sym_getrev(rfp, revstr);
1665 	}
1666 
1667 	if (rev == NULL)
1668 		return (NULL);
1669 
1670 	/*
1671 	 * If it was not a branch, thats ok the symbolic
1672 	 * name refered to a revision, so return the resolved
1673 	 * revision for the given name. */
1674 	if (!RCSNUM_ISBRANCH(rev)) {
1675 		/* Sanity check: The first two elements of any
1676 		 * revision (be it on a branch or on trunk) cannot
1677 		 * be greater than HEAD.
1678 		 *
1679 		 * XXX: To avoid comparing to uninitialized memory,
1680 		 * the minimum of both revision lengths is taken
1681 		 * instead of just 2.
1682 		 */
1683 		if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head,
1684 		    MINIMUM(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
1685 			rcsnum_free(rev);
1686 			return (NULL);
1687 		}
1688 		return (rev);
1689 	}
1690 
1691 	brev = rcsnum_alloc();
1692 	rcsnum_cpy(rev, brev, rev->rn_len - 1);
1693 
1694 	if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1695 		fatal("rcs_get_revision: tag `%s' does not exist", revstr);
1696 	rcsnum_free(brev);
1697 
1698 	TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1699 		for (i = 0; i < rev->rn_len; i++)
1700 			if (brp->rb_num->rn_id[i] != rev->rn_id[i])
1701 				break;
1702 		if (i != rev->rn_len)
1703 			continue;
1704 		break;
1705 	}
1706 
1707 	rcsnum_free(rev);
1708 	frev = rcsnum_alloc();
1709 	if (brp == NULL) {
1710 		rcsnum_cpy(rdp->rd_num, frev, 0);
1711 		return (frev);
1712 	} else {
1713 		/* Fetch the delta with the correct branch num */
1714 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1715 			fatal("rcs_get_revision: could not fetch branch "
1716 			    "delta");
1717 		rcsnum_cpy(rdp->rd_num, frev, 0);
1718 		return (frev);
1719 	}
1720 }
1721 
1722 /*
1723  * rcs_rev_getlines()
1724  *
1725  * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
1726  * return it as a pointer to a struct rcs_lines.
1727  */
1728 struct rcs_lines *
1729 rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
1730 {
1731 	size_t plen;
1732 	int annotate, done, i, nextroot;
1733 	RCSNUM *tnum, *bnum;
1734 	struct rcs_branch *brp;
1735 	struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
1736 	u_char *patch;
1737 	struct rcs_line *line, *nline;
1738 	struct rcs_lines *dlines, *plines;
1739 
1740 	hrdp = prdp = rdp = trdp = NULL;
1741 
1742 	if (rfp->rf_head == NULL ||
1743 	    (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
1744 		fatal("rcs_rev_getlines: no HEAD revision");
1745 
1746 	tnum = frev;
1747 	if (rcsparse_deltatexts(rfp, hrdp->rd_num))
1748 		fatal("rcs_rev_getlines: rcsparse_deltatexts");
1749 
1750 	/* revision on branch, get the branch root */
1751 	nextroot = 2;
1752 	bnum = rcsnum_alloc();
1753 	if (RCSNUM_ISBRANCHREV(tnum))
1754 		rcsnum_cpy(tnum, bnum, nextroot);
1755 	else
1756 		rcsnum_cpy(tnum, bnum, tnum->rn_len);
1757 
1758 	if (alines != NULL) {
1759 		/* start with annotate first at requested revision */
1760 		annotate = ANNOTATE_LATER;
1761 		*alines = NULL;
1762 	} else
1763 		annotate = ANNOTATE_NEVER;
1764 
1765 	dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
1766 
1767 	done = 0;
1768 
1769 	rdp = hrdp;
1770 	if (!rcsnum_differ(rdp->rd_num, bnum)) {
1771 		if (annotate == ANNOTATE_LATER) {
1772 			/* found requested revision for annotate */
1773 			i = 0;
1774 			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
1775 				line->l_lineno_orig = line->l_lineno;
1776 				i++;
1777 			}
1778 
1779 			*alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1780 			(*alines)[i] = NULL;
1781 			annotate = ANNOTATE_NOW;
1782 
1783 			/* annotate down to 1.1 from where we are */
1784 			rcsnum_free(bnum);
1785 			bnum = rcsnum_parse("1.1");
1786 			if (!rcsnum_differ(rdp->rd_num, bnum)) {
1787 				goto next;
1788 			}
1789 		} else
1790 			goto next;
1791 	}
1792 
1793 	prdp = hrdp;
1794 	if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
1795 		goto done;
1796 
1797 again:
1798 	for (;;) {
1799 		if (rdp->rd_next->rn_len != 0) {
1800 			trdp = rcs_findrev(rfp, rdp->rd_next);
1801 			if (trdp == NULL)
1802 				fatal("failed to grab next revision");
1803 		} else {
1804 			/*
1805 			 * XXX Fail, although the caller does not always do the
1806 			 * right thing (eg cvs diff when the tree is ahead of
1807 			 * the repository).
1808 			 */
1809 			break;
1810 		}
1811 
1812 		if (rdp->rd_tlen == 0) {
1813 			if (rcsparse_deltatexts(rfp, rdp->rd_num))
1814 				fatal("rcs_rev_getlines: rcsparse_deltatexts");
1815 			if (rdp->rd_tlen == 0) {
1816 				if (!rcsnum_differ(rdp->rd_num, bnum))
1817 					break;
1818 				rdp = trdp;
1819 				continue;
1820 			}
1821 		}
1822 
1823 		plen = rdp->rd_tlen;
1824 		patch = rdp->rd_text;
1825 		plines = cvs_splitlines(patch, plen);
1826 		if (annotate == ANNOTATE_NOW)
1827 			rcs_patch_lines(dlines, plines, *alines, prdp);
1828 		else
1829 			rcs_patch_lines(dlines, plines, NULL, NULL);
1830 		cvs_freelines(plines);
1831 
1832 		if (!rcsnum_differ(rdp->rd_num, bnum)) {
1833 			if (annotate != ANNOTATE_LATER)
1834 				break;
1835 
1836 			/* found requested revision for annotate */
1837 			i = 0;
1838 			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
1839 				line->l_lineno_orig = line->l_lineno;
1840 				i++;
1841 			}
1842 
1843 			*alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1844 			(*alines)[i] = NULL;
1845 			annotate = ANNOTATE_NOW;
1846 
1847 			/* annotate down to 1.1 from where we are */
1848 			rcsnum_free(bnum);
1849 			bnum = rcsnum_parse("1.1");
1850 
1851 			if (!rcsnum_differ(rdp->rd_num, bnum))
1852 				break;
1853 		}
1854 
1855 		prdp = rdp;
1856 		rdp = trdp;
1857 	}
1858 
1859 next:
1860 	if (!rcsnum_differ(rdp->rd_num, frev))
1861 		done = 1;
1862 
1863 	if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
1864 		nextroot += 2;
1865 		rcsnum_cpy(frev, bnum, nextroot);
1866 
1867 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1868 			for (i = 0; i < nextroot - 1; i++)
1869 				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1870 					break;
1871 			if (i == nextroot - 1)
1872 				break;
1873 		}
1874 
1875 		if (brp == NULL) {
1876 			if (annotate != ANNOTATE_NEVER) {
1877 				free(*alines);
1878 				*alines = NULL;
1879 				cvs_freelines(dlines);
1880 				rcsnum_free(bnum);
1881 				return (NULL);
1882 			}
1883 			fatal("expected branch not found on branch list");
1884 		}
1885 
1886 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1887 			fatal("rcs_rev_getlines: failed to get delta for target rev");
1888 
1889 		goto again;
1890 	}
1891 done:
1892 	/* put remaining lines into annotate buffer */
1893 	if (annotate == ANNOTATE_NOW) {
1894 		for (line = TAILQ_FIRST(&(dlines->l_lines));
1895 		    line != NULL; line = nline) {
1896 			nline = TAILQ_NEXT(line, l_list);
1897 			TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
1898 			if (line->l_line == NULL) {
1899 				free(line);
1900 				continue;
1901 			}
1902 
1903 			line->l_delta = rdp;
1904 			(*alines)[line->l_lineno_orig - 1] = line;
1905 		}
1906 
1907 		cvs_freelines(dlines);
1908 		dlines = NULL;
1909 	}
1910 
1911 	if (bnum != tnum)
1912 		rcsnum_free(bnum);
1913 
1914 	return (dlines);
1915 }
1916 
1917 void
1918 rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
1919 {
1920 	size_t plen;
1921 	int i, nextroot;
1922 	RCSNUM *bnum;
1923 	struct rcs_branch *brp;
1924 	struct rcs_delta *rdp, *trdp;
1925 	u_char *patch;
1926 	struct rcs_line *line;
1927 	struct rcs_lines *dlines, *plines;
1928 
1929 	rdp = trdp = NULL;
1930 
1931 	if (!RCSNUM_ISBRANCHREV(frev))
1932 		fatal("rcs_annotate_getlines: branch revision expected");
1933 
1934 	/* revision on branch, get the branch root */
1935 	nextroot = 2;
1936 	bnum = rcsnum_alloc();
1937 	rcsnum_cpy(frev, bnum, nextroot);
1938 
1939 	/*
1940 	 * Going from HEAD to 1.1 enables the use of an array, which is
1941 	 * much faster. Unfortunately this is not possible with branch
1942 	 * revisions, so copy over our alines (array) into dlines (tailq).
1943 	 */
1944 	dlines = xcalloc(1, sizeof(*dlines));
1945 	TAILQ_INIT(&(dlines->l_lines));
1946 	line = xcalloc(1, sizeof(*line));
1947 	TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
1948 
1949 	for (i = 0; (*alines)[i] != NULL; i++) {
1950 		line = (*alines)[i];
1951 		line->l_lineno = i + 1;
1952 		TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
1953 	}
1954 
1955 	rdp = rcs_findrev(rfp, bnum);
1956 	if (rdp == NULL)
1957 		fatal("failed to grab branch root revision");
1958 
1959 	do {
1960 		nextroot += 2;
1961 		rcsnum_cpy(frev, bnum, nextroot);
1962 
1963 		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1964 			for (i = 0; i < nextroot - 1; i++)
1965 				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1966 					break;
1967 			if (i == nextroot - 1)
1968 				break;
1969 		}
1970 
1971 		if (brp == NULL)
1972 			fatal("expected branch not found on branch list");
1973 
1974 		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1975 			fatal("failed to get delta for target rev");
1976 
1977 		for (;;) {
1978 			if (rdp->rd_next->rn_len != 0) {
1979 				trdp = rcs_findrev(rfp, rdp->rd_next);
1980 				if (trdp == NULL)
1981 					fatal("failed to grab next revision");
1982 			}
1983 
1984 			if (rdp->rd_tlen == 0) {
1985 				if (rcsparse_deltatexts(rfp, rdp->rd_num))
1986 					fatal("rcs_annotate_getlines: "
1987 					    "rcsparse_deltatexts");
1988 				if (rdp->rd_tlen == 0) {
1989 					if (!rcsnum_differ(rdp->rd_num, bnum))
1990 						break;
1991 					rdp = trdp;
1992 					continue;
1993 				}
1994 			}
1995 
1996 			plen = rdp->rd_tlen;
1997 			patch = rdp->rd_text;
1998 			plines = cvs_splitlines(patch, plen);
1999 			rcs_patch_lines(dlines, plines, NULL, rdp);
2000 			cvs_freelines(plines);
2001 
2002 			if (!rcsnum_differ(rdp->rd_num, bnum))
2003 				break;
2004 
2005 			rdp = trdp;
2006 		}
2007 	} while (rcsnum_differ(rdp->rd_num, frev));
2008 
2009 	if (bnum != frev)
2010 		rcsnum_free(bnum);
2011 
2012 	/*
2013 	 * All lines have been parsed, now they must be copied over
2014 	 * into alines (array) again.
2015 	 */
2016 	free(*alines);
2017 
2018 	i = 0;
2019 	TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2020 		if (line->l_line != NULL)
2021 			i++;
2022 	}
2023 	*alines = xcalloc(i + 1, sizeof(struct rcs_line *));
2024 	(*alines)[i] = NULL;
2025 
2026 	i = 0;
2027 	TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2028 		if (line->l_line != NULL)
2029 			(*alines)[i++] = line;
2030 	}
2031 }
2032 
2033 /*
2034  * rcs_rev_getbuf()
2035  *
2036  * XXX: This is really really slow and should be avoided if at all possible!
2037  *
2038  * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
2039  * return it as a BUF pointer.
2040  */
2041 BUF *
2042 rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
2043 {
2044 	int expmode, expand;
2045 	struct rcs_delta *rdp;
2046 	struct rcs_lines *lines;
2047 	struct rcs_line *lp, *nlp;
2048 	BUF *bp;
2049 
2050 	rdp = NULL;
2051 	expmode = RCS_KWEXP_NONE;
2052 	expand = 0;
2053 	lines = rcs_rev_getlines(rfp, rev, NULL);
2054 	bp = buf_alloc(1024 * 16);
2055 
2056 	if (!(mode & RCS_KWEXP_NONE)) {
2057 		expmode = rcs_kwexp_get(rfp);
2058 
2059 		if (!(expmode & RCS_KWEXP_NONE)) {
2060 			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2061 				fatal("could not fetch revision");
2062 			expand = 1;
2063 		}
2064 	}
2065 
2066 	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
2067 		nlp = TAILQ_NEXT(lp, l_list);
2068 
2069 		if (lp->l_line == NULL) {
2070 			lp = nlp;
2071 			continue;
2072 		}
2073 
2074 		if (expand)
2075 			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2076 
2077 		do {
2078 			buf_append(bp, lp->l_line, lp->l_len);
2079 		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
2080 	}
2081 
2082 	cvs_freelines(lines);
2083 
2084 	return (bp);
2085 }
2086 
2087 /*
2088  * rcs_rev_write_fd()
2089  *
2090  * Write the entire contents of revision <frev> from the rcsfile <rfp> to
2091  * file descriptor <fd>.
2092  */
2093 void
2094 rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
2095 {
2096 	int fd;
2097 	FILE *fp;
2098 	size_t ret;
2099 	int expmode, expand;
2100 	struct rcs_delta *rdp;
2101 	struct rcs_lines *lines;
2102 	struct rcs_line *lp, *nlp;
2103 	extern int print_stdout;
2104 
2105 	rdp = NULL;
2106 	expmode = RCS_KWEXP_NONE;
2107 	expand = 0;
2108 	lines = rcs_rev_getlines(rfp, rev, NULL);
2109 
2110 	if (!(mode & RCS_KWEXP_NONE)) {
2111 		expmode = rcs_kwexp_get(rfp);
2112 
2113 		if (!(expmode & RCS_KWEXP_NONE)) {
2114 			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2115 				fatal("could not fetch revision");
2116 			expand = 1;
2117 		}
2118 	}
2119 
2120 	fd = dup(_fd);
2121 	if (fd == -1)
2122 		fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
2123 
2124 	if ((fp = fdopen(fd, "w")) == NULL)
2125 		fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
2126 
2127 	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
2128 		nlp = TAILQ_NEXT(lp, l_list);
2129 
2130 		if (lp->l_line == NULL) {
2131 			lp = nlp;
2132 			continue;
2133 		}
2134 
2135 		if (expand)
2136 			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2137 
2138 		do {
2139 			/*
2140 			 * Solely for the checkout and update -p options.
2141 			 */
2142 			if (cvs_server_active == 1 &&
2143 			    (cvs_cmdop == CVS_OP_CHECKOUT ||
2144 			    cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
2145 				ret = fwrite("M ", 1, 2, fp);
2146 				if (ret != 2)
2147 					fatal("rcs_rev_write_fd: %s",
2148 					    strerror(errno));
2149 			}
2150 
2151 			ret = fwrite(lp->l_line, 1, lp->l_len, fp);
2152 			if (ret != lp->l_len)
2153 				fatal("rcs_rev_write_fd: %s", strerror(errno));
2154 		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
2155 	}
2156 
2157 	cvs_freelines(lines);
2158 	(void)fclose(fp);
2159 }
2160 
2161 /*
2162  * rcs_rev_write_stmp()
2163  *
2164  * Write the contents of the rev <rev> to a temporary file whose path is
2165  * specified using <template> (see mkstemp(3)). NB. This function will modify
2166  * <template>, as per mkstemp.
2167  */
2168 int
2169 rcs_rev_write_stmp(RCSFILE *rfp,  RCSNUM *rev, char *template, int mode)
2170 {
2171 	int fd;
2172 
2173 	if ((fd = mkstemp(template)) == -1)
2174 		fatal("mkstemp: `%s': %s", template, strerror(errno));
2175 
2176 	worklist_add(template, &temp_files);
2177 	rcs_rev_write_fd(rfp, rev, fd, mode);
2178 
2179 	if (lseek(fd, 0, SEEK_SET) < 0)
2180 		fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
2181 
2182 	return (fd);
2183 }
2184 
2185 static void
2186 rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct rcs_lines *lines,
2187     struct rcs_line *line, int mode)
2188 {
2189 	BUF *tmpbuf;
2190 	int kwtype;
2191 	u_int j, found;
2192 	const u_char *c, *start, *fin, *end;
2193 	char *kwstr;
2194 	char expbuf[256], buf[256];
2195 	size_t clen, kwlen, len, tlen;
2196 
2197 	kwtype = 0;
2198 	kwstr = NULL;
2199 
2200 	if (mode & RCS_KWEXP_OLD)
2201 		return;
2202 
2203 	len = line->l_len;
2204 	if (len == 0)
2205 		return;
2206 
2207 	c = line->l_line;
2208 	found = 0;
2209 	/* Final character in buffer. */
2210 	fin = c + len - 1;
2211 
2212 	/*
2213 	 * Keyword formats:
2214 	 * $Keyword$
2215 	 * $Keyword: value$
2216 	 */
2217 	for (; c < fin; c++) {
2218 		if (*c != '$')
2219 			continue;
2220 
2221 		/* remember start of this possible keyword */
2222 		start = c;
2223 
2224 		/* first following character has to be alphanumeric */
2225 		c++;
2226 		if (!isalpha(*c)) {
2227 			c = start;
2228 			continue;
2229 		}
2230 
2231 		/* Number of characters between c and fin, inclusive. */
2232 		clen = fin - c + 1;
2233 
2234 		/* look for any matching keywords */
2235 		found = 0;
2236 		for (j = 0; j < RCS_NKWORDS; j++) {
2237 			kwlen = strlen(rcs_expkw[j].kw_str);
2238 			/*
2239 			 * kwlen must be less than clen since clen
2240 			 * includes either a terminating `$' or a `:'.
2241 			 */
2242 			if (kwlen < clen &&
2243 			    memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
2244 			    (c[kwlen] == '$' || c[kwlen] == ':')) {
2245 				found = 1;
2246 				kwstr = rcs_expkw[j].kw_str;
2247 				kwtype = rcs_expkw[j].kw_type;
2248 				c += kwlen;
2249 				break;
2250 			}
2251 		}
2252 
2253 		if (found == 0 && cvs_tagname != NULL) {
2254 			kwlen = strlen(cvs_tagname);
2255 			if (kwlen < clen &&
2256 			    memcmp(c, cvs_tagname, kwlen) == 0 &&
2257 			    (c[kwlen] == '$' || c[kwlen] == ':')) {
2258 				found = 1;
2259 				kwstr = cvs_tagname;
2260 				kwtype = RCS_KW_ID;
2261 				c += kwlen;
2262 			}
2263 		}
2264 
2265 		/* unknown keyword, continue looking */
2266 		if (found == 0) {
2267 			c = start;
2268 			continue;
2269 		}
2270 
2271 		/*
2272 		 * if the next character was ':' we need to look for
2273 		 * an '$' before the end of the line to be sure it is
2274 		 * in fact a keyword.
2275 		 */
2276 		if (*c == ':') {
2277 			for (; c <= fin; ++c) {
2278 				if (*c == '$' || *c == '\n')
2279 					break;
2280 			}
2281 
2282 			if (*c != '$') {
2283 				c = start;
2284 				continue;
2285 			}
2286 		}
2287 		end = c + 1;
2288 
2289 		/* start constructing the expansion */
2290 		expbuf[0] = '\0';
2291 
2292 		if (mode & RCS_KWEXP_NAME) {
2293 			if (strlcat(expbuf, "$", sizeof(expbuf)) >=
2294 			    sizeof(expbuf) || strlcat(expbuf, kwstr,
2295 			    sizeof(expbuf)) >= sizeof(expbuf))
2296 				fatal("rcs_kwexp_line: truncated");
2297 			if ((mode & RCS_KWEXP_VAL) &&
2298 			    strlcat(expbuf, ": ", sizeof(expbuf)) >=
2299 			    sizeof(expbuf))
2300 				fatal("rcs_kwexp_line: truncated");
2301 		}
2302 
2303 		/*
2304 		 * order matters because of RCS_KW_ID and
2305 		 * RCS_KW_HEADER here
2306 		 */
2307 		if (mode & RCS_KWEXP_VAL) {
2308 			if (kwtype & RCS_KW_RCSFILE) {
2309 				if (!(kwtype & RCS_KW_FULLPATH))
2310 					(void)strlcat(expbuf, basename(rcsfile),
2311 					    sizeof(expbuf));
2312 				else
2313 					(void)strlcat(expbuf, rcsfile,
2314 					    sizeof(expbuf));
2315 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2316 				    sizeof(expbuf))
2317 					fatal("rcs_kwexp_line: truncated");
2318 			}
2319 
2320 			if (kwtype & RCS_KW_REVISION) {
2321 				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2322 				if (strlcat(buf, " ", sizeof(buf)) >=
2323 				    sizeof(buf) || strlcat(expbuf, buf,
2324 				    sizeof(expbuf)) >= sizeof(buf))
2325 					fatal("rcs_kwexp_line: truncated");
2326 			}
2327 
2328 			if (kwtype & RCS_KW_DATE) {
2329 				if (strftime(buf, sizeof(buf),
2330 				    "%Y/%m/%d %H:%M:%S ",
2331 				    &rdp->rd_date) == 0)
2332 					fatal("rcs_kwexp_line: strftime "
2333 					    "failure");
2334 				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2335 				    sizeof(expbuf))
2336 					fatal("rcs_kwexp_line: string "
2337 					    "truncated");
2338 			}
2339 
2340 			if (kwtype & RCS_KW_MDOCDATE) {
2341 				/*
2342 				 * Do not prepend ' ' for a single
2343 				 * digit, %e would do so and there is
2344 				 * no better format for strftime().
2345 				 */
2346 				if (strftime(buf, sizeof(buf),
2347 				    (rdp->rd_date.tm_mday < 10) ?
2348 				        "%B%e %Y " : "%B %e %Y ",
2349 				    &rdp->rd_date) == 0)
2350 					fatal("rcs_kwexp_line: strftime "
2351 					    "failure");
2352 				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2353 				    sizeof(expbuf))
2354 					fatal("rcs_kwexp_line: string "
2355 					    "truncated");
2356 			}
2357 
2358 			if (kwtype & RCS_KW_AUTHOR) {
2359 				if (strlcat(expbuf, rdp->rd_author,
2360 				    sizeof(expbuf)) >= sizeof(expbuf) ||
2361 				    strlcat(expbuf, " ", sizeof(expbuf)) >=
2362 				    sizeof(expbuf))
2363 					fatal("rcs_kwexp_line: string "
2364 					    "truncated");
2365 			}
2366 
2367 			if (kwtype & RCS_KW_STATE) {
2368 				if (strlcat(expbuf, rdp->rd_state,
2369 				    sizeof(expbuf)) >= sizeof(expbuf) ||
2370 				    strlcat(expbuf, " ", sizeof(expbuf)) >=
2371 				    sizeof(expbuf))
2372 					fatal("rcs_kwexp_line: string "
2373 					    "truncated");
2374 			}
2375 
2376 			/* order does not matter anymore below */
2377 			if (kwtype & RCS_KW_LOG) {
2378 				char linebuf[256];
2379 				struct rcs_line *cur, *lp;
2380 				char *logp, *l_line, *prefix, *q, *sprefix;
2381 				size_t i;
2382 
2383 				/* Log line */
2384 				if (!(kwtype & RCS_KW_FULLPATH))
2385 					(void)strlcat(expbuf,
2386 					    basename(rcsfile), sizeof(expbuf));
2387 				else
2388 					(void)strlcat(expbuf, rcsfile,
2389 					    sizeof(expbuf));
2390 
2391 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2392 				    sizeof(expbuf))
2393 					fatal("rcs_kwexp_line: string "
2394 					    "truncated");
2395 
2396 				cur = line;
2397 
2398 				/* copy rdp->rd_log for strsep */
2399 				logp = xstrdup(rdp->rd_log);
2400 
2401 				/* copy our prefix for later processing */
2402 				prefix = xmalloc(start - line->l_line + 1);
2403 				memcpy(prefix, line->l_line,
2404 				    start - line->l_line);
2405 				prefix[start - line->l_line] = '\0';
2406 
2407 				/* copy also prefix without trailing blanks. */
2408 				sprefix = xstrdup(prefix);
2409 				for (i = strlen(sprefix); i > 0 &&
2410 				    sprefix[i - 1] == ' '; i--)
2411 					sprefix[i - 1] = '\0';
2412 
2413 				/* new line: revision + date + author */
2414 				linebuf[0] = '\0';
2415 				if (strlcat(linebuf, "Revision ",
2416 				    sizeof(linebuf)) >= sizeof(linebuf))
2417 					fatal("rcs_kwexp_line: truncated");
2418 				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2419 				if (strlcat(linebuf, buf, sizeof(linebuf))
2420 				    >= sizeof(buf))
2421 					fatal("rcs_kwexp_line: truncated");
2422 				if (strftime(buf, sizeof(buf),
2423 				    "  %Y/%m/%d %H:%M:%S  ",
2424 				    &rdp->rd_date) == 0)
2425 					fatal("rcs_kwexp_line: strftime "
2426 					    "failure");
2427 				if (strlcat(linebuf, buf, sizeof(linebuf))
2428 				    >= sizeof(linebuf))
2429 					fatal("rcs_kwexp_line: string "
2430 					    "truncated");
2431 				if (strlcat(linebuf, rdp->rd_author,
2432 				    sizeof(linebuf)) >= sizeof(linebuf))
2433 					fatal("rcs_kwexp_line: string "
2434 					    "truncated");
2435 
2436 				lp = xcalloc(1, sizeof(*lp));
2437 				xasprintf(&(lp->l_line), "%s%s\n",
2438 				    prefix, linebuf);
2439 				lp->l_len = strlen(lp->l_line);
2440 				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2441 				    l_list);
2442 				cur = lp;
2443 
2444 				/* Log message */
2445 				q = logp;
2446 				while ((l_line = strsep(&q, "\n")) != NULL &&
2447 				    q != NULL) {
2448 					lp = xcalloc(1, sizeof(*lp));
2449 
2450 					if (l_line[0] == '\0') {
2451 						xasprintf(&(lp->l_line), "%s\n",
2452 						    sprefix);
2453 					} else {
2454 						xasprintf(&(lp->l_line),
2455 						    "%s%s\n", prefix, l_line);
2456 					}
2457 
2458 					lp->l_len = strlen(lp->l_line);
2459 					TAILQ_INSERT_AFTER(&(lines->l_lines),
2460 					    cur, lp, l_list);
2461 					cur = lp;
2462 				}
2463 				free(logp);
2464 
2465 				/*
2466 				 * This is just another hairy mess, but it must
2467 				 * be done: All characters behind Log will be
2468 				 * written in a new line next to log messages.
2469 				 * But that's not enough, we have to strip all
2470 				 * trailing whitespaces of our prefix.
2471 				 */
2472 				lp = xcalloc(1, sizeof(*lp));
2473 				lp->l_line = xcalloc(strlen(sprefix) +
2474 				    line->l_line + line->l_len - end + 1, 1);
2475 				strlcpy(lp->l_line, sprefix,
2476 				    strlen(sprefix) + 1);
2477 				memcpy(lp->l_line + strlen(sprefix),
2478 				    end, line->l_line + line->l_len - end);
2479 				lp->l_len = strlen(lp->l_line);
2480 				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2481 				    l_list);
2482 				cur = lp;
2483 
2484 				end = line->l_line + line->l_len - 1;
2485 
2486 				free(prefix);
2487 				free(sprefix);
2488 
2489 			}
2490 
2491 			if (kwtype & RCS_KW_SOURCE) {
2492 				if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
2493 				    sizeof(expbuf) || strlcat(expbuf, " ",
2494 				    sizeof(expbuf)) >= sizeof(expbuf))
2495 					fatal("rcs_kwexp_line: string "
2496 					    "truncated");
2497 			}
2498 
2499 			if (kwtype & RCS_KW_NAME)
2500 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2501 				    sizeof(expbuf))
2502 					fatal("rcs_kwexp_line: string "
2503 					    "truncated");
2504 
2505 			if (kwtype & RCS_KW_LOCKER)
2506 				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2507 				    sizeof(expbuf))
2508 					fatal("rcs_kwexp_line: string "
2509 					    "truncated");
2510 		}
2511 
2512 		/* end the expansion */
2513 		if (mode & RCS_KWEXP_NAME)
2514 			if (strlcat(expbuf, "$",
2515 			    sizeof(expbuf)) >= sizeof(expbuf))
2516 				fatal("rcs_kwexp_line: truncated");
2517 
2518 		/* Concatenate everything together. */
2519 		tmpbuf = buf_alloc(len + strlen(expbuf));
2520 		/* Append everything before keyword. */
2521 		buf_append(tmpbuf, line->l_line,
2522 		    start - line->l_line);
2523 		/* Append keyword. */
2524 		buf_puts(tmpbuf, expbuf);
2525 		/* Point c to end of keyword. */
2526 		tlen = buf_len(tmpbuf) - 1;
2527 		/* Append everything after keyword. */
2528 		buf_append(tmpbuf, end,
2529 		    line->l_line + line->l_len - end);
2530 		c = buf_get(tmpbuf) + tlen;
2531 		/* Point fin to end of data. */
2532 		fin = buf_get(tmpbuf) + buf_len(tmpbuf) - 1;
2533 		/* Recalculate new length. */
2534 		len = buf_len(tmpbuf);
2535 
2536 		/* tmpbuf is now ready, convert to string */
2537 		if (line->l_needsfree)
2538 			free(line->l_line);
2539 		line->l_len = len;
2540 		line->l_line = buf_release(tmpbuf);
2541 		line->l_needsfree = 1;
2542 	}
2543 }
2544 
2545 /* rcs_translate_tag() */
2546 RCSNUM *
2547 rcs_translate_tag(const char *revstr, RCSFILE *rfp)
2548 {
2549 	int follow;
2550 	time_t deltatime;
2551 	char branch[CVS_REV_BUFSZ];
2552 	RCSNUM *brev, *frev, *rev;
2553 	struct rcs_delta *rdp, *trdp;
2554 	time_t cdate;
2555 
2556 	brev = frev = NULL;
2557 
2558 	if (revstr == NULL) {
2559 		if (rfp->rf_branch != NULL) {
2560 			rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
2561 			revstr = branch;
2562 		} else {
2563 			revstr = RCS_HEAD_BRANCH;
2564 		}
2565 	}
2566 
2567 	if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
2568 		return (NULL);
2569 
2570 	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2571 		return (NULL);
2572 
2573 	/* let's see if we must follow a branch */
2574 	if (!strcmp(revstr, RCS_HEAD_BRANCH))
2575 		follow = 1;
2576 	else {
2577 		frev = rcs_sym_getrev(rfp, revstr);
2578 		if (frev == NULL)
2579 			frev = rcsnum_parse(revstr);
2580 
2581 		brev = rcsnum_alloc();
2582 		rcsnum_cpy(rev, brev, rev->rn_len - 1);
2583 
2584 		if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
2585 		    !rcsnum_cmp(frev, brev, 0)) {
2586 			follow = 1;
2587 		} else
2588 			follow = 0;
2589 
2590 		rcsnum_free(brev);
2591 	}
2592 
2593 	if (cvs_specified_date != -1)
2594 		cdate = cvs_specified_date;
2595 	else
2596 		cdate = cvs_directory_date;
2597 
2598 	if (cdate == -1) {
2599 		/* XXX */
2600 		if (rev->rn_len < 4 || !follow) {
2601 			return (rev);
2602 		}
2603 
2604 		/* Find the latest delta on that branch */
2605 		rcsnum_free(rev);
2606 		for (;;) {
2607 			if (rdp->rd_next->rn_len == 0)
2608 				break;
2609 			if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
2610 				fatal("rcs_translate_tag: could not fetch "
2611 				    "branch delta");
2612 		}
2613 
2614 		rev = rcsnum_alloc();
2615 		rcsnum_cpy(rdp->rd_num, rev, 0);
2616 		return (rev);
2617 	}
2618 
2619 	if (frev != NULL) {
2620 		brev = rcsnum_revtobr(frev);
2621 		brev->rn_len = rev->rn_len - 1;
2622 		rcsnum_free(frev);
2623 	}
2624 
2625 	rcsnum_free(rev);
2626 
2627 	do {
2628 		deltatime = timegm(&(rdp->rd_date));
2629 
2630 		if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
2631 			if (deltatime > cdate) {
2632 				trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
2633 				if (trdp == NULL)
2634 					trdp = rdp;
2635 
2636 				if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
2637 					return (NULL);
2638 
2639 				rev = rcsnum_alloc();
2640 				rcsnum_cpy(trdp->rd_num, rev, 0);
2641 				return (rev);
2642 			}
2643 
2644 			if (rdp->rd_next->rn_len == 0) {
2645 				rev = rcsnum_alloc();
2646 				rcsnum_cpy(rdp->rd_num, rev, 0);
2647 				return (rev);
2648 			}
2649 		} else {
2650 			if (deltatime < cdate) {
2651 				rev = rcsnum_alloc();
2652 				rcsnum_cpy(rdp->rd_num, rev, 0);
2653 				return (rev);
2654 			}
2655 		}
2656 
2657 		if (follow && rdp->rd_next->rn_len != 0) {
2658 			if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
2659 				break;
2660 
2661 			trdp = rcs_findrev(rfp, rdp->rd_next);
2662 			if (trdp == NULL)
2663 				fatal("failed to grab next revision");
2664 			rdp = trdp;
2665 		} else
2666 			follow = 0;
2667 	} while (follow);
2668 
2669 	return (NULL);
2670 }
2671