GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rcs/rlog.c Lines: 0 261 0.0 %
Date: 2016-12-06 Branches: 0 247 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: rlog.c,v 1.71 2015/01/16 06:40:11 deraadt Exp $	*/
2
/*
3
 * Copyright (c) 2005, 2009 Joris Vink <joris@openbsd.org>
4
 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. The name of the author may not be used to endorse or promote products
14
 *    derived from this software without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 */
27
28
#include <ctype.h>
29
#include <err.h>
30
#include <libgen.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
36
#include "rcsprog.h"
37
#include "diff.h"
38
39
#define RLOG_DATE_LATER		0x01
40
#define RLOG_DATE_EARLIER	0x02
41
#define RLOG_DATE_SINGLE	0x04
42
#define RLOG_DATE_RANGE		0x08
43
#define RLOG_DATE_INCLUSIVE	0x10
44
45
static int	rlog_select_daterev(RCSFILE *, char *);
46
static void	rlog_file(const char *, RCSFILE *);
47
static void	rlog_rev_print(struct rcs_delta *);
48
49
#define RLOG_OPTSTRING	"d:hLl::NqRr::s:TtVw::x::z::"
50
#define REVSEP		"----------------------------"
51
#define REVEND \
52
 "============================================================================="
53
54
static int dflag, hflag, Lflag, lflag, rflag, tflag, Nflag, wflag;
55
static char *llist = NULL;
56
static char *slist = NULL;
57
static char *wlist = NULL;
58
static char *revisions = NULL;
59
static char *rlog_dates = NULL;
60
61
__dead void
62
rlog_usage(void)
63
{
64
	fprintf(stderr,
65
	    "usage: rlog [-bhLNRtV] [-ddates] [-l[lockers]] [-r[revs]]\n"
66
	    "            [-sstates] [-w[logins]] [-xsuffixes]\n"
67
	    "            [-ztz] file ...\n");
68
69
	exit(1);
70
}
71
72
int
73
rlog_main(int argc, char **argv)
74
{
75
	RCSFILE *file;
76
	int Rflag;
77
	int i, ch, fd, status;
78
	char fpath[PATH_MAX];
79
80
	rcsnum_flags |= RCSNUM_NO_MAGIC;
81
	hflag = Rflag = rflag = status = 0;
82
	while ((ch = rcs_getopt(argc, argv, RLOG_OPTSTRING)) != -1) {
83
		switch (ch) {
84
		case 'd':
85
			dflag = 1;
86
			rlog_dates = rcs_optarg;
87
			break;
88
		case 'h':
89
			hflag = 1;
90
			break;
91
		case 'L':
92
			Lflag = 1;
93
			break;
94
		case 'l':
95
			lflag = 1;
96
			llist = rcs_optarg;
97
			break;
98
		case 'N':
99
			Nflag = 1;
100
			break;
101
		case 'q':
102
			/*
103
			 * kept for compatibility
104
			 */
105
			break;
106
		case 'R':
107
			Rflag = 1;
108
			break;
109
		case 'r':
110
			rflag = 1;
111
			revisions = rcs_optarg;
112
			break;
113
		case 's':
114
			slist = rcs_optarg;
115
			break;
116
		case 'T':
117
			/*
118
			 * kept for compatibility
119
			 */
120
			break;
121
		case 't':
122
			tflag = 1;
123
			break;
124
		case 'V':
125
			printf("%s\n", rcs_version);
126
			exit(0);
127
		case 'w':
128
			wflag = 1;
129
			wlist = rcs_optarg;
130
			break;
131
		case 'x':
132
			/* Use blank extension if none given. */
133
			rcs_suffixes = rcs_optarg ? rcs_optarg : "";
134
			break;
135
		case 'z':
136
			timezone_flag = rcs_optarg;
137
			break;
138
		default:
139
			(usage)();
140
		}
141
	}
142
143
	argc -= rcs_optind;
144
	argv += rcs_optind;
145
146
	if (argc == 0) {
147
		warnx("no input file");
148
		(usage)();
149
	}
150
151
	if (hflag == 1 && tflag == 1) {
152
		warnx("warning: -t overrides -h.");
153
		hflag = 0;
154
	}
155
156
	for (i = 0; i < argc; i++) {
157
		fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
158
		if (fd < 0) {
159
			warn("%s", fpath);
160
			status = 1;
161
			continue;
162
		}
163
164
		if ((file = rcs_open(fpath, fd,
165
		    RCS_READ|RCS_PARSE_FULLY)) == NULL) {
166
			status = 1;
167
			continue;
168
		}
169
170
		if (Lflag == 1 && TAILQ_EMPTY(&(file->rf_locks))) {
171
			rcs_close(file);
172
			continue;
173
		}
174
175
		if (Rflag == 1) {
176
			printf("%s\n", fpath);
177
			rcs_close(file);
178
			continue;
179
		}
180
181
		rlog_file(argv[i], file);
182
183
		rcs_close(file);
184
	}
185
186
	return (status);
187
}
188
189
static int
190
rlog_select_daterev(RCSFILE *rcsfile, char *date)
191
{
192
	int i, nrev, flags;
193
	struct rcs_delta *rdp;
194
	struct rcs_argvector *args;
195
	char *first, *last, delim;
196
	time_t firstdate, lastdate, rcsdate;
197
198
	nrev = 0;
199
	args = rcs_strsplit(date, ";");
200
201
	for (i = 0; args->argv[i] != NULL; i++) {
202
		flags = 0;
203
		firstdate = lastdate = -1;
204
205
		first = args->argv[i];
206
		last = strchr(args->argv[i], '<');
207
		if (last != NULL) {
208
			delim = *last;
209
			*last++ = '\0';
210
211
			if (*last == '=') {
212
				last++;
213
				flags |= RLOG_DATE_INCLUSIVE;
214
			}
215
		} else {
216
			last = strchr(args->argv[i], '>');
217
			if (last != NULL) {
218
				delim = *last;
219
				*last++ = '\0';
220
221
				if (*last == '=') {
222
					last++;
223
					flags |= RLOG_DATE_INCLUSIVE;
224
				}
225
			}
226
		}
227
228
		if (last == NULL) {
229
			flags |= RLOG_DATE_SINGLE;
230
			if ((firstdate = date_parse(first)) == -1)
231
				return -1;
232
			delim = '\0';
233
			last = "\0";
234
		} else {
235
			while (*last && isspace((unsigned char)*last))
236
				last++;
237
		}
238
239
		if (delim == '>' && *last == '\0') {
240
			flags |= RLOG_DATE_EARLIER;
241
			if ((firstdate = date_parse(first)) == -1)
242
				return -1;
243
		}
244
245
		if (delim == '>' && *first == '\0' && *last != '\0') {
246
			flags |= RLOG_DATE_LATER;
247
			if ((firstdate = date_parse(last)) == -1)
248
				return -1;
249
		}
250
251
		if (delim == '<' && *last == '\0') {
252
			flags |= RLOG_DATE_LATER;
253
			if ((firstdate = date_parse(first)) == -1)
254
				return -1;
255
		}
256
257
		if (delim == '<' && *first == '\0' && *last != '\0') {
258
			flags |= RLOG_DATE_EARLIER;
259
			if ((firstdate = date_parse(last)) == -1)
260
				return -1;
261
		}
262
263
		if (*first != '\0' && *last != '\0') {
264
			flags |= RLOG_DATE_RANGE;
265
266
			if (delim == '<') {
267
				firstdate = date_parse(first);
268
				lastdate = date_parse(last);
269
			} else {
270
				firstdate = date_parse(last);
271
				lastdate = date_parse(first);
272
			}
273
			if (firstdate == -1 || lastdate == -1)
274
				return -1;
275
		}
276
277
		TAILQ_FOREACH(rdp, &(rcsfile->rf_delta), rd_list) {
278
			rcsdate = mktime(&(rdp->rd_date));
279
280
			if (flags & RLOG_DATE_SINGLE) {
281
				if (rcsdate <= firstdate) {
282
					rdp->rd_flags |= RCS_RD_SELECT;
283
					nrev++;
284
					break;
285
				}
286
			}
287
288
			if (flags & RLOG_DATE_EARLIER) {
289
				if (rcsdate < firstdate) {
290
					rdp->rd_flags |= RCS_RD_SELECT;
291
					nrev++;
292
					continue;
293
				}
294
295
				if (flags & RLOG_DATE_INCLUSIVE &&
296
				    (rcsdate <= firstdate)) {
297
					rdp->rd_flags |= RCS_RD_SELECT;
298
					nrev++;
299
					continue;
300
				}
301
			}
302
303
			if (flags & RLOG_DATE_LATER) {
304
				if (rcsdate > firstdate) {
305
					rdp->rd_flags |= RCS_RD_SELECT;
306
					nrev++;
307
					continue;
308
				}
309
310
				if (flags & RLOG_DATE_INCLUSIVE &&
311
				    (rcsdate >= firstdate)) {
312
					rdp->rd_flags |= RCS_RD_SELECT;
313
					nrev++;
314
					continue;
315
				}
316
			}
317
318
			if (flags & RLOG_DATE_RANGE) {
319
				if ((rcsdate > firstdate) &&
320
				    (rcsdate < lastdate)) {
321
					rdp->rd_flags |= RCS_RD_SELECT;
322
					nrev++;
323
					continue;
324
				}
325
326
				if (flags & RLOG_DATE_INCLUSIVE &&
327
				    ((rcsdate >= firstdate) &&
328
				    (rcsdate <= lastdate))) {
329
					rdp->rd_flags |= RCS_RD_SELECT;
330
					nrev++;
331
					continue;
332
				}
333
			}
334
		}
335
	}
336
337
	return (nrev);
338
}
339
340
static void
341
rlog_file(const char *fname, RCSFILE *file)
342
{
343
	char numb[RCS_REV_BUFSZ];
344
	u_int nrev;
345
	struct rcs_sym *sym;
346
	struct rcs_access *acp;
347
	struct rcs_delta *rdp;
348
	struct rcs_lock *lkp;
349
	char *workfile, *p;
350
351
	if (rflag == 1)
352
		nrev = rcs_rev_select(file, revisions);
353
	else if (dflag == 1) {
354
		if ((nrev = rlog_select_daterev(file, rlog_dates)) == -1)
355
			errx(1, "invalid date: %s", rlog_dates);
356
	} else
357
		nrev = file->rf_ndelta;
358
359
	if ((workfile = basename(fname)) == NULL)
360
		err(1, "basename");
361
362
	/*
363
	 * In case they specified 'foo,v' as argument.
364
	 */
365
	if ((p = strrchr(workfile, ',')) != NULL)
366
		*p = '\0';
367
368
	printf("\nRCS file: %s", file->rf_path);
369
	printf("\nWorking file: %s", workfile);
370
	printf("\nhead:");
371
	if (file->rf_head != NULL)
372
		printf(" %s", rcsnum_tostr(file->rf_head, numb, sizeof(numb)));
373
374
	printf("\nbranch:");
375
	if (rcs_branch_get(file) != NULL) {
376
		printf(" %s", rcsnum_tostr(rcs_branch_get(file),
377
		    numb, sizeof(numb)));
378
	}
379
380
	printf("\nlocks: %s", (file->rf_flags & RCS_SLOCK) ? "strict" : "");
381
	TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list)
382
		printf("\n\t%s: %s", lkp->rl_name,
383
		    rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
384
	printf("\naccess list:\n");
385
	TAILQ_FOREACH(acp, &(file->rf_access), ra_list)
386
		printf("\t%s\n", acp->ra_name);
387
388
	if (Nflag == 0) {
389
		printf("symbolic names:\n");
390
		TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list) {
391
			printf("\t%s: %s\n", sym->rs_name,
392
			    rcsnum_tostr(sym->rs_num, numb, sizeof(numb)));
393
		}
394
	}
395
396
	printf("keyword substitution: %s\n",
397
	    file->rf_expand == NULL ? "kv" : file->rf_expand);
398
399
	printf("total revisions: %u", file->rf_ndelta);
400
401
	if (file->rf_head != NULL && hflag == 0 && tflag == 0)
402
		printf(";\tselected revisions: %u", nrev);
403
404
	printf("\n");
405
406
407
	if (hflag == 0 || tflag == 1)
408
		printf("description:\n%s", file->rf_desc);
409
410
	if (hflag == 0 && tflag == 0 &&
411
	    !(lflag == 1 && TAILQ_EMPTY(&file->rf_locks))) {
412
		TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
413
			/*
414
			 * if selections are enabled verify that entry is
415
			 * selected.
416
			 */
417
			if ((rflag == 0 && dflag == 0)
418
			    || (rdp->rd_flags & RCS_RD_SELECT))
419
				rlog_rev_print(rdp);
420
		}
421
	}
422
423
	printf("%s\n", REVEND);
424
}
425
426
static void
427
rlog_rev_print(struct rcs_delta *rdp)
428
{
429
	int i, found;
430
	struct tm t;
431
	char *author, numb[RCS_REV_BUFSZ], *fmt, timeb[RCS_TIME_BUFSZ];
432
	struct rcs_argvector *largv, *sargv, *wargv;
433
	struct rcs_branch *rb;
434
	struct rcs_delta *nrdp;
435
436
	found = 0;
437
	author = NULL;
438
439
	/* -l[lockers] */
440
	if (lflag == 1) {
441
		if (rdp->rd_locker != NULL)
442
			found++;
443
444
		if (llist != NULL) {
445
			/* if locker is empty, no need to go further. */
446
			if (rdp->rd_locker == NULL)
447
				return;
448
			largv = rcs_strsplit(llist, ",");
449
			for (i = 0; largv->argv[i] != NULL; i++) {
450
				if (strcmp(rdp->rd_locker, largv->argv[i])
451
				    == 0) {
452
					found++;
453
					break;
454
				}
455
				found = 0;
456
			}
457
			rcs_argv_destroy(largv);
458
		}
459
	}
460
461
	/* -sstates */
462
	if (slist != NULL) {
463
		sargv = rcs_strsplit(slist, ",");
464
		for (i = 0; sargv->argv[i] != NULL; i++) {
465
			if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
466
				found++;
467
				break;
468
			}
469
			found = 0;
470
		}
471
		rcs_argv_destroy(sargv);
472
	}
473
474
	/* -w[logins] */
475
	if (wflag == 1) {
476
		if (wlist != NULL) {
477
			wargv = rcs_strsplit(wlist, ",");
478
			for (i = 0; wargv->argv[i] != NULL; i++) {
479
				if (strcmp(rdp->rd_author, wargv->argv[i])
480
				    == 0) {
481
					found++;
482
					break;
483
				}
484
				found = 0;
485
			}
486
			rcs_argv_destroy(wargv);
487
		} else {
488
			if ((author = getlogin()) == NULL)
489
				err(1, "getlogin");
490
491
			if (strcmp(rdp->rd_author, author) == 0)
492
				found++;
493
		}
494
	}
495
496
	/* XXX dirty... */
497
	if ((((slist != NULL && wflag == 1) ||
498
	    (slist != NULL && lflag == 1) ||
499
	    (lflag == 1 && wflag == 1)) && found < 2) ||
500
	    (((slist != NULL && lflag == 1 && wflag == 1) ||
501
	    (slist != NULL || lflag == 1 || wflag == 1)) && found == 0))
502
		return;
503
504
	printf("%s\n", REVSEP);
505
506
	rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
507
508
	printf("revision %s", numb);
509
	if (rdp->rd_locker != NULL)
510
		printf("\tlocked by: %s;", rdp->rd_locker);
511
512
	if (timezone_flag != NULL) {
513
		rcs_set_tz(timezone_flag, rdp, &t);
514
		fmt = "%Y-%m-%d %H:%M:%S%z";
515
	} else {
516
		t = rdp->rd_date;
517
		fmt = "%Y/%m/%d %H:%M:%S";
518
	}
519
520
	(void)strftime(timeb, sizeof(timeb), fmt, &t);
521
522
	printf("\ndate: %s;  author: %s;  state: %s;", timeb, rdp->rd_author,
523
	    rdp->rd_state);
524
525
	/*
526
	 * If we are a branch revision, the diff of this revision is stored
527
	 * in place.
528
	 * Otherwise, it is stored in the previous revision as a reversed diff.
529
	 */
530
	if (RCSNUM_ISBRANCHREV(rdp->rd_num))
531
		nrdp = rdp;
532
	else
533
		nrdp = TAILQ_NEXT(rdp, rd_list);
534
535
	/*
536
	 * We do not write diff stats for the first revision of the default
537
	 * branch, since it was not a diff but a full text.
538
	 */
539
	if (nrdp != NULL && rdp->rd_num->rn_len == nrdp->rd_num->rn_len) {
540
		int added, removed;
541
542
		rcs_delta_stats(nrdp, &added, &removed);
543
		if (RCSNUM_ISBRANCHREV(rdp->rd_num))
544
			printf("  lines: +%d -%d;", added, removed);
545
		else
546
			printf("  lines: +%d -%d;", removed, added);
547
	}
548
549
	if (rdp->rd_commitid != NULL)
550
		printf("  commitid: %s;", rdp->rd_commitid);
551
552
	printf("\n");
553
554
	if (!TAILQ_EMPTY(&(rdp->rd_branches))) {
555
		printf("branches:");
556
		TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) {
557
			RCSNUM *branch;
558
			branch = rcsnum_revtobr(rb->rb_num);
559
			(void)rcsnum_tostr(branch, numb, sizeof(numb));
560
			printf("  %s;", numb);
561
			rcsnum_free(branch);
562
		}
563
		printf("\n");
564
	}
565
566
	printf("%s", rdp->rd_log);
567
}