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