xref: /openbsd-src/usr.bin/cvs/add.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: add.c,v 1.73 2007/01/27 21:18:17 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 "includes.h"
20 
21 #include "cvs.h"
22 #include "diff.h"
23 #include "log.h"
24 #include "remote.h"
25 
26 extern char *__progname;
27 
28 void	cvs_add_local(struct cvs_file *);
29 void	cvs_add_entry(struct cvs_file *);
30 
31 static void add_directory(struct cvs_file *);
32 static void add_file(struct cvs_file *);
33 static void add_entry(struct cvs_file *);
34 
35 static int	 kflag = RCS_KWEXP_DEFAULT;
36 static char	 kbuf[8], *koptstr;
37 
38 char	*logmsg;
39 
40 struct cvs_cmd cvs_cmd_add = {
41 	CVS_OP_ADD, 0, "add",
42 	{ "ad", "new" },
43 	"Add a new file or directory to the repository",
44 	"[-k mode] [-m message] ...",
45 	"k:m:",
46 	NULL,
47 	cvs_add
48 };
49 
50 int
51 cvs_add(int argc, char **argv)
52 {
53 	int ch;
54 	int flags;
55 	struct cvs_recursion cr;
56 
57 	flags = CR_REPO;
58 
59 	while ((ch = getopt(argc, argv, cvs_cmd_add.cmd_opts)) != -1) {
60 		switch (ch) {
61 		case 'k':
62 			koptstr = optarg;
63 			kflag = rcs_kflag_get(koptstr);
64 			if (RCS_KWEXP_INVAL(kflag)) {
65 				cvs_log(LP_ERR,
66 				    "invalid RCS keyword expension mode");
67 				fatal("%s", cvs_cmd_add.cmd_synopsis);
68 			}
69 			snprintf(kbuf, sizeof(kbuf), "-k%s", koptstr);
70 			break;
71 		case 'm':
72 			logmsg = xstrdup(optarg);
73 			break;
74 		default:
75 			fatal("%s", cvs_cmd_add.cmd_synopsis);
76 		}
77 	}
78 
79 	argc -= optind;
80 	argv += optind;
81 
82 	if (argc == 0)
83 		fatal("%s", cvs_cmd_add.cmd_synopsis);
84 
85 	cr.enterdir = NULL;
86 	cr.leavedir = NULL;
87 
88 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
89 		cvs_client_connect_to_server();
90 		cr.fileproc = cvs_client_sendfile;
91 
92 		if (kflag != RCS_KWEXP_DEFAULT)
93 			cvs_client_send_request("Argument %s", kbuf);
94 
95 		if (logmsg != NULL)
96 			cvs_client_send_request("Argument -m%s", logmsg);
97 	} else {
98 		cr.fileproc = cvs_add_local;
99 	}
100 
101 	cr.flags = flags;
102 
103 	cvs_file_run(argc, argv, &cr);
104 
105 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
106 		cvs_client_send_files(argv, argc);
107 		cvs_client_senddir(".");
108 		cvs_client_send_request("add");
109 		cvs_client_get_responses();
110 
111 		if (server_response == SERVER_OK) {
112 			cr.fileproc = cvs_add_entry;
113 			cvs_file_run(argc, argv, &cr);
114 		}
115 	}
116 
117 	return (0);
118 }
119 
120 void
121 cvs_add_entry(struct cvs_file *cf)
122 {
123 	int l;
124 	char entry[CVS_ENT_MAXLINELEN];
125 	CVSENTRIES *entlist;
126 
127 	if (cf->file_type == CVS_DIR) {
128 		l = snprintf(entry, CVS_ENT_MAXLINELEN,
129 			    "D/%s/////", cf->file_name);
130 		if (l == -1 || l >= CVS_ENT_MAXLINELEN)
131 			fatal("cvs_add_entry: overflow");
132 
133 		entlist = cvs_ent_open(cf->file_wd);
134 		cvs_ent_add(entlist, entry);
135 		cvs_ent_close(entlist, ENT_SYNC);
136 	} else {
137 		add_entry(cf);
138 	}
139 }
140 
141 void
142 cvs_add_local(struct cvs_file *cf)
143 {
144 	cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
145 
146 	cvs_file_classify(cf, NULL, 1);
147 
148 	/* dont use `cvs add *' */
149 	if (strcmp(cf->file_name, ".") == 0 ||
150 	    strcmp(cf->file_name, "..") == 0 ||
151 	    strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) {
152 		if (verbosity > 1)
153 			cvs_log(LP_ERR,
154 			    "cannot add special file `%s'; skipping",
155 			    cf->file_name);
156 		return;
157 	}
158 
159 	if (cf->file_type == CVS_DIR)
160 		add_directory(cf);
161 	else
162 		add_file(cf);
163 }
164 
165 static void
166 add_directory(struct cvs_file *cf)
167 {
168 	int l, added, nb;
169 	struct stat st;
170 	CVSENTRIES *entlist;
171 	char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p;
172 
173 	cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
174 
175 	l = snprintf(entry, MAXPATHLEN, "%s%s", cf->file_rpath, RCS_FILE_EXT);
176 	if (l == -1 || l >= MAXPATHLEN)
177 		fatal("cvs_add_local: overflow");
178 
179 	added = 1;
180 	if (stat(entry, &st) != -1) {
181 		cvs_log(LP_NOTICE, "cannot add directory %s: "
182 		    "a file with that name already exists",
183 		    cf->file_path);
184 		added = 0;
185 	} else {
186 		/* Let's see if we have any per-directory tags first. */
187 		cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
188 
189 		if (cvs_path_cat(cf->file_path, CVS_PATH_CVSDIR,
190 		    entry, MAXPATHLEN) >= MAXPATHLEN)
191 			fatal("add_directory: truncation");
192 
193 		if (stat(entry, &st) != -1) {
194 			if (!S_ISDIR(st.st_mode)) {
195 				cvs_log(LP_ERR, "%s exists but is not "
196 				    "directory", entry);
197 			} else {
198 				cvs_log(LP_NOTICE, "%s already exists",
199 				    entry);
200 			}
201 			added = 0;
202 		} else if (cvs_noexec != 1) {
203 			if (mkdir(cf->file_rpath, 0755) == -1 &&
204 			    errno != EEXIST)
205 				fatal("add_directory: %s: %s", cf->file_path,
206 				    strerror(errno));
207 
208 			cvs_get_repository_name(cf->file_wd, repo,
209 			    MAXPATHLEN);
210 
211 			if (cvs_path_cat(repo, cf->file_name, entry,
212 			    MAXPATHLEN) >= MAXPATHLEN)
213 				fatal("add_directory: truncation");
214 
215 			cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
216 			    entry, tag, date, nb);
217 
218 			p = xmalloc(CVS_ENT_MAXLINELEN);
219 			l = snprintf(p, CVS_ENT_MAXLINELEN,
220 			    "D/%s/////", cf->file_name);
221 			entlist = cvs_ent_open(cf->file_wd);
222 			cvs_ent_add(entlist, p);
223 			cvs_ent_close(entlist, ENT_SYNC);
224 		}
225 	}
226 
227 	if (added == 1) {
228 		snprintf(msg, sizeof(msg),
229 		    "Directory %s added to the repository", cf->file_rpath);
230 
231 		if (tag != NULL) {
232 			(void)strlcat(msg,
233 			    "\n--> Using per-directory sticky tag ",
234 			    sizeof(msg));
235 			(void)strlcat(msg, tag, sizeof(msg));
236 		}
237 		if (date != NULL) {
238 			(void)strlcat(msg,
239 			    "\n--> Using per-directory sticky date ",
240 			    sizeof(msg));
241 			(void)strlcat(msg, date, sizeof(msg));
242 		}
243 		cvs_printf("%s\n", msg);
244 
245 		if (tag != NULL)
246 			xfree(tag);
247 		if (date != NULL)
248 			xfree(date);
249 	}
250 
251 	cf->file_status = FILE_SKIP;
252 }
253 
254 static void
255 add_file(struct cvs_file *cf)
256 {
257 	int added, stop;
258 	char revbuf[16];
259 	RCSNUM *head;
260 
261 	if (cf->file_rcs != NULL) {
262 		head = rcs_head_get(cf->file_rcs);
263 		rcsnum_tostr(head, revbuf, sizeof(revbuf));
264 		rcsnum_free(head);
265 	}
266 
267 	added = stop = 0;
268 	switch (cf->file_status) {
269 	case FILE_ADDED:
270 		if (verbosity > 1)
271 			cvs_log(LP_NOTICE, "%s has already been entered",
272 			    cf->file_path);
273 		stop = 1;
274 		break;
275 	case FILE_REMOVED:
276 		if (cf->file_rcs == NULL) {
277 			cvs_log(LP_NOTICE, "cannot resurrect %s; "
278 			    "RCS file removed by second party", cf->file_name);
279 		} else if (cf->fd == -1) {
280 			add_entry(cf);
281 
282 			/* Restore the file. */
283 			head = rcs_head_get(cf->file_rcs);
284 			cvs_checkout_file(cf, head, 0);
285 			rcsnum_free(head);
286 
287 			cvs_printf("U %s\n", cf->file_path);
288 
289 			cvs_log(LP_NOTICE, "%s, version %s, resurrected",
290 			    cf->file_name, revbuf);
291 
292 			cf->file_status = FILE_UPTODATE;
293 		}
294 		stop = 1;
295 		break;
296 	case FILE_CONFLICT:
297 	case FILE_LOST:
298 	case FILE_MODIFIED:
299 	case FILE_UPTODATE:
300 		if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
301 			cvs_log(LP_NOTICE, "%s already exists, with version "
302 			     "number %s", cf->file_path, revbuf);
303 			stop = 1;
304 		}
305 		break;
306 	case FILE_UNKNOWN:
307 		if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
308 			cvs_log(LP_NOTICE, "re-adding file %s "
309 			    "(instead of dead revision %s)",
310 			    cf->file_path, revbuf);
311 			added++;
312 		} else if (cf->fd != -1) {
313 			cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
314 			    cf->file_path);
315 			added++;
316 		} else {
317 			stop = 1;
318 		}
319 		break;
320 	default:
321 		break;
322 	}
323 
324 	if (stop == 1)
325 		return;
326 
327 	add_entry(cf);
328 
329 	if (added != 0) {
330 		cvs_log(LP_NOTICE, "use '%s commit' to add %s "
331 		    "permanently", __progname,
332 		    (added == 1) ? "this file" : "these files");
333 	}
334 }
335 
336 static void
337 add_entry(struct cvs_file *cf)
338 {
339 	FILE *fp;
340 	int l;
341 	char entry[CVS_ENT_MAXLINELEN], path[MAXPATHLEN], revbuf[16], tbuf[32];
342 	CVSENTRIES *entlist;
343 
344 	if (cvs_noexec == 1)
345 		return;
346 
347 	if (cf->file_status == FILE_REMOVED) {
348 		rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
349 
350 		ctime_r(&cf->file_ent->ce_mtime, tbuf);
351 		if (tbuf[strlen(tbuf) - 1] == '\n')
352 			tbuf[strlen(tbuf) - 1] = '\0';
353 
354 		/* Remove the '-' prefixing the version number. */
355 		l = snprintf(entry, CVS_ENT_MAXLINELEN,
356 		    "/%s/%s/%s/%s/", cf->file_name, revbuf, tbuf,
357 		    cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "");
358 		if (l == -1 || l >= CVS_ENT_MAXLINELEN)
359                		fatal("add_entry: truncation");
360 	} else {
361 		if (logmsg != NULL) {
362 			l = snprintf(path, MAXPATHLEN, "%s/%s%s",
363 			    CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT);
364 			if (l == -1 || l >= MAXPATHLEN)
365                			fatal("add_entry: truncation");
366 
367 			if ((fp = fopen(path, "w+")) == NULL)
368 				fatal("add_entry: fopen `%s': %s",
369 				    path, strerror(errno));
370 
371 			if (fputs(logmsg, fp) == EOF) {
372 				(void)unlink(path);
373 				fatal("add_entry: fputs `%s': %s",
374 				    path, strerror(errno));
375 			}
376 			(void)fclose(fp);
377 		}
378 
379 		l = snprintf(entry, CVS_ENT_MAXLINELEN,
380 		    "/%s/0/Initial %s/%s/", cf->file_name, cf->file_name,
381 		    (kflag != RCS_KWEXP_DEFAULT) ? kbuf : "");
382 		if (l == -1 || l >= CVS_ENT_MAXLINELEN)
383                		fatal("add_entry: truncation");
384 	}
385 
386 	entlist = cvs_ent_open(cf->file_wd);
387 	cvs_ent_add(entlist, entry);
388 	cvs_ent_close(entlist, ENT_SYNC);
389 }
390