xref: /openbsd/usr.bin/cvs/add.c (revision 5cf15c45)
1 /*	$OpenBSD: add.c,v 1.107 2009/02/21 14:50:53 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 <string.h>
24 #include <unistd.h>
25 
26 #include "cvs.h"
27 #include "remote.h"
28 
29 extern char *__progname;
30 
31 void	cvs_add_loginfo(char *);
32 void	cvs_add_entry(struct cvs_file *);
33 void	cvs_add_remote(struct cvs_file *);
34 
35 static void add_directory(struct cvs_file *);
36 static void add_file(struct cvs_file *);
37 static void add_entry(struct cvs_file *);
38 
39 int		kflag = 0;
40 static char	kbuf[8];
41 
42 extern char	*logmsg;
43 extern char	*loginfo;
44 
45 struct cvs_cmd cvs_cmd_add = {
46 	CVS_OP_ADD, CVS_USE_WDIR, "add",
47 	{ "ad", "new" },
48 	"Add a new file or directory to the repository",
49 	"[-k mode] [-m message] ...",
50 	"k:m:",
51 	NULL,
52 	cvs_add
53 };
54 
55 int
56 cvs_add(int argc, char **argv)
57 {
58 	int ch;
59 	int flags;
60 	struct cvs_recursion cr;
61 
62 	flags = CR_REPO;
63 
64 	while ((ch = getopt(argc, argv, cvs_cmd_add.cmd_opts)) != -1) {
65 		switch (ch) {
66 		case 'k':
67 			kflag = rcs_kflag_get(optarg);
68 			if (RCS_KWEXP_INVAL(kflag)) {
69 				cvs_log(LP_ERR,
70 				    "invalid RCS keyword expansion mode");
71 				fatal("%s", cvs_cmd_add.cmd_synopsis);
72 			}
73 			(void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", optarg);
74 			break;
75 		case 'm':
76 			logmsg = optarg;
77 			break;
78 		default:
79 			fatal("%s", cvs_cmd_add.cmd_synopsis);
80 		}
81 	}
82 
83 	argc -= optind;
84 	argv += optind;
85 
86 	if (argc == 0)
87 		fatal("%s", cvs_cmd_add.cmd_synopsis);
88 
89 	cr.enterdir = NULL;
90 	cr.leavedir = NULL;
91 
92 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
93 		cvs_client_connect_to_server();
94 		cr.fileproc = cvs_add_remote;
95 		flags = 0;
96 
97 		if (kflag)
98 			cvs_client_send_request("Argument %s", kbuf);
99 
100 		if (logmsg != NULL)
101 			cvs_client_send_logmsg(logmsg);
102 	} else {
103 		if (logmsg != NULL && cvs_logmsg_verify(logmsg))
104 			return (0);
105 
106 		cr.fileproc = cvs_add_local;
107 	}
108 
109 	cr.flags = flags;
110 
111 	cvs_file_run(argc, argv, &cr);
112 
113 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
114 		cvs_client_senddir(".");
115 		cvs_client_send_files(argv, argc);
116 		cvs_client_send_request("add");
117 		cvs_client_get_responses();
118 
119 		if (server_response == SERVER_OK) {
120 			cr.fileproc = cvs_add_entry;
121 			cvs_file_run(argc, argv, &cr);
122 		}
123 	}
124 
125 	return (0);
126 }
127 
128 void
129 cvs_add_entry(struct cvs_file *cf)
130 {
131 	char *entry;
132 	CVSENTRIES *entlist;
133 
134 	if (cf->file_type == CVS_DIR) {
135 		entry = xmalloc(CVS_ENT_MAXLINELEN);
136 		cvs_ent_line_str(cf->file_name, NULL, NULL, NULL, NULL, 1, 0,
137 		    entry, CVS_ENT_MAXLINELEN);
138 
139 		entlist = cvs_ent_open(cf->file_wd);
140 		cvs_ent_add(entlist, entry);
141 
142 		xfree(entry);
143 	} else {
144 		add_entry(cf);
145 	}
146 }
147 
148 void
149 cvs_add_local(struct cvs_file *cf)
150 {
151 	cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
152 
153 	if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_UPDATE)
154 		cvs_file_classify(cf, cvs_directory_tag);
155 
156 	/* dont use `cvs add *' */
157 	if (strcmp(cf->file_name, ".") == 0 ||
158 	    strcmp(cf->file_name, "..") == 0 ||
159 	    strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) {
160 		if (verbosity > 1)
161 			cvs_log(LP_ERR,
162 			    "cannot add special file `%s'; skipping",
163 			    cf->file_name);
164 		return;
165 	}
166 
167 	if (cf->file_type == CVS_DIR)
168 		add_directory(cf);
169 	else
170 		add_file(cf);
171 }
172 
173 void
174 cvs_add_remote(struct cvs_file *cf)
175 {
176 	char path[MAXPATHLEN];
177 
178 	cvs_log(LP_TRACE, "cvs_add_remote(%s)", cf->file_path);
179 
180 	cvs_file_classify(cf, cvs_directory_tag);
181 
182 	if (cf->file_type == CVS_DIR) {
183 		cvs_get_repository_path(cf->file_wd, path, MAXPATHLEN);
184 		if (strlcat(path, "/", sizeof(path)) >= sizeof(path))
185 			fatal("cvs_add_remote: truncation");
186 		if (strlcat(path, cf->file_path, sizeof(path)) >= sizeof(path))
187 			fatal("cvs_add_remote: truncation");
188 		cvs_client_send_request("Directory %s\n%s", cf->file_path,
189 		    path);
190 
191 		add_directory(cf);
192 	} else {
193 		cvs_client_sendfile(cf);
194 	}
195 }
196 
197 void
198 cvs_add_loginfo(char *repo)
199 {
200 	BUF *buf;
201 	char pwd[MAXPATHLEN];
202 
203 	if (getcwd(pwd, sizeof(pwd)) == NULL)
204 		fatal("Can't get working directory");
205 
206 	buf = cvs_buf_alloc(1024);
207 
208 	cvs_trigger_loginfo_header(buf, repo);
209 
210 	cvs_buf_puts(buf, "Log Message:\nDirectory ");
211 	cvs_buf_puts(buf, current_cvsroot->cr_dir);
212 	cvs_buf_putc(buf, '/');
213 	cvs_buf_puts(buf, repo);
214 	cvs_buf_puts(buf, " added to the repository\n");
215 
216 	cvs_buf_putc(buf, '\0');
217 
218 	loginfo = cvs_buf_release(buf);
219 }
220 
221 void
222 cvs_add_tobranch(struct cvs_file *cf, char *tag)
223 {
224 	BUF *bp;
225 	char attic[MAXPATHLEN], repo[MAXPATHLEN];
226 	char *msg;
227 	struct stat st;
228 	struct rcs_delta *rdp;
229 	RCSNUM *branch;
230 
231 	cvs_log(LP_TRACE, "cvs_add_tobranch(%s)", cf->file_name);
232 
233 	if (cvs_noexec == 1)
234 		return;
235 
236 	if (fstat(cf->fd, &st) == -1)
237 		fatal("cvs_add_tobranch: %s", strerror(errno));
238 
239 	cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN);
240 	(void)xsnprintf(attic, MAXPATHLEN, "%s/%s",
241 	    repo, CVS_PATH_ATTIC);
242 
243 	if (mkdir(attic, 0755) == -1 && errno != EEXIST)
244 		fatal("cvs_add_tobranch: failed to create Attic");
245 
246 	(void)xsnprintf(attic, MAXPATHLEN, "%s/%s/%s%s", repo,
247 	    CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT);
248 
249 	xfree(cf->file_rpath);
250 	cf->file_rpath = xstrdup(attic);
251 
252 	cf->repo_fd = open(cf->file_rpath, O_CREAT|O_RDONLY);
253 	if (cf->repo_fd < 0)
254 		fatal("cvs_add_tobranch: %s: %s", cf->file_rpath,
255 		    strerror(errno));
256 
257 	cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd,
258 	    RCS_CREATE|RCS_WRITE, 0444);
259 	if (cf->file_rcs == NULL)
260 		fatal("cvs_add_tobranch: failed to create RCS file for %s",
261 		    cf->file_path);
262 
263 	if ((branch = rcsnum_parse("1.1.2")) == NULL)
264 		fatal("cvs_add_tobranch: failed to parse branch");
265 
266 	if (rcs_sym_add(cf->file_rcs, tag, branch) == -1)
267 		fatal("cvs_add_tobranch: failed to add vendor tag");
268 
269 	(void)xasprintf(&msg, "file %s was initially added on branch %s.",
270 	    cf->file_name, tag);
271 	if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, msg, -1, NULL) == -1)
272 		fatal("cvs_add_tobranch: failed to create first branch "
273 		    "revision");
274 	xfree(msg);
275 
276 	if ((rdp = rcs_findrev(cf->file_rcs, cf->file_rcs->rf_head)) == NULL)
277 		fatal("cvs_add_tobranch: cannot find newly added revision");
278 
279 	bp = cvs_buf_alloc(1);
280 
281 	if (rcs_deltatext_set(cf->file_rcs,
282 	    cf->file_rcs->rf_head, bp) == -1)
283 		fatal("cvs_add_tobranch: failed to set deltatext");
284 
285 	rcs_comment_set(cf->file_rcs, " * ");
286 
287 	if (rcs_state_set(cf->file_rcs, cf->file_rcs->rf_head, RCS_STATE_DEAD)
288 	    == -1)
289 		fatal("cvs_add_tobranch: failed to set state");
290 }
291 
292 static void
293 add_directory(struct cvs_file *cf)
294 {
295 	int added, nb;
296 	struct stat st;
297 	CVSENTRIES *entlist;
298 	char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p;
299 	struct file_info_list files_info;
300 	struct file_info *fi;
301 	struct trigger_list *line_list;
302 
303 	cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
304 
305 	(void)xsnprintf(entry, MAXPATHLEN, "%s%s",
306 	    cf->file_rpath, RCS_FILE_EXT);
307 
308 	added = 1;
309 	if (stat(entry, &st) != -1) {
310 		cvs_log(LP_NOTICE, "cannot add directory %s: "
311 		    "a file with that name already exists",
312 		    cf->file_path);
313 		added = 0;
314 	} else {
315 		/* Let's see if we have any per-directory tags first. */
316 		cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
317 
318 		(void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
319 		    cf->file_path, CVS_PATH_CVSDIR);
320 
321 		if (cvs_server_active) {
322 			if (mkdir(cf->file_rpath, 0755) == -1 &&
323 			    errno != EEXIST)
324 				fatal("add_directory: %s: %s", cf->file_rpath,
325 				    strerror(errno));
326 		} else if (stat(entry, &st) != -1) {
327 			if (!S_ISDIR(st.st_mode)) {
328 				cvs_log(LP_ERR, "%s exists but is not "
329 				    "directory", entry);
330 			} else {
331 				cvs_log(LP_NOTICE, "%s already exists",
332 				    entry);
333 			}
334 			added = 0;
335 		} else if (cvs_noexec != 1) {
336 			if (mkdir(cf->file_rpath, 0755) == -1 &&
337 			    errno != EEXIST)
338 				fatal("add_directory: %s: %s", cf->file_rpath,
339 				    strerror(errno));
340 
341 			cvs_get_repository_name(cf->file_wd, repo,
342 			    MAXPATHLEN);
343 
344 			(void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
345 			    repo, cf->file_name);
346 
347 			cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
348 			    entry, tag, date);
349 
350 			p = xmalloc(CVS_ENT_MAXLINELEN);
351 			cvs_ent_line_str(cf->file_name, NULL, NULL, NULL,
352 			    NULL, 1, 0, p, CVS_ENT_MAXLINELEN);
353 
354 			entlist = cvs_ent_open(cf->file_wd);
355 			cvs_ent_add(entlist, p);
356 			xfree(p);
357 		}
358 	}
359 
360 	if (added == 1 && current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
361 		(void)xsnprintf(msg, sizeof(msg),
362 		    "Directory %s added to the repository", cf->file_rpath);
363 
364 		if (tag != NULL) {
365 			(void)strlcat(msg,
366 			    "\n--> Using per-directory sticky tag ",
367 			    sizeof(msg));
368 			(void)strlcat(msg, tag, sizeof(msg));
369 		}
370 		if (date != NULL) {
371 			(void)strlcat(msg,
372 			    "\n--> Using per-directory sticky date ",
373 			    sizeof(msg));
374 			(void)strlcat(msg, date, sizeof(msg));
375 		}
376 		cvs_printf("%s\n", msg);
377 
378 		if (tag != NULL)
379 			xfree(tag);
380 		if (date != NULL)
381 			xfree(date);
382 
383 		cvs_get_repository_name(cf->file_path, repo, MAXPATHLEN);
384 		line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo);
385 		if (line_list != NULL) {
386 			TAILQ_INIT(&files_info);
387 			fi = xcalloc(1, sizeof(*fi));
388 			fi->file_path = xstrdup(cf->file_path);
389 			TAILQ_INSERT_TAIL(&files_info, fi, flist);
390 
391 			cvs_add_loginfo(repo);
392 			cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo,
393 			    loginfo, line_list, &files_info);
394 
395 			cvs_trigger_freeinfo(&files_info);
396 			cvs_trigger_freelist(line_list);
397 			if (loginfo != NULL)
398 				xfree(loginfo);
399 		}
400 	}
401 
402 	cf->file_status = FILE_SKIP;
403 }
404 
405 static void
406 add_file(struct cvs_file *cf)
407 {
408 	int added, nb, stop;
409 	char revbuf[CVS_REV_BUFSZ];
410 	RCSNUM *head = NULL;
411 	char *tag;
412 
413 	cvs_parse_tagfile(cf->file_wd, &tag, NULL, &nb);
414 	if (nb) {
415 		cvs_log(LP_ERR, "cannot add file on non-branch tag %s", tag);
416 		return;
417 	}
418 
419 	if (cf->file_rcs != NULL) {
420 		head = rcs_head_get(cf->file_rcs);
421 		if (head == NULL) {
422 			cvs_log(LP_NOTICE, "no head revision in RCS file for "
423 			    "%s", cf->file_path);
424 		}
425 		rcsnum_tostr(head, revbuf, sizeof(revbuf));
426 	}
427 
428 	added = stop = 0;
429 	switch (cf->file_status) {
430 	case FILE_ADDED:
431 	case FILE_CHECKOUT:
432 		if (verbosity > 1)
433 			cvs_log(LP_NOTICE, "%s has already been entered",
434 			    cf->file_path);
435 		stop = 1;
436 		break;
437 	case FILE_REMOVED:
438 		if (cf->file_rcs == NULL) {
439 			cvs_log(LP_NOTICE, "cannot resurrect %s; "
440 			    "RCS file removed by second party", cf->file_name);
441 		} else if (!(cf->file_flags & FILE_ON_DISK)) {
442 			add_entry(cf);
443 
444 			/* Restore the file. */
445 			cvs_checkout_file(cf, head, NULL, 0);
446 
447 			cvs_printf("U %s\n", cf->file_path);
448 
449 			cvs_log(LP_NOTICE, "%s, version %s, resurrected",
450 			    cf->file_name, revbuf);
451 
452 			cf->file_status = FILE_UPTODATE;
453 		}
454 		stop = 1;
455 		break;
456 	case FILE_CONFLICT:
457 	case FILE_LOST:
458 	case FILE_MODIFIED:
459 	case FILE_UPTODATE:
460 		if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
461 			cvs_log(LP_NOTICE, "%s already exists, with version "
462 			     "number %s", cf->file_path, revbuf);
463 			stop = 1;
464 		}
465 		break;
466 	case FILE_UNKNOWN:
467 		if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
468 			cvs_log(LP_NOTICE, "re-adding file %s "
469 			    "(instead of dead revision %s)",
470 			    cf->file_path, revbuf);
471 			added++;
472 		} else if (cf->file_flags & FILE_ON_DISK) {
473 			cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
474 			    cf->file_path);
475 			added++;
476 		} else {
477 			stop = 1;
478 		}
479 		break;
480 	default:
481 		break;
482 	}
483 
484 	if (head != NULL)
485 		rcsnum_free(head);
486 
487 	if (stop == 1)
488 		return;
489 
490 	add_entry(cf);
491 
492 	if (added != 0) {
493 		cvs_log(LP_NOTICE, "use '%s commit' to add %s "
494 		    "permanently", __progname,
495 		    (added == 1) ? "this file" : "these files");
496 	}
497 }
498 
499 static void
500 add_entry(struct cvs_file *cf)
501 {
502 	FILE *fp;
503 	char *entry, path[MAXPATHLEN];
504 	char revbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ];
505 	char sticky[CVS_ENT_MAXLINELEN];
506 	CVSENTRIES *entlist;
507 
508 	if (cvs_noexec == 1)
509 		return;
510 
511 	sticky[0] = '\0';
512 	entry = xmalloc(CVS_ENT_MAXLINELEN);
513 
514 	if (cf->file_status == FILE_REMOVED) {
515 		rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
516 
517 		ctime_r(&cf->file_ent->ce_mtime, tbuf);
518 		tbuf[strcspn(tbuf, "\n")] = '\0';
519 
520 		if (cf->file_ent->ce_tag != NULL)
521 			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
522 			    cf->file_ent->ce_tag);
523 
524 		/* Remove the '-' prefixing the version number. */
525 		cvs_ent_line_str(cf->file_name, revbuf, tbuf,
526 		    cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "", sticky,
527 		    0, 0, entry, CVS_ENT_MAXLINELEN);
528 	} else {
529 		if (logmsg != NULL) {
530 			(void)xsnprintf(path, MAXPATHLEN, "%s/%s/%s%s",
531 			    cf->file_wd, CVS_PATH_CVSDIR, cf->file_name,
532 			    CVS_DESCR_FILE_EXT);
533 
534 			if ((fp = fopen(path, "w+")) == NULL)
535 				fatal("add_entry: fopen `%s': %s",
536 				    path, strerror(errno));
537 
538 			if (fputs(logmsg, fp) == EOF) {
539 				(void)unlink(path);
540 				fatal("add_entry: fputs `%s': %s",
541 				    path, strerror(errno));
542 			}
543 			(void)fclose(fp);
544 		}
545 
546 		if (cvs_directory_tag != NULL)
547 			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
548 			    cvs_directory_tag);
549 
550 		tbuf[0] = '\0';
551 		if (!cvs_server_active)
552 			(void)xsnprintf(tbuf, sizeof(tbuf), "Initial %s",
553 			    cf->file_name);
554 
555 		cvs_ent_line_str(cf->file_name, "0", tbuf, kflag ? kbuf : "",
556 		    sticky, 0, 0, entry, CVS_ENT_MAXLINELEN);
557 	}
558 
559 	if (cvs_server_active) {
560 		cvs_server_send_response("Checked-in %s/", cf->file_wd);
561 		cvs_server_send_response("%s", cf->file_path);
562 		cvs_server_send_response("%s", entry);
563 	} else {
564 		entlist = cvs_ent_open(cf->file_wd);
565 		cvs_ent_add(entlist, entry);
566 	}
567 	xfree(entry);
568 }
569