GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rcs/rcsprog.c Lines: 165 287 57.5 %
Date: 2017-11-07 Branches: 105 197 53.3 %

Line Branch Exec Source
1
/*	$OpenBSD: rcsprog.c,v 1.161 2016/07/04 01:39:12 millert Exp $	*/
2
/*
3
 * Copyright (c) 2005 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 <err.h>
30
#include <signal.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
36
#include "rcsprog.h"
37
38
#define RCSPROG_OPTSTRING	"A:a:b::c:e::Iik:Ll::m:Mn:N:o:qt::TUu::Vx::z::"
39
40
const char rcs_version[] = "OpenRCS 4.5";
41
42
int	 rcsflags;
43
int	 rcs_optind;
44
char	*rcs_optarg;
45
char	*rcs_suffixes = RCS_DEFAULT_SUFFIX;
46
char	*rcs_tmpdir = RCS_TMPDIR_DEFAULT;
47
48
struct rcs_prog {
49
	char	*prog_name;
50
	int	(*prog_hdlr)(int, char **);
51
	void	(*prog_usage)(void);
52
} programs[] = {
53
	{ "rcs",	rcs_main,	rcs_usage	},
54
	{ "ci",		checkin_main,	checkin_usage   },
55
	{ "co",		checkout_main,	checkout_usage  },
56
	{ "rcsclean",	rcsclean_main,	rcsclean_usage	},
57
	{ "rcsdiff",	rcsdiff_main,	rcsdiff_usage	},
58
	{ "rcsmerge",	rcsmerge_main,	rcsmerge_usage	},
59
	{ "rlog",	rlog_main,	rlog_usage	},
60
	{ "ident",	ident_main,	ident_usage	},
61
	{ "merge",	merge_main,	merge_usage	},
62
};
63
64
struct wklhead temp_files;
65
66
void sighdlr(int);
67
static void  rcs_attach_symbol(RCSFILE *, const char *);
68
69
/* ARGSUSED */
70
void
71
sighdlr(int sig)
72
{
73
	worklist_clean(&temp_files, worklist_unlink);
74
	_exit(1);
75
}
76
77
int
78
build_cmd(char ***cmd_argv, char **argv, int argc)
79
{
80
	int cmd_argc, i, cur;
81
356
	char *cp, *rcsinit, *linebuf, *lp;
82
83
178
	if ((rcsinit = getenv("RCSINIT")) == NULL) {
84
177
		*cmd_argv = argv;
85
177
		return argc;
86
	}
87
88
1
	cur = argc + 2;
89
	cmd_argc = 0;
90
1
	*cmd_argv = xcalloc(cur, sizeof(char *));
91
1
	(*cmd_argv)[cmd_argc++] = argv[0];
92
93
1
	linebuf = xstrdup(rcsinit);
94
4
	for (lp = linebuf; lp != NULL;) {
95
1
		cp = strsep(&lp, " \t\b\f\n\r\t\v");
96
1
		if (cp == NULL)
97
			break;
98
1
		if (*cp == '\0')
99
			continue;
100
101
1
		if (cmd_argc == cur) {
102
			cur += 8;
103
			*cmd_argv = xreallocarray(*cmd_argv, cur,
104
			    sizeof(char *));
105
		}
106
107
1
		(*cmd_argv)[cmd_argc++] = cp;
108
	}
109
110
1
	if (cmd_argc + argc > cur) {
111
		cur = cmd_argc + argc + 1;
112
		*cmd_argv = xreallocarray(*cmd_argv, cur,
113
		    sizeof(char *));
114
	}
115
116
6
	for (i = 1; i < argc; i++)
117
2
		(*cmd_argv)[cmd_argc++] = argv[i];
118
119
1
	(*cmd_argv)[cmd_argc] = NULL;
120
121
1
	return cmd_argc;
122
178
}
123
124
int
125
main(int argc, char **argv)
126
{
127
	u_int i;
128
356
	char **cmd_argv;
129
	int ret, cmd_argc;
130
131
178
	if (pledge("stdio rpath wpath cpath fattr flock getpw", NULL) == -1)
132
		err(2, "pledge");
133
134
	ret = -1;
135
178
	rcs_optind = 1;
136
178
	SLIST_INIT(&temp_files);
137
138
178
	cmd_argc = build_cmd(&cmd_argv, argv, argc);
139
140
178
	if ((rcs_tmpdir = getenv("TMPDIR")) == NULL)
141
178
		rcs_tmpdir = RCS_TMPDIR_DEFAULT;
142
143
178
	signal(SIGHUP, sighdlr);
144
178
	signal(SIGINT, sighdlr);
145
178
	signal(SIGQUIT, sighdlr);
146
178
	signal(SIGABRT, sighdlr);
147
178
	signal(SIGALRM, sighdlr);
148
178
	signal(SIGTERM, sighdlr);
149
150
1102
	for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++)
151
551
		if (strcmp(__progname, programs[i].prog_name) == 0) {
152
178
			usage = programs[i].prog_usage;
153
178
			ret = programs[i].prog_hdlr(cmd_argc, cmd_argv);
154
178
			break;
155
		}
156
157
	/* clean up temporary files */
158
	worklist_run(&temp_files, worklist_unlink);
159
160
	exit(ret);
161
}
162
163
164
__dead void
165
rcs_usage(void)
166
{
167
	fprintf(stderr,
168
	    "usage: rcs [-IiLqTUV] [-Aoldfile] [-ausers] [-b[rev]]\n"
169
	    "           [-cstring] [-e[users]] [-kmode] [-l[rev]] [-mrev:msg]\n"
170
	    "           [-orev] [-t[str]] [-u[rev]] [-xsuffixes] file ...\n");
171
172
	exit(1);
173
}
174
175
/*
176
 * rcs_main()
177
 *
178
 * Handler for the `rcs' program.
179
 * Returns 0 on success, or >0 on error.
180
 */
181
int
182
rcs_main(int argc, char **argv)
183
{
184
	int fd;
185
	int i, j, ch, flags, kflag, lkmode;
186
	const char *nflag, *oldfilename, *orange;
187
58
	char fpath[PATH_MAX];
188
	char *logstr, *logmsg, *descfile;
189
	char *alist, *comment, *elist, *lrev, *urev;
190
	mode_t fmode;
191
	RCSFILE *file;
192
	RCSNUM *logrev;
193
	struct rcs_access *acp;
194
	time_t rcs_mtime = -1;
195
196
	kflag = RCS_KWEXP_ERR;
197
	lkmode = RCS_LOCK_INVAL;
198
	fmode =  S_IRUSR|S_IRGRP|S_IROTH;
199
	flags = RCS_RDWR|RCS_PARSE_FULLY;
200
	lrev = urev = descfile = NULL;
201
	logstr = alist = comment = elist = NULL;
202
	nflag = oldfilename = orange = NULL;
203
204
	/* match GNU */
205

58
	if (1 < argc && argv[1][0] != '-')
206
		warnx("warning: No options were given; "
207
		    "this usage is obsolescent.");
208
209
92
	while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) {
210





126
		switch (ch) {
211
		case 'A':
212
1
			oldfilename = rcs_optarg;
213
1
			rcsflags |= CO_ACLAPPEND;
214
1
			break;
215
		case 'a':
216
3
			alist = rcs_optarg;
217
3
			break;
218
		case 'c':
219
			comment = rcs_optarg;
220
			break;
221
		case 'e':
222
1
			elist = rcs_optarg;
223
1
			rcsflags |= RCSPROG_EFLAG;
224
1
			break;
225
		case 'I':
226
			rcsflags |= INTERACTIVE;
227
			break;
228
		case 'i':
229
10
			flags |= RCS_CREATE;
230
10
			break;
231
		case 'k':
232
			kflag = rcs_kflag_get(rcs_optarg);
233
			if (RCS_KWEXP_INVAL(kflag)) {
234
				warnx("invalid RCS keyword substitution mode");
235
				(usage)();
236
			}
237
			break;
238
		case 'L':
239
			if (lkmode == RCS_LOCK_LOOSE)
240
				warnx("-U overridden by -L");
241
			lkmode = RCS_LOCK_STRICT;
242
			break;
243
		case 'l':
244
6
			if (rcsflags & RCSPROG_UFLAG)
245
				warnx("-u overridden by -l");
246
6
			lrev = rcs_optarg;
247
6
			rcsflags &= ~RCSPROG_UFLAG;
248
6
			rcsflags |= RCSPROG_LFLAG;
249
6
			break;
250
		case 'm':
251
2
			free(logstr);
252
2
			logstr = xstrdup(rcs_optarg);
253
2
			break;
254
		case 'M':
255
			/* ignore for the moment */
256
			break;
257
		case 'n':
258
			nflag = rcs_optarg;
259
			break;
260
		case 'N':
261
			nflag = rcs_optarg;
262
			rcsflags |= RCSPROG_NFLAG;
263
			break;
264
		case 'o':
265
1
			orange = rcs_optarg;
266
1
			break;
267
		case 'q':
268
29
			rcsflags |= QUIET;
269
29
			break;
270
		case 't':
271
5
			descfile = rcs_optarg;
272
5
			rcsflags |= DESCRIPTION;
273
5
			break;
274
		case 'T':
275
			rcsflags |= PRESERVETIME;
276
			break;
277
		case 'U':
278
			if (lkmode == RCS_LOCK_STRICT)
279
				warnx("-L overridden by -U");
280
			lkmode = RCS_LOCK_LOOSE;
281
			break;
282
		case 'u':
283
5
			if (rcsflags & RCSPROG_LFLAG)
284
				warnx("-l overridden by -u");
285
5
			urev = rcs_optarg;
286
5
			rcsflags &= ~RCSPROG_LFLAG;
287
5
			rcsflags |= RCSPROG_UFLAG;
288
5
			break;
289
		case 'V':
290
			printf("%s\n", rcs_version);
291
			exit(0);
292
		case 'x':
293
			/* Use blank extension if none given. */
294
			rcs_suffixes = rcs_optarg ? rcs_optarg : "";
295
			break;
296
		case 'z':
297
			/*
298
			 * kept for compatibility
299
			 */
300
			break;
301
		default:
302
			(usage)();
303
		}
304
	}
305
306
29
	argc -= rcs_optind;
307
29
	argv += rcs_optind;
308
309
29
	if (argc == 0) {
310
		warnx("no input file");
311
		(usage)();
312
	}
313
314
112
	for (i = 0; i < argc; i++) {
315
29
		fd = rcs_choosefile(argv[i], fpath, sizeof(fpath));
316

39
		if (fd < 0 && !(flags & RCS_CREATE)) {
317
			warn("%s", fpath);
318
			continue;
319
		}
320
321
29
		if (!(rcsflags & QUIET))
322
			(void)fprintf(stderr, "RCS file: %s\n", fpath);
323
324
29
		if ((file = rcs_open(fpath, fd, flags, fmode)) == NULL) {
325
			close(fd);
326
			continue;
327
		}
328
329
29
		if (rcsflags & DESCRIPTION) {
330
5
			if (rcs_set_description(file, descfile, rcsflags) == -1) {
331
				warn("%s", descfile);
332
				rcs_close(file);
333
				continue;
334
			}
335
		}
336
24
		else if (flags & RCS_CREATE) {
337
5
			if (rcs_set_description(file, NULL, rcsflags) == -1) {
338
				warn("stdin");
339
				rcs_close(file);
340
				continue;
341
			}
342
		}
343
344
29
		if (rcsflags & PRESERVETIME)
345
			rcs_mtime = rcs_get_mtime(file);
346
347
29
		if (nflag != NULL)
348
			rcs_attach_symbol(file, nflag);
349
350
29
		if (logstr != NULL) {
351
2
			if ((logmsg = strchr(logstr, ':')) == NULL) {
352
				warnx("missing log message");
353
				rcs_close(file);
354
				continue;
355
			}
356
357
2
			*logmsg++ = '\0';
358
2
			if ((logrev = rcsnum_parse(logstr)) == NULL) {
359
				warnx("`%s' bad revision number", logstr);
360
				rcs_close(file);
361
				continue;
362
			}
363
364
2
			if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
365
				warnx("failed to set logmsg for `%s' to `%s'",
366
				    logstr, logmsg);
367
				rcs_close(file);
368
				rcsnum_free(logrev);
369
				continue;
370
			}
371
372
2
			rcsnum_free(logrev);
373
2
		}
374
375
		/* entries to add from <oldfile> */
376
29
		if (rcsflags & CO_ACLAPPEND) {
377
			RCSFILE *oldfile;
378
			int ofd;
379
1
			char ofpath[PATH_MAX];
380
381
1
			ofd = rcs_choosefile(oldfilename, ofpath, sizeof(ofpath));
382
1
			if (ofd < 0) {
383
				if (!(flags & RCS_CREATE))
384
					warn("%s", ofpath);
385
				exit(1);
386
			}
387
1
			if ((oldfile = rcs_open(ofpath, ofd, RCS_READ)) == NULL)
388
				exit(1);
389
390
8
			TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
391
3
				rcs_access_add(file, acp->ra_name);
392
393
1
			rcs_close(oldfile);
394
1
			(void)close(ofd);
395
1
		}
396
397
		/* entries to add to the access list */
398
29
		if (alist != NULL) {
399
			struct rcs_argvector *aargv;
400
401
3
			aargv = rcs_strsplit(alist, ",");
402
24
			for (j = 0; aargv->argv[j] != NULL; j++)
403
9
				rcs_access_add(file, aargv->argv[j]);
404
405
3
			rcs_argv_destroy(aargv);
406
3
		}
407
408
29
		if (comment != NULL)
409
			rcs_comment_set(file, comment);
410
411
29
		if (elist != NULL) {
412
			struct rcs_argvector *eargv;
413
414
1
			eargv = rcs_strsplit(elist, ",");
415
8
			for (j = 0; eargv->argv[j] != NULL; j++)
416
3
				rcs_access_remove(file, eargv->argv[j]);
417
418
1
			rcs_argv_destroy(eargv);
419
29
		} else if (rcsflags & RCSPROG_EFLAG) {
420
			struct rcs_access *rap;
421
422
			/* XXX rcs_access_remove(file, NULL); ?? */
423
			while (!TAILQ_EMPTY(&(file->rf_access))) {
424
				rap = TAILQ_FIRST(&(file->rf_access));
425
				TAILQ_REMOVE(&(file->rf_access), rap, ra_list);
426
				free(rap->ra_name);
427
				free(rap);
428
			}
429
			/* not synced anymore */
430
			file->rf_flags &= ~RCS_SYNCED;
431
		}
432
433
29
		rcs_kwexp_set(file, kflag);
434
435
29
		if (lkmode != RCS_LOCK_INVAL)
436
			(void)rcs_lock_setmode(file, lkmode);
437
438
29
		if (rcsflags & RCSPROG_LFLAG) {
439
			RCSNUM *rev;
440
			const char *username;
441
6
			char rev_str[RCS_REV_BUFSZ];
442
443
6
			if (file->rf_head == NULL) {
444
				warnx("%s contains no revisions", fpath);
445
				rcs_close(file);
446
				continue;
447
			}
448
449
6
			if ((username = getlogin()) == NULL)
450
				err(1, "getlogin");
451
6
			if (lrev == NULL) {
452
3
				rev = rcsnum_alloc();
453
3
				rcsnum_cpy(file->rf_head, rev, 0);
454
6
			} else if ((rev = rcsnum_parse(lrev)) == NULL) {
455
				warnx("unable to unlock file");
456
				rcs_close(file);
457
				continue;
458
			}
459
6
			rcsnum_tostr(rev, rev_str, sizeof(rev_str));
460
			/* Make sure revision exists. */
461
6
			if (rcs_findrev(file, rev) == NULL)
462
				errx(1, "%s: cannot lock nonexisting "
463
				    "revision %s", fpath, rev_str);
464

9
			if (rcs_lock_add(file, username, rev) != -1 &&
465
4
			    !(rcsflags & QUIET))
466
				(void)fprintf(stderr, "%s locked\n", rev_str);
467
5
			rcsnum_free(rev);
468
10
		}
469
470
28
		if (rcsflags & RCSPROG_UFLAG) {
471
			RCSNUM *rev;
472
			const char *username;
473
5
			char rev_str[RCS_REV_BUFSZ];
474
475
5
			if (file->rf_head == NULL) {
476
				warnx("%s contains no revisions", fpath);
477
				rcs_close(file);
478
				continue;
479
			}
480
481
5
			if ((username = getlogin()) == NULL)
482
				err(1, "getlogin");
483
5
			if (urev == NULL) {
484
2
				rev = rcsnum_alloc();
485
2
				rcsnum_cpy(file->rf_head, rev, 0);
486
5
			} else if ((rev = rcsnum_parse(urev)) == NULL) {
487
				warnx("unable to unlock file");
488
				rcs_close(file);
489
				continue;
490
			}
491
5
			rcsnum_tostr(rev, rev_str, sizeof(rev_str));
492
			/* Make sure revision exists. */
493
5
			if (rcs_findrev(file, rev) == NULL)
494
				errx(1, "%s: cannot unlock nonexisting "
495
				    "revision %s", fpath, rev_str);
496

5
			if (rcs_lock_remove(file, username, rev) == -1 &&
497
1
			    !(rcsflags & QUIET))
498
				warnx("%s: warning: No locks are set.", fpath);
499
			else {
500
4
				if (!(rcsflags & QUIET))
501
					(void)fprintf(stderr,
502
					    "%s unlocked\n", rev_str);
503
			}
504
4
			rcsnum_free(rev);
505
8
		}
506
507
27
		if (orange != NULL) {
508
			struct rcs_delta *rdp, *nrdp;
509
1
			char b[RCS_REV_BUFSZ];
510
511
1
			rcs_rev_select(file, orange);
512
14
			for (rdp = TAILQ_FIRST(&(file->rf_delta));
513
7
			    rdp != NULL; rdp = nrdp) {
514
6
				nrdp = TAILQ_NEXT(rdp, rd_list);
515
516
				/*
517
				 * Delete selected revisions.
518
				 */
519
6
				if (rdp->rd_flags & RCS_RD_SELECT) {
520
3
					rcsnum_tostr(rdp->rd_num, b, sizeof(b));
521
3
					if (!(rcsflags & QUIET)) {
522
						(void)fprintf(stderr, "deleting"
523
						    " revision %s\n", b);
524
					}
525
3
					(void)rcs_rev_remove(file, rdp->rd_num);
526
3
				}
527
			}
528
1
		}
529
530
27
		rcs_write(file);
531
532
27
		if (rcsflags & PRESERVETIME)
533
			rcs_set_mtime(file, rcs_mtime);
534
535
27
		rcs_close(file);
536
537
27
		if (!(rcsflags & QUIET))
538
			(void)fprintf(stderr, "done\n");
539
	}
540
541
27
	return (0);
542
27
}
543
544
static void
545
rcs_attach_symbol(RCSFILE *file, const char *symname)
546
{
547
	char *rnum;
548
	RCSNUM *rev;
549
	char rbuf[RCS_REV_BUFSZ];
550
	int rm;
551
552
	rm = 0;
553
	rev = NULL;
554
	if ((rnum = strrchr(symname, ':')) != NULL) {
555
		if (rnum[1] == '\0')
556
			rev = file->rf_head;
557
		*(rnum++) = '\0';
558
	} else {
559
		rm = 1;
560
	}
561
562
	if (rev == NULL && rm != 1) {
563
		if ((rev = rcsnum_parse(rnum)) == NULL)
564
			errx(1, "bad revision %s", rnum);
565
	}
566
567
	if (rcsflags & RCSPROG_NFLAG)
568
		rm = 1;
569
570
	if (rm == 1) {
571
		if (rcs_sym_remove(file, symname) < 0) {
572
			if (rcs_errno == RCS_ERR_NOENT &&
573
			    !(rcsflags & RCSPROG_NFLAG))
574
				warnx("cannot delete nonexisting symbol %s",
575
				    symname);
576
		} else {
577
			if (rcsflags & RCSPROG_NFLAG)
578
				rm = 0;
579
		}
580
	}
581
582
	if (rm == 0) {
583
		if (rcs_sym_add(file, symname, rev) < 0 &&
584
		    rcs_errno == RCS_ERR_DUPENT) {
585
			rcsnum_tostr(rcs_sym_getrev(file, symname),
586
			    rbuf, sizeof(rbuf));
587
			errx(1, "symbolic name %s already bound to %s",
588
			    symname, rbuf);
589
		}
590
	}
591
}