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