Annotation of src/usr.bin/cvs/add.c, Revision 1.103
1.103 ! joris 1: /* $OpenBSD: add.c,v 1.102 2008/06/14 02:34:27 tobias Exp $ */
1.1 jfb 2: /*
1.43 joris 3: * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
1.55 xsa 4: * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org>
1.1 jfb 5: *
1.43 joris 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.
1.1 jfb 9: *
1.43 joris 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.
1.1 jfb 17: */
18:
1.77 otto 19: #include <sys/stat.h>
20:
21: #include <errno.h>
22: #include <string.h>
23: #include <unistd.h>
1.1 jfb 24:
25: #include "cvs.h"
1.61 xsa 26: #include "remote.h"
1.1 jfb 27:
1.48 xsa 28: extern char *__progname;
29:
1.100 joris 30: void cvs_add_loginfo(char *);
1.63 joris 31: void cvs_add_entry(struct cvs_file *);
1.85 tobias 32: void cvs_add_remote(struct cvs_file *);
1.1 jfb 33:
1.45 joris 34: static void add_directory(struct cvs_file *);
35: static void add_file(struct cvs_file *);
1.55 xsa 36: static void add_entry(struct cvs_file *);
1.45 joris 37:
1.90 tobias 38: int kflag = 0;
39: static char kbuf[8];
1.72 xsa 40:
1.100 joris 41: extern char *logmsg;
42: extern char *loginfo;
1.21 jfb 43:
44: struct cvs_cmd cvs_cmd_add = {
1.88 tobias 45: CVS_OP_ADD, CVS_USE_WDIR, "add",
1.21 jfb 46: { "ad", "new" },
1.43 joris 47: "Add a new file or directory to the repository",
1.72 xsa 48: "[-k mode] [-m message] ...",
49: "k:m:",
1.21 jfb 50: NULL,
1.43 joris 51: cvs_add
1.15 joris 52: };
1.3 jfb 53:
1.43 joris 54: int
55: cvs_add(int argc, char **argv)
1.1 jfb 56: {
1.15 joris 57: int ch;
1.43 joris 58: int flags;
59: struct cvs_recursion cr;
1.1 jfb 60:
1.45 joris 61: flags = CR_REPO;
1.1 jfb 62:
1.43 joris 63: while ((ch = getopt(argc, argv, cvs_cmd_add.cmd_opts)) != -1) {
1.1 jfb 64: switch (ch) {
1.72 xsa 65: case 'k':
1.90 tobias 66: kflag = rcs_kflag_get(optarg);
1.72 xsa 67: if (RCS_KWEXP_INVAL(kflag)) {
68: cvs_log(LP_ERR,
1.98 tobias 69: "invalid RCS keyword expansion mode");
1.72 xsa 70: fatal("%s", cvs_cmd_add.cmd_synopsis);
71: }
1.90 tobias 72: (void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", optarg);
1.72 xsa 73: break;
1.1 jfb 74: case 'm':
1.87 tobias 75: logmsg = optarg;
1.1 jfb 76: break;
77: default:
1.43 joris 78: fatal("%s", cvs_cmd_add.cmd_synopsis);
1.1 jfb 79: }
80: }
81:
1.43 joris 82: argc -= optind;
83: argv += optind;
1.28 xsa 84:
1.46 joris 85: if (argc == 0)
86: fatal("%s", cvs_cmd_add.cmd_synopsis);
87:
1.43 joris 88: cr.enterdir = NULL;
89: cr.leavedir = NULL;
1.61 xsa 90:
91: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.66 joris 92: cvs_client_connect_to_server();
1.85 tobias 93: cr.fileproc = cvs_add_remote;
1.97 joris 94: flags = 0;
1.61 xsa 95:
1.92 joris 96: if (kflag)
1.72 xsa 97: cvs_client_send_request("Argument %s", kbuf);
98:
1.61 xsa 99: if (logmsg != NULL)
1.80 joris 100: cvs_client_send_logmsg(logmsg);
1.61 xsa 101: } else {
1.100 joris 102: if (logmsg != NULL && cvs_logmsg_verify(logmsg))
103: return (0);
104:
1.61 xsa 105: cr.fileproc = cvs_add_local;
106: }
107:
1.43 joris 108: cr.flags = flags;
109:
1.46 joris 110: cvs_file_run(argc, argv, &cr);
1.61 xsa 111:
112: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.85 tobias 113: cvs_client_senddir(".");
1.61 xsa 114: cvs_client_send_files(argv, argc);
115: cvs_client_send_request("add");
116: cvs_client_get_responses();
1.63 joris 117:
118: if (server_response == SERVER_OK) {
119: cr.fileproc = cvs_add_entry;
120: cvs_file_run(argc, argv, &cr);
121: }
1.61 xsa 122: }
123:
1.3 jfb 124: return (0);
1.63 joris 125: }
126:
127: void
128: cvs_add_entry(struct cvs_file *cf)
129: {
1.94 xsa 130: char *entry;
1.63 joris 131: CVSENTRIES *entlist;
132:
133: if (cf->file_type == CVS_DIR) {
1.94 xsa 134: entry = xmalloc(CVS_ENT_MAXLINELEN);
135: cvs_ent_line_str(cf->file_name, NULL, NULL, NULL, NULL, 1, 0,
136: entry, CVS_ENT_MAXLINELEN);
1.64 joris 137:
1.63 joris 138: entlist = cvs_ent_open(cf->file_wd);
139: cvs_ent_add(entlist, entry);
1.94 xsa 140:
141: xfree(entry);
1.63 joris 142: } else {
143: add_entry(cf);
144: }
1.3 jfb 145: }
146:
1.43 joris 147: void
148: cvs_add_local(struct cvs_file *cf)
1.3 jfb 149: {
1.45 joris 150: cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
151:
1.96 joris 152: if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_UPDATE)
153: cvs_file_classify(cf, cvs_directory_tag);
1.49 xsa 154:
155: /* dont use `cvs add *' */
156: if (strcmp(cf->file_name, ".") == 0 ||
157: strcmp(cf->file_name, "..") == 0 ||
158: strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) {
159: if (verbosity > 1)
160: cvs_log(LP_ERR,
161: "cannot add special file `%s'; skipping",
162: cf->file_name);
163: return;
164: }
1.45 joris 165:
166: if (cf->file_type == CVS_DIR)
167: add_directory(cf);
168: else
169: add_file(cf);
170: }
171:
1.85 tobias 172: void
173: cvs_add_remote(struct cvs_file *cf)
174: {
175: char path[MAXPATHLEN];
176:
177: cvs_log(LP_TRACE, "cvs_add_remote(%s)", cf->file_path);
178:
179: cvs_file_classify(cf, cvs_directory_tag);
180:
181: if (cf->file_type == CVS_DIR) {
182: cvs_get_repository_path(cf->file_wd, path, MAXPATHLEN);
183: if (strlcat(path, "/", sizeof(path)) >= sizeof(path))
184: fatal("cvs_add_remote: truncation");
185: if (strlcat(path, cf->file_path, sizeof(path)) >= sizeof(path))
186: fatal("cvs_add_remote: truncation");
187: cvs_client_send_request("Directory %s\n%s", cf->file_path,
188: path);
189:
190: add_directory(cf);
191: } else {
192: cvs_client_sendfile(cf);
193: }
194: }
195:
1.100 joris 196: void
197: cvs_add_loginfo(char *repo)
198: {
199: BUF *buf;
200: char pwd[MAXPATHLEN];
201:
202: if (getcwd(pwd, sizeof(pwd)) == NULL)
203: fatal("Can't get working directory");
204:
205: buf = cvs_buf_alloc(1024);
206:
207: cvs_trigger_loginfo_header(buf, repo);
208:
209: cvs_buf_puts(buf, "Log Message:\nDirectory ");
210: cvs_buf_puts(buf, current_cvsroot->cr_dir);
211: cvs_buf_putc(buf, '/');
212: cvs_buf_puts(buf, repo);
213: cvs_buf_puts(buf, " added to the repository\n");
214:
215: cvs_buf_putc(buf, '\0');
216:
217: loginfo = cvs_buf_release(buf);
218: }
219:
1.45 joris 220: static void
221: add_directory(struct cvs_file *cf)
222: {
1.74 xsa 223: int added, nb;
1.45 joris 224: struct stat st;
1.44 joris 225: CVSENTRIES *entlist;
1.70 otto 226: char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p;
1.100 joris 227: struct file_info_list files_info;
228: struct file_info *fi;
229: struct trigger_list *line_list;
1.45 joris 230:
231: cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
1.44 joris 232:
1.74 xsa 233: (void)xsnprintf(entry, MAXPATHLEN, "%s%s",
234: cf->file_rpath, RCS_FILE_EXT);
1.45 joris 235:
1.53 joris 236: added = 1;
1.45 joris 237: if (stat(entry, &st) != -1) {
238: cvs_log(LP_NOTICE, "cannot add directory %s: "
239: "a file with that name already exists",
240: cf->file_path);
1.53 joris 241: added = 0;
1.45 joris 242: } else {
1.56 xsa 243: /* Let's see if we have any per-directory tags first. */
244: cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
245:
1.76 xsa 246: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
247: cf->file_path, CVS_PATH_CVSDIR);
1.45 joris 248:
1.85 tobias 249: if (cvs_server_active) {
250: if (mkdir(cf->file_rpath, 0755) == -1 &&
251: errno != EEXIST)
252: fatal("add_directory: %s: %s", cf->file_rpath,
253: strerror(errno));
254: } else if (stat(entry, &st) != -1) {
1.45 joris 255: if (!S_ISDIR(st.st_mode)) {
256: cvs_log(LP_ERR, "%s exists but is not "
257: "directory", entry);
258: } else {
259: cvs_log(LP_NOTICE, "%s already exists",
260: entry);
261: }
1.53 joris 262: added = 0;
263: } else if (cvs_noexec != 1) {
1.45 joris 264: if (mkdir(cf->file_rpath, 0755) == -1 &&
265: errno != EEXIST)
1.85 tobias 266: fatal("add_directory: %s: %s", cf->file_rpath,
1.45 joris 267: strerror(errno));
268:
269: cvs_get_repository_name(cf->file_wd, repo,
270: MAXPATHLEN);
271:
1.76 xsa 272: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
273: repo, cf->file_name);
1.45 joris 274:
275: cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
1.91 tobias 276: entry, tag, date);
1.45 joris 277:
1.70 otto 278: p = xmalloc(CVS_ENT_MAXLINELEN);
1.94 xsa 279: cvs_ent_line_str(cf->file_name, NULL, NULL, NULL,
280: NULL, 1, 0, p, CVS_ENT_MAXLINELEN);
281:
1.45 joris 282: entlist = cvs_ent_open(cf->file_wd);
1.70 otto 283: cvs_ent_add(entlist, p);
1.79 joris 284: xfree(p);
1.53 joris 285: }
286: }
1.45 joris 287:
1.85 tobias 288: if (added == 1 && current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
1.74 xsa 289: (void)xsnprintf(msg, sizeof(msg),
1.56 xsa 290: "Directory %s added to the repository", cf->file_rpath);
291:
292: if (tag != NULL) {
293: (void)strlcat(msg,
294: "\n--> Using per-directory sticky tag ",
295: sizeof(msg));
296: (void)strlcat(msg, tag, sizeof(msg));
297: }
298: if (date != NULL) {
299: (void)strlcat(msg,
300: "\n--> Using per-directory sticky date ",
301: sizeof(msg));
302: (void)strlcat(msg, date, sizeof(msg));
303: }
304: cvs_printf("%s\n", msg);
305:
306: if (tag != NULL)
307: xfree(tag);
308: if (date != NULL)
309: xfree(date);
1.100 joris 310:
311: cvs_get_repository_name(cf->file_path, repo, MAXPATHLEN);
312: line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo);
313: if (line_list != NULL) {
314: TAILQ_INIT(&files_info);
315: fi = xcalloc(1, sizeof(*fi));
316: fi->file_path = xstrdup(cf->file_path);
317: TAILQ_INSERT_TAIL(&files_info, fi, flist);
318:
319: cvs_add_loginfo(repo);
320: cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo,
321: loginfo, line_list, &files_info);
322:
323: cvs_trigger_freeinfo(&files_info);
324: cvs_trigger_freelist(line_list);
325: if (loginfo != NULL)
326: xfree(loginfo);
327: }
1.45 joris 328: }
329:
330: cf->file_status = FILE_SKIP;
331: }
1.3 jfb 332:
1.45 joris 333: static void
334: add_file(struct cvs_file *cf)
335: {
1.93 tobias 336: int added, nb, stop;
1.78 xsa 337: char revbuf[CVS_REV_BUFSZ];
1.52 joris 338: RCSNUM *head;
1.93 tobias 339: char *tag;
340:
341: cvs_parse_tagfile(cf->file_wd, &tag, NULL, &nb);
342: if (nb) {
343: cvs_log(LP_ERR, "cannot add file on non-branch tag %s", tag);
344: return;
345: }
1.10 xsa 346:
1.68 joris 347: if (cf->file_rcs != NULL) {
348: head = rcs_head_get(cf->file_rcs);
1.83 tobias 349: if (head == NULL)
1.102 tobias 350: fatal("RCS head empty or missing in %s",
1.83 tobias 351: cf->file_rcs->rf_path);
1.68 joris 352: rcsnum_tostr(head, revbuf, sizeof(revbuf));
353: rcsnum_free(head);
354: }
1.44 joris 355:
1.48 xsa 356: added = stop = 0;
1.43 joris 357: switch (cf->file_status) {
358: case FILE_ADDED:
1.101 tobias 359: case FILE_CHECKOUT:
1.48 xsa 360: if (verbosity > 1)
361: cvs_log(LP_NOTICE, "%s has already been entered",
362: cf->file_path);
363: stop = 1;
364: break;
365: case FILE_REMOVED:
366: if (cf->file_rcs == NULL) {
367: cvs_log(LP_NOTICE, "cannot resurrect %s; "
368: "RCS file removed by second party", cf->file_name);
1.73 joris 369: } else if (cf->fd == -1) {
1.55 xsa 370: add_entry(cf);
1.48 xsa 371:
1.55 xsa 372: /* Restore the file. */
1.52 joris 373: head = rcs_head_get(cf->file_rcs);
1.83 tobias 374: if (head == NULL)
1.102 tobias 375: fatal("RCS head empty or missing in %s",
1.83 tobias 376: cf->file_rcs->rf_path);
1.82 joris 377: cvs_checkout_file(cf, head, NULL, 0);
1.68 joris 378: rcsnum_free(head);
379:
1.48 xsa 380: cvs_printf("U %s\n", cf->file_path);
381:
382: cvs_log(LP_NOTICE, "%s, version %s, resurrected",
383: cf->file_name, revbuf);
384:
385: cf->file_status = FILE_UPTODATE;
386: }
1.44 joris 387: stop = 1;
388: break;
1.50 xsa 389: case FILE_CONFLICT:
390: case FILE_LOST:
391: case FILE_MODIFIED:
1.44 joris 392: case FILE_UPTODATE:
393: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
394: cvs_log(LP_NOTICE, "%s already exists, with version "
395: "number %s", cf->file_path, revbuf);
396: stop = 1;
397: }
1.43 joris 398: break;
399: case FILE_UNKNOWN:
1.44 joris 400: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
401: cvs_log(LP_NOTICE, "re-adding file %s "
402: "(instead of dead revision %s)",
403: cf->file_path, revbuf);
1.73 joris 404: added++;
405: } else if (cf->fd != -1) {
1.44 joris 406: cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
407: cf->file_path);
1.73 joris 408: added++;
409: } else {
410: stop = 1;
1.44 joris 411: }
1.43 joris 412: break;
413: default:
414: break;
1.1 jfb 415: }
1.44 joris 416:
417: if (stop == 1)
418: return;
419:
1.55 xsa 420: add_entry(cf);
1.44 joris 421:
1.48 xsa 422: if (added != 0) {
1.67 joris 423: cvs_log(LP_NOTICE, "use '%s commit' to add %s "
424: "permanently", __progname,
425: (added == 1) ? "this file" : "these files");
1.48 xsa 426: }
1.55 xsa 427: }
428:
429: static void
430: add_entry(struct cvs_file *cf)
431: {
432: FILE *fp;
1.94 xsa 433: char *entry, path[MAXPATHLEN];
1.78 xsa 434: char revbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ];
1.93 tobias 435: char sticky[CVS_ENT_MAXLINELEN];
1.55 xsa 436: CVSENTRIES *entlist;
437:
438: if (cvs_noexec == 1)
439: return;
440:
1.93 tobias 441: sticky[0] = '\0';
1.94 xsa 442: entry = xmalloc(CVS_ENT_MAXLINELEN);
1.93 tobias 443:
1.55 xsa 444: if (cf->file_status == FILE_REMOVED) {
445: rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
446:
447: ctime_r(&cf->file_ent->ce_mtime, tbuf);
1.86 tobias 448: tbuf[strcspn(tbuf, "\n")] = '\0';
1.55 xsa 449:
1.93 tobias 450: if (cf->file_ent->ce_tag != NULL)
451: (void)xsnprintf(sticky, sizeof(sticky), "T%s",
452: cf->file_ent->ce_tag);
453:
1.55 xsa 454: /* Remove the '-' prefixing the version number. */
1.94 xsa 455: cvs_ent_line_str(cf->file_name, revbuf, tbuf,
456: cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "", sticky,
457: 0, 0, entry, CVS_ENT_MAXLINELEN);
1.55 xsa 458: } else {
459: if (logmsg != NULL) {
1.99 tobias 460: (void)xsnprintf(path, MAXPATHLEN, "%s/%s/%s%s",
461: cf->file_wd, CVS_PATH_CVSDIR, cf->file_name,
462: CVS_DESCR_FILE_EXT);
1.55 xsa 463:
464: if ((fp = fopen(path, "w+")) == NULL)
465: fatal("add_entry: fopen `%s': %s",
466: path, strerror(errno));
467:
468: if (fputs(logmsg, fp) == EOF) {
469: (void)unlink(path);
470: fatal("add_entry: fputs `%s': %s",
471: path, strerror(errno));
472: }
473: (void)fclose(fp);
474: }
475:
1.93 tobias 476: if (cvs_directory_tag != NULL)
477: (void)xsnprintf(sticky, sizeof(sticky), "T%s",
478: cvs_directory_tag);
479:
480: tbuf[0] = '\0';
481: if (!cvs_server_active)
482: (void)xsnprintf(tbuf, sizeof(tbuf), "Initial %s",
483: cf->file_name);
484:
1.94 xsa 485: cvs_ent_line_str(cf->file_name, "0", tbuf, kflag ? kbuf : "",
486: sticky, 0, 0, entry, CVS_ENT_MAXLINELEN);
1.55 xsa 487: }
488:
1.84 tobias 489: if (cvs_server_active) {
490: cvs_server_send_response("Checked-in %s/", cf->file_wd);
491: cvs_server_send_response(cf->file_path);
492: cvs_server_send_response(entry);
493: } else {
494: entlist = cvs_ent_open(cf->file_wd);
495: cvs_ent_add(entlist, entry);
496: }
1.94 xsa 497: xfree(entry);
1.1 jfb 498: }