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