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