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