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