Annotation of src/usr.bin/cvs/rcs.c, Revision 1.285
1.285 ! joris 1: /* $OpenBSD: rcs.c,v 1.284 2009/02/15 13:40:33 joris Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.15 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.15 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.15 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.15 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.15 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.210 otto 27: #include <sys/stat.h>
28:
29: #include <ctype.h>
30: #include <errno.h>
31: #include <libgen.h>
32: #include <pwd.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <unistd.h>
1.1 jfb 36:
1.219 tobias 37: #include "atomicio.h"
1.94 joris 38: #include "cvs.h"
1.172 joris 39: #include "diff.h"
1.1 jfb 40: #include "rcs.h"
41:
1.57 xsa 42: #define RCS_BUFSIZE 16384
43: #define RCS_BUFEXTSIZE 8192
1.117 niallo 44: #define RCS_KWEXP_SIZE 1024
1.1 jfb 45:
46: /* RCS token types */
1.57 xsa 47: #define RCS_TOK_ERR -1
48: #define RCS_TOK_EOF 0
49: #define RCS_TOK_NUM 1
50: #define RCS_TOK_ID 2
51: #define RCS_TOK_STRING 3
52: #define RCS_TOK_SCOLON 4
53: #define RCS_TOK_COLON 5
54:
55: #define RCS_TOK_HEAD 8
56: #define RCS_TOK_BRANCH 9
57: #define RCS_TOK_ACCESS 10
58: #define RCS_TOK_SYMBOLS 11
59: #define RCS_TOK_LOCKS 12
60: #define RCS_TOK_COMMENT 13
61: #define RCS_TOK_EXPAND 14
62: #define RCS_TOK_DATE 15
63: #define RCS_TOK_AUTHOR 16
64: #define RCS_TOK_STATE 17
65: #define RCS_TOK_NEXT 18
66: #define RCS_TOK_BRANCHES 19
67: #define RCS_TOK_DESC 20
68: #define RCS_TOK_LOG 21
69: #define RCS_TOK_TEXT 22
70: #define RCS_TOK_STRICT 23
1.1 jfb 71:
1.57 xsa 72: #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
1.1 jfb 73:
1.57 xsa 74: #define RCS_NOSCOL 0x01 /* no terminating semi-colon */
75: #define RCS_VOPT 0x02 /* value is optional */
1.1 jfb 76:
1.218 tobias 77: #define ANNOTATE_NEVER 0
78: #define ANNOTATE_NOW 1
79: #define ANNOTATE_LATER 2
80:
1.1 jfb 81: /* opaque parse data */
82: struct rcs_pdata {
1.57 xsa 83: u_int rp_lines;
1.1 jfb 84:
1.57 xsa 85: char *rp_buf;
86: size_t rp_blen;
87: char *rp_bufend;
88: size_t rp_tlen;
1.1 jfb 89:
90: /* pushback token buffer */
1.57 xsa 91: char rp_ptok[128];
92: int rp_pttype; /* token type, RCS_TOK_ERR if no token */
1.1 jfb 93:
1.57 xsa 94: FILE *rp_file;
1.1 jfb 95: };
96:
1.57 xsa 97: #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
98: #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen
1.1 jfb 99:
1.47 jfb 100: /* invalid characters in RCS symbol names */
1.49 jfb 101: static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
1.47 jfb 102:
1.51 jfb 103: /* comment leaders, depending on the file's suffix */
104: static const struct rcs_comment {
1.57 xsa 105: const char *rc_suffix;
106: const char *rc_cstr;
1.51 jfb 107: } rcs_comments[] = {
108: { "1", ".\\\" " },
109: { "2", ".\\\" " },
110: { "3", ".\\\" " },
111: { "4", ".\\\" " },
112: { "5", ".\\\" " },
113: { "6", ".\\\" " },
114: { "7", ".\\\" " },
115: { "8", ".\\\" " },
116: { "9", ".\\\" " },
117: { "a", "-- " }, /* Ada */
118: { "ada", "-- " },
119: { "adb", "-- " },
120: { "asm", ";; " }, /* assembler (MS-DOS) */
121: { "ads", "-- " }, /* Ada */
122: { "bat", ":: " }, /* batch (MS-DOS) */
123: { "body", "-- " }, /* Ada */
124: { "c", " * " }, /* C */
125: { "c++", "// " }, /* C++ */
126: { "cc", "// " },
127: { "cpp", "// " },
128: { "cxx", "// " },
129: { "m", "// " }, /* Objective-C */
130: { "cl", ";;; " }, /* Common Lisp */
131: { "cmd", ":: " }, /* command (OS/2) */
132: { "cmf", "c " }, /* CM Fortran */
133: { "csh", "# " }, /* shell */
134: { "e", "# " }, /* efl */
135: { "epsf", "% " }, /* encapsulated postscript */
136: { "epsi", "% " }, /* encapsulated postscript */
137: { "el", "; " }, /* Emacs Lisp */
138: { "f", "c " }, /* Fortran */
139: { "for", "c " },
140: { "h", " * " }, /* C-header */
141: { "hh", "// " }, /* C++ header */
142: { "hpp", "// " },
143: { "hxx", "// " },
144: { "in", "# " }, /* for Makefile.in */
145: { "l", " * " }, /* lex */
146: { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
147: { "mak", "# " }, /* makefile, e.g. Visual C++ */
148: { "me", ".\\\" " }, /* me-macros t/nroff */
149: { "ml", "; " }, /* mocklisp */
150: { "mm", ".\\\" " }, /* mm-macros t/nroff */
151: { "ms", ".\\\" " }, /* ms-macros t/nroff */
152: { "man", ".\\\" " }, /* man-macros t/nroff */
153: { "p", " * " }, /* pascal */
154: { "pas", " * " },
1.52 jfb 155: { "pl", "# " }, /* Perl (conflict with Prolog) */
156: { "pm", "# " }, /* Perl module */
1.51 jfb 157: { "ps", "% " }, /* postscript */
158: { "psw", "% " }, /* postscript wrap */
159: { "pswm", "% " }, /* postscript wrap */
160: { "r", "# " }, /* ratfor */
161: { "rc", " * " }, /* Microsoft Windows resource file */
162: { "red", "% " }, /* psl/rlisp */
163: { "sh", "# " }, /* shell */
164: { "sl", "% " }, /* psl */
165: { "spec", "-- " }, /* Ada */
166: { "tex", "% " }, /* tex */
167: { "y", " * " }, /* yacc */
168: { "ye", " * " }, /* yacc-efl */
169: { "yr", " * " }, /* yacc-ratfor */
170: };
171:
1.128 niallo 172: struct rcs_kw rcs_expkw[] = {
173: { "Author", RCS_KW_AUTHOR },
174: { "Date", RCS_KW_DATE },
175: { "Header", RCS_KW_HEADER },
176: { "Id", RCS_KW_ID },
1.229 tobias 177: { "Locker", RCS_KW_LOCKER },
1.128 niallo 178: { "Log", RCS_KW_LOG },
179: { "Name", RCS_KW_NAME },
180: { "RCSfile", RCS_KW_RCSFILE },
181: { "Revision", RCS_KW_REVISION },
182: { "Source", RCS_KW_SOURCE },
183: { "State", RCS_KW_STATE },
1.211 niallo 184: { "Mdocdate", RCS_KW_MDOCDATE },
1.128 niallo 185: };
186:
1.57 xsa 187: #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
1.51 jfb 188:
1.1 jfb 189: static struct rcs_key {
1.57 xsa 190: char rk_str[16];
191: int rk_id;
192: int rk_val;
193: int rk_flags;
1.1 jfb 194: } rcs_keys[] = {
195: { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT },
1.41 jfb 196: { "author", RCS_TOK_AUTHOR, RCS_TOK_ID, 0 },
1.1 jfb 197: { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT },
198: { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT },
199: { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT },
200: { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 },
201: { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL },
202: { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT },
203: { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT },
204: { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 },
205: { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL },
206: { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT },
1.41 jfb 207: { "state", RCS_TOK_STATE, RCS_TOK_ID, RCS_VOPT },
1.1 jfb 208: { "strict", RCS_TOK_STRICT, 0, 0, },
209: { "symbols", RCS_TOK_SYMBOLS, 0, 0 },
210: { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL },
211: };
212:
1.57 xsa 213: #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0]))
1.1 jfb 214:
1.246 tobias 215: static RCSNUM *rcs_get_revision(const char *, RCSFILE *);
1.218 tobias 216: int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *,
217: struct cvs_line **, struct rcs_delta *);
1.167 ray 218: static void rcs_parse_init(RCSFILE *);
1.57 xsa 219: static int rcs_parse_admin(RCSFILE *);
220: static int rcs_parse_delta(RCSFILE *);
1.146 xsa 221: static void rcs_parse_deltas(RCSFILE *, RCSNUM *);
1.57 xsa 222: static int rcs_parse_deltatext(RCSFILE *);
1.146 xsa 223: static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *);
1.148 xsa 224: static void rcs_parse_desc(RCSFILE *, RCSNUM *);
1.57 xsa 225:
226: static int rcs_parse_access(RCSFILE *);
227: static int rcs_parse_symbols(RCSFILE *);
228: static int rcs_parse_locks(RCSFILE *);
229: static int rcs_parse_branches(RCSFILE *, struct rcs_delta *);
230: static void rcs_freedelta(struct rcs_delta *);
231: static void rcs_freepdata(struct rcs_pdata *);
232: static int rcs_gettok(RCSFILE *);
233: static int rcs_pushtok(RCSFILE *, const char *, int);
1.151 xsa 234: static void rcs_growbuf(RCSFILE *);
1.155 ray 235: static void rcs_strprint(const u_char *, size_t, FILE *);
1.57 xsa 236:
1.233 tobias 237: static void rcs_kwexp_line(char *, struct rcs_delta *, struct cvs_lines *,
238: struct cvs_line *, int mode);
1.26 jfb 239:
1.271 joris 240: static int rcs_ignore_keys = 0;
241:
1.60 xsa 242: RCSFILE *
1.172 joris 243: rcs_open(const char *path, int fd, int flags, ...)
1.1 jfb 244: {
1.172 joris 245: int mode;
1.259 tobias 246: mode_t fmode;
1.1 jfb 247: RCSFILE *rfp;
1.26 jfb 248: va_list vap;
1.269 joris 249: struct stat st;
1.109 joris 250: struct rcs_delta *rdp;
251: struct rcs_lock *lkr;
1.26 jfb 252:
1.152 niallo 253: fmode = S_IRUSR|S_IRGRP|S_IROTH;
1.26 jfb 254: flags &= 0xffff; /* ditch any internal flags */
1.1 jfb 255:
1.172 joris 256: if (flags & RCS_CREATE) {
257: va_start(vap, flags);
258: mode = va_arg(vap, int);
259: va_end(vap);
260: fmode = (mode_t)mode;
1.269 joris 261: } else {
262: if (fstat(fd, &st) == -1)
263: fatal("rcs_open: %s: fstat: %s", path, strerror(errno));
264: fmode = st.st_mode;
1.1 jfb 265: }
1.228 tobias 266:
1.259 tobias 267: fmode &= ~cvs_umask;
1.1 jfb 268:
1.161 ray 269: rfp = xcalloc(1, sizeof(*rfp));
1.1 jfb 270:
1.110 joris 271: rfp->rf_path = xstrdup(path);
1.142 niallo 272: rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
1.26 jfb 273: rfp->rf_mode = fmode;
1.172 joris 274: rfp->fd = fd;
1.175 joris 275: rfp->rf_dead = 0;
1.1 jfb 276:
277: TAILQ_INIT(&(rfp->rf_delta));
1.29 jfb 278: TAILQ_INIT(&(rfp->rf_access));
1.1 jfb 279: TAILQ_INIT(&(rfp->rf_symbols));
280: TAILQ_INIT(&(rfp->rf_locks));
281:
1.167 ray 282: if (!(rfp->rf_flags & RCS_CREATE))
283: rcs_parse_init(rfp);
1.1 jfb 284:
1.109 joris 285: /* fill in rd_locker */
286: TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
287: if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
288: rcs_close(rfp);
289: return (NULL);
290: }
291:
1.110 joris 292: rdp->rd_locker = xstrdup(lkr->rl_name);
1.109 joris 293: }
294:
1.1 jfb 295: return (rfp);
296: }
297:
298: /*
299: * rcs_close()
300: *
301: * Close an RCS file handle.
302: */
303: void
304: rcs_close(RCSFILE *rfp)
305: {
306: struct rcs_delta *rdp;
1.71 moritz 307: struct rcs_access *rap;
1.13 jfb 308: struct rcs_lock *rlp;
309: struct rcs_sym *rsp;
1.1 jfb 310:
1.26 jfb 311: if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
312: rcs_write(rfp);
313:
1.1 jfb 314: while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
315: rdp = TAILQ_FIRST(&(rfp->rf_delta));
316: TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
317: rcs_freedelta(rdp);
1.71 moritz 318: }
319:
320: while (!TAILQ_EMPTY(&(rfp->rf_access))) {
321: rap = TAILQ_FIRST(&(rfp->rf_access));
322: TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
1.110 joris 323: xfree(rap->ra_name);
324: xfree(rap);
1.1 jfb 325: }
326:
1.13 jfb 327: while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
328: rsp = TAILQ_FIRST(&(rfp->rf_symbols));
329: TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
330: rcsnum_free(rsp->rs_num);
1.110 joris 331: xfree(rsp->rs_name);
332: xfree(rsp);
1.13 jfb 333: }
334:
335: while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
336: rlp = TAILQ_FIRST(&(rfp->rf_locks));
337: TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
338: rcsnum_free(rlp->rl_num);
1.110 joris 339: xfree(rlp->rl_name);
340: xfree(rlp);
1.13 jfb 341: }
342:
1.1 jfb 343: if (rfp->rf_head != NULL)
344: rcsnum_free(rfp->rf_head);
1.11 joris 345: if (rfp->rf_branch != NULL)
346: rcsnum_free(rfp->rf_branch);
1.1 jfb 347:
348: if (rfp->rf_path != NULL)
1.110 joris 349: xfree(rfp->rf_path);
1.1 jfb 350: if (rfp->rf_comment != NULL)
1.110 joris 351: xfree(rfp->rf_comment);
1.1 jfb 352: if (rfp->rf_expand != NULL)
1.110 joris 353: xfree(rfp->rf_expand);
1.1 jfb 354: if (rfp->rf_desc != NULL)
1.110 joris 355: xfree(rfp->rf_desc);
1.118 joris 356: if (rfp->rf_pdata != NULL)
357: rcs_freepdata(rfp->rf_pdata);
1.110 joris 358: xfree(rfp);
1.1 jfb 359: }
360:
361: /*
362: * rcs_write()
363: *
364: * Write the contents of the RCS file handle <rfp> to disk in the file whose
365: * path is in <rf_path>.
366: */
1.172 joris 367: void
1.1 jfb 368: rcs_write(RCSFILE *rfp)
369: {
370: FILE *fp;
1.214 xsa 371: char buf[1024], numbuf[CVS_REV_BUFSZ], *fn, tmpdir[MAXPATHLEN];
1.29 jfb 372: struct rcs_access *ap;
1.1 jfb 373: struct rcs_sym *symp;
1.46 jfb 374: struct rcs_branch *brp;
1.1 jfb 375: struct rcs_delta *rdp;
1.75 joris 376: struct rcs_lock *lkp;
1.100 xsa 377: size_t len;
1.207 otto 378: int fd, saved_errno;
1.75 joris 379:
1.207 otto 380: fd = -1;
1.1 jfb 381:
1.137 joris 382: if (rfp->rf_flags & RCS_SYNCED)
1.181 joris 383: return;
384:
385: if (cvs_noexec == 1)
1.172 joris 386: return;
1.137 joris 387:
1.117 niallo 388: /* Write operations need the whole file parsed */
389: rcs_parse_deltatexts(rfp, NULL);
1.1 jfb 390:
1.207 otto 391: if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
392: fatal("rcs_write: truncation");
393: (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
1.172 joris 394:
1.113 xsa 395: if ((fd = mkstemp(fn)) == -1)
1.172 joris 396: fatal("%s", fn);
1.113 xsa 397:
1.207 otto 398: if ((fp = fdopen(fd, "w")) == NULL) {
1.172 joris 399: saved_errno = errno;
400: (void)unlink(fn);
1.207 otto 401: fatal("fdopen %s: %s", fn, strerror(saved_errno));
1.1 jfb 402: }
1.284 joris 403:
404: cvs_worklist_add(fn, &temp_files);
1.1 jfb 405:
1.28 jfb 406: if (rfp->rf_head != NULL)
407: rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
408: else
409: numbuf[0] = '\0';
410:
1.1 jfb 411: fprintf(fp, "head\t%s;\n", numbuf);
1.35 jfb 412:
413: if (rfp->rf_branch != NULL) {
414: rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
415: fprintf(fp, "branch\t%s;\n", numbuf);
416: }
417:
1.29 jfb 418: fputs("access", fp);
419: TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
420: fprintf(fp, "\n\t%s", ap->ra_name);
421: }
422: fputs(";\n", fp);
1.1 jfb 423:
1.85 joris 424: fprintf(fp, "symbols");
1.1 jfb 425: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
1.243 tobias 426: if (RCSNUM_ISBRANCH(symp->rs_num))
427: rcsnum_addmagic(symp->rs_num);
1.1 jfb 428: rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
1.172 joris 429: if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) ||
430: strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) ||
431: strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf))
432: fatal("rcs_write: string overflow");
1.85 joris 433: fprintf(fp, "\n\t%s", buf);
1.1 jfb 434: }
435: fprintf(fp, ";\n");
436:
1.75 joris 437: fprintf(fp, "locks");
438: TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
439: rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
440: fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
441: }
442:
443: fprintf(fp, ";");
1.1 jfb 444:
1.26 jfb 445: if (rfp->rf_flags & RCS_SLOCK)
1.1 jfb 446: fprintf(fp, " strict;");
447: fputc('\n', fp);
448:
1.129 xsa 449: fputs("comment\t@", fp);
1.42 jfb 450: if (rfp->rf_comment != NULL) {
1.58 xsa 451: rcs_strprint((const u_char *)rfp->rf_comment,
452: strlen(rfp->rf_comment), fp);
1.42 jfb 453: fputs("@;\n", fp);
1.129 xsa 454: } else
455: fputs("# @;\n", fp);
1.1 jfb 456:
1.42 jfb 457: if (rfp->rf_expand != NULL) {
458: fputs("expand @", fp);
1.58 xsa 459: rcs_strprint((const u_char *)rfp->rf_expand,
460: strlen(rfp->rf_expand), fp);
1.42 jfb 461: fputs("@;\n", fp);
462: }
1.1 jfb 463:
1.46 jfb 464: fputs("\n\n", fp);
1.1 jfb 465:
466: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
467: fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
468: sizeof(numbuf)));
469: fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
1.44 jfb 470: rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
1.1 jfb 471: rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
472: rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
473: fprintf(fp, "\tauthor %s;\tstate %s;\n",
474: rdp->rd_author, rdp->rd_state);
1.46 jfb 475: fputs("branches", fp);
476: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.232 tobias 477: fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
1.46 jfb 478: sizeof(numbuf)));
479: }
480: fputs(";\n", fp);
1.1 jfb 481: fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
482: numbuf, sizeof(numbuf)));
483: }
484:
1.42 jfb 485: fputs("\ndesc\n@", fp);
1.141 ray 486: if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
1.100 xsa 487: rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
488: if (rfp->rf_desc[len-1] != '\n')
489: fputc('\n', fp);
490: }
491: fputs("@\n", fp);
1.1 jfb 492:
493: /* deltatexts */
494: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.100 xsa 495: fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
1.1 jfb 496: sizeof(numbuf)));
1.42 jfb 497: fputs("log\n@", fp);
1.100 xsa 498: if (rdp->rd_log != NULL) {
499: len = strlen(rdp->rd_log);
500: rcs_strprint((const u_char *)rdp->rd_log, len, fp);
501: if (rdp->rd_log[len-1] != '\n')
502: fputc('\n', fp);
503: }
1.42 jfb 504: fputs("@\ntext\n@", fp);
1.100 xsa 505: if (rdp->rd_text != NULL) {
506: rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
507: }
508: fputs("@\n", fp);
1.1 jfb 509: }
1.269 joris 510:
1.207 otto 511: if (fchmod(fd, rfp->rf_mode) == -1) {
512: saved_errno = errno;
513: (void)unlink(fn);
514: fatal("fchmod %s: %s", fn, strerror(saved_errno));
515: }
516:
1.172 joris 517: (void)fclose(fp);
1.73 joris 518:
1.207 otto 519: if (rename(fn, rfp->rf_path) == -1) {
520: saved_errno = errno;
1.172 joris 521: (void)unlink(fn);
1.207 otto 522: fatal("rename(%s, %s): %s", fn, rfp->rf_path,
523: strerror(saved_errno));
1.172 joris 524: }
1.73 joris 525:
1.172 joris 526: rfp->rf_flags |= RCS_SYNCED;
1.73 joris 527:
1.172 joris 528: if (fn != NULL)
529: xfree(fn);
1.1 jfb 530: }
531:
532: /*
1.43 jfb 533: * rcs_head_get()
534: *
535: * Retrieve the revision number of the head revision for the RCS file <file>.
536: */
1.180 joris 537: RCSNUM *
1.43 jfb 538: rcs_head_get(RCSFILE *file)
539: {
1.213 niallo 540: struct rcs_branch *brp;
541: struct rcs_delta *rdp;
542: RCSNUM *rev, *rootrev;
1.180 joris 543:
1.234 tobias 544: if (file->rf_head == NULL)
545: return NULL;
546:
1.213 niallo 547: rev = rcsnum_alloc();
1.180 joris 548: if (file->rf_branch != NULL) {
1.213 niallo 549: /* we have a default branch, use that to calculate the
550: * real HEAD*/
551: rootrev = rcsnum_alloc();
552: rcsnum_cpy(file->rf_branch, rootrev, 2);
553: if ((rdp = rcs_findrev(file, rootrev)) == NULL)
554: fatal("rcs_head_get: could not find root revision");
555:
556: /* HEAD should be the last revision on the default branch */
557: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
558: if (TAILQ_NEXT(brp, rb_list) == NULL)
559: break;
560: }
561: rcsnum_free(rootrev);
1.226 tobias 562:
563: if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
564: fatal("rcs_head_get: could not find branch revision");
565: while (rdp->rd_next->rn_len != 0)
566: if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
567: fatal("rcs_head_get: could not find "
568: "next branch revision");
569:
570: rcsnum_cpy(rdp->rd_num, rev, 0);
1.195 joris 571: } else {
572: rcsnum_cpy(file->rf_head, rev, 0);
1.180 joris 573: }
574:
1.195 joris 575: return (rev);
1.43 jfb 576: }
577:
578: /*
579: * rcs_head_set()
580: *
581: * Set the revision number of the head revision for the RCS file <file> to
582: * <rev>, which must reference a valid revision within the file.
583: */
584: int
1.117 niallo 585: rcs_head_set(RCSFILE *file, RCSNUM *rev)
1.43 jfb 586: {
1.167 ray 587: if (rcs_findrev(file, rev) == NULL)
1.43 jfb 588: return (-1);
589:
1.111 joris 590: if (file->rf_head == NULL)
591: file->rf_head = rcsnum_alloc();
1.43 jfb 592:
1.111 joris 593: rcsnum_cpy(rev, file->rf_head, 0);
1.70 moritz 594: file->rf_flags &= ~RCS_SYNCED;
1.43 jfb 595: return (0);
596: }
597:
1.276 tobias 598: /*
599: * rcs_branch_new()
600: *
601: * Create a new branch out of supplied revision for the RCS file <file>.
602: */
603: RCSNUM *
604: rcs_branch_new(RCSFILE *file, RCSNUM *rev)
605: {
606: RCSNUM *brev;
607: struct rcs_sym *sym;
608:
609: if ((brev = rcsnum_new_branch(rev)) == NULL)
610: return (NULL);
611:
612: for (;;) {
613: TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)
614: if (!rcsnum_cmp(sym->rs_num, brev, 0))
615: break;
616:
617: if (sym != NULL) {
618: if (rcsnum_inc(brev) == NULL ||
619: rcsnum_inc(brev) == NULL)
620: return (NULL);
621: } else
622: break;
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:
1.18 jfb 1653: pdp->rp_lines = 0;
1.1 jfb 1654: pdp->rp_pttype = RCS_TOK_ERR;
1655:
1.172 joris 1656: if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL)
1.216 xsa 1657: fatal("fdopen: `%s'", rfp->rf_path);
1.1 jfb 1658:
1.161 ray 1659: pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE);
1.1 jfb 1660: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 1661: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 1662:
1663: /* ditch the strict lock */
1.26 jfb 1664: rfp->rf_flags &= ~RCS_SLOCK;
1.1 jfb 1665: rfp->rf_pdata = pdp;
1666:
1.167 ray 1667: if (rcs_parse_admin(rfp) < 0) {
1.1 jfb 1668: rcs_freepdata(pdp);
1.117 niallo 1669: fatal("could not parse admin data");
1.1 jfb 1670: }
1671:
1.277 joris 1672: if (rfp->rf_flags & RCS_PARSE_FULLY) {
1.117 niallo 1673: rcs_parse_deltatexts(rfp, NULL);
1.277 joris 1674: (void)close(rfp->fd);
1675: rfp->fd = -1;
1676: }
1.1 jfb 1677:
1.117 niallo 1678: rfp->rf_flags |= RCS_SYNCED;
1.1 jfb 1679: }
1680:
1681: /*
1682: * rcs_parse_admin()
1683: *
1684: * Parse the administrative portion of an RCS file.
1.31 jfb 1685: * Returns the type of the first token found after the admin section on
1686: * success, or -1 on failure.
1.1 jfb 1687: */
1688: static int
1689: rcs_parse_admin(RCSFILE *rfp)
1690: {
1691: u_int i;
1692: int tok, ntok, hmask;
1693: struct rcs_key *rk;
1694:
1.180 joris 1695: rfp->rf_head = NULL;
1696: rfp->rf_branch = NULL;
1697:
1.1 jfb 1698: /* hmask is a mask of the headers already encountered */
1699: hmask = 0;
1700: for (;;) {
1701: tok = rcs_gettok(rfp);
1702: if (tok == RCS_TOK_ERR) {
1703: cvs_log(LP_ERR, "parse error in RCS admin section");
1.147 ray 1704: goto fail;
1.168 deraadt 1705: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.31 jfb 1706: /*
1707: * Assume this is the start of the first delta or
1708: * that we are dealing with an empty RCS file and
1709: * we just found the description.
1710: */
1.275 tobias 1711: if (!(hmask & (1 << RCS_TOK_HEAD))) {
1712: cvs_log(LP_ERR, "head missing");
1713: goto fail;
1714: }
1.1 jfb 1715: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.31 jfb 1716: return (tok);
1.1 jfb 1717: }
1718:
1719: rk = NULL;
1.18 jfb 1720: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1721: if (rcs_keys[i].rk_id == tok)
1722: rk = &(rcs_keys[i]);
1723:
1724: if (hmask & (1 << tok)) {
1725: cvs_log(LP_ERR, "duplicate RCS key");
1.147 ray 1726: goto fail;
1.1 jfb 1727: }
1728: hmask |= (1 << tok);
1729:
1730: switch (tok) {
1731: case RCS_TOK_HEAD:
1732: case RCS_TOK_BRANCH:
1733: case RCS_TOK_COMMENT:
1734: case RCS_TOK_EXPAND:
1735: ntok = rcs_gettok(rfp);
1736: if (ntok == RCS_TOK_SCOLON)
1737: break;
1738: if (ntok != rk->rk_val) {
1739: cvs_log(LP_ERR,
1740: "invalid value type for RCS key `%s'",
1741: rk->rk_str);
1742: }
1743:
1744: if (tok == RCS_TOK_HEAD) {
1.111 joris 1745: if (rfp->rf_head == NULL)
1.28 jfb 1746: rfp->rf_head = rcsnum_alloc();
1.275 tobias 1747: if (RCS_TOKSTR(rfp)[0] == '\0' ||
1748: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1749: rfp->rf_head) < 0) {
1750: rcsnum_free(rfp->rf_head);
1751: rfp->rf_head = NULL;
1752: }
1.14 deraadt 1753: } else if (tok == RCS_TOK_BRANCH) {
1.111 joris 1754: if (rfp->rf_branch == NULL)
1.35 jfb 1755: rfp->rf_branch = rcsnum_alloc();
1756: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1757: rfp->rf_branch) < 0)
1.147 ray 1758: goto fail;
1.14 deraadt 1759: } else if (tok == RCS_TOK_COMMENT) {
1.110 joris 1760: rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp));
1.14 deraadt 1761: } else if (tok == RCS_TOK_EXPAND) {
1.110 joris 1762: rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1763: }
1764:
1765: /* now get the expected semi-colon */
1766: ntok = rcs_gettok(rfp);
1767: if (ntok != RCS_TOK_SCOLON) {
1768: cvs_log(LP_ERR,
1769: "missing semi-colon after RCS `%s' key",
1.26 jfb 1770: rk->rk_str);
1.147 ray 1771: goto fail;
1.1 jfb 1772: }
1773: break;
1774: case RCS_TOK_ACCESS:
1.29 jfb 1775: if (rcs_parse_access(rfp) < 0)
1.147 ray 1776: goto fail;
1.1 jfb 1777: break;
1778: case RCS_TOK_SYMBOLS:
1.271 joris 1779: rcs_ignore_keys = 1;
1.29 jfb 1780: if (rcs_parse_symbols(rfp) < 0)
1.147 ray 1781: goto fail;
1.1 jfb 1782: break;
1783: case RCS_TOK_LOCKS:
1.29 jfb 1784: if (rcs_parse_locks(rfp) < 0)
1.147 ray 1785: goto fail;
1.1 jfb 1786: break;
1787: default:
1788: cvs_log(LP_ERR,
1789: "unexpected token `%s' in RCS admin section",
1790: RCS_TOKSTR(rfp));
1.147 ray 1791: goto fail;
1.1 jfb 1792: }
1.271 joris 1793:
1794: rcs_ignore_keys = 0;
1795:
1.1 jfb 1796: }
1797:
1.147 ray 1798: fail:
1799: return (-1);
1.1 jfb 1800: }
1801:
1802: /*
1803: * rcs_parse_delta()
1804: *
1805: * Parse an RCS delta section and allocate the structure to store that delta's
1806: * information in the <rfp> delta list.
1807: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1808: * -1 on error.
1809: */
1810: static int
1811: rcs_parse_delta(RCSFILE *rfp)
1812: {
1813: int ret, tok, ntok, hmask;
1814: u_int i;
1815: char *tokstr;
1.3 vincent 1816: RCSNUM *datenum;
1.1 jfb 1817: struct rcs_delta *rdp;
1818: struct rcs_key *rk;
1819:
1820: tok = rcs_gettok(rfp);
1.154 ray 1821: if (tok == RCS_TOK_DESC) {
1822: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1823: return (0);
1824: } else if (tok != RCS_TOK_NUM) {
1.1 jfb 1825: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1826: RCS_TOKSTR(rfp));
1827: return (-1);
1828: }
1.192 niallo 1829:
1830: rdp = xcalloc(1, sizeof(*rdp));
1831:
1832: rdp->rd_num = rcsnum_alloc();
1833: rdp->rd_next = rcsnum_alloc();
1834:
1835: TAILQ_INIT(&(rdp->rd_branches));
1836:
1.1 jfb 1837: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1838:
1839: hmask = 0;
1840: ret = 0;
1841: tokstr = NULL;
1842:
1843: for (;;) {
1844: tok = rcs_gettok(rfp);
1845: if (tok == RCS_TOK_ERR) {
1846: cvs_log(LP_ERR, "parse error in RCS delta section");
1847: rcs_freedelta(rdp);
1848: return (-1);
1.14 deraadt 1849: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1850: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1851: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1852: break;
1853: }
1854:
1855: rk = NULL;
1.18 jfb 1856: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1857: if (rcs_keys[i].rk_id == tok)
1858: rk = &(rcs_keys[i]);
1859:
1860: if (hmask & (1 << tok)) {
1861: cvs_log(LP_ERR, "duplicate RCS key");
1862: rcs_freedelta(rdp);
1863: return (-1);
1864: }
1865: hmask |= (1 << tok);
1866:
1867: switch (tok) {
1868: case RCS_TOK_DATE:
1869: case RCS_TOK_AUTHOR:
1870: case RCS_TOK_STATE:
1871: case RCS_TOK_NEXT:
1872: ntok = rcs_gettok(rfp);
1873: if (ntok == RCS_TOK_SCOLON) {
1874: if (rk->rk_flags & RCS_VOPT)
1875: break;
1876: else {
1877: cvs_log(LP_ERR, "missing mandatory "
1878: "value to RCS key `%s'",
1879: rk->rk_str);
1880: rcs_freedelta(rdp);
1881: return (-1);
1882: }
1883: }
1884:
1885: if (ntok != rk->rk_val) {
1886: cvs_log(LP_ERR,
1887: "invalid value type for RCS key `%s'",
1888: rk->rk_str);
1889: rcs_freedelta(rdp);
1890: return (-1);
1891: }
1892:
1893: if (tokstr != NULL)
1.110 joris 1894: xfree(tokstr);
1895: tokstr = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 1896: /* now get the expected semi-colon */
1897: ntok = rcs_gettok(rfp);
1898: if (ntok != RCS_TOK_SCOLON) {
1899: cvs_log(LP_ERR,
1900: "missing semi-colon after RCS `%s' key",
1.26 jfb 1901: rk->rk_str);
1.110 joris 1902: xfree(tokstr);
1.1 jfb 1903: rcs_freedelta(rdp);
1904: return (-1);
1905: }
1906:
1907: if (tok == RCS_TOK_DATE) {
1.25 jfb 1908: if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1.110 joris 1909: xfree(tokstr);
1.11 joris 1910: rcs_freedelta(rdp);
1911: return (-1);
1912: }
1.3 vincent 1913: if (datenum->rn_len != 6) {
1.1 jfb 1914: cvs_log(LP_ERR,
1915: "RCS date specification has %s "
1916: "fields",
1.3 vincent 1917: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 1918: "missing");
1.110 joris 1919: xfree(tokstr);
1.1 jfb 1920: rcs_freedelta(rdp);
1.37 tedu 1921: rcsnum_free(datenum);
1922: return (-1);
1.1 jfb 1923: }
1.3 vincent 1924: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 1925: if (rdp->rd_date.tm_year >= 1900)
1926: rdp->rd_date.tm_year -= 1900;
1.3 vincent 1927: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1928: rdp->rd_date.tm_mday = datenum->rn_id[2];
1929: rdp->rd_date.tm_hour = datenum->rn_id[3];
1930: rdp->rd_date.tm_min = datenum->rn_id[4];
1931: rdp->rd_date.tm_sec = datenum->rn_id[5];
1932: rcsnum_free(datenum);
1.14 deraadt 1933: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 1934: rdp->rd_author = tokstr;
1935: tokstr = NULL;
1.14 deraadt 1936: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 1937: rdp->rd_state = tokstr;
1938: tokstr = NULL;
1.14 deraadt 1939: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 1940: rcsnum_aton(tokstr, NULL, rdp->rd_next);
1941: }
1942: break;
1943: case RCS_TOK_BRANCHES:
1.46 jfb 1944: if (rcs_parse_branches(rfp, rdp) < 0) {
1945: rcs_freedelta(rdp);
1946: return (-1);
1947: }
1.1 jfb 1948: break;
1949: default:
1.172 joris 1950: cvs_log(LP_ERR, "unexpected token `%s' in RCS delta",
1.1 jfb 1951: RCS_TOKSTR(rfp));
1952: rcs_freedelta(rdp);
1953: return (-1);
1954: }
1955: }
1956:
1.13 jfb 1957: if (tokstr != NULL)
1.110 joris 1958: xfree(tokstr);
1.13 jfb 1959:
1.1 jfb 1960: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1.26 jfb 1961: rfp->rf_ndelta++;
1.1 jfb 1962:
1963: return (ret);
1964: }
1965:
1966: /*
1967: * rcs_parse_deltatext()
1968: *
1969: * Parse an RCS delta text section and fill in the log and text field of the
1970: * appropriate delta section.
1971: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1972: * -1 on error.
1973: */
1974: static int
1975: rcs_parse_deltatext(RCSFILE *rfp)
1976: {
1977: int tok;
1978: RCSNUM *tnum;
1979: struct rcs_delta *rdp;
1980:
1981: tok = rcs_gettok(rfp);
1982: if (tok == RCS_TOK_EOF)
1983: return (0);
1984:
1985: if (tok != RCS_TOK_NUM) {
1986: cvs_log(LP_ERR,
1987: "unexpected token `%s' at start of RCS delta text",
1988: RCS_TOKSTR(rfp));
1989: return (-1);
1990: }
1.13 jfb 1991:
1992: tnum = rcsnum_alloc();
1.1 jfb 1993: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1994:
1995: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1996: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1997: break;
1998: }
1.13 jfb 1999: rcsnum_free(tnum);
2000:
1.1 jfb 2001: if (rdp == NULL) {
2002: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
2003: RCS_TOKSTR(rfp));
2004: return (-1);
2005: }
2006:
2007: tok = rcs_gettok(rfp);
2008: if (tok != RCS_TOK_LOG) {
2009: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2010: RCS_TOKSTR(rfp));
2011: return (-1);
2012: }
2013:
2014: tok = rcs_gettok(rfp);
2015: if (tok != RCS_TOK_STRING) {
2016: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2017: RCS_TOKSTR(rfp));
2018: return (-1);
2019: }
1.110 joris 2020: rdp->rd_log = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2021: tok = rcs_gettok(rfp);
2022: if (tok != RCS_TOK_TEXT) {
2023: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2024: RCS_TOKSTR(rfp));
2025: return (-1);
2026: }
2027:
2028: tok = rcs_gettok(rfp);
2029: if (tok != RCS_TOK_STRING) {
2030: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2031: RCS_TOKSTR(rfp));
2032: return (-1);
2033: }
2034:
1.193 niallo 2035: if (RCS_TOKLEN(rfp) == 0) {
2036: rdp->rd_text = xmalloc(1);
2037: rdp->rd_text[0] = '\0';
2038: rdp->rd_tlen = 0;
2039: } else {
2040: rdp->rd_text = xmalloc(RCS_TOKLEN(rfp));
2041: memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp));
2042: rdp->rd_tlen = RCS_TOKLEN(rfp);
2043: }
1.1 jfb 2044:
2045: return (1);
2046: }
2047:
2048: /*
2049: * rcs_parse_access()
2050: *
2051: * Parse the access list given as value to the `access' keyword.
2052: * Returns 0 on success, or -1 on failure.
2053: */
2054: static int
2055: rcs_parse_access(RCSFILE *rfp)
2056: {
2057: int type;
2058:
2059: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
2060: if (type != RCS_TOK_ID) {
2061: cvs_log(LP_ERR, "unexpected token `%s' in access list",
2062: RCS_TOKSTR(rfp));
2063: return (-1);
2064: }
1.29 jfb 2065:
2066: if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
2067: return (-1);
1.1 jfb 2068: }
2069:
2070: return (0);
2071: }
2072:
2073: /*
2074: * rcs_parse_symbols()
2075: *
2076: * Parse the symbol list given as value to the `symbols' keyword.
2077: * Returns 0 on success, or -1 on failure.
2078: */
2079: static int
2080: rcs_parse_symbols(RCSFILE *rfp)
2081: {
2082: int type;
2083: struct rcs_sym *symp;
2084:
2085: for (;;) {
2086: type = rcs_gettok(rfp);
2087: if (type == RCS_TOK_SCOLON)
2088: break;
2089:
1.41 jfb 2090: if (type != RCS_TOK_ID) {
1.1 jfb 2091: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2092: RCS_TOKSTR(rfp));
2093: return (-1);
2094: }
2095:
1.161 ray 2096: symp = xmalloc(sizeof(*symp));
1.110 joris 2097: symp->rs_name = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2098: symp->rs_num = rcsnum_alloc();
2099:
2100: type = rcs_gettok(rfp);
2101: if (type != RCS_TOK_COLON) {
2102: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2103: RCS_TOKSTR(rfp));
1.11 joris 2104: rcsnum_free(symp->rs_num);
1.110 joris 2105: xfree(symp->rs_name);
2106: xfree(symp);
1.1 jfb 2107: return (-1);
2108: }
2109:
2110: type = rcs_gettok(rfp);
2111: if (type != RCS_TOK_NUM) {
2112: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2113: RCS_TOKSTR(rfp));
1.11 joris 2114: rcsnum_free(symp->rs_num);
1.110 joris 2115: xfree(symp->rs_name);
2116: xfree(symp);
1.1 jfb 2117: return (-1);
2118: }
2119:
2120: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
2121: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2122: RCS_TOKSTR(rfp));
1.11 joris 2123: rcsnum_free(symp->rs_num);
1.110 joris 2124: xfree(symp->rs_name);
2125: xfree(symp);
1.1 jfb 2126: return (-1);
2127: }
2128:
1.43 jfb 2129: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
1.1 jfb 2130: }
2131:
2132: return (0);
2133: }
2134:
2135: /*
2136: * rcs_parse_locks()
2137: *
2138: * Parse the lock list given as value to the `locks' keyword.
2139: * Returns 0 on success, or -1 on failure.
2140: */
2141: static int
2142: rcs_parse_locks(RCSFILE *rfp)
2143: {
2144: int type;
2145: struct rcs_lock *lkp;
2146:
2147: for (;;) {
2148: type = rcs_gettok(rfp);
2149: if (type == RCS_TOK_SCOLON)
2150: break;
2151:
2152: if (type != RCS_TOK_ID) {
2153: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
2154: RCS_TOKSTR(rfp));
2155: return (-1);
2156: }
2157:
1.161 ray 2158: lkp = xmalloc(sizeof(*lkp));
1.110 joris 2159: lkp->rl_name = xstrdup(RCS_TOKSTR(rfp));
1.1 jfb 2160: lkp->rl_num = rcsnum_alloc();
2161:
2162: type = rcs_gettok(rfp);
2163: if (type != RCS_TOK_COLON) {
2164: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2165: RCS_TOKSTR(rfp));
1.37 tedu 2166: rcsnum_free(lkp->rl_num);
1.110 joris 2167: xfree(lkp->rl_name);
2168: xfree(lkp);
1.1 jfb 2169: return (-1);
2170: }
2171:
2172: type = rcs_gettok(rfp);
2173: if (type != RCS_TOK_NUM) {
2174: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2175: RCS_TOKSTR(rfp));
1.37 tedu 2176: rcsnum_free(lkp->rl_num);
1.110 joris 2177: xfree(lkp->rl_name);
2178: xfree(lkp);
1.1 jfb 2179: return (-1);
2180: }
2181:
2182: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
2183: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2184: RCS_TOKSTR(rfp));
1.37 tedu 2185: rcsnum_free(lkp->rl_num);
1.110 joris 2186: xfree(lkp->rl_name);
2187: xfree(lkp);
1.1 jfb 2188: return (-1);
2189: }
2190:
2191: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
2192: }
2193:
2194: /* check if we have a `strict' */
2195: type = rcs_gettok(rfp);
2196: if (type != RCS_TOK_STRICT) {
2197: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 2198: } else {
1.26 jfb 2199: rfp->rf_flags |= RCS_SLOCK;
1.1 jfb 2200:
2201: type = rcs_gettok(rfp);
2202: if (type != RCS_TOK_SCOLON) {
2203: cvs_log(LP_ERR,
2204: "missing semi-colon after `strict' keyword");
2205: return (-1);
2206: }
2207: }
2208:
2209: return (0);
2210: }
2211:
2212: /*
2213: * rcs_parse_branches()
2214: *
2215: * Parse the list of branches following a `branches' keyword in a delta.
2216: * Returns 0 on success, or -1 on failure.
2217: */
2218: static int
2219: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
2220: {
2221: int type;
2222: struct rcs_branch *brp;
2223:
2224: for (;;) {
2225: type = rcs_gettok(rfp);
2226: if (type == RCS_TOK_SCOLON)
2227: break;
2228:
2229: if (type != RCS_TOK_NUM) {
2230: cvs_log(LP_ERR,
2231: "unexpected token `%s' in list of branches",
2232: RCS_TOKSTR(rfp));
2233: return (-1);
2234: }
2235:
1.161 ray 2236: brp = xmalloc(sizeof(*brp));
1.46 jfb 2237: brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
1.11 joris 2238: if (brp->rb_num == NULL) {
1.110 joris 2239: xfree(brp);
1.11 joris 2240: return (-1);
2241: }
1.1 jfb 2242:
2243: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
2244: }
2245:
2246: return (0);
2247: }
2248:
2249: /*
2250: * rcs_freedelta()
2251: *
2252: * Free the contents of a delta structure.
2253: */
1.18 jfb 2254: static void
1.1 jfb 2255: rcs_freedelta(struct rcs_delta *rdp)
2256: {
1.12 jfb 2257: struct rcs_branch *rb;
1.1 jfb 2258:
1.12 jfb 2259: if (rdp->rd_num != NULL)
2260: rcsnum_free(rdp->rd_num);
2261: if (rdp->rd_next != NULL)
2262: rcsnum_free(rdp->rd_next);
2263:
1.1 jfb 2264: if (rdp->rd_author != NULL)
1.110 joris 2265: xfree(rdp->rd_author);
1.109 joris 2266: if (rdp->rd_locker != NULL)
1.110 joris 2267: xfree(rdp->rd_locker);
1.1 jfb 2268: if (rdp->rd_state != NULL)
1.110 joris 2269: xfree(rdp->rd_state);
1.1 jfb 2270: if (rdp->rd_log != NULL)
1.110 joris 2271: xfree(rdp->rd_log);
1.1 jfb 2272: if (rdp->rd_text != NULL)
1.110 joris 2273: xfree(rdp->rd_text);
1.12 jfb 2274:
2275: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
2276: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
2277: rcsnum_free(rb->rb_num);
1.110 joris 2278: xfree(rb);
1.1 jfb 2279: }
2280:
1.110 joris 2281: xfree(rdp);
1.1 jfb 2282: }
2283:
2284: /*
2285: * rcs_freepdata()
2286: *
2287: * Free the contents of the parser data structure.
2288: */
2289: static void
2290: rcs_freepdata(struct rcs_pdata *pd)
2291: {
2292: if (pd->rp_file != NULL)
2293: (void)fclose(pd->rp_file);
2294: if (pd->rp_buf != NULL)
1.110 joris 2295: xfree(pd->rp_buf);
2296: xfree(pd);
1.1 jfb 2297: }
2298:
2299: /*
2300: * rcs_gettok()
2301: *
2302: * Get the next RCS token from the string <str>.
2303: */
2304: static int
2305: rcs_gettok(RCSFILE *rfp)
2306: {
2307: u_int i;
2308: int ch, last, type;
1.18 jfb 2309: size_t len;
2310: char *bp;
1.1 jfb 2311: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2312:
2313: type = RCS_TOK_ERR;
2314: bp = pdp->rp_buf;
1.42 jfb 2315: pdp->rp_tlen = 0;
1.1 jfb 2316: *bp = '\0';
2317:
2318: if (pdp->rp_pttype != RCS_TOK_ERR) {
2319: type = pdp->rp_pttype;
1.172 joris 2320: if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >=
2321: pdp->rp_blen)
2322: fatal("rcs_gettok: strlcpy");
1.1 jfb 2323: pdp->rp_pttype = RCS_TOK_ERR;
2324: return (type);
2325: }
2326:
2327: /* skip leading whitespace */
2328: /* XXX we must skip backspace too for compatibility, should we? */
2329: do {
2330: ch = getc(pdp->rp_file);
2331: if (ch == '\n')
1.18 jfb 2332: pdp->rp_lines++;
1.1 jfb 2333: } while (isspace(ch));
2334:
2335: if (ch == EOF) {
2336: type = RCS_TOK_EOF;
1.14 deraadt 2337: } else if (ch == ';') {
1.1 jfb 2338: type = RCS_TOK_SCOLON;
1.14 deraadt 2339: } else if (ch == ':') {
1.1 jfb 2340: type = RCS_TOK_COLON;
1.14 deraadt 2341: } else if (isalpha(ch)) {
1.31 jfb 2342: type = RCS_TOK_ID;
1.1 jfb 2343: *(bp++) = ch;
1.18 jfb 2344: for (;;) {
1.1 jfb 2345: ch = getc(pdp->rp_file);
1.188 joris 2346: if (ch == EOF) {
2347: type = RCS_TOK_EOF;
2348: break;
2349: } else if (!isalnum(ch) && ch != '_' && ch != '-' &&
1.268 joris 2350: ch != '/' && ch != '+' && ch != '|') {
1.1 jfb 2351: ungetc(ch, pdp->rp_file);
2352: break;
2353: }
2354: *(bp++) = ch;
1.42 jfb 2355: pdp->rp_tlen++;
1.18 jfb 2356: if (bp == pdp->rp_bufend - 1) {
2357: len = bp - pdp->rp_buf;
1.151 xsa 2358: rcs_growbuf(rfp);
1.18 jfb 2359: bp = pdp->rp_buf + len;
2360: }
1.1 jfb 2361: }
2362: *bp = '\0';
2363:
1.271 joris 2364: if (type != RCS_TOK_ERR && rcs_ignore_keys != 1) {
1.18 jfb 2365: for (i = 0; i < RCS_NKEYS; i++) {
2366: if (strcmp(rcs_keys[i].rk_str,
2367: pdp->rp_buf) == 0) {
2368: type = rcs_keys[i].rk_id;
2369: break;
2370: }
1.1 jfb 2371: }
2372: }
1.14 deraadt 2373: } else if (ch == '@') {
1.1 jfb 2374: /* we have a string */
1.18 jfb 2375: type = RCS_TOK_STRING;
1.1 jfb 2376: for (;;) {
2377: ch = getc(pdp->rp_file);
1.188 joris 2378: if (ch == EOF) {
2379: type = RCS_TOK_EOF;
2380: break;
2381: } else if (ch == '@') {
1.1 jfb 2382: ch = getc(pdp->rp_file);
2383: if (ch != '@') {
2384: ungetc(ch, pdp->rp_file);
2385: break;
2386: }
1.14 deraadt 2387: } else if (ch == '\n')
1.18 jfb 2388: pdp->rp_lines++;
1.1 jfb 2389:
2390: *(bp++) = ch;
1.42 jfb 2391: pdp->rp_tlen++;
1.18 jfb 2392: if (bp == pdp->rp_bufend - 1) {
2393: len = bp - pdp->rp_buf;
1.151 xsa 2394: rcs_growbuf(rfp);
1.18 jfb 2395: bp = pdp->rp_buf + len;
2396: }
1.1 jfb 2397: }
2398:
2399: *bp = '\0';
1.14 deraadt 2400: } else if (isdigit(ch)) {
1.1 jfb 2401: *(bp++) = ch;
2402: last = ch;
2403: type = RCS_TOK_NUM;
2404:
2405: for (;;) {
2406: ch = getc(pdp->rp_file);
1.188 joris 2407: if (ch == EOF) {
2408: type = RCS_TOK_EOF;
2409: break;
2410: }
1.18 jfb 2411: if (bp == pdp->rp_bufend)
1.1 jfb 2412: break;
2413: if (!isdigit(ch) && ch != '.') {
2414: ungetc(ch, pdp->rp_file);
2415: break;
2416: }
2417:
2418: if (last == '.' && ch == '.') {
2419: type = RCS_TOK_ERR;
2420: break;
2421: }
2422: last = ch;
2423: *(bp++) = ch;
1.42 jfb 2424: pdp->rp_tlen++;
1.1 jfb 2425: }
1.18 jfb 2426: *bp = '\0';
1.1 jfb 2427: }
2428:
2429: return (type);
2430: }
2431:
2432: /*
2433: * rcs_pushtok()
2434: *
2435: * Push a token back in the parser's token buffer.
2436: */
2437: static int
2438: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
2439: {
2440: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2441:
2442: if (pdp->rp_pttype != RCS_TOK_ERR)
2443: return (-1);
2444:
2445: pdp->rp_pttype = type;
1.172 joris 2446: if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >=
2447: sizeof(pdp->rp_ptok))
2448: fatal("rcs_pushtok: strlcpy");
1.1 jfb 2449: return (0);
2450: }
2451:
1.18 jfb 2452:
2453: /*
2454: * rcs_growbuf()
2455: *
2456: * Attempt to grow the internal parse buffer for the RCS file <rf> by
2457: * RCS_BUFEXTSIZE.
2458: * In case of failure, the original buffer is left unmodified.
2459: */
1.151 xsa 2460: static void
1.18 jfb 2461: rcs_growbuf(RCSFILE *rf)
2462: {
2463: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2464:
1.212 ray 2465: pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
2466: pdp->rp_blen + RCS_BUFEXTSIZE);
1.18 jfb 2467: pdp->rp_blen += RCS_BUFEXTSIZE;
2468: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.42 jfb 2469: }
2470:
2471: /*
2472: * rcs_strprint()
2473: *
2474: * Output an RCS string <str> of size <slen> to the stream <stream>. Any
2475: * '@' characters are escaped. Otherwise, the string can contain arbitrary
2476: * binary data.
2477: */
1.155 ray 2478: static void
1.42 jfb 2479: rcs_strprint(const u_char *str, size_t slen, FILE *stream)
2480: {
2481: const u_char *ap, *ep, *sp;
1.52 jfb 2482:
2483: if (slen == 0)
1.155 ray 2484: return;
1.42 jfb 2485:
2486: ep = str + slen - 1;
2487:
2488: for (sp = str; sp <= ep;) {
2489: ap = memchr(sp, '@', ep - sp);
2490: if (ap == NULL)
2491: ap = ep;
1.155 ray 2492: (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1.42 jfb 2493:
2494: if (*ap == '@')
2495: putc('@', stream);
2496: sp = ap + 1;
1.63 joris 2497: }
2498: }
2499:
2500: /*
1.81 niallo 2501: * rcs_deltatext_set()
2502: *
2503: * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1.96 xsa 2504: * Returns -1 on error, 0 on success.
1.81 niallo 2505: */
2506: int
1.194 joris 2507: rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1.81 niallo 2508: {
2509: size_t len;
1.194 joris 2510: u_char *dtext;
1.81 niallo 2511: struct rcs_delta *rdp;
1.117 niallo 2512:
2513: /* Write operations require full parsing */
2514: rcs_parse_deltatexts(rfp, NULL);
1.81 niallo 2515:
2516: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2517: return (-1);
2518:
2519: if (rdp->rd_text != NULL)
1.110 joris 2520: xfree(rdp->rd_text);
1.81 niallo 2521:
1.194 joris 2522: len = cvs_buf_len(bp);
2523: dtext = cvs_buf_release(bp);
2524: bp = NULL;
2525:
1.103 joris 2526: if (len != 0) {
1.194 joris 2527: rdp->rd_text = xmalloc(len);
1.155 ray 2528: rdp->rd_tlen = len;
1.194 joris 2529: (void)memcpy(rdp->rd_text, dtext, len);
1.103 joris 2530: } else {
2531: rdp->rd_text = NULL;
2532: rdp->rd_tlen = 0;
2533: }
1.194 joris 2534:
2535: if (dtext != NULL)
2536: xfree(dtext);
1.18 jfb 2537:
1.86 joris 2538: return (0);
2539: }
2540:
2541: /*
2542: * rcs_rev_setlog()
2543: *
1.282 tobias 2544: * Sets the log message of revision <rev> to <logtext>.
1.86 joris 2545: */
2546: int
2547: rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
2548: {
2549: struct rcs_delta *rdp;
2550:
2551: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2552: return (-1);
2553:
2554: if (rdp->rd_log != NULL)
1.110 joris 2555: xfree(rdp->rd_log);
1.86 joris 2556:
1.110 joris 2557: rdp->rd_log = xstrdup(logtext);
1.86 joris 2558: rfp->rf_flags &= ~RCS_SYNCED;
1.95 niallo 2559: return (0);
2560: }
1.97 niallo 2561: /*
2562: * rcs_rev_getdate()
2563: *
2564: * Get the date corresponding to a given revision.
2565: * Returns the date on success, -1 on failure.
2566: */
2567: time_t
2568: rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
2569: {
2570: struct rcs_delta *rdp;
2571:
2572: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2573: return (-1);
2574:
1.204 joris 2575: return (timegm(&rdp->rd_date));
1.97 niallo 2576: }
1.95 niallo 2577:
2578: /*
2579: * rcs_state_set()
2580: *
2581: * Sets the state of revision <rev> to <state>
2582: * NOTE: default state is 'Exp'. States may not contain spaces.
2583: *
2584: * Returns -1 on failure, 0 on success.
2585: */
2586: int
2587: rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
2588: {
2589: struct rcs_delta *rdp;
2590:
2591: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2592: return (-1);
2593:
2594: if (rdp->rd_state != NULL)
1.110 joris 2595: xfree(rdp->rd_state);
1.95 niallo 2596:
1.110 joris 2597: rdp->rd_state = xstrdup(state);
1.95 niallo 2598:
2599: rfp->rf_flags &= ~RCS_SYNCED;
2600:
2601: return (0);
2602: }
2603:
2604: /*
2605: * rcs_state_check()
2606: *
2607: * Check if string <state> is valid.
2608: *
1.96 xsa 2609: * Returns 0 if the string is valid, -1 otherwise.
1.95 niallo 2610: */
2611: int
2612: rcs_state_check(const char *state)
2613: {
1.279 tobias 2614: if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP))
1.95 niallo 2615: return (-1);
2616:
1.18 jfb 2617: return (0);
1.1 jfb 2618: }
1.97 niallo 2619:
2620: /*
2621: * rcs_state_get()
2622: *
2623: * Get the state for a given revision of a specified RCSFILE.
2624: *
2625: * Returns NULL on failure.
2626: */
2627: const char *
2628: rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
2629: {
2630: struct rcs_delta *rdp;
2631:
2632: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2633: return (NULL);
2634:
2635: return (rdp->rd_state);
2636: }
2637:
1.246 tobias 2638: /* rcs_get_revision() */
2639: static RCSNUM *
2640: rcs_get_revision(const char *revstr, RCSFILE *rfp)
1.176 joris 2641: {
1.213 niallo 2642: RCSNUM *rev, *brev, *frev;
1.178 joris 2643: struct rcs_branch *brp;
1.183 joris 2644: struct rcs_delta *rdp;
1.213 niallo 2645: size_t i;
1.183 joris 2646:
2647: rdp = NULL;
1.215 joris 2648:
2649: if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
1.261 tobias 2650: if (rfp->rf_head == NULL)
1.280 tobias 2651: return (NULL);
1.261 tobias 2652:
1.215 joris 2653: frev = rcsnum_alloc();
2654: rcsnum_cpy(rfp->rf_head, frev, 0);
2655: return (frev);
2656: }
1.176 joris 2657:
1.213 niallo 2658: /* Possibly we could be passed a version number */
1.223 tobias 2659: if ((rev = rcsnum_parse(revstr)) != NULL) {
2660: /* Do not return if it is not in RCS file */
1.280 tobias 2661: if ((rdp = rcs_findrev(rfp, rev)) != NULL)
2662: return (rev);
1.223 tobias 2663: } else {
2664: /* More likely we will be passed a symbol */
2665: rev = rcs_sym_getrev(rfp, revstr);
2666: }
1.222 joris 2667:
1.213 niallo 2668: if (rev == NULL)
1.245 tobias 2669: return (NULL);
1.183 joris 2670:
1.220 joris 2671: /*
2672: * If it was not a branch, thats ok the symbolic
2673: * name refered to a revision, so return the resolved
1.265 joris 2674: * revision for the given name. */
1.224 tobias 2675: if (!RCSNUM_ISBRANCH(rev)) {
1.265 joris 2676: /* Sanity check: The first two elements of any
2677: * revision (be it on a branch or on trunk) cannot
2678: * be greater than HEAD.
2679: *
2680: * XXX: To avoid comparing to uninitialized memory,
2681: * the minimum of both revision lengths is taken
2682: * instead of just 2.
2683: */
1.280 tobias 2684: if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head,
1.265 joris 2685: MIN(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
1.224 tobias 2686: rcsnum_free(rev);
1.280 tobias 2687: return (NULL);
1.224 tobias 2688: }
1.220 joris 2689: return (rev);
1.224 tobias 2690: }
1.183 joris 2691:
1.213 niallo 2692: brev = rcsnum_alloc();
2693: rcsnum_cpy(rev, brev, rev->rn_len - 1);
1.178 joris 2694:
1.180 joris 2695: if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1.246 tobias 2696: fatal("rcs_get_revision: tag `%s' does not exist", revstr);
1.213 niallo 2697: rcsnum_free(brev);
1.178 joris 2698:
1.213 niallo 2699: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2700: for (i = 0; i < rev->rn_len; i++)
2701: if (brp->rb_num->rn_id[i] != rev->rn_id[i])
2702: break;
2703: if (i != rev->rn_len)
2704: continue;
2705: break;
2706: }
1.178 joris 2707:
1.240 tobias 2708: rcsnum_free(rev);
1.213 niallo 2709: frev = rcsnum_alloc();
2710: if (brp == NULL) {
2711: rcsnum_cpy(rdp->rd_num, frev, 0);
2712: return (frev);
2713: } else {
2714: /* Fetch the delta with the correct branch num */
1.180 joris 2715: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.246 tobias 2716: fatal("rcs_get_revision: could not fetch branch "
2717: "delta");
1.213 niallo 2718: rcsnum_cpy(rdp->rd_num, frev, 0);
2719: return (frev);
1.176 joris 2720: }
1.196 niallo 2721: }
2722:
2723: /*
2724: * rcs_rev_getlines()
2725: *
1.238 tobias 2726: * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
1.196 niallo 2727: * return it as a pointer to a struct cvs_lines.
2728: */
2729: struct cvs_lines *
1.218 tobias 2730: rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
1.196 niallo 2731: {
1.209 otto 2732: size_t plen;
1.218 tobias 2733: int annotate, done, i, nextroot;
1.196 niallo 2734: RCSNUM *tnum, *bnum;
2735: struct rcs_branch *brp;
1.218 tobias 2736: struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
1.196 niallo 2737: u_char *patch;
1.218 tobias 2738: struct cvs_line *line, *nline;
1.196 niallo 2739: struct cvs_lines *dlines, *plines;
2740:
1.275 tobias 2741: if (rfp->rf_head == NULL ||
2742: (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
1.197 joris 2743: fatal("rcs_rev_getlines: no HEAD revision");
1.196 niallo 2744:
2745: tnum = frev;
2746: rcs_parse_deltatexts(rfp, hrdp->rd_num);
2747:
2748: /* revision on branch, get the branch root */
2749: nextroot = 2;
1.278 joris 2750: bnum = rcsnum_alloc();
2751: if (RCSNUM_ISBRANCHREV(tnum))
1.196 niallo 2752: rcsnum_cpy(tnum, bnum, nextroot);
1.278 joris 2753: else
2754: rcsnum_cpy(tnum, bnum, tnum->rn_len);
1.196 niallo 2755:
1.218 tobias 2756: if (alines != NULL) {
2757: /* start with annotate first at requested revision */
2758: annotate = ANNOTATE_LATER;
2759: *alines = NULL;
2760: } else
2761: annotate = ANNOTATE_NEVER;
2762:
1.196 niallo 2763: dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
2764:
2765: done = 0;
2766:
2767: rdp = hrdp;
1.218 tobias 2768: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2769: if (annotate == ANNOTATE_LATER) {
2770: /* found requested revision for annotate */
2771: i = 0;
2772: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2773: line->l_lineno_orig = line->l_lineno;
2774: i++;
2775: }
2776:
2777: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2778: (*alines)[i] = NULL;
2779: annotate = ANNOTATE_NOW;
2780:
2781: /* annotate down to 1.1 from where we are */
1.278 joris 2782: rcsnum_free(bnum);
1.218 tobias 2783: bnum = rcsnum_parse("1.1");
2784: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2785: goto next;
2786: }
2787: } else
2788: goto next;
2789: }
1.196 niallo 2790:
1.218 tobias 2791: prdp = hrdp;
1.196 niallo 2792: if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
2793: goto done;
2794:
2795: again:
2796: for (;;) {
2797: if (rdp->rd_next->rn_len != 0) {
2798: trdp = rcs_findrev(rfp, rdp->rd_next);
2799: if (trdp == NULL)
2800: fatal("failed to grab next revision");
2801: }
2802:
2803: if (rdp->rd_tlen == 0) {
2804: rcs_parse_deltatexts(rfp, rdp->rd_num);
2805: if (rdp->rd_tlen == 0) {
2806: if (!rcsnum_differ(rdp->rd_num, bnum))
2807: break;
2808: rdp = trdp;
2809: continue;
2810: }
2811: }
2812:
2813: plen = rdp->rd_tlen;
2814: patch = rdp->rd_text;
2815: plines = cvs_splitlines(patch, plen);
1.218 tobias 2816: if (annotate == ANNOTATE_NOW)
2817: rcs_patch_lines(dlines, plines, *alines, prdp);
2818: else
2819: rcs_patch_lines(dlines, plines, NULL, NULL);
1.196 niallo 2820: cvs_freelines(plines);
2821:
1.218 tobias 2822: if (!rcsnum_differ(rdp->rd_num, bnum)) {
2823: if (annotate != ANNOTATE_LATER)
2824: break;
2825:
2826: /* found requested revision for annotate */
2827: i = 0;
2828: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2829: line->l_lineno_orig = line->l_lineno;
2830: i++;
2831: }
2832:
2833: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
2834: (*alines)[i] = NULL;
2835: annotate = ANNOTATE_NOW;
2836:
2837: /* annotate down to 1.1 from where we are */
1.278 joris 2838: rcsnum_free(bnum);
1.218 tobias 2839: bnum = rcsnum_parse("1.1");
2840:
2841: if (!rcsnum_differ(rdp->rd_num, bnum))
2842: break;
2843: }
1.196 niallo 2844:
1.218 tobias 2845: prdp = rdp;
1.196 niallo 2846: rdp = trdp;
2847: }
2848:
2849: next:
2850: if (!rcsnum_differ(rdp->rd_num, frev))
2851: done = 1;
2852:
2853: if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
2854: nextroot += 2;
2855: rcsnum_cpy(frev, bnum, nextroot);
2856:
2857: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.213 niallo 2858: for (i = 0; i < nextroot - 1; i++)
2859: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1.196 niallo 2860: break;
1.213 niallo 2861: if (i == nextroot - 1)
2862: break;
1.196 niallo 2863: }
2864:
1.218 tobias 2865: if (brp == NULL) {
2866: if (annotate != ANNOTATE_NEVER) {
2867: if (*alines != NULL)
2868: xfree(*alines);
2869: *alines = NULL;
2870: cvs_freelines(dlines);
1.278 joris 2871: rcsnum_free(bnum);
1.218 tobias 2872: return (NULL);
2873: }
1.196 niallo 2874: fatal("expected branch not found on branch list");
1.218 tobias 2875: }
1.196 niallo 2876:
2877: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.197 joris 2878: fatal("rcs_rev_getlines: failed to get delta for target rev");
1.196 niallo 2879:
2880: goto again;
2881: }
2882: done:
1.225 tobias 2883: /* put remaining lines into annotate buffer */
1.218 tobias 2884: if (annotate == ANNOTATE_NOW) {
2885: for (line = TAILQ_FIRST(&(dlines->l_lines));
2886: line != NULL; line = nline) {
2887: nline = TAILQ_NEXT(line, l_list);
2888: TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
2889: if (line->l_line == NULL) {
2890: xfree(line);
2891: continue;
2892: }
2893:
2894: line->l_delta = rdp;
2895: (*alines)[line->l_lineno_orig - 1] = line;
2896: }
2897:
2898: cvs_freelines(dlines);
2899: dlines = NULL;
2900: }
2901:
1.196 niallo 2902: if (bnum != tnum)
2903: rcsnum_free(bnum);
2904:
2905: return (dlines);
1.225 tobias 2906: }
2907:
2908: void
2909: rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines)
2910: {
2911: size_t plen;
2912: int i, nextroot;
2913: RCSNUM *bnum;
2914: struct rcs_branch *brp;
2915: struct rcs_delta *rdp, *trdp;
2916: u_char *patch;
2917: struct cvs_line *line;
2918: struct cvs_lines *dlines, *plines;
2919:
2920: if (!RCSNUM_ISBRANCHREV(frev))
2921: fatal("rcs_annotate_getlines: branch revision expected");
2922:
2923: /* revision on branch, get the branch root */
2924: nextroot = 2;
2925: bnum = rcsnum_alloc();
2926: rcsnum_cpy(frev, bnum, nextroot);
2927:
2928: /*
2929: * Going from HEAD to 1.1 enables the use of an array, which is
2930: * much faster. Unfortunately this is not possible with branch
2931: * revisions, so copy over our alines (array) into dlines (tailq).
2932: */
2933: dlines = xcalloc(1, sizeof(*dlines));
2934: TAILQ_INIT(&(dlines->l_lines));
2935: line = xcalloc(1, sizeof(*line));
2936: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2937:
2938: for (i = 0; (*alines)[i] != NULL; i++) {
2939: line = (*alines)[i];
2940: line->l_lineno = i + 1;
2941: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
2942: }
2943:
2944: rdp = rcs_findrev(rfp, bnum);
2945: if (rdp == NULL)
2946: fatal("failed to grab branch root revision");
2947:
2948: do {
2949: nextroot += 2;
2950: rcsnum_cpy(frev, bnum, nextroot);
2951:
2952: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
2953: for (i = 0; i < nextroot - 1; i++)
2954: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
2955: break;
2956: if (i == nextroot - 1)
2957: break;
2958: }
2959:
2960: if (brp == NULL)
2961: fatal("expected branch not found on branch list");
2962:
2963: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
2964: fatal("failed to get delta for target rev");
2965:
2966: for (;;) {
2967: if (rdp->rd_next->rn_len != 0) {
2968: trdp = rcs_findrev(rfp, rdp->rd_next);
2969: if (trdp == NULL)
2970: fatal("failed to grab next revision");
2971: }
2972:
2973: if (rdp->rd_tlen == 0) {
2974: rcs_parse_deltatexts(rfp, rdp->rd_num);
2975: if (rdp->rd_tlen == 0) {
2976: if (!rcsnum_differ(rdp->rd_num, bnum))
2977: break;
2978: rdp = trdp;
2979: continue;
2980: }
2981: }
2982:
2983: plen = rdp->rd_tlen;
2984: patch = rdp->rd_text;
2985: plines = cvs_splitlines(patch, plen);
2986: rcs_patch_lines(dlines, plines, NULL, rdp);
2987: cvs_freelines(plines);
2988:
2989: if (!rcsnum_differ(rdp->rd_num, bnum))
2990: break;
2991:
2992: rdp = trdp;
2993: }
2994: } while (rcsnum_differ(rdp->rd_num, frev));
2995:
2996: if (bnum != frev)
2997: rcsnum_free(bnum);
2998:
2999: /*
3000: * All lines have been parsed, now they must be copied over
3001: * into alines (array) again.
3002: */
3003: xfree(*alines);
3004:
3005: i = 0;
3006: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3007: if (line->l_line != NULL)
3008: i++;
3009: }
1.239 tobias 3010: *alines = xcalloc(i + 1, sizeof(struct cvs_line *));
1.225 tobias 3011: (*alines)[i] = NULL;
3012:
3013: i = 0;
3014: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
3015: if (line->l_line != NULL)
3016: (*alines)[i++] = line;
3017: }
1.196 niallo 3018: }
3019:
3020: /*
3021: * rcs_rev_getbuf()
3022: *
3023: * XXX: This is really really slow and should be avoided if at all possible!
3024: *
3025: * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
3026: * return it as a BUF pointer.
3027: */
3028: BUF *
1.200 joris 3029: rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
1.196 niallo 3030: {
1.200 joris 3031: int expmode, expand;
3032: struct rcs_delta *rdp;
1.196 niallo 3033: struct cvs_lines *lines;
1.233 tobias 3034: struct cvs_line *lp, *nlp;
1.196 niallo 3035: BUF *bp;
3036:
1.200 joris 3037: expand = 0;
1.218 tobias 3038: lines = rcs_rev_getlines(rfp, rev, NULL);
1.273 joris 3039: bp = cvs_buf_alloc(1024 * 16);
1.200 joris 3040:
3041: if (!(mode & RCS_KWEXP_NONE)) {
3042: if (rfp->rf_expand != NULL)
3043: expmode = rcs_kwexp_get(rfp);
3044: else
3045: expmode = RCS_KWEXP_DEFAULT;
3046:
3047: if (!(expmode & RCS_KWEXP_NONE)) {
3048: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3049: fatal("could not fetch revision");
3050: expand = 1;
3051: }
3052: }
3053:
1.255 deraadt 3054: for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
1.233 tobias 3055: nlp = TAILQ_NEXT(lp, l_list);
3056:
3057: if (lp->l_line == NULL) {
3058: lp = nlp;
1.196 niallo 3059: continue;
1.233 tobias 3060: }
1.200 joris 3061:
3062: if (expand)
1.233 tobias 3063: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.200 joris 3064:
1.233 tobias 3065: do {
3066: cvs_buf_append(bp, lp->l_line, lp->l_len);
3067: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 3068: }
3069:
3070: cvs_freelines(lines);
3071:
3072: return (bp);
3073: }
3074:
3075: /*
3076: * rcs_rev_write_fd()
3077: *
3078: * Write the entire contents of revision <frev> from the rcsfile <rfp> to
3079: * file descriptor <fd>.
3080: */
3081: void
1.273 joris 3082: rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
1.196 niallo 3083: {
1.273 joris 3084: int fd;
3085: FILE *fp;
3086: size_t ret;
1.200 joris 3087: int expmode, expand;
1.196 niallo 3088: struct rcs_delta *rdp;
3089: struct cvs_lines *lines;
1.233 tobias 3090: struct cvs_line *lp, *nlp;
1.217 joris 3091: extern int print_stdout;
1.196 niallo 3092:
1.200 joris 3093: expand = 0;
1.218 tobias 3094: lines = rcs_rev_getlines(rfp, rev, NULL);
1.200 joris 3095:
1.196 niallo 3096: if (!(mode & RCS_KWEXP_NONE)) {
3097: if (rfp->rf_expand != NULL)
3098: expmode = rcs_kwexp_get(rfp);
3099: else
3100: expmode = RCS_KWEXP_DEFAULT;
3101:
3102: if (!(expmode & RCS_KWEXP_NONE)) {
3103: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
3104: fatal("could not fetch revision");
1.200 joris 3105: expand = 1;
1.196 niallo 3106: }
3107: }
1.200 joris 3108:
1.273 joris 3109: fd = dup(_fd);
3110: if (fd == -1)
3111: fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
3112:
3113: if ((fp = fdopen(fd, "w")) == NULL)
3114: fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
3115:
1.255 deraadt 3116: for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
1.233 tobias 3117: nlp = TAILQ_NEXT(lp, l_list);
3118:
3119: if (lp->l_line == NULL) {
3120: lp = nlp;
1.196 niallo 3121: continue;
1.233 tobias 3122: }
1.200 joris 3123:
3124: if (expand)
1.233 tobias 3125: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.217 joris 3126:
1.233 tobias 3127: do {
3128: /*
3129: * Solely for the checkout and update -p options.
3130: */
3131: if (cvs_server_active == 1 &&
3132: (cvs_cmdop == CVS_OP_CHECKOUT ||
3133: cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
1.273 joris 3134: ret = fwrite("M ", 1, 2, fp);
3135: if (ret != 2)
1.233 tobias 3136: fatal("rcs_rev_write_fd: %s",
3137: strerror(errno));
3138: }
3139:
1.273 joris 3140: ret = fwrite(lp->l_line, 1, lp->l_len, fp);
3141: if (ret != lp->l_len)
1.217 joris 3142: fatal("rcs_rev_write_fd: %s", strerror(errno));
1.233 tobias 3143: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 3144: }
3145:
3146: cvs_freelines(lines);
1.273 joris 3147: (void)fclose(fp);
1.196 niallo 3148: }
3149:
3150: /*
3151: * rcs_rev_write_stmp()
3152: *
3153: * Write the contents of the rev <rev> to a temporary file whose path is
3154: * specified using <template> (see mkstemp(3)). NB. This function will modify
3155: * <template>, as per mkstemp.
3156: */
1.251 joris 3157: int
1.196 niallo 3158: rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode)
3159: {
3160: int fd;
3161:
3162: if ((fd = mkstemp(template)) == -1)
3163: fatal("mkstemp: `%s': %s", template, strerror(errno));
3164:
3165: cvs_worklist_add(template, &temp_files);
3166: rcs_rev_write_fd(rfp, rev, fd, mode);
3167:
1.257 joris 3168: if (lseek(fd, 0, SEEK_SET) < 0)
1.251 joris 3169: fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
3170:
3171: return (fd);
1.196 niallo 3172: }
3173:
3174: static void
1.233 tobias 3175: rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines,
3176: struct cvs_line *line, int mode)
1.196 niallo 3177: {
1.231 tobias 3178: BUF *tmpbuf;
1.196 niallo 3179: int kwtype;
3180: u_int j, found;
1.209 otto 3181: const u_char *c, *start, *fin, *end;
3182: char *kwstr;
1.196 niallo 3183: char expbuf[256], buf[256];
1.231 tobias 3184: size_t clen, kwlen, len, tlen;
1.196 niallo 3185:
3186: kwtype = 0;
3187: kwstr = NULL;
1.230 tobias 3188:
3189: if (mode & RCS_KWEXP_OLD)
3190: return;
1.196 niallo 3191:
3192: len = line->l_len;
3193: if (len == 0)
3194: return;
3195:
3196: c = line->l_line;
3197: found = 0;
3198: /* Final character in buffer. */
3199: fin = c + len - 1;
3200:
3201: /*
3202: * Keyword formats:
3203: * $Keyword$
3204: * $Keyword: value$
3205: */
3206: for (; c < fin; c++) {
1.231 tobias 3207: if (*c != '$')
3208: continue;
1.196 niallo 3209:
1.231 tobias 3210: /* remember start of this possible keyword */
3211: start = c;
1.196 niallo 3212:
1.231 tobias 3213: /* first following character has to be alphanumeric */
3214: c++;
3215: if (!isalpha(*c)) {
3216: c = start;
3217: continue;
3218: }
1.196 niallo 3219:
1.231 tobias 3220: /* Number of characters between c and fin, inclusive. */
3221: clen = fin - c + 1;
1.196 niallo 3222:
1.231 tobias 3223: /* look for any matching keywords */
3224: found = 0;
3225: for (j = 0; j < RCS_NKWORDS; j++) {
3226: kwlen = strlen(rcs_expkw[j].kw_str);
1.196 niallo 3227: /*
1.231 tobias 3228: * kwlen must be less than clen since clen
3229: * includes either a terminating `$' or a `:'.
1.196 niallo 3230: */
1.231 tobias 3231: if (kwlen < clen &&
3232: memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
3233: (c[kwlen] == '$' || c[kwlen] == ':')) {
3234: found = 1;
3235: kwstr = rcs_expkw[j].kw_str;
3236: kwtype = rcs_expkw[j].kw_type;
3237: c += kwlen;
3238: break;
3239: }
3240: }
1.196 niallo 3241:
1.231 tobias 3242: if (found == 0 && cvs_tagname != NULL) {
3243: kwlen = strlen(cvs_tagname);
3244: if (kwlen < clen &&
3245: memcmp(c, cvs_tagname, kwlen) == 0 &&
3246: (c[kwlen] == '$' || c[kwlen] == ':')) {
3247: found = 1;
3248: kwstr = cvs_tagname;
3249: kwtype = RCS_KW_ID;
3250: c += kwlen;
1.196 niallo 3251: }
1.231 tobias 3252: }
1.196 niallo 3253:
1.231 tobias 3254: /* unknown keyword, continue looking */
3255: if (found == 0) {
3256: c = start;
3257: continue;
3258: }
1.196 niallo 3259:
1.231 tobias 3260: /*
3261: * if the next character was ':' we need to look for
3262: * an '$' before the end of the line to be sure it is
3263: * in fact a keyword.
3264: */
3265: if (*c == ':') {
3266: for (; c <= fin; ++c) {
3267: if (*c == '$' || *c == '\n')
3268: break;
1.196 niallo 3269: }
3270:
1.231 tobias 3271: if (*c != '$') {
3272: c = start;
3273: continue;
3274: }
3275: }
3276: end = c + 1;
1.196 niallo 3277:
1.231 tobias 3278: /* start constructing the expansion */
3279: expbuf[0] = '\0';
1.196 niallo 3280:
1.231 tobias 3281: if (mode & RCS_KWEXP_NAME) {
3282: if (strlcat(expbuf, "$", sizeof(expbuf)) >=
3283: sizeof(expbuf) || strlcat(expbuf, kwstr,
3284: sizeof(expbuf)) >= sizeof(expbuf))
3285: fatal("rcs_kwexp_line: truncated");
3286: if ((mode & RCS_KWEXP_VAL) &&
3287: strlcat(expbuf, ": ", sizeof(expbuf)) >=
3288: sizeof(expbuf))
3289: fatal("rcs_kwexp_line: truncated");
3290: }
1.196 niallo 3291:
1.231 tobias 3292: /*
3293: * order matters because of RCS_KW_ID and
3294: * RCS_KW_HEADER here
3295: */
3296: if (mode & RCS_KWEXP_VAL) {
3297: if (kwtype & RCS_KW_RCSFILE) {
3298: if (!(kwtype & RCS_KW_FULLPATH))
3299: (void)strlcat(expbuf, basename(rcsfile),
3300: sizeof(expbuf));
3301: else
3302: (void)strlcat(expbuf, rcsfile,
3303: sizeof(expbuf));
3304: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3305: sizeof(expbuf))
3306: fatal("rcs_kwexp_line: truncated");
3307: }
1.211 niallo 3308:
1.231 tobias 3309: if (kwtype & RCS_KW_REVISION) {
3310: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3311: if (strlcat(buf, " ", sizeof(buf)) >=
3312: sizeof(buf) || strlcat(expbuf, buf,
3313: sizeof(expbuf)) >= sizeof(buf))
3314: fatal("rcs_kwexp_line: truncated");
3315: }
1.196 niallo 3316:
1.231 tobias 3317: if (kwtype & RCS_KW_DATE) {
1.283 canacar 3318: if (strftime(buf, sizeof(buf),
3319: "%Y/%m/%d %H:%M:%S ",
1.231 tobias 3320: &rdp->rd_date) == 0)
3321: fatal("rcs_kwexp_line: strftime "
3322: "failure");
3323: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3324: sizeof(expbuf))
3325: fatal("rcs_kwexp_line: string "
3326: "truncated");
3327: }
1.196 niallo 3328:
1.231 tobias 3329: if (kwtype & RCS_KW_MDOCDATE) {
3330: /*
3331: * Do not prepend ' ' for a single
3332: * digit, %e would do so and there is
3333: * no better format for strftime().
3334: */
1.283 canacar 3335: if (strftime(buf, sizeof(buf),
3336: (rdp->rd_date.tm_mday < 10) ?
1.285 ! joris 3337: "%B%e %Y " : "%B %e %Y ",
1.231 tobias 3338: &rdp->rd_date) == 0)
3339: fatal("rcs_kwexp_line: strftime "
3340: "failure");
3341: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
3342: sizeof(expbuf))
3343: fatal("rcs_kwexp_line: string "
3344: "truncated");
3345: }
3346:
3347: if (kwtype & RCS_KW_AUTHOR) {
3348: if (strlcat(expbuf, rdp->rd_author,
3349: sizeof(expbuf)) >= sizeof(expbuf) ||
3350: strlcat(expbuf, " ", sizeof(expbuf)) >=
3351: sizeof(expbuf))
3352: fatal("rcs_kwexp_line: string "
3353: "truncated");
3354: }
3355:
3356: if (kwtype & RCS_KW_STATE) {
3357: if (strlcat(expbuf, rdp->rd_state,
3358: sizeof(expbuf)) >= sizeof(expbuf) ||
3359: strlcat(expbuf, " ", sizeof(expbuf)) >=
3360: sizeof(expbuf))
3361: fatal("rcs_kwexp_line: string "
3362: "truncated");
3363: }
3364:
3365: /* order does not matter anymore below */
1.233 tobias 3366: if (kwtype & RCS_KW_LOG) {
3367: char linebuf[256];
3368: struct cvs_line *cur, *lp;
3369: char *logp, *l_line, *prefix, *q, *sprefix;
3370: size_t i;
3371:
1.236 tobias 3372: /* Log line */
1.233 tobias 3373: if (!(kwtype & RCS_KW_FULLPATH))
3374: (void)strlcat(expbuf,
3375: basename(rcsfile), sizeof(expbuf));
3376: else
3377: (void)strlcat(expbuf, rcsfile,
3378: sizeof(expbuf));
3379:
1.231 tobias 3380: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3381: sizeof(expbuf))
3382: fatal("rcs_kwexp_line: string "
3383: "truncated");
1.233 tobias 3384:
3385: cur = line;
3386:
3387: /* copy rdp->rd_log for strsep */
3388: logp = xstrdup(rdp->rd_log);
3389:
3390: /* copy our prefix for later processing */
3391: prefix = xmalloc(start - line->l_line + 1);
3392: memcpy(prefix, line->l_line,
3393: start - line->l_line);
3394: prefix[start - line->l_line] = '\0';
3395:
3396: /* copy also prefix without trailing blanks. */
3397: sprefix = xstrdup(prefix);
3398: for (i = strlen(sprefix); i > 0 &&
3399: sprefix[i - 1] == ' '; i--)
3400: sprefix[i - 1] = '\0';
3401:
3402: /* new line: revision + date + author */
3403: linebuf[0] = '\0';
3404: if (strlcat(linebuf, "Revision ",
3405: sizeof(linebuf)) >= sizeof(linebuf))
3406: fatal("rcs_kwexp_line: truncated");
3407: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
3408: if (strlcat(linebuf, buf, sizeof(linebuf))
3409: >= sizeof(buf))
3410: fatal("rcs_kwexp_line: truncated");
1.283 canacar 3411: if (strftime(buf, sizeof(buf),
3412: " %Y/%m/%d %H:%M:%S ",
1.233 tobias 3413: &rdp->rd_date) == 0)
3414: fatal("rcs_kwexp_line: strftime "
3415: "failure");
3416: if (strlcat(linebuf, buf, sizeof(linebuf))
3417: >= sizeof(linebuf))
3418: fatal("rcs_kwexp_line: string "
3419: "truncated");
3420: if (strlcat(linebuf, rdp->rd_author,
3421: sizeof(linebuf)) >= sizeof(linebuf))
3422: fatal("rcs_kwexp_line: string "
3423: "truncated");
3424:
3425: lp = xcalloc(1, sizeof(*lp));
3426: xasprintf(&(lp->l_line), "%s%s\n",
3427: prefix, linebuf);
3428: lp->l_len = strlen(lp->l_line);
3429: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3430: l_list);
3431: cur = lp;
3432:
3433: /* Log message */
3434: q = logp;
3435: while ((l_line = strsep(&q, "\n")) != NULL &&
3436: q != NULL) {
3437: lp = xcalloc(1, sizeof(*lp));
3438:
3439: if (l_line[0] == '\0') {
3440: xasprintf(&(lp->l_line), "%s\n",
3441: sprefix);
3442: } else {
3443: xasprintf(&(lp->l_line),
3444: "%s%s\n", prefix, l_line);
3445: }
3446:
3447: lp->l_len = strlen(lp->l_line);
3448: TAILQ_INSERT_AFTER(&(lines->l_lines),
3449: cur, lp, l_list);
3450: cur = lp;
3451: }
3452: xfree(logp);
3453:
3454: /*
3455: * This is just another hairy mess, but it must
1.237 tobias 3456: * be done: All characters behind Log will be
1.233 tobias 3457: * written in a new line next to log messages.
3458: * But that's not enough, we have to strip all
3459: * trailing whitespaces of our prefix.
3460: */
3461: lp = xcalloc(1, sizeof(*lp));
3462: lp->l_line = xcalloc(strlen(sprefix) +
3463: line->l_line + line->l_len - end + 1, 1);
3464: strlcpy(lp->l_line, sprefix,
3465: strlen(sprefix) + 1);
3466: memcpy(lp->l_line + strlen(sprefix),
3467: end, line->l_line + line->l_len - end);
3468: lp->l_len = strlen(lp->l_line);
3469: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
3470: l_list);
3471: cur = lp;
3472:
3473: end = line->l_line + line->l_len - 1;
3474:
3475: xfree(prefix);
3476: xfree(sprefix);
3477:
3478: }
1.231 tobias 3479:
3480: if (kwtype & RCS_KW_SOURCE) {
3481: if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
3482: sizeof(expbuf) || strlcat(expbuf, " ",
3483: sizeof(expbuf)) >= sizeof(expbuf))
3484: fatal("rcs_kwexp_line: string "
3485: "truncated");
1.196 niallo 3486: }
3487:
1.231 tobias 3488: if (kwtype & RCS_KW_NAME)
3489: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3490: sizeof(expbuf))
3491: fatal("rcs_kwexp_line: string "
3492: "truncated");
1.196 niallo 3493:
1.231 tobias 3494: if (kwtype & RCS_KW_LOCKER)
3495: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
3496: sizeof(expbuf))
3497: fatal("rcs_kwexp_line: string "
3498: "truncated");
1.196 niallo 3499: }
1.231 tobias 3500:
3501: /* end the expansion */
3502: if (mode & RCS_KWEXP_NAME)
3503: if (strlcat(expbuf, "$",
3504: sizeof(expbuf)) >= sizeof(expbuf))
3505: fatal("rcs_kwexp_line: truncated");
3506:
3507: /* Concatenate everything together. */
1.249 tobias 3508: tmpbuf = cvs_buf_alloc(len + strlen(expbuf));
1.231 tobias 3509: /* Append everything before keyword. */
3510: cvs_buf_append(tmpbuf, line->l_line,
3511: start - line->l_line);
3512: /* Append keyword. */
1.267 tobias 3513: cvs_buf_puts(tmpbuf, expbuf);
1.231 tobias 3514: /* Point c to end of keyword. */
3515: tlen = cvs_buf_len(tmpbuf) - 1;
3516: /* Append everything after keyword. */
3517: cvs_buf_append(tmpbuf, end,
3518: line->l_line + line->l_len - end);
3519: c = cvs_buf_get(tmpbuf) + tlen;
3520: /* Point fin to end of data. */
3521: fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1;
3522: /* Recalculate new length. */
3523: len = cvs_buf_len(tmpbuf);
3524:
3525: /* tmpbuf is now ready, convert to string */
3526: if (line->l_needsfree)
3527: xfree(line->l_line);
3528: line->l_len = len;
3529: line->l_line = cvs_buf_release(tmpbuf);
3530: line->l_needsfree = 1;
1.196 niallo 3531: }
1.131 niallo 3532: }
1.246 tobias 3533:
3534: /* rcs_translate_tag() */
3535: RCSNUM *
3536: rcs_translate_tag(const char *revstr, RCSFILE *rfp)
3537: {
3538: int follow;
1.258 joris 3539: time_t deltatime;
1.250 joris 3540: char branch[CVS_REV_BUFSZ];
1.264 tobias 3541: RCSNUM *brev, *frev, *rev, *rrev;
1.246 tobias 3542: struct rcs_delta *rdp, *trdp;
1.274 tobias 3543: time_t cdate;
1.246 tobias 3544:
1.264 tobias 3545: brev = frev = rrev = NULL;
1.246 tobias 3546:
1.250 joris 3547: if (revstr == NULL) {
3548: if (rfp->rf_branch != NULL) {
3549: rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
3550: revstr = branch;
3551: } else {
3552: revstr = RCS_HEAD_BRANCH;
3553: }
3554: }
1.246 tobias 3555:
3556: if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
1.258 joris 3557: return (NULL);
3558:
3559: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1.266 tobias 3560: return (NULL);
1.258 joris 3561:
1.264 tobias 3562: /* let's see if we must follow a branch */
3563: if (!strcmp(revstr, RCS_HEAD_BRANCH))
3564: follow = 1;
3565: else {
3566: frev = rcs_sym_getrev(rfp, revstr);
3567: if (frev == NULL)
3568: frev = rrev = rcsnum_parse(revstr);
3569:
3570: brev = rcsnum_alloc();
3571: rcsnum_cpy(rev, brev, rev->rn_len - 1);
3572:
3573: if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
3574: !rcsnum_cmp(frev, brev, 0)) {
3575: follow = 1;
3576: } else
3577: follow = 0;
3578:
3579: rcsnum_free(brev);
3580: if (rrev != NULL)
3581: rcsnum_free(rrev);
3582: }
3583:
1.274 tobias 3584: if (cvs_specified_date != -1)
3585: cdate = cvs_specified_date;
3586: else
3587: cdate = cvs_directory_date;
3588:
3589: if (cdate == -1) {
1.258 joris 3590: /* XXX */
1.264 tobias 3591: if (rev->rn_len < 4 || !follow) {
1.258 joris 3592: return (rev);
3593: }
3594:
3595: /* Find the latest delta on that branch */
3596: rcsnum_free(rev);
3597: for (;;) {
3598: if (rdp->rd_next->rn_len == 0)
3599: break;
3600: if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
1.272 tobias 3601: fatal("rcs_translate_tag: could not fetch "
1.258 joris 3602: "branch delta");
3603: }
3604:
3605: rev = rcsnum_alloc();
3606: rcsnum_cpy(rdp->rd_num, rev, 0);
3607: return (rev);
1.246 tobias 3608: }
3609:
1.258 joris 3610: if (frev != NULL)
3611: rcsnum_tostr(frev, branch, sizeof(branch));
1.246 tobias 3612:
3613: if (frev != NULL) {
3614: brev = rcsnum_revtobr(frev);
1.260 joris 3615: brev->rn_len = rev->rn_len - 1;
1.246 tobias 3616: }
3617:
3618: rcsnum_free(rev);
3619:
3620: do {
1.258 joris 3621: deltatime = timelocal(&(rdp->rd_date));
3622:
3623: if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
1.274 tobias 3624: if (deltatime > cdate) {
1.258 joris 3625: trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
3626: if (trdp == NULL)
3627: trdp = rdp;
1.260 joris 3628:
3629: if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
3630: return (NULL);
3631:
1.258 joris 3632: rev = rcsnum_alloc();
3633: rcsnum_cpy(trdp->rd_num, rev, 0);
1.260 joris 3634: return (rev);
3635: }
3636:
3637: if (rdp->rd_next->rn_len == 0) {
3638: rev = rcsnum_alloc();
3639: rcsnum_cpy(rdp->rd_num, rev, 0);
1.258 joris 3640: return (rev);
3641: }
3642: } else {
1.274 tobias 3643: if (deltatime < cdate) {
1.258 joris 3644: rev = rcsnum_alloc();
3645: rcsnum_cpy(rdp->rd_num, rev, 0);
3646: return (rev);
3647: }
1.246 tobias 3648: }
3649:
3650: if (follow && rdp->rd_next->rn_len != 0) {
3651: if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
3652: break;
3653:
3654: trdp = rcs_findrev(rfp, rdp->rd_next);
3655: if (trdp == NULL)
3656: fatal("failed to grab next revision");
3657: rdp = trdp;
3658: } else
3659: follow = 0;
3660: } while (follow);
3661:
1.258 joris 3662: return (NULL);
1.246 tobias 3663: }