GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/cvs/add.c Lines: 106 291 36.4 %
Date: 2017-11-07 Branches: 36 156 23.1 %

Line Branch Exec Source
1
/*	$OpenBSD: add.c,v 1.114 2017/06/01 08:08:24 joris Exp $	*/
2
/*
3
 * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
4
 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@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 <sys/stat.h>
20
21
#include <errno.h>
22
#include <fcntl.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
27
#include "cvs.h"
28
#include "remote.h"
29
30
extern char *__progname;
31
32
void	cvs_add_loginfo(char *);
33
void	cvs_add_entry(struct cvs_file *);
34
void	cvs_add_remote(struct cvs_file *);
35
36
static void add_directory(struct cvs_file *);
37
static void add_file(struct cvs_file *);
38
static void add_entry(struct cvs_file *);
39
40
int		kflag = 0;
41
static u_int	added_files = 0;
42
static char	kbuf[8];
43
44
extern char	*logmsg;
45
extern char	*loginfo;
46
47
struct cvs_cmd cvs_cmd_add = {
48
	CVS_OP_ADD, CVS_USE_WDIR, "add",
49
	{ "ad", "new" },
50
	"Add a new file or directory to the repository",
51
	"[-k mode] [-m message] ...",
52
	"k:m:",
53
	NULL,
54
	cvs_add
55
};
56
57
int
58
cvs_add(int argc, char **argv)
59
{
60
	int ch;
61
	int flags;
62
8
	struct cvs_recursion cr;
63
64
	flags = CR_REPO;
65
66
8
	while ((ch = getopt(argc, argv, cvs_cmd_add.cmd_opts)) != -1) {
67
		switch (ch) {
68
		case 'k':
69
			kflag = rcs_kflag_get(optarg);
70
			if (RCS_KWEXP_INVAL(kflag)) {
71
				cvs_log(LP_ERR,
72
				    "invalid RCS keyword expansion mode");
73
				fatal("%s", cvs_cmd_add.cmd_synopsis);
74
			}
75
			(void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", optarg);
76
			break;
77
		case 'm':
78
			logmsg = optarg;
79
			break;
80
		default:
81
			fatal("%s", cvs_cmd_add.cmd_synopsis);
82
		}
83
	}
84
85
4
	argc -= optind;
86
4
	argv += optind;
87
88
4
	if (argc == 0)
89
		fatal("%s", cvs_cmd_add.cmd_synopsis);
90
91
4
	cr.enterdir = NULL;
92
4
	cr.leavedir = NULL;
93
94
4
	if (cvsroot_is_remote()) {
95
		cvs_client_connect_to_server();
96
		cr.fileproc = cvs_add_remote;
97
		flags = 0;
98
99
		if (kflag)
100
			cvs_client_send_request("Argument %s", kbuf);
101
102
		if (logmsg != NULL)
103
			cvs_client_send_logmsg(logmsg);
104
	} else {
105

4
		if (logmsg != NULL && cvs_logmsg_verify(logmsg))
106
			return (0);
107
108
4
		cr.fileproc = cvs_add_local;
109
	}
110
111
4
	cr.flags = flags;
112
113
4
	cvs_file_run(argc, argv, &cr);
114
115
4
	if (added_files != 0) {
116
3
		cvs_log(LP_NOTICE, "use '%s commit' to add %s "
117
3
		    "permanently", __progname,
118
3
		    (added_files == 1) ? "this file" : "these files");
119
3
	}
120
121
4
	if (cvsroot_is_remote()) {
122
		cvs_client_senddir(".");
123
		cvs_client_send_files(argv, argc);
124
		cvs_client_send_request("add");
125
		cvs_client_get_responses();
126
127
		if (server_response == SERVER_OK) {
128
			cr.fileproc = cvs_add_entry;
129
			cvs_file_run(argc, argv, &cr);
130
		}
131
	}
132
133
4
	return (0);
134
4
}
135
136
void
137
cvs_add_entry(struct cvs_file *cf)
138
{
139
	char *entry;
140
	CVSENTRIES *entlist;
141
142
	if (cf->file_type == CVS_DIR) {
143
		entry = xmalloc(CVS_ENT_MAXLINELEN);
144
		cvs_ent_line_str(cf->file_name, NULL, NULL, NULL, NULL, 1, 0,
145
		    entry, CVS_ENT_MAXLINELEN);
146
147
		entlist = cvs_ent_open(cf->file_wd);
148
		cvs_ent_add(entlist, entry);
149
150
		free(entry);
151
	} else {
152
		add_entry(cf);
153
	}
154
}
155
156
void
157
cvs_add_local(struct cvs_file *cf)
158
{
159
8
	cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
160
161
4
	if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_UPDATE)
162
4
		cvs_file_classify(cf, cvs_directory_tag);
163
164
	/* dont use `cvs add *' */
165

8
	if (strcmp(cf->file_name, ".") == 0 ||
166
4
	    strcmp(cf->file_name, "..") == 0 ||
167
4
	    strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) {
168
		if (verbosity > 1)
169
			cvs_log(LP_ERR,
170
			    "cannot add special file `%s'; skipping",
171
			    cf->file_name);
172
		return;
173
	}
174
175
4
	if (cf->file_type == CVS_DIR)
176
1
		add_directory(cf);
177
	else
178
3
		add_file(cf);
179
4
}
180
181
void
182
cvs_add_remote(struct cvs_file *cf)
183
{
184
	char path[PATH_MAX];
185
186
	cvs_log(LP_TRACE, "cvs_add_remote(%s)", cf->file_path);
187
188
	cvs_file_classify(cf, cvs_directory_tag);
189
190
	if (cf->file_type == CVS_DIR) {
191
		cvs_get_repository_path(cf->file_wd, path, PATH_MAX);
192
		if (strlcat(path, "/", sizeof(path)) >= sizeof(path))
193
			fatal("cvs_add_remote: truncation");
194
		if (strlcat(path, cf->file_path, sizeof(path)) >= sizeof(path))
195
			fatal("cvs_add_remote: truncation");
196
		cvs_client_send_request("Directory %s\n%s", cf->file_path,
197
		    path);
198
199
		add_directory(cf);
200
	} else {
201
		cvs_client_sendfile(cf);
202
	}
203
}
204
205
void
206
cvs_add_loginfo(char *repo)
207
{
208
	BUF *buf;
209
	char pwd[PATH_MAX];
210
211
	if (getcwd(pwd, sizeof(pwd)) == NULL)
212
		fatal("Can't get working directory");
213
214
	buf = buf_alloc(1024);
215
216
	cvs_trigger_loginfo_header(buf, repo);
217
218
	buf_puts(buf, "Log Message:\nDirectory ");
219
	buf_puts(buf, current_cvsroot->cr_dir);
220
	buf_putc(buf, '/');
221
	buf_puts(buf, repo);
222
	buf_puts(buf, " added to the repository\n");
223
224
	buf_putc(buf, '\0');
225
226
	loginfo = buf_release(buf);
227
}
228
229
void
230
cvs_add_tobranch(struct cvs_file *cf, char *tag)
231
{
232
	BUF *bp;
233
	char attic[PATH_MAX], repo[PATH_MAX];
234
	char *msg;
235
	struct stat st;
236
	RCSNUM *branch;
237
238
	cvs_log(LP_TRACE, "cvs_add_tobranch(%s)", cf->file_name);
239
240
	if (cvs_noexec == 1)
241
		return;
242
243
	if (fstat(cf->fd, &st) == -1)
244
		fatal("cvs_add_tobranch: %s", strerror(errno));
245
246
	cvs_get_repository_path(cf->file_wd, repo, PATH_MAX);
247
	(void)xsnprintf(attic, PATH_MAX, "%s/%s",
248
	    repo, CVS_PATH_ATTIC);
249
250
	if (mkdir(attic, 0755) == -1 && errno != EEXIST)
251
		fatal("cvs_add_tobranch: failed to create Attic");
252
253
	(void)xsnprintf(attic, PATH_MAX, "%s/%s/%s%s", repo,
254
	    CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT);
255
256
	free(cf->file_rpath);
257
	cf->file_rpath = xstrdup(attic);
258
259
	cf->repo_fd = open(cf->file_rpath, O_CREAT|O_RDONLY);
260
	if (cf->repo_fd < 0)
261
		fatal("cvs_add_tobranch: %s: %s", cf->file_rpath,
262
		    strerror(errno));
263
264
	cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd,
265
	    RCS_CREATE|RCS_WRITE, 0444);
266
	if (cf->file_rcs == NULL)
267
		fatal("cvs_add_tobranch: failed to create RCS file for %s",
268
		    cf->file_path);
269
270
	if ((branch = rcsnum_parse("1.1.2")) == NULL)
271
		fatal("cvs_add_tobranch: failed to parse branch");
272
273
	if (rcs_sym_add(cf->file_rcs, tag, branch) == -1)
274
		fatal("cvs_add_tobranch: failed to add vendor tag");
275
276
	(void)xasprintf(&msg, "file %s was initially added on branch %s.",
277
	    cf->file_name, tag);
278
	if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, msg, -1, NULL) == -1)
279
		fatal("cvs_add_tobranch: failed to create first branch "
280
		    "revision");
281
	free(msg);
282
283
	if (rcs_findrev(cf->file_rcs, cf->file_rcs->rf_head) == NULL)
284
		fatal("cvs_add_tobranch: cannot find newly added revision");
285
286
	bp = buf_alloc(1);
287
288
	if (rcs_deltatext_set(cf->file_rcs,
289
	    cf->file_rcs->rf_head, bp) == -1)
290
		fatal("cvs_add_tobranch: failed to set deltatext");
291
292
	rcs_comment_set(cf->file_rcs, " * ");
293
294
	if (rcs_state_set(cf->file_rcs, cf->file_rcs->rf_head, RCS_STATE_DEAD)
295
	    == -1)
296
		fatal("cvs_add_tobranch: failed to set state");
297
}
298
299
static void
300
add_directory(struct cvs_file *cf)
301
{
302
2
	int added, nb;
303
1
	struct stat st;
304
	CVSENTRIES *entlist;
305
1
	char *date, entry[PATH_MAX], msg[1024], repo[PATH_MAX], *tag, *p;
306
1
	struct file_info_list files_info;
307
	struct file_info *fi;
308
	struct trigger_list *line_list;
309
310
1
	cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
311
312
2
	(void)xsnprintf(entry, PATH_MAX, "%s%s",
313
1
	    cf->file_rpath, RCS_FILE_EXT);
314
315
	added = 1;
316
1
	if (stat(entry, &st) != -1) {
317
		cvs_log(LP_NOTICE, "cannot add directory %s: "
318
		    "a file with that name already exists",
319
		    cf->file_path);
320
		added = 0;
321
	} else {
322
		/* Let's see if we have any per-directory tags first. */
323
1
		cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
324
325
1
		(void)xsnprintf(entry, PATH_MAX, "%s/%s",
326
1
		    cf->file_path, CVS_PATH_CVSDIR);
327
328
1
		if (cvs_server_active) {
329
			if (mkdir(cf->file_rpath, 0755) == -1 &&
330
			    errno != EEXIST)
331
				fatal("add_directory: %s: %s", cf->file_rpath,
332
				    strerror(errno));
333
1
		} else if (stat(entry, &st) != -1) {
334
			if (!S_ISDIR(st.st_mode)) {
335
				cvs_log(LP_ERR, "%s exists but is not "
336
				    "directory", entry);
337
			} else {
338
				cvs_log(LP_NOTICE, "%s already exists",
339
				    entry);
340
			}
341
			added = 0;
342
1
		} else if (cvs_noexec != 1) {
343

1
			if (mkdir(cf->file_rpath, 0755) == -1 &&
344
			    errno != EEXIST)
345
				fatal("add_directory: %s: %s", cf->file_rpath,
346
				    strerror(errno));
347
348
1
			cvs_get_repository_name(cf->file_wd, repo,
349
			    PATH_MAX);
350
351
1
			(void)xsnprintf(entry, PATH_MAX, "%s/%s",
352
1
			    repo, cf->file_name);
353
354
2
			cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
355
1
			    entry, tag, date);
356
357
1
			p = xmalloc(CVS_ENT_MAXLINELEN);
358
1
			cvs_ent_line_str(cf->file_name, NULL, NULL, NULL,
359
			    NULL, 1, 0, p, CVS_ENT_MAXLINELEN);
360
361
1
			entlist = cvs_ent_open(cf->file_wd);
362
1
			cvs_ent_add(entlist, p);
363
1
			free(p);
364
1
		}
365
	}
366
367

2
	if (added == 1 && cvsroot_is_local()) {
368
2
		(void)xsnprintf(msg, sizeof(msg),
369
1
		    "Directory %s added to the repository", cf->file_rpath);
370
371
1
		if (tag != NULL) {
372
			(void)strlcat(msg,
373
			    "\n--> Using per-directory sticky tag ",
374
			    sizeof(msg));
375
			(void)strlcat(msg, tag, sizeof(msg));
376
		}
377
1
		if (date != NULL) {
378
			(void)strlcat(msg,
379
			    "\n--> Using per-directory sticky date ",
380
			    sizeof(msg));
381
			(void)strlcat(msg, date, sizeof(msg));
382
		}
383
1
		cvs_printf("%s\n", msg);
384
385
1
		free(tag);
386
1
		free(date);
387
388
1
		cvs_get_repository_name(cf->file_path, repo, PATH_MAX);
389
1
		line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo);
390
1
		if (line_list != NULL) {
391
			TAILQ_INIT(&files_info);
392
			fi = xcalloc(1, sizeof(*fi));
393
			fi->file_path = xstrdup(cf->file_path);
394
			TAILQ_INSERT_TAIL(&files_info, fi, flist);
395
396
			cvs_add_loginfo(repo);
397
			cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo,
398
			    loginfo, line_list, &files_info);
399
400
			cvs_trigger_freeinfo(&files_info);
401
			cvs_trigger_freelist(line_list);
402
			free(loginfo);
403
		}
404
	}
405
406
1
	cf->file_status = FILE_SKIP;
407
1
}
408
409
static void
410
add_file(struct cvs_file *cf)
411
{
412
6
	int nb, stop;
413
3
	char revbuf[CVS_REV_BUFSZ];
414
	RCSNUM *head = NULL;
415
3
	char *tag;
416
417
3
	cvs_parse_tagfile(cf->file_wd, &tag, NULL, &nb);
418
3
	if (nb) {
419
		cvs_log(LP_ERR, "cannot add file on non-branch tag %s", tag);
420
		return;
421
	}
422
423
3
	if (cf->file_rcs != NULL) {
424
		head = rcs_head_get(cf->file_rcs);
425
		if (head == NULL) {
426
			cvs_log(LP_NOTICE, "no head revision in RCS file for "
427
			    "%s", cf->file_path);
428
		}
429
		rcsnum_tostr(head, revbuf, sizeof(revbuf));
430
	}
431
432
	stop = 0;
433


6
	switch (cf->file_status) {
434
	case FILE_ADDED:
435
	case FILE_CHECKOUT:
436
		if (verbosity > 1)
437
			cvs_log(LP_NOTICE, "%s has already been entered",
438
			    cf->file_path);
439
		stop = 1;
440
		break;
441
	case FILE_REMOVED:
442
		if (cf->file_rcs == NULL) {
443
			cvs_log(LP_NOTICE, "cannot resurrect %s; "
444
			    "RCS file removed by second party", cf->file_name);
445
		} else if (!(cf->file_flags & FILE_ON_DISK)) {
446
			add_entry(cf);
447
448
			/* Restore the file. */
449
			cvs_checkout_file(cf, head, NULL, 0);
450
451
			cvs_printf("U %s\n", cf->file_path);
452
453
			cvs_log(LP_NOTICE, "%s, version %s, resurrected",
454
			    cf->file_name, revbuf);
455
456
			cf->file_status = FILE_UPTODATE;
457
		}
458
		stop = 1;
459
		break;
460
	case FILE_CONFLICT:
461
	case FILE_LOST:
462
	case FILE_MODIFIED:
463
	case FILE_UPTODATE:
464
		if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
465
			cvs_log(LP_NOTICE, "%s already exists, with version "
466
			     "number %s", cf->file_path, revbuf);
467
			stop = 1;
468
		}
469
		break;
470
	case FILE_UNKNOWN:
471

3
		if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
472
			cvs_log(LP_NOTICE, "re-adding file %s "
473
			    "(instead of dead revision %s)",
474
			    cf->file_path, revbuf);
475
			added_files++;
476
3
		} else if (cf->file_flags & FILE_ON_DISK) {
477
3
			cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
478
3
			    cf->file_path);
479
3
			added_files++;
480
3
		} else {
481
			stop = 1;
482
		}
483
		break;
484
	default:
485
		break;
486
	}
487
488
3
	free(head);
489
490
3
	if (stop == 1)
491
		return;
492
493
3
	add_entry(cf);
494
6
}
495
496
static void
497
add_entry(struct cvs_file *cf)
498
{
499
	FILE *fp;
500
6
	char *entry, path[PATH_MAX];
501
3
	char revbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ];
502
3
	char sticky[CVS_ENT_MAXLINELEN];
503
	CVSENTRIES *entlist;
504
505
3
	if (cvs_noexec == 1)
506
		return;
507
508
3
	sticky[0] = '\0';
509
3
	entry = xmalloc(CVS_ENT_MAXLINELEN);
510
511
3
	if (cf->file_status == FILE_REMOVED) {
512
		rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
513
514
		ctime_r(&cf->file_ent->ce_mtime, tbuf);
515
		tbuf[strcspn(tbuf, "\n")] = '\0';
516
517
		if (cf->file_ent->ce_tag != NULL)
518
			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
519
			    cf->file_ent->ce_tag);
520
521
		/* Remove the '-' prefixing the version number. */
522
		cvs_ent_line_str(cf->file_name, revbuf, tbuf,
523
		    cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "", sticky,
524
		    0, 0, entry, CVS_ENT_MAXLINELEN);
525
	} else {
526
3
		if (logmsg != NULL) {
527
			(void)xsnprintf(path, PATH_MAX, "%s/%s/%s%s",
528
			    cf->file_wd, CVS_PATH_CVSDIR, cf->file_name,
529
			    CVS_DESCR_FILE_EXT);
530
531
			if ((fp = fopen(path, "w+")) == NULL)
532
				fatal("add_entry: fopen `%s': %s",
533
				    path, strerror(errno));
534
535
			if (fputs(logmsg, fp) == EOF) {
536
				(void)unlink(path);
537
				fatal("add_entry: fputs `%s': %s",
538
				    path, strerror(errno));
539
			}
540
			(void)fclose(fp);
541
		}
542
543
3
		if (cvs_directory_tag != NULL)
544
			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
545
			    cvs_directory_tag);
546
547
3
		tbuf[0] = '\0';
548
3
		if (!cvs_server_active)
549
6
			(void)xsnprintf(tbuf, sizeof(tbuf), "Initial %s",
550
3
			    cf->file_name);
551
552
6
		cvs_ent_line_str(cf->file_name, "0", tbuf, kflag ? kbuf : "",
553
3
		    sticky, 0, 0, entry, CVS_ENT_MAXLINELEN);
554
	}
555
556
3
	if (cvs_server_active) {
557
		cvs_server_send_response("Checked-in %s/", cf->file_wd);
558
		cvs_server_send_response("%s", cf->file_path);
559
		cvs_server_send_response("%s", entry);
560
	} else {
561
3
		entlist = cvs_ent_open(cf->file_wd);
562
3
		cvs_ent_add(entlist, entry);
563
	}
564
3
	free(entry);
565
6
}