Annotation of src/usr.bin/cvs/rcs.c, Revision 1.100
1.100 ! xsa 1: /* $OpenBSD: rcs.c,v 1.99 2005/11/12 21:34:48 niallo 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.83 joris 1393: rcsnum_cpy(rf->rf_head, old, 0);
1.81 niallo 1394: rev = rcsnum_inc(rf->rf_head);
1.83 joris 1395: } else {
1396: if ((rdp = rcs_findrev(rf, rev)) != NULL) {
1397: rcs_errno = RCS_ERR_DUPENT;
1.92 joris 1398: rcsnum_free(old);
1.83 joris 1399: return (-1);
1400: }
1401:
1.93 joris 1402: if (!(rf->rf_flags & RCS_CREATE)) {
1403: ordp = NULL;
1404: rcsnum_cpy(rev, old, 0);
1405: while (ordp == NULL) {
1406: old = rcsnum_dec(old);
1407: ordp = rcs_findrev(rf, old);
1408: }
1.83 joris 1409: }
1.52 jfb 1410: }
1411:
1412: if ((pw = getpwuid(getuid())) == NULL) {
1413: rcs_errno = RCS_ERR_ERRNO;
1.92 joris 1414: rcsnum_free(old);
1.52 jfb 1415: return (-1);
1416: }
1417:
1418: if ((rdp = (struct rcs_delta *)malloc(sizeof(*rdp))) == NULL) {
1419: rcs_errno = RCS_ERR_ERRNO;
1.92 joris 1420: rcsnum_free(old);
1.52 jfb 1421: return (-1);
1422: }
1423: memset(rdp, 0, sizeof(*rdp));
1424:
1425: TAILQ_INIT(&(rdp->rd_branches));
1426: TAILQ_INIT(&(rdp->rd_snodes));
1427:
1428: if ((rdp->rd_num = rcsnum_alloc()) == NULL) {
1429: rcs_freedelta(rdp);
1.92 joris 1430: rcsnum_free(old);
1.52 jfb 1431: return (-1);
1432: }
1433: rcsnum_cpy(rev, rdp->rd_num, 0);
1.83 joris 1434:
1435: if ((rdp->rd_next = rcsnum_alloc()) == NULL) {
1436: rcs_freedelta(rdp);
1.92 joris 1437: rcsnum_free(old);
1.83 joris 1438: return (-1);
1439: }
1.92 joris 1440:
1.83 joris 1441: rcsnum_cpy(old, rdp->rd_next, 0);
1442: rcsnum_free(old);
1.52 jfb 1443:
1.90 niallo 1444: if (username == NULL)
1445: username = pw->pw_name;
1446:
1.99 niallo 1447: if ((rdp->rd_author = strdup(username)) == NULL) {
1.52 jfb 1448: rcs_freedelta(rdp);
1.92 joris 1449: rcsnum_free(old);
1.52 jfb 1450: return (-1);
1451: }
1452:
1.99 niallo 1453: if ((rdp->rd_state = strdup(RCS_STATE_EXP)) == NULL) {
1.52 jfb 1454: rcs_freedelta(rdp);
1.92 joris 1455: rcsnum_free(old);
1.52 jfb 1456: return (-1);
1457: }
1458:
1.99 niallo 1459: if ((rdp->rd_log = strdup(msg)) == NULL) {
1.52 jfb 1460: rcs_errno = RCS_ERR_ERRNO;
1461: rcs_freedelta(rdp);
1.92 joris 1462: rcsnum_free(old);
1.52 jfb 1463: return (-1);
1464: }
1465:
1.53 jfb 1466: if (date != (time_t)(-1))
1467: now = date;
1468: else
1469: time(&now);
1.52 jfb 1470: gmtime_r(&now, &(rdp->rd_date));
1471:
1472: TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1473: rf->rf_ndelta++;
1.81 niallo 1474:
1.64 niallo 1475: /* not synced anymore */
1476: rf->rf_flags &= ~RCS_SYNCED;
1.52 jfb 1477:
1478: return (0);
1479: }
1480:
1481: /*
1482: * rcs_rev_remove()
1483: *
1484: * Remove the revision whose number is <rev> from the RCS file <rf>.
1485: */
1486: int
1487: rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1488: {
1489: int ret;
1490: struct rcs_delta *rdp;
1491:
1492: ret = 0;
1493: if (rev == RCS_HEAD_REV)
1494: rev = rf->rf_head;
1495:
1496: /* do we actually have that revision? */
1497: if ((rdp = rcs_findrev(rf, rev)) == NULL) {
1498: rcs_errno = RCS_ERR_NOENT;
1499: ret = -1;
1500: } else {
1501: /* XXX assumes it's not a sub node */
1502: TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1503: rf->rf_ndelta--;
1504: rf->rf_flags &= ~RCS_SYNCED;
1505: }
1506:
1507: return (ret);
1508:
1509: }
1510:
1511: /*
1.1 jfb 1512: * rcs_findrev()
1513: *
1514: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1515: * The revision number is given in <rev>.
1516: * Returns a pointer to the delta on success, or NULL on failure.
1517: */
1518: static struct rcs_delta*
1.43 jfb 1519: rcs_findrev(RCSFILE *rfp, const RCSNUM *rev)
1.1 jfb 1520: {
1521: u_int cmplen;
1522: struct rcs_delta *rdp;
1523: struct rcs_dlist *hp;
1.6 vincent 1524: int found;
1.26 jfb 1525:
1.1 jfb 1526: cmplen = 2;
1527: hp = &(rfp->rf_delta);
1528:
1.6 vincent 1529: do {
1530: found = 0;
1531: TAILQ_FOREACH(rdp, hp, rd_list) {
1532: if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) {
1533: if (cmplen == rev->rn_len)
1534: return (rdp);
1.1 jfb 1535:
1.6 vincent 1536: hp = &(rdp->rd_snodes);
1537: cmplen += 2;
1538: found = 1;
1539: break;
1540: }
1.1 jfb 1541: }
1.6 vincent 1542: } while (found && cmplen < rev->rn_len);
1.1 jfb 1543:
1544: return (NULL);
1.20 jfb 1545: }
1546:
1547: /*
1.26 jfb 1548: * rcs_kwexp_set()
1549: *
1550: * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1551: * Returns 0 on success, or -1 on failure.
1552: */
1553: int
1554: rcs_kwexp_set(RCSFILE *file, int mode)
1555: {
1556: int i;
1557: char *tmp, buf[8] = "";
1558:
1559: if (RCS_KWEXP_INVAL(mode))
1560: return (-1);
1561:
1562: i = 0;
1563: if (mode == RCS_KWEXP_NONE)
1564: buf[0] = 'b';
1565: else if (mode == RCS_KWEXP_OLD)
1566: buf[0] = 'o';
1567: else {
1568: if (mode & RCS_KWEXP_NAME)
1569: buf[i++] = 'k';
1570: if (mode & RCS_KWEXP_VAL)
1571: buf[i++] = 'v';
1572: if (mode & RCS_KWEXP_LKR)
1573: buf[i++] = 'l';
1574: }
1575:
1.99 niallo 1576: if ((tmp = strdup(buf)) == NULL) {
1.26 jfb 1577: cvs_log(LP_ERRNO, "%s: failed to copy expansion mode",
1578: file->rf_path);
1579: return (-1);
1580: }
1581:
1.27 jfb 1582: if (file->rf_expand != NULL)
1.99 niallo 1583: free(file->rf_expand);
1.26 jfb 1584: file->rf_expand = tmp;
1.64 niallo 1585: /* not synced anymore */
1586: file->rf_flags &= ~RCS_SYNCED;
1.26 jfb 1587:
1588: return (0);
1589: }
1590:
1591: /*
1592: * rcs_kwexp_get()
1593: *
1594: * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1595: */
1596: int
1597: rcs_kwexp_get(RCSFILE *file)
1598: {
1599: return rcs_kflag_get(file->rf_expand);
1600: }
1601:
1602: /*
1.20 jfb 1603: * rcs_kflag_get()
1604: *
1605: * Get the keyword expansion mode from a set of character flags given in
1606: * <flags> and return the appropriate flag mask. In case of an error, the
1607: * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1608: */
1609: int
1610: rcs_kflag_get(const char *flags)
1611: {
1612: int fl;
1613: size_t len;
1614: const char *fp;
1615:
1616: fl = 0;
1617: len = strlen(flags);
1618:
1619: for (fp = flags; *fp != '\0'; fp++) {
1620: if (*fp == 'k')
1621: fl |= RCS_KWEXP_NAME;
1622: else if (*fp == 'v')
1623: fl |= RCS_KWEXP_VAL;
1624: else if (*fp == 'l')
1625: fl |= RCS_KWEXP_LKR;
1626: else if (*fp == 'o') {
1627: if (len != 1)
1628: fl |= RCS_KWEXP_ERR;
1629: fl |= RCS_KWEXP_OLD;
1630: } else if (*fp == 'b') {
1631: if (len != 1)
1632: fl |= RCS_KWEXP_ERR;
1633: } else /* unknown letter */
1634: fl |= RCS_KWEXP_ERR;
1635: }
1636:
1637: return (fl);
1.32 jfb 1638: }
1639:
1640: /*
1641: * rcs_errstr()
1642: *
1643: * Get the error string matching the RCS error code <code>.
1644: */
1.57 xsa 1645: const char *
1.32 jfb 1646: rcs_errstr(int code)
1647: {
1.50 jfb 1648: const char *esp;
1649:
1650: if ((code < 0) || ((code >= (int)RCS_NERR) && (code != RCS_ERR_ERRNO)))
1651: esp = NULL;
1652: else if (code == RCS_ERR_ERRNO)
1653: esp = strerror(errno);
1654: else
1655: esp = rcs_errstrs[code];
1656: return (esp);
1.1 jfb 1657: }
1658:
1.21 jfb 1659: void
1660: rcs_kflag_usage(void)
1661: {
1662: fprintf(stderr, "Valid expansion modes include:\n"
1.22 jfb 1663: "\t-kkv\tGenerate keywords using the default form.\n"
1664: "\t-kkvl\tLike -kkv, except locker's name inserted.\n"
1665: "\t-kk\tGenerate only keyword names in keyword strings.\n"
1666: "\t-kv\tGenerate only keyword values in keyword strings.\n"
1667: "\t-ko\tGenerate old keyword string "
1.21 jfb 1668: "(no changes from checked in file).\n"
1.22 jfb 1669: "\t-kb\tGenerate binary file unmodified (merges not allowed).\n");
1.21 jfb 1670: }
1.1 jfb 1671:
1672: /*
1673: * rcs_parse()
1674: *
1675: * Parse the contents of file <path>, which are in the RCS format.
1676: * Returns 0 on success, or -1 on failure.
1677: */
1.26 jfb 1678: static int
1.1 jfb 1679: rcs_parse(RCSFILE *rfp)
1680: {
1681: int ret;
1682: struct rcs_pdata *pdp;
1683:
1.26 jfb 1684: if (rfp->rf_flags & RCS_PARSED)
1.1 jfb 1685: return (0);
1686:
1.26 jfb 1687: if ((pdp = (struct rcs_pdata *)malloc(sizeof(*pdp))) == NULL) {
1.50 jfb 1688: rcs_errno = RCS_ERR_ERRNO;
1.1 jfb 1689: cvs_log(LP_ERRNO, "failed to allocate RCS parser data");
1690: return (-1);
1691: }
1692: memset(pdp, 0, sizeof(*pdp));
1693:
1.18 jfb 1694: pdp->rp_lines = 0;
1.1 jfb 1695: pdp->rp_pttype = RCS_TOK_ERR;
1696:
1697: pdp->rp_file = fopen(rfp->rf_path, "r");
1698: if (pdp->rp_file == NULL) {
1.50 jfb 1699: rcs_errno = RCS_ERR_ERRNO;
1.1 jfb 1700: cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path);
1701: rcs_freepdata(pdp);
1702: return (-1);
1703: }
1704:
1.59 xsa 1705: pdp->rp_buf = (char *)malloc((size_t)RCS_BUFSIZE);
1.1 jfb 1706: if (pdp->rp_buf == NULL) {
1.50 jfb 1707: rcs_errno = RCS_ERR_ERRNO;
1.1 jfb 1708: cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer");
1709: rcs_freepdata(pdp);
1710: return (-1);
1711: }
1712: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 1713: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 1714:
1715: /* ditch the strict lock */
1.26 jfb 1716: rfp->rf_flags &= ~RCS_SLOCK;
1.1 jfb 1717: rfp->rf_pdata = pdp;
1718:
1.31 jfb 1719: if ((ret = rcs_parse_admin(rfp)) < 0) {
1.1 jfb 1720: rcs_freepdata(pdp);
1721: return (-1);
1.31 jfb 1722: } else if (ret == RCS_TOK_NUM) {
1723: for (;;) {
1724: ret = rcs_parse_delta(rfp);
1725: if (ret == 0)
1726: break;
1727: else if (ret == -1) {
1728: rcs_freepdata(pdp);
1729: return (-1);
1730: }
1.1 jfb 1731: }
1732: }
1733:
1734: ret = rcs_gettok(rfp);
1735: if (ret != RCS_TOK_DESC) {
1.50 jfb 1736: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1737: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
1738: RCS_TOKSTR(rfp));
1739: rcs_freepdata(pdp);
1740: return (-1);
1741: }
1742:
1743: ret = rcs_gettok(rfp);
1744: if (ret != RCS_TOK_STRING) {
1.50 jfb 1745: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1746: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
1747: RCS_TOKSTR(rfp));
1748: rcs_freepdata(pdp);
1749: return (-1);
1750: }
1751:
1.99 niallo 1752: rfp->rf_desc = strdup(RCS_TOKSTR(rfp));
1.10 joris 1753: if (rfp->rf_desc == NULL) {
1754: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
1755: rcs_freepdata(pdp);
1756: return (-1);
1757: }
1.1 jfb 1758:
1759: for (;;) {
1760: ret = rcs_parse_deltatext(rfp);
1761: if (ret == 0)
1762: break;
1763: else if (ret == -1) {
1764: rcs_freepdata(pdp);
1765: return (-1);
1766: }
1767: }
1768:
1769: rcs_freepdata(pdp);
1770:
1771: rfp->rf_pdata = NULL;
1.26 jfb 1772: rfp->rf_flags |= RCS_PARSED | RCS_SYNCED;
1.1 jfb 1773:
1774: return (0);
1775: }
1776:
1777: /*
1778: * rcs_parse_admin()
1779: *
1780: * Parse the administrative portion of an RCS file.
1.31 jfb 1781: * Returns the type of the first token found after the admin section on
1782: * success, or -1 on failure.
1.1 jfb 1783: */
1784: static int
1785: rcs_parse_admin(RCSFILE *rfp)
1786: {
1787: u_int i;
1788: int tok, ntok, hmask;
1789: struct rcs_key *rk;
1790:
1791: /* hmask is a mask of the headers already encountered */
1792: hmask = 0;
1793: for (;;) {
1794: tok = rcs_gettok(rfp);
1795: if (tok == RCS_TOK_ERR) {
1.50 jfb 1796: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1797: cvs_log(LP_ERR, "parse error in RCS admin section");
1798: return (-1);
1.31 jfb 1799: } else if ((tok == RCS_TOK_NUM) || (tok == RCS_TOK_DESC)) {
1800: /*
1801: * Assume this is the start of the first delta or
1802: * that we are dealing with an empty RCS file and
1803: * we just found the description.
1804: */
1.1 jfb 1805: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.31 jfb 1806: return (tok);
1.1 jfb 1807: }
1808:
1809: rk = NULL;
1.18 jfb 1810: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1811: if (rcs_keys[i].rk_id == tok)
1812: rk = &(rcs_keys[i]);
1813:
1814: if (hmask & (1 << tok)) {
1.50 jfb 1815: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1816: cvs_log(LP_ERR, "duplicate RCS key");
1817: return (-1);
1818: }
1819: hmask |= (1 << tok);
1820:
1821: switch (tok) {
1822: case RCS_TOK_HEAD:
1823: case RCS_TOK_BRANCH:
1824: case RCS_TOK_COMMENT:
1825: case RCS_TOK_EXPAND:
1826: ntok = rcs_gettok(rfp);
1827: if (ntok == RCS_TOK_SCOLON)
1828: break;
1829: if (ntok != rk->rk_val) {
1.50 jfb 1830: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1831: cvs_log(LP_ERR,
1832: "invalid value type for RCS key `%s'",
1833: rk->rk_str);
1834: }
1835:
1836: if (tok == RCS_TOK_HEAD) {
1.28 jfb 1837: if (rfp->rf_head == NULL) {
1838: rfp->rf_head = rcsnum_alloc();
1839: if (rfp->rf_head == NULL)
1840: return (-1);
1841: }
1.1 jfb 1842: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1843: rfp->rf_head);
1.14 deraadt 1844: } else if (tok == RCS_TOK_BRANCH) {
1.35 jfb 1845: if (rfp->rf_branch == NULL) {
1846: rfp->rf_branch = rcsnum_alloc();
1847: if (rfp->rf_branch == NULL)
1848: return (-1);
1849: }
1850: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1851: rfp->rf_branch) < 0)
1852: return (-1);
1.14 deraadt 1853: } else if (tok == RCS_TOK_COMMENT) {
1.99 niallo 1854: rfp->rf_comment = strdup(RCS_TOKSTR(rfp));
1.10 joris 1855: if (rfp->rf_comment == NULL) {
1856: cvs_log(LP_ERRNO,
1857: "failed to duplicate rcs token");
1858: return (-1);
1859: }
1.14 deraadt 1860: } else if (tok == RCS_TOK_EXPAND) {
1.99 niallo 1861: rfp->rf_expand = strdup(RCS_TOKSTR(rfp));
1.10 joris 1862: if (rfp->rf_expand == NULL) {
1863: cvs_log(LP_ERRNO,
1864: "failed to duplicate rcs token");
1865: return (-1);
1866: }
1.1 jfb 1867: }
1868:
1869: /* now get the expected semi-colon */
1870: ntok = rcs_gettok(rfp);
1871: if (ntok != RCS_TOK_SCOLON) {
1.50 jfb 1872: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1873: cvs_log(LP_ERR,
1874: "missing semi-colon after RCS `%s' key",
1.26 jfb 1875: rk->rk_str);
1.1 jfb 1876: return (-1);
1877: }
1878: break;
1879: case RCS_TOK_ACCESS:
1.29 jfb 1880: if (rcs_parse_access(rfp) < 0)
1881: return (-1);
1.1 jfb 1882: break;
1883: case RCS_TOK_SYMBOLS:
1.29 jfb 1884: if (rcs_parse_symbols(rfp) < 0)
1885: return (-1);
1.1 jfb 1886: break;
1887: case RCS_TOK_LOCKS:
1.29 jfb 1888: if (rcs_parse_locks(rfp) < 0)
1889: return (-1);
1.1 jfb 1890: break;
1891: default:
1.50 jfb 1892: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1893: cvs_log(LP_ERR,
1894: "unexpected token `%s' in RCS admin section",
1895: RCS_TOKSTR(rfp));
1896: return (-1);
1897: }
1898: }
1899:
1900: return (0);
1901: }
1902:
1903: /*
1904: * rcs_parse_delta()
1905: *
1906: * Parse an RCS delta section and allocate the structure to store that delta's
1907: * information in the <rfp> delta list.
1908: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1909: * -1 on error.
1910: */
1911: static int
1912: rcs_parse_delta(RCSFILE *rfp)
1913: {
1914: int ret, tok, ntok, hmask;
1915: u_int i;
1916: char *tokstr;
1.3 vincent 1917: RCSNUM *datenum;
1.1 jfb 1918: struct rcs_delta *rdp;
1919: struct rcs_key *rk;
1920:
1921: rdp = (struct rcs_delta *)malloc(sizeof(*rdp));
1922: if (rdp == NULL) {
1.50 jfb 1923: rcs_errno = RCS_ERR_ERRNO;
1.1 jfb 1924: cvs_log(LP_ERRNO, "failed to allocate RCS delta structure");
1925: return (-1);
1926: }
1927: memset(rdp, 0, sizeof(*rdp));
1928:
1929: rdp->rd_num = rcsnum_alloc();
1.11 joris 1930: if (rdp->rd_num == NULL) {
1931: rcs_freedelta(rdp);
1932: return (-1);
1933: }
1.1 jfb 1934: rdp->rd_next = rcsnum_alloc();
1.11 joris 1935: if (rdp->rd_next == NULL) {
1936: rcs_freedelta(rdp);
1937: return (-1);
1938: }
1.1 jfb 1939:
1940: TAILQ_INIT(&(rdp->rd_branches));
1.52 jfb 1941: TAILQ_INIT(&(rdp->rd_snodes));
1.1 jfb 1942:
1943: tok = rcs_gettok(rfp);
1944: if (tok != RCS_TOK_NUM) {
1.52 jfb 1945: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1946: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1947: RCS_TOKSTR(rfp));
1948: rcs_freedelta(rdp);
1949: return (-1);
1950: }
1951: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1952:
1953: hmask = 0;
1954: ret = 0;
1955: tokstr = NULL;
1956:
1957: for (;;) {
1958: tok = rcs_gettok(rfp);
1959: if (tok == RCS_TOK_ERR) {
1.50 jfb 1960: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1961: cvs_log(LP_ERR, "parse error in RCS delta section");
1962: rcs_freedelta(rdp);
1963: return (-1);
1.14 deraadt 1964: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1965: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1966: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1967: break;
1968: }
1969:
1970: rk = NULL;
1.18 jfb 1971: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1972: if (rcs_keys[i].rk_id == tok)
1973: rk = &(rcs_keys[i]);
1974:
1975: if (hmask & (1 << tok)) {
1.50 jfb 1976: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1977: cvs_log(LP_ERR, "duplicate RCS key");
1978: rcs_freedelta(rdp);
1979: return (-1);
1980: }
1981: hmask |= (1 << tok);
1982:
1983: switch (tok) {
1984: case RCS_TOK_DATE:
1985: case RCS_TOK_AUTHOR:
1986: case RCS_TOK_STATE:
1987: case RCS_TOK_NEXT:
1988: ntok = rcs_gettok(rfp);
1989: if (ntok == RCS_TOK_SCOLON) {
1990: if (rk->rk_flags & RCS_VOPT)
1991: break;
1992: else {
1.50 jfb 1993: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 1994: cvs_log(LP_ERR, "missing mandatory "
1995: "value to RCS key `%s'",
1996: rk->rk_str);
1997: rcs_freedelta(rdp);
1998: return (-1);
1999: }
2000: }
2001:
2002: if (ntok != rk->rk_val) {
1.50 jfb 2003: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2004: cvs_log(LP_ERR,
2005: "invalid value type for RCS key `%s'",
2006: rk->rk_str);
2007: rcs_freedelta(rdp);
2008: return (-1);
2009: }
2010:
2011: if (tokstr != NULL)
1.99 niallo 2012: free(tokstr);
2013: tokstr = strdup(RCS_TOKSTR(rfp));
1.10 joris 2014: if (tokstr == NULL) {
1.15 tedu 2015: cvs_log(LP_ERRNO,
1.10 joris 2016: "failed to duplicate rcs token");
2017: rcs_freedelta(rdp);
2018: return (-1);
2019: }
1.1 jfb 2020:
2021: /* now get the expected semi-colon */
2022: ntok = rcs_gettok(rfp);
2023: if (ntok != RCS_TOK_SCOLON) {
1.50 jfb 2024: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2025: cvs_log(LP_ERR,
2026: "missing semi-colon after RCS `%s' key",
1.26 jfb 2027: rk->rk_str);
1.99 niallo 2028: free(tokstr);
1.1 jfb 2029: rcs_freedelta(rdp);
2030: return (-1);
2031: }
2032:
2033: if (tok == RCS_TOK_DATE) {
1.25 jfb 2034: if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1.99 niallo 2035: free(tokstr);
1.11 joris 2036: rcs_freedelta(rdp);
2037: return (-1);
2038: }
1.3 vincent 2039: if (datenum->rn_len != 6) {
1.50 jfb 2040: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2041: cvs_log(LP_ERR,
2042: "RCS date specification has %s "
2043: "fields",
1.3 vincent 2044: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 2045: "missing");
1.99 niallo 2046: free(tokstr);
1.1 jfb 2047: rcs_freedelta(rdp);
1.37 tedu 2048: rcsnum_free(datenum);
2049: return (-1);
1.1 jfb 2050: }
1.3 vincent 2051: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 2052: if (rdp->rd_date.tm_year >= 1900)
2053: rdp->rd_date.tm_year -= 1900;
1.3 vincent 2054: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
2055: rdp->rd_date.tm_mday = datenum->rn_id[2];
2056: rdp->rd_date.tm_hour = datenum->rn_id[3];
2057: rdp->rd_date.tm_min = datenum->rn_id[4];
2058: rdp->rd_date.tm_sec = datenum->rn_id[5];
2059: rcsnum_free(datenum);
1.14 deraadt 2060: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 2061: rdp->rd_author = tokstr;
2062: tokstr = NULL;
1.14 deraadt 2063: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 2064: rdp->rd_state = tokstr;
2065: tokstr = NULL;
1.14 deraadt 2066: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 2067: rcsnum_aton(tokstr, NULL, rdp->rd_next);
2068: }
2069: break;
2070: case RCS_TOK_BRANCHES:
1.46 jfb 2071: if (rcs_parse_branches(rfp, rdp) < 0) {
2072: rcs_freedelta(rdp);
2073: return (-1);
2074: }
1.1 jfb 2075: break;
2076: default:
1.50 jfb 2077: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2078: cvs_log(LP_ERR,
2079: "unexpected token `%s' in RCS delta",
2080: RCS_TOKSTR(rfp));
2081: rcs_freedelta(rdp);
2082: return (-1);
2083: }
2084: }
2085:
1.13 jfb 2086: if (tokstr != NULL)
1.99 niallo 2087: free(tokstr);
1.13 jfb 2088:
1.1 jfb 2089: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1.26 jfb 2090: rfp->rf_ndelta++;
1.1 jfb 2091:
2092: return (ret);
2093: }
2094:
2095: /*
2096: * rcs_parse_deltatext()
2097: *
2098: * Parse an RCS delta text section and fill in the log and text field of the
2099: * appropriate delta section.
2100: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
2101: * -1 on error.
2102: */
2103: static int
2104: rcs_parse_deltatext(RCSFILE *rfp)
2105: {
2106: int tok;
2107: RCSNUM *tnum;
2108: struct rcs_delta *rdp;
2109:
2110: tok = rcs_gettok(rfp);
2111: if (tok == RCS_TOK_EOF)
2112: return (0);
2113:
2114: if (tok != RCS_TOK_NUM) {
1.50 jfb 2115: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2116: cvs_log(LP_ERR,
2117: "unexpected token `%s' at start of RCS delta text",
2118: RCS_TOKSTR(rfp));
2119: return (-1);
2120: }
1.13 jfb 2121:
2122: tnum = rcsnum_alloc();
2123: if (tnum == NULL)
2124: return (-1);
1.1 jfb 2125: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
2126:
2127: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
2128: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
2129: break;
2130: }
1.13 jfb 2131: rcsnum_free(tnum);
2132:
1.1 jfb 2133: if (rdp == NULL) {
2134: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
2135: RCS_TOKSTR(rfp));
2136: return (-1);
2137: }
2138:
2139: tok = rcs_gettok(rfp);
2140: if (tok != RCS_TOK_LOG) {
1.50 jfb 2141: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2142: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2143: RCS_TOKSTR(rfp));
2144: return (-1);
2145: }
2146:
2147: tok = rcs_gettok(rfp);
2148: if (tok != RCS_TOK_STRING) {
1.50 jfb 2149: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2150: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
2151: RCS_TOKSTR(rfp));
2152: return (-1);
2153: }
1.99 niallo 2154: rdp->rd_log = strdup(RCS_TOKSTR(rfp));
1.1 jfb 2155: if (rdp->rd_log == NULL) {
2156: cvs_log(LP_ERRNO, "failed to copy RCS deltatext log");
2157: return (-1);
2158: }
2159:
2160: tok = rcs_gettok(rfp);
2161: if (tok != RCS_TOK_TEXT) {
1.50 jfb 2162: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2163: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2164: RCS_TOKSTR(rfp));
2165: return (-1);
2166: }
2167:
2168: tok = rcs_gettok(rfp);
2169: if (tok != RCS_TOK_STRING) {
1.50 jfb 2170: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2171: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
2172: RCS_TOKSTR(rfp));
2173: return (-1);
2174: }
2175:
1.74 joris 2176: rdp->rd_text = (u_char *)malloc(RCS_TOKLEN(rfp) + 1);
1.1 jfb 2177: if (rdp->rd_text == NULL) {
2178: cvs_log(LP_ERRNO, "failed to copy RCS delta text");
2179: return (-1);
2180: }
1.74 joris 2181: strlcpy(rdp->rd_text, RCS_TOKSTR(rfp), (RCS_TOKLEN(rfp) + 1));
1.42 jfb 2182: rdp->rd_tlen = RCS_TOKLEN(rfp);
1.1 jfb 2183:
2184: return (1);
2185: }
2186:
2187: /*
2188: * rcs_parse_access()
2189: *
2190: * Parse the access list given as value to the `access' keyword.
2191: * Returns 0 on success, or -1 on failure.
2192: */
2193: static int
2194: rcs_parse_access(RCSFILE *rfp)
2195: {
2196: int type;
2197:
2198: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
2199: if (type != RCS_TOK_ID) {
1.50 jfb 2200: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2201: cvs_log(LP_ERR, "unexpected token `%s' in access list",
2202: RCS_TOKSTR(rfp));
2203: return (-1);
2204: }
1.29 jfb 2205:
2206: if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
2207: return (-1);
1.1 jfb 2208: }
2209:
2210: return (0);
2211: }
2212:
2213: /*
2214: * rcs_parse_symbols()
2215: *
2216: * Parse the symbol list given as value to the `symbols' keyword.
2217: * Returns 0 on success, or -1 on failure.
2218: */
2219: static int
2220: rcs_parse_symbols(RCSFILE *rfp)
2221: {
2222: int type;
2223: struct rcs_sym *symp;
2224:
2225: for (;;) {
2226: type = rcs_gettok(rfp);
2227: if (type == RCS_TOK_SCOLON)
2228: break;
2229:
1.41 jfb 2230: if (type != RCS_TOK_ID) {
1.50 jfb 2231: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2232: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2233: RCS_TOKSTR(rfp));
2234: return (-1);
2235: }
2236:
2237: symp = (struct rcs_sym *)malloc(sizeof(*symp));
2238: if (symp == NULL) {
1.50 jfb 2239: rcs_errno = RCS_ERR_ERRNO;
1.1 jfb 2240: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
2241: return (-1);
2242: }
1.99 niallo 2243: symp->rs_name = strdup(RCS_TOKSTR(rfp));
1.10 joris 2244: if (symp->rs_name == NULL) {
2245: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
2246: free(symp);
2247: return (-1);
2248: }
2249:
1.1 jfb 2250: symp->rs_num = rcsnum_alloc();
1.11 joris 2251: if (symp->rs_num == NULL) {
2252: cvs_log(LP_ERRNO, "failed to allocate rcsnum info");
1.99 niallo 2253: free(symp->rs_name);
1.11 joris 2254: free(symp);
2255: return (-1);
2256: }
1.1 jfb 2257:
2258: type = rcs_gettok(rfp);
2259: if (type != RCS_TOK_COLON) {
1.50 jfb 2260: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2261: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2262: RCS_TOKSTR(rfp));
1.11 joris 2263: rcsnum_free(symp->rs_num);
1.99 niallo 2264: free(symp->rs_name);
1.1 jfb 2265: free(symp);
2266: return (-1);
2267: }
2268:
2269: type = rcs_gettok(rfp);
2270: if (type != RCS_TOK_NUM) {
1.50 jfb 2271: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2272: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2273: RCS_TOKSTR(rfp));
1.11 joris 2274: rcsnum_free(symp->rs_num);
1.99 niallo 2275: free(symp->rs_name);
1.1 jfb 2276: free(symp);
2277: return (-1);
2278: }
2279:
2280: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
2281: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2282: RCS_TOKSTR(rfp));
1.11 joris 2283: rcsnum_free(symp->rs_num);
1.99 niallo 2284: free(symp->rs_name);
1.1 jfb 2285: free(symp);
2286: return (-1);
2287: }
2288:
1.43 jfb 2289: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
1.1 jfb 2290: }
2291:
2292: return (0);
2293: }
2294:
2295: /*
2296: * rcs_parse_locks()
2297: *
2298: * Parse the lock list given as value to the `locks' keyword.
2299: * Returns 0 on success, or -1 on failure.
2300: */
2301: static int
2302: rcs_parse_locks(RCSFILE *rfp)
2303: {
2304: int type;
2305: struct rcs_lock *lkp;
2306:
2307: for (;;) {
2308: type = rcs_gettok(rfp);
2309: if (type == RCS_TOK_SCOLON)
2310: break;
2311:
2312: if (type != RCS_TOK_ID) {
1.50 jfb 2313: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2314: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
2315: RCS_TOKSTR(rfp));
2316: return (-1);
2317: }
2318:
2319: lkp = (struct rcs_lock *)malloc(sizeof(*lkp));
2320: if (lkp == NULL) {
2321: cvs_log(LP_ERRNO, "failed to allocate RCS lock");
2322: return (-1);
2323: }
1.76 joris 2324:
1.99 niallo 2325: if ((lkp->rl_name = strdup(RCS_TOKSTR(rfp))) == NULL) {
1.76 joris 2326: cvs_log(LP_ERR, "failed to save locking user");
2327: free(lkp);
2328: return (-1);
2329: }
2330:
1.1 jfb 2331: lkp->rl_num = rcsnum_alloc();
1.11 joris 2332: if (lkp->rl_num == NULL) {
1.99 niallo 2333: free(lkp->rl_name);
1.11 joris 2334: free(lkp);
2335: return (-1);
2336: }
1.1 jfb 2337:
2338: type = rcs_gettok(rfp);
2339: if (type != RCS_TOK_COLON) {
1.50 jfb 2340: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2341: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2342: RCS_TOKSTR(rfp));
1.37 tedu 2343: rcsnum_free(lkp->rl_num);
1.99 niallo 2344: free(lkp->rl_name);
1.1 jfb 2345: free(lkp);
2346: return (-1);
2347: }
2348:
2349: type = rcs_gettok(rfp);
2350: if (type != RCS_TOK_NUM) {
1.50 jfb 2351: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2352: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
2353: RCS_TOKSTR(rfp));
1.37 tedu 2354: rcsnum_free(lkp->rl_num);
1.99 niallo 2355: free(lkp->rl_name);
1.1 jfb 2356: free(lkp);
2357: return (-1);
2358: }
2359:
2360: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
2361: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
2362: RCS_TOKSTR(rfp));
1.37 tedu 2363: rcsnum_free(lkp->rl_num);
1.99 niallo 2364: free(lkp->rl_name);
1.1 jfb 2365: free(lkp);
2366: return (-1);
2367: }
2368:
2369: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
2370: }
2371:
2372: /* check if we have a `strict' */
2373: type = rcs_gettok(rfp);
2374: if (type != RCS_TOK_STRICT) {
2375: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 2376: } else {
1.26 jfb 2377: rfp->rf_flags |= RCS_SLOCK;
1.1 jfb 2378:
2379: type = rcs_gettok(rfp);
2380: if (type != RCS_TOK_SCOLON) {
1.50 jfb 2381: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2382: cvs_log(LP_ERR,
2383: "missing semi-colon after `strict' keyword");
2384: return (-1);
2385: }
2386: }
2387:
2388: return (0);
2389: }
2390:
2391: /*
2392: * rcs_parse_branches()
2393: *
2394: * Parse the list of branches following a `branches' keyword in a delta.
2395: * Returns 0 on success, or -1 on failure.
2396: */
2397: static int
2398: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
2399: {
2400: int type;
2401: struct rcs_branch *brp;
2402:
2403: for (;;) {
2404: type = rcs_gettok(rfp);
2405: if (type == RCS_TOK_SCOLON)
2406: break;
2407:
2408: if (type != RCS_TOK_NUM) {
1.50 jfb 2409: rcs_errno = RCS_ERR_PARSE;
1.1 jfb 2410: cvs_log(LP_ERR,
2411: "unexpected token `%s' in list of branches",
2412: RCS_TOKSTR(rfp));
2413: return (-1);
2414: }
2415:
2416: brp = (struct rcs_branch *)malloc(sizeof(*brp));
2417: if (brp == NULL) {
1.50 jfb 2418: rcs_errno = RCS_ERR_ERRNO;
1.1 jfb 2419: cvs_log(LP_ERRNO, "failed to allocate RCS branch");
2420: return (-1);
2421: }
1.46 jfb 2422: brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
1.11 joris 2423: if (brp->rb_num == NULL) {
2424: free(brp);
2425: return (-1);
2426: }
1.1 jfb 2427:
2428: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
2429: }
2430:
2431: return (0);
2432: }
2433:
2434: /*
2435: * rcs_freedelta()
2436: *
2437: * Free the contents of a delta structure.
2438: */
1.18 jfb 2439: static void
1.1 jfb 2440: rcs_freedelta(struct rcs_delta *rdp)
2441: {
1.12 jfb 2442: struct rcs_branch *rb;
1.1 jfb 2443: struct rcs_delta *crdp;
2444:
1.12 jfb 2445: if (rdp->rd_num != NULL)
2446: rcsnum_free(rdp->rd_num);
2447: if (rdp->rd_next != NULL)
2448: rcsnum_free(rdp->rd_next);
2449:
1.1 jfb 2450: if (rdp->rd_author != NULL)
1.99 niallo 2451: free(rdp->rd_author);
1.1 jfb 2452: if (rdp->rd_state != NULL)
1.99 niallo 2453: free(rdp->rd_state);
1.1 jfb 2454: if (rdp->rd_log != NULL)
1.99 niallo 2455: free(rdp->rd_log);
1.1 jfb 2456: if (rdp->rd_text != NULL)
1.45 jfb 2457: free(rdp->rd_text);
1.12 jfb 2458:
2459: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
2460: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
2461: rcsnum_free(rb->rb_num);
2462: free(rb);
2463: }
1.1 jfb 2464:
2465: while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) {
2466: TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list);
2467: rcs_freedelta(crdp);
2468: }
2469:
2470: free(rdp);
2471: }
2472:
2473: /*
2474: * rcs_freepdata()
2475: *
2476: * Free the contents of the parser data structure.
2477: */
2478: static void
2479: rcs_freepdata(struct rcs_pdata *pd)
2480: {
2481: if (pd->rp_file != NULL)
2482: (void)fclose(pd->rp_file);
2483: if (pd->rp_buf != NULL)
2484: free(pd->rp_buf);
2485: free(pd);
2486: }
2487:
2488: /*
2489: * rcs_gettok()
2490: *
2491: * Get the next RCS token from the string <str>.
2492: */
2493: static int
2494: rcs_gettok(RCSFILE *rfp)
2495: {
2496: u_int i;
2497: int ch, last, type;
1.18 jfb 2498: size_t len;
2499: char *bp;
1.1 jfb 2500: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2501:
2502: type = RCS_TOK_ERR;
2503: bp = pdp->rp_buf;
1.42 jfb 2504: pdp->rp_tlen = 0;
1.1 jfb 2505: *bp = '\0';
2506:
2507: if (pdp->rp_pttype != RCS_TOK_ERR) {
2508: type = pdp->rp_pttype;
2509: strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen);
2510: pdp->rp_pttype = RCS_TOK_ERR;
2511: return (type);
2512: }
2513:
2514: /* skip leading whitespace */
2515: /* XXX we must skip backspace too for compatibility, should we? */
2516: do {
2517: ch = getc(pdp->rp_file);
2518: if (ch == '\n')
1.18 jfb 2519: pdp->rp_lines++;
1.1 jfb 2520: } while (isspace(ch));
2521:
2522: if (ch == EOF) {
2523: type = RCS_TOK_EOF;
1.14 deraadt 2524: } else if (ch == ';') {
1.1 jfb 2525: type = RCS_TOK_SCOLON;
1.14 deraadt 2526: } else if (ch == ':') {
1.1 jfb 2527: type = RCS_TOK_COLON;
1.14 deraadt 2528: } else if (isalpha(ch)) {
1.31 jfb 2529: type = RCS_TOK_ID;
1.1 jfb 2530: *(bp++) = ch;
1.18 jfb 2531: for (;;) {
1.1 jfb 2532: ch = getc(pdp->rp_file);
1.11 joris 2533: if (!isalnum(ch) && ch != '_' && ch != '-') {
1.1 jfb 2534: ungetc(ch, pdp->rp_file);
2535: break;
2536: }
2537: *(bp++) = ch;
1.42 jfb 2538: pdp->rp_tlen++;
1.18 jfb 2539: if (bp == pdp->rp_bufend - 1) {
2540: len = bp - pdp->rp_buf;
2541: if (rcs_growbuf(rfp) < 0) {
2542: type = RCS_TOK_ERR;
2543: break;
2544: }
2545: bp = pdp->rp_buf + len;
2546: }
1.1 jfb 2547: }
2548: *bp = '\0';
2549:
1.18 jfb 2550: if (type != RCS_TOK_ERR) {
2551: for (i = 0; i < RCS_NKEYS; i++) {
2552: if (strcmp(rcs_keys[i].rk_str,
2553: pdp->rp_buf) == 0) {
2554: type = rcs_keys[i].rk_id;
2555: break;
2556: }
1.1 jfb 2557: }
2558: }
1.14 deraadt 2559: } else if (ch == '@') {
1.1 jfb 2560: /* we have a string */
1.18 jfb 2561: type = RCS_TOK_STRING;
1.1 jfb 2562: for (;;) {
2563: ch = getc(pdp->rp_file);
2564: if (ch == '@') {
2565: ch = getc(pdp->rp_file);
2566: if (ch != '@') {
2567: ungetc(ch, pdp->rp_file);
2568: break;
2569: }
1.14 deraadt 2570: } else if (ch == '\n')
1.18 jfb 2571: pdp->rp_lines++;
1.1 jfb 2572:
2573: *(bp++) = ch;
1.42 jfb 2574: pdp->rp_tlen++;
1.18 jfb 2575: if (bp == pdp->rp_bufend - 1) {
2576: len = bp - pdp->rp_buf;
2577: if (rcs_growbuf(rfp) < 0) {
2578: type = RCS_TOK_ERR;
2579: break;
2580: }
2581: bp = pdp->rp_buf + len;
2582: }
1.1 jfb 2583: }
2584:
2585: *bp = '\0';
1.14 deraadt 2586: } else if (isdigit(ch)) {
1.1 jfb 2587: *(bp++) = ch;
2588: last = ch;
2589: type = RCS_TOK_NUM;
2590:
2591: for (;;) {
2592: ch = getc(pdp->rp_file);
1.18 jfb 2593: if (bp == pdp->rp_bufend)
1.1 jfb 2594: break;
2595: if (!isdigit(ch) && ch != '.') {
2596: ungetc(ch, pdp->rp_file);
2597: break;
2598: }
2599:
2600: if (last == '.' && ch == '.') {
2601: type = RCS_TOK_ERR;
2602: break;
2603: }
2604: last = ch;
2605: *(bp++) = ch;
1.42 jfb 2606: pdp->rp_tlen++;
1.1 jfb 2607: }
1.18 jfb 2608: *bp = '\0';
1.1 jfb 2609: }
2610:
2611: return (type);
2612: }
2613:
2614: /*
2615: * rcs_pushtok()
2616: *
2617: * Push a token back in the parser's token buffer.
2618: */
2619: static int
2620: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
2621: {
2622: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
2623:
2624: if (pdp->rp_pttype != RCS_TOK_ERR)
2625: return (-1);
2626:
2627: pdp->rp_pttype = type;
2628: strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok));
2629: return (0);
2630: }
2631:
1.18 jfb 2632:
2633: /*
2634: * rcs_growbuf()
2635: *
2636: * Attempt to grow the internal parse buffer for the RCS file <rf> by
2637: * RCS_BUFEXTSIZE.
2638: * In case of failure, the original buffer is left unmodified.
2639: * Returns 0 on success, or -1 on failure.
2640: */
2641: static int
2642: rcs_growbuf(RCSFILE *rf)
2643: {
2644: void *tmp;
2645: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2646:
2647: tmp = realloc(pdp->rp_buf, pdp->rp_blen + RCS_BUFEXTSIZE);
2648: if (tmp == NULL) {
1.50 jfb 2649: rcs_errno = RCS_ERR_ERRNO;
1.18 jfb 2650: cvs_log(LP_ERRNO, "failed to grow RCS parse buffer");
2651: return (-1);
2652: }
2653:
2654: pdp->rp_buf = (char *)tmp;
2655: pdp->rp_blen += RCS_BUFEXTSIZE;
2656: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.42 jfb 2657:
2658: return (0);
2659: }
2660:
2661: /*
2662: * rcs_strprint()
2663: *
2664: * Output an RCS string <str> of size <slen> to the stream <stream>. Any
2665: * '@' characters are escaped. Otherwise, the string can contain arbitrary
2666: * binary data.
2667: */
2668: static int
2669: rcs_strprint(const u_char *str, size_t slen, FILE *stream)
2670: {
2671: const u_char *ap, *ep, *sp;
2672: size_t ret;
1.52 jfb 2673:
2674: if (slen == 0)
2675: return (0);
1.42 jfb 2676:
2677: ep = str + slen - 1;
2678:
2679: for (sp = str; sp <= ep;) {
2680: ap = memchr(sp, '@', ep - sp);
2681: if (ap == NULL)
2682: ap = ep;
2683: ret = fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
2684:
2685: if (*ap == '@')
2686: putc('@', stream);
2687: sp = ap + 1;
1.63 joris 2688: }
2689:
2690: return (0);
2691: }
2692:
2693: /*
2694: * rcs_expand_keywords()
2695: *
2696: * Expand any RCS keywords in <line> into <out>
2697: */
2698: static int
2699: rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, char *line, char *out,
2700: size_t len, int mode)
2701: {
2702: int kwtype;
2703: u_int i, j, found;
2704: char *c, *kwstr, *start;
2705: char expbuf[128], buf[128];
2706:
1.65 niallo 2707: kwtype = 0;
2708: kwstr = NULL;
1.63 joris 2709: i = 0;
2710:
2711: /*
2712: * Keyword formats:
2713: * $Keyword$
2714: * $Keyword: value$
2715: */
2716: memset(out, '\0', len);
2717: for (c = line; *c != '\0' && i < len; *c++) {
2718: out[i++] = *c;
2719: if (*c == '$') {
2720: /* remember start of this possible keyword */
2721: start = c;
2722:
2723: /* first following character has to be alphanumeric */
2724: *c++;
2725: if (!isalpha(*c)) {
2726: c = start;
2727: continue;
2728: }
2729:
2730: /* look for any matching keywords */
2731: found = 0;
2732: for (j = 0; j < RCS_NKWORDS; j++) {
2733: if (!strncmp(c, rcs_expkw[j].kw_str,
2734: strlen(rcs_expkw[j].kw_str))) {
2735: found = 1;
2736: kwstr = rcs_expkw[j].kw_str;
2737: kwtype = rcs_expkw[j].kw_type;
2738: break;
2739: }
2740: }
2741:
2742: /* unknown keyword, continue looking */
2743: if (found == 0) {
2744: c = start;
2745: continue;
2746: }
2747:
2748: /* next character has to be ':' or '$' */
2749: c += strlen(kwstr);
2750: if (*c != ':' && *c != '$') {
2751: c = start;
2752: continue;
2753: }
2754:
2755: /*
2756: * if the next character was ':' we need to look for
2757: * an '$' before the end of the line to be sure it is
2758: * in fact a keyword.
2759: */
2760: if (*c == ':') {
2761: while (*c++) {
2762: if (*c == '$' || *c == '\n')
2763: break;
2764: }
2765:
2766: if (*c != '$') {
2767: c = start;
2768: continue;
2769: }
2770: }
2771:
2772: /* start constructing the expansion */
2773: expbuf[0] = '\0';
2774:
2775: if (mode & RCS_KWEXP_NAME) {
2776: strlcat(expbuf, "$", sizeof(expbuf));
2777: strlcat(expbuf, kwstr, sizeof(expbuf));
2778: if (mode & RCS_KWEXP_VAL)
2779: strlcat(expbuf, ": ", sizeof(expbuf));
2780: }
2781:
2782: /*
1.80 reyk 2783: * order matters because of RCS_KW_ID and
2784: * RCS_KW_HEADER here
1.63 joris 2785: */
2786: if (mode & RCS_KWEXP_VAL) {
2787: if (kwtype & RCS_KW_RCSFILE) {
2788: if (!(kwtype & RCS_KW_FULLPATH))
1.80 reyk 2789: strlcat(expbuf,
2790: basename(rcsfile),
1.63 joris 2791: sizeof(expbuf));
2792: else
2793: strlcat(expbuf, rcsfile,
2794: sizeof(expbuf));
2795: strlcat(expbuf, " ", sizeof(expbuf));
2796: }
2797:
2798: if (kwtype & RCS_KW_REVISION) {
1.80 reyk 2799: rcsnum_tostr(rdp->rd_num, buf,
2800: sizeof(buf));
1.63 joris 2801: strlcat(buf, " ", sizeof(buf));
2802: strlcat(expbuf, buf, sizeof(expbuf));
2803: }
2804:
2805: if (kwtype & RCS_KW_DATE) {
2806: strftime(buf, sizeof(buf),
1.80 reyk 2807: "%Y/%m/%d %H:%M:%S ",
2808: &rdp->rd_date);
1.63 joris 2809: strlcat(expbuf, buf, sizeof(expbuf));
2810: }
2811:
2812: if (kwtype & RCS_KW_AUTHOR) {
2813: strlcat(expbuf, rdp->rd_author,
2814: sizeof(expbuf));
2815: strlcat(expbuf, " ", sizeof(expbuf));
2816: }
2817:
2818: if (kwtype & RCS_KW_STATE) {
2819: strlcat(expbuf, rdp->rd_state,
2820: sizeof(expbuf));
2821: strlcat(expbuf, " ", sizeof(expbuf));
2822: }
2823:
2824: /* order does not matter anymore below */
2825: if (kwtype & RCS_KW_LOG)
2826: strlcat(expbuf, " ", sizeof(expbuf));
2827:
2828: if (kwtype & RCS_KW_SOURCE) {
1.80 reyk 2829: strlcat(expbuf, rcsfile,
2830: sizeof(expbuf));
1.63 joris 2831: strlcat(expbuf, " ", sizeof(expbuf));
2832: }
2833:
2834: if (kwtype & RCS_KW_NAME)
2835: strlcat(expbuf, " ", sizeof(expbuf));
2836: }
2837:
2838: /* end the expansion */
2839: if (mode & RCS_KWEXP_NAME)
2840: strlcat(expbuf, "$", sizeof(expbuf));
2841:
2842: out[--i] = '\0';
2843: strlcat(out, expbuf, len);
2844: i += strlen(expbuf);
2845: }
1.42 jfb 2846: }
1.81 niallo 2847:
2848: return (0);
2849: }
2850:
2851: /*
2852: * rcs_deltatext_set()
2853: *
2854: * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1.96 xsa 2855: * Returns -1 on error, 0 on success.
1.81 niallo 2856: */
2857: int
2858: rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, const char *dtext)
2859: {
2860: size_t len;
2861: struct rcs_delta *rdp;
2862:
2863: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2864: return (-1);
2865:
2866: if (rdp->rd_text != NULL)
2867: free(rdp->rd_text);
2868:
2869: len = strlen(dtext);
2870: if ((rdp->rd_text = (u_char *)malloc(len)) == NULL)
2871: return (-1);
2872:
2873: rdp->rd_tlen = len - 1;
2874: strlcpy(rdp->rd_text, dtext, len);
1.18 jfb 2875:
1.86 joris 2876: return (0);
2877: }
2878:
2879: /*
2880: * rcs_rev_setlog()
2881: *
2882: * Sets the log message of revision <rev> to <logtext>
2883: */
2884: int
2885: rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
2886: {
2887: struct rcs_delta *rdp;
2888: char buf[16];
2889:
2890: rcsnum_tostr(rev, buf, sizeof(buf));
2891: printf("setting log for %s to '%s'\n", buf, logtext);
2892:
2893: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2894: return (-1);
2895:
2896: if (rdp->rd_log != NULL)
1.99 niallo 2897: free(rdp->rd_log);
1.86 joris 2898:
1.99 niallo 2899: if ((rdp->rd_log = strdup(logtext)) == NULL)
1.86 joris 2900: return (-1);
2901:
2902: rfp->rf_flags &= ~RCS_SYNCED;
1.95 niallo 2903: return (0);
2904: }
1.97 niallo 2905: /*
2906: * rcs_rev_getdate()
2907: *
2908: * Get the date corresponding to a given revision.
2909: * Returns the date on success, -1 on failure.
2910: */
2911: time_t
2912: rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
2913: {
2914: struct rcs_delta *rdp;
2915:
2916: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2917: return (-1);
2918:
2919: return (mktime(&rdp->rd_date));
2920: }
1.95 niallo 2921:
2922: /*
2923: * rcs_state_set()
2924: *
2925: * Sets the state of revision <rev> to <state>
2926: * NOTE: default state is 'Exp'. States may not contain spaces.
2927: *
2928: * Returns -1 on failure, 0 on success.
2929: */
2930: int
2931: rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
2932: {
2933: struct rcs_delta *rdp;
2934:
2935: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2936: return (-1);
2937:
2938: if (rdp->rd_state != NULL)
1.99 niallo 2939: free(rdp->rd_state);
1.95 niallo 2940:
1.99 niallo 2941: if ((rdp->rd_state = strdup(state)) == NULL)
1.95 niallo 2942: return (-1);
2943:
2944: rfp->rf_flags &= ~RCS_SYNCED;
2945:
2946: return (0);
2947: }
2948:
2949: /*
2950: * rcs_state_check()
2951: *
2952: * Check if string <state> is valid.
2953: *
1.96 xsa 2954: * Returns 0 if the string is valid, -1 otherwise.
1.95 niallo 2955: */
2956: int
2957: rcs_state_check(const char *state)
2958: {
2959: if (strchr(state, ' ') != NULL)
2960: return (-1);
2961:
1.18 jfb 2962: return (0);
1.1 jfb 2963: }
1.97 niallo 2964:
2965: /*
2966: * rcs_state_get()
2967: *
2968: * Get the state for a given revision of a specified RCSFILE.
2969: *
2970: * Returns NULL on failure.
2971: */
2972: const char *
2973: rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
2974: {
2975: struct rcs_delta *rdp;
2976:
2977: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2978: return (NULL);
2979:
2980: return (rdp->rd_state);
2981: }
2982: