GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/getlog.c Lines: 108 283 38.2 %
Date: 2017-11-07 Branches: 64 230 27.8 %

Line Branch Exec Source
1
/*	$OpenBSD: getlog.c,v 1.101 2017/06/01 08:08:24 joris Exp $	*/
2
/*
3
 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
4
 * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <unistd.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <errno.h>
23
#include <ctype.h>
24
25
#include "cvs.h"
26
#include "remote.h"
27
28
#define L_HEAD		0x01
29
#define L_HEAD_DESCR	0x02
30
#define L_NAME		0x04
31
#define L_NOTAGS	0x08
32
#define L_LOGINS	0x10
33
#define L_STATES	0x20
34
35
#define LDATE_LATER	0x01
36
#define LDATE_EARLIER	0x02
37
#define LDATE_SINGLE	0x04
38
#define LDATE_RANGE	0x08
39
#define LDATE_INCLUSIVE	0x10
40
41
void		 cvs_log_local(struct cvs_file *);
42
static void	 log_rev_print(struct rcs_delta *);
43
static char 	*push_date(char *dest, const char *);
44
static u_int	 date_select(RCSFILE *, char *);
45
46
int	 runflags = 0;
47
char	*logrev = NULL;
48
char	*logdate = NULL;
49
char	*slist = NULL;
50
char	*wlist = NULL;
51
52
struct cvs_cmd cvs_cmd_log = {
53
	CVS_OP_LOG, CVS_USE_WDIR, "log",
54
	{ "lo" },
55
	"Print out history information for files",
56
	"[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]",
57
	"bd:hlNRr:s:tw:",
58
	NULL,
59
	cvs_getlog
60
};
61
62
struct cvs_cmd cvs_cmd_rlog = {
63
	CVS_OP_RLOG, 0, "rlog",
64
	{ "rlo" },
65
	"Print out history information for files",
66
	"[-bhlNRt] [-d dates] [-r revisions] [-s states] [-w logins]",
67
	"bd:hlNRr:s:tw:",
68
	NULL,
69
	cvs_getlog
70
};
71
72
int
73
cvs_getlog(int argc, char **argv)
74
{
75
	int ch, flags, i;
76
4
	char *arg = ".";
77
2
	struct cvs_recursion cr;
78
79
	flags = CR_RECURSE_DIRS;
80
81
6
	while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_LOG ?
82
2
	    cvs_cmd_log.cmd_opts : cvs_cmd_rlog.cmd_opts)) != -1) {
83
		switch (ch) {
84
		case 'd':
85
			logdate = push_date(logdate, optarg);
86
			break;
87
		case 'h':
88
			runflags |= L_HEAD;
89
			break;
90
		case 'l':
91
			flags &= ~CR_RECURSE_DIRS;
92
			break;
93
		case 'N':
94
			runflags |= L_NOTAGS;
95
			break;
96
		case 'R':
97
			runflags |= L_NAME;
98
			break;
99
		case 'r':
100
			logrev = optarg;
101
			break;
102
		case 's':
103
			runflags |= L_STATES;
104
			slist = optarg;
105
			break;
106
		case 't':
107
			runflags |= L_HEAD_DESCR;
108
			break;
109
		case 'w':
110
			runflags |= L_LOGINS;
111
			wlist = optarg;
112
			break;
113
		default:
114
			fatal("%s", cvs_cmdop == CVS_OP_LOG ?
115
			    cvs_cmd_log.cmd_synopsis :
116
			    cvs_cmd_rlog.cmd_synopsis);
117
		}
118
	}
119
120
2
	argc -= optind;
121
2
	argv += optind;
122
123
2
	if (cvs_cmdop == CVS_OP_RLOG) {
124
1
		flags |= CR_REPO;
125
126
1
		if (argc == 0)
127
			return 0;
128
129
4
		for (i = 0; i < argc; i++)
130
1
			if (argv[i][0] == '/')
131
				fatal("Absolute path name is invalid: %s",
132
				    argv[i]);
133
	}
134
135
2
	cr.enterdir = NULL;
136
2
	cr.leavedir = NULL;
137
138
2
	if (cvsroot_is_remote()) {
139
		cvs_client_connect_to_server();
140
		cr.fileproc = cvs_client_sendfile;
141
142
		if (logdate != NULL)
143
			cvs_client_send_request("Argument -d%s", logdate);
144
145
		if (runflags & L_HEAD)
146
			cvs_client_send_request("Argument -h");
147
148
		if (!(flags & CR_RECURSE_DIRS))
149
			cvs_client_send_request("Argument -l");
150
151
		if (runflags & L_NOTAGS)
152
			cvs_client_send_request("Argument -N");
153
154
		if (runflags & L_NAME)
155
			cvs_client_send_request("Argument -R");
156
157
		if (logrev != NULL)
158
			cvs_client_send_request("Argument -r%s", logrev);
159
160
		if (runflags & L_STATES)
161
			cvs_client_send_request("Argument -s%s", slist);
162
163
		if (runflags & L_HEAD_DESCR)
164
			cvs_client_send_request("Argument -t");
165
166
		if (runflags & L_LOGINS)
167
			cvs_client_send_request("Argument -w%s", wlist);
168
	} else {
169

3
		if (cvs_cmdop == CVS_OP_RLOG &&
170
1
		    chdir(current_cvsroot->cr_dir) == -1)
171
			fatal("cvs_getlog: %s", strerror(errno));
172
173
2
		cr.fileproc = cvs_log_local;
174
	}
175
176
2
	cr.flags = flags;
177
178

3
	if (cvs_cmdop == CVS_OP_LOG || cvsroot_is_local()) {
179
2
		if (argc > 0)
180
1
			cvs_file_run(argc, argv, &cr);
181
		else
182
1
			cvs_file_run(1, &arg, &cr);
183
	}
184
185
2
	if (cvsroot_is_remote()) {
186
		cvs_client_send_files(argv, argc);
187
		cvs_client_senddir(".");
188
189
		cvs_client_send_request((cvs_cmdop == CVS_OP_RLOG) ?
190
		    "rlog" : "log");
191
192
		cvs_client_get_responses();
193
	}
194
195
2
	return (0);
196
2
}
197
198
void
199
cvs_log_local(struct cvs_file *cf)
200
{
201
	u_int nrev;
202
	RCSNUM *rev;
203
	struct rcs_sym *sym;
204
	struct rcs_lock *lkp;
205
	struct rcs_delta *rdp;
206
	struct rcs_access *acp;
207
20
	char numb[CVS_REV_BUFSZ];
208
209
10
	cvs_log(LP_TRACE, "cvs_log_local(%s)", cf->file_path);
210
211
10
	cvs_file_classify(cf, cvs_directory_tag);
212
213
10
	if (cf->file_type == CVS_DIR) {
214
4
		if (verbosity > 1)
215
			cvs_log(LP_ERR, "Logging %s", cf->file_path);
216
4
		return;
217
	}
218
219
6
	if (cf->file_rcs == NULL) {
220
		return;
221
6
	} else if (cf->file_status == FILE_ADDED) {
222
		if (verbosity > 0)
223
			cvs_log(LP_ERR, "%s has been added, but not committed",
224
			    cf->file_path);
225
		return;
226
	}
227
228
6
	if (runflags & L_NAME) {
229
		cvs_printf("%s\n", cf->file_rpath);
230
		return;
231
	}
232
233
6
	if (logrev != NULL)
234
		nrev = cvs_revision_select(cf->file_rcs, logrev);
235
6
	else if (logdate != NULL) {
236
		if ((nrev = date_select(cf->file_rcs, logdate)) == (u_int)-1) {
237
			cvs_log(LP_ERR, "invalid date: %s", logdate);
238
			return;
239
		}
240
	} else
241
6
		nrev = cf->file_rcs->rf_ndelta;
242
243
6
	cvs_printf("\nRCS file: %s", cf->file_rpath);
244
245
6
	if (cvs_cmdop != CVS_OP_RLOG)
246
3
		cvs_printf("\nWorking file: %s", cf->file_path);
247
248
6
	cvs_printf("\nhead:");
249
6
	if (cf->file_rcs->rf_head != NULL)
250
6
		cvs_printf(" %s", rcsnum_tostr(cf->file_rcs->rf_head,
251
6
		    numb, sizeof(numb)));
252
253
6
	cvs_printf("\nbranch:");
254
6
	if (rcs_branch_get(cf->file_rcs) != NULL) {
255
		cvs_printf(" %s", rcsnum_tostr(rcs_branch_get(cf->file_rcs),
256
		    numb, sizeof(numb)));
257
	}
258
259
6
	cvs_printf("\nlocks: %s", (cf->file_rcs->rf_flags & RCS_SLOCK)
260
	    ? "strict" : "");
261
12
	TAILQ_FOREACH(lkp, &(cf->file_rcs->rf_locks), rl_list)
262
		cvs_printf("\n\t%s: %s", lkp->rl_name,
263
		    rcsnum_tostr(lkp->rl_num, numb, sizeof(numb)));
264
265
6
	cvs_printf("\naccess list:\n");
266
12
	TAILQ_FOREACH(acp, &(cf->file_rcs->rf_access), ra_list)
267
		cvs_printf("\t%s\n", acp->ra_name);
268
269
6
	if (!(runflags & L_NOTAGS)) {
270
6
		cvs_printf("symbolic names:\n");
271
36
		TAILQ_FOREACH(sym, &(cf->file_rcs->rf_symbols), rs_list) {
272
12
			rev = rcsnum_alloc();
273
12
			rcsnum_cpy(sym->rs_num, rev, 0);
274
12
			if (RCSNUM_ISBRANCH(sym->rs_num))
275
8
				rcsnum_addmagic(rev);
276
277
24
			cvs_printf("\t%s: %s\n", sym->rs_name,
278
12
			    rcsnum_tostr(rev, numb, sizeof(numb)));
279
12
			free(rev);
280
		}
281
	}
282
283
6
	cvs_printf("keyword substitution: %s\n",
284
12
	    cf->file_rcs->rf_expand == NULL ? "kv" : cf->file_rcs->rf_expand);
285
286
6
	cvs_printf("total revisions: %u", cf->file_rcs->rf_ndelta);
287
288

12
	if (cf->file_rcs->rf_head != NULL &&
289
12
	    !(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR))
290
6
		cvs_printf(";\tselected revisions: %u", nrev);
291
292
6
	cvs_printf("\n");
293
294

6
	if (!(runflags & L_HEAD) || (runflags & L_HEAD_DESCR))
295
6
		cvs_printf("description:\n%s", cf->file_rcs->rf_desc);
296
297

12
	if (!(runflags & L_HEAD) && !(runflags & L_HEAD_DESCR)) {
298
36
		TAILQ_FOREACH(rdp, &(cf->file_rcs->rf_delta), rd_list) {
299
			/*
300
			 * if selections are enabled verify that entry is
301
			 * selected.
302
			 */
303

12
			if ((logrev == NULL && logdate == NULL) ||
304
			    (rdp->rd_flags & RCS_RD_SELECT))
305
12
				log_rev_print(rdp);
306
		}
307
	}
308
309
6
	cvs_printf("%s\n", LOG_REVEND);
310
16
}
311
312
static void
313
log_rev_print(struct rcs_delta *rdp)
314
{
315
	int i, found;
316
24
	char numb[CVS_REV_BUFSZ], timeb[CVS_TIME_BUFSZ];
317
	struct cvs_argvector *sargv, *wargv;
318
	struct rcs_branch *rb;
319
	struct rcs_delta *nrdp;
320
321
	i = found = 0;
322
323
	/* -s states */
324
12
	if (runflags & L_STATES) {
325
		sargv = cvs_strsplit(slist, ",");
326
		for (i = 0; sargv->argv[i] != NULL; i++) {
327
			if (strcmp(rdp->rd_state, sargv->argv[i]) == 0) {
328
				found++;
329
				break;
330
			}
331
			found = 0;
332
		}
333
		cvs_argv_destroy(sargv);
334
	}
335
336
	/* -w[logins] */
337
12
	if (runflags & L_LOGINS) {
338
		wargv = cvs_strsplit(wlist, ",");
339
		for (i = 0; wargv->argv[i] != NULL; i++) {
340
			if (strcmp(rdp->rd_author, wargv->argv[i]) == 0) {
341
				found++;
342
				break;
343
			}
344
			found = 0;
345
		}
346
		cvs_argv_destroy(wargv);
347
	}
348
349
12
	if ((runflags & (L_STATES|L_LOGINS)) && found == 0)
350
		return;
351
352
12
	cvs_printf("%s\n", LOG_REVSEP);
353
354
12
	rcsnum_tostr(rdp->rd_num, numb, sizeof(numb));
355
12
	cvs_printf("revision %s", numb);
356
357
12
	strftime(timeb, sizeof(timeb), "%Y/%m/%d %H:%M:%S", &rdp->rd_date);
358
12
	cvs_printf("\ndate: %s;  author: %s;  state: %s;",
359
12
	    timeb, rdp->rd_author, rdp->rd_state);
360
361
	/*
362
	 * If we are a branch revision, the diff of this revision is stored
363
	 * in place.
364
	 * Otherwise, it is stored in the previous revision as a reversed diff.
365
	 */
366

24
	if (RCSNUM_ISBRANCHREV(rdp->rd_num))
367
4
		nrdp = rdp;
368
	else
369
8
		nrdp = TAILQ_NEXT(rdp, rd_list);
370
371
	/*
372
	 * We do not write diff stats for the first revision of the default
373
	 * branch, since it was not a diff but a full text.
374
	 */
375

20
	if (nrdp != NULL && rdp->rd_num->rn_len == nrdp->rd_num->rn_len) {
376
6
		int added, removed;
377
6
		rcs_delta_stats(nrdp, &added, &removed);
378

12
		if (RCSNUM_ISBRANCHREV(rdp->rd_num))
379
4
			cvs_printf("  lines: +%d -%d;", added, removed);
380
		else
381
2
			cvs_printf("  lines: +%d -%d;", removed, added);
382
6
	}
383
384
12
	if (rdp->rd_commitid != NULL)
385
		printf("  commitid: %s;", rdp->rd_commitid);
386
387
12
	cvs_printf("\n");
388
389
12
	if (!TAILQ_EMPTY(&(rdp->rd_branches))) {
390
4
		cvs_printf("branches:");
391
16
		TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) {
392
			RCSNUM *branch;
393
4
			branch = rcsnum_revtobr(rb->rb_num);
394
4
			rcsnum_tostr(branch, numb, sizeof(numb));
395
4
			cvs_printf("  %s;", numb);
396
4
			free(branch);
397
		}
398
4
		cvs_printf("\n");
399
4
	}
400
401
12
	cvs_printf("%s", rdp->rd_log);
402
24
}
403
404
static char *
405
push_date(char *dest, const char *src)
406
{
407
	size_t len;
408
409
	if (dest == NULL)
410
		return (xstrdup(src));
411
412
	/* 2 = ; and '\0' */
413
	len = strlen(dest) + strlen(src) + 2;
414
415
	dest[strlen(dest)] = ';';
416
	dest = xreallocarray(dest, len, 1);
417
	strlcat(dest, src, len);
418
	return (dest);
419
}
420
421
static u_int
422
date_select(RCSFILE *file, char *date)
423
{
424
	int i, nrev, flags;
425
	struct rcs_delta *rdp;
426
	struct cvs_argvector *args;
427
	char *first, *last, delim;
428
	time_t firstdate, lastdate, rcsdate;
429
430
	nrev = 0;
431
	args = cvs_strsplit(date, ";");
432
433
	for (i = 0; args->argv[i] != NULL; i++) {
434
		flags = 0;
435
		firstdate = lastdate = -1;
436
437
		first = args->argv[i];
438
		last = strchr(args->argv[i], '<');
439
		if (last != NULL) {
440
			delim = *last;
441
			*last++ = '\0';
442
443
			if (*last == '=') {
444
				last++;
445
				flags |= LDATE_INCLUSIVE;
446
			}
447
		} else {
448
			last = strchr(args->argv[i], '>');
449
			if (last != NULL) {
450
				delim = *last;
451
				*last++ = '\0';
452
453
				if (*last == '=') {
454
					last++;
455
					flags |= LDATE_INCLUSIVE;
456
				}
457
			}
458
		}
459
460
		if (last == NULL) {
461
			flags |= LDATE_SINGLE;
462
			if ((firstdate = date_parse(first)) == -1)
463
				return -1;
464
			delim = '\0';
465
			last = "\0";
466
		} else {
467
			while (*last && isspace((unsigned char)*last))
468
				last++;
469
		}
470
471
		if (delim == '>' && *last == '\0') {
472
			flags |= LDATE_EARLIER;
473
			if ((firstdate = date_parse(first)) == -1)
474
				return -1;
475
		}
476
477
		if (delim == '>' && *first == '\0' && *last != '\0') {
478
			flags |= LDATE_LATER;
479
			if ((firstdate = date_parse(last)) == -1)
480
				return -1;
481
		}
482
483
		if (delim == '<' && *last == '\0') {
484
			flags |= LDATE_LATER;
485
			if ((firstdate = date_parse(first)) == -1)
486
				return -1;
487
		}
488
489
		if (delim == '<' && *first == '\0' && *last != '\0') {
490
			flags |= LDATE_EARLIER;
491
			if ((firstdate = date_parse(last)) == -1)
492
				return -1;
493
		}
494
495
		if (*first != '\0' && *last != '\0') {
496
			flags |= LDATE_RANGE;
497
498
			if (delim == '<') {
499
				firstdate = date_parse(first);
500
				lastdate = date_parse(last);
501
			} else {
502
				firstdate = date_parse(last);
503
				lastdate = date_parse(first);
504
			}
505
			if (firstdate == -1 || lastdate == -1)
506
				return -1;
507
		}
508
509
		TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
510
			rcsdate = mktime(&(rdp->rd_date));
511
512
			if (flags & LDATE_SINGLE) {
513
				if (rcsdate <= firstdate) {
514
					rdp->rd_flags |= RCS_RD_SELECT;
515
					nrev++;
516
					break;
517
				}
518
			}
519
520
			if (flags & LDATE_EARLIER) {
521
				if (rcsdate < firstdate) {
522
					rdp->rd_flags |= RCS_RD_SELECT;
523
					nrev++;
524
					continue;
525
				}
526
527
				if (flags & LDATE_INCLUSIVE &&
528
				    (rcsdate <= firstdate)) {
529
					rdp->rd_flags |= RCS_RD_SELECT;
530
					nrev++;
531
					continue;
532
				}
533
			}
534
535
			if (flags & LDATE_LATER) {
536
				if (rcsdate > firstdate) {
537
					rdp->rd_flags |= RCS_RD_SELECT;
538
					nrev++;
539
					continue;
540
				}
541
542
				if (flags & LDATE_INCLUSIVE &&
543
				    (rcsdate >= firstdate)) {
544
					rdp->rd_flags |= RCS_RD_SELECT;
545
					nrev++;
546
					continue;
547
				}
548
			}
549
550
			if (flags & LDATE_RANGE) {
551
				if ((rcsdate > firstdate) &&
552
				    (rcsdate < lastdate)) {
553
					rdp->rd_flags |= RCS_RD_SELECT;
554
					nrev++;
555
					continue;
556
				}
557
558
				if (flags & LDATE_INCLUSIVE &&
559
				    ((rcsdate >= firstdate) &&
560
				    (rcsdate <= lastdate))) {
561
					rdp->rd_flags |= RCS_RD_SELECT;
562
					nrev++;
563
					continue;
564
				}
565
			}
566
		}
567
	}
568
569
	cvs_argv_destroy(args);
570
571
	return (nrev);
572
}