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