Annotation of src/usr.bin/cvs/add.c, Revision 1.86
1.86 ! tobias 1: /* $OpenBSD: add.c,v 1.85 2008/01/10 09:44:32 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.43 joris 30: void cvs_add_local(struct cvs_file *);
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.72 xsa 38: static int kflag = RCS_KWEXP_DEFAULT;
39: static char kbuf[8], *koptstr;
40:
1.43 joris 41: char *logmsg;
1.21 jfb 42:
43: struct cvs_cmd cvs_cmd_add = {
1.57 joris 44: CVS_OP_ADD, 0, "add",
1.21 jfb 45: { "ad", "new" },
1.43 joris 46: "Add a new file or directory to the repository",
1.72 xsa 47: "[-k mode] [-m message] ...",
48: "k:m:",
1.21 jfb 49: NULL,
1.43 joris 50: cvs_add
1.15 joris 51: };
1.3 jfb 52:
1.43 joris 53: int
54: cvs_add(int argc, char **argv)
1.1 jfb 55: {
1.15 joris 56: int ch;
1.43 joris 57: int flags;
58: struct cvs_recursion cr;
1.1 jfb 59:
1.45 joris 60: flags = CR_REPO;
1.1 jfb 61:
1.43 joris 62: while ((ch = getopt(argc, argv, cvs_cmd_add.cmd_opts)) != -1) {
1.1 jfb 63: switch (ch) {
1.72 xsa 64: case 'k':
65: koptstr = optarg;
66: kflag = rcs_kflag_get(koptstr);
67: if (RCS_KWEXP_INVAL(kflag)) {
68: cvs_log(LP_ERR,
69: "invalid RCS keyword expension mode");
70: fatal("%s", cvs_cmd_add.cmd_synopsis);
71: }
1.74 xsa 72: (void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", koptstr);
1.72 xsa 73: break;
1.1 jfb 74: case 'm':
1.43 joris 75: logmsg = xstrdup(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.61 xsa 94:
1.72 xsa 95: if (kflag != RCS_KWEXP_DEFAULT)
96: cvs_client_send_request("Argument %s", kbuf);
97:
1.61 xsa 98: if (logmsg != NULL)
1.80 joris 99: cvs_client_send_logmsg(logmsg);
1.61 xsa 100: } else {
101: cr.fileproc = cvs_add_local;
102: }
103:
1.43 joris 104: cr.flags = flags;
105:
1.46 joris 106: cvs_file_run(argc, argv, &cr);
1.61 xsa 107:
108: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.85 tobias 109: cvs_client_senddir(".");
1.61 xsa 110: cvs_client_send_files(argv, argc);
111: cvs_client_send_request("add");
112: cvs_client_get_responses();
1.63 joris 113:
114: if (server_response == SERVER_OK) {
115: cr.fileproc = cvs_add_entry;
116: cvs_file_run(argc, argv, &cr);
117: }
1.61 xsa 118: }
119:
1.3 jfb 120: return (0);
1.63 joris 121: }
122:
123: void
124: cvs_add_entry(struct cvs_file *cf)
125: {
1.70 otto 126: char entry[CVS_ENT_MAXLINELEN];
1.63 joris 127: CVSENTRIES *entlist;
128:
129: if (cf->file_type == CVS_DIR) {
1.74 xsa 130: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN,
131: "D/%s/////", cf->file_name);
1.64 joris 132:
1.63 joris 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: }
1.3 jfb 139: }
140:
1.43 joris 141: void
142: cvs_add_local(struct cvs_file *cf)
1.3 jfb 143: {
1.45 joris 144: cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
145:
1.81 joris 146: cvs_file_classify(cf, cvs_directory_tag);
1.49 xsa 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: }
1.45 joris 158:
159: if (cf->file_type == CVS_DIR)
160: add_directory(cf);
161: else
162: add_file(cf);
163: }
164:
1.85 tobias 165: void
166: cvs_add_remote(struct cvs_file *cf)
167: {
168: char path[MAXPATHLEN];
169:
170: cvs_log(LP_TRACE, "cvs_add_remote(%s)", cf->file_path);
171:
172: cvs_file_classify(cf, cvs_directory_tag);
173:
174: if (cf->file_type == CVS_DIR) {
175: cvs_get_repository_path(cf->file_wd, path, MAXPATHLEN);
176: if (strlcat(path, "/", sizeof(path)) >= sizeof(path))
177: fatal("cvs_add_remote: truncation");
178: if (strlcat(path, cf->file_path, sizeof(path)) >= sizeof(path))
179: fatal("cvs_add_remote: truncation");
180: cvs_client_send_request("Directory %s\n%s", cf->file_path,
181: path);
182:
183: add_directory(cf);
184: } else {
185: cvs_client_sendfile(cf);
186: }
187: }
188:
1.45 joris 189: static void
190: add_directory(struct cvs_file *cf)
191: {
1.74 xsa 192: int added, nb;
1.45 joris 193: struct stat st;
1.44 joris 194: CVSENTRIES *entlist;
1.70 otto 195: char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p;
1.45 joris 196:
197: cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
1.44 joris 198:
1.74 xsa 199: (void)xsnprintf(entry, MAXPATHLEN, "%s%s",
200: cf->file_rpath, RCS_FILE_EXT);
1.45 joris 201:
1.53 joris 202: added = 1;
1.45 joris 203: if (stat(entry, &st) != -1) {
204: cvs_log(LP_NOTICE, "cannot add directory %s: "
205: "a file with that name already exists",
206: cf->file_path);
1.53 joris 207: added = 0;
1.45 joris 208: } else {
1.56 xsa 209: /* Let's see if we have any per-directory tags first. */
210: cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
211:
1.76 xsa 212: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
213: cf->file_path, CVS_PATH_CVSDIR);
1.45 joris 214:
1.85 tobias 215: if (cvs_server_active) {
216: if (mkdir(cf->file_rpath, 0755) == -1 &&
217: errno != EEXIST)
218: fatal("add_directory: %s: %s", cf->file_rpath,
219: strerror(errno));
220: } else if (stat(entry, &st) != -1) {
1.45 joris 221: if (!S_ISDIR(st.st_mode)) {
222: cvs_log(LP_ERR, "%s exists but is not "
223: "directory", entry);
224: } else {
225: cvs_log(LP_NOTICE, "%s already exists",
226: entry);
227: }
1.53 joris 228: added = 0;
229: } else if (cvs_noexec != 1) {
1.45 joris 230: if (mkdir(cf->file_rpath, 0755) == -1 &&
231: errno != EEXIST)
1.85 tobias 232: fatal("add_directory: %s: %s", cf->file_rpath,
1.45 joris 233: strerror(errno));
234:
235: cvs_get_repository_name(cf->file_wd, repo,
236: MAXPATHLEN);
237:
1.76 xsa 238: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
239: repo, cf->file_name);
1.45 joris 240:
241: cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
1.56 xsa 242: entry, tag, date, nb);
1.45 joris 243:
1.70 otto 244: p = xmalloc(CVS_ENT_MAXLINELEN);
1.74 xsa 245: (void)xsnprintf(p, CVS_ENT_MAXLINELEN,
1.45 joris 246: "D/%s/////", cf->file_name);
247: entlist = cvs_ent_open(cf->file_wd);
1.70 otto 248: cvs_ent_add(entlist, p);
1.45 joris 249: cvs_ent_close(entlist, ENT_SYNC);
1.79 joris 250: xfree(p);
1.53 joris 251: }
252: }
1.45 joris 253:
1.85 tobias 254: if (added == 1 && current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
1.74 xsa 255: (void)xsnprintf(msg, sizeof(msg),
1.56 xsa 256: "Directory %s added to the repository", cf->file_rpath);
257:
258: if (tag != NULL) {
259: (void)strlcat(msg,
260: "\n--> Using per-directory sticky tag ",
261: sizeof(msg));
262: (void)strlcat(msg, tag, sizeof(msg));
263: }
264: if (date != NULL) {
265: (void)strlcat(msg,
266: "\n--> Using per-directory sticky date ",
267: sizeof(msg));
268: (void)strlcat(msg, date, sizeof(msg));
269: }
270: cvs_printf("%s\n", msg);
271:
272: if (tag != NULL)
273: xfree(tag);
274: if (date != NULL)
275: xfree(date);
1.45 joris 276: }
277:
278: cf->file_status = FILE_SKIP;
279: }
1.3 jfb 280:
1.45 joris 281: static void
282: add_file(struct cvs_file *cf)
283: {
1.55 xsa 284: int added, stop;
1.78 xsa 285: char revbuf[CVS_REV_BUFSZ];
1.52 joris 286: RCSNUM *head;
1.10 xsa 287:
1.68 joris 288: if (cf->file_rcs != NULL) {
289: head = rcs_head_get(cf->file_rcs);
1.83 tobias 290: if (head == NULL)
291: fatal("RCS head empty or missing in %s\n",
292: cf->file_rcs->rf_path);
1.68 joris 293: rcsnum_tostr(head, revbuf, sizeof(revbuf));
294: rcsnum_free(head);
295: }
1.44 joris 296:
1.48 xsa 297: added = stop = 0;
1.43 joris 298: switch (cf->file_status) {
299: case FILE_ADDED:
1.48 xsa 300: if (verbosity > 1)
301: cvs_log(LP_NOTICE, "%s has already been entered",
302: cf->file_path);
303: stop = 1;
304: break;
305: case FILE_REMOVED:
306: if (cf->file_rcs == NULL) {
307: cvs_log(LP_NOTICE, "cannot resurrect %s; "
308: "RCS file removed by second party", cf->file_name);
1.73 joris 309: } else if (cf->fd == -1) {
1.55 xsa 310: add_entry(cf);
1.48 xsa 311:
1.55 xsa 312: /* Restore the file. */
1.52 joris 313: head = rcs_head_get(cf->file_rcs);
1.83 tobias 314: if (head == NULL)
315: fatal("RCS head empty or missing in %s\n",
316: cf->file_rcs->rf_path);
1.82 joris 317: cvs_checkout_file(cf, head, NULL, 0);
1.68 joris 318: rcsnum_free(head);
319:
1.48 xsa 320: cvs_printf("U %s\n", cf->file_path);
321:
322: cvs_log(LP_NOTICE, "%s, version %s, resurrected",
323: cf->file_name, revbuf);
324:
325: cf->file_status = FILE_UPTODATE;
326: }
1.44 joris 327: stop = 1;
328: break;
1.50 xsa 329: case FILE_CONFLICT:
330: case FILE_LOST:
331: case FILE_MODIFIED:
1.44 joris 332: case FILE_UPTODATE:
333: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
334: cvs_log(LP_NOTICE, "%s already exists, with version "
335: "number %s", cf->file_path, revbuf);
336: stop = 1;
337: }
1.43 joris 338: break;
339: case FILE_UNKNOWN:
1.44 joris 340: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
341: cvs_log(LP_NOTICE, "re-adding file %s "
342: "(instead of dead revision %s)",
343: cf->file_path, revbuf);
1.73 joris 344: added++;
345: } else if (cf->fd != -1) {
1.44 joris 346: cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
347: cf->file_path);
1.73 joris 348: added++;
349: } else {
350: stop = 1;
1.44 joris 351: }
1.43 joris 352: break;
353: default:
354: break;
1.1 jfb 355: }
1.44 joris 356:
357: if (stop == 1)
358: return;
359:
1.55 xsa 360: add_entry(cf);
1.44 joris 361:
1.48 xsa 362: if (added != 0) {
1.67 joris 363: cvs_log(LP_NOTICE, "use '%s commit' to add %s "
364: "permanently", __progname,
365: (added == 1) ? "this file" : "these files");
1.48 xsa 366: }
1.55 xsa 367: }
368:
369: static void
370: add_entry(struct cvs_file *cf)
371: {
372: FILE *fp;
1.78 xsa 373: char entry[CVS_ENT_MAXLINELEN], path[MAXPATHLEN];
374: char revbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ];
1.55 xsa 375: CVSENTRIES *entlist;
376:
377: if (cvs_noexec == 1)
378: return;
379:
380: if (cf->file_status == FILE_REMOVED) {
381: rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
382:
383: ctime_r(&cf->file_ent->ce_mtime, tbuf);
1.86 ! tobias 384: tbuf[strcspn(tbuf, "\n")] = '\0';
1.55 xsa 385:
386: /* Remove the '-' prefixing the version number. */
1.74 xsa 387: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN,
1.72 xsa 388: "/%s/%s/%s/%s/", cf->file_name, revbuf, tbuf,
389: cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "");
1.55 xsa 390: } else {
391: if (logmsg != NULL) {
1.74 xsa 392: (void)xsnprintf(path, MAXPATHLEN, "%s/%s%s",
1.55 xsa 393: CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT);
394:
395: if ((fp = fopen(path, "w+")) == NULL)
396: fatal("add_entry: fopen `%s': %s",
397: path, strerror(errno));
398:
399: if (fputs(logmsg, fp) == EOF) {
400: (void)unlink(path);
401: fatal("add_entry: fputs `%s': %s",
402: path, strerror(errno));
403: }
404: (void)fclose(fp);
405: }
406:
1.74 xsa 407: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN,
1.72 xsa 408: "/%s/0/Initial %s/%s/", cf->file_name, cf->file_name,
409: (kflag != RCS_KWEXP_DEFAULT) ? kbuf : "");
1.55 xsa 410: }
411:
1.84 tobias 412: if (cvs_server_active) {
413: cvs_server_send_response("Checked-in %s/", cf->file_wd);
414: cvs_server_send_response(cf->file_path);
415: cvs_server_send_response(entry);
416: } else {
417: entlist = cvs_ent_open(cf->file_wd);
418: cvs_ent_add(entlist, entry);
419: cvs_ent_close(entlist, ENT_SYNC);
420: }
1.1 jfb 421: }