Annotation of src/usr.bin/cvs/add.c, Revision 1.76
1.76 ! xsa 1: /* $OpenBSD: add.c,v 1.75 2007/02/09 03:49:15 joris 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: }
1.74 xsa 69: (void)xsnprintf(kbuf, sizeof(kbuf), "-k%s", koptstr);
1.72 xsa 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: {
1.70 otto 123: char entry[CVS_ENT_MAXLINELEN];
1.63 joris 124: CVSENTRIES *entlist;
125:
126: if (cf->file_type == CVS_DIR) {
1.74 xsa 127: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN,
128: "D/%s/////", cf->file_name);
1.64 joris 129:
1.63 joris 130: entlist = cvs_ent_open(cf->file_wd);
131: cvs_ent_add(entlist, entry);
132: cvs_ent_close(entlist, ENT_SYNC);
133: } else {
134: add_entry(cf);
135: }
1.3 jfb 136: }
137:
1.43 joris 138: void
139: cvs_add_local(struct cvs_file *cf)
1.3 jfb 140: {
1.45 joris 141: cvs_log(LP_TRACE, "cvs_add_local(%s)", cf->file_path);
142:
1.75 joris 143: cvs_file_classify(cf, NULL);
1.49 xsa 144:
145: /* dont use `cvs add *' */
146: if (strcmp(cf->file_name, ".") == 0 ||
147: strcmp(cf->file_name, "..") == 0 ||
148: strcmp(cf->file_name, CVS_PATH_CVSDIR) == 0) {
149: if (verbosity > 1)
150: cvs_log(LP_ERR,
151: "cannot add special file `%s'; skipping",
152: cf->file_name);
153: return;
154: }
1.45 joris 155:
156: if (cf->file_type == CVS_DIR)
157: add_directory(cf);
158: else
159: add_file(cf);
160: }
161:
162: static void
163: add_directory(struct cvs_file *cf)
164: {
1.74 xsa 165: int added, nb;
1.45 joris 166: struct stat st;
1.44 joris 167: CVSENTRIES *entlist;
1.70 otto 168: char *date, entry[MAXPATHLEN], msg[1024], repo[MAXPATHLEN], *tag, *p;
1.45 joris 169:
170: cvs_log(LP_TRACE, "add_directory(%s)", cf->file_path);
1.44 joris 171:
1.74 xsa 172: (void)xsnprintf(entry, MAXPATHLEN, "%s%s",
173: cf->file_rpath, RCS_FILE_EXT);
1.45 joris 174:
1.53 joris 175: added = 1;
1.45 joris 176: if (stat(entry, &st) != -1) {
177: cvs_log(LP_NOTICE, "cannot add directory %s: "
178: "a file with that name already exists",
179: cf->file_path);
1.53 joris 180: added = 0;
1.45 joris 181: } else {
1.56 xsa 182: /* Let's see if we have any per-directory tags first. */
183: cvs_parse_tagfile(cf->file_wd, &tag, &date, &nb);
184:
1.76 ! xsa 185: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
! 186: cf->file_path, CVS_PATH_CVSDIR);
1.45 joris 187:
188: if (stat(entry, &st) != -1) {
189: if (!S_ISDIR(st.st_mode)) {
190: cvs_log(LP_ERR, "%s exists but is not "
191: "directory", entry);
192: } else {
193: cvs_log(LP_NOTICE, "%s already exists",
194: entry);
195: }
1.53 joris 196: added = 0;
197: } else if (cvs_noexec != 1) {
1.45 joris 198: if (mkdir(cf->file_rpath, 0755) == -1 &&
199: errno != EEXIST)
200: fatal("add_directory: %s: %s", cf->file_path,
201: strerror(errno));
202:
203: cvs_get_repository_name(cf->file_wd, repo,
204: MAXPATHLEN);
205:
1.76 ! xsa 206: (void)xsnprintf(entry, MAXPATHLEN, "%s/%s",
! 207: repo, cf->file_name);
1.45 joris 208:
209: cvs_mkadmin(cf->file_path, current_cvsroot->cr_dir,
1.56 xsa 210: entry, tag, date, nb);
1.45 joris 211:
1.70 otto 212: p = xmalloc(CVS_ENT_MAXLINELEN);
1.74 xsa 213: (void)xsnprintf(p, CVS_ENT_MAXLINELEN,
1.45 joris 214: "D/%s/////", cf->file_name);
215: entlist = cvs_ent_open(cf->file_wd);
1.70 otto 216: cvs_ent_add(entlist, p);
1.45 joris 217: cvs_ent_close(entlist, ENT_SYNC);
1.53 joris 218: }
219: }
1.45 joris 220:
1.53 joris 221: if (added == 1) {
1.74 xsa 222: (void)xsnprintf(msg, sizeof(msg),
1.56 xsa 223: "Directory %s added to the repository", cf->file_rpath);
224:
225: if (tag != NULL) {
226: (void)strlcat(msg,
227: "\n--> Using per-directory sticky tag ",
228: sizeof(msg));
229: (void)strlcat(msg, tag, sizeof(msg));
230: }
231: if (date != NULL) {
232: (void)strlcat(msg,
233: "\n--> Using per-directory sticky date ",
234: sizeof(msg));
235: (void)strlcat(msg, date, sizeof(msg));
236: }
237: cvs_printf("%s\n", msg);
238:
239: if (tag != NULL)
240: xfree(tag);
241: if (date != NULL)
242: xfree(date);
1.45 joris 243: }
244:
245: cf->file_status = FILE_SKIP;
246: }
1.3 jfb 247:
1.45 joris 248: static void
249: add_file(struct cvs_file *cf)
250: {
1.55 xsa 251: int added, stop;
252: char revbuf[16];
1.52 joris 253: RCSNUM *head;
1.10 xsa 254:
1.68 joris 255: if (cf->file_rcs != NULL) {
256: head = rcs_head_get(cf->file_rcs);
257: rcsnum_tostr(head, revbuf, sizeof(revbuf));
258: rcsnum_free(head);
259: }
1.44 joris 260:
1.48 xsa 261: added = stop = 0;
1.43 joris 262: switch (cf->file_status) {
263: case FILE_ADDED:
1.48 xsa 264: if (verbosity > 1)
265: cvs_log(LP_NOTICE, "%s has already been entered",
266: cf->file_path);
267: stop = 1;
268: break;
269: case FILE_REMOVED:
270: if (cf->file_rcs == NULL) {
271: cvs_log(LP_NOTICE, "cannot resurrect %s; "
272: "RCS file removed by second party", cf->file_name);
1.73 joris 273: } else if (cf->fd == -1) {
1.55 xsa 274: add_entry(cf);
1.48 xsa 275:
1.55 xsa 276: /* Restore the file. */
1.52 joris 277: head = rcs_head_get(cf->file_rcs);
1.69 joris 278: cvs_checkout_file(cf, head, 0);
1.68 joris 279: rcsnum_free(head);
280:
1.48 xsa 281: cvs_printf("U %s\n", cf->file_path);
282:
283: cvs_log(LP_NOTICE, "%s, version %s, resurrected",
284: cf->file_name, revbuf);
285:
286: cf->file_status = FILE_UPTODATE;
287: }
1.44 joris 288: stop = 1;
289: break;
1.50 xsa 290: case FILE_CONFLICT:
291: case FILE_LOST:
292: case FILE_MODIFIED:
1.44 joris 293: case FILE_UPTODATE:
294: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 0) {
295: cvs_log(LP_NOTICE, "%s already exists, with version "
296: "number %s", cf->file_path, revbuf);
297: stop = 1;
298: }
1.43 joris 299: break;
300: case FILE_UNKNOWN:
1.44 joris 301: if (cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1) {
302: cvs_log(LP_NOTICE, "re-adding file %s "
303: "(instead of dead revision %s)",
304: cf->file_path, revbuf);
1.73 joris 305: added++;
306: } else if (cf->fd != -1) {
1.44 joris 307: cvs_log(LP_NOTICE, "scheduling file '%s' for addition",
308: cf->file_path);
1.73 joris 309: added++;
310: } else {
311: stop = 1;
1.44 joris 312: }
1.43 joris 313: break;
314: default:
315: break;
1.1 jfb 316: }
1.44 joris 317:
318: if (stop == 1)
319: return;
320:
1.55 xsa 321: add_entry(cf);
1.44 joris 322:
1.48 xsa 323: if (added != 0) {
1.67 joris 324: cvs_log(LP_NOTICE, "use '%s commit' to add %s "
325: "permanently", __progname,
326: (added == 1) ? "this file" : "these files");
1.48 xsa 327: }
1.55 xsa 328: }
329:
330: static void
331: add_entry(struct cvs_file *cf)
332: {
333: FILE *fp;
1.70 otto 334: char entry[CVS_ENT_MAXLINELEN], path[MAXPATHLEN], revbuf[16], tbuf[32];
1.55 xsa 335: CVSENTRIES *entlist;
336:
337: if (cvs_noexec == 1)
338: return;
339:
340: if (cf->file_status == FILE_REMOVED) {
341: rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf));
342:
343: ctime_r(&cf->file_ent->ce_mtime, tbuf);
344: if (tbuf[strlen(tbuf) - 1] == '\n')
345: tbuf[strlen(tbuf) - 1] = '\0';
346:
347: /* Remove the '-' prefixing the version number. */
1.74 xsa 348: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN,
1.72 xsa 349: "/%s/%s/%s/%s/", cf->file_name, revbuf, tbuf,
350: cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "");
1.55 xsa 351: } else {
352: if (logmsg != NULL) {
1.74 xsa 353: (void)xsnprintf(path, MAXPATHLEN, "%s/%s%s",
1.55 xsa 354: CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT);
355:
356: if ((fp = fopen(path, "w+")) == NULL)
357: fatal("add_entry: fopen `%s': %s",
358: path, strerror(errno));
359:
360: if (fputs(logmsg, fp) == EOF) {
361: (void)unlink(path);
362: fatal("add_entry: fputs `%s': %s",
363: path, strerror(errno));
364: }
365: (void)fclose(fp);
366: }
367:
1.74 xsa 368: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN,
1.72 xsa 369: "/%s/0/Initial %s/%s/", cf->file_name, cf->file_name,
370: (kflag != RCS_KWEXP_DEFAULT) ? kbuf : "");
1.55 xsa 371: }
372:
373: entlist = cvs_ent_open(cf->file_wd);
374: cvs_ent_add(entlist, entry);
375: cvs_ent_close(entlist, ENT_SYNC);
1.1 jfb 376: }