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