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