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