Annotation of src/usr.bin/cvs/rcs.c, Revision 1.241
1.241 ! joris 1: /* $OpenBSD: rcs.c,v 1.240 2008/01/28 21:33:20 tobias Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.15 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.15 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.15 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.15 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 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
1.15 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.210 otto 27: #include <sys/stat.h>
28:
29: #include <ctype.h>
30: #include <errno.h>
31: #include <libgen.h>
32: #include <pwd.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <unistd.h>
1.1 jfb 36:
1.219 tobias 37: #include "atomicio.h"
1.94 joris 38: #include "cvs.h"
1.172 joris 39: #include "diff.h"
1.1 jfb 40: #include "rcs.h"
41:
1.57 xsa 42: #define RCS_BUFSIZE 16384
43: #define RCS_BUFEXTSIZE 8192
1.117 niallo 44: #define RCS_KWEXP_SIZE 1024
1.1 jfb 45:
46: /* RCS token types */
1.57 xsa 47: #define RCS_TOK_ERR -1
48: #define RCS_TOK_EOF 0
49: #define RCS_TOK_NUM 1
50: #define RCS_TOK_ID 2
51: #define RCS_TOK_STRING 3
52: #define RCS_TOK_SCOLON 4
53: #define RCS_TOK_COLON 5
54:
55: #define RCS_TOK_HEAD 8
56: #define RCS_TOK_BRANCH 9
57: #define RCS_TOK_ACCESS 10
58: #define RCS_TOK_SYMBOLS 11
59: #define RCS_TOK_LOCKS 12
60: #define RCS_TOK_COMMENT 13
61: #define RCS_TOK_EXPAND 14
62: #define RCS_TOK_DATE 15
63: #define RCS_TOK_AUTHOR 16
64: #define RCS_TOK_STATE 17
65: #define RCS_TOK_NEXT 18
66: #define RCS_TOK_BRANCHES 19
67: #define RCS_TOK_DESC 20
68: #define RCS_TOK_LOG 21
69: #define RCS_TOK_TEXT 22
70: #define RCS_TOK_STRICT 23
1.1 jfb 71:
1.57 xsa 72: #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
1.1 jfb 73:
1.57 xsa 74: #define RCS_NOSCOL 0x01 /* no terminating semi-colon */
75: #define RCS_VOPT 0x02 /* value is optional */
1.1 jfb 76:
1.218 tobias 77: #define ANNOTATE_NEVER 0
78: #define ANNOTATE_NOW 1
79: #define ANNOTATE_LATER 2
80:
1.1 jfb 81: /* opaque parse data */
82: struct rcs_pdata {
1.57 xsa 83: u_int rp_lines;
1.1 jfb 84:
1.57 xsa 85: char *rp_buf;
86: size_t rp_blen;
87: char *rp_bufend;
88: size_t rp_tlen;
1.1 jfb 89:
90: /* pushback token buffer */
1.57 xsa 91: char rp_ptok[128];
92: int rp_pttype; /* token type, RCS_TOK_ERR if no token */
1.1 jfb 93:
1.57 xsa 94: FILE *rp_file;
1.1 jfb 95: };
96:
1.57 xsa 97: #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
98: #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen
1.1 jfb 99:
1.47 jfb 100: /* invalid characters in RCS symbol names */
1.49 jfb 101: static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
1.47 jfb 102:
1.51 jfb 103: /* comment leaders, depending on the file's suffix */
104: static const struct rcs_comment {
1.57 xsa 105: const char *rc_suffix;
106: const char *rc_cstr;
1.51 jfb 107: } rcs_comments[] = {
108: { "1", ".\\\" " },
109: { "2", ".\\\" " },
110: { "3", ".\\\" " },
111: { "4", ".\\\" " },
112: { "5", ".\\\" " },
113: { "6", ".\\\" " },
114: { "7", ".\\\" " },
115: { "8", ".\\\" " },
116: { "9", ".\\\" " },
117: { "a", "-- " }, /* Ada */
118: { "ada", "-- " },
119: { "adb", "-- " },
120: { "asm", ";; " }, /* assembler (MS-DOS) */
121: { "ads", "-- " }, /* Ada */
122: { "bat", ":: " }, /* batch (MS-DOS) */
123: { "body", "-- " }, /* Ada */
124: { "c", " * " }, /* C */
125: { "c++", "// " }, /* C++ */
126: { "cc", "// " },
127: { "cpp", "// " },
128: { "cxx", "// " },
129: { "m", "// " }, /* Objective-C */
130: { "cl", ";;; " }, /* Common Lisp */
131: { "cmd", ":: " }, /* command (OS/2) */
132: { "cmf", "c " }, /* CM Fortran */
133: { "csh", "# " }, /* shell */
134: { "e", "# " }, /* efl */
135: { "epsf", "% " }, /* encapsulated postscript */
136: { "epsi", "% " }, /* encapsulated postscript */
137: { "el", "; " }, /* Emacs Lisp */
138: { "f", "c " }, /* Fortran */
139: { "for", "c " },
140: { "h", " * " }, /* C-header */
141: { "hh", "// " }, /* C++ header */
142: { "hpp", "// " },
143: { "hxx", "// " },
144: { "in", "# " }, /* for Makefile.in */
145: { "l", " * " }, /* lex */
146: { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
147: { "mak", "# " }, /* makefile, e.g. Visual C++ */
148: { "me", ".\\\" " }, /* me-macros t/nroff */
149: { "ml", "; " }, /* mocklisp */
150: { "mm", ".\\\" " }, /* mm-macros t/nroff */
151: { "ms", ".\\\" " }, /* ms-macros t/nroff */
152: { "man", ".\\\" " }, /* man-macros t/nroff */
153: { "p", " * " }, /* pascal */
154: { "pas", " * " },
1.52 jfb 155: { "pl", "# " }, /* Perl (conflict with Prolog) */
156: { "pm", "# " }, /* Perl module */
1.51 jfb 157: { "ps", "% " }, /* postscript */
158: { "psw", "% " }, /* postscript wrap */
159: { "pswm", "% " }, /* postscript wrap */
160: { "r", "# " }, /* ratfor */
161: { "rc", " * " }, /* Microsoft Windows resource file */
162: { "red", "% " }, /* psl/rlisp */
163: { "sh", "# " }, /* shell */
164: { "sl", "% " }, /* psl */
165: { "spec", "-- " }, /* Ada */
166: { "tex", "% " }, /* tex */
167: { "y", " * " }, /* yacc */
168: { "ye", " * " }, /* yacc-efl */
169: { "yr", " * " }, /* yacc-ratfor */
170: };
171:
1.128 niallo 172: struct rcs_kw rcs_expkw[] = {
173: { "Author", RCS_KW_AUTHOR },
174: { "Date", RCS_KW_DATE },
175: { "Header", RCS_KW_HEADER },
176: { "Id", RCS_KW_ID },
1.229 tobias 177: { "Locker", RCS_KW_LOCKER },
1.128 niallo 178: { "Log", RCS_KW_LOG },
179: { "Name", RCS_KW_NAME },
180: { "RCSfile", RCS_KW_RCSFILE },
181: { "Revision", RCS_KW_REVISION },
182: { "Source", RCS_KW_SOURCE },
183: { "State", RCS_KW_STATE },
1.211 niallo 184: { "Mdocdate", RCS_KW_MDOCDATE },
1.128 niallo 185: };
186:
1.57 xsa 187: #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
1.51 jfb 188:
1.1 jfb 189: static struct rcs_key {
1.57 xsa 190: char rk_str[16];
191: int rk_id;
192: int rk_val;
193: int rk_flags;
1.1 jfb 194: } rcs_keys[] = {
195: { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT },
1.41 jfb 196: { "author", RCS_TOK_AUTHOR, RCS_TOK_ID, 0 },
1.1 jfb 197: { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT },
198: { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT },
199: { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT },
200: { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 },
201: { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL },
202: { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT },
203: { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT },
204: { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 },
205: { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL },
206: { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT },
1.41 jfb 207: { "state", RCS_TOK_STATE, RCS_TOK_ID, RCS_VOPT },
1.1 jfb 208: { "strict", RCS_TOK_STRICT, 0, 0, },
209: { "symbols", RCS_TOK_SYMBOLS, 0, 0 },
210: { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL },
211: };
212:
1.57 xsa 213: #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0]))
1.1 jfb 214:
1.32 jfb 215: static const char *rcs_errstrs[] = {
216: "No error",
217: "No such entry",
218: "Duplicate entry found",
219: "Bad RCS number",
1.48 jfb 220: "Invalid RCS symbol",
221: "Parse error",
1.32 jfb 222: };
223:
224: #define RCS_NERR (sizeof(rcs_errstrs)/sizeof(rcs_errstrs[0]))
225:
226: int rcs_errno = RCS_ERR_NOERR;
1.1 jfb 227:
1.218 tobias 228: int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *,
229: struct cvs_line **, struct rcs_delta *);
1.167 ray 230: static void rcs_parse_init(RCSFILE *);
1.57 xsa 231: static int rcs_parse_admin(RCSFILE *);
232: static int rcs_parse_delta(RCSFILE *);
1.146 xsa 233: static void rcs_parse_deltas(RCSFILE *, RCSNUM *);
1.57 xsa 234: static int rcs_parse_deltatext(RCSFILE *);
1.146 xsa 235: static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *);
1.148 xsa 236: static void rcs_parse_desc(RCSFILE *, RCSNUM *);
1.57 xsa 237:
238: static int rcs_parse_access(RCSFILE *);
239: static int rcs_parse_symbols(RCSFILE *);
240: static int rcs_parse_locks(RCSFILE *);
241: static int rcs_parse_branches(RCSFILE *, struct rcs_delta *);
242: static void rcs_freedelta(struct rcs_delta *);
243: static void rcs_freepdata(struct rcs_pdata *);
244: static int rcs_gettok(RCSFILE *);
245: static int rcs_pushtok(RCSFILE *, const char *, int);
1.151 xsa 246: static void rcs_growbuf(RCSFILE *);
1.155 ray 247: static void rcs_strprint(const u_char *, size_t, FILE *);
1.57 xsa 248:
1.233 tobias 249: static void rcs_kwexp_line(char *, struct rcs_delta *, struct cvs_lines *,
250: struct cvs_line *, int mode);
1.26 jfb 251:
1.60 xsa 252: RCSFILE *
1.172 joris 253: rcs_open(const char *path, int fd, int flags, ...)
1.1 jfb 254: {
1.172 joris 255: int mode;
1.228 tobias 256: mode_t fmode, mask;
1.1 jfb 257: RCSFILE *rfp;
1.26 jfb 258: va_list vap;
1.109 joris 259: struct rcs_delta *rdp;
260: struct rcs_lock *lkr;
1.26 jfb 261:
1.152 niallo 262: fmode = S_IRUSR|S_IRGRP|S_IROTH;
1.26 jfb 263: flags &= 0xffff; /* ditch any internal flags */
1.1 jfb 264:
1.172 joris 265: if (flags & RCS_CREATE) {
266: va_start(vap, flags);
267: mode = va_arg(vap, int);
268: va_end(vap);
269: fmode = (mode_t)mode;
1.1 jfb 270: }
1.228 tobias 271:
272: mask = umask(0);
273: umask(mask);
274: fmode &= ~mask;
1.1 jfb 275:
1.161 ray 276: rfp = xcalloc(1, sizeof(*rfp));
1.1 jfb 277:
1.110 joris 278: rfp->rf_path = xstrdup(path);
1.142 niallo 279: rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
1.26 jfb 280: rfp->rf_mode = fmode;
1.172 joris 281: rfp->fd = fd;
1.175 joris 282: rfp->rf_dead = 0;
1.1 jfb 283:
284: TAILQ_INIT(&(rfp->rf_delta));
1.29 jfb 285: TAILQ_INIT(&(rfp->rf_access));
1.1 jfb 286: TAILQ_INIT(&(rfp->rf_symbols));
287: TAILQ_INIT(&(rfp->rf_locks));
288:
1.167 ray 289: if (!(rfp->rf_flags & RCS_CREATE))
290: rcs_parse_init(rfp);
1.1 jfb 291:
1.109 joris 292: /* fill in rd_locker */
293: TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
294: if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
295: rcs_close(rfp);
296: return (NULL);
297: }
298:
1.110 joris 299: rdp->rd_locker = xstrdup(lkr->rl_name);
1.109 joris 300: }
301:
1.1 jfb 302: return (rfp);
303: }
304:
305: /*
306: * rcs_close()
307: *
308: * Close an RCS file handle.
309: */
310: void
311: rcs_close(RCSFILE *rfp)
312: {
313: struct rcs_delta *rdp;
1.71 moritz 314: struct rcs_access *rap;
1.13 jfb 315: struct rcs_lock *rlp;
316: struct rcs_sym *rsp;
1.1 jfb 317:
1.26 jfb 318: if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
319: rcs_write(rfp);
320:
1.1 jfb 321: while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
322: rdp = TAILQ_FIRST(&(rfp->rf_delta));
323: TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
324: rcs_freedelta(rdp);
1.71 moritz 325: }
326:
327: while (!TAILQ_EMPTY(&(rfp->rf_access))) {
328: rap = TAILQ_FIRST(&(rfp->rf_access));
329: TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
1.110 joris 330: xfree(rap->ra_name);
331: xfree(rap);
1.1 jfb 332: }
333:
1.13 jfb 334: while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
335: rsp = TAILQ_FIRST(&(rfp->rf_symbols));
336: TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
337: rcsnum_free(rsp->rs_num);
1.110 joris 338: xfree(rsp->rs_name);
339: xfree(rsp);
1.13 jfb 340: }
341:
342: while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
343: rlp = TAILQ_FIRST(&(rfp->rf_locks));
344: TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
345: rcsnum_free(rlp->rl_num);
1.110 joris 346: xfree(rlp->rl_name);
347: xfree(rlp);
1.13 jfb 348: }
349:
1.1 jfb 350: if (rfp->rf_head != NULL)
351: rcsnum_free(rfp->rf_head);
1.11 joris 352: if (rfp->rf_branch != NULL)
353: rcsnum_free(rfp->rf_branch);
1.1 jfb 354:
355: if (rfp->rf_path != NULL)
1.110 joris 356: xfree(rfp->rf_path);
1.1 jfb 357: if (rfp->rf_comment != NULL)
1.110 joris 358: xfree(rfp->rf_comment);
1.1 jfb 359: if (rfp->rf_expand != NULL)
1.110 joris 360: xfree(rfp->rf_expand);
1.1 jfb 361: if (rfp->rf_desc != NULL)
1.110 joris 362: xfree(rfp->rf_desc);
1.118 joris 363: if (rfp->rf_pdata != NULL)
364: rcs_freepdata(rfp->rf_pdata);
1.110 joris 365: xfree(rfp);
1.1 jfb 366: }
367:
368: /*
369: * rcs_write()
370: *
371: * Write the contents of the RCS file handle <rfp> to disk in the file whose
372: * path is in <rf_path>.
373: */
1.172 joris 374: void
1.1 jfb 375: rcs_write(RCSFILE *rfp)
376: {
377: FILE *fp;
1.214 xsa 378: char buf[1024], numbuf[CVS_REV_BUFSZ], *fn, tmpdir[MAXPATHLEN];
1.29 jfb 379: struct rcs_access *ap;
1.1 jfb 380: struct rcs_sym *symp;
1.46 jfb 381: struct rcs_branch *brp;
1.1 jfb 382: struct rcs_delta *rdp;
1.75 joris 383: struct rcs_lock *lkp;
1.100 xsa 384: size_t len;
1.207 otto 385: int fd, saved_errno;
1.75 joris 386:
1.207 otto 387: fd = -1;
1.1 jfb 388:
1.137 joris 389: if (rfp->rf_flags & RCS_SYNCED)
1.181 joris 390: return;
391:
392: if (cvs_noexec == 1)
1.172 joris 393: return;
1.137 joris 394:
1.117 niallo 395: /* Write operations need the whole file parsed */
396: rcs_parse_deltatexts(rfp, NULL);
1.1 jfb 397:
1.207 otto 398: if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
399: fatal("rcs_write: truncation");
400: (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
1.172 joris 401:
1.113 xsa 402: if ((fd = mkstemp(fn)) == -1)
1.172 joris 403: fatal("%s", fn);
1.113 xsa 404:
1.207 otto 405: if ((fp = fdopen(fd, "w")) == NULL) {
1.172 joris 406: saved_errno = errno;
407: (void)unlink(fn);
1.207 otto 408: fatal("fdopen %s: %s", fn, strerror(saved_errno));
1.1 jfb 409: }
410:
1.28 jfb 411: if (rfp->rf_head != NULL)
412: rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
413: else
414: numbuf[0] = '\0';
415:
1.1 jfb 416: fprintf(fp, "head\t%s;\n", numbuf);
1.35 jfb 417:
418: if (rfp->rf_branch != NULL) {
419: rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
420: fprintf(fp, "branch\t%s;\n", numbuf);
421: }
422:
1.29 jfb 423: fputs("access", fp);
424: TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
425: fprintf(fp, "\n\t%s", ap->ra_name);
426: }
427: fputs(";\n", fp);
1.1 jfb 428:
1.85 joris 429: fprintf(fp, "symbols");
1.1 jfb 430: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
431: rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
1.172 joris 432: if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) ||
433: strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) ||
434: strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf))
435: fatal("rcs_write: string overflow");
1.85 joris 436: fprintf(fp, "\n\t%s", buf);
1.1 jfb 437: }
438: fprintf(fp, ";\n");
439:
1.75 joris 440: fprintf(fp, "locks");
441: TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
442: rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
443: fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
444: }
445:
446: fprintf(fp, ";");
1.1 jfb 447:
1.26 jfb 448: if (rfp->rf_flags & RCS_SLOCK)
1.1 jfb 449: fprintf(fp, " strict;");
450: fputc('\n', fp);
451:
1.129 xsa 452: fputs("comment\t@", fp);
1.42 jfb 453: if (rfp->rf_comment != NULL) {
1.58 xsa 454: rcs_strprint((const u_char *)rfp->rf_comment,
455: strlen(rfp->rf_comment), fp);
1.42 jfb 456: fputs("@;\n", fp);
1.129 xsa 457: } else
458: fputs("# @;\n", fp);
1.1 jfb 459:
1.42 jfb 460: if (rfp->rf_expand != NULL) {
461: fputs("expand @", fp);
1.58 xsa 462: rcs_strprint((const u_char *)rfp->rf_expand,
463: strlen(rfp->rf_expand), fp);
1.42 jfb 464: fputs("@;\n", fp);
465: }
1.1 jfb 466:
1.46 jfb 467: fputs("\n\n", fp);
1.1 jfb 468:
469: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
470: fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
471: sizeof(numbuf)));
472: fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
1.44 jfb 473: rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
1.1 jfb 474: rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
475: rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
476: fprintf(fp, "\tauthor %s;\tstate %s;\n",
477: rdp->rd_author, rdp->rd_state);
1.46 jfb 478: fputs("branches", fp);
479: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.232 tobias 480: fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
1.46 jfb 481: sizeof(numbuf)));
482: }
483: fputs(";\n", fp);
1.1 jfb 484: fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
485: numbuf, sizeof(numbuf)));
486: }
487:
1.42 jfb 488: fputs("\ndesc\n@", fp);
1.141 ray 489: if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
1.100 xsa 490: rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
491: if (rfp->rf_desc[len-1] != '\n')
492: fputc('\n', fp);
493: }
494: fputs("@\n", fp);
1.1 jfb 495:
496: /* deltatexts */
497: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.100 xsa 498: fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
1.1 jfb 499: sizeof(numbuf)));
1.42 jfb 500: fputs("log\n@", fp);
1.100 xsa 501: if (rdp->rd_log != NULL) {
502: len = strlen(rdp->rd_log);
503: rcs_strprint((const u_char *)rdp->rd_log, len, fp);
504: if (rdp->rd_log[len-1] != '\n')
505: fputc('\n', fp);
506: }
1.42 jfb 507: fputs("@\ntext\n@", fp);
1.100 xsa 508: if (rdp->rd_text != NULL) {
509: rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
510: }
511: fputs("@\n", fp);
1.1 jfb 512: }
1.207 otto 513: if (fchmod(fd, rfp->rf_mode) == -1) {
514: saved_errno = errno;
515: (void)unlink(fn);
516: fatal("fchmod %s: %s", fn, strerror(saved_errno));
517: }
518:
1.172 joris 519: (void)fclose(fp);
1.73 joris 520:
1.207 otto 521: if (rename(fn, rfp->rf_path) == -1) {
522: saved_errno = errno;
1.172 joris 523: (void)unlink(fn);
1.207 otto 524: fatal("rename(%s, %s): %s", fn, rfp->rf_path,
525: strerror(saved_errno));
1.172 joris 526: }
1.73 joris 527:
1.172 joris 528: rfp->rf_flags |= RCS_SYNCED;
1.73 joris 529:
1.172 joris 530: if (fn != NULL)
531: xfree(fn);
1.1 jfb 532: }
533:
534: /*
1.43 jfb 535: * rcs_head_get()
536: *
537: * Retrieve the revision number of the head revision for the RCS file <file>.
538: */
1.180 joris 539: RCSNUM *
1.43 jfb 540: rcs_head_get(RCSFILE *file)
541: {
1.213 niallo 542: struct rcs_branch *brp;
543: struct rcs_delta *rdp;
544: RCSNUM *rev, *rootrev;
1.180 joris 545:
1.234 tobias 546: if (file->rf_head == NULL)
547: return NULL;
548:
1.213 niallo 549: rev = rcsnum_alloc();
1.180 joris 550: if (file->rf_branch != NULL) {
1.213 niallo 551: /* we have a default branch, use that to calculate the
552: * real HEAD*/
553: rootrev = rcsnum_alloc();
554: rcsnum_cpy(file->rf_branch, rootrev, 2);
555: if ((rdp = rcs_findrev(file, rootrev)) == NULL)
556: fatal("rcs_head_get: could not find root revision");
557:
558: /* HEAD should be the last revision on the default branch */
559: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
560: if (TAILQ_NEXT(brp, rb_list) == NULL)
561: break;
562: }
563: rcsnum_free(rootrev);
1.226 tobias 564:
565: if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
566: fatal("rcs_head_get: could not find branch revision");
567: while (rdp->rd_next->rn_len != 0)
568: if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
569: fatal("rcs_head_get: could not find "
570: "next branch revision");
571:
572: rcsnum_cpy(rdp->rd_num, rev, 0);
1.195 joris 573: } else {
574: rcsnum_cpy(file->rf_head, rev, 0);
1.180 joris 575: }
576:
1.195 joris 577: return (rev);
1.43 jfb 578: }
579:
580: /*
581: * rcs_head_set()
582: *
583: * Set the revision number of the head revision for the RCS file <file> to
584: * <rev>, which must reference a valid revision within the file.
585: */
586: int
1.117 niallo 587: rcs_head_set(RCSFILE *file, RCSNUM *rev)
1.43 jfb 588: {
1.167 ray 589: if (rcs_findrev(file, rev) == NULL)
1.43 jfb 590: return (-1);
591:
1.111 joris 592: if (file->rf_head == NULL)
593: file->rf_head = rcsnum_alloc();
1.43 jfb 594:
1.111 joris 595: rcsnum_cpy(rev, file->rf_head, 0);
1.70 moritz 596: file->rf_flags &= ~RCS_SYNCED;
1.43 jfb 597: return (0);
598: }
599:
600:
601: /*
1.35 jfb 602: * rcs_branch_get()
603: *
604: * Retrieve the default branch number for the RCS file <file>.
605: * Returns the number on success. If NULL is returned, then there is no
606: * default branch for this file.
607: */
1.60 xsa 608: const RCSNUM *
1.35 jfb 609: rcs_branch_get(RCSFILE *file)
610: {
611: return (file->rf_branch);
612: }
613:
614: /*
615: * rcs_branch_set()
616: *
617: * Set the default branch for the RCS file <file> to <bnum>.
618: * Returns 0 on success, -1 on failure.
619: */
620: int
621: rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
622: {
1.111 joris 623: if (file->rf_branch == NULL)
624: file->rf_branch = rcsnum_alloc();
1.35 jfb 625:
1.111 joris 626: rcsnum_cpy(bnum, file->rf_branch, 0);
1.70 moritz 627: file->rf_flags &= ~RCS_SYNCED;
1.35 jfb 628: return (0);
629: }
630:
631: /*
1.29 jfb 632: * rcs_access_add()
633: *
634: * Add the login name <login> to the access list for the RCS file <file>.
635: * Returns 0 on success, or -1 on failure.
636: */
637: int
638: rcs_access_add(RCSFILE *file, const char *login)
639: {
640: struct rcs_access *ap;
641:
642: /* first look for duplication */
643: TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
644: if (strcmp(ap->ra_name, login) == 0) {
1.32 jfb 645: rcs_errno = RCS_ERR_DUPENT;
1.29 jfb 646: return (-1);
647: }
648: }
649:
1.161 ray 650: ap = xmalloc(sizeof(*ap));
1.110 joris 651: ap->ra_name = xstrdup(login);
1.29 jfb 652: TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
653:
654: /* not synced anymore */
655: file->rf_flags &= ~RCS_SYNCED;
656: return (0);
657: }
658:
659: /*
660: * rcs_access_remove()
661: *
662: * Remove an entry with login name <login> from the access list of the RCS
663: * file <file>.
664: * Returns 0 on success, or -1 on failure.
665: */
666: int
667: rcs_access_remove(RCSFILE *file, const char *login)
668: {
669: struct rcs_access *ap;
670:
671: TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
672: if (strcmp(ap->ra_name, login) == 0)
673: break;
674:
675: if (ap == NULL) {
1.32 jfb 676: rcs_errno = RCS_ERR_NOENT;
1.29 jfb 677: return (-1);
678: }
679:
680: TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
1.110 joris 681: xfree(ap->ra_name);
682: xfree(ap);
1.29 jfb 683:
684: /* not synced anymore */
685: file->rf_flags &= ~RCS_SYNCED;
686: return (0);
687: }
688:
689: /*
1.26 jfb 690: * rcs_sym_add()
1.1 jfb 691: *
692: * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
693: * is named <sym> and is bound to the RCS revision <snum>.
694: * Returns 0 on success, or -1 on failure.
695: */
696: int
1.26 jfb 697: rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
1.1 jfb 698: {
699: struct rcs_sym *symp;
700:
1.47 jfb 701: if (!rcs_sym_check(sym)) {
702: rcs_errno = RCS_ERR_BADSYM;
1.69 moritz 703: return (-1);
1.47 jfb 704: }
705:
1.1 jfb 706: /* first look for duplication */
707: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
708: if (strcmp(symp->rs_name, sym) == 0) {
1.32 jfb 709: rcs_errno = RCS_ERR_DUPENT;
1.1 jfb 710: return (-1);
711: }
712: }
713:
1.161 ray 714: symp = xmalloc(sizeof(*symp));
1.110 joris 715: symp->rs_name = xstrdup(sym);
1.111 joris 716: symp->rs_num = rcsnum_alloc();
1.1 jfb 717: rcsnum_cpy(snum, symp->rs_num, 0);
718:
719: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
720:
721: /* not synced anymore */
1.26 jfb 722: rfp->rf_flags &= ~RCS_SYNCED;
1.1 jfb 723: return (0);
724: }
725:
726: /*
1.27 jfb 727: * rcs_sym_remove()
728: *
729: * Remove the symbol with name <sym> from the symbol list for the RCS file
730: * <file>. If no such symbol is found, the call fails and returns with an
731: * error.
732: * Returns 0 on success, or -1 on failure.
733: */
734: int
735: rcs_sym_remove(RCSFILE *file, const char *sym)
736: {
737: struct rcs_sym *symp;
738:
1.47 jfb 739: if (!rcs_sym_check(sym)) {
740: rcs_errno = RCS_ERR_BADSYM;
1.69 moritz 741: return (-1);
1.47 jfb 742: }
743:
1.27 jfb 744: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
745: if (strcmp(symp->rs_name, sym) == 0)
746: break;
747:
748: if (symp == NULL) {
1.32 jfb 749: rcs_errno = RCS_ERR_NOENT;
1.27 jfb 750: return (-1);
751: }
752:
753: TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
1.110 joris 754: xfree(symp->rs_name);
1.27 jfb 755: rcsnum_free(symp->rs_num);
1.110 joris 756: xfree(symp);
1.27 jfb 757:
758: /* not synced anymore */
759: file->rf_flags &= ~RCS_SYNCED;
760: return (0);
1.184 xsa 761: }
762:
763: /*
764: * rcs_sym_get()
765: *
766: * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
767: *
768: * Returns a pointer to the symbol on success, or NULL on failure.
769: */
770: struct rcs_sym *
771: rcs_sym_get(RCSFILE *file, const char *sym)
772: {
773: struct rcs_sym *symp;
774:
775: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
776: if (strcmp(symp->rs_name, sym) == 0)
777: return (symp);
778:
779: return (NULL);
1.27 jfb 780: }
781:
782: /*
783: * rcs_sym_getrev()
784: *
785: * Retrieve the RCS revision number associated with the symbol <sym> for the
786: * RCS file <file>. The returned value is a dynamically-allocated copy and
787: * should be freed by the caller once they are done with it.
788: * Returns the RCSNUM on success, or NULL on failure.
789: */
1.60 xsa 790: RCSNUM *
1.27 jfb 791: rcs_sym_getrev(RCSFILE *file, const char *sym)
792: {
793: RCSNUM *num;
794: struct rcs_sym *symp;
795:
1.47 jfb 796: if (!rcs_sym_check(sym)) {
797: rcs_errno = RCS_ERR_BADSYM;
798: return (NULL);
799: }
800:
1.176 joris 801: if (!strcmp(sym, RCS_HEAD_BRANCH)) {
802: num = rcsnum_alloc();
803: rcsnum_cpy(file->rf_head, num, 0);
804: return (num);
805: }
806:
1.27 jfb 807: num = NULL;
808: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
809: if (strcmp(symp->rs_name, sym) == 0)
810: break;
811:
1.111 joris 812: if (symp == NULL) {
1.36 jfb 813: rcs_errno = RCS_ERR_NOENT;
1.111 joris 814: } else {
815: num = rcsnum_alloc();
816: rcsnum_cpy(symp->rs_num, num, 0);
1.27 jfb 817: }
818:
819: return (num);
1.47 jfb 820: }
821:
822: /*
823: * rcs_sym_check()
824: *
825: * Check the RCS symbol name <sym> for any unsupported characters.
826: * Returns 1 if the tag is correct, 0 if it isn't valid.
827: */
828: int
829: rcs_sym_check(const char *sym)
830: {
831: int ret;
832: const char *cp;
833:
834: ret = 1;
835: cp = sym;
836: if (!isalpha(*cp++))
837: return (0);
838:
839: for (; *cp != '\0'; cp++)
840: if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
841: ret = 0;
842: break;
843: }
844:
845: return (ret);
1.30 jfb 846: }
847:
848: /*
849: * rcs_lock_getmode()
850: *
851: * Retrieve the locking mode of the RCS file <file>.
852: */
853: int
854: rcs_lock_getmode(RCSFILE *file)
855: {
856: return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
857: }
858:
859: /*
860: * rcs_lock_setmode()
861: *
862: * Set the locking mode of the RCS file <file> to <mode>, which must either
863: * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
864: * Returns the previous mode on success, or -1 on failure.
865: */
866: int
867: rcs_lock_setmode(RCSFILE *file, int mode)
868: {
869: int pmode;
870: pmode = rcs_lock_getmode(file);
871:
872: if (mode == RCS_LOCK_STRICT)
873: file->rf_flags |= RCS_SLOCK;
874: else if (mode == RCS_LOCK_LOOSE)
875: file->rf_flags &= ~RCS_SLOCK;
1.145 xsa 876: else
877: fatal("rcs_lock_setmode: invalid mode `%d'", mode);
1.30 jfb 878:
1.70 moritz 879: file->rf_flags &= ~RCS_SYNCED;
1.30 jfb 880: return (pmode);
1.27 jfb 881: }
882:
883: /*
1.40 jfb 884: * rcs_lock_add()
885: *
886: * Add an RCS lock for the user <user> on revision <rev>.
887: * Returns 0 on success, or -1 on failure.
888: */
889: int
890: rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
891: {
892: struct rcs_lock *lkp;
893:
894: /* first look for duplication */
895: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
1.168 deraadt 896: if (strcmp(lkp->rl_name, user) == 0 &&
897: rcsnum_cmp(rev, lkp->rl_num, 0) == 0) {
1.40 jfb 898: rcs_errno = RCS_ERR_DUPENT;
899: return (-1);
900: }
901: }
902:
1.161 ray 903: lkp = xmalloc(sizeof(*lkp));
1.110 joris 904: lkp->rl_name = xstrdup(user);
1.111 joris 905: lkp->rl_num = rcsnum_alloc();
1.68 moritz 906: rcsnum_cpy(rev, lkp->rl_num, 0);
1.40 jfb 907:
908: TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list);
909:
910: /* not synced anymore */
911: file->rf_flags &= ~RCS_SYNCED;
912: return (0);
913: }
914:
915:
916: /*
917: * rcs_lock_remove()
918: *
919: * Remove the RCS lock on revision <rev>.
920: * Returns 0 on success, or -1 on failure.
921: */
922: int
1.109 joris 923: rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
1.40 jfb 924: {
925: struct rcs_lock *lkp;
926:
1.109 joris 927: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
1.168 deraadt 928: if (strcmp(lkp->rl_name, user) == 0 &&
929: rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
1.40 jfb 930: break;
1.109 joris 931: }
1.40 jfb 932:
933: if (lkp == NULL) {
934: rcs_errno = RCS_ERR_NOENT;
935: return (-1);
936: }
937:
938: TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list);
939: rcsnum_free(lkp->rl_num);
1.110 joris 940: xfree(lkp->rl_name);
941: xfree(lkp);
1.40 jfb 942:
943: /* not synced anymore */
944: file->rf_flags &= ~RCS_SYNCED;
945: return (0);
946: }
947:
948: /*
1.27 jfb 949: * rcs_desc_get()
950: *
951: * Retrieve the description for the RCS file <file>.
952: */
1.57 xsa 953: const char *
1.27 jfb 954: rcs_desc_get(RCSFILE *file)
955: {
956: return (file->rf_desc);
957: }
958:
959: /*
960: * rcs_desc_set()
961: *
962: * Set the description for the RCS file <file>.
963: */
1.149 xsa 964: void
1.27 jfb 965: rcs_desc_set(RCSFILE *file, const char *desc)
966: {
967: char *tmp;
968:
1.110 joris 969: tmp = xstrdup(desc);
1.27 jfb 970: if (file->rf_desc != NULL)
1.110 joris 971: xfree(file->rf_desc);
1.27 jfb 972: file->rf_desc = tmp;
973: file->rf_flags &= ~RCS_SYNCED;
1.51 jfb 974: }
975:
976: /*
977: * rcs_comment_lookup()
978: *
979: * Lookup the assumed comment leader based on a file's suffix.
980: * Returns a pointer to the string on success, or NULL on failure.
981: */
1.57 xsa 982: const char *
1.51 jfb 983: rcs_comment_lookup(const char *filename)
984: {
985: int i;
986: const char *sp;
987:
988: if ((sp = strrchr(filename, '.')) == NULL) {
989: rcs_errno = RCS_ERR_NOENT;
990: return (NULL);
991: }
992: sp++;
993:
994: for (i = 0; i < (int)NB_COMTYPES; i++)
995: if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
996: return (rcs_comments[i].rc_cstr);
997: return (NULL);
1.27 jfb 998: }
999:
1.33 jfb 1000: /*
1001: * rcs_comment_get()
1002: *
1003: * Retrieve the comment leader for the RCS file <file>.
1004: */
1.57 xsa 1005: const char *
1.33 jfb 1006: rcs_comment_get(RCSFILE *file)
1007: {
1008: return (file->rf_comment);
1009: }
1010:
1011: /*
1012: * rcs_comment_set()
1013: *
1014: * Set the comment leader for the RCS file <file>.
1015: */
1.150 xsa 1016: void
1.33 jfb 1017: rcs_comment_set(RCSFILE *file, const char *comment)
1018: {
1019: char *tmp;
1020:
1.110 joris 1021: tmp = xstrdup(comment);
1.33 jfb 1022: if (file->rf_comment != NULL)
1.110 joris 1023: xfree(file->rf_comment);
1.33 jfb 1024: file->rf_comment = tmp;
1025: file->rf_flags &= ~RCS_SYNCED;
1.40 jfb 1026: }
1027:
1.94 joris 1028: int
1.218 tobias 1029: rcs_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines,
1030: struct cvs_line **alines, struct rcs_delta *rdp)
1.5 vincent 1031: {
1.209 otto 1032: u_char op;
1033: char *ep;
1.94 joris 1034: struct cvs_line *lp, *dlp, *ndlp;
1.5 vincent 1035: int i, lineno, nbln;
1.193 niallo 1036: u_char tmp;
1.1 jfb 1037:
1.94 joris 1038: dlp = TAILQ_FIRST(&(dlines->l_lines));
1039: lp = TAILQ_FIRST(&(plines->l_lines));
1.1 jfb 1040:
1041: /* skip first bogus line */
1.94 joris 1042: for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1043: lp = TAILQ_NEXT(lp, l_list)) {
1.193 niallo 1044: if (lp->l_len < 2)
1045: fatal("line too short, RCS patch seems broken");
1.94 joris 1046: op = *(lp->l_line);
1.193 niallo 1047: /* NUL-terminate line buffer for strtol() safety. */
1048: tmp = lp->l_line[lp->l_len - 1];
1049: lp->l_line[lp->l_len - 1] = '\0';
1.209 otto 1050: lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
1.193 niallo 1051: if (lineno - 1 > dlines->l_nblines || lineno < 0) {
1052: fatal("invalid line specification in RCS patch");
1053: }
1.1 jfb 1054: ep++;
1055: nbln = (int)strtol(ep, &ep, 10);
1.193 niallo 1056: /* Restore the last byte of the buffer */
1057: lp->l_line[lp->l_len - 1] = tmp;
1058: if (nbln < 0)
1.132 niallo 1059: fatal("invalid line number specification in RCS patch");
1.1 jfb 1060:
1061: /* find the appropriate line */
1062: for (;;) {
1063: if (dlp == NULL)
1064: break;
1.94 joris 1065: if (dlp->l_lineno == lineno)
1.1 jfb 1066: break;
1.94 joris 1067: if (dlp->l_lineno > lineno) {
1068: dlp = TAILQ_PREV(dlp, cvs_tqh, l_list);
1069: } else if (dlp->l_lineno < lineno) {
1.133 niallo 1070: if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
1.168 deraadt 1071: ndlp->l_lineno > lineno)
1.1 jfb 1072: break;
1073: dlp = ndlp;
1074: }
1075: }
1.132 niallo 1076: if (dlp == NULL)
1077: fatal("can't find referenced line in RCS patch");
1.1 jfb 1078:
1079: if (op == 'd') {
1080: for (i = 0; (i < nbln) && (dlp != NULL); i++) {
1.94 joris 1081: ndlp = TAILQ_NEXT(dlp, l_list);
1082: TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
1.218 tobias 1083: if (alines != NULL && dlp->l_line != NULL) {
1084: dlp->l_delta = rdp;
1085: alines[dlp->l_lineno_orig - 1] =
1086: dlp;
1087: } else
1088: xfree(dlp);
1.1 jfb 1089: dlp = ndlp;
1.133 niallo 1090: /* last line is gone - reset dlp */
1091: if (dlp == NULL) {
1092: ndlp = TAILQ_LAST(&(dlines->l_lines),
1093: cvs_tqh);
1094: dlp = ndlp;
1095: }
1.1 jfb 1096: }
1.14 deraadt 1097: } else if (op == 'a') {
1.1 jfb 1098: for (i = 0; i < nbln; i++) {
1099: ndlp = lp;
1.94 joris 1100: lp = TAILQ_NEXT(lp, l_list);
1.132 niallo 1101: if (lp == NULL)
1102: fatal("truncated RCS patch");
1.94 joris 1103: TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
1.218 tobias 1104: if (alines != NULL) {
1105: if (lp->l_needsfree == 1)
1106: xfree(lp->l_line);
1107: lp->l_line = NULL;
1108: lp->l_needsfree = 0;
1109: }
1.225 tobias 1110: lp->l_delta = rdp;
1.94 joris 1111: TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
1112: lp, l_list);
1.1 jfb 1113: dlp = lp;
1114:
1115: /* we don't want lookup to block on those */
1.94 joris 1116: lp->l_lineno = lineno;
1.1 jfb 1117:
1118: lp = ndlp;
1119: }
1.132 niallo 1120: } else
1121: fatal("unknown RCS patch operation `%c'", op);
1.1 jfb 1122:
1123: /* last line of the patch, done */
1.94 joris 1124: if (lp->l_lineno == plines->l_nblines)
1.1 jfb 1125: break;
1126: }
1127:
1128: /* once we're done patching, rebuild the line numbers */
1.2 vincent 1129: lineno = 0;
1.94 joris 1130: TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
1131: lp->l_lineno = lineno++;
1132: dlines->l_nblines = lineno - 1;
1.1 jfb 1133:
1.5 vincent 1134: return (0);
1.241 ! joris 1135: }
! 1136:
! 1137: void
! 1138: rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
! 1139: {
! 1140: struct cvs_lines *plines;
! 1141: struct cvs_line *lp;
! 1142: int added, i, lineno, nbln, removed;
! 1143: char op, *ep;
! 1144: u_char tmp;
! 1145:
! 1146: added = removed = 0;
! 1147:
! 1148: plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
! 1149: lp = TAILQ_FIRST(&(plines->l_lines));
! 1150:
! 1151: /* skip first bogus line */
! 1152: for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
! 1153: lp = TAILQ_NEXT(lp, l_list)) {
! 1154: if (lp->l_len < 2)
! 1155: fatal("line too short, RCS patch seems broken");
! 1156: op = *(lp->l_line);
! 1157: /* NUL-terminate line buffer for strtol() safety. */
! 1158: tmp = lp->l_line[lp->l_len - 1];
! 1159: lp->l_line[lp->l_len - 1] = '\0';
! 1160: lineno = (int)strtol((lp->l_line + 1), &ep, 10);
! 1161: ep++;
! 1162: nbln = (int)strtol(ep, &ep, 10);
! 1163: /* Restore the last byte of the buffer */
! 1164: lp->l_line[lp->l_len - 1] = tmp;
! 1165: if (nbln < 0)
! 1166: fatal("invalid line number specification in RCS patch");
! 1167:
! 1168: if (op == 'a') {
! 1169: added += nbln;
! 1170: for (i = 0; i < nbln; i++) {
! 1171: lp = TAILQ_NEXT(lp, l_list);
! 1172: if (lp == NULL)
! 1173: fatal("truncated RCS patch");
! 1174: }
! 1175: }
! 1176: else if (op == 'd')
! 1177: removed += nbln;
! 1178: else
! 1179: fatal("unknown RCS patch operation '%c'", op);
! 1180: }
! 1181:
! 1182: *ladded = added;
! 1183: *lremoved = removed;
1.1 jfb 1184: }
1185:
1186: /*
1.52 jfb 1187: * rcs_rev_add()
1188: *
1.53 jfb 1189: * Add a revision to the RCS file <rf>. The new revision's number can be
1190: * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1191: * new revision will have a number equal to the previous head revision plus
1192: * one). The <msg> argument specifies the log message for that revision, and
1193: * <date> specifies the revision's date (a value of -1 is
1194: * equivalent to using the current time).
1.96 xsa 1195: * If <username> is NULL, set the author for this revision to the current user.
1.90 niallo 1196: * Otherwise, set it to <username>.
1.52 jfb 1197: * Returns 0 on success, or -1 on failure.
1198: */
1199: int
1.90 niallo 1200: rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1201: const char *username)
1.52 jfb 1202: {
1203: time_t now;
1.222 joris 1204: RCSNUM *root;
1.52 jfb 1205: struct passwd *pw;
1.180 joris 1206: struct rcs_branch *brp;
1.83 joris 1207: struct rcs_delta *ordp, *rdp;
1208:
1.52 jfb 1209: if (rev == RCS_HEAD_REV) {
1.101 niallo 1210: if (rf->rf_flags & RCS_CREATE) {
1211: if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1212: return (-1);
1.111 joris 1213: rf->rf_head = rcsnum_alloc();
1.101 niallo 1214: rcsnum_cpy(rev, rf->rf_head, 0);
1215: } else {
1216: rev = rcsnum_inc(rf->rf_head);
1217: }
1.83 joris 1218: } else {
1219: if ((rdp = rcs_findrev(rf, rev)) != NULL) {
1220: rcs_errno = RCS_ERR_DUPENT;
1221: return (-1);
1222: }
1.52 jfb 1223: }
1224:
1.112 xsa 1225: if ((pw = getpwuid(getuid())) == NULL)
1226: fatal("getpwuid failed");
1.52 jfb 1227:
1.161 ray 1228: rdp = xcalloc(1, sizeof(*rdp));
1.52 jfb 1229:
1230: TAILQ_INIT(&(rdp->rd_branches));
1231:
1.111 joris 1232: rdp->rd_num = rcsnum_alloc();
1.52 jfb 1233: rcsnum_cpy(rev, rdp->rd_num, 0);
1.83 joris 1234:
1.111 joris 1235: rdp->rd_next = rcsnum_alloc();
1.92 joris 1236:
1.90 niallo 1237: if (username == NULL)
1238: username = pw->pw_name;
1239:
1.110 joris 1240: rdp->rd_author = xstrdup(username);
1241: rdp->rd_state = xstrdup(RCS_STATE_EXP);
1242: rdp->rd_log = xstrdup(msg);
1.52 jfb 1243:
1.53 jfb 1244: if (date != (time_t)(-1))
1245: now = date;
1246: else
1247: time(&now);
1.52 jfb 1248: gmtime_r(&now, &(rdp->rd_date));
1249:
1.180 joris 1250: if (RCSNUM_ISBRANCHREV(rev))
1251: TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1252: else
1253: TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1.52 jfb 1254: rf->rf_ndelta++;
1.81 niallo 1255:
1.180 joris 1256: if (!(rf->rf_flags & RCS_CREATE)) {
1257: if (RCSNUM_ISBRANCHREV(rev)) {
1.222 joris 1258: root = rcsnum_branch_root(rev);
1.180 joris 1259: brp = xmalloc(sizeof(*brp));
1260: brp->rb_num = rcsnum_alloc();
1261: rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1.222 joris 1262: if ((ordp = rcs_findrev(rf, root)) == NULL)
1263: fatal("root node not found");
1264: if (TAILQ_EMPTY(&(ordp->rd_branches))) {
1265: TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1266: brp, rb_list);
1267: }
1268:
1.208 otto 1269: ordp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
1.222 joris 1270: if (RCSNUM_ISBRANCHREV(ordp->rd_num))
1271: rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1.180 joris 1272: } else {
1273: ordp = TAILQ_NEXT(rdp, rd_list);
1274: rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1275: }
1276: }
1277:
1.64 niallo 1278: /* not synced anymore */
1279: rf->rf_flags &= ~RCS_SYNCED;
1.52 jfb 1280:
1281: return (0);
1282: }
1283:
1284: /*
1285: * rcs_rev_remove()
1286: *
1287: * Remove the revision whose number is <rev> from the RCS file <rf>.
1288: */
1289: int
1290: rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1291: {
1.194 joris 1292: char *path_tmp1, *path_tmp2;
1.166 joris 1293: struct rcs_delta *rdp, *prevrdp, *nextrdp;
1.209 otto 1294: BUF *prevbuf, *newdiff, *newdeltatext;
1.166 joris 1295:
1.52 jfb 1296: if (rev == RCS_HEAD_REV)
1297: rev = rf->rf_head;
1298:
1299: /* do we actually have that revision? */
1300: if ((rdp = rcs_findrev(rf, rev)) == NULL) {
1301: rcs_errno = RCS_ERR_NOENT;
1.166 joris 1302: return (-1);
1303: }
1304:
1305: /*
1306: * This is confusing, the previous delta is next in the TAILQ list.
1307: * the next delta is the previous one in the TAILQ list.
1308: *
1309: * When the HEAD revision got specified, nextrdp will be NULL.
1310: * When the first revision got specified, prevrdp will be NULL.
1311: */
1312: prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
1313: nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list);
1314:
1315: newdeltatext = NULL;
1.209 otto 1316: prevbuf = NULL;
1.166 joris 1317:
1.168 deraadt 1318: if (prevrdp != NULL && nextrdp != NULL) {
1.166 joris 1319: newdiff = cvs_buf_alloc(64, BUF_AUTOEXT);
1320:
1321: /* calculate new diff */
1.172 joris 1322: (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1.196 niallo 1323: rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1.166 joris 1324:
1.172 joris 1325: (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1.196 niallo 1326: rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1.166 joris 1327:
1328: diff_format = D_RCSDIFF;
1.172 joris 1329: if (cvs_diffreg(path_tmp1, path_tmp2, newdiff) == D_ERROR)
1330: fatal("rcs_diffreg failed");
1.166 joris 1331:
1.194 joris 1332: newdeltatext = newdiff;
1.168 deraadt 1333: } else if (nextrdp == NULL && prevrdp != NULL) {
1.194 joris 1334: newdeltatext = prevbuf;
1.166 joris 1335: }
1336:
1337: if (newdeltatext != NULL) {
1338: if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1339: fatal("error setting new deltatext");
1340: }
1341:
1342: TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1343:
1344: /* update pointers */
1.168 deraadt 1345: if (prevrdp != NULL && nextrdp != NULL) {
1.166 joris 1346: rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1347: } else if (prevrdp != NULL) {
1.170 xsa 1348: if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1349: fatal("rcs_head_set failed");
1.166 joris 1350: } else if (nextrdp != NULL) {
1351: rcsnum_free(nextrdp->rd_next);
1352: nextrdp->rd_next = rcsnum_alloc();
1.52 jfb 1353: } else {
1.166 joris 1354: rcsnum_free(rf->rf_head);
1355: rf->rf_head = NULL;
1.52 jfb 1356: }
1357:
1.166 joris 1358: rf->rf_ndelta--;
1359: rf->rf_flags &= ~RCS_SYNCED;
1360:
1361: rcs_freedelta(rdp);
1362:
1363: if (newdeltatext != NULL)
1364: xfree(newdeltatext);
1.52 jfb 1365:
1.172 joris 1366: if (path_tmp1 != NULL)
1367: xfree(path_tmp1);
1368: if (path_tmp2 != NULL)
1369: xfree(path_tmp2);
1370:
1.166 joris 1371: return (0);
1.52 jfb 1372: }
1373:
1374: /*
1.1 jfb 1375: * rcs_findrev()
1376: *
1377: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1378: * The revision number is given in <rev>.
1.156 joris 1379: *
1.1 jfb 1380: * Returns a pointer to the delta on success, or NULL on failure.
1381: */
1.102 xsa 1382: struct rcs_delta *
1.117 niallo 1383: rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1.1 jfb 1384: {
1.178 joris 1385: int isbrev;
1.156 joris 1386: struct rcs_delta *rdp;
1.178 joris 1387:
1388: isbrev = RCSNUM_ISBRANCHREV(rev);
1.26 jfb 1389:
1.117 niallo 1390: /*
1391: * We need to do more parsing if the last revision in the linked list
1392: * is greater than the requested revision.
1393: */
1.156 joris 1394: rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1.168 deraadt 1395: if (rdp == NULL ||
1.178 joris 1396: (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1397: ((isbrev && rdp->rd_num->rn_len < 4) ||
1398: (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1.117 niallo 1399: rcs_parse_deltas(rfp, rev);
1400: }
1401:
1.156 joris 1402: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.178 joris 1403: if (rcsnum_differ(rdp->rd_num, rev))
1404: continue;
1405: else
1.156 joris 1406: return (rdp);
1407: }
1.1 jfb 1408:
1409: return (NULL);
1.20 jfb 1410: }
1411:
1412: /*
1.26 jfb 1413: * rcs_kwexp_set()
1414: *
1415: * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1416: */
1.162 xsa 1417: void
1.26 jfb 1418: rcs_kwexp_set(RCSFILE *file, int mode)
1419: {
1420: int i;
1421: char *tmp, buf[8] = "";
1422:
1423: if (RCS_KWEXP_INVAL(mode))
1.162 xsa 1424: return;
1.26 jfb 1425:
1426: i = 0;
1427: if (mode == RCS_KWEXP_NONE)
1428: buf[0] = 'b';
1429: else if (mode == RCS_KWEXP_OLD)
1430: buf[0] = 'o';
1431: else {
1432: if (mode & RCS_KWEXP_NAME)
1433: buf[i++] = 'k';
1434: if (mode & RCS_KWEXP_VAL)
1435: buf[i++] = 'v';
1436: if (mode & RCS_KWEXP_LKR)
1437: buf[i++] = 'l';
1438: }
1439:
1.110 joris 1440: tmp = xstrdup(buf);
1.27 jfb 1441: if (file->rf_expand != NULL)
1.147 ray 1442: xfree(file->rf_expand);
1.26 jfb 1443: file->rf_expand = tmp;
1.64 niallo 1444: /* not synced anymore */
1445: file->rf_flags &= ~RCS_SYNCED;
1.26 jfb 1446: }
1447:
1448: /*
1449: * rcs_kwexp_get()
1450: *
1451: * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1452: */
1453: int
1454: rcs_kwexp_get(RCSFILE *file)
1455: {
1456: return rcs_kflag_get(file->rf_expand);
1457: }
1458:
1459: /*
1.20 jfb 1460: * rcs_kflag_get()
1461: *
1462: * Get the keyword expansion mode from a set of character flags given in
1463: * <flags> and return the appropriate flag mask. In case of an error, the
1464: * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1465: */
1466: int
1467: rcs_kflag_get(const char *flags)
1468: {
1469: int fl;
1470: size_t len;
1471: const char *fp;
1472:
1473: fl = 0;
1.235 tobias 1474: if (!(len = strlen(flags)))
1475: return RCS_KWEXP_ERR;
1.20 jfb 1476:
1477: for (fp = flags; *fp != '\0'; fp++) {
1478: if (*fp == 'k')
1479: fl |= RCS_KWEXP_NAME;
1480: else if (*fp == 'v')
1481: fl |= RCS_KWEXP_VAL;
1482: else if (*fp == 'l')
1483: fl |= RCS_KWEXP_LKR;
1484: else if (*fp == 'o') {
1485: if (len != 1)
1486: fl |= RCS_KWEXP_ERR;
1487: fl |= RCS_KWEXP_OLD;
1488: } else if (*fp == 'b') {
1489: if (len != 1)
1490: fl |= RCS_KWEXP_ERR;
1.205 xsa 1491: fl |= RCS_KWEXP_NONE;
1.20 jfb 1492: } else /* unknown letter */
1493: fl |= RCS_KWEXP_ERR;
1494: }
1495:
1496: return (fl);
1.32 jfb 1497: }
1498:
1499: /*
1500: * rcs_errstr()
1501: *
1502: * Get the error string matching the RCS error code <code>.
1503: */
1.57 xsa 1504: const char *
1.32 jfb 1505: rcs_errstr(int code)
1506: {
1.50 jfb 1507: const char *esp;
1508:
1.168 deraadt 1509: if (code < 0 || (code >= (int)RCS_NERR && code != RCS_ERR_ERRNO))
1.50 jfb 1510: esp = NULL;
1511: else if (code == RCS_ERR_ERRNO)
1512: esp = strerror(errno);
1513: else
1514: esp = rcs_errstrs[code];
1515: return (esp);
1.1 jfb 1516: }
1517:
1.117 niallo 1518: /* rcs_parse_deltas()
1519: *
1520: * Parse deltas. If <rev> is not NULL, parse only as far as that
1521: * revision. If <rev> is NULL, parse all deltas.
1522: */
1.146 xsa 1523: static void
1.117 niallo 1524: rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev)
1525: {
1526: int ret;
1527: struct rcs_delta *enddelta;
1.146 xsa 1528:
1529: if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
1530: return;
1531:
1.117 niallo 1532: for (;;) {
1533: ret = rcs_parse_delta(rfp);
1534: if (rev != NULL) {
1535: enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1.155 ray 1536: if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
1.117 niallo 1537: break;
1538: }
1.180 joris 1539:
1.117 niallo 1540: if (ret == 0) {
1541: rfp->rf_flags |= PARSED_DELTAS;
1542: break;
1543: }
1544: else if (ret == -1)
1545: fatal("error parsing deltas");
1546: }
1547: }
1548:
1549: /* rcs_parse_deltatexts()
1550: *
1551: * Parse deltatexts. If <rev> is not NULL, parse only as far as that
1552: * revision. If <rev> is NULL, parse everything.
1553: */
1.146 xsa 1554: static void
1.117 niallo 1555: rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
1556: {
1557: int ret;
1558: struct rcs_delta *rdp;
1.146 xsa 1559:
1.168 deraadt 1560: if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
1561: (rfp->rf_flags & RCS_CREATE))
1.146 xsa 1562: return;
1563:
1.117 niallo 1564: if (!(rfp->rf_flags & PARSED_DESC))
1565: rcs_parse_desc(rfp, rev);
1566: for (;;) {
1567: if (rev != NULL) {
1568: rdp = rcs_findrev(rfp, rev);
1569: if (rdp->rd_text != NULL)
1570: break;
1571: else
1572: ret = rcs_parse_deltatext(rfp);
1573: } else
1574: ret = rcs_parse_deltatext(rfp);
1575: if (ret == 0) {
1576: rfp->rf_flags |= PARSED_DELTATEXTS;
1577: break;
1578: }
1.146 xsa 1579: else if (ret == -1)
1.117 niallo 1580: fatal("problem parsing deltatexts");
1581: }
1582: }
1583:
1584: /* rcs_parse_desc()
1585: *
1586: * Parse RCS description.
1587: */
1.148 xsa 1588: static void
1.117 niallo 1589: rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev)
1590: {
1591: int ret = 0;
1.148 xsa 1592:
1593: if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE))
1594: return;
1.122 reyk 1595: if (!(rfp->rf_flags & PARSED_DELTAS))
1.117 niallo 1596: rcs_parse_deltas(rfp, rev);
1597: /* do parsing */
1598: ret = rcs_gettok(rfp);
1.148 xsa 1599: if (ret != RCS_TOK_DESC)
1600: fatal("token `%s' found where RCS desc expected",
1.117 niallo 1601: RCS_TOKSTR(rfp));
1602:
1603: ret = rcs_gettok(rfp);
1.148 xsa 1604: if (ret != RCS_TOK_STRING)
1605: fatal("token `%s' found where RCS desc expected",
1.117 niallo 1606: RCS_TOKSTR(rfp));
1607:
1608: rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp));
1609: rfp->rf_flags |= PARSED_DESC;
1610: }
1611:
1.1 jfb 1612: /*
1.117 niallo 1613: * rcs_parse_init()
1.1 jfb 1614: *
1.117 niallo 1615: * Initial parsing of file <path>, which are in the RCS format.
1.167 ray 1616: * Just does admin section.
1.1 jfb 1617: */
1.167 ray 1618: static void
1.117 niallo 1619: rcs_parse_init(RCSFILE *rfp)
1.1 jfb 1620: {
1621: struct rcs_pdata *pdp;
1622:
1.26 jfb 1623: if (rfp->rf_flags & RCS_PARSED)
1.167 ray 1624: return;
1.1 jfb 1625:
1.161 ray 1626: pdp = xcalloc(1, sizeof(*pdp));
1.1 jfb 1627:
1.18 jfb 1628: pdp->rp_lines = 0;
1.1 jfb 1629: pdp->rp_pttype = RCS_TOK_ERR;
1630:
1.172 joris 1631: if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL)
1.216 xsa 1632: fatal("fdopen: `%s'", rfp->rf_path);
1.1 jfb 1633:
1.161 ray 1634: pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE);
1.1 jfb 1635: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 1636: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 1637:
1638: /* ditch the strict lock */
1.26 jfb 1639: rfp->rf_flags &= ~RCS_SLOCK;
1.1 jfb 1640: rfp->rf_pdata = pdp;
1641:
1.167 ray 1642: if (rcs_parse_admin(rfp) < 0) {
1.1 jfb 1643: rcs_freepdata(pdp);
1.117 niallo 1644: fatal("could not parse admin data");
1.1 jfb 1645: }
1646:
1.117 niallo 1647: if (rfp->rf_flags & RCS_PARSE_FULLY)
1648: rcs_parse_deltatexts(rfp, NULL);
1.1 jfb 1649:
1.117 niallo 1650: rfp->rf_flags |= RCS_SYNCED;
1.1 jfb 1651: }
1652:
1653: /*
1654: * rcs_parse_admin()
1655: *
1656: * Parse the administrative portion of an RCS file.
1.31 jfb 1657: * Returns the type of the first token found after the admin section on
1658: * success, or -1 on failure.
1.1 jfb 1659: */
1660: static int
1661: rcs_parse_admin(RCSFILE *rfp)
1662: {
1663: u_int i;
1664: int tok, ntok, hmask;
1665: struct rcs_key *rk;
1666:
1.180 joris 1667: rfp->rf_head = NULL;
1668: rfp->rf_branch = NULL;
1669:
1.1 jfb 1670: /* hmask is a mask of the headers already encountered */
1671: hmask = 0;
1672: for (;;) {
1673: tok = rcs_gettok(rfp);
1674: if (tok == RCS_TOK_ERR) {
1.50 jfb 1675: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1676: cvs_log(LP_ERR, "parse error in RCS admin section");
1.147 ray 1677: goto fail;
1.168 deraadt 1678: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.31 jfb 1679: /*
1680: * Assume this is the start of the first delta or
1681: * that we are dealing with an empty RCS file and
1682: * we just found the description.
1683: */
1.1 jfb 1684: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.31 jfb 1685: return (tok);
1.1 jfb 1686: }
1687:
1688: rk = NULL;
1.18 jfb 1689: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1690: if (rcs_keys[i].rk_id == tok)
1691: rk = &(rcs_keys[i]);
1692:
1693: if (hmask & (1 << tok)) {
1.50 jfb 1694: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1695: cvs_log(LP_ERR, "duplicate RCS key");
1.147 ray 1696: goto fail;
1.1 jfb 1697: }
1698: hmask |= (1 << tok);
1699:
1700: switch (tok) {
1701: case RCS_TOK_HEAD:
1702: case RCS_TOK_BRANCH:
1703: case RCS_TOK_COMMENT:
1704: case RCS_TOK_EXPAND:
1705: ntok = rcs_gettok(rfp);
1706: if (ntok == RCS_TOK_SCOLON)
1707: break;
1708: if (ntok != rk->rk_val) {
1.50 jfb 1709: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1710: cvs_log(LP_ERR,
1711: "invalid value type for RCS key `%s'",
1712: rk->rk_str);
1713: }
1714:
1715: if (tok == RCS_TOK_HEAD) {
1.111 joris 1716: if (rfp->rf_head == NULL)
1.28 jfb 1717: rfp->rf_head = rcsnum_alloc();
1.1 jfb 1718: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1719: rfp->rf_head);
1.14 deraadt 1720: } else if (tok == RCS_TOK_BRANCH) {
1.111 joris 1721: if (rfp->rf_branch == NULL)
1.35 jfb 1722: rfp->rf_branch = rcsnum_alloc();
1723: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1724: rfp->rf_branch) < 0)
1.147 ray 1725: goto fail;
1.14 deraadt 1726: } else if (tok == RCS_TOK_COMMENT) {
1.110 joris 1727: rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp));
1.14 deraadt 1728: } else if (tok == RCS_TOK_EXPAND) {
1.110 joris 1729: rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1730: }
1731:
1732: /* now get the expected semi-colon */
1733: ntok = rcs_gettok(rfp);
1734: if (ntok != RCS_TOK_SCOLON) {
1.50 jfb 1735: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1736: cvs_log(LP_ERR,
1737: "missing semi-colon after RCS `%s' key",
1.26 jfb 1738: rk->rk_str);
1.147 ray 1739: goto fail;
1.1 jfb 1740: }
1741: break;
1742: case RCS_TOK_ACCESS:
1.29 jfb 1743: if (rcs_parse_access(rfp) < 0)
1.147 ray 1744: goto fail;
1.1 jfb 1745: break;
1746: case RCS_TOK_SYMBOLS:
1.29 jfb 1747: if (rcs_parse_symbols(rfp) < 0)
1.147 ray 1748: goto fail;
1.1 jfb 1749: break;
1750: case RCS_TOK_LOCKS:
1.29 jfb 1751: if (rcs_parse_locks(rfp) < 0)
1.147 ray 1752: goto fail;
1.1 jfb 1753: break;
1754: default:
1.50 jfb 1755: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1756: cvs_log(LP_ERR,
1757: "unexpected token `%s' in RCS admin section",
1758: RCS_TOKSTR(rfp));
1.147 ray 1759: goto fail;
1.1 jfb 1760: }
1761: }
1762:
1.147 ray 1763: fail:
1764: return (-1);
1.1 jfb 1765: }
1766:
1767: /*
1768: * rcs_parse_delta()
1769: *
1770: * Parse an RCS delta section and allocate the structure to store that delta's
1771: * information in the <rfp> delta list.
1772: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1773: * -1 on error.
1774: */
1775: static int
1776: rcs_parse_delta(RCSFILE *rfp)
1777: {
1778: int ret, tok, ntok, hmask;
1779: u_int i;
1780: char *tokstr;
1.3 vincent 1781: RCSNUM *datenum;
1.1 jfb 1782: struct rcs_delta *rdp;
1783: struct rcs_key *rk;
1784:
1785: tok = rcs_gettok(rfp);
1.154 ray 1786: if (tok == RCS_TOK_DESC) {
1787: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1788: return (0);
1789: } else if (tok != RCS_TOK_NUM) {
1.52 jfb 1790: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1791: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1792: RCS_TOKSTR(rfp));
1793: return (-1);
1794: }
1.192 niallo 1795:
1796: rdp = xcalloc(1, sizeof(*rdp));
1797:
1798: rdp->rd_num = rcsnum_alloc();
1799: rdp->rd_next = rcsnum_alloc();
1800:
1801: TAILQ_INIT(&(rdp->rd_branches));
1802:
1.1 jfb 1803: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1804:
1805: hmask = 0;
1806: ret = 0;
1807: tokstr = NULL;
1808:
1809: for (;;) {
1810: tok = rcs_gettok(rfp);
1811: if (tok == RCS_TOK_ERR) {
1.50 jfb 1812: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1813: cvs_log(LP_ERR, "parse error in RCS delta section");
1814: rcs_freedelta(rdp);
1815: return (-1);
1.14 deraadt 1816: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1817: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1818: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1819: break;
1820: }
1821:
1822: rk = NULL;
1.18 jfb 1823: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1824: if (rcs_keys[i].rk_id == tok)
1825: rk = &(rcs_keys[i]);
1826:
1827: if (hmask & (1 << tok)) {
1.50 jfb 1828: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1829: cvs_log(LP_ERR, "duplicate RCS key");
1830: rcs_freedelta(rdp);
1831: return (-1);
1832: }
1833: hmask |= (1 << tok);
1834:
1835: switch (tok) {
1836: case RCS_TOK_DATE:
1837: case RCS_TOK_AUTHOR:
1838: case RCS_TOK_STATE:
1839: case RCS_TOK_NEXT:
1840: ntok = rcs_gettok(rfp);
1841: if (ntok == RCS_TOK_SCOLON) {
1842: if (rk->rk_flags & RCS_VOPT)
1843: break;
1844: else {
1.50 jfb 1845: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1846: cvs_log(LP_ERR, "missing mandatory "
1847: "value to RCS key `%s'",
1848: rk->rk_str);
1849: rcs_freedelta(rdp);
1850: return (-1);
1851: }
1852: }
1853:
1854: if (ntok != rk->rk_val) {
1.50 jfb 1855: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1856: cvs_log(LP_ERR,
1857: "invalid value type for RCS key `%s'",
1858: rk->rk_str);
1859: rcs_freedelta(rdp);
1860: return (-1);
1861: }
1862:
1863: if (tokstr != NULL)
1.110 joris 1864: xfree(tokstr);
1865: tokstr = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1866: /* now get the expected semi-colon */
1867: ntok = rcs_gettok(rfp);
1868: if (ntok != RCS_TOK_SCOLON) {
1.50 jfb 1869: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1870: cvs_log(LP_ERR,
1871: "missing semi-colon after RCS `%s' key",
1.26 jfb 1872: rk->rk_str);
1.110 joris 1873: xfree(tokstr);
1.1 jfb 1874: rcs_freedelta(rdp);
1875: return (-1);
1876: }
1877:
1878: if (tok == RCS_TOK_DATE) {
1.25 jfb 1879: if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1.110 joris 1880: xfree(tokstr);
1.11 joris 1881: rcs_freedelta(rdp);
1882: return (-1);
1883: }
1.3 vincent 1884: if (datenum->rn_len != 6) {
1.50 jfb 1885: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1886: cvs_log(LP_ERR,
1887: "RCS date specification has %s "
1888: "fields",
1.3 vincent 1889: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 1890: "missing");
1.110 joris 1891: xfree(tokstr);
1.1 jfb 1892: rcs_freedelta(rdp);
1.37 tedu 1893: rcsnum_free(datenum);
1894: return (-1);
1.1 jfb 1895: }
1.3 vincent 1896: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 1897: if (rdp->rd_date.tm_year >= 1900)
1898: rdp->rd_date.tm_year -= 1900;
1.3 vincent 1899: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1900: rdp->rd_date.tm_mday = datenum->rn_id[2];
1901: rdp->rd_date.tm_hour = datenum->rn_id[3];
1902: rdp->rd_date.tm_min = datenum->rn_id[4];
1903: rdp->rd_date.tm_sec = datenum->rn_id[5];
1904: rcsnum_free(datenum);
1.14 deraadt 1905: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 1906: rdp->rd_author = tokstr;
1907: tokstr = NULL;
1.14 deraadt 1908: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 1909: rdp->rd_state = tokstr;
1910: tokstr = NULL;
1.14 deraadt 1911: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 1912: rcsnum_aton(tokstr, NULL, rdp->rd_next);
1913: }
1914: break;
1915: case RCS_TOK_BRANCHES:
1.46 jfb 1916: if (rcs_parse_branches(rfp, rdp) < 0) {
1917: rcs_freedelta(rdp);
1918: return (-1);
1919: }
1.1 jfb 1920: break;
1921: default:
1.50 jfb 1922: rcs_errno = RCS_ERR_PARSE;
1.172 joris 1923: cvs_log(LP_ERR, "unexpected token `%s' in RCS delta",
1.1 jfb 1924: RCS_TOKSTR(rfp));
1925: rcs_freedelta(rdp);
1926: return (-1);
1927: }
1928: }
1929:
1.13 jfb 1930: if (tokstr != NULL)
1.110 joris 1931: xfree(tokstr);
1.13 jfb 1932:
1.1 jfb 1933: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1.26 jfb 1934: rfp->rf_ndelta++;
1.1 jfb 1935:
1936: return (ret);
1937: }
1938:
1939: /*
1940: * rcs_parse_deltatext()
1941: *
1942: * Parse an RCS delta text section and fill in the log and text field of the
1943: * appropriate delta section.
1944: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1945: * -1 on error.
1946: */
1947: static int
1948: rcs_parse_deltatext(RCSFILE *rfp)
1949: {
1950: int tok;
1951: RCSNUM *tnum;
1952: struct rcs_delta *rdp;
1953:
1954: tok = rcs_gettok(rfp);
1955: if (tok == RCS_TOK_EOF)
1956: return (0);
1957:
1958: if (tok != RCS_TOK_NUM) {
1.50 jfb 1959: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1960: cvs_log(LP_ERR,
1961: "unexpected token `%s' at start of RCS delta text",
1962: RCS_TOKSTR(rfp));
1963: return (-1);
1964: }
1.13 jfb 1965:
1966: tnum = rcsnum_alloc();
1.1 jfb 1967: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1968:
1969: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1970: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1971: break;
1972: }
1.13 jfb 1973: rcsnum_free(tnum);
1974:
1.1 jfb 1975: if (rdp == NULL) {
1976: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1977: RCS_TOKSTR(rfp));
1978: return (-1);
1979: }
1980:
1981: tok = rcs_gettok(rfp);
1982: if (tok != RCS_TOK_LOG) {
1.50 jfb 1983: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1984: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1985: RCS_TOKSTR(rfp));
1986: return (-1);
1987: }
1988:
1989: tok = rcs_gettok(rfp);
1990: if (tok != RCS_TOK_STRING) {
1.50 jfb 1991: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1992: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1993: RCS_TOKSTR(rfp));
1994: return (-1);
1995: }
1.110 joris 1996: rdp->rd_log = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1997: tok = rcs_gettok(rfp);
1998: if (tok != RCS_TOK_TEXT) {
1.50 jfb 1999: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2000: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2001: RCS_TOKSTR(rfp));
2002: return (-1);
2003: }
2004:
2005: tok = rcs_gettok(rfp);
2006: if (tok != RCS_TOK_STRING) {
1.50 jfb 2007: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2008: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2009: RCS_TOKSTR(rfp));
2010: return (-1);
2011: }
2012:
1.193 niallo 2013: if (RCS_TOKLEN(rfp) == 0) {
2014: rdp->rd_text = xmalloc(1);
2015: rdp->rd_text[0] = '\0';
2016: rdp->rd_tlen = 0;
2017: } else {
2018: rdp->rd_text = xmalloc(RCS_TOKLEN(rfp));
2019: memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp));
2020: rdp->rd_tlen = RCS_TOKLEN(rfp);
2021: }
1.1 jfb 2022:
2023: return (1);
2024: }
2025:
2026: /*
2027: * rcs_parse_access()
2028: *
2029: * Parse the access list given as value to the `access' keyword.
2030: * Returns 0 on success, or -1 on failure.
2031: */
2032: static int
2033: rcs_parse_access(RCSFILE *rfp)
2034: {
2035: int type;
2036:
2037: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
2038: if (type != RCS_TOK_ID) {
1.50 jfb 2039: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2040: cvs_log(LP_ERR, "unexpected token `%s' in access list",
2041: RCS_TOKSTR(rfp));
2042: return (-1);
2043: }
1.29 jfb 2044:
2045: if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
2046: return (-1);
1.1 jfb 2047: }
2048:
2049: return (0);
2050: }
2051:
2052: /*
2053: * rcs_parse_symbols()
2054: *
2055: * Parse the symbol list given as value to the `symbols' keyword.
2056: * Returns 0 on success, or -1 on failure.
2057: */
2058: static int
2059: rcs_parse_symbols(RCSFILE *rfp)
2060: {
2061: int type;
2062: struct rcs_sym *symp;
2063:
2064: for (;;) {
2065: type = rcs_gettok(rfp);
2066: if (type == RCS_TOK_SCOLON)
2067: break;
2068:
1.41 jfb 2069: if (type != RCS_TOK_ID) {
1.50 jfb 2070: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2071: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2072: RCS_TOKSTR(rfp));
2073: return (-1);
2074: }
2075:
1.161 ray 2076: symp = xmalloc(sizeof(*symp));
1.110 joris 2077: symp->rs_name = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2078: symp->rs_num = rcsnum_alloc();
2079:
2080: type = rcs_gettok(rfp);
2081: if (type != RCS_TOK_COLON) {
1.50 jfb 2082: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2083: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2084: RCS_TOKSTR(rfp));
1.11 joris 2085: rcsnum_free(symp->rs_num);
1.110 joris 2086: xfree(symp->rs_name);
2087: xfree(symp);
1.1 jfb 2088: return (-1);
2089: }
2090:
2091: type = rcs_gettok(rfp);
2092: if (type != RCS_TOK_NUM) {
1.50 jfb 2093: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2094: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2095: RCS_TOKSTR(rfp));
1.11 joris 2096: rcsnum_free(symp->rs_num);
1.110 joris 2097: xfree(symp->rs_name);
2098: xfree(symp);
1.1 jfb 2099: return (-1);
2100: }
2101:
2102: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
2103: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2104: RCS_TOKSTR(rfp));
1.11 joris 2105: rcsnum_free(symp->rs_num);
1.110 joris 2106: xfree(symp->rs_name);
2107: xfree(symp);
1.1 jfb 2108: return (-1);
2109: }
2110:
1.43 jfb 2111: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
1.1 jfb 2112: }
2113:
2114: return (0);
2115: }
2116:
2117: /*
2118: * rcs_parse_locks()
2119: *
2120: * Parse the lock list given as value to the `locks' keyword.
2121: * Returns 0 on success, or -1 on failure.
2122: */
2123: static int
2124: rcs_parse_locks(RCSFILE *rfp)
2125: {
2126: int type;
2127: struct rcs_lock *lkp;
2128:
2129: for (;;) {
2130: type = rcs_gettok(rfp);
2131: if (type == RCS_TOK_SCOLON)
2132: break;
2133:
2134: if (type != RCS_TOK_ID) {
1.50 jfb 2135: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2136: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
2137: RCS_TOKSTR(rfp));
2138: return (-1);
2139: }
2140:
1.161 ray 2141: lkp = xmalloc(sizeof(*lkp));
1.110 joris 2142: lkp->rl_name = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2143: lkp->rl_num = rcsnum_alloc();
2144:
2145: type = rcs_gettok(rfp);
2146: if (type != RCS_TOK_COLON) {
1.50 jfb 2147: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2148: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2149: RCS_TOKSTR(rfp));
1.37 tedu 2150: rcsnum_free(lkp->rl_num);
1.110 joris 2151: xfree(lkp->rl_name);
2152: xfree(lkp);
1.1 jfb 2153: return (-1);
2154: }
2155:
2156: type = rcs_gettok(rfp);
2157: if (type != RCS_TOK_NUM) {
1.50 jfb 2158: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2159: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2160: RCS_TOKSTR(rfp));
1.37 tedu 2161: rcsnum_free(lkp->rl_num);
1.110 joris 2162: xfree(lkp->rl_name);
2163: xfree(lkp);
1.1 jfb 2164: return (-1);
2165: }
2166:
2167: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
2168: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2169: RCS_TOKSTR(rfp));
1.37 tedu 2170: rcsnum_free(lkp->rl_num);
1.110 joris 2171: xfree(lkp->rl_name);
2172: xfree(lkp);
1.1 jfb 2173: return (-1);
2174: }
2175:
2176: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
2177: }
2178:
2179: /* check if we have a `strict' */
2180: type = rcs_gettok(rfp);
2181: if (type != RCS_TOK_STRICT) {
2182: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 2183: } else {
1.26 jfb 2184: rfp->rf_flags |= RCS_SLOCK;
1.1 jfb 2185:
2186: type = rcs_gettok(rfp);
2187: if (type != RCS_TOK_SCOLON) {
1.50 jfb 2188: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2189: cvs_log(LP_ERR,
2190: "missing semi-colon after `strict' keyword");
2191: return (-1);
2192: }
2193: }
2194:
2195: return (0);
2196: }
2197:
2198: /*
2199: * rcs_parse_branches()
2200: *
2201: * Parse the list of branches following a `branches' keyword in a delta.
2202: * Returns 0 on success, or -1 on failure.
2203: */
2204: static int
2205: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
2206: {
2207: int type;
2208: struct rcs_branch *brp;
2209:
2210: for (;;) {
2211: type = rcs_gettok(rfp);
2212: if (type == RCS_TOK_SCOLON)
2213: break;
2214:
2215: if (type != RCS_TOK_NUM) {
1.50 jfb 2216: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2217: cvs_log(LP_ERR,
2218: "unexpected token `%s' in list of branches",
2219: RCS_TOKSTR(rfp));
2220: return (-1);
2221: }
2222:
1.161 ray 2223: brp = xmalloc(sizeof(*brp));
1.46 jfb 2224: brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
1.11 joris 2225: if (brp->rb_num == NULL) {
1.110 joris 2226: xfree(brp);
1.11 joris 2227: return (-1);
2228: }
1.1 jfb 2229:
2230: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
2231: }
2232:
2233: return (0);
2234: }
2235:
2236: /*
2237: * rcs_freedelta()
2238: *
2239: * Free the contents of a delta structure.
2240: */
1.18 jfb 2241: static void
1.1 jfb 2242: rcs_freedelta(struct rcs_delta *rdp)
2243: {
1.12 jfb 2244: struct rcs_branch *rb;
1.1 jfb 2245:
1.12 jfb 2246: if (rdp->rd_num != NULL)
2247: rcsnum_free(rdp->rd_num);
2248: if (rdp->rd_next != NULL)
2249: rcsnum_free(rdp->rd_next);
2250:
1.1 jfb 2251: if (rdp->rd_author != NULL)
1.110 joris 2252: xfree(rdp->rd_author);
1.109 joris 2253: if (rdp->rd_locker != NULL)
1.110 joris 2254: xfree(rdp->rd_locker);
1.1 jfb 2255: if (rdp->rd_state != NULL)
1.110 joris 2256: xfree(rdp->rd_state);
1.1 jfb 2257: if (rdp->rd_log != NULL)
1.110 joris 2258: xfree(rdp->rd_log);
1.1 jfb 2259: if (rdp->rd_text != NULL)
1.110 joris 2260: xfree(rdp->rd_text);
1.12 jfb 2261:
2262: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
2263: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
2264: rcsnum_free(rb->rb_num);
1.110 joris 2265: xfree(rb);
1.1 jfb 2266: }
2267:
1.110 joris 2268: xfree(rdp);
1.1 jfb 2269: }
2270:
2271: /*
2272: * rcs_freepdata()
2273: *
2274: * Free the contents of the parser data structure.
2275: */
2276: static void
2277: rcs_freepdata(struct rcs_pdata *pd)
2278: {
2279: if (pd->rp_file != NULL)
2280: (void)fclose(pd->rp_file);
2281: if (pd->rp_buf != NULL)
1.110 joris 2282: xfree(pd->rp_buf);
2283: xfree(pd);
1.1 jfb 2284: }
2285:
2286: /*
2287: * rcs_gettok()
2288: *
2289: * Get the next RCS token from the string <str>.
2290: */
2291: static int
2292: rcs_gettok(RCSFILE *rfp)
2293: {
2294: u_int i;
2295: int ch, last, type;
1.18 jfb 2296: size_t len;
2297: char *bp;
1.1 jfb 2298: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2299:
2300: type = RCS_TOK_ERR;
2301: bp = pdp->rp_buf;
1.42 jfb 2302: pdp->rp_tlen = 0;
1.1 jfb 2303: *bp = '\0';
2304:
2305: if (pdp->rp_pttype != RCS_TOK_ERR) {
2306: type = pdp->rp_pttype;
1.172 joris 2307: if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >=
2308: pdp->rp_blen)
2309: fatal("rcs_gettok: strlcpy");
1.1 jfb 2310: pdp->rp_pttype = RCS_TOK_ERR;
2311: return (type);
2312: }
2313:
2314: /* skip leading whitespace */
2315: /* XXX we must skip backspace too for compatibility, should we? */
2316: do {
2317: ch = getc(pdp->rp_file);
2318: if (ch == '\n')
1.18 jfb 2319: pdp->rp_lines++;
1.1 jfb 2320: } while (isspace(ch));
2321:
2322: if (ch == EOF) {
2323: type = RCS_TOK_EOF;
1.14 deraadt 2324: } else if (ch == ';') {
1.1 jfb 2325: type = RCS_TOK_SCOLON;
1.14 deraadt 2326: } else if (ch == ':') {
1.1 jfb 2327: type = RCS_TOK_COLON;
1.14 deraadt 2328: } else if (isalpha(ch)) {
1.31 jfb 2329: type = RCS_TOK_ID;
1.1 jfb 2330: *(bp++) = ch;
1.18 jfb 2331: for (;;) {
1.1 jfb 2332: ch = getc(pdp->rp_file);
1.188 joris 2333: if (ch == EOF) {
2334: type = RCS_TOK_EOF;
2335: break;
2336: } else if (!isalnum(ch) && ch != '_' && ch != '-' &&
1.201 joris 2337: ch != '/' && ch != '+') {
1.1 jfb 2338: ungetc(ch, pdp->rp_file);
2339: break;
2340: }
2341: *(bp++) = ch;
1.42 jfb 2342: pdp->rp_tlen++;
1.18 jfb 2343: if (bp == pdp->rp_bufend - 1) {
2344: len = bp - pdp->rp_buf;
1.151 xsa 2345: rcs_growbuf(rfp);
1.18 jfb 2346: bp = pdp->rp_buf + len;
2347: }
1.1 jfb 2348: }
2349: *bp = '\0';
2350:
1.18 jfb 2351: if (type != RCS_TOK_ERR) {
2352: for (i = 0; i < RCS_NKEYS; i++) {
2353: if (strcmp(rcs_keys[i].rk_str,
2354: pdp->rp_buf) == 0) {
2355: type = rcs_keys[i].rk_id;
2356: break;
2357: }
1.1 jfb 2358: }
2359: }
1.14 deraadt 2360: } else if (ch == '@') {
1.1 jfb 2361: /* we have a string */
1.18 jfb 2362: type = RCS_TOK_STRING;
1.1 jfb 2363: for (;;) {
2364: ch = getc(pdp->rp_file);
1.188 joris 2365: if (ch == EOF) {
2366: type = RCS_TOK_EOF;
2367: break;
2368: } else if (ch == '@') {
1.1 jfb 2369: ch = getc(pdp->rp_file);
2370: if (ch != '@') {
2371: ungetc(ch, pdp->rp_file);
2372: break;
2373: }
1.14 deraadt 2374: } else if (ch == '\n')
1.18 jfb 2375: pdp->rp_lines++;
1.1 jfb 2376:
2377: *(bp++) = ch;
1.42 jfb 2378: pdp->rp_tlen++;
1.18 jfb 2379: if (bp == pdp->rp_bufend - 1) {
2380: len = bp - pdp->rp_buf;
1.151 xsa 2381: rcs_growbuf(rfp);
1.18 jfb 2382: bp = pdp->rp_buf + len;
2383: }
1.1 jfb 2384: }
2385:
2386: *bp = '\0';
1.14 deraadt 2387: } else if (isdigit(ch)) {
1.1 jfb 2388: *(bp++) = ch;
2389: last = ch;
2390: type = RCS_TOK_NUM;
2391:
2392: for (;;) {
2393: ch = getc(pdp->rp_file);
1.188 joris 2394: if (ch == EOF) {
2395: type = RCS_TOK_EOF;
2396: break;
2397: }
1.18 jfb 2398: if (bp == pdp->rp_bufend)
1.1 jfb 2399: break;
2400: if (!isdigit(ch) && ch != '.') {
2401: ungetc(ch, pdp->rp_file);
2402: break;
2403: }
2404:
2405: if (last == '.' && ch == '.') {
2406: type = RCS_TOK_ERR;
2407: break;
2408: }
2409: last = ch;
2410: *(bp++) = ch;
1.42 jfb 2411: pdp->rp_tlen++;
1.1 jfb 2412: }
1.18 jfb 2413: *bp = '\0';
1.1 jfb 2414: }
2415:
2416: return (type);
2417: }
2418:
2419: /*
2420: * rcs_pushtok()
2421: *
2422: * Push a token back in the parser's token buffer.
2423: */
2424: static int
2425: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
2426: {
2427: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2428:
2429: if (pdp->rp_pttype != RCS_TOK_ERR)
2430: return (-1);
2431:
2432: pdp->rp_pttype = type;
1.172 joris 2433: if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >=
2434: sizeof(pdp->rp_ptok))
2435: fatal("rcs_pushtok: strlcpy");
1.1 jfb 2436: return (0);
2437: }
2438:
1.18 jfb 2439:
2440: /*
2441: * rcs_growbuf()
2442: *
2443: * Attempt to grow the internal parse buffer for the RCS file <rf> by
2444: * RCS_BUFEXTSIZE.
2445: * In case of failure, the original buffer is left unmodified.
2446: */
1.151 xsa 2447: static void
1.18 jfb 2448: rcs_growbuf(RCSFILE *rf)
2449: {
2450: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2451:
1.212 ray 2452: pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
2453: pdp->rp_blen + RCS_BUFEXTSIZE);
1.18 jfb 2454: pdp->rp_blen += RCS_BUFEXTSIZE;
2455: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.42 jfb 2456: }
2457:
2458: /*
2459: * rcs_strprint()
2460: *
2461: * Output an RCS string <str> of size <slen> to the stream <stream>. Any
2462: * '@' characters are escaped. Otherwise, the string can contain arbitrary
2463: * binary data.
2464: */
1.155 ray 2465: static void
1.42 jfb 2466: rcs_strprint(const u_char *str, size_t slen, FILE *stream)
2467: {
2468: const u_char *ap, *ep, *sp;
1.52 jfb 2469:
2470: if (slen == 0)
1.155 ray 2471: return;
1.42 jfb 2472:
2473: ep = str + slen - 1;
2474:
2475: for (sp = str; sp <= ep;) {
2476: ap = memchr(sp, '@', ep - sp);
2477: if (ap == NULL)
2478: ap = ep;
1.155 ray 2479: (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1.42 jfb 2480:
2481: if (*ap == '@')
2482: putc('@', stream);
2483: sp = ap + 1;
1.63 joris 2484: }
2485: }
2486:
2487: /*
1.81 niallo 2488: * rcs_deltatext_set()
2489: *
2490: * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1.96 xsa 2491: * Returns -1 on error, 0 on success.
1.81 niallo 2492: */
2493: int
1.194 joris 2494: rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1.81 niallo 2495: {
2496: size_t len;
1.194 joris 2497: u_char *dtext;
1.81 niallo 2498: struct rcs_delta *rdp;
1.117 niallo 2499:
2500: /* Write operations require full parsing */
2501: rcs_parse_deltatexts(rfp, NULL);
1.81 niallo 2502:
2503: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2504: return (-1);
2505:
2506: if (rdp->rd_text != NULL)
1.110 joris 2507: xfree(rdp->rd_text);
1.81 niallo 2508:
1.194 joris 2509: len = cvs_buf_len(bp);
2510: dtext = cvs_buf_release(bp);
2511: bp = NULL;
2512:
1.103 joris 2513: if (len != 0) {
1.194 joris 2514: rdp->rd_text = xmalloc(len);
1.155 ray 2515: rdp->rd_tlen = len;
1.194 joris 2516: (void)memcpy(rdp->rd_text, dtext, len);
1.103 joris 2517: } else {
2518: rdp->rd_text = NULL;
2519: rdp->rd_tlen = 0;
2520: }
1.194 joris 2521:
2522: if (dtext != NULL)
2523: xfree(dtext);
1.18 jfb 2524:
1.86 joris 2525: return (0);
2526: }
2527:
2528: /*
2529: * rcs_rev_setlog()
2530: *
2531: * Sets the log message of revision <rev> to <logtext>
2532: */
2533: int
2534: rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
2535: {
2536: struct rcs_delta *rdp;
1.214 xsa 2537: char buf[CVS_REV_BUFSZ];
1.86 joris 2538:
2539: rcsnum_tostr(rev, buf, sizeof(buf));
2540:
2541: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2542: return (-1);
2543:
2544: if (rdp->rd_log != NULL)
1.110 joris 2545: xfree(rdp->rd_log);
1.86 joris 2546:
1.110 joris 2547: rdp->rd_log = xstrdup(logtext);
1.86 joris 2548: rfp->rf_flags &= ~RCS_SYNCED;
1.95 niallo 2549: return (0);
2550: }
1.97 niallo 2551: /*
2552: * rcs_rev_getdate()
2553: *
2554: * Get the date corresponding to a given revision.
2555: * Returns the date on success, -1 on failure.
2556: */
2557: time_t
2558: rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
2559: {
2560: struct rcs_delta *rdp;
2561:
2562: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2563: return (-1);
2564:
1.204 joris 2565: return (timegm(&rdp->rd_date));
1.97 niallo 2566: }
1.95 niallo 2567:
2568: /*
2569: * rcs_state_set()
2570: *
2571: * Sets the state of revision <rev> to <state>
2572: * NOTE: default state is 'Exp'. States may not contain spaces.
2573: *
2574: * Returns -1 on failure, 0 on success.
2575: */
2576: int
2577: rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
2578: {
2579: struct rcs_delta *rdp;
2580:
2581: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2582: return (-1);
2583:
2584: if (rdp->rd_state != NULL)
1.110 joris 2585: xfree(rdp->rd_state);
1.95 niallo 2586:
1.110 joris 2587: rdp->rd_state = xstrdup(state);
1.95 niallo 2588:
2589: rfp->rf_flags &= ~RCS_SYNCED;
2590:
2591: return (0);
2592: }
2593:
2594: /*
2595: * rcs_state_check()
2596: *
2597: * Check if string <state> is valid.
2598: *
1.96 xsa 2599: * Returns 0 if the string is valid, -1 otherwise.
1.95 niallo 2600: */
2601: int
2602: rcs_state_check(const char *state)
2603: {
2604: if (strchr(state, ' ') != NULL)
2605: return (-1);
2606:
1.18 jfb 2607: return (0);
1.1 jfb 2608: }
1.97 niallo 2609:
2610: /*
2611: * rcs_state_get()
2612: *
2613: * Get the state for a given revision of a specified RCSFILE.
2614: *
2615: * Returns NULL on failure.
2616: */
2617: const char *
2618: rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
2619: {
2620: struct rcs_delta *rdp;
2621:
2622: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2623: return (NULL);
2624:
2625: return (rdp->rd_state);
2626: }
2627:
1.213 niallo 2628: /* rcs_translate_tag() */
1.176 joris 2629: RCSNUM *
2630: rcs_translate_tag(const char *revstr, RCSFILE *rfp)
2631: {
1.213 niallo 2632: RCSNUM *rev, *brev, *frev;
1.178 joris 2633: struct rcs_branch *brp;
1.183 joris 2634: struct rcs_delta *rdp;
1.213 niallo 2635: size_t i;
1.183 joris 2636:
2637: rdp = NULL;
1.215 joris 2638:
2639: if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
2640: frev = rcsnum_alloc();
2641: rcsnum_cpy(rfp->rf_head, frev, 0);
2642: return (frev);
2643: }
1.176 joris 2644:
1.213 niallo 2645: /* Possibly we could be passed a version number */
1.223 tobias 2646: if ((rev = rcsnum_parse(revstr)) != NULL) {
2647: /* Do not return if it is not in RCS file */
2648: if ((rdp = rcs_findrev(rfp, rev)) != NULL)
2649: return (rev);
2650: } else {
2651: /* More likely we will be passed a symbol */
2652: rev = rcs_sym_getrev(rfp, revstr);
2653: }
1.222 joris 2654:
1.213 niallo 2655: if (rev == NULL)
2656: return (NULL);
1.183 joris 2657:
1.220 joris 2658: /*
2659: * If it was not a branch, thats ok the symbolic
2660: * name refered to a revision, so return the resolved
1.224 tobias 2661: * revision for the given name if it is not newer than HEAD.
1.220 joris 2662: */
1.224 tobias 2663: if (!RCSNUM_ISBRANCH(rev)) {
2664: if (rcsnum_cmp(rev, rfp->rf_head, 0) < 0) {
2665: rcsnum_free(rev);
2666: return NULL;
2667: }
1.220 joris 2668: return (rev);
1.224 tobias 2669: }
1.183 joris 2670:
1.213 niallo 2671: brev = rcsnum_alloc();
2672: rcsnum_cpy(rev, brev, rev->rn_len - 1);
1.178 joris 2673:
1.180 joris 2674: if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1.213 niallo 2675: fatal("rcs_translate_tag: tag `%s' does not exist", revstr);
2676: rcsnum_free(brev);
1.178 joris 2677:
1.213 niallo 2678: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2679: for (i = 0; i < rev->rn_len; i++)
2680: if (brp->rb_num->rn_id[i] != rev->rn_id[i])
2681: break;
2682: if (i != rev->rn_len)
2683: continue;
2684: break;
2685: }
1.178 joris 2686:
1.240 tobias 2687: rcsnum_free(rev);
1.213 niallo 2688: frev = rcsnum_alloc();
2689: if (brp == NULL) {
2690: rcsnum_cpy(rdp->rd_num, frev, 0);
2691: return (frev);
2692: } else {
2693: /* Fetch the delta with the correct branch num */
1.180 joris 2694: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.213 niallo 2695: fatal("rcs_translate_tag: could not fetch branch delta");
2696: /* Find the latest delta on that branch */
2697: for (;;) {
2698: if (rdp->rd_next->rn_len == 0)
2699: break;
1.180 joris 2700: if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
1.213 niallo 2701: fatal("rcs_translate_tag: could not fetch branch delta");
1.178 joris 2702: }
1.213 niallo 2703: rcsnum_cpy(rdp->rd_num, frev, 0);
2704: return (frev);
1.176 joris 2705: }
1.196 niallo 2706: }
2707:
2708: /*
2709: * rcs_rev_getlines()
2710: *
1.238 tobias 2711: * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
1.196 niallo 2712: * return it as a pointer to a struct cvs_lines.
2713: */
2714: struct cvs_lines *
1.218 tobias 2715: rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
1.196 niallo 2716: {
1.209 otto 2717: size_t plen;
1.218 tobias 2718: int annotate, done, i, nextroot;
1.196 niallo 2719: RCSNUM *tnum, *bnum;
2720: struct rcs_branch *brp;
1.218 tobias 2721: struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
1.196 niallo 2722: u_char *patch;
1.218 tobias 2723: struct cvs_line *line, *nline;
1.196 niallo 2724: struct cvs_lines *dlines, *plines;
2725:
2726: if ((hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
1.197 joris 2727: fatal("rcs_rev_getlines: no HEAD revision");
1.196 niallo 2728:
2729: tnum = frev;
2730: rcs_parse_deltatexts(rfp, hrdp->rd_num);
2731:
2732: /* revision on branch, get the branch root */
2733: nextroot = 2;
2734: if (RCSNUM_ISBRANCHREV(tnum)) {
2735: bnum = rcsnum_alloc();
2736: rcsnum_cpy(tnum, bnum, nextroot);
2737: } else {
2738: bnum = tnum;
2739: }
2740:
1.218 tobias 2741: if (alines != NULL) {
2742: /* start with annotate first at requested revision */
2743: annotate = ANNOTATE_LATER;
2744: *alines = NULL;
2745: } else
2746: annotate = ANNOTATE_NEVER;
2747:
1.196 niallo 2748: dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
2749:
2750: done = 0;
2751:
2752: rdp = hrdp;
1.218 tobias 2753: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2754: if (annotate == ANNOTATE_LATER) {
2755: /* found requested revision for annotate */
2756: i = 0;
2757: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2758: line->l_lineno_orig = line->l_lineno;
2759: i++;
2760: }
2761:
2762: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2763: (*alines)[i] = NULL;
2764: annotate = ANNOTATE_NOW;
2765:
2766: /* annotate down to 1.1 from where we are */
2767: if (bnum == tnum)
2768: bnum = rcsnum_alloc();
2769: bnum = rcsnum_parse("1.1");
2770: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2771: goto next;
2772: }
2773: } else
2774: goto next;
2775: }
1.196 niallo 2776:
1.218 tobias 2777: prdp = hrdp;
1.196 niallo 2778: if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
2779: goto done;
2780:
2781: again:
2782: for (;;) {
2783: if (rdp->rd_next->rn_len != 0) {
2784: trdp = rcs_findrev(rfp, rdp->rd_next);
2785: if (trdp == NULL)
2786: fatal("failed to grab next revision");
2787: }
2788:
2789: if (rdp->rd_tlen == 0) {
2790: rcs_parse_deltatexts(rfp, rdp->rd_num);
2791: if (rdp->rd_tlen == 0) {
2792: if (!rcsnum_differ(rdp->rd_num, bnum))
2793: break;
2794: rdp = trdp;
2795: continue;
2796: }
2797: }
2798:
2799: plen = rdp->rd_tlen;
2800: patch = rdp->rd_text;
2801: plines = cvs_splitlines(patch, plen);
1.218 tobias 2802: if (annotate == ANNOTATE_NOW)
2803: rcs_patch_lines(dlines, plines, *alines, prdp);
2804: else
2805: rcs_patch_lines(dlines, plines, NULL, NULL);
1.196 niallo 2806: cvs_freelines(plines);
2807:
1.218 tobias 2808: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2809: if (annotate != ANNOTATE_LATER)
2810: break;
2811:
2812: /* found requested revision for annotate */
2813: i = 0;
2814: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2815: line->l_lineno_orig = line->l_lineno;
2816: i++;
2817: }
2818:
2819: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2820: (*alines)[i] = NULL;
2821: annotate = ANNOTATE_NOW;
2822:
2823: /* annotate down to 1.1 from where we are */
2824: if (bnum == tnum)
2825: bnum = rcsnum_alloc();
2826: bnum = rcsnum_parse("1.1");
2827:
2828: if (!rcsnum_differ(rdp->rd_num, bnum))
2829: break;
2830: }
1.196 niallo 2831:
1.218 tobias 2832: prdp = rdp;
1.196 niallo 2833: rdp = trdp;
2834: }
2835:
2836: next:
2837: if (!rcsnum_differ(rdp->rd_num, frev))
2838: done = 1;
2839:
2840: if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
2841: nextroot += 2;
2842: rcsnum_cpy(frev, bnum, nextroot);
2843:
2844: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.213 niallo 2845: for (i = 0; i < nextroot - 1; i++)
2846: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1.196 niallo 2847: break;
1.213 niallo 2848: if (i == nextroot - 1)
2849: break;
1.196 niallo 2850: }
2851:
1.218 tobias 2852: if (brp == NULL) {
2853: if (annotate != ANNOTATE_NEVER) {
2854: if (*alines != NULL)
2855: xfree(*alines);
2856: *alines = NULL;
2857: cvs_freelines(dlines);
2858: if (bnum != tnum)
2859: rcsnum_free(bnum);
2860: return (NULL);
2861: }
1.196 niallo 2862: fatal("expected branch not found on branch list");
1.218 tobias 2863: }
1.196 niallo 2864:
2865: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.197 joris 2866: fatal("rcs_rev_getlines: failed to get delta for target rev");
1.196 niallo 2867:
2868: goto again;
2869: }
2870: done:
1.225 tobias 2871: /* put remaining lines into annotate buffer */
1.218 tobias 2872: if (annotate == ANNOTATE_NOW) {
2873: for (line = TAILQ_FIRST(&(dlines->l_lines));
2874: line != NULL; line = nline) {
2875: nline = TAILQ_NEXT(line, l_list);
2876: TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
2877: if (line->l_line == NULL) {
2878: xfree(line);
2879: continue;
2880: }
2881:
2882: line->l_delta = rdp;
2883: (*alines)[line->l_lineno_orig - 1] = line;
2884: }
2885:
2886: cvs_freelines(dlines);
2887: dlines = NULL;
2888: }
2889:
1.196 niallo 2890: if (bnum != tnum)
2891: rcsnum_free(bnum);
2892:
2893: return (dlines);
1.225 tobias 2894: }
2895:
2896: void
2897: rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2898: {
2899: size_t plen;
2900: int i, nextroot;
2901: RCSNUM *bnum;
2902: struct rcs_branch *brp;
2903: struct rcs_delta *rdp, *trdp;
2904: u_char *patch;
2905: struct cvs_line *line;
2906: struct cvs_lines *dlines, *plines;
2907:
2908: if (!RCSNUM_ISBRANCHREV(frev))
2909: fatal("rcs_annotate_getlines: branch revision expected");
2910:
2911: /* revision on branch, get the branch root */
2912: nextroot = 2;
2913: bnum = rcsnum_alloc();
2914: rcsnum_cpy(frev, bnum, nextroot);
2915:
2916: /*
2917: * Going from HEAD to 1.1 enables the use of an array, which is
2918: * much faster. Unfortunately this is not possible with branch
2919: * revisions, so copy over our alines (array) into dlines (tailq).
2920: */
2921: dlines = xcalloc(1, sizeof(*dlines));
2922: TAILQ_INIT(&(dlines->l_lines));
2923: line = xcalloc(1, sizeof(*line));
2924: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2925:
2926: for (i = 0; (*alines)[i] != NULL; i++) {
2927: line = (*alines)[i];
2928: line->l_lineno = i + 1;
2929: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2930: }
2931:
2932: rdp = rcs_findrev(rfp, bnum);
2933: if (rdp == NULL)
2934: fatal("failed to grab branch root revision");
2935:
2936: do {
2937: nextroot += 2;
2938: rcsnum_cpy(frev, bnum, nextroot);
2939:
2940: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2941: for (i = 0; i < nextroot - 1; i++)
2942: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2943: break;
2944: if (i == nextroot - 1)
2945: break;
2946: }
2947:
2948: if (brp == NULL)
2949: fatal("expected branch not found on branch list");
2950:
2951: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2952: fatal("failed to get delta for target rev");
2953:
2954: for (;;) {
2955: if (rdp->rd_next->rn_len != 0) {
2956: trdp = rcs_findrev(rfp, rdp->rd_next);
2957: if (trdp == NULL)
2958: fatal("failed to grab next revision");
2959: }
2960:
2961: if (rdp->rd_tlen == 0) {
2962: rcs_parse_deltatexts(rfp, rdp->rd_num);
2963: if (rdp->rd_tlen == 0) {
2964: if (!rcsnum_differ(rdp->rd_num, bnum))
2965: break;
2966: rdp = trdp;
2967: continue;
2968: }
2969: }
2970:
2971: plen = rdp->rd_tlen;
2972: patch = rdp->rd_text;
2973: plines = cvs_splitlines(patch, plen);
2974: rcs_patch_lines(dlines, plines, NULL, rdp);
2975: cvs_freelines(plines);
2976:
2977: if (!rcsnum_differ(rdp->rd_num, bnum))
2978: break;
2979:
2980: rdp = trdp;
2981: }
2982: } while (rcsnum_differ(rdp->rd_num, frev));
2983:
2984: if (bnum != frev)
2985: rcsnum_free(bnum);
2986:
2987: /*
2988: * All lines have been parsed, now they must be copied over
2989: * into alines (array) again.
2990: */
2991: xfree(*alines);
2992:
2993: i = 0;
2994: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2995: if (line->l_line != NULL)
2996: i++;
2997: }
1.239 tobias 2998: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
1.225 tobias 2999: (*alines)[i] = NULL;
3000:
3001: i = 0;
3002: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3003: if (line->l_line != NULL)
3004: (*alines)[i++] = line;
3005: }
1.196 niallo 3006: }
3007:
3008: /*
3009: * rcs_rev_getbuf()
3010: *
3011: * XXX: This is really really slow and should be avoided if at all possible!
3012: *
3013: * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
3014: * return it as a BUF pointer.
3015: */
3016: BUF *
1.200 joris 3017: rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
1.196 niallo 3018: {
1.200 joris 3019: int expmode, expand;
3020: struct rcs_delta *rdp;
1.196 niallo 3021: struct cvs_lines *lines;
1.233 tobias 3022: struct cvs_line *lp, *nlp;
1.196 niallo 3023: BUF *bp;
3024:
1.200 joris 3025: expand = 0;
1.218 tobias 3026: lines = rcs_rev_getlines(rfp, rev, NULL);
1.196 niallo 3027: bp = cvs_buf_alloc(1024, BUF_AUTOEXT);
1.200 joris 3028:
3029: if (!(mode & RCS_KWEXP_NONE)) {
3030: if (rfp->rf_expand != NULL)
3031: expmode = rcs_kwexp_get(rfp);
3032: else
3033: expmode = RCS_KWEXP_DEFAULT;
3034:
3035: if (!(expmode & RCS_KWEXP_NONE)) {
3036: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3037: fatal("could not fetch revision");
3038: expand = 1;
3039: }
3040: }
3041:
1.233 tobias 3042: for(lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
3043: nlp = TAILQ_NEXT(lp, l_list);
3044:
3045: if (lp->l_line == NULL) {
3046: lp = nlp;
1.196 niallo 3047: continue;
1.233 tobias 3048: }
1.200 joris 3049:
3050: if (expand)
1.233 tobias 3051: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.200 joris 3052:
1.233 tobias 3053: do {
3054: cvs_buf_append(bp, lp->l_line, lp->l_len);
3055: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 3056: }
3057:
3058: cvs_freelines(lines);
3059:
3060: return (bp);
3061: }
3062:
3063: /*
3064: * rcs_rev_write_fd()
3065: *
3066: * Write the entire contents of revision <frev> from the rcsfile <rfp> to
3067: * file descriptor <fd>.
3068: */
3069: void
3070: rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int fd, int mode)
3071: {
1.200 joris 3072: int expmode, expand;
1.196 niallo 3073: struct rcs_delta *rdp;
3074: struct cvs_lines *lines;
1.233 tobias 3075: struct cvs_line *lp, *nlp;
1.217 joris 3076: extern int print_stdout;
1.196 niallo 3077:
1.200 joris 3078: expand = 0;
1.218 tobias 3079: lines = rcs_rev_getlines(rfp, rev, NULL);
1.200 joris 3080:
1.196 niallo 3081: if (!(mode & RCS_KWEXP_NONE)) {
3082: if (rfp->rf_expand != NULL)
3083: expmode = rcs_kwexp_get(rfp);
3084: else
3085: expmode = RCS_KWEXP_DEFAULT;
3086:
3087: if (!(expmode & RCS_KWEXP_NONE)) {
3088: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3089: fatal("could not fetch revision");
1.200 joris 3090: expand = 1;
1.196 niallo 3091: }
3092: }
1.200 joris 3093:
1.233 tobias 3094: for(lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
3095: nlp = TAILQ_NEXT(lp, l_list);
3096:
3097: if (lp->l_line == NULL) {
3098: lp = nlp;
1.196 niallo 3099: continue;
1.233 tobias 3100: }
1.200 joris 3101:
3102: if (expand)
1.233 tobias 3103: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.217 joris 3104:
1.233 tobias 3105: do {
3106: /*
3107: * Solely for the checkout and update -p options.
3108: */
3109: if (cvs_server_active == 1 &&
3110: (cvs_cmdop == CVS_OP_CHECKOUT ||
3111: cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
3112: if (atomicio(vwrite, fd, "M ", 2) != 2)
3113: fatal("rcs_rev_write_fd: %s",
3114: strerror(errno));
3115: }
3116:
3117: if (atomicio(vwrite, fd, lp->l_line, lp->l_len) !=
3118: lp->l_len)
1.217 joris 3119: fatal("rcs_rev_write_fd: %s", strerror(errno));
1.233 tobias 3120: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 3121: }
3122:
3123: /* XXX: do we need to call futimes(2) on the output fd? */
3124:
3125: cvs_freelines(lines);
3126:
3127: }
3128:
3129: /*
3130: * rcs_rev_write_stmp()
3131: *
3132: * Write the contents of the rev <rev> to a temporary file whose path is
3133: * specified using <template> (see mkstemp(3)). NB. This function will modify
3134: * <template>, as per mkstemp.
3135: */
3136: void
3137: rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode)
3138: {
3139: int fd;
3140:
3141: if ((fd = mkstemp(template)) == -1)
3142: fatal("mkstemp: `%s': %s", template, strerror(errno));
3143:
3144: cvs_worklist_add(template, &temp_files);
3145: rcs_rev_write_fd(rfp, rev, fd, mode);
3146:
3147: (void)close(fd);
3148: }
3149:
3150: static void
1.233 tobias 3151: rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines,
3152: struct cvs_line *line, int mode)
1.196 niallo 3153: {
1.231 tobias 3154: BUF *tmpbuf;
1.196 niallo 3155: int kwtype;
3156: u_int j, found;
1.209 otto 3157: const u_char *c, *start, *fin, *end;
3158: char *kwstr;
1.196 niallo 3159: char expbuf[256], buf[256];
3160: char *fmt;
1.231 tobias 3161: size_t clen, kwlen, len, tlen;
1.196 niallo 3162:
3163: kwtype = 0;
3164: kwstr = NULL;
1.230 tobias 3165:
3166: if (mode & RCS_KWEXP_OLD)
3167: return;
1.196 niallo 3168:
3169: len = line->l_len;
3170: if (len == 0)
3171: return;
3172:
3173: c = line->l_line;
3174: found = 0;
3175: /* Final character in buffer. */
3176: fin = c + len - 1;
3177:
3178: /*
3179: * Keyword formats:
3180: * $Keyword$
3181: * $Keyword: value$
3182: */
3183: for (; c < fin; c++) {
1.231 tobias 3184: if (*c != '$')
3185: continue;
1.196 niallo 3186:
1.231 tobias 3187: /* remember start of this possible keyword */
3188: start = c;
1.196 niallo 3189:
1.231 tobias 3190: /* first following character has to be alphanumeric */
3191: c++;
3192: if (!isalpha(*c)) {
3193: c = start;
3194: continue;
3195: }
1.196 niallo 3196:
1.231 tobias 3197: /* Number of characters between c and fin, inclusive. */
3198: clen = fin - c + 1;
1.196 niallo 3199:
1.231 tobias 3200: /* look for any matching keywords */
3201: found = 0;
3202: for (j = 0; j < RCS_NKWORDS; j++) {
3203: kwlen = strlen(rcs_expkw[j].kw_str);
1.196 niallo 3204: /*
1.231 tobias 3205: * kwlen must be less than clen since clen
3206: * includes either a terminating `$' or a `:'.
1.196 niallo 3207: */
1.231 tobias 3208: if (kwlen < clen &&
3209: memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
3210: (c[kwlen] == '$' || c[kwlen] == ':')) {
3211: found = 1;
3212: kwstr = rcs_expkw[j].kw_str;
3213: kwtype = rcs_expkw[j].kw_type;
3214: c += kwlen;
3215: break;
3216: }
3217: }
1.196 niallo 3218:
1.231 tobias 3219: if (found == 0 && cvs_tagname != NULL) {
3220: kwlen = strlen(cvs_tagname);
3221: if (kwlen < clen &&
3222: memcmp(c, cvs_tagname, kwlen) == 0 &&
3223: (c[kwlen] == '$' || c[kwlen] == ':')) {
3224: found = 1;
3225: kwstr = cvs_tagname;
3226: kwtype = RCS_KW_ID;
3227: c += kwlen;
1.196 niallo 3228: }
1.231 tobias 3229: }
1.196 niallo 3230:
1.231 tobias 3231: /* unknown keyword, continue looking */
3232: if (found == 0) {
3233: c = start;
3234: continue;
3235: }
1.196 niallo 3236:
1.231 tobias 3237: /*
3238: * if the next character was ':' we need to look for
3239: * an '$' before the end of the line to be sure it is
3240: * in fact a keyword.
3241: */
3242: if (*c == ':') {
3243: for (; c <= fin; ++c) {
3244: if (*c == '$' || *c == '\n')
3245: break;
1.196 niallo 3246: }
3247:
1.231 tobias 3248: if (*c != '$') {
3249: c = start;
3250: continue;
3251: }
3252: }
3253: end = c + 1;
1.196 niallo 3254:
1.231 tobias 3255: /* start constructing the expansion */
3256: expbuf[0] = '\0';
1.196 niallo 3257:
1.231 tobias 3258: if (mode & RCS_KWEXP_NAME) {
3259: if (strlcat(expbuf, "$", sizeof(expbuf)) >=
3260: sizeof(expbuf) || strlcat(expbuf, kwstr,
3261: sizeof(expbuf)) >= sizeof(expbuf))
3262: fatal("rcs_kwexp_line: truncated");
3263: if ((mode & RCS_KWEXP_VAL) &&
3264: strlcat(expbuf, ": ", sizeof(expbuf)) >=
3265: sizeof(expbuf))
3266: fatal("rcs_kwexp_line: truncated");
3267: }
1.196 niallo 3268:
1.231 tobias 3269: /*
3270: * order matters because of RCS_KW_ID and
3271: * RCS_KW_HEADER here
3272: */
3273: if (mode & RCS_KWEXP_VAL) {
3274: if (kwtype & RCS_KW_RCSFILE) {
3275: if (!(kwtype & RCS_KW_FULLPATH))
3276: (void)strlcat(expbuf, basename(rcsfile),
3277: sizeof(expbuf));
3278: else
3279: (void)strlcat(expbuf, rcsfile,
3280: sizeof(expbuf));
3281: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3282: sizeof(expbuf))
3283: fatal("rcs_kwexp_line: truncated");
3284: }
1.211 niallo 3285:
1.231 tobias 3286: if (kwtype & RCS_KW_REVISION) {
3287: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3288: if (strlcat(buf, " ", sizeof(buf)) >=
3289: sizeof(buf) || strlcat(expbuf, buf,
3290: sizeof(expbuf)) >= sizeof(buf))
3291: fatal("rcs_kwexp_line: truncated");
3292: }
1.196 niallo 3293:
1.231 tobias 3294: if (kwtype & RCS_KW_DATE) {
3295: fmt = "%Y/%m/%d %H:%M:%S ";
1.196 niallo 3296:
1.231 tobias 3297: if (strftime(buf, sizeof(buf), fmt,
3298: &rdp->rd_date) == 0)
3299: fatal("rcs_kwexp_line: strftime "
3300: "failure");
3301: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3302: sizeof(expbuf))
3303: fatal("rcs_kwexp_line: string "
3304: "truncated");
3305: }
1.196 niallo 3306:
1.231 tobias 3307: if (kwtype & RCS_KW_MDOCDATE) {
3308: /*
3309: * Do not prepend ' ' for a single
3310: * digit, %e would do so and there is
3311: * no better format for strftime().
3312: */
3313: if (rdp->rd_date.tm_mday < 10)
3314: fmt = "%B%e %Y ";
3315: else
3316: fmt = "%B %e %Y ";
3317:
3318: if (strftime(buf, sizeof(buf), fmt,
3319: &rdp->rd_date) == 0)
3320: fatal("rcs_kwexp_line: strftime "
3321: "failure");
3322: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3323: sizeof(expbuf))
3324: fatal("rcs_kwexp_line: string "
3325: "truncated");
3326: }
3327:
3328: if (kwtype & RCS_KW_AUTHOR) {
3329: if (strlcat(expbuf, rdp->rd_author,
3330: sizeof(expbuf)) >= sizeof(expbuf) ||
3331: strlcat(expbuf, " ", sizeof(expbuf)) >=
3332: sizeof(expbuf))
3333: fatal("rcs_kwexp_line: string "
3334: "truncated");
3335: }
3336:
3337: if (kwtype & RCS_KW_STATE) {
3338: if (strlcat(expbuf, rdp->rd_state,
3339: sizeof(expbuf)) >= sizeof(expbuf) ||
3340: strlcat(expbuf, " ", sizeof(expbuf)) >=
3341: sizeof(expbuf))
3342: fatal("rcs_kwexp_line: string "
3343: "truncated");
3344: }
3345:
3346: /* order does not matter anymore below */
1.233 tobias 3347: if (kwtype & RCS_KW_LOG) {
3348: char linebuf[256];
3349: struct cvs_line *cur, *lp;
3350: char *logp, *l_line, *prefix, *q, *sprefix;
3351: size_t i;
3352:
1.236 tobias 3353: /* Log line */
1.233 tobias 3354: if (!(kwtype & RCS_KW_FULLPATH))
3355: (void)strlcat(expbuf,
3356: basename(rcsfile), sizeof(expbuf));
3357: else
3358: (void)strlcat(expbuf, rcsfile,
3359: sizeof(expbuf));
3360:
1.231 tobias 3361: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3362: sizeof(expbuf))
3363: fatal("rcs_kwexp_line: string "
3364: "truncated");
1.233 tobias 3365:
3366: cur = line;
3367:
3368: /* copy rdp->rd_log for strsep */
3369: logp = xstrdup(rdp->rd_log);
3370:
3371: /* copy our prefix for later processing */
3372: prefix = xmalloc(start - line->l_line + 1);
3373: memcpy(prefix, line->l_line,
3374: start - line->l_line);
3375: prefix[start - line->l_line] = '\0';
3376:
3377: /* copy also prefix without trailing blanks. */
3378: sprefix = xstrdup(prefix);
3379: for (i = strlen(sprefix); i > 0 &&
3380: sprefix[i - 1] == ' '; i--)
3381: sprefix[i - 1] = '\0';
3382:
3383: /* new line: revision + date + author */
3384: linebuf[0] = '\0';
3385: if (strlcat(linebuf, "Revision ",
3386: sizeof(linebuf)) >= sizeof(linebuf))
3387: fatal("rcs_kwexp_line: truncated");
3388: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3389: if (strlcat(linebuf, buf, sizeof(linebuf))
3390: >= sizeof(buf))
3391: fatal("rcs_kwexp_line: truncated");
3392: fmt = " %Y/%m/%d %H:%M:%S ";
3393: if (strftime(buf, sizeof(buf), fmt,
3394: &rdp->rd_date) == 0)
3395: fatal("rcs_kwexp_line: strftime "
3396: "failure");
3397: if (strlcat(linebuf, buf, sizeof(linebuf))
3398: >= sizeof(linebuf))
3399: fatal("rcs_kwexp_line: string "
3400: "truncated");
3401: if (strlcat(linebuf, rdp->rd_author,
3402: sizeof(linebuf)) >= sizeof(linebuf))
3403: fatal("rcs_kwexp_line: string "
3404: "truncated");
3405:
3406: lp = xcalloc(1, sizeof(*lp));
3407: xasprintf(&(lp->l_line), "%s%s\n",
3408: prefix, linebuf);
3409: lp->l_len = strlen(lp->l_line);
3410: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3411: l_list);
3412: cur = lp;
3413:
3414: /* Log message */
3415: q = logp;
3416: while ((l_line = strsep(&q, "\n")) != NULL &&
3417: q != NULL) {
3418: lp = xcalloc(1, sizeof(*lp));
3419:
3420: if (l_line[0] == '\0') {
3421: xasprintf(&(lp->l_line), "%s\n",
3422: sprefix);
3423: } else {
3424: xasprintf(&(lp->l_line),
3425: "%s%s\n", prefix, l_line);
3426: }
3427:
3428: lp->l_len = strlen(lp->l_line);
3429: TAILQ_INSERT_AFTER(&(lines->l_lines),
3430: cur, lp, l_list);
3431: cur = lp;
3432: }
3433: xfree(logp);
3434:
3435: /*
3436: * This is just another hairy mess, but it must
1.237 tobias 3437: * be done: All characters behind Log will be
1.233 tobias 3438: * written in a new line next to log messages.
3439: * But that's not enough, we have to strip all
3440: * trailing whitespaces of our prefix.
3441: */
3442: lp = xcalloc(1, sizeof(*lp));
3443: lp->l_line = xcalloc(strlen(sprefix) +
3444: line->l_line + line->l_len - end + 1, 1);
3445: strlcpy(lp->l_line, sprefix,
3446: strlen(sprefix) + 1);
3447: memcpy(lp->l_line + strlen(sprefix),
3448: end, line->l_line + line->l_len - end);
3449: lp->l_len = strlen(lp->l_line);
3450: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3451: l_list);
3452: cur = lp;
3453:
3454: end = line->l_line + line->l_len - 1;
3455:
3456: xfree(prefix);
3457: xfree(sprefix);
3458:
3459: }
1.231 tobias 3460:
3461: if (kwtype & RCS_KW_SOURCE) {
3462: if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
3463: sizeof(expbuf) || strlcat(expbuf, " ",
3464: sizeof(expbuf)) >= sizeof(expbuf))
3465: fatal("rcs_kwexp_line: string "
3466: "truncated");
1.196 niallo 3467: }
3468:
1.231 tobias 3469: if (kwtype & RCS_KW_NAME)
3470: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3471: sizeof(expbuf))
3472: fatal("rcs_kwexp_line: string "
3473: "truncated");
1.196 niallo 3474:
1.231 tobias 3475: if (kwtype & RCS_KW_LOCKER)
3476: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3477: sizeof(expbuf))
3478: fatal("rcs_kwexp_line: string "
3479: "truncated");
1.196 niallo 3480: }
1.231 tobias 3481:
3482: /* end the expansion */
3483: if (mode & RCS_KWEXP_NAME)
3484: if (strlcat(expbuf, "$",
3485: sizeof(expbuf)) >= sizeof(expbuf))
3486: fatal("rcs_kwexp_line: truncated");
3487:
3488: /* Concatenate everything together. */
3489: tmpbuf = cvs_buf_alloc(len + strlen(expbuf), BUF_AUTOEXT);
3490: /* Append everything before keyword. */
3491: cvs_buf_append(tmpbuf, line->l_line,
3492: start - line->l_line);
3493: /* Append keyword. */
3494: cvs_buf_append(tmpbuf, expbuf, strlen(expbuf));
3495: /* Point c to end of keyword. */
3496: tlen = cvs_buf_len(tmpbuf) - 1;
3497: /* Append everything after keyword. */
3498: cvs_buf_append(tmpbuf, end,
3499: line->l_line + line->l_len - end);
3500: c = cvs_buf_get(tmpbuf) + tlen;
3501: /* Point fin to end of data. */
3502: fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1;
3503: /* Recalculate new length. */
3504: len = cvs_buf_len(tmpbuf);
3505:
3506: /* tmpbuf is now ready, convert to string */
3507: if (line->l_needsfree)
3508: xfree(line->l_line);
3509: line->l_len = len;
3510: line->l_line = cvs_buf_release(tmpbuf);
3511: line->l_needsfree = 1;
1.196 niallo 3512: }
1.131 niallo 3513: }