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