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