Annotation of src/usr.bin/rcs/rcsprog.c, Revision 1.91
1.91 ! ray 1: /* $OpenBSD: rcsprog.c,v 1.90 2006/03/28 09:51:09 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.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.85 ray 32: #define RCSPROG_OPTSTRING "A:a:b::c:e::hik:Lm:Mn:N:qt::TUVx::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.67 xsa 424: " [-cstring] [-e[users]] [-kmode] [-mrev:msg]\n"
1.66 jmc 425: " [-orev] [-sstate[:rev]] [-tfile|str]\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.79 xsa 441: char *alist, *comment, *elist;
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.56 joris 451: 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.24 joris 492: case 'm':
1.53 joris 493: logstr = xstrdup(rcs_optarg);
1.24 joris 494: break;
1.1 deraadt 495: case 'M':
496: /* ignore for the moment */
497: break;
1.56 joris 498: case 'n':
499: nflag = xstrdup(rcs_optarg);
500: break;
501: case 'N':
502: nflag = xstrdup(rcs_optarg);
1.87 ray 503: rcsflags |= RCSPROG_NFLAG;
1.56 joris 504: break;
1.12 joris 505: case 'q':
506: verbose = 0;
1.46 xsa 507: break;
1.56 joris 508: case 't':
509: descfile = rcs_optarg;
1.87 ray 510: rcsflags |= RCSPROG_TFLAG;
1.56 joris 511: break;
1.46 xsa 512: case 'T':
1.56 joris 513: rcsflags |= PRESERVETIME;
1.12 joris 514: break;
1.1 deraadt 515: case 'U':
516: if (lkmode == RCS_LOCK_STRICT)
517: cvs_log(LP_WARN, "-L overriden by -U");
518: lkmode = RCS_LOCK_LOOSE;
519: break;
520: case 'V':
521: printf("%s\n", rcs_version);
522: exit(0);
1.76 ray 523: /* NOTREACHED */
1.45 xsa 524: case 'x':
1.85 ray 525: /* Use blank extension if none given. */
526: rcs_suffixes = rcs_optarg ? rcs_optarg : "";
1.51 xsa 527: break;
528: case 'z':
529: /*
530: * kept for compatibility
531: */
1.45 xsa 532: break;
1.1 deraadt 533: default:
1.2 deraadt 534: (usage)();
1.1 deraadt 535: exit(1);
536: }
537: }
538:
1.28 joris 539: argc -= rcs_optind;
540: argv += rcs_optind;
1.30 joris 541:
1.1 deraadt 542: if (argc == 0) {
543: cvs_log(LP_ERR, "no input file");
1.5 joris 544: (usage)();
1.1 deraadt 545: exit(1);
546: }
547:
548: for (i = 0; i < argc; i++) {
1.9 joris 549: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
1.8 joris 550: continue;
1.6 joris 551:
1.35 niallo 552: if (verbose == 1)
553: printf("RCS file: %s\n", fpath);
1.48 xsa 554:
1.49 niallo 555: if ((file = rcs_open(fpath, flags, fmode)) == NULL)
1.6 joris 556: continue;
1.1 deraadt 557:
1.87 ray 558: if (rcsflags & RCSPROG_TFLAG)
559: rcs_set_description(file, descfile);
560: else if (flags & RCS_CREATE)
1.56 joris 561: rcs_set_description(file, NULL);
562:
563: if (rcsflags & PRESERVETIME)
1.48 xsa 564: rcs_mtime = rcs_get_mtime(file->rf_path);
565:
1.56 joris 566: if (nflag != NULL)
567: rcs_attach_symbol(file, nflag);
568:
1.24 joris 569: if (logstr != NULL) {
570: if ((logmsg = strchr(logstr, ':')) == NULL) {
571: cvs_log(LP_ERR, "missing log message");
572: rcs_close(file);
573: continue;
574: }
575:
576: *logmsg++ = '\0';
577: if ((logrev = rcsnum_parse(logstr)) == NULL) {
1.48 xsa 578: cvs_log(LP_ERR,
579: "'%s' bad revision number", logstr);
1.24 joris 580: rcs_close(file);
581: continue;
582: }
583:
584: if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
585: cvs_log(LP_ERR,
586: "failed to set logmsg for '%s' to '%s'",
587: logstr, logmsg);
588: rcs_close(file);
1.25 joris 589: rcsnum_free(logrev);
1.24 joris 590: continue;
591: }
592:
593: rcsnum_free(logrev);
1.39 xsa 594: }
595:
596: /* entries to add from <oldfile> */
1.56 joris 597: if (rcsflags & CO_ACLAPPEND) {
1.39 xsa 598: /* XXX */
599: if ((oldfile = rcs_open(ofpath, RCS_READ)) == NULL)
600: exit(1);
601:
602: TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
603: rcs_access_add(file, acp->ra_name);
604:
605: rcs_close(oldfile);
1.24 joris 606: }
607:
1.1 deraadt 608: /* entries to add to the access list */
609: if (alist != NULL) {
1.86 pat 610: struct cvs_argvector *aargv;
1.1 deraadt 611:
1.79 xsa 612: aargv = cvs_strsplit(alist, ",");
1.86 pat 613: for (j = 0; aargv->argv[j] != NULL; j++)
614: rcs_access_add(file, aargv->argv[j]);
1.1 deraadt 615:
1.86 pat 616: cvs_argv_destroy(aargv);
1.1 deraadt 617: }
618:
619: if (comment != NULL)
620: rcs_comment_set(file, comment);
1.82 xsa 621:
622: if (elist != NULL) {
1.86 pat 623: struct cvs_argvector *eargv;
1.82 xsa 624:
625: eargv = cvs_strsplit(elist, ",");
1.86 pat 626: for (j = 0; eargv->argv[j] != NULL; j++)
627: rcs_access_remove(file, eargv->argv[j]);
1.82 xsa 628:
1.86 pat 629: cvs_argv_destroy(eargv);
1.87 ray 630: } else if (rcsflags & RCSPROG_EFLAG) {
1.82 xsa 631: struct rcs_access *rap;
632:
633: /* XXX rcs_access_remove(file, NULL); ?? */
634: while (!TAILQ_EMPTY(&(file->rf_access))) {
635: rap = TAILQ_FIRST(&(file->rf_access));
636: TAILQ_REMOVE(&(file->rf_access), rap, ra_list);
637: xfree(rap->ra_name);
638: xfree(rap);
639: }
640: /* not synced anymore */
641: file->rf_flags &= ~RCS_SYNCED;
642: }
1.1 deraadt 643:
644: if (kflag != -1)
645: rcs_kwexp_set(file, kflag);
646:
647: if (lkmode != -1)
1.84 xsa 648: (void)rcs_lock_setmode(file, lkmode);
1.1 deraadt 649:
650: rcs_close(file);
1.48 xsa 651:
1.56 joris 652: if (rcsflags & PRESERVETIME)
1.48 xsa 653: rcs_set_mtime(fpath, rcs_mtime);
1.9 joris 654:
1.14 xsa 655: if (verbose == 1)
1.12 joris 656: printf("done\n");
1.1 deraadt 657: }
1.24 joris 658:
659: if (logstr != NULL)
1.53 joris 660: xfree(logstr);
1.1 deraadt 661:
1.56 joris 662: if (nflag != NULL)
663: xfree(nflag);
664:
1.1 deraadt 665: return (0);
1.56 joris 666: }
667:
668: static void
669: rcs_attach_symbol(RCSFILE *file, const char *symname)
670: {
671: char *rnum;
672: RCSNUM *rev;
673: char rbuf[16];
674: int rm;
675:
676: rm = 0;
677: rev = NULL;
678: if ((rnum = strrchr(symname, ':')) != NULL) {
679: if (rnum[1] == '\0')
680: rev = file->rf_head;
681: *(rnum++) = '\0';
682: } else {
683: rm = 1;
684: }
685:
686: if (rev == NULL && rm != 1) {
687: if ((rev = rcsnum_parse(rnum)) == NULL)
688: fatal("bad revision %s", rnum);
689: }
690:
1.87 ray 691: if (rcsflags & RCSPROG_NFLAG)
1.56 joris 692: rm = 1;
693:
694: if (rm == 1) {
695: if (rcs_sym_remove(file, symname) < 0) {
696: if ((rcs_errno == RCS_ERR_NOENT) &&
1.87 ray 697: !(rcsflags & RCSPROG_NFLAG))
1.56 joris 698: cvs_log(LP_WARN,
699: "can't delete nonexisting symbol %s", symname);
700: } else {
1.87 ray 701: if (rcsflags & RCSPROG_NFLAG)
1.56 joris 702: rm = 0;
703: }
704: }
705:
706: if (rm == 0) {
707: if ((rcs_sym_add(file, symname, rev) < 0) &&
708: (rcs_errno == RCS_ERR_DUPENT)) {
709: rcsnum_tostr(rcs_sym_getrev(file, symname),
710: rbuf, sizeof(rbuf));
711: fatal("symbolic name %s already bound to %s",
712: symname, rbuf);
713: }
714: }
715: }
716:
1.88 ray 717: /*
718: * Load description from <in> to <file>.
719: * If <in> starts with a `-', <in> is taken as the description.
720: * Otherwise <in> is the name of the file containing the description.
721: * If <in> is NULL, the description is read from stdin.
722: */
1.56 joris 723: static void
724: rcs_set_description(RCSFILE *file, const char *in)
725: {
726: BUF *bp;
727: char *content, buf[128];
728:
729: content = NULL;
1.88 ray 730: /* Description is in file <in>. */
731: if (in != NULL && *in != '-')
1.56 joris 732: bp = cvs_buf_load(in, BUF_AUTOEXT);
1.88 ray 733: /* Description is in <in>. */
734: else if (in != NULL) {
735: size_t len;
736: const char *desc;
737:
738: /* Skip leading `-'. */
739: desc = in + 1;
740: len = strlen(desc);
741:
742: bp = cvs_buf_alloc(len + 1, BUF_AUTOEXT);
743: cvs_buf_append(bp, desc, len);
744: /* Get description from stdin. */
1.56 joris 745: } else {
746: bp = cvs_buf_alloc(64, BUF_AUTOEXT);
747:
1.90 xsa 748: if (isatty(STDIN_FILENO))
749: (void)fprintf(stderr, "%s", DESC_PROMPT);
1.56 joris 750: for (;;) {
1.88 ray 751: /* XXX - fgetln() may be more elegant. */
1.56 joris 752: fgets(buf, sizeof(buf), stdin);
1.88 ray 753: if (feof(stdin) || ferror(stdin) ||
754: strcmp(buf, ".\n") == 0 ||
755: strcmp(buf, ".") == 0)
1.56 joris 756: break;
757: cvs_buf_append(bp, buf, strlen(buf));
1.90 xsa 758: if (isatty(STDIN_FILENO))
759: (void)fprintf(stderr, ">> ");
1.56 joris 760: }
761: }
762:
763: cvs_buf_putc(bp, '\0');
764: content = cvs_buf_release(bp);
765:
766: rcs_desc_set(file, content);
1.70 joris 767: xfree(content);
1.1 deraadt 768: }