Annotation of src/usr.bin/rcs/rcsprog.c, Revision 1.68
1.68 ! joris 1: /* $OpenBSD: rcsprog.c,v 1.67 2006/03/08 12:25:34 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.42 xsa 32: #define RCS_DEFAULT_SUFFIX ",v/"
1.56 joris 33: #define RCSPROG_OPTSTRING "A:a:b::c:e::hik:Lm:Mn:N:qt::TUVx:z:"
34:
35: #define DESC_PROMPT "enter description, terminated with single '.' " \
36: "or end of file:\nNOTE: This is NOT the log message!" \
37: "\n>> "
1.29 joris 38:
1.1 deraadt 39: const char rcs_version[] = "OpenCVS RCS version 3.6";
1.12 joris 40: int verbose = 1;
1.34 joris 41: int pipeout = 0;
1.1 deraadt 42:
1.56 joris 43: #define RCS_NFLAG 1
44: #define RCS_TFLAG 2
45: static int rcsflags = 0;
46:
1.36 xsa 47: int rcs_optind;
1.28 joris 48: char *rcs_optarg;
1.42 xsa 49: char *rcs_suffixes;
1.36 xsa 50: char *rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.28 joris 51:
1.1 deraadt 52: struct rcs_prog {
1.26 deraadt 53: char *prog_name;
54: int (*prog_hdlr)(int, char **);
55: void (*prog_usage)(void);
1.1 deraadt 56: } programs[] = {
1.2 deraadt 57: { "rcs", rcs_main, rcs_usage },
1.17 joris 58: { "ci", checkin_main, checkin_usage },
1.11 joris 59: { "co", checkout_main, checkout_usage },
1.20 joris 60: { "rcsclean", rcsclean_main, rcsclean_usage },
1.18 joris 61: { "rcsdiff", rcsdiff_main, rcsdiff_usage },
1.33 xsa 62: { "rcsmerge", rcsmerge_main, rcsmerge_usage },
1.21 joris 63: { "rlog", rlog_main, rlog_usage },
1.22 joris 64: { "ident", ident_main, ident_usage },
1.1 deraadt 65: };
1.31 joris 66:
1.68 ! joris 67: struct cvs_wklhead rcs_temp_files;
! 68:
! 69: void sighdlr(int);
1.56 joris 70: static void rcs_set_description(RCSFILE *, const char *);
71: static void rcs_attach_symbol(RCSFILE *, const char *);
72:
1.31 joris 73: void
1.68 ! joris 74: sighdlr(int sig)
! 75: {
! 76: cvs_worklist_clean(&rcs_temp_files, cvs_worklist_unlink);
! 77: _exit(1);
! 78: }
! 79:
! 80: void
1.31 joris 81: rcs_set_rev(const char *str, RCSNUM **rev)
82: {
1.63 niallo 83: RCSNUM *t;
84:
1.32 joris 85: if (str == NULL)
86: return;
87:
1.52 joris 88: if ((*rev != NULL) && (*rev != RCS_HEAD_REV))
1.31 joris 89: cvs_log(LP_WARN, "redefinition of revision number");
90:
1.55 xsa 91: if ((*rev = rcsnum_parse(str)) == NULL)
92: fatal("bad revision number '%s'", str);
1.64 deraadt 93: /*
1.63 niallo 94: * If 0 is specified as a revision number, exit and warn the user.
95: * This differs from GNU ci's plainly buggy behaviour, where 0 ends up
96: * being 0.1 and other weird stuff.
97: */
98: t = rcsnum_parse("0");
99: if (rcsnum_cmp(*rev, t, 0) == 0)
100: fatal("0 is not a valid revision number");
101: rcsnum_free(t);
102: /*
103: * If 1 is specified as revision number, silently assume 1.1.
1.64 deraadt 104: * This is what GNU ci does.
1.63 niallo 105: */
106: t = rcsnum_parse(RCS_HEAD_INIT);
107: if (rcsnum_cmp(*rev, t, 0) > 0)
1.64 deraadt 108: rcsnum_cpy(t, *rev, 0);
1.63 niallo 109: rcsnum_free(t);
1.47 xsa 110: }
111:
112: /*
113: * rcs_get_mtime()
114: *
115: * Get <filename> last modified time.
116: * Returns last modified time on success, or -1 on failure.
117: */
118: time_t
119: rcs_get_mtime(const char *filename)
120: {
121: struct stat st;
122: time_t mtime;
123:
124: if (stat(filename, &st) == -1) {
125: cvs_log(LP_ERRNO, "failed to stat `%s'", filename);
126: return (-1);
127: }
128: mtime = (time_t)st.st_mtimespec.tv_sec;
129:
130: return (mtime);
131: }
132:
133: /*
134: * rcs_set_mtime()
135: *
136: * Set <filename> last modified time to <mtime> if it's not set to -1.
137: * Returns 0 on success, or -1 on failure.
138: */
139: int
140: rcs_set_mtime(const char *filename, time_t mtime)
141: {
142: static struct timeval tv[2];
143:
144: if (mtime == -1)
145: return (0);
146:
147: tv[0].tv_sec = mtime;
148: tv[1].tv_sec = tv[0].tv_sec;
149:
150: if (utimes(filename, tv) == -1) {
151: cvs_log(LP_ERRNO, "error setting utimes");
152: return (-1);
153: }
154:
155: return (0);
1.31 joris 156: }
1.1 deraadt 157:
1.9 joris 158: int
1.29 joris 159: rcs_init(char *envstr, char **argv, int argvlen)
160: {
161: u_int i;
162: int argc, error;
163: char linebuf[256], *lp, *cp;
164:
165: strlcpy(linebuf, envstr, sizeof(linebuf));
166: memset(argv, 0, argvlen * sizeof(char *));
167:
168: error = argc = 0;
169: for (lp = linebuf; lp != NULL;) {
1.61 xsa 170: cp = strsep(&lp, " \t\b\f\n\r\t\v");
1.29 joris 171: if (cp == NULL)
172: break;
173: else if (*cp == '\0')
174: continue;
175:
176: if (argc == argvlen) {
177: error++;
178: break;
179: }
180:
1.53 joris 181: argv[argc] = xstrdup(cp);
1.29 joris 182: argc++;
183: }
184:
185: if (error != 0) {
186: for (i = 0; i < (u_int)argc; i++)
1.53 joris 187: xfree(argv[i]);
1.29 joris 188: argc = -1;
189: }
190:
191: return (argc);
192: }
193:
194: int
1.28 joris 195: rcs_getopt(int argc, char **argv, const char *optstr)
196: {
197: char *a;
198: const char *c;
199: static int i = 1;
200: int opt, hasargument, ret;
201:
202: hasargument = 0;
203: rcs_optarg = NULL;
204:
205: if (i >= argc)
206: return (-1);
207:
208: a = argv[i++];
209: if (*a++ != '-')
210: return (-1);
211:
212: ret = 0;
213: opt = *a;
214: for (c = optstr; *c != '\0'; c++) {
215: if (*c == opt) {
216: a++;
217: ret = opt;
218:
219: if (*(c + 1) == ':') {
220: if (*(c + 2) == ':') {
221: if (*a != '\0')
222: hasargument = 1;
223: } else {
224: if (*a != '\0') {
225: hasargument = 1;
226: } else {
227: ret = 1;
228: break;
229: }
230: }
231: }
232:
233: if (hasargument == 1)
234: rcs_optarg = a;
235:
236: if (ret == opt)
237: rcs_optind++;
238: break;
239: }
240: }
241:
242: if (ret == 0)
243: cvs_log(LP_ERR, "unknown option -%c", opt);
244: else if (ret == 1)
245: cvs_log(LP_ERR, "missing argument for option -%c", opt);
246:
247: return (ret);
248: }
249:
250: int
1.9 joris 251: rcs_statfile(char *fname, char *out, size_t len)
252: {
1.62 xsa 253: int found, strdir;
1.42 xsa 254: char defaultsuffix[] = RCS_DEFAULT_SUFFIX;
1.9 joris 255: char filev[MAXPATHLEN], fpath[MAXPATHLEN];
1.42 xsa 256: char *ext, *slash;
1.9 joris 257: struct stat st;
258:
1.42 xsa 259: strdir = found = 0;
260:
261: /* we might have gotten a RCS file as argument */
262: if ((ext = strchr(fname, ',')) != NULL)
263: *ext = '\0';
264:
265: /* we might have gotten the RCS/ dir in the argument string */
266: if (strstr(fname, RCSDIR) != NULL)
267: strdir = 1;
268:
269: if (rcs_suffixes != NULL)
270: ext = rcs_suffixes;
271: else
272: ext = defaultsuffix;
1.43 xsa 273:
1.42 xsa 274: for (;;) {
275: /*
276: * GNU documentation says -x,v/ specifies two suffixes,
277: * namely the ,v one and an empty one (which matches
278: * everything).
279: * The problem is that they don't follow this rule at
280: * all, and their documentation seems flawed.
281: * We try to be compatible, so let's do so.
282: */
283: if (*ext == '\0')
284: break;
285:
286: if ((slash = strchr(ext, '/')) != NULL)
287: *slash = '\0';
1.9 joris 288:
1.62 xsa 289: if (strlcpy(filev, fname, sizeof(filev)) >= sizeof(filev) ||
290: strlcat(filev, ext, sizeof(filev)) >= sizeof(filev))
1.55 xsa 291: fatal("rcs_statfile: path truncation");
1.42 xsa 292:
293: if ((strdir == 0) &&
294: (stat(RCSDIR, &st) != -1) && (st.st_mode & S_IFDIR)) {
1.62 xsa 295: if (strlcpy(fpath, RCSDIR,
296: sizeof(fpath)) >= sizeof(fpath) ||
297: strlcat(fpath, "/",
298: sizeof(fpath)) >= sizeof(fpath) ||
299: strlcat(fpath, filev,
300: sizeof(fpath)) >= sizeof(fpath))
1.55 xsa 301: fatal("rcs_statfile: path truncation");
1.42 xsa 302: } else {
1.62 xsa 303: if (strlcpy(fpath, filev,
304: sizeof(fpath)) >= sizeof(fpath))
1.57 xsa 305: fatal("rcs_statfile: path truncation");
1.42 xsa 306: }
307:
1.56 joris 308: if ((stat(fpath, &st) != -1) || (rcsflags & RCS_CREATE)) {
1.42 xsa 309: found++;
310: break;
311: }
312:
313: if (slash == NULL)
314: break;
315:
316: *slash++ = '/';
317: ext = slash;
1.9 joris 318: }
319:
1.42 xsa 320: if (found != 1) {
1.44 niallo 321: if ((strcmp(__progname, "rcsclean") != 0)
322: && (strcmp(__progname, "ci") != 0))
1.20 joris 323: cvs_log(LP_ERRNO, "%s", fpath);
1.9 joris 324: return (-1);
325: }
326:
1.62 xsa 327: if (strlcpy(out, fpath, len) >= len)
1.57 xsa 328: fatal("rcs_statfile: path truncation");
1.9 joris 329:
330: return (0);
331: }
1.1 deraadt 332:
333: int
334: main(int argc, char **argv)
335: {
336: u_int i;
1.29 joris 337: char *rcsinit, *cmd_argv[RCS_CMD_MAXARG];
338: int ret, cmd_argc;
1.1 deraadt 339:
340: ret = -1;
1.28 joris 341: rcs_optind = 1;
1.9 joris 342: cvs_log_init(LD_STD, 0);
1.68 ! joris 343: SLIST_INIT(&rcs_temp_files);
1.1 deraadt 344:
1.29 joris 345: cmd_argc = 0;
1.30 joris 346: cmd_argv[cmd_argc++] = argv[0];
1.29 joris 347: if ((rcsinit = getenv("RCSINIT")) != NULL) {
348: ret = rcs_init(rcsinit, cmd_argv + 1,
349: RCS_CMD_MAXARG - 1);
350: if (ret < 0) {
351: cvs_log(LP_ERRNO, "failed to prepend RCSINIT options");
352: exit (1);
353: }
354:
355: cmd_argc += ret;
356: }
1.36 xsa 357:
358: if ((rcs_tmpdir = getenv("TMPDIR")) == NULL)
359: rcs_tmpdir = RCS_TMPDIR_DEFAULT;
1.29 joris 360:
361: for (ret = 1; ret < argc; ret++)
362: cmd_argv[cmd_argc++] = argv[ret];
1.68 ! joris 363:
! 364: signal(SIGHUP, sighdlr);
! 365: signal(SIGINT, sighdlr);
! 366: signal(SIGQUIT, sighdlr);
! 367: signal(SIGABRT, sighdlr);
! 368: signal(SIGALRM, sighdlr);
! 369: signal(SIGTERM, sighdlr);
1.29 joris 370:
1.1 deraadt 371: for (i = 0; i < (sizeof(programs)/sizeof(programs[0])); i++)
1.2 deraadt 372: if (strcmp(__progname, programs[i].prog_name) == 0) {
373: usage = programs[i].prog_usage;
1.29 joris 374: ret = programs[i].prog_hdlr(cmd_argc, cmd_argv);
1.2 deraadt 375: break;
376: }
1.1 deraadt 377:
1.23 niallo 378: exit(ret);
1.1 deraadt 379: }
380:
381:
382: void
383: rcs_usage(void)
384: {
385: fprintf(stderr,
1.66 jmc 386: "usage: rcs [-ehIiLMqTUV] [-Aoldfile] [-ausers] [-b[rev]]\n"
1.67 xsa 387: " [-cstring] [-e[users]] [-kmode] [-mrev:msg]\n"
1.66 jmc 388: " [-orev] [-sstate[:rev]] [-tfile|str]\n"
1.67 xsa 389: " [-xsuffixes] file ...\n");
1.1 deraadt 390: }
391:
392: /*
393: * rcs_main()
394: *
395: * Handler for the `rcs' program.
396: * Returns 0 on success, or >0 on error.
397: */
398: int
399: rcs_main(int argc, char **argv)
400: {
401: int i, ch, flags, kflag, lkmode;
1.39 xsa 402: char fpath[MAXPATHLEN], ofpath[MAXPATHLEN];
1.56 joris 403: char *logstr, *logmsg, *nflag, *descfile;
1.39 xsa 404: char *alist, *comment, *elist, *unp, *sp;
1.1 deraadt 405: mode_t fmode;
1.39 xsa 406: RCSFILE *file, *oldfile;
1.24 joris 407: RCSNUM *logrev;
1.39 xsa 408: struct rcs_access *acp;
1.48 xsa 409: time_t rcs_mtime = -1;
1.1 deraadt 410:
411: kflag = lkmode = -1;
412: fmode = 0;
1.58 niallo 413: flags = RCS_RDWR|RCS_PARSE_FULLY;
1.56 joris 414: descfile = nflag = NULL;
1.39 xsa 415: logstr = alist = comment = elist = NULL;
1.1 deraadt 416:
1.51 xsa 417: while ((ch = rcs_getopt(argc, argv, RCSPROG_OPTSTRING)) != -1) {
1.1 deraadt 418: switch (ch) {
419: case 'A':
1.39 xsa 420: if (rcs_statfile(rcs_optarg, ofpath, sizeof(ofpath)) < 0)
421: exit(1);
1.56 joris 422: rcsflags |= CO_ACLAPPEND;
1.1 deraadt 423: break;
424: case 'a':
1.28 joris 425: alist = rcs_optarg;
1.1 deraadt 426: break;
427: case 'c':
1.28 joris 428: comment = rcs_optarg;
1.1 deraadt 429: break;
430: case 'e':
1.28 joris 431: elist = rcs_optarg;
1.1 deraadt 432: break;
433: case 'h':
1.2 deraadt 434: (usage)();
1.1 deraadt 435: exit(0);
436: case 'i':
437: flags |= RCS_CREATE;
1.56 joris 438: rcsflags |= RCS_CREATE;
1.1 deraadt 439: break;
440: case 'k':
1.28 joris 441: kflag = rcs_kflag_get(rcs_optarg);
1.1 deraadt 442: if (RCS_KWEXP_INVAL(kflag)) {
443: cvs_log(LP_ERR,
444: "invalid keyword substitution mode `%s'",
1.28 joris 445: rcs_optarg);
1.1 deraadt 446: exit(1);
447: }
448: break;
449: case 'L':
450: if (lkmode == RCS_LOCK_LOOSE)
451: cvs_log(LP_WARN, "-U overriden by -L");
452: lkmode = RCS_LOCK_STRICT;
453: break;
1.24 joris 454: case 'm':
1.53 joris 455: logstr = xstrdup(rcs_optarg);
1.24 joris 456: break;
1.1 deraadt 457: case 'M':
458: /* ignore for the moment */
459: break;
1.56 joris 460: case 'n':
461: nflag = xstrdup(rcs_optarg);
462: break;
463: case 'N':
464: nflag = xstrdup(rcs_optarg);
465: rcsflags |= RCS_NFLAG;
466: break;
1.12 joris 467: case 'q':
468: verbose = 0;
1.46 xsa 469: break;
1.56 joris 470: case 't':
471: descfile = rcs_optarg;
472: rcsflags |= RCS_TFLAG;
473: break;
1.46 xsa 474: case 'T':
1.56 joris 475: rcsflags |= PRESERVETIME;
1.12 joris 476: break;
1.1 deraadt 477: case 'U':
478: if (lkmode == RCS_LOCK_STRICT)
479: cvs_log(LP_WARN, "-L overriden by -U");
480: lkmode = RCS_LOCK_LOOSE;
481: break;
482: case 'V':
483: printf("%s\n", rcs_version);
484: exit(0);
1.45 xsa 485: case 'x':
486: rcs_suffixes = rcs_optarg;
1.51 xsa 487: break;
488: case 'z':
489: /*
490: * kept for compatibility
491: */
1.45 xsa 492: break;
1.1 deraadt 493: default:
1.2 deraadt 494: (usage)();
1.1 deraadt 495: exit(1);
496: }
497: }
498:
1.28 joris 499: argc -= rcs_optind;
500: argv += rcs_optind;
1.30 joris 501:
1.1 deraadt 502: if (argc == 0) {
503: cvs_log(LP_ERR, "no input file");
1.5 joris 504: (usage)();
1.1 deraadt 505: exit(1);
506: }
507:
508: for (i = 0; i < argc; i++) {
1.9 joris 509: if (rcs_statfile(argv[i], fpath, sizeof(fpath)) < 0)
1.8 joris 510: continue;
1.6 joris 511:
1.35 niallo 512: if (verbose == 1)
513: printf("RCS file: %s\n", fpath);
1.48 xsa 514:
1.49 niallo 515: if ((file = rcs_open(fpath, flags, fmode)) == NULL)
1.6 joris 516: continue;
1.1 deraadt 517:
1.56 joris 518: if (rcsflags & RCS_CREATE)
519: rcs_set_description(file, NULL);
520:
521: if (rcsflags & RCS_TFLAG)
522: rcs_set_description(file, descfile);
523:
524: if (rcsflags & PRESERVETIME)
1.48 xsa 525: rcs_mtime = rcs_get_mtime(file->rf_path);
526:
1.56 joris 527: if (nflag != NULL)
528: rcs_attach_symbol(file, nflag);
529:
1.24 joris 530: if (logstr != NULL) {
531: if ((logmsg = strchr(logstr, ':')) == NULL) {
532: cvs_log(LP_ERR, "missing log message");
533: rcs_close(file);
534: continue;
535: }
536:
537: *logmsg++ = '\0';
538: if ((logrev = rcsnum_parse(logstr)) == NULL) {
1.48 xsa 539: cvs_log(LP_ERR,
540: "'%s' bad revision number", logstr);
1.24 joris 541: rcs_close(file);
542: continue;
543: }
544:
545: if (rcs_rev_setlog(file, logrev, logmsg) < 0) {
546: cvs_log(LP_ERR,
547: "failed to set logmsg for '%s' to '%s'",
548: logstr, logmsg);
549: rcs_close(file);
1.25 joris 550: rcsnum_free(logrev);
1.24 joris 551: continue;
552: }
553:
554: rcsnum_free(logrev);
1.39 xsa 555: }
556:
557: /* entries to add from <oldfile> */
1.56 joris 558: if (rcsflags & CO_ACLAPPEND) {
1.39 xsa 559: /* XXX */
560: if ((oldfile = rcs_open(ofpath, RCS_READ)) == NULL)
561: exit(1);
562:
563: TAILQ_FOREACH(acp, &(oldfile->rf_access), ra_list)
564: rcs_access_add(file, acp->ra_name);
565:
566: rcs_close(oldfile);
1.24 joris 567: }
568:
1.1 deraadt 569: /* entries to add to the access list */
570: if (alist != NULL) {
571: unp = alist;
572: do {
573: sp = strchr(unp, ',');
574: if (sp != NULL)
575: *(sp++) = '\0';
576:
577: rcs_access_add(file, unp);
578:
579: unp = sp;
580: } while (sp != NULL);
581: }
582:
583: if (comment != NULL)
584: rcs_comment_set(file, comment);
585:
586: if (kflag != -1)
587: rcs_kwexp_set(file, kflag);
588:
589: if (lkmode != -1)
590: rcs_lock_setmode(file, lkmode);
591:
592: rcs_close(file);
1.48 xsa 593:
1.56 joris 594: if (rcsflags & PRESERVETIME)
1.48 xsa 595: rcs_set_mtime(fpath, rcs_mtime);
1.9 joris 596:
1.14 xsa 597: if (verbose == 1)
1.12 joris 598: printf("done\n");
1.1 deraadt 599: }
1.24 joris 600:
601: if (logstr != NULL)
1.53 joris 602: xfree(logstr);
1.1 deraadt 603:
1.56 joris 604: if (nflag != NULL)
605: xfree(nflag);
606:
1.1 deraadt 607: return (0);
1.56 joris 608: }
609:
610: static void
611: rcs_attach_symbol(RCSFILE *file, const char *symname)
612: {
613: char *rnum;
614: RCSNUM *rev;
615: char rbuf[16];
616: int rm;
617:
618: rm = 0;
619: rev = NULL;
620: if ((rnum = strrchr(symname, ':')) != NULL) {
621: if (rnum[1] == '\0')
622: rev = file->rf_head;
623: *(rnum++) = '\0';
624: } else {
625: rm = 1;
626: }
627:
628: if (rev == NULL && rm != 1) {
629: if ((rev = rcsnum_parse(rnum)) == NULL)
630: fatal("bad revision %s", rnum);
631: }
632:
633: if (rcsflags & RCS_NFLAG)
634: rm = 1;
635:
636: if (rm == 1) {
637: if (rcs_sym_remove(file, symname) < 0) {
638: if ((rcs_errno == RCS_ERR_NOENT) &&
639: !(rcsflags & RCS_NFLAG))
640: cvs_log(LP_WARN,
641: "can't delete nonexisting symbol %s", symname);
642: } else {
643: if (rcsflags & RCS_NFLAG)
644: rm = 0;
645: }
646: }
647:
648: if (rm == 0) {
649: if ((rcs_sym_add(file, symname, rev) < 0) &&
650: (rcs_errno == RCS_ERR_DUPENT)) {
651: rcsnum_tostr(rcs_sym_getrev(file, symname),
652: rbuf, sizeof(rbuf));
653: fatal("symbolic name %s already bound to %s",
654: symname, rbuf);
655: }
656: }
657: }
658:
659: static void
660: rcs_set_description(RCSFILE *file, const char *in)
661: {
662: BUF *bp;
663: char *content, buf[128];
664:
665: content = NULL;
666: if (in != NULL) {
667: bp = cvs_buf_load(in, BUF_AUTOEXT);
668: } else {
669: bp = cvs_buf_alloc(64, BUF_AUTOEXT);
670:
671: printf(DESC_PROMPT);
672: for (;;) {
673: fgets(buf, sizeof(buf), stdin);
674: if (feof(stdin) || ferror(stdin) || buf[0] == '.')
675: break;
676: cvs_buf_append(bp, buf, strlen(buf));
677: printf(">> ");
678: }
679: }
680:
681: cvs_buf_putc(bp, '\0');
682: content = cvs_buf_release(bp);
683:
684: rcs_desc_set(file, content);
1.1 deraadt 685: }