xref: /openbsd-src/usr.bin/cvs/import.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: import.c,v 1.97 2008/06/15 04:21:26 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/stat.h>
19 
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <unistd.h>
24 
25 #include "cvs.h"
26 #include "diff.h"
27 #include "remote.h"
28 
29 void	cvs_import_local(struct cvs_file *);
30 
31 static void import_loginfo(char *);
32 static void import_new(struct cvs_file *);
33 static void import_printf(const char *, ...);
34 static void import_update(struct cvs_file *);
35 static void import_tag(struct cvs_file *, RCSNUM *, RCSNUM *);
36 static BUF *import_get_rcsdiff(struct cvs_file *, RCSNUM *);
37 
38 #define IMPORT_DEFAULT_BRANCH	"1.1.1"
39 
40 extern char *loginfo;
41 extern char *logmsg;
42 
43 static char *import_branch = IMPORT_DEFAULT_BRANCH;
44 static char *vendor_tag = NULL;
45 static char **release_tags;
46 static char *koptstr;
47 static int dflag = 0;
48 static int tagcount = 0;
49 static BUF *logbuf;
50 
51 char *import_repository = NULL;
52 int import_conflicts = 0;
53 
54 struct cvs_cmd cvs_cmd_import = {
55 	CVS_OP_IMPORT, CVS_USE_WDIR, "import",
56 	{ "im", "imp" },
57 	"Import sources into CVS, using vendor branches",
58 	"[-b branch] [-d] [-k mode] [-m message] "
59 	"repository vendor-tag release-tags",
60 	"b:dk:m:",
61 	NULL,
62 	cvs_import
63 };
64 
65 int
66 cvs_import(int argc, char **argv)
67 {
68 	int i, ch;
69 	char repo[MAXPATHLEN], *arg = ".";
70 	struct cvs_recursion cr;
71 	struct trigger_list *line_list;
72 
73 	while ((ch = getopt(argc, argv, cvs_cmd_import.cmd_opts)) != -1) {
74 		switch (ch) {
75 		case 'b':
76 			import_branch = optarg;
77 			break;
78 		case 'd':
79 			dflag = 1;
80 			break;
81 		case 'k':
82 			koptstr = optarg;
83 			kflag = rcs_kflag_get(koptstr);
84 			if (RCS_KWEXP_INVAL(kflag)) {
85 				cvs_log(LP_ERR,
86 				    "invalid RCS keyword expansion mode");
87 				fatal("%s", cvs_cmd_import.cmd_synopsis);
88 			}
89 			break;
90 		case 'm':
91 			logmsg = optarg;
92 			break;
93 		default:
94 			fatal("%s", cvs_cmd_import.cmd_synopsis);
95 			break;
96 		}
97 	}
98 
99 	argc -= optind;
100 	argv += optind;
101 
102 	if (argc < 3)
103 		fatal("%s", cvs_cmd_import.cmd_synopsis);
104 
105 	import_repository = argv[0];
106 	vendor_tag = argv[1];
107 	argc -= 2;
108 	argv += 2;
109 
110 	release_tags = argv;
111 	tagcount = argc;
112 
113 	if (!rcs_sym_check(vendor_tag))
114 		fatal("invalid symbol: %s", vendor_tag);
115 
116 	for (i = 0; i < tagcount; i++) {
117 		if (!rcs_sym_check(release_tags[i]))
118 			fatal("invalid symbol: %s", release_tags[i]);
119 	}
120 
121 	if (logmsg == NULL) {
122 		if (cvs_server_active)
123 			fatal("no log message specified");
124 		else
125 			logmsg = cvs_logmsg_create(NULL, NULL, NULL, NULL);
126 	}
127 
128 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
129 		cvs_client_connect_to_server();
130 
131 		cvs_client_send_request("Argument -b%s", IMPORT_DEFAULT_BRANCH);
132 
133 		if (kflag)
134 			cvs_client_send_request("Argument -k%s", koptstr);
135 
136 		cvs_client_send_logmsg(logmsg);
137 		cvs_client_send_request("Argument %s", import_repository);
138 		cvs_client_send_request("Argument %s", vendor_tag);
139 		for (i = 0; i < tagcount; i++)
140 			cvs_client_send_request("Argument %s", release_tags[i]);
141 
142 		cr.enterdir = NULL;
143 		cr.leavedir = NULL;
144 		cr.fileproc = cvs_client_sendfile;
145 		cr.flags = CR_RECURSE_DIRS;
146 
147 		cvs_file_run(1, &arg, &cr);
148 		cvs_client_senddir(".");
149 		cvs_client_send_request("import");
150 
151 		cvs_client_get_responses();
152 		return (0);
153 	}
154 
155 	if (cvs_logmsg_verify(logmsg))
156 		return (0);
157 
158 	(void)xsnprintf(repo, sizeof(repo), "%s/%s",
159 	    current_cvsroot->cr_dir, import_repository);
160 
161 	import_loginfo(import_repository);
162 
163 	if (cvs_noexec != 1) {
164 		if (mkdir(repo, 0755) == -1 && errno != EEXIST)
165 			fatal("cvs_import: %s: %s", repo, strerror(errno));
166 	}
167 
168 	cr.enterdir = NULL;
169 	cr.leavedir = NULL;
170 	cr.fileproc = cvs_import_local;
171 	cr.flags = CR_RECURSE_DIRS;
172 	cvs_file_run(1, &arg, &cr);
173 
174 	if (import_conflicts != 0) {
175 		import_printf("\n%d conflicts created by this import.\n\n",
176 		    import_conflicts);
177 		import_printf("Use the following command to help the merge:\n");
178 		import_printf("\topencvs checkout ");
179 		import_printf("-j%s:yesterday -j%s %s\n\n", vendor_tag,
180 		    vendor_tag, import_repository);
181 	} else {
182 		import_printf("\nNo conflicts created by this import.\n\n");
183 	}
184 
185 	loginfo = cvs_buf_release(logbuf);
186 	logbuf = NULL;
187 
188 	line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, import_repository);
189 	if (line_list != NULL) {
190 		cvs_trigger_handle(CVS_TRIGGER_LOGINFO, import_repository,
191 		    loginfo, line_list, NULL);
192 		cvs_trigger_freelist(line_list);
193 	}
194 
195 	xfree(loginfo);
196 	return (0);
197 }
198 
199 static void
200 import_printf(const char *fmt, ...)
201 {
202 	char *str;
203 	va_list vap;
204 
205 	va_start(vap, fmt);
206 	if (vasprintf(&str, fmt, vap) == -1)
207 		fatal("import_printf: could not allocate memory");
208 	va_end(vap);
209 
210 	cvs_printf("%s", str);
211 	cvs_buf_puts(logbuf, str);
212 }
213 
214 void
215 cvs_import_local(struct cvs_file *cf)
216 {
217 	int isnew;
218 	struct stat st;
219 	char repo[MAXPATHLEN];
220 
221 	cvs_log(LP_TRACE, "cvs_import_local(%s)", cf->file_path);
222 
223 	cvs_file_classify(cf, cvs_directory_tag);
224 
225 	if (cf->file_type == CVS_DIR) {
226 		if (!strcmp(cf->file_path, "."))
227 			return;
228 
229 		if (verbosity > 1)
230 			cvs_log(LP_NOTICE, "Importing %s", cf->file_path);
231 
232 		if (cvs_noexec == 1)
233 			return;
234 
235 		if (mkdir(cf->file_rpath, 0755) == -1 && errno != EEXIST)
236 			fatal("cvs_import_local: %s: %s", cf->file_rpath,
237 			    strerror(errno));
238 
239 		return;
240 	}
241 
242 	isnew = 1;
243 	(void)xsnprintf(repo, sizeof(repo), "%s/%s/%s/%s%s",
244 	    current_cvsroot->cr_dir, cf->file_wd, CVS_PATH_ATTIC,
245 	    cf->file_name, RCS_FILE_EXT);
246 
247 	if (cf->file_rcs != NULL || stat(repo, &st) != -1)
248 		isnew = 0;
249 
250 	if (isnew == 1)
251 		import_new(cf);
252 	else
253 		import_update(cf);
254 }
255 
256 static void
257 import_loginfo(char *repo)
258 {
259 	int i;
260 	char pwd[MAXPATHLEN];
261 
262 	if (getcwd(pwd, sizeof(pwd)) == NULL)
263 		fatal("Can't get working directory");
264 
265 	logbuf = cvs_buf_alloc(1024);
266 	cvs_trigger_loginfo_header(logbuf, repo);
267 
268 	cvs_buf_puts(logbuf, "Log Message:\n");
269 	cvs_buf_puts(logbuf, logmsg);
270 	if (logmsg[0] != '\0' && logmsg[strlen(logmsg) - 1] != '\n')
271 		cvs_buf_putc(logbuf, '\n');
272 	cvs_buf_putc(logbuf, '\n');
273 
274 	cvs_buf_puts(logbuf, "Status:\n\n");
275 
276 	cvs_buf_puts(logbuf, "Vendor Tag:\t");
277 	cvs_buf_puts(logbuf, vendor_tag);
278 	cvs_buf_putc(logbuf, '\n');
279 	cvs_buf_puts(logbuf, "Release Tags:\t");
280 
281 	for (i = 0; i < tagcount ; i++) {
282 		cvs_buf_puts(logbuf, "\t\t");
283 		cvs_buf_puts(logbuf, release_tags[i]);
284 		cvs_buf_putc(logbuf, '\n');
285 	}
286 	cvs_buf_putc(logbuf, '\n');
287 	cvs_buf_putc(logbuf, '\n');
288 }
289 
290 static void
291 import_new(struct cvs_file *cf)
292 {
293 	int i;
294 	BUF *bp;
295 	mode_t mode;
296 	time_t tstamp;
297 	struct stat st;
298 	struct rcs_branch *brp;
299 	struct rcs_delta *rdp;
300 	RCSNUM *branch, *brev;
301 
302 	tstamp = -1;
303 
304 	cvs_log(LP_TRACE, "import_new(%s)", cf->file_name);
305 
306 	if (cvs_noexec == 1) {
307 		import_printf("N %s/%s\n", import_repository, cf->file_path);
308 		return;
309 	}
310 
311 	if (fstat(cf->fd, &st) == -1)
312 		fatal("import_new: %s", strerror(errno));
313 
314 	mode = st.st_mode;
315 
316 	if (dflag == 1)
317 		tstamp = st.st_mtime;
318 
319 	if ((branch = rcsnum_parse(import_branch)) == NULL)
320 		fatal("import_new: failed to parse branch");
321 
322 	bp = cvs_buf_load_fd(cf->fd);
323 
324 	if ((brev = rcsnum_brtorev(branch)) == NULL)
325 		fatal("import_new: failed to get first branch revision");
326 
327 	cf->repo_fd = open(cf->file_rpath, O_CREAT | O_RDONLY);
328 	if (cf->repo_fd < 0)
329 		fatal("import_new: %s: %s", cf->file_rpath, strerror(errno));
330 
331 	cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, RCS_CREATE,
332 	    (mode & ~(S_IWUSR | S_IWGRP | S_IWOTH)));
333 	if (cf->file_rcs == NULL)
334 		fatal("import_new: failed to create RCS file for %s",
335 		    cf->file_path);
336 
337 	rcs_branch_set(cf->file_rcs, branch);
338 
339 	if (rcs_sym_add(cf->file_rcs, vendor_tag, branch) == -1)
340 		fatal("import_new: failed to add vendor tag");
341 
342 	for (i = 0; i < tagcount; i++) {
343 		if (rcs_sym_add(cf->file_rcs, release_tags[i], brev) == -1)
344 			fatal("import_new: failed to add release tag");
345 	}
346 
347 	if (rcs_rev_add(cf->file_rcs, brev, logmsg, tstamp, NULL) == -1)
348 		fatal("import_new: failed to create first branch revision");
349 
350 	if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, "Initial revision",
351 	    tstamp, NULL) == -1)
352 		fatal("import_new: failed to create first revision");
353 
354 	if ((rdp = rcs_findrev(cf->file_rcs, cf->file_rcs->rf_head)) == NULL)
355 		fatal("import_new: cannot find newly added revision");
356 
357 	brp = xmalloc(sizeof(*brp));
358 	brp->rb_num = rcsnum_alloc();
359 	rcsnum_cpy(brev, brp->rb_num, 0);
360 	TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
361 
362 	if (rcs_deltatext_set(cf->file_rcs,
363 	    cf->file_rcs->rf_head, bp) == -1)
364 		fatal("import_new: failed to set deltatext");
365 
366 	if (kflag)
367 		rcs_kwexp_set(cf->file_rcs, kflag);
368 
369 	rcs_write(cf->file_rcs);
370 	import_printf("N %s/%s\n", import_repository, cf->file_path);
371 
372 	rcsnum_free(branch);
373 	rcsnum_free(brev);
374 }
375 
376 static void
377 import_update(struct cvs_file *cf)
378 {
379 	int ret;
380 	BUF *b1, *b2, *d;
381 	char branch[CVS_REV_BUFSZ];
382 	RCSNUM *newrev, *rev, *brev;
383 
384 	cvs_log(LP_TRACE, "import_update(%s)", cf->file_path);
385 
386 	if (cf->file_rcs->rf_head == NULL)
387 		fatal("no head revision in RCS file for `%s'", cf->file_path);
388 
389 	if ((rev = rcs_translate_tag(import_branch, cf->file_rcs)) == NULL)
390 		fatal("import_update: could not translate tag `%s'",
391 		    import_branch);
392 
393 	if ((brev = rcsnum_parse(import_branch)) == NULL)
394 		fatal("import_update: rcsnum_parse failed");
395 
396 	b1 = rcs_rev_getbuf(cf->file_rcs, rev, RCS_KWEXP_NONE);
397 	b2 = cvs_buf_load_fd(cf->fd);
398 
399 	ret = cvs_buf_differ(b1, b2);
400 	cvs_buf_free(b1);
401 	cvs_buf_free(b2);
402 	if (ret == 0) {
403 		import_tag(cf, brev, rev);
404 		rcsnum_free(brev);
405 		if (cvs_noexec != 1)
406 			rcs_write(cf->file_rcs);
407 		import_printf("U %s/%s\n", import_repository, cf->file_path);
408 		return;
409 	}
410 
411 	if (cf->file_rcs->rf_branch != NULL)
412 		rcsnum_tostr(cf->file_rcs->rf_branch, branch, sizeof(branch));
413 
414 	if (cf->file_rcs->rf_branch == NULL || cf->in_attic == 1 ||
415 	    strcmp(branch, import_branch)) {
416 		import_conflicts++;
417 		import_printf("C %s/%s\n", import_repository, cf->file_path);
418 	} else {
419 		import_printf("U %s/%s\n", import_repository, cf->file_path);
420 	}
421 
422 	if (cvs_noexec == 1)
423 		return;
424 
425 	d = import_get_rcsdiff(cf, rev);
426 	newrev = rcsnum_inc(rev);
427 
428 	if (rcs_rev_add(cf->file_rcs, newrev, logmsg, -1, NULL) == -1)
429 		fatal("import_update: failed to add new revision");
430 
431 	if (rcs_deltatext_set(cf->file_rcs, newrev, d) == -1)
432 		fatal("import_update: failed to set deltatext");
433 
434 	import_tag(cf, brev, newrev);
435 
436 	if (kflag)
437 		rcs_kwexp_set(cf->file_rcs, kflag);
438 
439 	rcsnum_free(brev);
440 	rcs_write(cf->file_rcs);
441 }
442 
443 static void
444 import_tag(struct cvs_file *cf, RCSNUM *branch, RCSNUM *newrev)
445 {
446 	int i;
447 
448 	if (cvs_noexec != 1) {
449 		rcs_sym_add(cf->file_rcs, vendor_tag, branch);
450 
451 		for (i = 0; i < tagcount; i++)
452 			rcs_sym_add(cf->file_rcs, release_tags[i], newrev);
453 	}
454 }
455 
456 static BUF *
457 import_get_rcsdiff(struct cvs_file *cf, RCSNUM *rev)
458 {
459 	char *p1, *p2;
460 	BUF *b1, *b2;
461 	int fd1, fd2;
462 
463 	b2 = cvs_buf_alloc(128);
464 
465 	b1 = cvs_buf_load_fd(cf->fd);
466 
467 	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
468 	fd1 = cvs_buf_write_stmp(b1, p1, NULL);
469 	cvs_buf_free(b1);
470 
471 	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
472 	fd2 = rcs_rev_write_stmp(cf->file_rcs, rev, p2, RCS_KWEXP_NONE);
473 
474 	diff_format = D_RCSDIFF;
475 	if (cvs_diffreg(p2, p1, fd2, fd1, b2) == D_ERROR)
476 		fatal("import_get_rcsdiff: failed to get RCS patch");
477 
478 	close(fd1);
479 	close(fd2);
480 
481 	(void)unlink(p1);
482 	(void)unlink(p2);
483 
484 	xfree(p1);
485 	xfree(p2);
486 
487 	return (b2);
488 }
489