GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/rcs.c Lines: 639 1125 56.8 %
Date: 2017-11-07 Branches: 409 836 48.9 %

Line Branch Exec Source
1
/*	$OpenBSD: rcs.c,v 1.318 2017/08/28 18:52:25 millert 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
200
	va_list vap;
160
100
	struct stat st;
161
	struct rcs_delta *rdp;
162
	struct rcs_lock *lkr;
163
164
	fmode = S_IRUSR|S_IRGRP|S_IROTH;
165
100
	flags &= 0xffff;	/* ditch any internal flags */
166
167
100
	if (flags & RCS_CREATE) {
168
15
		va_start(vap, flags);
169
45
		mode = va_arg(vap, int);
170
15
		va_end(vap);
171
		fmode = (mode_t)mode;
172
15
	} else {
173
85
		if (fstat(fd, &st) == -1)
174
			fatal("rcs_open: %s: fstat: %s", path, strerror(errno));
175
85
		fmode = st.st_mode;
176
	}
177
178
100
	fmode &= ~cvs_umask;
179
180
100
	rfp = xcalloc(1, sizeof(*rfp));
181
182
100
	rfp->rf_path = xstrdup(path);
183
100
	rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
184
100
	rfp->rf_mode = fmode;
185
100
	if (fd == -1)
186
11
		rfp->rf_file = NULL;
187
89
	else if ((rfp->rf_file = fdopen(fd, "r")) == NULL)
188
		fatal("rcs_open: %s: fdopen: %s", path, strerror(errno));
189
100
	rfp->rf_dead = 0;
190
191
100
	TAILQ_INIT(&(rfp->rf_delta));
192
100
	TAILQ_INIT(&(rfp->rf_access));
193
100
	TAILQ_INIT(&(rfp->rf_symbols));
194
100
	TAILQ_INIT(&(rfp->rf_locks));
195
196
100
	if (!(rfp->rf_flags & RCS_CREATE)) {
197
85
		if (rcsparse_init(rfp))
198
			fatal("could not parse admin data");
199
	}
200
201
	/* fill in rd_locker */
202
200
	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
100
	return (rfp);
212
100
}
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

232
	if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
228
18
		rcs_write(rfp);
229
230
433
	while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
231
		rdp = TAILQ_FIRST(&(rfp->rf_delta));
232
501
		TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
233
167
		rcs_freedelta(rdp);
234
	}
235
236
99
	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
489
	while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
244
		rsp = TAILQ_FIRST(&(rfp->rf_symbols));
245
585
		TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
246
195
		free(rsp->rs_num);
247
195
		free(rsp->rs_name);
248
195
		free(rsp);
249
	}
250
251
99
	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
99
	free(rfp->rf_head);
260
99
	free(rfp->rf_branch);
261
262
99
	if (rfp->rf_file != NULL)
263
88
		fclose(rfp->rf_file);
264
99
	free(rfp->rf_path);
265
99
	free(rfp->rf_comment);
266
99
	free(rfp->rf_expand);
267
99
	free(rfp->rf_desc);
268
99
	if (rfp->rf_pdata != NULL)
269
84
		rcsparse_free(rfp);
270
99
	free(rfp);
271
99
}
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
280
rcs_write(RCSFILE *rfp)
281
{
282
	FILE *fp;
283
48
	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
24
	if (rfp->rf_flags & RCS_SYNCED)
295
		return;
296
297
24
	if (cvs_noexec == 1)
298
		return;
299
300
	/* Write operations need the whole file parsed */
301
24
	if (rcsparse_deltatexts(rfp, NULL))
302
		fatal("rcs_write: rcsparse_deltatexts");
303
304
24
	if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
305
		fatal("rcs_write: truncation");
306
24
	(void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
307
308
24
	if ((fd = mkstemp(fn)) == -1)
309
		fatal("%s", fn);
310
311
24
	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
24
	worklist_add(fn, &temp_files);
318
319
24
	if (rfp->rf_head != NULL)
320
24
		rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
321
	else
322
		numbuf[0] = '\0';
323
324
24
	fprintf(fp, "head\t%s;\n", numbuf);
325
326
24
	if (rfp->rf_branch != NULL) {
327
2
		rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
328
2
		fprintf(fp, "branch\t%s;\n", numbuf);
329
2
	}
330
331
24
	fputs("access", fp);
332
48
	TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
333
		fprintf(fp, "\n\t%s", ap->ra_name);
334
	}
335
24
	fputs(";\n", fp);
336
337
24
	fprintf(fp, "symbols");
338
102
	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
339
27
		if (RCSNUM_ISBRANCH(symp->rs_num))
340
16
			rcsnum_addmagic(symp->rs_num);
341
27
		rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
342
27
		fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf);
343
	}
344
24
	fprintf(fp, ";\n");
345
346
24
	fprintf(fp, "locks");
347
48
	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
24
	fprintf(fp, ";");
353
354
24
	if (rfp->rf_flags & RCS_SLOCK)
355
24
		fprintf(fp, " strict;");
356
24
	fputc('\n', fp);
357
358
24
	fputs("comment\t@", fp);
359
24
	if (rfp->rf_comment != NULL) {
360
9
		rcs_strprint((const u_char *)rfp->rf_comment,
361
9
		    strlen(rfp->rf_comment), fp);
362
9
		fputs("@;\n", fp);
363
9
	} else
364
15
		fputs("# @;\n", fp);
365
366
24
	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
24
	fputs("\n\n", fp);
374
375
120
	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
376
36
		fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
377
		    sizeof(numbuf)));
378
36
		fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
379
36
		    rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
380
36
		    rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
381
36
		    rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
382
36
		fprintf(fp, "\tauthor %s;\tstate %s;\n",
383
36
		    rdp->rd_author, rdp->rd_state);
384
36
		fputs("branches", fp);
385
88
		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
386
8
			fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
387
			    sizeof(numbuf)));
388
		}
389
36
		fputs(";\n", fp);
390
36
		fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
391
		    numbuf, sizeof(numbuf)));
392
	}
393
394
24
	fputs("\ndesc\n@", fp);
395

33
	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
24
	fputs("@\n", fp);
401
402
	/* deltatexts */
403
120
	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
404
36
		fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
405
		    sizeof(numbuf)));
406
36
		fputs("log\n@", fp);
407
36
		if (rdp->rd_log != NULL) {
408
36
			len = strlen(rdp->rd_log);
409
36
			rcs_strprint((const u_char *)rdp->rd_log, len, fp);
410

72
			if (len == 0 || rdp->rd_log[len-1] != '\n')
411
15
				fputc('\n', fp);
412
		}
413
36
		fputs("@\ntext\n@", fp);
414
36
		if (rdp->rd_text != NULL)
415
25
			rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
416
36
		fputs("@\n", fp);
417
	}
418
419
24
	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
24
	(void)fclose(fp);
426
427
24
	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
24
	rfp->rf_flags |= RCS_SYNCED;
435
24
	free(fn);
436
48
}
437
438
/*
439
 * rcs_head_get()
440
 *
441
 * Retrieve the revision number of the head revision for the RCS file <file>.
442
 */
443
RCSNUM *
444
rcs_head_get(RCSFILE *file)
445
{
446
	struct rcs_branch *brp;
447
	struct rcs_delta *rdp;
448
	RCSNUM *rev, *rootrev;
449
450
54
	if (file->rf_head == NULL)
451
		return NULL;
452
453
27
	rev = rcsnum_alloc();
454
27
	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
27
		rcsnum_cpy(file->rf_head, rev, 0);
485
	}
486
487
27
	return (rev);
488
27
}
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
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 *
516
rcs_branch_new(RCSFILE *file, RCSNUM *rev)
517
{
518
	RCSNUM *brev;
519
	struct rcs_sym *sym;
520
521
12
	if ((brev = rcsnum_new_branch(rev)) == NULL)
522
		return (NULL);
523
524
	for (;;) {
525
36
		TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)
526
12
			if (!rcsnum_cmp(sym->rs_num, brev, 0))
527
				break;
528
529
9
		if (sym == NULL)
530
			break;
531
532

6
		if (rcsnum_inc(brev) == NULL ||
533
3
		    rcsnum_inc(brev) == NULL) {
534
			free(brev);
535
			return (NULL);
536
		}
537
	}
538
539
6
	return (brev);
540
6
}
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 *
550
rcs_branch_get(RCSFILE *file)
551
{
552
12
	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
562
rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
563
{
564
2
	if (file->rf_branch == NULL)
565
1
		file->rf_branch = rcsnum_alloc();
566
567
1
	rcsnum_cpy(bnum, file->rf_branch, 0);
568
1
	file->rf_flags &= ~RCS_SYNCED;
569
1
	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
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
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
633
rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
634
{
635
	struct rcs_sym *symp;
636
637
18
	if (!rcs_sym_check(sym))
638
		return (-1);
639
640
	/* first look for duplication */
641
42
	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
642
12
		if (strcmp(symp->rs_name, sym) == 0)
643
			return (1);
644
	}
645
646
9
	symp = xmalloc(sizeof(*symp));
647
9
	symp->rs_name = xstrdup(sym);
648
9
	symp->rs_num = rcsnum_alloc();
649
9
	rcsnum_cpy(snum, symp->rs_num, 0);
650
651
27
	TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
652
653
	/* not synced anymore */
654
9
	rfp->rf_flags &= ~RCS_SYNCED;
655
9
	return (0);
656
9
}
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
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 *
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 *
719
rcs_sym_getrev(RCSFILE *file, const char *sym)
720
{
721
	RCSNUM *num;
722
	struct rcs_sym *symp;
723
724

177
	if (!rcs_sym_check(sym) || file->rf_head == NULL)
725
24
		return (NULL);
726
727
43
	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
138
	TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
735
54
		if (strcmp(symp->rs_name, sym) == 0)
736
			break;
737
738
43
	if (symp != NULL) {
739
28
		num = rcsnum_alloc();
740
28
		rcsnum_cpy(symp->rs_num, num, 0);
741
28
	}
742
743
43
	return (num);
744
67
}
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
753
rcs_sym_check(const char *sym)
754
{
755
	int ret;
756
	const unsigned char *cp;
757
758
	ret = 1;
759
	cp = sym;
760
542
	if (!isalpha(*cp++))
761
24
		return (0);
762
763
5259
	for (; *cp != '\0'; cp++)
764

5012
		if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
765
			ret = 0;
766
			break;
767
		}
768
769
247
	return (ret);
770
271
}
771
772
/*
773
 * rcs_lock_getmode()
774
 *
775
 * Retrieve the locking mode of the RCS file <file>.
776
 */
777
int
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
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
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
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 *
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
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 *
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 *
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
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
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
12
	char *ep;
950
	struct rcs_line *lp, *dlp, *ndlp;
951
	int i, lineno, nbln;
952
	u_char tmp;
953
954
6
	dlp = TAILQ_FIRST(&(dlines->l_lines));
955
6
	lp = TAILQ_FIRST(&(plines->l_lines));
956
957
	/* skip first bogus line */
958
38
	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
959
13
	    lp = TAILQ_NEXT(lp, l_list)) {
960
18
		if (lp->l_len < 2)
961
			fatal("line too short, RCS patch seems broken");
962
18
		op = *(lp->l_line);
963
		/* NUL-terminate line buffer for strtol() safety. */
964
18
		tmp = lp->l_line[lp->l_len - 1];
965
18
		lp->l_line[lp->l_len - 1] = '\0';
966
18
		lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
967
18
		if (lineno - 1 > dlines->l_nblines || lineno < 0) {
968
			fatal("invalid line specification in RCS patch");
969
		}
970
18
		ep++;
971
18
		nbln = (int)strtol(ep, &ep, 10);
972
		/* Restore the last byte of the buffer */
973
18
		lp->l_line[lp->l_len - 1] = tmp;
974
18
		if (nbln < 0)
975
			fatal("invalid line number specification in RCS patch");
976
977
		/* find the appropriate line */
978
		for (;;) {
979
42
			if (dlp == NULL)
980
				break;
981
42
			if (dlp->l_lineno == lineno)
982
				break;
983
30
			if (dlp->l_lineno > lineno) {
984
6
				dlp = TAILQ_PREV(dlp, tqh, l_list);
985
30
			} else if (dlp->l_lineno < lineno) {
986

48
				if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
987
24
				    ndlp->l_lineno > lineno)
988
					break;
989
				dlp = ndlp;
990
18
			}
991
		}
992
18
		if (dlp == NULL)
993
			fatal("can't find referenced line in RCS patch");
994
995
18
		if (op == 'd') {
996
44
			for (i = 0; (i < nbln) && (dlp != NULL); i++) {
997
11
				ndlp = TAILQ_NEXT(dlp, l_list);
998
33
				TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
999

15
				if (alines != NULL && dlp->l_line != NULL) {
1000
4
					dlp->l_delta = rdp;
1001
4
					alines[dlp->l_lineno_orig - 1] =
1002
						dlp;
1003
4
				} else
1004
7
					free(dlp);
1005
				dlp = ndlp;
1006
				/* last line is gone - reset dlp */
1007
11
				if (dlp == NULL) {
1008
5
					ndlp = TAILQ_LAST(&(dlines->l_lines),
1009
					    tqh);
1010
					dlp = ndlp;
1011
5
				}
1012
			}
1013
7
		} else if (op == 'a') {
1014
28
			for (i = 0; i < nbln; i++) {
1015
				ndlp = lp;
1016
7
				lp = TAILQ_NEXT(lp, l_list);
1017
7
				if (lp == NULL)
1018
					fatal("truncated RCS patch");
1019
21
				TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
1020
7
				if (alines != NULL) {
1021
2
					if (lp->l_needsfree == 1)
1022
						free(lp->l_line);
1023
2
					lp->l_line = NULL;
1024
2
					lp->l_needsfree = 0;
1025
2
				}
1026
7
				lp->l_delta = rdp;
1027
21
				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
7
				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
18
		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
52
	TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
1047
20
		lp->l_lineno = lineno++;
1048
6
	dlines->l_nblines = lineno - 1;
1049
1050
6
	return (0);
1051
6
}
1052
1053
void
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
12
	char op, *ep;
1060
	u_char tmp;
1061
1062
	added = removed = 0;
1063
1064
6
	plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
1065
6
	lp = TAILQ_FIRST(&(plines->l_lines));
1066
1067
	/* skip first bogus line */
1068
36
	for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1069
12
	    lp = TAILQ_NEXT(lp, l_list)) {
1070
12
		if (lp->l_len < 2)
1071
			fatal("line too short, RCS patch seems broken");
1072
12
		op = *(lp->l_line);
1073
		/* NUL-terminate line buffer for strtol() safety. */
1074
12
		tmp = lp->l_line[lp->l_len - 1];
1075
12
		lp->l_line[lp->l_len - 1] = '\0';
1076
12
		(void)strtol((lp->l_line + 1), &ep, 10);
1077
12
		ep++;
1078
12
		nbln = (int)strtol(ep, &ep, 10);
1079
		/* Restore the last byte of the buffer */
1080
12
		lp->l_line[lp->l_len - 1] = tmp;
1081
12
		if (nbln < 0)
1082
			fatal("invalid line number specification in RCS patch");
1083
1084
12
		if (op == 'a') {
1085
6
			added += nbln;
1086
24
			for (i = 0; i < nbln; i++) {
1087
6
				lp = TAILQ_NEXT(lp, l_list);
1088
6
				if (lp == NULL)
1089
					fatal("truncated RCS patch");
1090
			}
1091
		}
1092
6
		else if (op == 'd')
1093
6
			removed += nbln;
1094
		else
1095
			fatal("unknown RCS patch operation '%c'", op);
1096
	}
1097
1098
6
	cvs_freelines(plines);
1099
1100
6
	*ladded = added;
1101
6
	*lremoved = removed;
1102
6
}
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
1117
rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1118
    const char *author)
1119
{
1120
36
	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
18
	if (rev == RCS_HEAD_REV) {
1127
16
		if (rf->rf_flags & RCS_CREATE) {
1128
15
			if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1129
				return (-1);
1130
15
			free(rf->rf_head);
1131
15
			rf->rf_head = rev;
1132
16
		} else if (rf->rf_head == NULL) {
1133
			return (-1);
1134
		} else {
1135
1
			rev = rcsnum_inc(rf->rf_head);
1136
		}
1137
	} else {
1138
2
		if ((rdp = rcs_findrev(rf, rev)) != NULL)
1139
			return (-1);
1140
	}
1141
1142
18
	rdp = xcalloc(1, sizeof(*rdp));
1143
1144
18
	TAILQ_INIT(&(rdp->rd_branches));
1145
1146
18
	rdp->rd_num = rcsnum_alloc();
1147
18
	rcsnum_cpy(rev, rdp->rd_num, 0);
1148
1149
18
	rdp->rd_next = rcsnum_alloc();
1150
1151

36
	if (!author && !(author = getlogin())) {
1152
		if (!(pw = getpwuid(getuid())))
1153
			fatal("getpwuid failed");
1154
		author = pw->pw_name;
1155
	}
1156
18
	rdp->rd_author = xstrdup(author);
1157
18
	rdp->rd_state = xstrdup(RCS_STATE_EXP);
1158
18
	rdp->rd_log = xstrdup(msg);
1159
1160
18
	if (date != (time_t)(-1))
1161
		now = date;
1162
	else
1163
18
		time(&now);
1164
18
	gmtime_r(&now, &(rdp->rd_date));
1165
1166

36
	if (RCSNUM_ISBRANCHREV(rev))
1167
2
		TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1168
	else
1169
48
		TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1170
18
	rf->rf_ndelta++;
1171
1172
18
	if (!(rf->rf_flags & RCS_CREATE)) {
1173

4
		if (RCSNUM_ISBRANCHREV(rev)) {
1174
1
			if (rev->rn_id[rev->rn_len - 1] == 1) {
1175
				/* a new branch */
1176
1
				root = rcsnum_branch_root(rev);
1177
1
				brp = xmalloc(sizeof(*brp));
1178
1
				brp->rb_num = rcsnum_alloc();
1179
1
				rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1180
1181
1
				if ((ordp = rcs_findrev(rf, root)) == NULL)
1182
					fatal("root node not found");
1183
1184
2
				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
1
				if (obrp == NULL) {
1193
1
					TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1194
					    brp, rb_list);
1195
1
				}
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
1
			ordp = TAILQ_NEXT(rdp, rd_list);
1206
1
			rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1207
		}
1208
	}
1209
1210
18
	free(root);
1211
1212
	/* not synced anymore */
1213
18
	rf->rf_flags &= ~RCS_SYNCED;
1214
1215
18
	return (0);
1216
18
}
1217
1218
/*
1219
 * rcs_rev_remove()
1220
 *
1221
 * Remove the revision whose number is <rev> from the RCS file <rf>.
1222
 */
1223
int
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 *
1319
rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1320
{
1321
	int isbrev;
1322
	struct rcs_delta *rdp;
1323
1324
922
	if (rev == NULL)
1325
		return NULL;
1326
1327
1374
	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
461
	rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1334

521
	if (rdp == NULL ||
1335

698
	    (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1336

373
	    ((isbrev && rdp->rd_num->rn_len < 4) ||
1337
365
	    (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1338
161
		if (rcsparse_deltas(rfp, rev))
1339
			fatal("error parsing deltas");
1340
	}
1341
1342
1240
	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1343
603
		if (rcsnum_differ(rdp->rd_num, rev))
1344
			continue;
1345
		else
1346
444
			return (rdp);
1347
	}
1348
1349
17
	return (NULL);
1350
461
}
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
1358
rcs_kwexp_set(RCSFILE *file, int mode)
1359
{
1360
	int i;
1361
20
	char *tmp, buf[8] = "";
1362
1363

20
	if (RCS_KWEXP_INVAL(mode))
1364
		return;
1365
1366
	i = 0;
1367
10
	if (mode == RCS_KWEXP_NONE)
1368
		buf[0] = 'b';
1369
10
	else if (mode == RCS_KWEXP_OLD)
1370
		buf[0] = 'o';
1371
	else {
1372
10
		if (mode & RCS_KWEXP_NAME)
1373
7
			buf[i++] = 'k';
1374
10
		if (mode & RCS_KWEXP_VAL)
1375
7
			buf[i++] = 'v';
1376
10
		if (mode & RCS_KWEXP_LKR)
1377
			buf[i++] = 'l';
1378
	}
1379
1380
10
	tmp = xstrdup(buf);
1381
10
	free(file->rf_expand);
1382
10
	file->rf_expand = tmp;
1383
	/* not synced anymore */
1384
10
	file->rf_flags &= ~RCS_SYNCED;
1385
20
}
1386
1387
/*
1388
 * rcs_kwexp_get()
1389
 *
1390
 * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1391
 */
1392
int
1393
rcs_kwexp_get(RCSFILE *file)
1394
{
1395
118
	if (file->rf_expand == NULL)
1396
43
		return (RCS_KWEXP_DEFAULT);
1397
1398
16
	return (rcs_kflag_get(file->rf_expand));
1399
59
}
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
1409
rcs_kflag_get(const char *flags)
1410
{
1411
	int fl;
1412
	size_t len;
1413
	const char *fp;
1414
1415

117
	if (flags == NULL || !(len = strlen(flags)))
1416
		return (RCS_KWEXP_ERR);
1417
1418
	fl = 0;
1419
182
	for (fp = flags; *fp != '\0'; fp++) {
1420
52
		if (*fp == 'k')
1421
26
			fl |= RCS_KWEXP_NAME;
1422
26
		else if (*fp == 'v')
1423
26
			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
39
	return (fl);
1439
39
}
1440
1441
/*
1442
 * rcs_freedelta()
1443
 *
1444
 * Free the contents of a delta structure.
1445
 */
1446
static void
1447
rcs_freedelta(struct rcs_delta *rdp)
1448
{
1449
	struct rcs_branch *rb;
1450
1451
334
	free(rdp->rd_num);
1452
167
	free(rdp->rd_next);
1453
167
	free(rdp->rd_author);
1454
167
	free(rdp->rd_locker);
1455
167
	free(rdp->rd_state);
1456
167
	free(rdp->rd_log);
1457
167
	free(rdp->rd_text);
1458
1459
442
	while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1460
162
		TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
1461
54
		free(rb->rb_num);
1462
54
		free(rb);
1463
	}
1464
1465
167
	free(rdp);
1466
167
}
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
1476
rcs_strprint(const u_char *str, size_t slen, FILE *stream)
1477
{
1478
	const u_char *ap, *ep, *sp;
1479
1480
140
	if (slen == 0)
1481
5
		return;
1482
1483
65
	ep = str + slen - 1;
1484
1485
260
	for (sp = str; sp <= ep;)  {
1486
65
		ap = memchr(sp, '@', ep - sp);
1487
65
		if (ap == NULL)
1488
65
			ap = ep;
1489
65
		(void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1490
1491
65
		if (*ap == '@')
1492
			putc('@', stream);
1493
65
		sp = ap + 1;
1494
	}
1495
135
}
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
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
36
	if (rcsparse_deltatexts(rfp, NULL))
1512
		return (-1);
1513
1514
18
	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1515
		return (-1);
1516
1517
18
	free(rdp->rd_text);
1518
1519
18
	len = buf_len(bp);
1520
18
	dtext = buf_release(bp);
1521
	bp = NULL;
1522
1523
18
	if (len != 0) {
1524
8
		rdp->rd_text = xmalloc(len);
1525
8
		rdp->rd_tlen = len;
1526
8
		(void)memcpy(rdp->rd_text, dtext, len);
1527
8
	} else {
1528
10
		rdp->rd_text = NULL;
1529
10
		rdp->rd_tlen = 0;
1530
	}
1531
1532
18
	free(dtext);
1533
18
	return (0);
1534
18
}
1535
1536
/*
1537
 * rcs_rev_setlog()
1538
 *
1539
 * Sets the log message of revision <rev> to <logtext>.
1540
 */
1541
int
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
1562
rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
1563
{
1564
	struct rcs_delta *rdp;
1565
1566
48
	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1567
		return (-1);
1568
1569
24
	return (timegm(&rdp->rd_date));
1570
24
}
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
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
1605
rcs_state_check(const char *state)
1606
{
1607

459
	if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP))
1608
		return (-1);
1609
1610
153
	return (0);
1611
153
}
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 *
1621
rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
1622
{
1623
	struct rcs_delta *rdp;
1624
1625
170
	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1626
		return (NULL);
1627
1628
85
	return (rdp->rd_state);
1629
85
}
1630
1631
/* rcs_get_revision() */
1632
static RCSNUM *
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
182
	if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
1643
49
		if (rfp->rf_head == NULL)
1644
			return (NULL);
1645
1646
49
		frev = rcsnum_alloc();
1647
49
		rcsnum_cpy(rfp->rf_head, frev, 0);
1648
49
		return (frev);
1649
	}
1650
1651
	/* Possibly we could be passed a version number */
1652
42
	if ((rev = rcsnum_parse(revstr)) != NULL) {
1653
		/* Do not return if it is not in RCS file */
1654
28
		if ((rdp = rcs_findrev(rfp, rev)) != NULL)
1655
15
			return (rev);
1656
	} else {
1657
		/* More likely we will be passed a symbol */
1658
14
		rev = rcs_sym_getrev(rfp, revstr);
1659
	}
1660
1661
27
	if (rev == NULL)
1662
1
		return (NULL);
1663
1664
	/*
1665
	 * If it was not a branch, thats ok the symbolic
1666
	 * name refered to a revision, so return the resolved
1667
	 * revision for the given name. */
1668
26
	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

12
		if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head,
1678
12
		    MINIMUM(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
1679
2
			free(rev);
1680
2
			return (NULL);
1681
		}
1682
2
		return (rev);
1683
	}
1684
1685
22
	brev = rcsnum_alloc();
1686
22
	rcsnum_cpy(rev, brev, rev->rn_len - 1);
1687
1688
22
	if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1689
		fatal("rcs_get_revision: tag `%s' does not exist", revstr);
1690
22
	free(brev);
1691
1692
44
	TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1693
104
		for (i = 0; i < rev->rn_len; i++)
1694
39
			if (brp->rb_num->rn_id[i] != rev->rn_id[i])
1695
				break;
1696
13
		if (i != rev->rn_len)
1697
			continue;
1698
		break;
1699
	}
1700
1701
22
	free(rev);
1702
22
	frev = rcsnum_alloc();
1703
22
	if (brp == NULL) {
1704
9
		rcsnum_cpy(rdp->rd_num, frev, 0);
1705
9
		return (frev);
1706
	} else {
1707
		/* Fetch the delta with the correct branch num */
1708
13
		if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1709
			fatal("rcs_get_revision: could not fetch branch "
1710
			    "delta");
1711
13
		rcsnum_cpy(rdp->rd_num, frev, 0);
1712
13
		return (frev);
1713
	}
1714
91
}
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 *
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

162
	if (rfp->rf_head == NULL ||
1737
54
	    (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
1738
		fatal("rcs_rev_getlines: no HEAD revision");
1739
1740
	tnum = frev;
1741
54
	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
54
	bnum = rcsnum_alloc();
1747

108
	if (RCSNUM_ISBRANCHREV(tnum))
1748
6
		rcsnum_cpy(tnum, bnum, nextroot);
1749
	else
1750
48
		rcsnum_cpy(tnum, bnum, tnum->rn_len);
1751
1752
54
	if (alines != NULL) {
1753
		/* start with annotate first at requested revision */
1754
		annotate = ANNOTATE_LATER;
1755
2
		*alines = NULL;
1756
2
	} else
1757
		annotate = ANNOTATE_NEVER;
1758
1759
54
	dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
1760
1761
	done = 0;
1762
1763
	rdp = hrdp;
1764
54
	if (!rcsnum_differ(rdp->rd_num, bnum)) {
1765
51
		if (annotate == ANNOTATE_LATER) {
1766
			/* found requested revision for annotate */
1767
			i = 0;
1768
20
			TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
1769
8
				line->l_lineno_orig = line->l_lineno;
1770
8
				i++;
1771
			}
1772
1773
2
			*alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1774
2
			(*alines)[i] = NULL;
1775
			annotate = ANNOTATE_NOW;
1776
1777
			/* annotate down to 1.1 from where we are */
1778
2
			free(bnum);
1779
2
			bnum = rcsnum_parse("1.1");
1780
2
			if (!rcsnum_differ(rdp->rd_num, bnum)) {
1781
				goto next;
1782
			}
1783
		} else
1784
			goto next;
1785
	}
1786
1787
	prdp = hrdp;
1788
5
	if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
1789
		goto done;
1790
1791
again:
1792
22
	while (rdp != NULL) {
1793
11
		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
11
		if (rdp->rd_tlen == 0) {
1800
10
			if (rcsparse_deltatexts(rfp, rdp->rd_num))
1801
				fatal("rcs_rev_getlines: rcsparse_deltatexts");
1802
10
			if (rdp->rd_tlen == 0) {
1803
5
				if (!rcsnum_differ(rdp->rd_num, bnum))
1804
					break;
1805
				rdp = trdp;
1806
				continue;
1807
			}
1808
		}
1809
1810
6
		plen = rdp->rd_tlen;
1811
6
		patch = rdp->rd_text;
1812
6
		plines = cvs_splitlines(patch, plen);
1813
6
		if (annotate == ANNOTATE_NOW)
1814
2
			rcs_patch_lines(dlines, plines, *alines, prdp);
1815
		else
1816
4
			rcs_patch_lines(dlines, plines, NULL, NULL);
1817
6
		cvs_freelines(plines);
1818
1819
6
		if (!rcsnum_differ(rdp->rd_num, bnum)) {
1820
6
			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

120
	if (rdp == NULL || !rcsnum_differ(rdp->rd_num, frev))
1848
52
		done = 1;
1849
1850

120
	if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
1851
6
		nextroot += 2;
1852
6
		rcsnum_cpy(frev, bnum, nextroot);
1853
1854
12
		TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1855
48
			for (i = 0; i < nextroot - 1; i++)
1856
18
				if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1857
					break;
1858
6
			if (i == nextroot - 1)
1859
				break;
1860
		}
1861
1862
6
		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
6
		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
54
	if (annotate == ANNOTATE_NOW) {
1881
16
		for (line = TAILQ_FIRST(&(dlines->l_lines));
1882
8
		    line != NULL; line = nline) {
1883
6
			nline = TAILQ_NEXT(line, l_list);
1884
18
			TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
1885
6
			if (line->l_line == NULL) {
1886
4
				free(line);
1887
4
				continue;
1888
			}
1889
1890
2
			line->l_delta = rdp;
1891
2
			(*alines)[line->l_lineno_orig - 1] = line;
1892
2
		}
1893
1894
2
		cvs_freelines(dlines);
1895
		dlines = NULL;
1896
2
	}
1897
1898
54
	if (bnum != tnum)
1899
54
		free(bnum);
1900
1901
54
	return (dlines);
1902
54
}
1903
1904
void
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 *
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
16
	lines = rcs_rev_getlines(rfp, rev, NULL);
2041
8
	bp = buf_alloc(1024 * 16);
2042
2043
8
	if (!(mode & RCS_KWEXP_NONE)) {
2044
8
		expmode = rcs_kwexp_get(rfp);
2045
2046
8
		if (!(expmode & RCS_KWEXP_NONE)) {
2047
8
			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
8
		}
2057
	}
2058
2059
40
	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
2060
24
		nlp = TAILQ_NEXT(lp, l_list);
2061
2062
24
		if (lp->l_line == NULL) {
2063
			lp = nlp;
2064
8
			continue;
2065
		}
2066
2067
16
		if (expand)
2068
16
			rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2069
2070
		do {
2071
16
			buf_append(bp, lp->l_line, lp->l_len);
2072
16
		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
2073
	}
2074
2075
8
	cvs_freelines(lines);
2076
2077
8
	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
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
88
	lines = rcs_rev_getlines(rfp, rev, NULL);
2102
2103
44
	if (!(mode & RCS_KWEXP_NONE)) {
2104
42
		expmode = rcs_kwexp_get(rfp);
2105
2106
42
		if (!(expmode & RCS_KWEXP_NONE)) {
2107
42
			if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2108
				fatal("could not fetch revision");
2109
			expand = 1;
2110
42
		}
2111
	}
2112
2113
44
	fd = dup(_fd);
2114
44
	if (fd == -1)
2115
		fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
2116
2117
44
	if ((fp = fdopen(fd, "w")) == NULL)
2118
		fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
2119
2120
208
	for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
2121
120
		nlp = TAILQ_NEXT(lp, l_list);
2122
2123
120
		if (lp->l_line == NULL) {
2124
			lp = nlp;
2125
44
			continue;
2126
		}
2127
2128
76
		if (expand)
2129
71
			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
76
			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
76
			ret = fwrite(lp->l_line, 1, lp->l_len, fp);
2145
76
			if (ret != lp->l_len)
2146
				fatal("rcs_rev_write_fd: %s", strerror(errno));
2147
76
		} while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
2148
	}
2149
2150
44
	cvs_freelines(lines);
2151
44
	(void)fclose(fp);
2152
44
}
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
2162
rcs_rev_write_stmp(RCSFILE *rfp,  RCSNUM *rev, char *template, int mode)
2163
{
2164
	int fd;
2165
2166
8
	if ((fd = mkstemp(template)) == -1)
2167
		fatal("mkstemp: `%s': %s", template, strerror(errno));
2168
2169
4
	worklist_add(template, &temp_files);
2170
4
	rcs_rev_write_fd(rfp, rev, fd, mode);
2171
2172
4
	if (lseek(fd, 0, SEEK_SET) < 0)
2173
		fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
2174
2175
4
	return (fd);
2176
}
2177
2178
static void
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;
2187
174
	char expbuf[256], buf[256];
2188
	size_t clen, kwlen, len, tlen;
2189
2190
	kwtype = 0;
2191
	kwstr = NULL;
2192
2193
87
	if (mode & RCS_KWEXP_OLD)
2194
		return;
2195
2196
87
	len = line->l_len;
2197
87
	if (len == 0)
2198
		return;
2199
2200
87
	c = line->l_line;
2201
	found = 0;
2202
	/* Final character in buffer. */
2203
87
	fin = c + len - 1;
2204
2205
	/*
2206
	 * Keyword formats:
2207
	 * $Keyword$
2208
	 * $Keyword: value$
2209
	 */
2210
3232
	for (; c < fin; c++) {
2211
1529
		if (*c != '$')
2212
			continue;
2213
2214
		/* remember start of this possible keyword */
2215
		start = c;
2216
2217
		/* first following character has to be alphanumeric */
2218
23
		c++;
2219
23
		if (!isalpha(*c)) {
2220
			c = start;
2221
			continue;
2222
		}
2223
2224
		/* Number of characters between c and fin, inclusive. */
2225
23
		clen = fin - c + 1;
2226
2227
		/* look for any matching keywords */
2228
		found = 0;
2229
184
		for (j = 0; j < RCS_NKWORDS; j++) {
2230
92
			kwlen = strlen(rcs_expkw[j].kw_str);
2231
			/*
2232
			 * kwlen must be less than clen since clen
2233
			 * includes either a terminating `$' or a `:'.
2234
			 */
2235

115
			if (kwlen < clen &&
2236
92
			    memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
2237
46
			    (c[kwlen] == '$' || c[kwlen] == ':')) {
2238
				found = 1;
2239
				kwstr = rcs_expkw[j].kw_str;
2240
23
				kwtype = rcs_expkw[j].kw_type;
2241
				c += kwlen;
2242
23
				break;
2243
			}
2244
		}
2245
2246
23
		if (found == 0 && cvs_tagname != NULL) {
2247
			kwlen = strlen(cvs_tagname);
2248
			if (kwlen < clen &&
2249
			    memcmp(c, cvs_tagname, kwlen) == 0 &&
2250
			    (c[kwlen] == '$' || c[kwlen] == ':')) {
2251
				found = 1;
2252
				kwstr = cvs_tagname;
2253
				kwtype = RCS_KW_ID;
2254
				c += kwlen;
2255
			}
2256
		}
2257
2258
		/* unknown keyword, continue looking */
2259
23
		if (found == 0) {
2260
			c = start;
2261
			continue;
2262
		}
2263
2264
		/*
2265
		 * if the next character was ':' we need to look for
2266
		 * an '$' before the end of the line to be sure it is
2267
		 * in fact a keyword.
2268
		 */
2269
23
		if (*c == ':') {
2270
2301
			for (; c <= fin; ++c) {
2271

2301
				if (*c == '$' || *c == '\n')
2272
					break;
2273
			}
2274
2275
23
			if (*c != '$') {
2276
				c = start;
2277
				continue;
2278
			}
2279
		}
2280
23
		end = c + 1;
2281
2282
		/* start constructing the expansion */
2283
23
		expbuf[0] = '\0';
2284
2285
23
		if (mode & RCS_KWEXP_NAME) {
2286
44
			if (strlcat(expbuf, "$", sizeof(expbuf)) >=
2287
44
			    sizeof(expbuf) || strlcat(expbuf, kwstr,
2288
22
			    sizeof(expbuf)) >= sizeof(expbuf))
2289
				fatal("rcs_kwexp_line: truncated");
2290

43
			if ((mode & RCS_KWEXP_VAL) &&
2291
21
			    strlcat(expbuf, ": ", sizeof(expbuf)) >=
2292
			    sizeof(expbuf))
2293
				fatal("rcs_kwexp_line: truncated");
2294
		}
2295
2296
		/*
2297
		 * order matters because of RCS_KW_ID and
2298
		 * RCS_KW_HEADER here
2299
		 */
2300
23
		if (mode & RCS_KWEXP_VAL) {
2301
22
			if (kwtype & RCS_KW_RCSFILE) {
2302
22
				if (!(kwtype & RCS_KW_FULLPATH))
2303
22
					(void)strlcat(expbuf, basename(rcsfile),
2304
					    sizeof(expbuf));
2305
				else
2306
					(void)strlcat(expbuf, rcsfile,
2307
					    sizeof(expbuf));
2308
22
				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2309
				    sizeof(expbuf))
2310
					fatal("rcs_kwexp_line: truncated");
2311
			}
2312
2313
22
			if (kwtype & RCS_KW_REVISION) {
2314
22
				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2315
44
				if (strlcat(buf, " ", sizeof(buf)) >=
2316
44
				    sizeof(buf) || strlcat(expbuf, buf,
2317
22
				    sizeof(expbuf)) >= sizeof(buf))
2318
					fatal("rcs_kwexp_line: truncated");
2319
			}
2320
2321
22
			if (kwtype & RCS_KW_DATE) {
2322
66
				if (strftime(buf, sizeof(buf),
2323
				    "%Y/%m/%d %H:%M:%S ",
2324
44
				    &rdp->rd_date) == 0)
2325
					fatal("rcs_kwexp_line: strftime "
2326
					    "failure");
2327
22
				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2328
				    sizeof(expbuf))
2329
					fatal("rcs_kwexp_line: string "
2330
					    "truncated");
2331
			}
2332
2333
22
			if (kwtype & RCS_KW_MDOCDATE) {
2334
				/*
2335
				 * Do not prepend ' ' for a single
2336
				 * digit, %e would do so and there is
2337
				 * no better format for strftime().
2338
				 */
2339
				if (strftime(buf, sizeof(buf),
2340
				    (rdp->rd_date.tm_mday < 10) ?
2341
				        "%B%e %Y " : "%B %e %Y ",
2342
				    &rdp->rd_date) == 0)
2343
					fatal("rcs_kwexp_line: strftime "
2344
					    "failure");
2345
				if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2346
				    sizeof(expbuf))
2347
					fatal("rcs_kwexp_line: string "
2348
					    "truncated");
2349
			}
2350
2351
22
			if (kwtype & RCS_KW_AUTHOR) {
2352
44
				if (strlcat(expbuf, rdp->rd_author,
2353
22
				    sizeof(expbuf)) >= sizeof(expbuf) ||
2354
22
				    strlcat(expbuf, " ", sizeof(expbuf)) >=
2355
				    sizeof(expbuf))
2356
					fatal("rcs_kwexp_line: string "
2357
					    "truncated");
2358
			}
2359
2360
22
			if (kwtype & RCS_KW_STATE) {
2361
44
				if (strlcat(expbuf, rdp->rd_state,
2362
22
				    sizeof(expbuf)) >= sizeof(expbuf) ||
2363
22
				    strlcat(expbuf, " ", sizeof(expbuf)) >=
2364
				    sizeof(expbuf))
2365
					fatal("rcs_kwexp_line: string "
2366
					    "truncated");
2367
			}
2368
2369
			/* order does not matter anymore below */
2370
22
			if (kwtype & RCS_KW_LOG) {
2371
				char linebuf[256];
2372
				struct rcs_line *cur, *lp;
2373
				char *logp, *l_line, *prefix, *q, *sprefix;
2374
				size_t i;
2375
2376
				/* Log line */
2377
				if (!(kwtype & RCS_KW_FULLPATH))
2378
					(void)strlcat(expbuf,
2379
					    basename(rcsfile), sizeof(expbuf));
2380
				else
2381
					(void)strlcat(expbuf, rcsfile,
2382
					    sizeof(expbuf));
2383
2384
				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2385
				    sizeof(expbuf))
2386
					fatal("rcs_kwexp_line: string "
2387
					    "truncated");
2388
2389
				cur = line;
2390
2391
				/* copy rdp->rd_log for strsep */
2392
				logp = xstrdup(rdp->rd_log);
2393
2394
				/* copy our prefix for later processing */
2395
				prefix = xmalloc(start - line->l_line + 1);
2396
				memcpy(prefix, line->l_line,
2397
				    start - line->l_line);
2398
				prefix[start - line->l_line] = '\0';
2399
2400
				/* copy also prefix without trailing blanks. */
2401
				sprefix = xstrdup(prefix);
2402
				for (i = strlen(sprefix); i > 0 &&
2403
				    sprefix[i - 1] == ' '; i--)
2404
					sprefix[i - 1] = '\0';
2405
2406
				/* new line: revision + date + author */
2407
				linebuf[0] = '\0';
2408
				if (strlcat(linebuf, "Revision ",
2409
				    sizeof(linebuf)) >= sizeof(linebuf))
2410
					fatal("rcs_kwexp_line: truncated");
2411
				rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2412
				if (strlcat(linebuf, buf, sizeof(linebuf))
2413
				    >= sizeof(buf))
2414
					fatal("rcs_kwexp_line: truncated");
2415
				if (strftime(buf, sizeof(buf),
2416
				    "  %Y/%m/%d %H:%M:%S  ",
2417
				    &rdp->rd_date) == 0)
2418
					fatal("rcs_kwexp_line: strftime "
2419
					    "failure");
2420
				if (strlcat(linebuf, buf, sizeof(linebuf))
2421
				    >= sizeof(linebuf))
2422
					fatal("rcs_kwexp_line: string "
2423
					    "truncated");
2424
				if (strlcat(linebuf, rdp->rd_author,
2425
				    sizeof(linebuf)) >= sizeof(linebuf))
2426
					fatal("rcs_kwexp_line: string "
2427
					    "truncated");
2428
2429
				lp = xcalloc(1, sizeof(*lp));
2430
				xasprintf((char **)&(lp->l_line), "%s%s\n",
2431
				    prefix, linebuf);
2432
				lp->l_len = strlen(lp->l_line);
2433
				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2434
				    l_list);
2435
				cur = lp;
2436
2437
				/* Log message */
2438
				q = logp;
2439
				while ((l_line = strsep(&q, "\n")) != NULL &&
2440
				    q != NULL) {
2441
					lp = xcalloc(1, sizeof(*lp));
2442
2443
					if (l_line[0] == '\0') {
2444
						xasprintf((char **)&(lp->l_line),
2445
						    "%s\n", sprefix);
2446
					} else {
2447
						xasprintf((char **)&(lp->l_line),
2448
						    "%s%s\n", prefix, l_line);
2449
					}
2450
2451
					lp->l_len = strlen(lp->l_line);
2452
					TAILQ_INSERT_AFTER(&(lines->l_lines),
2453
					    cur, lp, l_list);
2454
					cur = lp;
2455
				}
2456
				free(logp);
2457
2458
				/*
2459
				 * This is just another hairy mess, but it must
2460
				 * be done: All characters behind Log will be
2461
				 * written in a new line next to log messages.
2462
				 * But that's not enough, we have to strip all
2463
				 * trailing whitespaces of our prefix.
2464
				 */
2465
				lp = xcalloc(1, sizeof(*lp));
2466
				xasprintf((char **)&lp->l_line, "%s%s",
2467
				    sprefix, end);
2468
				lp->l_len = strlen(lp->l_line);
2469
				TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2470
				    l_list);
2471
				cur = lp;
2472
2473
				end = line->l_line + line->l_len - 1;
2474
2475
				free(prefix);
2476
				free(sprefix);
2477
2478
			}
2479
2480
22
			if (kwtype & RCS_KW_SOURCE) {
2481
				if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
2482
				    sizeof(expbuf) || strlcat(expbuf, " ",
2483
				    sizeof(expbuf)) >= sizeof(expbuf))
2484
					fatal("rcs_kwexp_line: string "
2485
					    "truncated");
2486
			}
2487
2488
22
			if (kwtype & RCS_KW_NAME)
2489
				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2490
				    sizeof(expbuf))
2491
					fatal("rcs_kwexp_line: string "
2492
					    "truncated");
2493
2494
22
			if (kwtype & RCS_KW_LOCKER)
2495
				if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2496
				    sizeof(expbuf))
2497
					fatal("rcs_kwexp_line: string "
2498
					    "truncated");
2499
		}
2500
2501
		/* end the expansion */
2502
23
		if (mode & RCS_KWEXP_NAME)
2503
44
			if (strlcat(expbuf, "$",
2504
22
			    sizeof(expbuf)) >= sizeof(expbuf))
2505
				fatal("rcs_kwexp_line: truncated");
2506
2507
		/* Concatenate everything together. */
2508
23
		tmpbuf = buf_alloc(len + strlen(expbuf));
2509
		/* Append everything before keyword. */
2510
46
		buf_append(tmpbuf, line->l_line,
2511
23
		    start - line->l_line);
2512
		/* Append keyword. */
2513
23
		buf_puts(tmpbuf, expbuf);
2514
		/* Point c to end of keyword. */
2515
23
		tlen = buf_len(tmpbuf) - 1;
2516
		/* Append everything after keyword. */
2517
23
		buf_append(tmpbuf, end,
2518
23
		    line->l_line + line->l_len - end);
2519
23
		c = buf_get(tmpbuf) + tlen;
2520
		/* Point fin to end of data. */
2521
23
		fin = buf_get(tmpbuf) + buf_len(tmpbuf) - 1;
2522
		/* Recalculate new length. */
2523
23
		len = buf_len(tmpbuf);
2524
2525
		/* tmpbuf is now ready, convert to string */
2526
23
		if (line->l_needsfree)
2527
			free(line->l_line);
2528
23
		line->l_len = len;
2529
23
		line->l_line = buf_release(tmpbuf);
2530
23
		line->l_needsfree = 1;
2531
23
	}
2532
174
}
2533
2534
/* rcs_translate_tag() */
2535
RCSNUM *
2536
rcs_translate_tag(const char *revstr, RCSFILE *rfp)
2537
{
2538
	int follow;
2539
	time_t deltatime;
2540
182
	char branch[CVS_REV_BUFSZ];
2541
	RCSNUM *brev, *frev, *rev;
2542
	struct rcs_delta *rdp, *trdp;
2543
	time_t cdate;
2544
2545
	brev = frev = NULL;
2546
2547
91
	if (revstr == NULL) {
2548
53
		if (rfp->rf_branch != NULL) {
2549
9
			rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
2550
			revstr = branch;
2551
9
		} else {
2552
			revstr = RCS_HEAD_BRANCH;
2553
		}
2554
	}
2555
2556
91
	if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
2557
3
		return (NULL);
2558
2559
88
	if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2560
2
		return (NULL);
2561
2562
	/* let's see if we must follow a branch */
2563
86
	if (!strcmp(revstr, RCS_HEAD_BRANCH))
2564
49
		follow = 1;
2565
	else {
2566
37
		frev = rcs_sym_getrev(rfp, revstr);
2567
37
		if (frev == NULL)
2568
24
			frev = rcsnum_parse(revstr);
2569
2570
37
		brev = rcsnum_alloc();
2571
37
		rcsnum_cpy(rev, brev, rev->rn_len - 1);
2572
2573

96
		if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
2574
22
		    !rcsnum_cmp(frev, brev, 0)) {
2575
			follow = 1;
2576
13
		} else
2577
			follow = 0;
2578
2579
37
		free(brev);
2580
	}
2581
2582
86
	if (cvs_specified_date != -1)
2583
		cdate = cvs_specified_date;
2584
	else
2585
86
		cdate = cvs_directory_date;
2586
2587
86
	if (cdate == -1) {
2588
86
		free(frev);
2589
2590
		/* XXX */
2591
86
		if (rev->rn_len < 4 || !follow) {
2592
73
			return (rev);
2593
		}
2594
2595
		/* Find the latest delta on that branch */
2596
13
		free(rev);
2597
13
		for (;;) {
2598
13
			if (rdp->rd_next->rn_len == 0)
2599
				break;
2600
			if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
2601
				fatal("rcs_translate_tag: could not fetch "
2602
				    "branch delta");
2603
		}
2604
2605
13
		rev = rcsnum_alloc();
2606
13
		rcsnum_cpy(rdp->rd_num, rev, 0);
2607
13
		return (rev);
2608
	}
2609
2610
	if (frev != NULL) {
2611
		brev = rcsnum_revtobr(frev);
2612
		brev->rn_len = rev->rn_len - 1;
2613
		free(frev);
2614
	}
2615
2616
	free(rev);
2617
2618
	do {
2619
		deltatime = timegm(&(rdp->rd_date));
2620
2621
		if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
2622
			if (deltatime > cdate) {
2623
				trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
2624
				if (trdp == NULL)
2625
					trdp = rdp;
2626
2627
				if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
2628
					return (NULL);
2629
2630
				rev = rcsnum_alloc();
2631
				rcsnum_cpy(trdp->rd_num, rev, 0);
2632
				return (rev);
2633
			}
2634
2635
			if (rdp->rd_next->rn_len == 0) {
2636
				rev = rcsnum_alloc();
2637
				rcsnum_cpy(rdp->rd_num, rev, 0);
2638
				return (rev);
2639
			}
2640
		} else {
2641
			if (deltatime < cdate) {
2642
				rev = rcsnum_alloc();
2643
				rcsnum_cpy(rdp->rd_num, rev, 0);
2644
				return (rev);
2645
			}
2646
		}
2647
2648
		if (follow && rdp->rd_next->rn_len != 0) {
2649
			if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
2650
				break;
2651
2652
			trdp = rcs_findrev(rfp, rdp->rd_next);
2653
			if (trdp == NULL)
2654
				fatal("failed to grab next revision");
2655
			rdp = trdp;
2656
		} else
2657
			follow = 0;
2658
	} while (follow);
2659
2660
	return (NULL);
2661
91
}