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