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