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