Annotation of src/usr.bin/rcs/rcsprog.c, Revision 1.44
1.44 ! niallo 1: /* $OpenBSD: rcsprog.c,v 1.43 2005/11/21 11:17:19 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.6 joris 27: #include <sys/param.h>
1.1 deraadt 28: #include <sys/wait.h>
1.8 joris 29: #include <sys/stat.h>
1.1 deraadt 30:
1.3 joris 31: #include <ctype.h>
1.1 deraadt 32: #include <err.h>
1.3 joris 33: #include <errno.h>
1.1 deraadt 34: #include <pwd.h>
1.3 joris 35: #include <string.h>
1.1 deraadt 36: #include <stdio.h>
37: #include <stdlib.h>
38: #include <unistd.h>
39:
40: #include "log.h"
41: #include "rcs.h"
1.9 joris 42: #include "rcsprog.h"
1.1 deraadt 43:
1.29 joris 44: #define RCS_CMD_MAXARG 128
1.42 xsa 45: #define RCS_DEFAULT_SUFFIX ",v/"
1.29 joris 46:
1.1 deraadt 47: const char rcs_version[] = "OpenCVS RCS version 3.6";
1.12 joris 48: int verbose = 1;
1.34 joris 49: int pipeout = 0;
1.1 deraadt 50:
1.36 xsa 51: int rcs_optind;
1.28 joris 52: char *rcs_optarg;
1.42 xsa 53: char *rcs_suffixes;
1.36 xsa 54: char *rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.28 joris 55:
1.1 deraadt 56: struct rcs_prog {
1.26 deraadt 57: char *prog_name;
58: int (*prog_hdlr)(int, char **);
59: void (*prog_usage)(void);
1.1 deraadt 60: } programs[] = {
1.2 deraadt 61: { "rcs", rcs_main, rcs_usage },
1.17 joris 62: { "ci", checkin_main, checkin_usage },
1.11 joris 63: { "co", checkout_main, checkout_usage },
1.20 joris 64: { "rcsclean", rcsclean_main, rcsclean_usage },
1.18 joris 65: { "rcsdiff", rcsdiff_main, rcsdiff_usage },
1.33 xsa 66: { "rcsmerge", rcsmerge_main, rcsmerge_usage },
1.21 joris 67: { "rlog", rlog_main, rlog_usage },
1.22 joris 68: { "ident", ident_main, ident_usage },
1.1 deraadt 69: };
1.31 joris 70:
71: void
72: rcs_set_rev(const char *str, RCSNUM **rev)
73: {
1.32 joris 74: if (str == NULL)
75: return;
76:
1.31 joris 77: if (*rev != RCS_HEAD_REV)
78: cvs_log(LP_WARN, "redefinition of revision number");
79:
80: if ((*rev = rcsnum_parse(str)) == NULL) {
81: cvs_log(LP_ERR, "bad revision number '%s'", str);
82: exit (1);
83: }
84: }
1.1 deraadt 85:
1.9 joris 86: int
1.29 joris 87: rcs_init(char *envstr, char **argv, int argvlen)
88: {
89: u_int i;
90: int argc, error;
91: char linebuf[256], *lp, *cp;
92:
93: strlcpy(linebuf, envstr, sizeof(linebuf));
94: memset(argv, 0, argvlen * sizeof(char *));
95:
96: error = argc = 0;
97: for (lp = linebuf; lp != NULL;) {
98: cp = strsep(&lp, " \t\b\f\n\r\t\v");;
99: if (cp == NULL)
100: break;
101: else if (*cp == '\0')
102: continue;
103:
104: if (argc == argvlen) {
105: error++;
106: break;
107: }
108:
109: argv[argc] = strdup(cp);
110: if (argv[argc] == NULL) {
111: cvs_log(LP_ERRNO, "failed to copy argument");
112: error++;
113: break;
114: }
115:
116: argc++;
117: }
118:
119: if (error != 0) {
120: for (i = 0; i < (u_int)argc; i++)
121: free(argv[i]);
122: argc = -1;
123: }
124:
125: return (argc);
126: }
127:
128: int
1.28 joris 129: rcs_getopt(int argc, char **argv, const char *optstr)
130: {
131: char *a;
132: const char *c;
133: static int i = 1;
134: int opt, hasargument, ret;
135:
136: hasargument = 0;
137: rcs_optarg = NULL;
138:
139: if (i >= argc)
140: return (-1);
141:
142: a = argv[i++];
143: if (*a++ != '-')
144: return (-1);
145:
146: ret = 0;
147: opt = *a;
148: for (c = optstr; *c != '\0'; c++) {
149: if (*c == opt) {
150: a++;
151: ret = opt;
152:
153: if (*(c + 1) == ':') {
154: if (*(c + 2) == ':') {
155: if (*a != '\0')
156: hasargument = 1;
157: } else {
158: if (*a != '\0') {
159: hasargument = 1;
160: } else {
161: ret = 1;
162: break;
163: }
164: }
165: }
166:
167: if (hasargument == 1)
168: rcs_optarg = a;
169:
170: if (ret == opt)
171: rcs_optind++;
172: break;
173: }
174: }
175:
176: if (ret == 0)
177: cvs_log(LP_ERR, "unknown option -%c", opt);
178: else if (ret == 1)
179: cvs_log(LP_ERR, "missing argument for option -%c", opt);
180:
181: return (ret);
182: }
183:
184: int
1.9 joris 185: rcs_statfile(char *fname, char *out, size_t len)
186: {
1.42 xsa 187: int l, found, strdir;
188: char defaultsuffix[] = RCS_DEFAULT_SUFFIX;
1.9 joris 189: char filev[MAXPATHLEN], fpath[MAXPATHLEN];
1.42 xsa 190: char *ext, *slash;
1.9 joris 191: struct stat st;
192:
1.42 xsa 193: strdir = found = 0;
194:
195: /* we might have gotten a RCS file as argument */
196: if ((ext = strchr(fname, ',')) != NULL)
197: *ext = '\0';
198:
199: /* we might have gotten the RCS/ dir in the argument string */
200: if (strstr(fname, RCSDIR) != NULL)
201: strdir = 1;
202:
203: if (rcs_suffixes != NULL)
204: ext = rcs_suffixes;
205: else
206: ext = defaultsuffix;
1.43 xsa 207:
1.42 xsa 208: for (;;) {
209: /*
210: * GNU documentation says -x,v/ specifies two suffixes,
211: * namely the ,v one and an empty one (which matches
212: * everything).
213: * The problem is that they don't follow this rule at
214: * all, and their documentation seems flawed.
215: * We try to be compatible, so let's do so.
216: */
217: if (*ext == '\0')
218: break;
219:
220: if ((slash = strchr(ext, '/')) != NULL)
221: *slash = '\0';
1.9 joris 222:
1.42 xsa 223: l = snprintf(filev, sizeof(filev), "%s%s", fname, ext);
1.43 xsa 224: if (l == -1 || l >= (int)sizeof(filev)) {
225: errno = ENAMETOOLONG;
226: cvs_log(LP_ERRNO, "%s", filev);
1.9 joris 227: return (-1);
1.43 xsa 228: }
1.42 xsa 229:
230: if ((strdir == 0) &&
231: (stat(RCSDIR, &st) != -1) && (st.st_mode & S_IFDIR)) {
232: l = snprintf(fpath, sizeof(fpath), "%s/%s",
233: RCSDIR, filev);
1.43 xsa 234: if (l == -1 || l >= (int)sizeof(fpath)) {
235: errno = ENAMETOOLONG;
236: cvs_log(LP_ERRNO, "%s", fpath);
1.42 xsa 237: return (-1);
1.43 xsa 238: }
1.42 xsa 239: } else {
240: strlcpy(fpath, filev, sizeof(fpath));
241: }
242:
243: if (stat(fpath, &st) != -1) {
244: found++;
245: break;
246: }
247:
248: if (slash == NULL)
249: break;
250:
251: *slash++ = '/';
252: ext = slash;
1.9 joris 253: }
254:
1.42 xsa 255: if (found != 1) {
1.44 ! niallo 256: if ((strcmp(__progname, "rcsclean") != 0)
! 257: && (strcmp(__progname, "ci") != 0))
1.20 joris 258: cvs_log(LP_ERRNO, "%s", fpath);
1.9 joris 259: return (-1);
260: }
261:
262: strlcpy(out, fpath, len);
263:
264: return (0);
265: }
1.1 deraadt 266:
267: int
268: main(int argc, char **argv)
269: {
270: u_int i;
1.29 joris 271: char *rcsinit, *cmd_argv[RCS_CMD_MAXARG];
272: int ret, cmd_argc;
1.1 deraadt 273:
274: ret = -1;
1.28 joris 275: rcs_optind = 1;
1.9 joris 276: cvs_log_init(LD_STD, 0);
1.1 deraadt 277:
1.29 joris 278: cmd_argc = 0;
1.30 joris 279: cmd_argv[cmd_argc++] = argv[0];
1.29 joris 280: if ((rcsinit = getenv("RCSINIT")) != NULL) {
281: ret = rcs_init(rcsinit, cmd_argv + 1,
282: RCS_CMD_MAXARG - 1);
283: if (ret < 0) {
284: cvs_log(LP_ERRNO, "failed to prepend RCSINIT options");
285: exit (1);
286: }
287:
288: cmd_argc += ret;
289: }
1.36 xsa 290:
291: if ((rcs_tmpdir = getenv("TMPDIR")) == NULL)
292: rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.29 joris 293:
294: for (ret = 1; ret < argc; ret++)
295: cmd_argv[cmd_argc++] = argv[ret];
296:
1.1 deraadt 297: for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++)
1.2 deraadt 298: if (strcmp(__progname, programs[i].prog_name) == 0) {
299: usage = programs[i].prog_usage;
1.29 joris 300: ret = programs[i].prog_hdlr(cmd_argc, cmd_argv);
1.2 deraadt 301: break;
302: }
1.1 deraadt 303:
1.23 niallo 304: exit(ret);
1.1 deraadt 305: }
306:
307:
308: void
309: rcs_usage(void)
310: {
311: fprintf(stderr,
1.39 xsa 312: "usage: rcs [-hiLMUV] [-Aoldfile] [-ausers] [-b[rev]] [-cstring]\n"
1.38 xsa 313: " [-eusers] [-kmode] [-mrev:log] file ...\n");
1.1 deraadt 314: }
315:
316: /*
317: * rcs_main()
318: *
319: * Handler for the `rcs' program.
320: * Returns 0 on success, or >0 on error.
321: */
322: int
323: rcs_main(int argc, char **argv)
324: {
325: int i, ch, flags, kflag, lkmode;
1.39 xsa 326: char fpath[MAXPATHLEN], ofpath[MAXPATHLEN];
1.24 joris 327: char *logstr, *logmsg;
1.39 xsa 328: char *alist, *comment, *elist, *unp, *sp;
1.1 deraadt 329: mode_t fmode;
1.39 xsa 330: RCSFILE *file, *oldfile;
1.24 joris 331: RCSNUM *logrev;
1.39 xsa 332: struct rcs_access *acp;
1.1 deraadt 333:
334: kflag = lkmode = -1;
335: fmode = 0;
336: flags = RCS_RDWR;
1.39 xsa 337: logstr = alist = comment = elist = NULL;
1.1 deraadt 338:
1.28 joris 339: while ((ch = rcs_getopt(argc, argv, "A:a:b::c:e::hik:Lm:MqUV")) != -1) {
1.1 deraadt 340: switch (ch) {
341: case 'A':
1.39 xsa 342: if (rcs_statfile(rcs_optarg, ofpath, sizeof(ofpath)) < 0)
343: exit(1);
1.41 niallo 344: flags |= CO_ACLAPPEND;
1.1 deraadt 345: break;
346: case 'a':
1.28 joris 347: alist = rcs_optarg;
1.1 deraadt 348: break;
349: case 'c':
1.28 joris 350: comment = rcs_optarg;
1.1 deraadt 351: break;
352: case 'e':
1.28 joris 353: elist = rcs_optarg;
1.1 deraadt 354: break;
355: case 'h':
1.2 deraadt 356: (usage)();
1.1 deraadt 357: exit(0);
358: case 'i':
359: flags |= RCS_CREATE;
360: break;
361: case 'k':
1.28 joris 362: kflag = rcs_kflag_get(rcs_optarg);
1.1 deraadt 363: if (RCS_KWEXP_INVAL(kflag)) {
364: cvs_log(LP_ERR,
365: "invalid keyword substitution mode `%s'",
1.28 joris 366: rcs_optarg);
1.1 deraadt 367: exit(1);
368: }
369: break;
370: case 'L':
371: if (lkmode == RCS_LOCK_LOOSE)
372: cvs_log(LP_WARN, "-U overriden by -L");
373: lkmode = RCS_LOCK_STRICT;
374: break;
1.24 joris 375: case 'm':
1.28 joris 376: if ((logstr = strdup(rcs_optarg)) == NULL) {
1.24 joris 377: cvs_log(LP_ERRNO, "failed to copy logstring");
378: exit(1);
379: }
380: break;
1.1 deraadt 381: case 'M':
382: /* ignore for the moment */
383: break;
1.12 joris 384: case 'q':
385: verbose = 0;
386: break;
1.1 deraadt 387: case 'U':
388: if (lkmode == RCS_LOCK_STRICT)
389: cvs_log(LP_WARN, "-L overriden by -U");
390: lkmode = RCS_LOCK_LOOSE;
391: break;
392: case 'V':
393: printf("%s\n", rcs_version);
394: exit(0);
395: default:
1.2 deraadt 396: (usage)();
1.1 deraadt 397: exit(1);
398: }
399: }
400:
1.28 joris 401: argc -= rcs_optind;
402: argv += rcs_optind;
1.30 joris 403:
1.1 deraadt 404: if (argc == 0) {
405: cvs_log(LP_ERR, "no input file");
1.5 joris 406: (usage)();
1.1 deraadt 407: exit(1);
408: }
409:
410: for (i = 0; i < argc; i++) {
1.9 joris 411: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
1.8 joris 412: continue;
1.6 joris 413:
1.35 niallo 414: if (verbose == 1)
415: printf("RCS file: %s\n", fpath);
1.6 joris 416: file = rcs_open(fpath, flags, fmode);
417: if (file == NULL)
418: continue;
1.1 deraadt 419:
1.24 joris 420: if (logstr != NULL) {
421: if ((logmsg = strchr(logstr, ':')) == NULL) {
422: cvs_log(LP_ERR, "missing log message");
423: rcs_close(file);
424: continue;
425: }
426:
427: *logmsg++ = '\0';
428: if ((logrev = rcsnum_parse(logstr)) == NULL) {
429: cvs_log(LP_ERR, "'%s' bad revision number", logstr);
430: rcs_close(file);
431: continue;
432: }
433:
434: if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
435: cvs_log(LP_ERR,
436: "failed to set logmsg for '%s' to '%s'",
437: logstr, logmsg);
438: rcs_close(file);
1.25 joris 439: rcsnum_free(logrev);
1.24 joris 440: continue;
441: }
442:
443: rcsnum_free(logrev);
1.39 xsa 444: }
445:
446: /* entries to add from <oldfile> */
1.41 niallo 447: if (flags & CO_ACLAPPEND) {
1.39 xsa 448: /* XXX */
449: if ((oldfile = rcs_open(ofpath, RCS_READ)) == NULL)
450: exit(1);
451:
452: TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
453: rcs_access_add(file, acp->ra_name);
454:
455: rcs_close(oldfile);
1.24 joris 456: }
457:
1.1 deraadt 458: /* entries to add to the access list */
459: if (alist != NULL) {
460: unp = alist;
461: do {
462: sp = strchr(unp, ',');
463: if (sp != NULL)
464: *(sp++) = '\0';
465:
466: rcs_access_add(file, unp);
467:
468: unp = sp;
469: } while (sp != NULL);
470: }
471:
472: if (comment != NULL)
473: rcs_comment_set(file, comment);
474:
475: if (kflag != -1)
476: rcs_kwexp_set(file, kflag);
477:
478: if (lkmode != -1)
479: rcs_lock_setmode(file, lkmode);
480:
481: rcs_close(file);
1.9 joris 482:
1.14 xsa 483: if (verbose == 1)
1.12 joris 484: printf("done\n");
1.1 deraadt 485: }
1.24 joris 486:
487: if (logstr != NULL)
488: free(logstr);
1.1 deraadt 489:
490: return (0);
491: }