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