Annotation of src/usr.bin/cvs/rcs.c, Revision 1.26
1.25 jfb 1: /* $OpenBSD: rcs.c,v 1.24 2005/02/16 19:45:59 jfb 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/queue.h>
29: #include <sys/stat.h>
30:
31: #include <errno.h>
32: #include <stdio.h>
33: #include <ctype.h>
34: #include <stdlib.h>
1.26 ! jfb 35: #include <stdarg.h>
1.1 jfb 36: #include <string.h>
37:
38: #include "rcs.h"
39: #include "log.h"
40:
1.26 ! jfb 41: #define RCS_BUFSIZE 16384
1.18 jfb 42: #define RCS_BUFEXTSIZE 8192
1.1 jfb 43:
44:
45: /* RCS token types */
46: #define RCS_TOK_ERR -1
47: #define RCS_TOK_EOF 0
48: #define RCS_TOK_NUM 1
49: #define RCS_TOK_ID 2
50: #define RCS_TOK_STRING 3
51: #define RCS_TOK_SCOLON 4
52: #define RCS_TOK_COLON 5
53:
54:
55: #define RCS_TOK_HEAD 8
56: #define RCS_TOK_BRANCH 9
57: #define RCS_TOK_ACCESS 10
58: #define RCS_TOK_SYMBOLS 11
59: #define RCS_TOK_LOCKS 12
60: #define RCS_TOK_COMMENT 13
61: #define RCS_TOK_EXPAND 14
62: #define RCS_TOK_DATE 15
63: #define RCS_TOK_AUTHOR 16
64: #define RCS_TOK_STATE 17
65: #define RCS_TOK_NEXT 18
66: #define RCS_TOK_BRANCHES 19
67: #define RCS_TOK_DESC 20
68: #define RCS_TOK_LOG 21
69: #define RCS_TOK_TEXT 22
70: #define RCS_TOK_STRICT 23
71:
72: #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
73:
74:
75: #define RCS_NOSCOL 0x01 /* no terminating semi-colon */
76: #define RCS_VOPT 0x02 /* value is optional */
77:
78:
79: /* opaque parse data */
80: struct rcs_pdata {
1.18 jfb 81: u_int rp_lines;
1.1 jfb 82:
83: char *rp_buf;
84: size_t rp_blen;
1.18 jfb 85: char *rp_bufend;
1.1 jfb 86:
87: /* pushback token buffer */
88: char rp_ptok[128];
89: int rp_pttype; /* token type, RCS_TOK_ERR if no token */
90:
91: FILE *rp_file;
92: };
93:
94:
95: struct rcs_line {
96: char *rl_line;
97: int rl_lineno;
98: TAILQ_ENTRY(rcs_line) rl_list;
99: };
1.5 vincent 100: TAILQ_HEAD(rcs_tqh, rcs_line);
1.1 jfb 101:
102: struct rcs_foo {
103: int rl_nblines;
104: char *rl_data;
1.5 vincent 105: struct rcs_tqh rl_lines;
1.1 jfb 106: };
107:
108: #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
109: #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_blen
110:
111:
1.20 jfb 112: static struct rcs_kfl {
113: char rk_char;
114: int rk_val;
115: } rcs_kflags[] = {
116: { 'k', RCS_KWEXP_NAME },
117: { 'v', RCS_KWEXP_VAL },
118: { 'l', RCS_KWEXP_LKR },
119: { 'o', RCS_KWEXP_OLD },
120: { 'b', RCS_KWEXP_NONE },
121: };
122:
1.1 jfb 123: static struct rcs_key {
124: char rk_str[16];
125: int rk_id;
126: int rk_val;
127: int rk_flags;
128: } rcs_keys[] = {
129: { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT },
130: { "author", RCS_TOK_AUTHOR, RCS_TOK_STRING, 0 },
131: { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT },
132: { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT },
133: { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT },
134: { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 },
135: { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL },
136: { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT },
137: { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT },
138: { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 },
139: { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL },
140: { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT },
141: { "state", RCS_TOK_STATE, RCS_TOK_STRING, RCS_VOPT },
142: { "strict", RCS_TOK_STRICT, 0, 0, },
143: { "symbols", RCS_TOK_SYMBOLS, 0, 0 },
144: { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL },
145: };
146:
1.18 jfb 147: #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0]))
1.1 jfb 148:
149:
1.26 ! jfb 150: static int rcs_parse (RCSFILE *);
! 151: static int rcs_parse_admin (RCSFILE *);
! 152: static int rcs_parse_delta (RCSFILE *);
! 153: static int rcs_parse_deltatext (RCSFILE *);
! 154:
! 155: static int rcs_parse_access (RCSFILE *);
! 156: static int rcs_parse_symbols (RCSFILE *);
! 157: static int rcs_parse_locks (RCSFILE *);
! 158: static int rcs_parse_branches (RCSFILE *, struct rcs_delta *);
! 159: static void rcs_freedelta (struct rcs_delta *);
! 160: static void rcs_freepdata (struct rcs_pdata *);
! 161: static int rcs_gettok (RCSFILE *);
! 162: static int rcs_pushtok (RCSFILE *, const char *, int);
! 163: static int rcs_growbuf (RCSFILE *);
! 164: static int rcs_patch_lines (struct rcs_foo *, struct rcs_foo *);
! 165:
! 166: static struct rcs_delta* rcs_findrev (RCSFILE *, RCSNUM *);
! 167: static struct rcs_foo* rcs_splitlines (const char *);
! 168: static void rcs_freefoo (struct rcs_foo *);
! 169:
! 170:
1.1 jfb 171: /*
172: * rcs_open()
173: *
174: * Open a file containing RCS-formatted information. The file's path is
1.26 ! jfb 175: * given in <path>, and the opening flags are given in <flags>, which is either
! 176: * RCS_READ, RCS_WRITE, or RCS_RDWR. If the open requests write access and
! 177: * the file does not exist, the RCS_CREATE flag must also be given, in which
! 178: * case it will be created with the mode specified in a third argument of
! 179: * type mode_t. If the file exists and RCS_CREATE is passed, the open will
! 180: * fail.
1.1 jfb 181: * Returns a handle to the opened file on success, or NULL on failure.
182: */
183: RCSFILE*
1.26 ! jfb 184: rcs_open(const char *path, int flags, ...)
1.1 jfb 185: {
1.26 ! jfb 186: int ret;
! 187: mode_t fmode;
1.1 jfb 188: RCSFILE *rfp;
189: struct stat st;
1.26 ! jfb 190: va_list vap;
! 191:
! 192: fmode = 0;
! 193: flags &= 0xffff; /* ditch any internal flags */
1.1 jfb 194:
1.26 ! jfb 195: if (((ret = stat(path, &st)) == -1) && (errno == ENOENT)) {
! 196: if (flags & RCS_CREATE) {
! 197: va_start(vap, flags);
! 198: fmode = va_arg(vap, mode_t);
! 199: va_end(vap);
! 200: } else {
! 201: cvs_log(LP_ERR, "RCS file `%s' does not exist", path);
! 202: return (NULL);
! 203: }
! 204: } else if ((ret == 0) && (flags & RCS_CREATE)) {
! 205: cvs_log(LP_ERR, "RCS file `%s' exists", path);
1.1 jfb 206: return (NULL);
207: }
208:
1.26 ! jfb 209: if ((rfp = (RCSFILE *)malloc(sizeof(*rfp))) == NULL) {
1.1 jfb 210: cvs_log(LP_ERRNO, "failed to allocate RCS file structure");
211: return (NULL);
212: }
213: memset(rfp, 0, sizeof(*rfp));
214:
1.26 ! jfb 215: if ((rfp->rf_head = rcsnum_parse(RCS_HEAD_INIT)) == NULL) {
1.1 jfb 216: free(rfp);
217: return (NULL);
218: }
219:
1.26 ! jfb 220: if ((rfp->rf_branch = rcsnum_alloc()) == NULL) {
! 221: rcsnum_free(rfp->rf_head);
! 222: free(rfp);
1.11 joris 223: return (NULL);
224: }
225:
1.26 ! jfb 226: if ((rfp->rf_path = strdup(path)) == NULL) {
1.1 jfb 227: cvs_log(LP_ERRNO, "failed to duplicate RCS file path");
1.26 ! jfb 228: rcsnum_free(rfp->rf_branch);
! 229: rcsnum_free(rfp->rf_head);
! 230: free(rfp);
1.1 jfb 231: return (NULL);
232: }
233:
234: rfp->rf_ref = 1;
1.26 ! jfb 235: rfp->rf_flags = flags | RCS_SLOCK;
! 236: rfp->rf_mode = fmode;
1.1 jfb 237:
238: TAILQ_INIT(&(rfp->rf_delta));
239: TAILQ_INIT(&(rfp->rf_symbols));
240: TAILQ_INIT(&(rfp->rf_locks));
241:
1.26 ! jfb 242: if (rfp->rf_flags & RCS_CREATE) {
! 243: } else if (rcs_parse(rfp) < 0) {
1.1 jfb 244: rcs_close(rfp);
245: return (NULL);
246: }
247:
248: return (rfp);
249: }
250:
251:
252: /*
253: * rcs_close()
254: *
255: * Close an RCS file handle.
256: */
257: void
258: rcs_close(RCSFILE *rfp)
259: {
260: struct rcs_delta *rdp;
1.13 jfb 261: struct rcs_lock *rlp;
262: struct rcs_sym *rsp;
1.1 jfb 263:
264: if (rfp->rf_ref > 1) {
265: rfp->rf_ref--;
266: return;
267: }
268:
1.26 ! jfb 269: if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
! 270: rcs_write(rfp);
! 271:
1.1 jfb 272: while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
273: rdp = TAILQ_FIRST(&(rfp->rf_delta));
274: TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
275: rcs_freedelta(rdp);
276: }
277:
1.13 jfb 278: while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
279: rsp = TAILQ_FIRST(&(rfp->rf_symbols));
280: TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
281: rcsnum_free(rsp->rs_num);
282: free(rsp->rs_name);
283: free(rsp);
284: }
285:
286: while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
287: rlp = TAILQ_FIRST(&(rfp->rf_locks));
288: TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
289: rcsnum_free(rlp->rl_num);
290: free(rlp);
291: }
292:
1.1 jfb 293: if (rfp->rf_head != NULL)
294: rcsnum_free(rfp->rf_head);
1.11 joris 295: if (rfp->rf_branch != NULL)
296: rcsnum_free(rfp->rf_branch);
1.1 jfb 297:
298: if (rfp->rf_path != NULL)
299: free(rfp->rf_path);
300: if (rfp->rf_comment != NULL)
301: free(rfp->rf_comment);
302: if (rfp->rf_expand != NULL)
303: free(rfp->rf_expand);
304: if (rfp->rf_desc != NULL)
305: free(rfp->rf_desc);
306: free(rfp);
307: }
308:
309:
310: /*
311: * rcs_write()
312: *
313: * Write the contents of the RCS file handle <rfp> to disk in the file whose
314: * path is in <rf_path>.
315: * Returns 0 on success, or -1 on failure.
316: */
317: int
318: rcs_write(RCSFILE *rfp)
319: {
320: FILE *fp;
1.7 jfb 321: char buf[1024], numbuf[64], *cp;
322: size_t rlen, len;
1.1 jfb 323: struct rcs_sym *symp;
324: struct rcs_delta *rdp;
325:
1.26 ! jfb 326: if ((rfp->rf_flags & RCS_SYNCED) || (rfp->rf_ndelta == 0))
1.1 jfb 327: return (0);
328:
329: fp = fopen(rfp->rf_path, "w");
330: if (fp == NULL) {
331: cvs_log(LP_ERRNO, "failed to open RCS output file `%s'",
332: rfp->rf_path);
333: return (-1);
334: }
335:
336: rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
337: fprintf(fp, "head\t%s;\n", numbuf);
338: fprintf(fp, "access;\n");
339:
340: fprintf(fp, "symbols\n");
341: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
342: rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
343: snprintf(buf, sizeof(buf), "%s:%s", symp->rs_name, numbuf);
344: fprintf(fp, "\t%s", buf);
345: if (symp != TAILQ_LAST(&(rfp->rf_symbols), rcs_slist))
346: fputc('\n', fp);
347: }
348: fprintf(fp, ";\n");
349:
350: fprintf(fp, "locks;");
351:
1.26 ! jfb 352: if (rfp->rf_flags & RCS_SLOCK)
1.1 jfb 353: fprintf(fp, " strict;");
354: fputc('\n', fp);
355:
356: if (rfp->rf_comment != NULL)
357: fprintf(fp, "comment\t@%s@;\n", rfp->rf_comment);
358:
359: if (rfp->rf_expand != NULL)
360: fprintf(fp, "expand @ %s @;\n", rfp->rf_expand);
361:
362: fprintf(fp, "\n\n");
363:
364: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
365: fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
366: sizeof(numbuf)));
367: fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
368: rdp->rd_date.tm_year, rdp->rd_date.tm_mon + 1,
369: rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
370: rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
371: fprintf(fp, "\tauthor %s;\tstate %s;\n",
372: rdp->rd_author, rdp->rd_state);
373: fprintf(fp, "branches;\n");
374: fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
375: numbuf, sizeof(numbuf)));
376: }
377:
1.26 ! jfb 378: fprintf(fp, "\ndesc\n@%s@\n\n",
! 379: (rfp->rf_desc == NULL) ? "" : rfp->rf_desc);
1.1 jfb 380:
381: /* deltatexts */
382: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
383: fprintf(fp, "\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
384: sizeof(numbuf)));
1.7 jfb 385: fprintf(fp, "log\n@%s@\ntext\n@", rdp->rd_log);
386:
387: cp = rdp->rd_text;
388: do {
389: len = sizeof(buf);
390: rlen = rcs_stresc(1, cp, buf, &len);
391: fprintf(fp, "%s", buf);
392: cp += rlen;
393: } while (len != 0);
394: fprintf(fp, "@\n\n");
1.1 jfb 395: }
396: fclose(fp);
397:
1.26 ! jfb 398: rfp->rf_flags |= RCS_SYNCED;
1.1 jfb 399:
400: return (0);
401: }
402:
403:
404: /*
1.26 ! jfb 405: * rcs_sym_add()
1.1 jfb 406: *
407: * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
408: * is named <sym> and is bound to the RCS revision <snum>.
409: * Returns 0 on success, or -1 on failure.
410: */
411: int
1.26 ! jfb 412: rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
1.1 jfb 413: {
414: struct rcs_sym *symp;
415:
416: /* first look for duplication */
417: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
418: if (strcmp(symp->rs_name, sym) == 0) {
1.17 jfb 419: cvs_log(LP_ERR, "attempt to add duplicate symbol `%s'",
420: sym);
1.1 jfb 421: return (-1);
422: }
423: }
424:
425: symp = (struct rcs_sym *)malloc(sizeof(*symp));
426: if (symp == NULL) {
427: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
428: return (-1);
429: }
430:
431: symp->rs_name = strdup(sym);
1.10 joris 432: if (symp->rs_name == NULL) {
433: cvs_log(LP_ERRNO, "failed to duplicate symbol");
434: free(symp);
435: return (-1);
436: }
437:
1.1 jfb 438: symp->rs_num = rcsnum_alloc();
1.11 joris 439: if (symp->rs_num == NULL) {
1.17 jfb 440: free(symp->rs_name);
1.11 joris 441: free(symp);
442: return (-1);
443: }
1.1 jfb 444: rcsnum_cpy(snum, symp->rs_num, 0);
445:
446: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
447:
448: /* not synced anymore */
1.26 ! jfb 449: rfp->rf_flags &= ~RCS_SYNCED;
1.1 jfb 450:
451: return (0);
452: }
453:
454:
455: /*
456: * rcs_patch()
457: *
458: * Apply an RCS-format patch pointed to by <patch> to the file contents
459: * found in <data>.
460: * Returns 0 on success, or -1 on failure.
461: */
462: BUF*
463: rcs_patch(const char *data, const char *patch)
464: {
1.5 vincent 465: struct rcs_foo *dlines, *plines;
466: struct rcs_line *lp;
1.1 jfb 467: size_t len;
1.5 vincent 468: int lineno;
1.1 jfb 469: BUF *res;
470:
471: len = strlen(data);
472: res = cvs_buf_alloc(len, BUF_AUTOEXT);
473: if (res == NULL)
474: return (NULL);
475:
476: dlines = rcs_splitlines(data);
1.17 jfb 477: if (dlines == NULL) {
478: cvs_buf_free(res);
1.1 jfb 479: return (NULL);
1.17 jfb 480: }
1.5 vincent 481:
1.1 jfb 482: plines = rcs_splitlines(patch);
1.5 vincent 483: if (plines == NULL) {
1.17 jfb 484: cvs_buf_free(res);
1.5 vincent 485: rcs_freefoo(dlines);
1.1 jfb 486: return (NULL);
1.5 vincent 487: }
488:
489: if (rcs_patch_lines(dlines, plines) < 0) {
1.17 jfb 490: cvs_buf_free(res);
1.5 vincent 491: rcs_freefoo(plines);
492: rcs_freefoo(dlines);
493: return (NULL);
494: }
495:
496: lineno = 0;
497: TAILQ_FOREACH(lp, &dlines->rl_lines, rl_list) {
498: if (lineno != 0)
499: cvs_buf_fappend(res, "%s\n", lp->rl_line);
500: lineno++;
501: }
502:
503: rcs_freefoo(dlines);
504: rcs_freefoo(plines);
505: return (res);
506: }
507:
1.7 jfb 508: static int
1.5 vincent 509: rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
510: {
511: char op, *ep;
512: struct rcs_line *lp, *dlp, *ndlp;
513: int i, lineno, nbln;
1.1 jfb 514:
515: dlp = TAILQ_FIRST(&(dlines->rl_lines));
516: lp = TAILQ_FIRST(&(plines->rl_lines));
517:
518: /* skip first bogus line */
519: for (lp = TAILQ_NEXT(lp, rl_list); lp != NULL;
520: lp = TAILQ_NEXT(lp, rl_list)) {
521: op = *(lp->rl_line);
522: lineno = (int)strtol((lp->rl_line + 1), &ep, 10);
523: if ((lineno > dlines->rl_nblines) || (lineno <= 0) ||
524: (*ep != ' ')) {
525: cvs_log(LP_ERR,
526: "invalid line specification in RCS patch");
527: return (NULL);
528: }
529: ep++;
530: nbln = (int)strtol(ep, &ep, 10);
531: if ((nbln <= 0) || (*ep != '\0')) {
532: cvs_log(LP_ERR,
533: "invalid line number specification in RCS patch");
534: return (NULL);
535: }
536:
537: /* find the appropriate line */
538: for (;;) {
539: if (dlp == NULL)
540: break;
541: if (dlp->rl_lineno == lineno)
542: break;
543: if (dlp->rl_lineno > lineno) {
544: dlp = TAILQ_PREV(dlp, rcs_tqh, rl_list);
1.14 deraadt 545: } else if (dlp->rl_lineno < lineno) {
1.1 jfb 546: ndlp = TAILQ_NEXT(dlp, rl_list);
547: if (ndlp->rl_lineno > lineno)
548: break;
549: dlp = ndlp;
550: }
551: }
552: if (dlp == NULL) {
553: cvs_log(LP_ERR,
554: "can't find referenced line in RCS patch");
555: return (NULL);
556: }
557:
558: if (op == 'd') {
559: for (i = 0; (i < nbln) && (dlp != NULL); i++) {
560: ndlp = TAILQ_NEXT(dlp, rl_list);
561: TAILQ_REMOVE(&(dlines->rl_lines), dlp, rl_list);
562: dlp = ndlp;
563: }
1.14 deraadt 564: } else if (op == 'a') {
1.1 jfb 565: for (i = 0; i < nbln; i++) {
566: ndlp = lp;
567: lp = TAILQ_NEXT(lp, rl_list);
568: if (lp == NULL) {
569: cvs_log(LP_ERR, "truncated RCS patch");
1.5 vincent 570: return (-1);
1.1 jfb 571: }
572: TAILQ_REMOVE(&(plines->rl_lines), lp, rl_list);
573: TAILQ_INSERT_AFTER(&(dlines->rl_lines), dlp,
574: lp, rl_list);
575: dlp = lp;
576:
577: /* we don't want lookup to block on those */
578: lp->rl_lineno = lineno;
579:
580: lp = ndlp;
581: }
1.14 deraadt 582: } else {
1.1 jfb 583: cvs_log(LP_ERR, "unknown RCS patch operation `%c'", op);
1.5 vincent 584: return (-1);
1.1 jfb 585: }
586:
587: /* last line of the patch, done */
588: if (lp->rl_lineno == plines->rl_nblines)
589: break;
590: }
591:
592: /* once we're done patching, rebuild the line numbers */
1.2 vincent 593: lineno = 0;
1.5 vincent 594: TAILQ_FOREACH(lp, &(dlines->rl_lines), rl_list)
1.1 jfb 595: lp->rl_lineno = lineno++;
596: dlines->rl_nblines = lineno - 1;
597:
1.5 vincent 598: return (0);
1.1 jfb 599: }
600:
601:
602: /*
603: * rcs_getrev()
604: *
605: * Get the whole contents of revision <rev> from the RCSFILE <rfp>. The
1.4 vincent 606: * returned buffer is dynamically allocated and should be released using
607: * cvs_buf_free() once the caller is done using it.
1.1 jfb 608: */
609: BUF*
610: rcs_getrev(RCSFILE *rfp, RCSNUM *rev)
611: {
612: int res;
613: size_t len;
614: void *bp;
615: RCSNUM *crev;
616: BUF *rbuf;
617: struct rcs_delta *rdp = NULL;
618:
619: res = rcsnum_cmp(rfp->rf_head, rev, 0);
620: if (res == 1) {
621: cvs_log(LP_ERR, "sorry, can't travel in the future yet");
622: return (NULL);
1.26 ! jfb 623: }
! 624:
! 625: rdp = rcs_findrev(rfp, rfp->rf_head);
! 626: if (rdp == NULL) {
! 627: cvs_log(LP_ERR, "failed to get RCS HEAD revision");
! 628: return (NULL);
! 629: }
! 630:
! 631: len = strlen(rdp->rd_text);
! 632: if ((rbuf = cvs_buf_alloc(len, BUF_AUTOEXT)) == NULL)
! 633: return (NULL);
! 634:
! 635: cvs_buf_append(rbuf, rdp->rd_text, len);
! 636:
! 637: if (res != 0) {
! 638: /* Apply patches backwards to get the right version.
! 639: * This will need some rework to support sub branches.
! 640: */
! 641: if ((crev = rcsnum_alloc()) == NULL) {
! 642: cvs_buf_free(rbuf);
1.1 jfb 643: return (NULL);
644: }
1.26 ! jfb 645: rcsnum_cpy(rfp->rf_head, crev, 0);
! 646: do {
! 647: crev->rn_id[crev->rn_len - 1]--;
! 648: rdp = rcs_findrev(rfp, crev);
! 649: if (rdp == NULL) {
! 650: rcsnum_free(crev);
! 651: cvs_buf_free(rbuf);
! 652: return (NULL);
! 653: }
1.1 jfb 654:
1.26 ! jfb 655: if (cvs_buf_putc(rbuf, '\0') < 0) {
! 656: rcsnum_free(crev);
1.17 jfb 657: cvs_buf_free(rbuf);
1.11 joris 658: return (NULL);
1.17 jfb 659: }
1.26 ! jfb 660: bp = cvs_buf_release(rbuf);
! 661: rbuf = rcs_patch((char *)bp, rdp->rd_text);
! 662: if (rbuf == NULL)
! 663: break;
! 664: } while (rcsnum_cmp(crev, rev, 0) != 0);
1.1 jfb 665:
1.26 ! jfb 666: rcsnum_free(crev);
1.1 jfb 667: }
668:
669: return (rbuf);
1.16 jfb 670: }
671:
672:
673: /*
674: * rcs_gethead()
675: *
676: * Get the head revision for the RCS file <rf>.
677: */
678: BUF*
679: rcs_gethead(RCSFILE *rf)
680: {
681: return rcs_getrev(rf, rf->rf_head);
1.1 jfb 682: }
683:
684:
685: /*
686: * rcs_getrevbydate()
687: *
688: * Get an RCS revision by a specific date.
689: */
690: RCSNUM*
691: rcs_getrevbydate(RCSFILE *rfp, struct tm *date)
692: {
693: return (NULL);
694: }
695:
696:
697: /*
698: * rcs_findrev()
699: *
700: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
701: * The revision number is given in <rev>.
702: * Returns a pointer to the delta on success, or NULL on failure.
703: */
704: static struct rcs_delta*
705: rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
706: {
707: u_int cmplen;
708: struct rcs_delta *rdp;
709: struct rcs_dlist *hp;
1.6 vincent 710: int found;
1.26 ! jfb 711:
1.1 jfb 712: cmplen = 2;
713: hp = &(rfp->rf_delta);
714:
1.6 vincent 715: do {
716: found = 0;
717: TAILQ_FOREACH(rdp, hp, rd_list) {
718: if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) {
719: if (cmplen == rev->rn_len)
720: return (rdp);
1.1 jfb 721:
1.6 vincent 722: hp = &(rdp->rd_snodes);
723: cmplen += 2;
724: found = 1;
725: break;
726: }
1.1 jfb 727: }
1.6 vincent 728: } while (found && cmplen < rev->rn_len);
1.1 jfb 729:
730: return (NULL);
1.20 jfb 731: }
732:
733:
734: /*
1.26 ! jfb 735: * rcs_kwexp_set()
! 736: *
! 737: * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
! 738: * Returns 0 on success, or -1 on failure.
! 739: */
! 740: int
! 741: rcs_kwexp_set(RCSFILE *file, int mode)
! 742: {
! 743: int i;
! 744: char *tmp, buf[8] = "";
! 745:
! 746: if (RCS_KWEXP_INVAL(mode))
! 747: return (-1);
! 748:
! 749: i = 0;
! 750: if (mode == RCS_KWEXP_NONE)
! 751: buf[0] = 'b';
! 752: else if (mode == RCS_KWEXP_OLD)
! 753: buf[0] = 'o';
! 754: else {
! 755: if (mode & RCS_KWEXP_NAME)
! 756: buf[i++] = 'k';
! 757: if (mode & RCS_KWEXP_VAL)
! 758: buf[i++] = 'v';
! 759: if (mode & RCS_KWEXP_LKR)
! 760: buf[i++] = 'l';
! 761: }
! 762:
! 763: if ((tmp = strdup(buf)) == NULL) {
! 764: cvs_log(LP_ERRNO, "%s: failed to copy expansion mode",
! 765: file->rf_path);
! 766: return (-1);
! 767: }
! 768:
! 769: free(file->rf_expand);
! 770: file->rf_expand = tmp;
! 771:
! 772: return (0);
! 773: }
! 774:
! 775: /*
! 776: * rcs_kwexp_get()
! 777: *
! 778: * Retrieve the keyword expansion mode to be used for the RCS file <file>.
! 779: */
! 780: int
! 781: rcs_kwexp_get(RCSFILE *file)
! 782: {
! 783: return rcs_kflag_get(file->rf_expand);
! 784: }
! 785:
! 786: /*
1.20 jfb 787: * rcs_kflag_get()
788: *
789: * Get the keyword expansion mode from a set of character flags given in
790: * <flags> and return the appropriate flag mask. In case of an error, the
791: * returned mask will have the RCS_KWEXP_ERR bit set to 1.
792: */
793: int
794: rcs_kflag_get(const char *flags)
795: {
796: int fl;
797: size_t len;
798: const char *fp;
799:
800: fl = 0;
801: len = strlen(flags);
802:
803: for (fp = flags; *fp != '\0'; fp++) {
804: if (*fp == 'k')
805: fl |= RCS_KWEXP_NAME;
806: else if (*fp == 'v')
807: fl |= RCS_KWEXP_VAL;
808: else if (*fp == 'l')
809: fl |= RCS_KWEXP_LKR;
810: else if (*fp == 'o') {
811: if (len != 1)
812: fl |= RCS_KWEXP_ERR;
813: fl |= RCS_KWEXP_OLD;
814: } else if (*fp == 'b') {
815: if (len != 1)
816: fl |= RCS_KWEXP_ERR;
817: } else /* unknown letter */
818: fl |= RCS_KWEXP_ERR;
819: }
820:
821: return (fl);
1.1 jfb 822: }
823:
1.21 jfb 824: void
825: rcs_kflag_usage(void)
826: {
827: fprintf(stderr, "Valid expansion modes include:\n"
1.22 jfb 828: "\t-kkv\tGenerate keywords using the default form.\n"
829: "\t-kkvl\tLike -kkv, except locker's name inserted.\n"
830: "\t-kk\tGenerate only keyword names in keyword strings.\n"
831: "\t-kv\tGenerate only keyword values in keyword strings.\n"
832: "\t-ko\tGenerate old keyword string "
1.21 jfb 833: "(no changes from checked in file).\n"
1.22 jfb 834: "\t-kb\tGenerate binary file unmodified (merges not allowed).\n");
1.21 jfb 835: }
1.1 jfb 836:
1.26 ! jfb 837:
1.1 jfb 838: /*
839: * rcs_parse()
840: *
841: * Parse the contents of file <path>, which are in the RCS format.
842: * Returns 0 on success, or -1 on failure.
843: */
1.26 ! jfb 844: static int
1.1 jfb 845: rcs_parse(RCSFILE *rfp)
846: {
847: int ret;
848: struct rcs_pdata *pdp;
849:
1.26 ! jfb 850: if (rfp->rf_flags & RCS_PARSED)
1.1 jfb 851: return (0);
852:
1.26 ! jfb 853: if ((pdp = (struct rcs_pdata *)malloc(sizeof(*pdp))) == NULL) {
1.1 jfb 854: cvs_log(LP_ERRNO, "failed to allocate RCS parser data");
855: return (-1);
856: }
857: memset(pdp, 0, sizeof(*pdp));
858:
1.18 jfb 859: pdp->rp_lines = 0;
1.1 jfb 860: pdp->rp_pttype = RCS_TOK_ERR;
861:
862: pdp->rp_file = fopen(rfp->rf_path, "r");
863: if (pdp->rp_file == NULL) {
864: cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path);
865: rcs_freepdata(pdp);
866: return (-1);
867: }
868:
869: pdp->rp_buf = (char *)malloc(RCS_BUFSIZE);
870: if (pdp->rp_buf == NULL) {
871: cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer");
872: rcs_freepdata(pdp);
873: return (-1);
874: }
875: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 876: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 877:
878: /* ditch the strict lock */
1.26 ! jfb 879: rfp->rf_flags &= ~RCS_SLOCK;
1.1 jfb 880: rfp->rf_pdata = pdp;
881:
882: if (rcs_parse_admin(rfp) < 0) {
883: rcs_freepdata(pdp);
884: return (-1);
885: }
886:
887: for (;;) {
888: ret = rcs_parse_delta(rfp);
889: if (ret == 0)
890: break;
891: else if (ret == -1) {
892: rcs_freepdata(pdp);
893: return (-1);
894: }
895: }
896:
897: ret = rcs_gettok(rfp);
898: if (ret != RCS_TOK_DESC) {
899: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
900: RCS_TOKSTR(rfp));
901: rcs_freepdata(pdp);
902: return (-1);
903: }
904:
905: ret = rcs_gettok(rfp);
906: if (ret != RCS_TOK_STRING) {
907: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
908: RCS_TOKSTR(rfp));
909: rcs_freepdata(pdp);
910: return (-1);
911: }
912:
913: rfp->rf_desc = strdup(RCS_TOKSTR(rfp));
1.10 joris 914: if (rfp->rf_desc == NULL) {
915: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
916: rcs_freepdata(pdp);
917: return (-1);
918: }
1.1 jfb 919:
920: for (;;) {
921: ret = rcs_parse_deltatext(rfp);
922: if (ret == 0)
923: break;
924: else if (ret == -1) {
925: rcs_freepdata(pdp);
926: return (-1);
927: }
928: }
929:
930: cvs_log(LP_DEBUG, "RCS file `%s' parsed OK (%u lines)", rfp->rf_path,
1.18 jfb 931: pdp->rp_lines);
1.1 jfb 932:
933: rcs_freepdata(pdp);
934:
935: rfp->rf_pdata = NULL;
1.26 ! jfb 936: rfp->rf_flags |= RCS_PARSED | RCS_SYNCED;
1.1 jfb 937:
938: return (0);
939: }
940:
941:
942: /*
943: * rcs_parse_admin()
944: *
945: * Parse the administrative portion of an RCS file.
946: * Returns 0 on success, or -1 on failure.
947: */
948: static int
949: rcs_parse_admin(RCSFILE *rfp)
950: {
951: u_int i;
952: int tok, ntok, hmask;
953: struct rcs_key *rk;
954:
955: /* hmask is a mask of the headers already encountered */
956: hmask = 0;
957: for (;;) {
958: tok = rcs_gettok(rfp);
959: if (tok == RCS_TOK_ERR) {
960: cvs_log(LP_ERR, "parse error in RCS admin section");
961: return (-1);
1.14 deraadt 962: } else if (tok == RCS_TOK_NUM) {
1.1 jfb 963: /* assume this is the start of the first delta */
964: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
965: return (0);
966: }
967:
968: rk = NULL;
1.18 jfb 969: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 970: if (rcs_keys[i].rk_id == tok)
971: rk = &(rcs_keys[i]);
972:
973: if (hmask & (1 << tok)) {
974: cvs_log(LP_ERR, "duplicate RCS key");
975: return (-1);
976: }
977: hmask |= (1 << tok);
978:
979: switch (tok) {
980: case RCS_TOK_HEAD:
981: case RCS_TOK_BRANCH:
982: case RCS_TOK_COMMENT:
983: case RCS_TOK_EXPAND:
984: ntok = rcs_gettok(rfp);
985: if (ntok == RCS_TOK_SCOLON)
986: break;
987: if (ntok != rk->rk_val) {
988: cvs_log(LP_ERR,
989: "invalid value type for RCS key `%s'",
990: rk->rk_str);
991: }
992:
993: if (tok == RCS_TOK_HEAD) {
994: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
995: rfp->rf_head);
1.14 deraadt 996: } else if (tok == RCS_TOK_BRANCH) {
1.1 jfb 997: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
998: rfp->rf_branch);
1.14 deraadt 999: } else if (tok == RCS_TOK_COMMENT) {
1.1 jfb 1000: rfp->rf_comment = strdup(RCS_TOKSTR(rfp));
1.10 joris 1001: if (rfp->rf_comment == NULL) {
1002: cvs_log(LP_ERRNO,
1003: "failed to duplicate rcs token");
1004: return (-1);
1005: }
1.14 deraadt 1006: } else if (tok == RCS_TOK_EXPAND) {
1.1 jfb 1007: rfp->rf_expand = strdup(RCS_TOKSTR(rfp));
1.10 joris 1008: if (rfp->rf_expand == NULL) {
1009: cvs_log(LP_ERRNO,
1010: "failed to duplicate rcs token");
1011: return (-1);
1012: }
1.1 jfb 1013: }
1014:
1015: /* now get the expected semi-colon */
1016: ntok = rcs_gettok(rfp);
1017: if (ntok != RCS_TOK_SCOLON) {
1018: cvs_log(LP_ERR,
1019: "missing semi-colon after RCS `%s' key",
1.26 ! jfb 1020: rk->rk_str);
1.1 jfb 1021: return (-1);
1022: }
1023: break;
1024: case RCS_TOK_ACCESS:
1025: rcs_parse_access(rfp);
1026: break;
1027: case RCS_TOK_SYMBOLS:
1028: rcs_parse_symbols(rfp);
1029: break;
1030: case RCS_TOK_LOCKS:
1031: rcs_parse_locks(rfp);
1032: break;
1033: default:
1034: cvs_log(LP_ERR,
1035: "unexpected token `%s' in RCS admin section",
1036: RCS_TOKSTR(rfp));
1037: return (-1);
1038: }
1039: }
1040:
1041: return (0);
1042: }
1043:
1044:
1045: /*
1046: * rcs_parse_delta()
1047: *
1048: * Parse an RCS delta section and allocate the structure to store that delta's
1049: * information in the <rfp> delta list.
1050: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1051: * -1 on error.
1052: */
1053: static int
1054: rcs_parse_delta(RCSFILE *rfp)
1055: {
1056: int ret, tok, ntok, hmask;
1057: u_int i;
1058: char *tokstr;
1.3 vincent 1059: RCSNUM *datenum;
1.1 jfb 1060: struct rcs_delta *rdp;
1061: struct rcs_key *rk;
1062:
1063: rdp = (struct rcs_delta *)malloc(sizeof(*rdp));
1064: if (rdp == NULL) {
1065: cvs_log(LP_ERRNO, "failed to allocate RCS delta structure");
1066: return (-1);
1067: }
1068: memset(rdp, 0, sizeof(*rdp));
1069:
1070: rdp->rd_num = rcsnum_alloc();
1.11 joris 1071: if (rdp->rd_num == NULL) {
1072: rcs_freedelta(rdp);
1073: return (-1);
1074: }
1.1 jfb 1075: rdp->rd_next = rcsnum_alloc();
1.11 joris 1076: if (rdp->rd_next == NULL) {
1077: rcs_freedelta(rdp);
1078: return (-1);
1079: }
1.1 jfb 1080:
1081: TAILQ_INIT(&(rdp->rd_branches));
1082:
1083: tok = rcs_gettok(rfp);
1084: if (tok != RCS_TOK_NUM) {
1085: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1086: RCS_TOKSTR(rfp));
1087: rcs_freedelta(rdp);
1088: return (-1);
1089: }
1090: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1091:
1092: hmask = 0;
1093: ret = 0;
1094: tokstr = NULL;
1095:
1096: for (;;) {
1097: tok = rcs_gettok(rfp);
1098: if (tok == RCS_TOK_ERR) {
1099: cvs_log(LP_ERR, "parse error in RCS delta section");
1100: rcs_freedelta(rdp);
1101: return (-1);
1.14 deraadt 1102: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1103: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1104: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1105: break;
1106: }
1107:
1108: rk = NULL;
1.18 jfb 1109: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1110: if (rcs_keys[i].rk_id == tok)
1111: rk = &(rcs_keys[i]);
1112:
1113: if (hmask & (1 << tok)) {
1114: cvs_log(LP_ERR, "duplicate RCS key");
1115: rcs_freedelta(rdp);
1116: return (-1);
1117: }
1118: hmask |= (1 << tok);
1119:
1120: switch (tok) {
1121: case RCS_TOK_DATE:
1122: case RCS_TOK_AUTHOR:
1123: case RCS_TOK_STATE:
1124: case RCS_TOK_NEXT:
1125: ntok = rcs_gettok(rfp);
1126: if (ntok == RCS_TOK_SCOLON) {
1127: if (rk->rk_flags & RCS_VOPT)
1128: break;
1129: else {
1130: cvs_log(LP_ERR, "missing mandatory "
1131: "value to RCS key `%s'",
1132: rk->rk_str);
1133: rcs_freedelta(rdp);
1134: return (-1);
1135: }
1136: }
1137:
1138: if (ntok != rk->rk_val) {
1139: cvs_log(LP_ERR,
1140: "invalid value type for RCS key `%s'",
1141: rk->rk_str);
1142: rcs_freedelta(rdp);
1143: return (-1);
1144: }
1145:
1146: if (tokstr != NULL)
1147: free(tokstr);
1148: tokstr = strdup(RCS_TOKSTR(rfp));
1.10 joris 1149: if (tokstr == NULL) {
1.15 tedu 1150: cvs_log(LP_ERRNO,
1.10 joris 1151: "failed to duplicate rcs token");
1152: rcs_freedelta(rdp);
1153: return (-1);
1154: }
1.1 jfb 1155:
1156: /* now get the expected semi-colon */
1157: ntok = rcs_gettok(rfp);
1158: if (ntok != RCS_TOK_SCOLON) {
1159: cvs_log(LP_ERR,
1160: "missing semi-colon after RCS `%s' key",
1.26 ! jfb 1161: rk->rk_str);
1.1 jfb 1162: rcs_freedelta(rdp);
1163: return (-1);
1164: }
1165:
1166: if (tok == RCS_TOK_DATE) {
1.25 jfb 1167: if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1.11 joris 1168: rcs_freedelta(rdp);
1169: return (-1);
1170: }
1.3 vincent 1171: if (datenum->rn_len != 6) {
1.1 jfb 1172: cvs_log(LP_ERR,
1173: "RCS date specification has %s "
1174: "fields",
1.3 vincent 1175: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 1176: "missing");
1177: rcs_freedelta(rdp);
1178: }
1.3 vincent 1179: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 1180: if (rdp->rd_date.tm_year >= 1900)
1181: rdp->rd_date.tm_year -= 1900;
1.3 vincent 1182: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1183: rdp->rd_date.tm_mday = datenum->rn_id[2];
1184: rdp->rd_date.tm_hour = datenum->rn_id[3];
1185: rdp->rd_date.tm_min = datenum->rn_id[4];
1186: rdp->rd_date.tm_sec = datenum->rn_id[5];
1187: rcsnum_free(datenum);
1.14 deraadt 1188: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 1189: rdp->rd_author = tokstr;
1190: tokstr = NULL;
1.14 deraadt 1191: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 1192: rdp->rd_state = tokstr;
1193: tokstr = NULL;
1.14 deraadt 1194: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 1195: rcsnum_aton(tokstr, NULL, rdp->rd_next);
1196: }
1197: break;
1198: case RCS_TOK_BRANCHES:
1199: rcs_parse_branches(rfp, rdp);
1200: break;
1201: default:
1202: cvs_log(LP_ERR,
1203: "unexpected token `%s' in RCS delta",
1204: RCS_TOKSTR(rfp));
1205: rcs_freedelta(rdp);
1206: return (-1);
1207: }
1208: }
1209:
1.13 jfb 1210: if (tokstr != NULL)
1211: free(tokstr);
1212:
1.1 jfb 1213: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1.26 ! jfb 1214: rfp->rf_ndelta++;
1.1 jfb 1215:
1216: return (ret);
1217: }
1218:
1219:
1220: /*
1221: * rcs_parse_deltatext()
1222: *
1223: * Parse an RCS delta text section and fill in the log and text field of the
1224: * appropriate delta section.
1225: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1226: * -1 on error.
1227: */
1228: static int
1229: rcs_parse_deltatext(RCSFILE *rfp)
1230: {
1231: int tok;
1232: RCSNUM *tnum;
1233: struct rcs_delta *rdp;
1234:
1235: tok = rcs_gettok(rfp);
1236: if (tok == RCS_TOK_EOF)
1237: return (0);
1238:
1239: if (tok != RCS_TOK_NUM) {
1240: cvs_log(LP_ERR,
1241: "unexpected token `%s' at start of RCS delta text",
1242: RCS_TOKSTR(rfp));
1243: return (-1);
1244: }
1.13 jfb 1245:
1246: tnum = rcsnum_alloc();
1247: if (tnum == NULL)
1248: return (-1);
1.1 jfb 1249: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1250:
1251: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1252: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1253: break;
1254: }
1.13 jfb 1255: rcsnum_free(tnum);
1256:
1.1 jfb 1257: if (rdp == NULL) {
1258: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1259: RCS_TOKSTR(rfp));
1260: return (-1);
1261: }
1262:
1263: tok = rcs_gettok(rfp);
1264: if (tok != RCS_TOK_LOG) {
1265: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1266: RCS_TOKSTR(rfp));
1267: return (-1);
1268: }
1269:
1270: tok = rcs_gettok(rfp);
1271: if (tok != RCS_TOK_STRING) {
1272: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1273: RCS_TOKSTR(rfp));
1274: return (-1);
1275: }
1276: rdp->rd_log = strdup(RCS_TOKSTR(rfp));
1277: if (rdp->rd_log == NULL) {
1278: cvs_log(LP_ERRNO, "failed to copy RCS deltatext log");
1279: return (-1);
1280: }
1281:
1282: tok = rcs_gettok(rfp);
1283: if (tok != RCS_TOK_TEXT) {
1284: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1285: RCS_TOKSTR(rfp));
1286: return (-1);
1287: }
1288:
1289: tok = rcs_gettok(rfp);
1290: if (tok != RCS_TOK_STRING) {
1291: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1292: RCS_TOKSTR(rfp));
1293: return (-1);
1294: }
1295:
1296: rdp->rd_text = strdup(RCS_TOKSTR(rfp));
1297: if (rdp->rd_text == NULL) {
1298: cvs_log(LP_ERRNO, "failed to copy RCS delta text");
1299: return (-1);
1300: }
1301:
1302: return (1);
1303: }
1304:
1305:
1306: /*
1307: * rcs_parse_access()
1308: *
1309: * Parse the access list given as value to the `access' keyword.
1310: * Returns 0 on success, or -1 on failure.
1311: */
1312: static int
1313: rcs_parse_access(RCSFILE *rfp)
1314: {
1315: int type;
1316:
1317: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
1318: if (type != RCS_TOK_ID) {
1319: cvs_log(LP_ERR, "unexpected token `%s' in access list",
1320: RCS_TOKSTR(rfp));
1321: return (-1);
1322: }
1323: }
1324:
1325: return (0);
1326: }
1327:
1328:
1329: /*
1330: * rcs_parse_symbols()
1331: *
1332: * Parse the symbol list given as value to the `symbols' keyword.
1333: * Returns 0 on success, or -1 on failure.
1334: */
1335: static int
1336: rcs_parse_symbols(RCSFILE *rfp)
1337: {
1338: int type;
1339: struct rcs_sym *symp;
1340:
1341: for (;;) {
1342: type = rcs_gettok(rfp);
1343: if (type == RCS_TOK_SCOLON)
1344: break;
1345:
1346: if (type != RCS_TOK_STRING) {
1347: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1348: RCS_TOKSTR(rfp));
1349: return (-1);
1350: }
1351:
1352: symp = (struct rcs_sym *)malloc(sizeof(*symp));
1353: if (symp == NULL) {
1354: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
1355: return (-1);
1356: }
1357: symp->rs_name = strdup(RCS_TOKSTR(rfp));
1.10 joris 1358: if (symp->rs_name == NULL) {
1359: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
1360: free(symp);
1361: return (-1);
1362: }
1363:
1.1 jfb 1364: symp->rs_num = rcsnum_alloc();
1.11 joris 1365: if (symp->rs_num == NULL) {
1366: cvs_log(LP_ERRNO, "failed to allocate rcsnum info");
1367: free(symp);
1368: return (-1);
1369: }
1.1 jfb 1370:
1371: type = rcs_gettok(rfp);
1372: if (type != RCS_TOK_COLON) {
1373: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1374: RCS_TOKSTR(rfp));
1.11 joris 1375: rcsnum_free(symp->rs_num);
1.1 jfb 1376: free(symp->rs_name);
1377: free(symp);
1378: return (-1);
1379: }
1380:
1381: type = rcs_gettok(rfp);
1382: if (type != RCS_TOK_NUM) {
1383: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1384: RCS_TOKSTR(rfp));
1.11 joris 1385: rcsnum_free(symp->rs_num);
1.1 jfb 1386: free(symp->rs_name);
1387: free(symp);
1388: return (-1);
1389: }
1390:
1391: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
1392: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1393: RCS_TOKSTR(rfp));
1.11 joris 1394: rcsnum_free(symp->rs_num);
1.1 jfb 1395: free(symp->rs_name);
1396: free(symp);
1397: return (-1);
1398: }
1399:
1400: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
1401: }
1402:
1403: return (0);
1404: }
1405:
1406:
1407: /*
1408: * rcs_parse_locks()
1409: *
1410: * Parse the lock list given as value to the `locks' keyword.
1411: * Returns 0 on success, or -1 on failure.
1412: */
1413: static int
1414: rcs_parse_locks(RCSFILE *rfp)
1415: {
1416: int type;
1417: struct rcs_lock *lkp;
1418:
1419: for (;;) {
1420: type = rcs_gettok(rfp);
1421: if (type == RCS_TOK_SCOLON)
1422: break;
1423:
1424: if (type != RCS_TOK_ID) {
1425: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
1426: RCS_TOKSTR(rfp));
1427: return (-1);
1428: }
1429:
1430: lkp = (struct rcs_lock *)malloc(sizeof(*lkp));
1431: if (lkp == NULL) {
1432: cvs_log(LP_ERRNO, "failed to allocate RCS lock");
1433: return (-1);
1434: }
1435: lkp->rl_num = rcsnum_alloc();
1.11 joris 1436: if (lkp->rl_num == NULL) {
1437: free(lkp);
1438: return (-1);
1439: }
1.1 jfb 1440:
1441: type = rcs_gettok(rfp);
1442: if (type != RCS_TOK_COLON) {
1443: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1444: RCS_TOKSTR(rfp));
1445: free(lkp);
1446: return (-1);
1447: }
1448:
1449: type = rcs_gettok(rfp);
1450: if (type != RCS_TOK_NUM) {
1451: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1452: RCS_TOKSTR(rfp));
1453: free(lkp);
1454: return (-1);
1455: }
1456:
1457: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
1458: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1459: RCS_TOKSTR(rfp));
1460: free(lkp);
1461: return (-1);
1462: }
1463:
1464: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
1465: }
1466:
1467: /* check if we have a `strict' */
1468: type = rcs_gettok(rfp);
1469: if (type != RCS_TOK_STRICT) {
1470: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 1471: } else {
1.26 ! jfb 1472: rfp->rf_flags |= RCS_SLOCK;
1.1 jfb 1473:
1474: type = rcs_gettok(rfp);
1475: if (type != RCS_TOK_SCOLON) {
1476: cvs_log(LP_ERR,
1477: "missing semi-colon after `strict' keyword");
1478: return (-1);
1479: }
1480: }
1481:
1482: return (0);
1483: }
1484:
1485: /*
1486: * rcs_parse_branches()
1487: *
1488: * Parse the list of branches following a `branches' keyword in a delta.
1489: * Returns 0 on success, or -1 on failure.
1490: */
1491: static int
1492: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
1493: {
1494: int type;
1495: struct rcs_branch *brp;
1496:
1497: for (;;) {
1498: type = rcs_gettok(rfp);
1499: if (type == RCS_TOK_SCOLON)
1500: break;
1501:
1502: if (type != RCS_TOK_NUM) {
1503: cvs_log(LP_ERR,
1504: "unexpected token `%s' in list of branches",
1505: RCS_TOKSTR(rfp));
1506: return (-1);
1507: }
1508:
1509: brp = (struct rcs_branch *)malloc(sizeof(*brp));
1510: if (brp == NULL) {
1511: cvs_log(LP_ERRNO, "failed to allocate RCS branch");
1512: return (-1);
1513: }
1514: brp->rb_num = rcsnum_alloc();
1.11 joris 1515: if (brp->rb_num == NULL) {
1516: free(brp);
1517: return (-1);
1518: }
1519:
1.1 jfb 1520: rcsnum_aton(RCS_TOKSTR(rfp), NULL, brp->rb_num);
1521:
1522: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
1523: }
1524:
1525: return (0);
1526: }
1527:
1528:
1529: /*
1530: * rcs_freedelta()
1531: *
1532: * Free the contents of a delta structure.
1533: */
1.18 jfb 1534: static void
1.1 jfb 1535: rcs_freedelta(struct rcs_delta *rdp)
1536: {
1.12 jfb 1537: struct rcs_branch *rb;
1.1 jfb 1538: struct rcs_delta *crdp;
1539:
1.12 jfb 1540: if (rdp->rd_num != NULL)
1541: rcsnum_free(rdp->rd_num);
1542: if (rdp->rd_next != NULL)
1543: rcsnum_free(rdp->rd_next);
1544:
1.1 jfb 1545: if (rdp->rd_author != NULL)
1546: free(rdp->rd_author);
1547: if (rdp->rd_state != NULL)
1548: free(rdp->rd_state);
1549: if (rdp->rd_log != NULL)
1550: free(rdp->rd_log);
1551: if (rdp->rd_text != NULL)
1552: free(rdp->rd_text);
1.12 jfb 1553:
1554: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1555: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
1556: rcsnum_free(rb->rb_num);
1557: free(rb);
1558: }
1.1 jfb 1559:
1560: while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) {
1561: TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list);
1562: rcs_freedelta(crdp);
1563: }
1564:
1565: free(rdp);
1566: }
1567:
1568:
1569: /*
1570: * rcs_freepdata()
1571: *
1572: * Free the contents of the parser data structure.
1573: */
1574: static void
1575: rcs_freepdata(struct rcs_pdata *pd)
1576: {
1577: if (pd->rp_file != NULL)
1578: (void)fclose(pd->rp_file);
1579: if (pd->rp_buf != NULL)
1580: free(pd->rp_buf);
1581: free(pd);
1582: }
1583:
1584:
1585: /*
1586: * rcs_gettok()
1587: *
1588: * Get the next RCS token from the string <str>.
1589: */
1590: static int
1591: rcs_gettok(RCSFILE *rfp)
1592: {
1593: u_int i;
1594: int ch, last, type;
1.18 jfb 1595: size_t len;
1596: char *bp;
1.1 jfb 1597: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1598:
1599: type = RCS_TOK_ERR;
1600: bp = pdp->rp_buf;
1601: *bp = '\0';
1602:
1603: if (pdp->rp_pttype != RCS_TOK_ERR) {
1604: type = pdp->rp_pttype;
1605: strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen);
1606: pdp->rp_pttype = RCS_TOK_ERR;
1607: return (type);
1608: }
1609:
1610: /* skip leading whitespace */
1611: /* XXX we must skip backspace too for compatibility, should we? */
1612: do {
1613: ch = getc(pdp->rp_file);
1614: if (ch == '\n')
1.18 jfb 1615: pdp->rp_lines++;
1.1 jfb 1616: } while (isspace(ch));
1617:
1618: if (ch == EOF) {
1619: type = RCS_TOK_EOF;
1.14 deraadt 1620: } else if (ch == ';') {
1.1 jfb 1621: type = RCS_TOK_SCOLON;
1.14 deraadt 1622: } else if (ch == ':') {
1.1 jfb 1623: type = RCS_TOK_COLON;
1.14 deraadt 1624: } else if (isalpha(ch)) {
1.18 jfb 1625: type = RCS_TOK_STRING;
1.1 jfb 1626: *(bp++) = ch;
1.18 jfb 1627: for (;;) {
1.1 jfb 1628: ch = getc(pdp->rp_file);
1.11 joris 1629: if (!isalnum(ch) && ch != '_' && ch != '-') {
1.1 jfb 1630: ungetc(ch, pdp->rp_file);
1631: break;
1632: }
1633: *(bp++) = ch;
1.18 jfb 1634: if (bp == pdp->rp_bufend - 1) {
1635: len = bp - pdp->rp_buf;
1636: if (rcs_growbuf(rfp) < 0) {
1637: type = RCS_TOK_ERR;
1638: break;
1639: }
1640: bp = pdp->rp_buf + len;
1641: }
1.1 jfb 1642: }
1643: *bp = '\0';
1644:
1.18 jfb 1645: if (type != RCS_TOK_ERR) {
1646: for (i = 0; i < RCS_NKEYS; i++) {
1647: if (strcmp(rcs_keys[i].rk_str,
1648: pdp->rp_buf) == 0) {
1649: type = rcs_keys[i].rk_id;
1650: break;
1651: }
1.1 jfb 1652: }
1653: }
1.14 deraadt 1654: } else if (ch == '@') {
1.1 jfb 1655: /* we have a string */
1.18 jfb 1656: type = RCS_TOK_STRING;
1.1 jfb 1657: for (;;) {
1658: ch = getc(pdp->rp_file);
1659: if (ch == '@') {
1660: ch = getc(pdp->rp_file);
1661: if (ch != '@') {
1662: ungetc(ch, pdp->rp_file);
1663: break;
1664: }
1.14 deraadt 1665: } else if (ch == '\n')
1.18 jfb 1666: pdp->rp_lines++;
1.1 jfb 1667:
1668: *(bp++) = ch;
1.18 jfb 1669: if (bp == pdp->rp_bufend - 1) {
1670: len = bp - pdp->rp_buf;
1671: if (rcs_growbuf(rfp) < 0) {
1672: type = RCS_TOK_ERR;
1673: break;
1674: }
1675: bp = pdp->rp_buf + len;
1676: }
1.1 jfb 1677: }
1678:
1679: *bp = '\0';
1.14 deraadt 1680: } else if (isdigit(ch)) {
1.1 jfb 1681: *(bp++) = ch;
1682: last = ch;
1683: type = RCS_TOK_NUM;
1684:
1685: for (;;) {
1686: ch = getc(pdp->rp_file);
1.18 jfb 1687: if (bp == pdp->rp_bufend)
1.1 jfb 1688: break;
1689: if (!isdigit(ch) && ch != '.') {
1690: ungetc(ch, pdp->rp_file);
1691: break;
1692: }
1693:
1694: if (last == '.' && ch == '.') {
1695: type = RCS_TOK_ERR;
1696: break;
1697: }
1698: last = ch;
1699: *(bp++) = ch;
1700: }
1.18 jfb 1701: *bp = '\0';
1.1 jfb 1702: }
1703:
1704: return (type);
1705: }
1706:
1707:
1708: /*
1709: * rcs_pushtok()
1710: *
1711: * Push a token back in the parser's token buffer.
1712: */
1713: static int
1714: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
1715: {
1716: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1717:
1718: if (pdp->rp_pttype != RCS_TOK_ERR)
1719: return (-1);
1720:
1721: pdp->rp_pttype = type;
1722: strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok));
1723: return (0);
1724: }
1725:
1726:
1727: /*
1728: * rcs_stresc()
1729: *
1730: * Performs either escaping or unescaping of the string stored in <str>.
1731: * The operation is to escape special RCS characters if the <esc> argument
1732: * is 1, or unescape otherwise. The result is stored in the <buf> destination
1733: * buffer, and <blen> must originally point to the size of <buf>.
1734: * Returns the number of bytes which have been read from the source <str> and
1735: * operated on. The <blen> parameter will contain the number of bytes
1736: * actually copied in <buf>.
1737: */
1738: size_t
1739: rcs_stresc(int esc, const char *str, char *buf, size_t *blen)
1740: {
1741: size_t rlen;
1742: const char *sp;
1743: char *bp, *bep;
1744:
1745: rlen = 0;
1746: bp = buf;
1747: bep = buf + *blen - 1;
1748:
1749: for (sp = str; (*sp != '\0') && (bp <= (bep - 1)); sp++) {
1750: if (*sp == '@') {
1751: if (esc) {
1752: if (bp > (bep - 2))
1753: break;
1754: *(bp++) = '@';
1.14 deraadt 1755: } else {
1.1 jfb 1756: sp++;
1757: if (*sp != '@') {
1758: cvs_log(LP_WARN,
1759: "unknown escape character `%c' in "
1760: "RCS file", *sp);
1761: if (*sp == '\0')
1762: break;
1763: }
1764: }
1765: }
1766:
1767: *(bp++) = *sp;
1768: }
1769:
1770: *bp = '\0';
1771: *blen = (bp - buf);
1772: return (sp - str);
1773: }
1774:
1775:
1776: /*
1777: * rcs_splitlines()
1778: *
1779: * Split the contents of a file into a list of lines.
1780: */
1781: static struct rcs_foo*
1782: rcs_splitlines(const char *fcont)
1783: {
1784: char *dcp;
1785: struct rcs_foo *foo;
1786: struct rcs_line *lp;
1787:
1788: foo = (struct rcs_foo *)malloc(sizeof(*foo));
1789: if (foo == NULL) {
1790: cvs_log(LP_ERR, "failed to allocate line structure");
1791: return (NULL);
1792: }
1793: TAILQ_INIT(&(foo->rl_lines));
1794: foo->rl_nblines = 0;
1795: foo->rl_data = strdup(fcont);
1796: if (foo->rl_data == NULL) {
1797: cvs_log(LP_ERRNO, "failed to copy file contents");
1798: free(foo);
1799: return (NULL);
1800: }
1801:
1802: /*
1803: * Add a first bogus line with line number 0. This is used so we
1804: * can position the line pointer before 1 when changing the first line
1805: * in rcs_patch().
1806: */
1807: lp = (struct rcs_line *)malloc(sizeof(*lp));
1.5 vincent 1808: if (lp == NULL)
1.1 jfb 1809: return (NULL);
1.5 vincent 1810:
1.1 jfb 1811: lp->rl_line = NULL;
1812: lp->rl_lineno = 0;
1813: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1814:
1815:
1816: for (dcp = foo->rl_data; *dcp != '\0';) {
1817: lp = (struct rcs_line *)malloc(sizeof(*lp));
1818: if (lp == NULL) {
1819: cvs_log(LP_ERR, "failed to allocate line entry");
1820: return (NULL);
1821: }
1822:
1823: lp->rl_line = dcp;
1824: lp->rl_lineno = ++(foo->rl_nblines);
1825: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1826:
1827: dcp = strchr(dcp, '\n');
1828: if (dcp == NULL) {
1829: break;
1830: }
1831: *(dcp++) = '\0';
1832: }
1833:
1834: return (foo);
1.5 vincent 1835: }
1836:
1837: static void
1838: rcs_freefoo(struct rcs_foo *fp)
1839: {
1840: struct rcs_line *lp;
1841:
1842: while ((lp = TAILQ_FIRST(&fp->rl_lines)) != NULL) {
1843: TAILQ_REMOVE(&fp->rl_lines, lp, rl_list);
1844: free(lp);
1845: }
1846: free(fp->rl_data);
1847: free(fp);
1.18 jfb 1848: }
1849:
1850: /*
1851: * rcs_growbuf()
1852: *
1853: * Attempt to grow the internal parse buffer for the RCS file <rf> by
1854: * RCS_BUFEXTSIZE.
1855: * In case of failure, the original buffer is left unmodified.
1856: * Returns 0 on success, or -1 on failure.
1857: */
1858: static int
1859: rcs_growbuf(RCSFILE *rf)
1860: {
1861: void *tmp;
1862: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
1863:
1864: tmp = realloc(pdp->rp_buf, pdp->rp_blen + RCS_BUFEXTSIZE);
1865: if (tmp == NULL) {
1866: cvs_log(LP_ERRNO, "failed to grow RCS parse buffer");
1867: return (-1);
1868: }
1869:
1870: pdp->rp_buf = (char *)tmp;
1871: pdp->rp_blen += RCS_BUFEXTSIZE;
1872: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1873:
1874: return (0);
1.1 jfb 1875: }