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