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