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