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