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