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