GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/checkout.c Lines: 178 333 53.5 %
Date: 2017-11-07 Branches: 88 212 41.5 %

Line Branch Exec Source
1
/*	$OpenBSD: checkout.c,v 1.171 2017/06/01 08:08:24 joris Exp $	*/
2
/*
3
 * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <sys/types.h>
19
#include <sys/dirent.h>
20
#include <sys/stat.h>
21
#include <sys/time.h>
22
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <libgen.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <time.h>
29
#include <unistd.h>
30
31
#include "cvs.h"
32
#include "diff.h"
33
#include "remote.h"
34
35
static void checkout_check_repository(int, char **);
36
static int checkout_classify(const char *, const char *);
37
static void checkout_repository(const char *, const char *);
38
39
extern int print_stdout;
40
extern int prune_dirs;
41
extern int build_dirs;
42
43
static int flags = CR_REPO | CR_RECURSE_DIRS;
44
static int Aflag = 0;
45
static char *dflag = NULL;
46
static char *koptstr = NULL;
47
static char *dateflag = NULL;
48
49
static int nflag = 0;
50
51
char *checkout_target_dir = NULL;
52
53
time_t cvs_specified_date = -1;
54
time_t cvs_directory_date = -1;
55
int disable_fast_checkout = 0;
56
57
struct cvs_cmd cvs_cmd_checkout = {
58
	CVS_OP_CHECKOUT, CVS_USE_WDIR, "checkout",
59
	{ "co", "get" },
60
	"Checkout a working copy of a repository",
61
	"[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] "
62
	"[-t id] module ...",
63
	"AcD:d:fj:k:lNnPpRr:st:",
64
	NULL,
65
	cvs_checkout
66
};
67
68
struct cvs_cmd cvs_cmd_export = {
69
	CVS_OP_EXPORT, CVS_USE_WDIR, "export",
70
	{ "exp", "ex" },
71
	"Export sources from CVS, similar to checkout",
72
	"[-flNnR] [-d dir] [-k mode] -D date | -r rev module ...",
73
	"D:d:k:flNnRr:",
74
	NULL,
75
	cvs_export
76
};
77
78
int
79
cvs_checkout(int argc, char **argv)
80
{
81
	int ch;
82
83
46
	while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) {
84



20
		switch (ch) {
85
		case 'A':
86
			Aflag = 1;
87
			if (koptstr == NULL)
88
				reset_option = 1;
89
			if (cvs_specified_tag == NULL)
90
				reset_tag = 1;
91
			break;
92
		case 'c':
93
			cvs_modules_list();
94
			exit(0);
95
		case 'D':
96
			dateflag = optarg;
97
			if ((cvs_specified_date = date_parse(dateflag)) == -1)
98
				fatal("invalid date: %s", dateflag);
99
			reset_tag = 0;
100
			break;
101
		case 'd':
102
1
			if (dflag != NULL)
103
				fatal("-d specified two or more times");
104
1
			dflag = optarg;
105
1
			checkout_target_dir = dflag;
106
107
1
			if (cvs_server_active == 1)
108
				disable_fast_checkout = 1;
109
			break;
110
		case 'j':
111
			if (cvs_join_rev1 == NULL)
112
				cvs_join_rev1 = optarg;
113
			else if (cvs_join_rev2 == NULL)
114
				cvs_join_rev2 = optarg;
115
			else
116
				fatal("too many -j options");
117
			break;
118
		case 'k':
119
2
			reset_option = 0;
120
2
			koptstr = optarg;
121
2
			kflag = rcs_kflag_get(koptstr);
122

4
			if (RCS_KWEXP_INVAL(kflag)) {
123
				cvs_log(LP_ERR,
124
				    "invalid RCS keyword expansion mode");
125
				fatal("%s", cvs_cmd_checkout.cmd_synopsis);
126
			}
127
			break;
128
		case 'l':
129
			flags &= ~CR_RECURSE_DIRS;
130
			break;
131
		case 'N':
132
			break;
133
		case 'n':
134
			nflag = 1;
135
			break;
136
		case 'P':
137
			prune_dirs = 1;
138
			break;
139
		case 'p':
140
1
			cmdp->cmd_flags &= ~CVS_USE_WDIR;
141
1
			print_stdout = 1;
142
1
			cvs_noexec = 1;
143
1
			nflag = 1;
144
1
			break;
145
		case 'R':
146
			flags |= CR_RECURSE_DIRS;
147
			break;
148
		case 'r':
149
6
			reset_tag = 0;
150
6
			cvs_specified_tag = optarg;
151
6
			break;
152
		default:
153
			fatal("%s", cvs_cmd_checkout.cmd_synopsis);
154
		}
155
	}
156
157
12
	argc -= optind;
158
12
	argv += optind;
159
160
12
	if (argc == 0)
161
		fatal("%s", cvs_cmd_checkout.cmd_synopsis);
162
163
12
	if (cvs_server_active == 1 && disable_fast_checkout != 1) {
164
		cmdp->cmd_flags &= ~CVS_USE_WDIR;
165
		cvs_noexec = 1;
166
	}
167
168
12
	checkout_check_repository(argc, argv);
169
170
12
	if (cvs_server_active == 1 && disable_fast_checkout != 1)
171
		cvs_noexec = 0;
172
173
12
	return (0);
174
}
175
176
int
177
cvs_export(int argc, char **argv)
178
{
179
	int ch;
180
181
2
	prune_dirs = 1;
182
183
3
	while ((ch = getopt(argc, argv, cvs_cmd_export.cmd_opts)) != -1) {
184

2
		switch (ch) {
185
		case 'd':
186
			if (dflag != NULL)
187
				fatal("-d specified two or more times");
188
			dflag = optarg;
189
			checkout_target_dir = dflag;
190
191
			if (cvs_server_active == 1)
192
				disable_fast_checkout = 1;
193
			break;
194
		case 'k':
195
			koptstr = optarg;
196
			kflag = rcs_kflag_get(koptstr);
197
			if (RCS_KWEXP_INVAL(kflag)) {
198
				cvs_log(LP_ERR,
199
				    "invalid RCS keyword expansion mode");
200
				fatal("%s", cvs_cmd_export.cmd_synopsis);
201
			}
202
			break;
203
		case 'l':
204
			flags &= ~CR_RECURSE_DIRS;
205
			break;
206
		case 'N':
207
			break;
208
		case 'R':
209
			flags |= CR_RECURSE_DIRS;
210
			break;
211
		case 'r':
212
1
			cvs_specified_tag = optarg;
213
1
			break;
214
		default:
215
			fatal("%s", cvs_cmd_export.cmd_synopsis);
216
		}
217
	}
218
219
1
	argc -= optind;
220
1
	argv += optind;
221
222
1
	if (cvs_specified_tag == NULL)
223
		fatal("must specify a tag or date");
224
225
1
	if (argc == 0)
226
		fatal("%s", cvs_cmd_export.cmd_synopsis);
227
228
1
	checkout_check_repository(argc, argv);
229
230
1
	return (0);
231
}
232
233
static void
234
checkout_check_repository(int argc, char **argv)
235
{
236
	int i;
237
	char *wdir, *d;
238
26
	struct cvs_recursion cr;
239
	struct module_checkout *mc;
240
	struct cvs_ignpat *ip;
241
	struct cvs_filelist *fl, *nxt;
242
13
	char repo[PATH_MAX], fpath[PATH_MAX], *f[1];
243
244
13
	build_dirs = print_stdout ? 0 : 1;
245
246
13
	if (cvsroot_is_remote()) {
247
		cvs_client_connect_to_server();
248
249
		if (cvs_specified_tag != NULL)
250
			cvs_client_send_request("Argument -r%s",
251
			    cvs_specified_tag);
252
		if (Aflag)
253
			cvs_client_send_request("Argument -A");
254
255
		if (dateflag != NULL)
256
			cvs_client_send_request("Argument -D%s", dateflag);
257
258
		if (kflag)
259
			cvs_client_send_request("Argument -k%s", koptstr);
260
261
		if (dflag != NULL)
262
			cvs_client_send_request("Argument -d%s", dflag);
263
264
		if (!(flags & CR_RECURSE_DIRS))
265
			cvs_client_send_request("Argument -l");
266
267
		if (cvs_cmdop == CVS_OP_CHECKOUT && prune_dirs == 1)
268
			cvs_client_send_request("Argument -P");
269
270
		if (print_stdout == 1)
271
			cvs_client_send_request("Argument -p");
272
273
		if (nflag == 1)
274
			cvs_client_send_request("Argument -n");
275
276
		cr.enterdir = NULL;
277
		cr.leavedir = NULL;
278
		if (print_stdout)
279
			cr.fileproc = NULL;
280
		else
281
			cr.fileproc = cvs_client_sendfile;
282
283
		flags &= ~CR_REPO;
284
		cr.flags = flags;
285
286
		if (cvs_cmdop != CVS_OP_EXPORT)
287
			cvs_file_run(argc, argv, &cr);
288
289
		cvs_client_send_files(argv, argc);
290
		cvs_client_senddir(".");
291
292
		cvs_client_send_request("%s",
293
		    (cvs_cmdop == CVS_OP_CHECKOUT) ? "co" : "export");
294
295
		cvs_client_get_responses();
296
297
		return;
298
	}
299
300
52
	for (i = 0; i < argc; i++) {
301
13
		mc = cvs_module_lookup(argv[i]);
302
13
		current_module = mc;
303
304
26
		RB_FOREACH(fl, cvs_flisthead, &(mc->mc_ignores))
305
			cvs_file_ignore(fl->file_path, &checkout_ign_pats);
306
307
52
		RB_FOREACH(fl, cvs_flisthead, &(mc->mc_modules)) {
308
13
			module_repo_root = NULL;
309
310
26
			(void)xsnprintf(repo, sizeof(repo), "%s/%s",
311
13
			    current_cvsroot->cr_dir, fl->file_path);
312
313
13
			if (!(mc->mc_flags & MODULE_ALIAS) || dflag != NULL)
314
1
				module_repo_root = xstrdup(fl->file_path);
315
316
13
			if (mc->mc_flags & MODULE_NORECURSE)
317
				flags &= ~CR_RECURSE_DIRS;
318
319
13
			if (dflag != NULL)
320
1
				wdir = dflag;
321
12
			else if (mc->mc_flags & MODULE_ALIAS)
322
12
				wdir = fl->file_path;
323
			else
324
				wdir = mc->mc_name;
325
326
26
			switch (checkout_classify(repo, fl->file_path)) {
327
			case CVS_FILE:
328
1
				cr.fileproc = cvs_update_local;
329
1
				cr.flags = flags;
330
331
1
				if (!(mc->mc_flags & MODULE_ALIAS)) {
332
					module_repo_root =
333
					    xstrdup(dirname(fl->file_path));
334
					d = wdir;
335
					(void)xsnprintf(fpath, sizeof(fpath),
336
					    "%s/%s", d,
337
					    basename(fl->file_path));
338
				} else {
339
1
					d = dirname(wdir);
340
1
					strlcpy(fpath, fl->file_path,
341
					    sizeof(fpath));
342
				}
343
344
1
				if (build_dirs == 1)
345
					cvs_mkpath(d, cvs_specified_tag);
346
347
1
				f[0] = fpath;
348
1
				cvs_file_run(1, f, &cr);
349
1
				break;
350
			case CVS_DIR:
351
12
				if (build_dirs == 1)
352
12
					cvs_mkpath(wdir, cvs_specified_tag);
353
12
				checkout_repository(repo, wdir);
354
12
				break;
355
			default:
356
				break;
357
			}
358
359

25
			if (nflag != 1 && mc->mc_prog != NULL &&
360
			    mc->mc_flags & MODULE_RUN_ON_CHECKOUT)
361
				cvs_exec(mc->mc_prog, NULL, 0);
362
363
13
			free(module_repo_root);
364
		}
365
366
13
		if (mc->mc_canfree == 1) {
367
52
			for (fl = RB_MIN(cvs_flisthead, &(mc->mc_modules));
368
26
			    fl != NULL; fl = nxt) {
369
13
				nxt = RB_NEXT(cvs_flisthead,
370
				    &(mc->mc_modules), fl);
371
13
				RB_REMOVE(cvs_flisthead,
372
				    &(mc->mc_modules), fl);
373
13
				free(fl->file_path);
374
13
				free(fl);
375
			}
376
		}
377
378
13
		while ((ip = TAILQ_FIRST(&checkout_ign_pats)) != NULL) {
379
			TAILQ_REMOVE(&checkout_ign_pats, ip, ip_list);
380
			free(ip);
381
		}
382
383
13
		free(mc);
384
	}
385
26
}
386
387
static int
388
checkout_classify(const char *repo, const char *arg)
389
{
390
26
	char *d, *f, fpath[PATH_MAX];
391
13
	struct stat sb;
392
393
13
	if (stat(repo, &sb) == 0) {
394
12
		if (S_ISDIR(sb.st_mode))
395
12
			return CVS_DIR;
396
	}
397
398
1
	d = dirname(repo);
399
1
	f = basename(repo);
400
401
1
	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s%s", d, f, RCS_FILE_EXT);
402
1
	if (stat(fpath, &sb) == 0) {
403
1
		if (!S_ISREG(sb.st_mode)) {
404
			cvs_log(LP_ERR, "ignoring %s: not a regular file", arg);
405
			return 0;
406
		}
407
1
		return CVS_FILE;
408
	}
409
410
	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s/%s%s",
411
	    d, CVS_PATH_ATTIC, f, RCS_FILE_EXT);
412
	if (stat(fpath, &sb) == 0) {
413
		if (!S_ISREG(sb.st_mode)) {
414
			cvs_log(LP_ERR, "ignoring %s: not a regular file", arg);
415
			return 0;
416
		}
417
		return CVS_FILE;
418
	}
419
420
	cvs_log(LP_ERR, "cannot find module `%s' - ignored", arg);
421
	return 0;
422
13
}
423
424
static void
425
checkout_repository(const char *repobase, const char *wdbase)
426
{
427
24
	struct cvs_flisthead fl, dl;
428
12
	struct cvs_recursion cr;
429
430
12
	RB_INIT(&fl);
431
12
	RB_INIT(&dl);
432
433
12
	cvs_history_add((cvs_cmdop == CVS_OP_CHECKOUT) ?
434
	    CVS_HISTORY_CHECKOUT : CVS_HISTORY_EXPORT, NULL, wdbase);
435
436
12
	if (print_stdout) {
437
		cr.enterdir = NULL;
438
		cr.leavedir = NULL;
439
	} else {
440
12
		cr.enterdir = cvs_update_enterdir;
441
12
		if (cvs_server_active == 1) {
442
			if (disable_fast_checkout != 1)
443
				cr.leavedir = NULL;
444
			else
445
				cr.leavedir = cvs_update_leavedir;
446
		} else {
447
12
			cr.leavedir = prune_dirs ? cvs_update_leavedir : NULL;
448
		}
449
	}
450
12
	cr.fileproc = cvs_update_local;
451
12
	cr.flags = flags;
452
453
12
	cvs_repository_lock(repobase, 0);
454
12
	cvs_repository_getdir(repobase, wdbase, &fl, &dl,
455
12
	    flags & CR_RECURSE_DIRS ? REPOSITORY_DODIRS : 0);
456
457
12
	cvs_file_walklist(&fl, &cr);
458
12
	cvs_file_freelist(&fl);
459
460
12
	cvs_repository_unlock(repobase);
461
462
12
	cvs_file_walklist(&dl, &cr);
463
12
	cvs_file_freelist(&dl);
464
12
}
465
466
void
467
cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, char *tag, int co_flags)
468
{
469
	BUF *bp;
470
	mode_t mode;
471
	int cf_kflag, exists;
472
80
	time_t rcstime;
473
	CVSENTRIES *ent;
474
40
	struct timeval tv[2];
475
40
	struct tm datetm;
476
	char *entry, *tosend;
477
40
	char kbuf[8], sticky[CVS_REV_BUFSZ], rev[CVS_REV_BUFSZ];
478
40
	char timebuf[CVS_TIME_BUFSZ], tbuf[CVS_TIME_BUFSZ];
479
	static char lastwd[PATH_MAX];
480
481
	exists = 0;
482
	tosend = NULL;
483
484
40
	if (!(co_flags & CO_REMOVE))
485
40
		rcsnum_tostr(rnum, rev, sizeof(rev));
486
	else
487
		rev[0] = '\0';
488
489
40
	cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d) -> %s",
490
40
	    cf->file_path, rev, co_flags,
491
40
	    (cvs_server_active) ? "to client" : "to disk");
492
493
40
	if (co_flags & CO_DUMP) {
494
1
		rcs_rev_write_fd(cf->file_rcs, rnum, STDOUT_FILENO, 0);
495
1
		return;
496
	}
497
498
39
	if (cvs_server_active == 0) {
499
39
		(void)unlink(cf->file_path);
500
501
39
		if (!(co_flags & CO_MERGE)) {
502
39
			if (cf->file_flags & FILE_ON_DISK) {
503
				exists = 1;
504
17
				(void)close(cf->fd);
505
17
			}
506
507
39
			cf->fd = open(cf->file_path,
508
			    O_CREAT | O_RDWR | O_TRUNC);
509
39
			if (cf->fd == -1)
510
				fatal("cvs_checkout_file: open: %s",
511
				    strerror(errno));
512
513
39
			rcs_rev_write_fd(cf->file_rcs, rnum, cf->fd, 0);
514
39
			cf->file_flags |= FILE_ON_DISK;
515
39
		} else {
516
			cvs_merge_file(cf, (cvs_join_rev1 == NULL));
517
		}
518
519
39
		mode = cf->file_rcs->rf_mode;
520
39
		mode |= S_IWUSR;
521
522
39
		if (fchmod(cf->fd, mode) == -1)
523
			fatal("cvs_checkout_file: fchmod: %s", strerror(errno));
524
525

83
		if ((exists == 0) && (cf->file_ent == NULL) &&
526
		    !(co_flags & CO_MERGE))
527
22
			rcstime = rcs_rev_getdate(cf->file_rcs, rnum);
528
		else
529
17
			time(&rcstime);
530
531
39
		tv[0].tv_sec = rcstime;
532
39
		tv[0].tv_usec = 0;
533
39
		tv[1] = tv[0];
534
39
		if (futimes(cf->fd, tv) == -1)
535
			fatal("cvs_checkout_file: futimes: %s",
536
			    strerror(errno));
537
	} else {
538
		time(&rcstime);
539
	}
540
541
39
	gmtime_r(&rcstime, &datetm);
542
39
	asctime_r(&datetm, tbuf);
543
39
	tbuf[strcspn(tbuf, "\n")] = '\0';
544
545
39
	if (co_flags & CO_MERGE) {
546
		(void)xsnprintf(timebuf, sizeof(timebuf), "Result of merge+%s",
547
		    tbuf);
548
	} else {
549
39
		strlcpy(timebuf, tbuf, sizeof(timebuf));
550
	}
551
552
39
	if (reset_tag) {
553
6
		sticky[0] = '\0';
554
39
	} else if (co_flags & CO_SETSTICKY)
555
17
		if (tag != NULL)
556
17
			(void)xsnprintf(sticky, sizeof(sticky), "T%s", tag);
557
		else if (cvs_specified_date != -1) {
558
			gmtime_r(&cvs_specified_date, &datetm);
559
			(void)strftime(sticky, sizeof(sticky),
560
			    "D"CVS_DATE_FMT, &datetm);
561
		} else if (cvs_directory_date != -1) {
562
			gmtime_r(&cvs_directory_date, &datetm);
563
			(void)strftime(sticky, sizeof(sticky),
564
			    "D"CVS_DATE_FMT, &datetm);
565
		} else
566
			(void)xsnprintf(sticky, sizeof(sticky), "T%s", rev);
567

21
	else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
568
1
		(void)xsnprintf(sticky, sizeof(sticky), "T%s",
569
		    cf->file_ent->ce_tag);
570
	else
571
15
		sticky[0] = '\0';
572
573
39
	kbuf[0] = '\0';
574

78
	if (cf->file_rcs != NULL && cf->file_rcs->rf_expand != NULL) {
575
10
		cf_kflag = rcs_kflag_get(cf->file_rcs->rf_expand);
576
10
		if (kflag || cf_kflag != RCS_KWEXP_DEFAULT)
577
20
			(void)xsnprintf(kbuf, sizeof(kbuf),
578
10
			    "-k%s", cf->file_rcs->rf_expand);
579

52
	} else if (!reset_option && cf->file_ent != NULL) {
580
5
		if (cf->file_ent->ce_opts != NULL)
581
			strlcpy(kbuf, cf->file_ent->ce_opts, sizeof(kbuf));
582
	}
583
584
39
	entry = xmalloc(CVS_ENT_MAXLINELEN);
585
39
	cvs_ent_line_str(cf->file_name, rev, timebuf, kbuf, sticky, 0, 0,
586
	    entry, CVS_ENT_MAXLINELEN);
587
588
39
	if (cvs_server_active == 0) {
589
39
		if (!(co_flags & CO_REMOVE) && cvs_cmdop != CVS_OP_EXPORT) {
590
36
			ent = cvs_ent_open(cf->file_wd);
591
36
			cvs_ent_add(ent, entry);
592
36
			cf->file_ent = cvs_ent_parse(entry);
593
36
		}
594
	} else {
595
		if (co_flags & CO_MERGE) {
596
			(void)unlink(cf->file_path);
597
			cvs_merge_file(cf, (cvs_join_rev1 == NULL));
598
			tosend = cf->file_path;
599
		}
600
601
		/*
602
		 * If this file has a tag, push out the Directory with the
603
		 * tag to the client. Except when this file was explicitly
604
		 * specified on the command line.
605
		 */
606
		if (tag != NULL && strcmp(cf->file_wd, lastwd) &&
607
		    !(cf->file_flags & FILE_USER_SUPPLIED)) {
608
			strlcpy(lastwd, cf->file_wd, PATH_MAX);
609
			cvs_server_set_sticky(cf->file_wd, sticky);
610
		}
611
612
		if (co_flags & CO_COMMIT)
613
			cvs_server_update_entry("Updated", cf);
614
		else if (co_flags & CO_MERGE)
615
			cvs_server_update_entry("Merged", cf);
616
		else if (co_flags & CO_REMOVE)
617
			cvs_server_update_entry("Removed", cf);
618
		else
619
			cvs_server_update_entry("Updated", cf);
620
621
		if (!(co_flags & CO_REMOVE)) {
622
			cvs_remote_output(entry);
623
624
			if (!(co_flags & CO_MERGE)) {
625
				mode = cf->file_rcs->rf_mode;
626
				mode |= S_IWUSR;
627
				bp = rcs_rev_getbuf(cf->file_rcs, rnum, 0);
628
				cvs_remote_send_file_buf(cf->file_path,
629
				    bp, mode);
630
			} else {
631
				cvs_remote_send_file(tosend, cf->fd);
632
			}
633
		}
634
	}
635
636
39
	free(entry);
637
79
}