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