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