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