Annotation of src/usr.bin/rcs/rcsprog.c, Revision 1.113
1.113 ! ray 1: /* $OpenBSD: rcsprog.c,v 1.112 2006/04/21 17:17:29 xsa Exp $ */
1.1 deraadt 2: /*
3: * Copyright (c) 2005 Jean-Francois Brousseau <jfb@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
1.59 xsa 27: #include "includes.h"
1.1 deraadt 28:
1.9 joris 29: #include "rcsprog.h"
1.1 deraadt 30:
1.29 joris 31: #define RCS_CMD_MAXARG 128
1.106 ray 32: #define RCSPROG_OPTSTRING "A:a:b::c:e::ik:Ll::m:Mn:N:o:qt::TUu::Vx::z::"
1.56 joris 33:
1.1 deraadt 34: const char rcs_version[] = "OpenCVS RCS version 3.6";
35:
1.91 ray 36: int rcsflags;
1.36 xsa 37: int rcs_optind;
1.28 joris 38: char *rcs_optarg;
1.42 xsa 39: char *rcs_suffixes;
1.36 xsa 40: char *rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.28 joris 41:
1.1 deraadt 42: struct rcs_prog {
1.26 deraadt 43: char *prog_name;
44: int (*prog_hdlr)(int, char **);
45: void (*prog_usage)(void);
1.1 deraadt 46: } programs[] = {
1.2 deraadt 47: { "rcs", rcs_main, rcs_usage },
1.17 joris 48: { "ci", checkin_main, checkin_usage },
1.11 joris 49: { "co", checkout_main, checkout_usage },
1.20 joris 50: { "rcsclean", rcsclean_main, rcsclean_usage },
1.18 joris 51: { "rcsdiff", rcsdiff_main, rcsdiff_usage },
1.33 xsa 52: { "rcsmerge", rcsmerge_main, rcsmerge_usage },
1.21 joris 53: { "rlog", rlog_main, rlog_usage },
1.22 joris 54: { "ident", ident_main, ident_usage },
1.1 deraadt 55: };
1.31 joris 56:
1.68 joris 57: struct cvs_wklhead rcs_temp_files;
58:
59: void sighdlr(int);
1.101 joris 60: static void rcs_attach_symbol(RCSFILE *, const char *);
1.56 joris 61:
1.78 ray 62: /* ARGSUSED */
1.31 joris 63: void
1.68 joris 64: sighdlr(int sig)
65: {
66: cvs_worklist_clean(&rcs_temp_files, cvs_worklist_unlink);
67: _exit(1);
68: }
69:
1.9 joris 70: int
1.29 joris 71: rcs_init(char *envstr, char **argv, int argvlen)
72: {
73: u_int i;
74: int argc, error;
75: char linebuf[256], *lp, *cp;
76:
77: strlcpy(linebuf, envstr, sizeof(linebuf));
78: memset(argv, 0, argvlen * sizeof(char *));
79:
80: error = argc = 0;
81: for (lp = linebuf; lp != NULL;) {
1.61 xsa 82: cp = strsep(&lp, " \t\b\f\n\r\t\v");
1.29 joris 83: if (cp == NULL)
84: break;
85: else if (*cp == '\0')
86: continue;
87:
88: if (argc == argvlen) {
89: error++;
90: break;
91: }
92:
1.53 joris 93: argv[argc] = xstrdup(cp);
1.29 joris 94: argc++;
95: }
96:
97: if (error != 0) {
98: for (i = 0; i < (u_int)argc; i++)
1.53 joris 99: xfree(argv[i]);
1.29 joris 100: argc = -1;
101: }
102:
103: return (argc);
104: }
105:
106: int
1.1 deraadt 107: main(int argc, char **argv)
108: {
109: u_int i;
1.29 joris 110: char *rcsinit, *cmd_argv[RCS_CMD_MAXARG];
111: int ret, cmd_argc;
1.1 deraadt 112:
113: ret = -1;
1.28 joris 114: rcs_optind = 1;
1.68 joris 115: SLIST_INIT(&rcs_temp_files);
1.1 deraadt 116:
1.29 joris 117: cmd_argc = 0;
1.30 joris 118: cmd_argv[cmd_argc++] = argv[0];
1.29 joris 119: if ((rcsinit = getenv("RCSINIT")) != NULL) {
120: ret = rcs_init(rcsinit, cmd_argv + 1,
121: RCS_CMD_MAXARG - 1);
122: if (ret < 0) {
1.110 xsa 123: warnx("failed to prepend RCSINIT options");
1.29 joris 124: exit (1);
125: }
126:
127: cmd_argc += ret;
128: }
1.36 xsa 129:
130: if ((rcs_tmpdir = getenv("TMPDIR")) == NULL)
131: rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.29 joris 132:
133: for (ret = 1; ret < argc; ret++)
134: cmd_argv[cmd_argc++] = argv[ret];
1.68 joris 135:
136: signal(SIGHUP, sighdlr);
137: signal(SIGINT, sighdlr);
138: signal(SIGQUIT, sighdlr);
139: signal(SIGABRT, sighdlr);
140: signal(SIGALRM, sighdlr);
141: signal(SIGTERM, sighdlr);
1.29 joris 142:
1.1 deraadt 143: for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++)
1.2 deraadt 144: if (strcmp(__progname, programs[i].prog_name) == 0) {
145: usage = programs[i].prog_usage;
1.29 joris 146: ret = programs[i].prog_hdlr(cmd_argc, cmd_argv);
1.2 deraadt 147: break;
148: }
1.100 joris 149:
150: /* clean up temporary files */
151: cvs_worklist_run(&rcs_temp_files, cvs_worklist_unlink);
1.1 deraadt 152:
1.23 niallo 153: exit(ret);
1.76 ray 154: /* NOTREACHED */
1.1 deraadt 155: }
156:
157:
158: void
159: rcs_usage(void)
160: {
161: fprintf(stderr,
1.66 jmc 162: "usage: rcs [-ehIiLMqTUV] [-Aoldfile] [-ausers] [-b[rev]]\n"
1.92 ray 163: " [-cstring] [-e[users]] [-kmode] [-l[rev]] [-mrev:msg]\n"
1.101 joris 164: " [-orange] [-sstate[:rev]] [-tfile|str] [-u[rev]]\n"
1.67 xsa 165: " [-xsuffixes] file ...\n");
1.1 deraadt 166: }
167:
168: /*
169: * rcs_main()
170: *
171: * Handler for the `rcs' program.
172: * Returns 0 on success, or >0 on error.
173: */
174: int
175: rcs_main(int argc, char **argv)
176: {
1.112 xsa 177: int i, j, ch, flags, kflag, lkmode;
1.39 xsa 178: char fpath[MAXPATHLEN], ofpath[MAXPATHLEN];
1.56 joris 179: char *logstr, *logmsg, *nflag, *descfile;
1.101 joris 180: char *alist, *comment, *elist, *lrev, *urev, *orange;
1.1 deraadt 181: mode_t fmode;
1.39 xsa 182: RCSFILE *file, *oldfile;
1.24 joris 183: RCSNUM *logrev;
1.39 xsa 184: struct rcs_access *acp;
1.48 xsa 185: time_t rcs_mtime = -1;
1.1 deraadt 186:
1.95 xsa 187: kflag = RCS_KWEXP_ERR;
188: lkmode = -1;
1.89 niallo 189: fmode = S_IRUSR|S_IRGRP|S_IROTH;
1.58 niallo 190: flags = RCS_RDWR|RCS_PARSE_FULLY;
1.92 ray 191: lrev = urev = descfile = nflag = NULL;
1.101 joris 192: logstr = alist = comment = elist = orange = NULL;
1.111 xsa 193:
194: /* match GNU */
195: if (1 < argc && argv[1][0] != '-')
196: warnx("warning: No options were given; "
197: "this usage is obsolescent.");
1.1 deraadt 198:
1.51 xsa 199: while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) {
1.1 deraadt 200: switch (ch) {
201: case 'A':
1.112 xsa 202: if (rcs_statfile(rcs_optarg, ofpath,
203: sizeof(ofpath), flags) < 0)
1.39 xsa 204: exit(1);
1.56 joris 205: rcsflags |= CO_ACLAPPEND;
1.1 deraadt 206: break;
207: case 'a':
1.28 joris 208: alist = rcs_optarg;
1.1 deraadt 209: break;
210: case 'c':
1.28 joris 211: comment = rcs_optarg;
1.1 deraadt 212: break;
213: case 'e':
1.28 joris 214: elist = rcs_optarg;
1.87 ray 215: rcsflags |= RCSPROG_EFLAG;
1.1 deraadt 216: break;
217: case 'i':
218: flags |= RCS_CREATE;
219: break;
220: case 'k':
1.28 joris 221: kflag = rcs_kflag_get(rcs_optarg);
1.1 deraadt 222: if (RCS_KWEXP_INVAL(kflag)) {
1.110 xsa 223: warnx("invalid keyword substitution "
224: "mode `%s'", rcs_optarg);
1.1 deraadt 225: exit(1);
226: }
227: break;
228: case 'L':
229: if (lkmode == RCS_LOCK_LOOSE)
1.110 xsa 230: warnx("-U overriden by -L");
1.1 deraadt 231: lkmode = RCS_LOCK_STRICT;
232: break;
1.92 ray 233: case 'l':
234: /* XXX - Check with -u flag. */
235: lrev = rcs_optarg;
236: rcsflags |= RCSPROG_LFLAG;
237: break;
1.24 joris 238: case 'm':
1.53 joris 239: logstr = xstrdup(rcs_optarg);
1.24 joris 240: break;
1.1 deraadt 241: case 'M':
242: /* ignore for the moment */
243: break;
1.56 joris 244: case 'n':
245: nflag = xstrdup(rcs_optarg);
246: break;
247: case 'N':
248: nflag = xstrdup(rcs_optarg);
1.87 ray 249: rcsflags |= RCSPROG_NFLAG;
1.56 joris 250: break;
1.101 joris 251: case 'o':
252: orange = xstrdup(rcs_optarg);
253: break;
1.12 joris 254: case 'q':
1.108 xsa 255: rcsflags |= QUIET;
1.46 xsa 256: break;
1.56 joris 257: case 't':
258: descfile = rcs_optarg;
1.113 ! ray 259: rcsflags |= DESCRIPTION;
1.56 joris 260: break;
1.46 xsa 261: case 'T':
1.56 joris 262: rcsflags |= PRESERVETIME;
1.12 joris 263: break;
1.1 deraadt 264: case 'U':
265: if (lkmode == RCS_LOCK_STRICT)
1.110 xsa 266: warnx("-L overriden by -U");
1.1 deraadt 267: lkmode = RCS_LOCK_LOOSE;
268: break;
1.92 ray 269: case 'u':
270: /* XXX - Check with -l flag. */
271: urev = rcs_optarg;
272: rcsflags |= RCSPROG_UFLAG;
273: break;
1.1 deraadt 274: case 'V':
275: printf("%s\n", rcs_version);
276: exit(0);
1.76 ray 277: /* NOTREACHED */
1.45 xsa 278: case 'x':
1.85 ray 279: /* Use blank extension if none given. */
280: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.51 xsa 281: break;
282: case 'z':
283: /*
284: * kept for compatibility
285: */
1.45 xsa 286: break;
1.1 deraadt 287: default:
1.2 deraadt 288: (usage)();
1.1 deraadt 289: exit(1);
290: }
291: }
292:
1.28 joris 293: argc -= rcs_optind;
294: argv += rcs_optind;
1.30 joris 295:
1.1 deraadt 296: if (argc == 0) {
1.110 xsa 297: warnx("no input file");
1.5 joris 298: (usage)();
1.1 deraadt 299: exit(1);
300: }
301:
302: for (i = 0; i < argc; i++) {
1.112 xsa 303: if (rcs_statfile(argv[i], fpath, sizeof(fpath), flags) < 0)
1.8 joris 304: continue;
1.6 joris 305:
1.108 xsa 306: if (!(rcsflags & QUIET))
1.35 niallo 307: printf("RCS file: %s\n", fpath);
1.48 xsa 308:
1.49 niallo 309: if ((file = rcs_open(fpath, flags, fmode)) == NULL)
1.6 joris 310: continue;
1.1 deraadt 311:
1.113 ! ray 312: if (rcsflags & DESCRIPTION)
1.87 ray 313: rcs_set_description(file, descfile);
314: else if (flags & RCS_CREATE)
1.56 joris 315: rcs_set_description(file, NULL);
316:
317: if (rcsflags & PRESERVETIME)
1.48 xsa 318: rcs_mtime = rcs_get_mtime(file->rf_path);
319:
1.56 joris 320: if (nflag != NULL)
321: rcs_attach_symbol(file, nflag);
322:
1.24 joris 323: if (logstr != NULL) {
324: if ((logmsg = strchr(logstr, ':')) == NULL) {
1.110 xsa 325: warnx("missing log message");
1.24 joris 326: rcs_close(file);
327: continue;
328: }
329:
330: *logmsg++ = '\0';
331: if ((logrev = rcsnum_parse(logstr)) == NULL) {
1.110 xsa 332: warnx("'%s' bad revision number", logstr);
1.24 joris 333: rcs_close(file);
334: continue;
335: }
336:
337: if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
1.110 xsa 338: warnx("failed to set logmsg for '%s' to '%s'",
1.24 joris 339: logstr, logmsg);
340: rcs_close(file);
1.25 joris 341: rcsnum_free(logrev);
1.24 joris 342: continue;
343: }
344:
345: rcsnum_free(logrev);
1.39 xsa 346: }
347:
348: /* entries to add from <oldfile> */
1.56 joris 349: if (rcsflags & CO_ACLAPPEND) {
1.39 xsa 350: /* XXX */
351: if ((oldfile = rcs_open(ofpath, RCS_READ)) == NULL)
352: exit(1);
353:
354: TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
355: rcs_access_add(file, acp->ra_name);
356:
357: rcs_close(oldfile);
1.24 joris 358: }
359:
1.1 deraadt 360: /* entries to add to the access list */
361: if (alist != NULL) {
1.86 pat 362: struct cvs_argvector *aargv;
1.1 deraadt 363:
1.79 xsa 364: aargv = cvs_strsplit(alist, ",");
1.86 pat 365: for (j = 0; aargv->argv[j] != NULL; j++)
366: rcs_access_add(file, aargv->argv[j]);
1.1 deraadt 367:
1.86 pat 368: cvs_argv_destroy(aargv);
1.1 deraadt 369: }
370:
371: if (comment != NULL)
372: rcs_comment_set(file, comment);
1.82 xsa 373:
374: if (elist != NULL) {
1.86 pat 375: struct cvs_argvector *eargv;
1.82 xsa 376:
377: eargv = cvs_strsplit(elist, ",");
1.86 pat 378: for (j = 0; eargv->argv[j] != NULL; j++)
379: rcs_access_remove(file, eargv->argv[j]);
1.82 xsa 380:
1.86 pat 381: cvs_argv_destroy(eargv);
1.87 ray 382: } else if (rcsflags & RCSPROG_EFLAG) {
1.82 xsa 383: struct rcs_access *rap;
384:
385: /* XXX rcs_access_remove(file, NULL); ?? */
386: while (!TAILQ_EMPTY(&(file->rf_access))) {
387: rap = TAILQ_FIRST(&(file->rf_access));
388: TAILQ_REMOVE(&(file->rf_access), rap, ra_list);
389: xfree(rap->ra_name);
390: xfree(rap);
391: }
392: /* not synced anymore */
393: file->rf_flags &= ~RCS_SYNCED;
394: }
1.1 deraadt 395:
1.95 xsa 396: rcs_kwexp_set(file, kflag);
1.1 deraadt 397:
398: if (lkmode != -1)
1.84 xsa 399: (void)rcs_lock_setmode(file, lkmode);
1.92 ray 400:
401: if (rcsflags & RCSPROG_LFLAG) {
402: RCSNUM *rev;
403: const char *username;
1.98 ray 404: char rev_str[16];
1.92 ray 405:
406: if ((username = getlogin()) == NULL)
407: fatal("could not get username");
408: if (lrev == NULL) {
409: rev = rcsnum_alloc();
410: rcsnum_cpy(file->rf_head, rev, 0);
411: } else if ((rev = rcsnum_parse(lrev)) == NULL) {
1.110 xsa 412: warnx("unable to unlock file");
1.92 ray 413: rcs_close(file);
414: continue;
415: }
1.98 ray 416: rcsnum_tostr(rev, rev_str, sizeof(rev_str));
1.92 ray 417: /* Make sure revision exists. */
418: if (rcs_findrev(file, rev) == NULL)
1.98 ray 419: fatal("%s: can't lock nonexisting revision %s",
420: fpath, rev_str);
421: if (rcs_lock_add(file, username, rev) != -1 &&
1.108 xsa 422: !(rcsflags & QUIET))
1.98 ray 423: printf("%s locked\n", rev_str);
1.92 ray 424: rcsnum_free(rev);
425: }
426:
427: if (rcsflags & RCSPROG_UFLAG) {
428: RCSNUM *rev;
429: const char *username;
1.98 ray 430: char rev_str[16];
1.92 ray 431:
432: if ((username = getlogin()) == NULL)
433: fatal("could not get username");
434: if (urev == NULL) {
435: rev = rcsnum_alloc();
436: rcsnum_cpy(file->rf_head, rev, 0);
437: } else if ((rev = rcsnum_parse(urev)) == NULL) {
1.110 xsa 438: warnx("unable to unlock file");
1.92 ray 439: rcs_close(file);
440: continue;
441: }
1.98 ray 442: rcsnum_tostr(rev, rev_str, sizeof(rev_str));
1.92 ray 443: /* Make sure revision exists. */
444: if (rcs_findrev(file, rev) == NULL)
1.98 ray 445: fatal("%s: can't unlock nonexisting revision %s",
446: fpath, rev_str);
447: if (rcs_lock_remove(file, username, rev) == -1 &&
1.108 xsa 448: !(rcsflags & QUIET))
1.110 xsa 449: warnx("%s: warning: No locks are set.", fpath);
1.92 ray 450: rcsnum_free(rev);
451: }
1.1 deraadt 452:
1.101 joris 453: if (orange != NULL) {
454: struct rcs_delta *rdp;
455: char b[16];
456:
457: rcs_rev_select(file, orange);
458: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
459: /*
460: * Delete selected revisions.
461: */
462: if (rdp->rd_flags & RCS_RD_SELECT) {
463: rcsnum_tostr(rdp->rd_num, b, sizeof(b));
464: printf("deleting revision %s\n", b);
465: (void)rcs_rev_remove(file, rdp->rd_num);
466: }
467: }
468: }
469:
1.1 deraadt 470: rcs_close(file);
1.48 xsa 471:
1.56 joris 472: if (rcsflags & PRESERVETIME)
1.48 xsa 473: rcs_set_mtime(fpath, rcs_mtime);
1.9 joris 474:
1.108 xsa 475: if (!(rcsflags & QUIET))
1.12 joris 476: printf("done\n");
1.1 deraadt 477: }
1.24 joris 478:
479: if (logstr != NULL)
1.53 joris 480: xfree(logstr);
1.1 deraadt 481:
1.56 joris 482: if (nflag != NULL)
483: xfree(nflag);
1.105 xsa 484:
485: if (orange != NULL)
486: xfree(orange);
1.56 joris 487:
1.1 deraadt 488: return (0);
1.56 joris 489: }
490:
491: static void
492: rcs_attach_symbol(RCSFILE *file, const char *symname)
493: {
494: char *rnum;
495: RCSNUM *rev;
496: char rbuf[16];
497: int rm;
498:
499: rm = 0;
500: rev = NULL;
501: if ((rnum = strrchr(symname, ':')) != NULL) {
502: if (rnum[1] == '\0')
503: rev = file->rf_head;
504: *(rnum++) = '\0';
505: } else {
506: rm = 1;
507: }
508:
509: if (rev == NULL && rm != 1) {
510: if ((rev = rcsnum_parse(rnum)) == NULL)
511: fatal("bad revision %s", rnum);
512: }
513:
1.87 ray 514: if (rcsflags & RCSPROG_NFLAG)
1.56 joris 515: rm = 1;
516:
517: if (rm == 1) {
518: if (rcs_sym_remove(file, symname) < 0) {
1.102 deraadt 519: if (rcs_errno == RCS_ERR_NOENT &&
1.87 ray 520: !(rcsflags & RCSPROG_NFLAG))
1.110 xsa 521: warnx("can't delete nonexisting symbol %s",
522: symname);
1.56 joris 523: } else {
1.87 ray 524: if (rcsflags & RCSPROG_NFLAG)
1.56 joris 525: rm = 0;
526: }
527: }
528:
529: if (rm == 0) {
1.102 deraadt 530: if (rcs_sym_add(file, symname, rev) < 0 &&
531: rcs_errno == RCS_ERR_DUPENT) {
1.56 joris 532: rcsnum_tostr(rcs_sym_getrev(file, symname),
533: rbuf, sizeof(rbuf));
534: fatal("symbolic name %s already bound to %s",
535: symname, rbuf);
536: }
537: }
1.1 deraadt 538: }