Annotation of src/usr.bin/rcs/rcsprog.c, Revision 1.108
1.108 ! xsa 1: /* $OpenBSD: rcsprog.c,v 1.107 2006/04/18 03:35:57 ray 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.59 xsa 27: #include "includes.h"
1.1 deraadt 28:
1.9 joris 29: #include "rcsprog.h"
1.1 deraadt 30:
1.29 joris 31: #define RCS_CMD_MAXARG 128
1.106 ray 32: #define RCSPROG_OPTSTRING "A:a:b::c:e::ik:Ll::m:Mn:N:o:qt::TUu::Vx::z::"
1.56 joris 33:
34: #define DESC_PROMPT "enter description, terminated with single '.' " \
1.107 ray 35: "or end of file:\nNOTE: This is NOT the log message!\n"
1.29 joris 36:
1.1 deraadt 37: const char rcs_version[] = "OpenCVS RCS version 3.6";
1.34 joris 38: int pipeout = 0;
1.1 deraadt 39:
1.87 ray 40: int flags;
1.91 ray 41: int rcsflags;
1.36 xsa 42: int rcs_optind;
1.28 joris 43: char *rcs_optarg;
1.42 xsa 44: char *rcs_suffixes;
1.36 xsa 45: char *rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.28 joris 46:
1.1 deraadt 47: struct rcs_prog {
1.26 deraadt 48: char *prog_name;
49: int (*prog_hdlr)(int, char **);
50: void (*prog_usage)(void);
1.1 deraadt 51: } programs[] = {
1.2 deraadt 52: { "rcs", rcs_main, rcs_usage },
1.17 joris 53: { "ci", checkin_main, checkin_usage },
1.11 joris 54: { "co", checkout_main, checkout_usage },
1.20 joris 55: { "rcsclean", rcsclean_main, rcsclean_usage },
1.18 joris 56: { "rcsdiff", rcsdiff_main, rcsdiff_usage },
1.33 xsa 57: { "rcsmerge", rcsmerge_main, rcsmerge_usage },
1.21 joris 58: { "rlog", rlog_main, rlog_usage },
1.22 joris 59: { "ident", ident_main, ident_usage },
1.1 deraadt 60: };
1.31 joris 61:
1.68 joris 62: struct cvs_wklhead rcs_temp_files;
63:
64: void sighdlr(int);
1.101 joris 65: static void rcs_set_description(RCSFILE *, const char *);
66: static void rcs_attach_symbol(RCSFILE *, const char *);
1.56 joris 67:
1.78 ray 68: /* ARGSUSED */
1.31 joris 69: void
1.68 joris 70: sighdlr(int sig)
71: {
72: cvs_worklist_clean(&rcs_temp_files, cvs_worklist_unlink);
73: _exit(1);
74: }
75:
1.97 ray 76: /*
77: * Allocate an RCSNUM and store in <rev>.
78: */
1.68 joris 79: void
1.31 joris 80: rcs_set_rev(const char *str, RCSNUM **rev)
81: {
1.97 ray 82: if (str == NULL || (*rev = rcsnum_parse(str)) == NULL)
1.55 xsa 83: fatal("bad revision number '%s'", str);
1.47 xsa 84: }
85:
86: /*
87: * rcs_get_mtime()
88: *
89: * Get <filename> last modified time.
90: * Returns last modified time on success, or -1 on failure.
91: */
92: time_t
93: rcs_get_mtime(const char *filename)
94: {
95: struct stat st;
96: time_t mtime;
97:
98: if (stat(filename, &st) == -1) {
99: cvs_log(LP_ERRNO, "failed to stat `%s'", filename);
100: return (-1);
101: }
102: mtime = (time_t)st.st_mtimespec.tv_sec;
103:
104: return (mtime);
105: }
106:
107: /*
108: * rcs_set_mtime()
109: *
110: * Set <filename> last modified time to <mtime> if it's not set to -1.
111: */
1.94 xsa 112: void
1.47 xsa 113: rcs_set_mtime(const char *filename, time_t mtime)
114: {
115: static struct timeval tv[2];
116:
117: if (mtime == -1)
1.94 xsa 118: return;
1.47 xsa 119:
120: tv[0].tv_sec = mtime;
121: tv[1].tv_sec = tv[0].tv_sec;
122:
1.94 xsa 123: if (utimes(filename, tv) == -1)
124: fatal("error setting utimes: %s", strerror(errno));
1.31 joris 125: }
1.1 deraadt 126:
1.9 joris 127: int
1.29 joris 128: rcs_init(char *envstr, char **argv, int argvlen)
129: {
130: u_int i;
131: int argc, error;
132: char linebuf[256], *lp, *cp;
133:
134: strlcpy(linebuf, envstr, sizeof(linebuf));
135: memset(argv, 0, argvlen * sizeof(char *));
136:
137: error = argc = 0;
138: for (lp = linebuf; lp != NULL;) {
1.61 xsa 139: cp = strsep(&lp, " \t\b\f\n\r\t\v");
1.29 joris 140: if (cp == NULL)
141: break;
142: else if (*cp == '\0')
143: continue;
144:
145: if (argc == argvlen) {
146: error++;
147: break;
148: }
149:
1.53 joris 150: argv[argc] = xstrdup(cp);
1.29 joris 151: argc++;
152: }
153:
154: if (error != 0) {
155: for (i = 0; i < (u_int)argc; i++)
1.53 joris 156: xfree(argv[i]);
1.29 joris 157: argc = -1;
158: }
159:
160: return (argc);
161: }
162:
163: int
1.28 joris 164: rcs_getopt(int argc, char **argv, const char *optstr)
165: {
166: char *a;
167: const char *c;
168: static int i = 1;
169: int opt, hasargument, ret;
170:
171: hasargument = 0;
172: rcs_optarg = NULL;
173:
174: if (i >= argc)
175: return (-1);
176:
177: a = argv[i++];
178: if (*a++ != '-')
179: return (-1);
180:
181: ret = 0;
182: opt = *a;
183: for (c = optstr; *c != '\0'; c++) {
184: if (*c == opt) {
185: a++;
186: ret = opt;
187:
188: if (*(c + 1) == ':') {
189: if (*(c + 2) == ':') {
190: if (*a != '\0')
191: hasargument = 1;
192: } else {
193: if (*a != '\0') {
194: hasargument = 1;
195: } else {
196: ret = 1;
197: break;
198: }
199: }
200: }
201:
202: if (hasargument == 1)
203: rcs_optarg = a;
204:
205: if (ret == opt)
206: rcs_optind++;
207: break;
208: }
209: }
210:
211: if (ret == 0)
212: cvs_log(LP_ERR, "unknown option -%c", opt);
213: else if (ret == 1)
214: cvs_log(LP_ERR, "missing argument for option -%c", opt);
1.74 ray 215:
216: return (ret);
217: }
218:
219: /*
220: * rcs_choosefile()
221: *
222: * Given a relative filename, decide where the corresponding RCS file
223: * should be. Tries each extension until a file is found. If no file
224: * was found, returns a path with the first extension.
225: *
226: * Returns pointer to a char array on success, NULL on failure.
227: */
228: char *
229: rcs_choosefile(const char *filename)
230: {
231: struct stat sb;
1.103 joris 232: char *p, *ext, name[MAXPATHLEN], *next, *ptr, rcsdir[MAXPATHLEN],
1.74 ray 233: *ret, *suffixes, rcspath[MAXPATHLEN];
234:
235: /* If -x flag was not given, use default. */
236: if (rcs_suffixes == NULL)
237: rcs_suffixes = RCS_DEFAULT_SUFFIX;
238:
239: /*
240: * If `filename' contains a directory, `rcspath' contains that
241: * directory, including a trailing slash. Otherwise `rcspath'
242: * contains an empty string.
243: */
244: if (strlcpy(rcspath, filename, sizeof(rcspath)) >= sizeof(rcspath))
245: return (NULL);
246: /* If `/' is found, end string after `/'. */
247: if ((ptr = strrchr(rcspath, '/')) != NULL)
248: *(++ptr) = '\0';
249: else
250: rcspath[0] = '\0';
251:
252: /* Append RCS/ to `rcspath' if it exists. */
253: if (strlcpy(rcsdir, rcspath, sizeof(rcsdir)) >= sizeof(rcsdir) ||
254: strlcat(rcsdir, RCSDIR, sizeof(rcsdir)) >= sizeof(rcsdir))
255: return (NULL);
1.102 deraadt 256: if (stat(rcsdir, &sb) == 0 && (sb.st_mode & S_IFDIR))
1.74 ray 257: if (strlcpy(rcspath, rcsdir, sizeof(rcspath)) >= sizeof(rcspath) ||
258: strlcat(rcspath, "/", sizeof(rcspath)) >= sizeof(rcspath))
259: return (NULL);
260:
261: /* Name of file without path. */
262: if ((ptr = strrchr(filename, '/')) == NULL) {
263: if (strlcpy(name, filename, sizeof(name)) >= sizeof(name))
264: return (NULL);
265: } else {
266: /* Skip `/'. */
267: if (strlcpy(name, ptr + 1, sizeof(name)) >= sizeof(name))
268: return (NULL);
269: }
270:
271: /* Name of RCS file without an extension. */
272: if (strlcat(rcspath, name, sizeof(rcspath)) >= sizeof(rcspath))
273: return (NULL);
274:
275: /*
276: * If only the empty suffix was given, use existing rcspath.
277: * This ensures that there is at least one suffix for strsep().
278: */
279: if (strcmp(rcs_suffixes, "") == 0) {
1.77 ray 280: ret = xstrdup(rcspath);
1.74 ray 281: return (ret);
282: }
283:
284: /*
285: * Cycle through slash-separated `rcs_suffixes', appending each
286: * extension to `rcspath' and testing if the file exists. If it
287: * does, return that string. Otherwise return path with first
288: * extension.
289: */
1.77 ray 290: suffixes = xstrdup(rcs_suffixes);
1.74 ray 291: for (ret = NULL, next = suffixes; (ext = strsep(&next, "/")) != NULL;) {
292: char fpath[MAXPATHLEN];
293:
1.103 joris 294: if ((p = strrchr(rcspath, ',')) != NULL) {
295: if (!strcmp(p, ext)) {
296: if (stat(rcspath, &sb) == 0) {
297: ret = xstrdup(rcspath);
298: goto out;
299: }
300: }
301:
302: continue;
303: }
304:
1.74 ray 305: /* Construct RCS file path. */
306: if (strlcpy(fpath, rcspath, sizeof(fpath)) >= sizeof(fpath) ||
1.83 ray 307: strlcat(fpath, ext, sizeof(fpath)) >= sizeof(fpath))
308: goto out;
1.74 ray 309:
310: /* Don't use `filename' as RCS file. */
311: if (strcmp(fpath, filename) == 0)
312: continue;
313:
314: if (stat(fpath, &sb) == 0) {
1.77 ray 315: ret = xstrdup(fpath);
1.83 ray 316: goto out;
1.74 ray 317: }
318: }
319:
320: /*
1.83 ray 321: * `ret' is still NULL. No RCS file with any extension exists
1.74 ray 322: * so we use the first extension.
1.83 ray 323: *
324: * `suffixes' should now be NUL separated, so the first
325: * extension can be read just by reading `suffixes'.
1.74 ray 326: */
1.83 ray 327: if (strlcat(rcspath, suffixes, sizeof(rcspath)) >=
328: sizeof(rcspath))
329: goto out;
330: ret = xstrdup(rcspath);
1.28 joris 331:
1.83 ray 332: out:
333: /* `ret' may be NULL, which indicates an error. */
1.81 ray 334: xfree(suffixes);
1.28 joris 335: return (ret);
336: }
337:
1.75 ray 338: /*
339: * Find the name of an RCS file, given a file name `fname'. If an RCS
340: * file is found, the name is copied to the `len' sized buffer `out'.
341: * Returns 0 if RCS file was found, -1 otherwise.
342: */
1.28 joris 343: int
1.9 joris 344: rcs_statfile(char *fname, char *out, size_t len)
345: {
346: struct stat st;
1.75 ray 347: char *rcspath;
1.9 joris 348:
1.75 ray 349: if ((rcspath = rcs_choosefile(fname)) == NULL)
350: fatal("rcs_statfile: path truncation");
1.9 joris 351:
1.80 ray 352: /* Error out if file not found and we are not creating one. */
1.87 ray 353: if (stat(rcspath, &st) == -1 && !(flags & RCS_CREATE)) {
1.102 deraadt 354: if (strcmp(__progname, "rcsclean") != 0 &&
355: strcmp(__progname, "ci") != 0)
1.75 ray 356: cvs_log(LP_ERRNO, "%s", rcspath);
357: xfree(rcspath);
1.9 joris 358: return (-1);
359: }
360:
1.75 ray 361: if (strlcpy(out, rcspath, len) >= len)
1.57 xsa 362: fatal("rcs_statfile: path truncation");
1.75 ray 363:
364: xfree(rcspath);
1.9 joris 365:
366: return (0);
1.97 ray 367: }
368:
369: /*
370: * Set <str> to <new_str>. Print warning if <str> is redefined.
371: */
372: void
373: rcs_setrevstr(char **str, char *new_str)
374: {
375: if (new_str == NULL)
376: return;
377: if (*str != NULL)
378: cvs_log(LP_WARN, "redefinition of revision number");
379: *str = new_str;
380: }
381:
382: /*
383: * Set <str1> or <str2> to <new_str>, depending on which is not set.
384: * If both are set, error out.
385: */
386: void
387: rcs_setrevstr2(char **str1, char **str2, char *new_str)
388: {
389: if (new_str == NULL)
390: return;
391: if (*str1 == NULL)
392: *str1 = new_str;
393: else if (*str2 == NULL)
394: *str2 = new_str;
395: else
396: fatal("too many revision numbers");
1.99 ray 397: }
398:
399: /*
400: * Get revision from file. The revision can be specified as a symbol or
401: * a revision number.
402: */
403: RCSNUM *
404: rcs_getrevnum(const char *rev_str, RCSFILE *file)
405: {
406: RCSNUM *rev;
407:
408: /* Search for symbol. */
409: rev = rcs_sym_getrev(file, rev_str);
410:
411: /* Search for revision number. */
412: if (rev == NULL)
413: rev = rcsnum_parse(rev_str);
414:
415: return (rev);
1.9 joris 416: }
1.1 deraadt 417:
418: int
419: main(int argc, char **argv)
420: {
421: u_int i;
1.29 joris 422: char *rcsinit, *cmd_argv[RCS_CMD_MAXARG];
423: int ret, cmd_argc;
1.1 deraadt 424:
425: ret = -1;
1.28 joris 426: rcs_optind = 1;
1.9 joris 427: cvs_log_init(LD_STD, 0);
1.68 joris 428: SLIST_INIT(&rcs_temp_files);
1.1 deraadt 429:
1.29 joris 430: cmd_argc = 0;
1.30 joris 431: cmd_argv[cmd_argc++] = argv[0];
1.29 joris 432: if ((rcsinit = getenv("RCSINIT")) != NULL) {
433: ret = rcs_init(rcsinit, cmd_argv + 1,
434: RCS_CMD_MAXARG - 1);
435: if (ret < 0) {
436: cvs_log(LP_ERRNO, "failed to prepend RCSINIT options");
437: exit (1);
438: }
439:
440: cmd_argc += ret;
441: }
1.36 xsa 442:
443: if ((rcs_tmpdir = getenv("TMPDIR")) == NULL)
444: rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.29 joris 445:
446: for (ret = 1; ret < argc; ret++)
447: cmd_argv[cmd_argc++] = argv[ret];
1.68 joris 448:
449: signal(SIGHUP, sighdlr);
450: signal(SIGINT, sighdlr);
451: signal(SIGQUIT, sighdlr);
452: signal(SIGABRT, sighdlr);
453: signal(SIGALRM, sighdlr);
454: signal(SIGTERM, sighdlr);
1.29 joris 455:
1.1 deraadt 456: for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++)
1.2 deraadt 457: if (strcmp(__progname, programs[i].prog_name) == 0) {
458: usage = programs[i].prog_usage;
1.29 joris 459: ret = programs[i].prog_hdlr(cmd_argc, cmd_argv);
1.2 deraadt 460: break;
461: }
1.100 joris 462:
463: /* clean up temporary files */
464: cvs_worklist_run(&rcs_temp_files, cvs_worklist_unlink);
1.1 deraadt 465:
1.23 niallo 466: exit(ret);
1.76 ray 467: /* NOTREACHED */
1.1 deraadt 468: }
469:
470:
471: void
472: rcs_usage(void)
473: {
474: fprintf(stderr,
1.66 jmc 475: "usage: rcs [-ehIiLMqTUV] [-Aoldfile] [-ausers] [-b[rev]]\n"
1.92 ray 476: " [-cstring] [-e[users]] [-kmode] [-l[rev]] [-mrev:msg]\n"
1.101 joris 477: " [-orange] [-sstate[:rev]] [-tfile|str] [-u[rev]]\n"
1.67 xsa 478: " [-xsuffixes] file ...\n");
1.1 deraadt 479: }
480:
481: /*
482: * rcs_main()
483: *
484: * Handler for the `rcs' program.
485: * Returns 0 on success, or >0 on error.
486: */
487: int
488: rcs_main(int argc, char **argv)
489: {
1.87 ray 490: int i, j, ch, kflag, lkmode;
1.39 xsa 491: char fpath[MAXPATHLEN], ofpath[MAXPATHLEN];
1.56 joris 492: char *logstr, *logmsg, *nflag, *descfile;
1.101 joris 493: char *alist, *comment, *elist, *lrev, *urev, *orange;
1.1 deraadt 494: mode_t fmode;
1.39 xsa 495: RCSFILE *file, *oldfile;
1.24 joris 496: RCSNUM *logrev;
1.39 xsa 497: struct rcs_access *acp;
1.48 xsa 498: time_t rcs_mtime = -1;
1.1 deraadt 499:
1.95 xsa 500: kflag = RCS_KWEXP_ERR;
501: lkmode = -1;
1.89 niallo 502: fmode = S_IRUSR|S_IRGRP|S_IROTH;
1.58 niallo 503: flags = RCS_RDWR|RCS_PARSE_FULLY;
1.92 ray 504: lrev = urev = descfile = nflag = NULL;
1.101 joris 505: logstr = alist = comment = elist = orange = NULL;
1.1 deraadt 506:
1.51 xsa 507: while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) {
1.1 deraadt 508: switch (ch) {
509: case 'A':
1.39 xsa 510: if (rcs_statfile(rcs_optarg, ofpath, sizeof(ofpath)) < 0)
511: exit(1);
1.56 joris 512: rcsflags |= CO_ACLAPPEND;
1.1 deraadt 513: break;
514: case 'a':
1.28 joris 515: alist = rcs_optarg;
1.1 deraadt 516: break;
517: case 'c':
1.28 joris 518: comment = rcs_optarg;
1.1 deraadt 519: break;
520: case 'e':
1.28 joris 521: elist = rcs_optarg;
1.87 ray 522: rcsflags |= RCSPROG_EFLAG;
1.1 deraadt 523: break;
524: case 'i':
525: flags |= RCS_CREATE;
526: break;
527: case 'k':
1.28 joris 528: kflag = rcs_kflag_get(rcs_optarg);
1.1 deraadt 529: if (RCS_KWEXP_INVAL(kflag)) {
530: cvs_log(LP_ERR,
531: "invalid keyword substitution mode `%s'",
1.28 joris 532: rcs_optarg);
1.1 deraadt 533: exit(1);
534: }
535: break;
536: case 'L':
537: if (lkmode == RCS_LOCK_LOOSE)
538: cvs_log(LP_WARN, "-U overriden by -L");
539: lkmode = RCS_LOCK_STRICT;
540: break;
1.92 ray 541: case 'l':
542: /* XXX - Check with -u flag. */
543: lrev = rcs_optarg;
544: rcsflags |= RCSPROG_LFLAG;
545: break;
1.24 joris 546: case 'm':
1.53 joris 547: logstr = xstrdup(rcs_optarg);
1.24 joris 548: break;
1.1 deraadt 549: case 'M':
550: /* ignore for the moment */
551: break;
1.56 joris 552: case 'n':
553: nflag = xstrdup(rcs_optarg);
554: break;
555: case 'N':
556: nflag = xstrdup(rcs_optarg);
1.87 ray 557: rcsflags |= RCSPROG_NFLAG;
1.56 joris 558: break;
1.101 joris 559: case 'o':
560: orange = xstrdup(rcs_optarg);
561: break;
1.12 joris 562: case 'q':
1.108 ! xsa 563: rcsflags |= QUIET;
1.46 xsa 564: break;
1.56 joris 565: case 't':
566: descfile = rcs_optarg;
1.87 ray 567: rcsflags |= RCSPROG_TFLAG;
1.56 joris 568: break;
1.46 xsa 569: case 'T':
1.56 joris 570: rcsflags |= PRESERVETIME;
1.12 joris 571: break;
1.1 deraadt 572: case 'U':
573: if (lkmode == RCS_LOCK_STRICT)
574: cvs_log(LP_WARN, "-L overriden by -U");
575: lkmode = RCS_LOCK_LOOSE;
576: break;
1.92 ray 577: case 'u':
578: /* XXX - Check with -l flag. */
579: urev = rcs_optarg;
580: rcsflags |= RCSPROG_UFLAG;
581: break;
1.1 deraadt 582: case 'V':
583: printf("%s\n", rcs_version);
584: exit(0);
1.76 ray 585: /* NOTREACHED */
1.45 xsa 586: case 'x':
1.85 ray 587: /* Use blank extension if none given. */
588: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.51 xsa 589: break;
590: case 'z':
591: /*
592: * kept for compatibility
593: */
1.45 xsa 594: break;
1.1 deraadt 595: default:
1.2 deraadt 596: (usage)();
1.1 deraadt 597: exit(1);
598: }
599: }
600:
1.28 joris 601: argc -= rcs_optind;
602: argv += rcs_optind;
1.30 joris 603:
1.1 deraadt 604: if (argc == 0) {
605: cvs_log(LP_ERR, "no input file");
1.5 joris 606: (usage)();
1.1 deraadt 607: exit(1);
608: }
609:
610: for (i = 0; i < argc; i++) {
1.9 joris 611: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
1.8 joris 612: continue;
1.6 joris 613:
1.108 ! xsa 614: if (!(rcsflags & QUIET))
1.35 niallo 615: printf("RCS file: %s\n", fpath);
1.48 xsa 616:
1.49 niallo 617: if ((file = rcs_open(fpath, flags, fmode)) == NULL)
1.6 joris 618: continue;
1.1 deraadt 619:
1.87 ray 620: if (rcsflags & RCSPROG_TFLAG)
621: rcs_set_description(file, descfile);
622: else if (flags & RCS_CREATE)
1.56 joris 623: rcs_set_description(file, NULL);
624:
625: if (rcsflags & PRESERVETIME)
1.48 xsa 626: rcs_mtime = rcs_get_mtime(file->rf_path);
627:
1.56 joris 628: if (nflag != NULL)
629: rcs_attach_symbol(file, nflag);
630:
1.24 joris 631: if (logstr != NULL) {
632: if ((logmsg = strchr(logstr, ':')) == NULL) {
633: cvs_log(LP_ERR, "missing log message");
634: rcs_close(file);
635: continue;
636: }
637:
638: *logmsg++ = '\0';
639: if ((logrev = rcsnum_parse(logstr)) == NULL) {
1.48 xsa 640: cvs_log(LP_ERR,
641: "'%s' bad revision number", logstr);
1.24 joris 642: rcs_close(file);
643: continue;
644: }
645:
646: if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
647: cvs_log(LP_ERR,
648: "failed to set logmsg for '%s' to '%s'",
649: logstr, logmsg);
650: rcs_close(file);
1.25 joris 651: rcsnum_free(logrev);
1.24 joris 652: continue;
653: }
654:
655: rcsnum_free(logrev);
1.39 xsa 656: }
657:
658: /* entries to add from <oldfile> */
1.56 joris 659: if (rcsflags & CO_ACLAPPEND) {
1.39 xsa 660: /* XXX */
661: if ((oldfile = rcs_open(ofpath, RCS_READ)) == NULL)
662: exit(1);
663:
664: TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
665: rcs_access_add(file, acp->ra_name);
666:
667: rcs_close(oldfile);
1.24 joris 668: }
669:
1.1 deraadt 670: /* entries to add to the access list */
671: if (alist != NULL) {
1.86 pat 672: struct cvs_argvector *aargv;
1.1 deraadt 673:
1.79 xsa 674: aargv = cvs_strsplit(alist, ",");
1.86 pat 675: for (j = 0; aargv->argv[j] != NULL; j++)
676: rcs_access_add(file, aargv->argv[j]);
1.1 deraadt 677:
1.86 pat 678: cvs_argv_destroy(aargv);
1.1 deraadt 679: }
680:
681: if (comment != NULL)
682: rcs_comment_set(file, comment);
1.82 xsa 683:
684: if (elist != NULL) {
1.86 pat 685: struct cvs_argvector *eargv;
1.82 xsa 686:
687: eargv = cvs_strsplit(elist, ",");
1.86 pat 688: for (j = 0; eargv->argv[j] != NULL; j++)
689: rcs_access_remove(file, eargv->argv[j]);
1.82 xsa 690:
1.86 pat 691: cvs_argv_destroy(eargv);
1.87 ray 692: } else if (rcsflags & RCSPROG_EFLAG) {
1.82 xsa 693: struct rcs_access *rap;
694:
695: /* XXX rcs_access_remove(file, NULL); ?? */
696: while (!TAILQ_EMPTY(&(file->rf_access))) {
697: rap = TAILQ_FIRST(&(file->rf_access));
698: TAILQ_REMOVE(&(file->rf_access), rap, ra_list);
699: xfree(rap->ra_name);
700: xfree(rap);
701: }
702: /* not synced anymore */
703: file->rf_flags &= ~RCS_SYNCED;
704: }
1.1 deraadt 705:
1.95 xsa 706: rcs_kwexp_set(file, kflag);
1.1 deraadt 707:
708: if (lkmode != -1)
1.84 xsa 709: (void)rcs_lock_setmode(file, lkmode);
1.92 ray 710:
711: if (rcsflags & RCSPROG_LFLAG) {
712: RCSNUM *rev;
713: const char *username;
1.98 ray 714: char rev_str[16];
1.92 ray 715:
716: if ((username = getlogin()) == NULL)
717: fatal("could not get username");
718: if (lrev == NULL) {
719: rev = rcsnum_alloc();
720: rcsnum_cpy(file->rf_head, rev, 0);
721: } else if ((rev = rcsnum_parse(lrev)) == NULL) {
722: cvs_log(LP_ERR, "unable to unlock file");
723: rcs_close(file);
724: continue;
725: }
1.98 ray 726: rcsnum_tostr(rev, rev_str, sizeof(rev_str));
1.92 ray 727: /* Make sure revision exists. */
728: if (rcs_findrev(file, rev) == NULL)
1.98 ray 729: fatal("%s: can't lock nonexisting revision %s",
730: fpath, rev_str);
731: if (rcs_lock_add(file, username, rev) != -1 &&
1.108 ! xsa 732: !(rcsflags & QUIET))
1.98 ray 733: printf("%s locked\n", rev_str);
1.92 ray 734: rcsnum_free(rev);
735: }
736:
737: if (rcsflags & RCSPROG_UFLAG) {
738: RCSNUM *rev;
739: const char *username;
1.98 ray 740: char rev_str[16];
1.92 ray 741:
742: if ((username = getlogin()) == NULL)
743: fatal("could not get username");
744: if (urev == NULL) {
745: rev = rcsnum_alloc();
746: rcsnum_cpy(file->rf_head, rev, 0);
747: } else if ((rev = rcsnum_parse(urev)) == NULL) {
748: cvs_log(LP_ERR, "unable to unlock file");
749: rcs_close(file);
750: continue;
751: }
1.98 ray 752: rcsnum_tostr(rev, rev_str, sizeof(rev_str));
1.92 ray 753: /* Make sure revision exists. */
754: if (rcs_findrev(file, rev) == NULL)
1.98 ray 755: fatal("%s: can't unlock nonexisting revision %s",
756: fpath, rev_str);
757: if (rcs_lock_remove(file, username, rev) == -1 &&
1.108 ! xsa 758: !(rcsflags & QUIET))
1.98 ray 759: cvs_log(LP_ERR,
760: "%s: warning: No locks are set.", fpath);
1.92 ray 761: rcsnum_free(rev);
762: }
1.1 deraadt 763:
1.101 joris 764: if (orange != NULL) {
765: struct rcs_delta *rdp;
766: char b[16];
767:
768: rcs_rev_select(file, orange);
769: TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) {
770: /*
771: * Delete selected revisions.
772: */
773: if (rdp->rd_flags & RCS_RD_SELECT) {
774: rcsnum_tostr(rdp->rd_num, b, sizeof(b));
775: printf("deleting revision %s\n", b);
776: (void)rcs_rev_remove(file, rdp->rd_num);
777: }
778: }
779: }
780:
1.1 deraadt 781: rcs_close(file);
1.48 xsa 782:
1.56 joris 783: if (rcsflags & PRESERVETIME)
1.48 xsa 784: rcs_set_mtime(fpath, rcs_mtime);
1.9 joris 785:
1.108 ! xsa 786: if (!(rcsflags & QUIET))
1.12 joris 787: printf("done\n");
1.1 deraadt 788: }
1.24 joris 789:
790: if (logstr != NULL)
1.53 joris 791: xfree(logstr);
1.1 deraadt 792:
1.56 joris 793: if (nflag != NULL)
794: xfree(nflag);
1.105 xsa 795:
796: if (orange != NULL)
797: xfree(orange);
1.56 joris 798:
1.1 deraadt 799: return (0);
1.56 joris 800: }
801:
802: static void
803: rcs_attach_symbol(RCSFILE *file, const char *symname)
804: {
805: char *rnum;
806: RCSNUM *rev;
807: char rbuf[16];
808: int rm;
809:
810: rm = 0;
811: rev = NULL;
812: if ((rnum = strrchr(symname, ':')) != NULL) {
813: if (rnum[1] == '\0')
814: rev = file->rf_head;
815: *(rnum++) = '\0';
816: } else {
817: rm = 1;
818: }
819:
820: if (rev == NULL && rm != 1) {
821: if ((rev = rcsnum_parse(rnum)) == NULL)
822: fatal("bad revision %s", rnum);
823: }
824:
1.87 ray 825: if (rcsflags & RCSPROG_NFLAG)
1.56 joris 826: rm = 1;
827:
828: if (rm == 1) {
829: if (rcs_sym_remove(file, symname) < 0) {
1.102 deraadt 830: if (rcs_errno == RCS_ERR_NOENT &&
1.87 ray 831: !(rcsflags & RCSPROG_NFLAG))
1.56 joris 832: cvs_log(LP_WARN,
833: "can't delete nonexisting symbol %s", symname);
834: } else {
1.87 ray 835: if (rcsflags & RCSPROG_NFLAG)
1.56 joris 836: rm = 0;
837: }
838: }
839:
840: if (rm == 0) {
1.102 deraadt 841: if (rcs_sym_add(file, symname, rev) < 0 &&
842: rcs_errno == RCS_ERR_DUPENT) {
1.56 joris 843: rcsnum_tostr(rcs_sym_getrev(file, symname),
844: rbuf, sizeof(rbuf));
845: fatal("symbolic name %s already bound to %s",
846: symname, rbuf);
847: }
848: }
849: }
850:
1.88 ray 851: /*
852: * Load description from <in> to <file>.
853: * If <in> starts with a `-', <in> is taken as the description.
854: * Otherwise <in> is the name of the file containing the description.
855: * If <in> is NULL, the description is read from stdin.
856: */
1.56 joris 857: static void
858: rcs_set_description(RCSFILE *file, const char *in)
859: {
860: BUF *bp;
1.107 ray 861: char *content;
1.56 joris 862:
1.88 ray 863: /* Description is in file <in>. */
1.107 ray 864: if (in != NULL && *in != '-') {
1.56 joris 865: bp = cvs_buf_load(in, BUF_AUTOEXT);
1.107 ray 866: cvs_buf_putc(bp, '\0');
867: content = cvs_buf_release(bp);
1.88 ray 868: /* Description is in <in>. */
1.107 ray 869: } else if (in != NULL)
870: /* Skip leading `-'. */
871: content = xstrdup(in + 1);
872: /* Get description from stdin. */
873: else
874: content = rcs_prompt(DESC_PROMPT);
1.88 ray 875:
1.107 ray 876: rcs_desc_set(file, content);
877: xfree(content);
878: }
879:
880: /*
881: * Prompt for and store user's input in an allocated string.
882: *
883: * Returns the string's pointer.
884: */
885: char *
886: rcs_prompt(const char *prompt)
887: {
888: BUF *bp;
889: size_t len;
890: char *buf;
1.88 ray 891:
1.107 ray 892: bp = cvs_buf_alloc(0, BUF_AUTOEXT);
893: if (isatty(STDIN_FILENO))
894: (void)fprintf(stderr, "%s", prompt);
895: if (isatty(STDIN_FILENO))
896: (void)fprintf(stderr, ">> ");
897: while ((buf = fgetln(stdin, &len)) != NULL) {
898: /* The last line may not be EOL terminated. */
899: if (buf[0] == '.' && (len == 1 || buf[1] == '\n'))
900: break;
901: else
902: cvs_buf_append(bp, buf, len);
1.56 joris 903:
1.90 xsa 904: if (isatty(STDIN_FILENO))
1.107 ray 905: (void)fprintf(stderr, ">> ");
1.56 joris 906: }
907: cvs_buf_putc(bp, '\0');
908:
1.107 ray 909: return (cvs_buf_release(bp));
1.101 joris 910: }
911:
1.104 xsa 912: u_int
1.101 joris 913: rcs_rev_select(RCSFILE *file, char *range)
914: {
915: int i;
916: u_int nrev;
917: char *ep;
918: char *lstr, *rstr;
919: struct rcs_delta *rdp;
920: struct cvs_argvector *revargv, *revrange;
921: RCSNUM lnum, rnum;
922:
923: nrev = 0;
924: (void)memset(&lnum, 0, sizeof(lnum));
925: (void)memset(&rnum, 0, sizeof(rnum));
926:
927: if (range == NULL) {
928: TAILQ_FOREACH(rdp, &file->rf_delta, rd_list)
929: if (rcsnum_cmp(rdp->rd_num, file->rf_head, 0) == 0) {
930: rdp->rd_flags |= RCS_RD_SELECT;
931: return (1);
932: }
933: return (0);
934: }
935:
936: revargv = cvs_strsplit(range, ",");
937: for (i = 0; revargv->argv[i] != NULL; i++) {
938: revrange = cvs_strsplit(revargv->argv[i], ":");
939: if (revrange->argv[0] == NULL)
940: /* should not happen */
941: fatal("invalid revision range: %s", revargv->argv[i]);
942: else if (revrange->argv[1] == NULL)
943: lstr = rstr = revrange->argv[0];
944: else {
945: if (revrange->argv[2] != NULL)
946: fatal("invalid revision range: %s",
947: revargv->argv[i]);
948: lstr = revrange->argv[0];
949: rstr = revrange->argv[1];
950: if (strcmp(lstr, "") == 0)
951: lstr = NULL;
952: if (strcmp(rstr, "") == 0)
953: rstr = NULL;
954: }
955:
956: if (lstr == NULL)
957: lstr = RCS_HEAD_INIT;
958: if (rcsnum_aton(lstr, &ep, &lnum) == 0 || (*ep != '\0'))
959: fatal("invalid revision: %s", lstr);
960:
961: if (rstr != NULL) {
962: if (rcsnum_aton(rstr, &ep, &rnum) == 0 || (*ep != '\0'))
963: fatal("invalid revision: %s", rstr);
964: } else
965: rcsnum_cpy(file->rf_head, &rnum, 0);
966:
967: cvs_argv_destroy(revrange);
968:
969: TAILQ_FOREACH(rdp, &file->rf_delta, rd_list)
1.102 deraadt 970: if (rcsnum_cmp(rdp->rd_num, &lnum, 0) <= 0 &&
971: rcsnum_cmp(rdp->rd_num, &rnum, 0) >= 0 &&
1.101 joris 972: !(rdp->rd_flags & RCS_RD_SELECT)) {
973: rdp->rd_flags |= RCS_RD_SELECT;
974: nrev++;
975: }
976: }
977: cvs_argv_destroy(revargv);
978:
979: if (lnum.rn_id != NULL)
980: xfree(lnum.rn_id);
981: if (rnum.rn_id != NULL)
982: xfree(rnum.rn_id);
983:
984: return (nrev);
1.1 deraadt 985: }