Annotation of src/usr.bin/cvs/add.c, Revision 1.102
1.102 ! tobias 1: /* $OpenBSD: add.c,v 1.101 2008/06/14 00:55:31 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);
140: cvs_ent_close(entlist, ENT_SYNC);
1.94 xsa 141:
142: xfree(entry);
1.63 joris 143: } else {
144: add_entry(cf);
145: }
1.3 jfb 146: }
147:
1.43 joris 148: void
149: cvs_add_local(struct cvs_file *cf)
1.3 jfb 150: {
1.45 joris 151: cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
152:
1.96 joris 153: if (cvs_cmdop != CVS_OP_CHECKOUT && cvs_cmdop != CVS_OP_UPDATE)
154: cvs_file_classify(cf, cvs_directory_tag);
1.49 xsa 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: }
1.45 joris 166:
167: if (cf->file_type == CVS_DIR)
168: add_directory(cf);
169: else
170: add_file(cf);
171: }
172:
1.85 tobias 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:
1.100 joris 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:
1.45 joris 221: static void
222: add_directory(struct cvs_file *cf)
223: {
1.74 xsa 224: int added, nb;
1.45 joris 225: struct stat st;
1.44 joris 226: CVSENTRIES *entlist;
1.70 otto 227: char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p;
1.100 joris 228: struct file_info_list files_info;
229: struct file_info *fi;
230: struct trigger_list *line_list;
1.45 joris 231:
232: cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
1.44 joris 233:
1.74 xsa 234: (void)xsnprintf(entry, MAXPATHLEN, "%s%s",
235: cf->file_rpath, RCS_FILE_EXT);
1.45 joris 236:
1.53 joris 237: added = 1;
1.45 joris 238: if (stat(entry, &st) != -1) {
239: cvs_log(LP_NOTICE, "cannot add directory %s: "
240: "a file with that name already exists",
241: cf->file_path);
1.53 joris 242: added = 0;
1.45 joris 243: } else {
1.56 xsa 244: /* Let's see if we have any per-directory tags first. */
245: cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
246:
1.76 xsa 247: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
248: cf->file_path, CVS_PATH_CVSDIR);
1.45 joris 249:
1.85 tobias 250: if (cvs_server_active) {
251: if (mkdir(cf->file_rpath, 0755) == -1 &&
252: errno != EEXIST)
253: fatal("add_directory: %s: %s", cf->file_rpath,
254: strerror(errno));
255: } else if (stat(entry, &st) != -1) {
1.45 joris 256: if (!S_ISDIR(st.st_mode)) {
257: cvs_log(LP_ERR, "%s exists but is not "
258: "directory", entry);
259: } else {
260: cvs_log(LP_NOTICE, "%s already exists",
261: entry);
262: }
1.53 joris 263: added = 0;
264: } else if (cvs_noexec != 1) {
1.45 joris 265: if (mkdir(cf->file_rpath, 0755) == -1 &&
266: errno != EEXIST)
1.85 tobias 267: fatal("add_directory: %s: %s", cf->file_rpath,
1.45 joris 268: strerror(errno));
269:
270: cvs_get_repository_name(cf->file_wd, repo,
271: MAXPATHLEN);
272:
1.76 xsa 273: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
274: repo, cf->file_name);
1.45 joris 275:
276: cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
1.91 tobias 277: entry, tag, date);
1.45 joris 278:
1.70 otto 279: p = xmalloc(CVS_ENT_MAXLINELEN);
1.94 xsa 280: cvs_ent_line_str(cf->file_name, NULL, NULL, NULL,
281: NULL, 1, 0, p, CVS_ENT_MAXLINELEN);
282:
1.45 joris 283: entlist = cvs_ent_open(cf->file_wd);
1.70 otto 284: cvs_ent_add(entlist, p);
1.45 joris 285: cvs_ent_close(entlist, ENT_SYNC);
1.79 joris 286: xfree(p);
1.53 joris 287: }
288: }
1.45 joris 289:
1.85 tobias 290: if (added == 1 && current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
1.74 xsa 291: (void)xsnprintf(msg, sizeof(msg),
1.56 xsa 292: "Directory %s added to the repository", cf->file_rpath);
293:
294: if (tag != NULL) {
295: (void)strlcat(msg,
296: "\n--> Using per-directory sticky tag ",
297: sizeof(msg));
298: (void)strlcat(msg, tag, sizeof(msg));
299: }
300: if (date != NULL) {
301: (void)strlcat(msg,
302: "\n--> Using per-directory sticky date ",
303: sizeof(msg));
304: (void)strlcat(msg, date, sizeof(msg));
305: }
306: cvs_printf("%s\n", msg);
307:
308: if (tag != NULL)
309: xfree(tag);
310: if (date != NULL)
311: xfree(date);
1.100 joris 312:
313: cvs_get_repository_name(cf->file_path, repo, MAXPATHLEN);
314: line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo);
315: if (line_list != NULL) {
316: TAILQ_INIT(&files_info);
317: fi = xcalloc(1, sizeof(*fi));
318: fi->file_path = xstrdup(cf->file_path);
319: TAILQ_INSERT_TAIL(&files_info, fi, flist);
320:
321: cvs_add_loginfo(repo);
322: cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo,
323: loginfo, line_list, &files_info);
324:
325: cvs_trigger_freeinfo(&files_info);
326: cvs_trigger_freelist(line_list);
327: if (loginfo != NULL)
328: xfree(loginfo);
329: }
1.45 joris 330: }
331:
332: cf->file_status = FILE_SKIP;
333: }
1.3 jfb 334:
1.45 joris 335: static void
336: add_file(struct cvs_file *cf)
337: {
1.93 tobias 338: int added, nb, stop;
1.78 xsa 339: char revbuf[CVS_REV_BUFSZ];
1.52 joris 340: RCSNUM *head;
1.93 tobias 341: char *tag;
342:
343: cvs_parse_tagfile(cf->file_wd, &tag, NULL, &nb);
344: if (nb) {
345: cvs_log(LP_ERR, "cannot add file on non-branch tag %s", tag);
346: return;
347: }
1.10 xsa 348:
1.68 joris 349: if (cf->file_rcs != NULL) {
350: head = rcs_head_get(cf->file_rcs);
1.83 tobias 351: if (head == NULL)
1.102 ! tobias 352: fatal("RCS head empty or missing in %s",
1.83 tobias 353: cf->file_rcs->rf_path);
1.68 joris 354: rcsnum_tostr(head, revbuf, sizeof(revbuf));
355: rcsnum_free(head);
356: }
1.44 joris 357:
1.48 xsa 358: added = stop = 0;
1.43 joris 359: switch (cf->file_status) {
360: case FILE_ADDED:
1.101 tobias 361: case FILE_CHECKOUT:
1.48 xsa 362: if (verbosity > 1)
363: cvs_log(LP_NOTICE, "%s has already been entered",
364: cf->file_path);
365: stop = 1;
366: break;
367: case FILE_REMOVED:
368: if (cf->file_rcs == NULL) {
369: cvs_log(LP_NOTICE, "cannot resurrect %s; "
370: "RCS file removed by second party", cf->file_name);
1.73 joris 371: } else if (cf->fd == -1) {
1.55 xsa 372: add_entry(cf);
1.48 xsa 373:
1.55 xsa 374: /* Restore the file. */
1.52 joris 375: head = rcs_head_get(cf->file_rcs);
1.83 tobias 376: if (head == NULL)
1.102 ! tobias 377: fatal("RCS head empty or missing in %s",
1.83 tobias 378: cf->file_rcs->rf_path);
1.82 joris 379: cvs_checkout_file(cf, head, NULL, 0);
1.68 joris 380: rcsnum_free(head);
381:
1.48 xsa 382: cvs_printf("U %s\n", cf->file_path);
383:
384: cvs_log(LP_NOTICE, "%s, version %s, resurrected",
385: cf->file_name, revbuf);
386:
387: cf->file_status = FILE_UPTODATE;
388: }
1.44 joris 389: stop = 1;
390: break;
1.50 xsa 391: case FILE_CONFLICT:
392: case FILE_LOST:
393: case FILE_MODIFIED:
1.44 joris 394: case FILE_UPTODATE:
395: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
396: cvs_log(LP_NOTICE, "%s already exists, with version "
397: "number %s", cf->file_path, revbuf);
398: stop = 1;
399: }
1.43 joris 400: break;
401: case FILE_UNKNOWN:
1.44 joris 402: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
403: cvs_log(LP_NOTICE, "re-adding file %s "
404: "(instead of dead revision %s)",
405: cf->file_path, revbuf);
1.73 joris 406: added++;
407: } else if (cf->fd != -1) {
1.44 joris 408: cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
409: cf->file_path);
1.73 joris 410: added++;
411: } else {
412: stop = 1;
1.44 joris 413: }
1.43 joris 414: break;
415: default:
416: break;
1.1 jfb 417: }
1.44 joris 418:
419: if (stop == 1)
420: return;
421:
1.55 xsa 422: add_entry(cf);
1.44 joris 423:
1.48 xsa 424: if (added != 0) {
1.67 joris 425: cvs_log(LP_NOTICE, "use '%s commit' to add %s "
426: "permanently", __progname,
427: (added == 1) ? "this file" : "these files");
1.48 xsa 428: }
1.55 xsa 429: }
430:
431: static void
432: add_entry(struct cvs_file *cf)
433: {
434: FILE *fp;
1.94 xsa 435: char *entry, path[MAXPATHLEN];
1.78 xsa 436: char revbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ];
1.93 tobias 437: char sticky[CVS_ENT_MAXLINELEN];
1.55 xsa 438: CVSENTRIES *entlist;
439:
440: if (cvs_noexec == 1)
441: return;
442:
1.93 tobias 443: sticky[0] = '\0';
1.94 xsa 444: entry = xmalloc(CVS_ENT_MAXLINELEN);
1.93 tobias 445:
1.55 xsa 446: if (cf->file_status == FILE_REMOVED) {
447: rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
448:
449: ctime_r(&cf->file_ent->ce_mtime, tbuf);
1.86 tobias 450: tbuf[strcspn(tbuf, "\n")] = '\0';
1.55 xsa 451:
1.93 tobias 452: if (cf->file_ent->ce_tag != NULL)
453: (void)xsnprintf(sticky, sizeof(sticky), "T%s",
454: cf->file_ent->ce_tag);
455:
1.55 xsa 456: /* Remove the '-' prefixing the version number. */
1.94 xsa 457: cvs_ent_line_str(cf->file_name, revbuf, tbuf,
458: cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "", sticky,
459: 0, 0, entry, CVS_ENT_MAXLINELEN);
1.55 xsa 460: } else {
461: if (logmsg != NULL) {
1.99 tobias 462: (void)xsnprintf(path, MAXPATHLEN, "%s/%s/%s%s",
463: cf->file_wd, CVS_PATH_CVSDIR, cf->file_name,
464: CVS_DESCR_FILE_EXT);
1.55 xsa 465:
466: if ((fp = fopen(path, "w+")) == NULL)
467: fatal("add_entry: fopen `%s': %s",
468: path, strerror(errno));
469:
470: if (fputs(logmsg, fp) == EOF) {
471: (void)unlink(path);
472: fatal("add_entry: fputs `%s': %s",
473: path, strerror(errno));
474: }
475: (void)fclose(fp);
476: }
477:
1.93 tobias 478: if (cvs_directory_tag != NULL)
479: (void)xsnprintf(sticky, sizeof(sticky), "T%s",
480: cvs_directory_tag);
481:
482: tbuf[0] = '\0';
483: if (!cvs_server_active)
484: (void)xsnprintf(tbuf, sizeof(tbuf), "Initial %s",
485: cf->file_name);
486:
1.94 xsa 487: cvs_ent_line_str(cf->file_name, "0", tbuf, kflag ? kbuf : "",
488: sticky, 0, 0, entry, CVS_ENT_MAXLINELEN);
1.55 xsa 489: }
490:
1.84 tobias 491: if (cvs_server_active) {
492: cvs_server_send_response("Checked-in %s/", cf->file_wd);
493: cvs_server_send_response(cf->file_path);
494: cvs_server_send_response(entry);
495: } else {
496: entlist = cvs_ent_open(cf->file_wd);
497: cvs_ent_add(entlist, entry);
498: cvs_ent_close(entlist, ENT_SYNC);
499: }
1.94 xsa 500: xfree(entry);
1.1 jfb 501: }