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