Annotation of src/usr.bin/cvs/rcs.c, Revision 1.22
1.22 ! jfb 1: /* $OpenBSD: rcs.c,v 1.21 2005/01/13 20:50:57 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:
1.21 jfb 753: void
754: rcs_kflag_usage(void)
755: {
756: fprintf(stderr, "Valid expansion modes include:\n"
1.22 ! jfb 757: "\t-kkv\tGenerate keywords using the default form.\n"
! 758: "\t-kkvl\tLike -kkv, except locker's name inserted.\n"
! 759: "\t-kk\tGenerate only keyword names in keyword strings.\n"
! 760: "\t-kv\tGenerate only keyword values in keyword strings.\n"
! 761: "\t-ko\tGenerate old keyword string "
1.21 jfb 762: "(no changes from checked in file).\n"
1.22 ! jfb 763: "\t-kb\tGenerate binary file unmodified (merges not allowed).\n");
1.21 jfb 764: }
1.1 jfb 765:
766: /*
767: * rcs_parse()
768: *
769: * Parse the contents of file <path>, which are in the RCS format.
770: * Returns 0 on success, or -1 on failure.
771: */
772:
773: int
774: rcs_parse(RCSFILE *rfp)
775: {
776: int ret;
777: struct rcs_pdata *pdp;
778:
779: if (rfp->rf_flags & RCS_RF_PARSED)
780: return (0);
781:
782: pdp = (struct rcs_pdata *)malloc(sizeof(*pdp));
783: if (pdp == NULL) {
784: cvs_log(LP_ERRNO, "failed to allocate RCS parser data");
785: return (-1);
786: }
787: memset(pdp, 0, sizeof(*pdp));
788:
1.18 jfb 789: pdp->rp_lines = 0;
1.1 jfb 790: pdp->rp_pttype = RCS_TOK_ERR;
791:
792: pdp->rp_file = fopen(rfp->rf_path, "r");
793: if (pdp->rp_file == NULL) {
794: cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path);
795: rcs_freepdata(pdp);
796: return (-1);
797: }
798:
799: pdp->rp_buf = (char *)malloc(RCS_BUFSIZE);
800: if (pdp->rp_buf == NULL) {
801: cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer");
802: rcs_freepdata(pdp);
803: return (-1);
804: }
805: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 806: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 807:
808: /* ditch the strict lock */
809: rfp->rf_flags &= ~RCS_RF_SLOCK;
810: rfp->rf_pdata = pdp;
811:
812: if (rcs_parse_admin(rfp) < 0) {
813: rcs_freepdata(pdp);
814: return (-1);
815: }
816:
817: for (;;) {
818: ret = rcs_parse_delta(rfp);
819: if (ret == 0)
820: break;
821: else if (ret == -1) {
822: rcs_freepdata(pdp);
823: return (-1);
824: }
825: }
826:
827: ret = rcs_gettok(rfp);
828: if (ret != RCS_TOK_DESC) {
829: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
830: RCS_TOKSTR(rfp));
831: rcs_freepdata(pdp);
832: return (-1);
833: }
834:
835: ret = rcs_gettok(rfp);
836: if (ret != RCS_TOK_STRING) {
837: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
838: RCS_TOKSTR(rfp));
839: rcs_freepdata(pdp);
840: return (-1);
841: }
842:
843: rfp->rf_desc = strdup(RCS_TOKSTR(rfp));
1.10 joris 844: if (rfp->rf_desc == NULL) {
845: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
846: rcs_freepdata(pdp);
847: return (-1);
848: }
1.1 jfb 849:
850: for (;;) {
851: ret = rcs_parse_deltatext(rfp);
852: if (ret == 0)
853: break;
854: else if (ret == -1) {
855: rcs_freepdata(pdp);
856: return (-1);
857: }
858: }
859:
860: cvs_log(LP_DEBUG, "RCS file `%s' parsed OK (%u lines)", rfp->rf_path,
1.18 jfb 861: pdp->rp_lines);
1.1 jfb 862:
863: rcs_freepdata(pdp);
864:
865: rfp->rf_pdata = NULL;
866: rfp->rf_flags |= RCS_RF_PARSED|RCS_RF_SYNCED;
867:
868: return (0);
869: }
870:
871:
872: /*
873: * rcs_parse_admin()
874: *
875: * Parse the administrative portion of an RCS file.
876: * Returns 0 on success, or -1 on failure.
877: */
878:
879: static int
880: rcs_parse_admin(RCSFILE *rfp)
881: {
882: u_int i;
883: int tok, ntok, hmask;
884: struct rcs_key *rk;
885:
886: /* hmask is a mask of the headers already encountered */
887: hmask = 0;
888: for (;;) {
889: tok = rcs_gettok(rfp);
890: if (tok == RCS_TOK_ERR) {
891: cvs_log(LP_ERR, "parse error in RCS admin section");
892: return (-1);
1.14 deraadt 893: } else if (tok == RCS_TOK_NUM) {
1.1 jfb 894: /* assume this is the start of the first delta */
895: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
896: return (0);
897: }
898:
899: rk = NULL;
1.18 jfb 900: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 901: if (rcs_keys[i].rk_id == tok)
902: rk = &(rcs_keys[i]);
903:
904: if (hmask & (1 << tok)) {
905: cvs_log(LP_ERR, "duplicate RCS key");
906: return (-1);
907: }
908: hmask |= (1 << tok);
909:
910: switch (tok) {
911: case RCS_TOK_HEAD:
912: case RCS_TOK_BRANCH:
913: case RCS_TOK_COMMENT:
914: case RCS_TOK_EXPAND:
915: ntok = rcs_gettok(rfp);
916: if (ntok == RCS_TOK_SCOLON)
917: break;
918: if (ntok != rk->rk_val) {
919: cvs_log(LP_ERR,
920: "invalid value type for RCS key `%s'",
921: rk->rk_str);
922: }
923:
924: if (tok == RCS_TOK_HEAD) {
925: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
926: rfp->rf_head);
1.14 deraadt 927: } else if (tok == RCS_TOK_BRANCH) {
1.1 jfb 928: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
929: rfp->rf_branch);
1.14 deraadt 930: } else if (tok == RCS_TOK_COMMENT) {
1.1 jfb 931: rfp->rf_comment = strdup(RCS_TOKSTR(rfp));
1.10 joris 932: if (rfp->rf_comment == NULL) {
933: cvs_log(LP_ERRNO,
934: "failed to duplicate rcs token");
935: return (-1);
936: }
1.14 deraadt 937: } else if (tok == RCS_TOK_EXPAND) {
1.1 jfb 938: rfp->rf_expand = strdup(RCS_TOKSTR(rfp));
1.10 joris 939: if (rfp->rf_expand == NULL) {
940: cvs_log(LP_ERRNO,
941: "failed to duplicate rcs token");
942: return (-1);
943: }
1.1 jfb 944: }
945:
946: /* now get the expected semi-colon */
947: ntok = rcs_gettok(rfp);
948: if (ntok != RCS_TOK_SCOLON) {
949: cvs_log(LP_ERR,
950: "missing semi-colon after RCS `%s' key",
951: rk->rk_str);
952: return (-1);
953: }
954: break;
955: case RCS_TOK_ACCESS:
956: rcs_parse_access(rfp);
957: break;
958: case RCS_TOK_SYMBOLS:
959: rcs_parse_symbols(rfp);
960: break;
961: case RCS_TOK_LOCKS:
962: rcs_parse_locks(rfp);
963: break;
964: default:
965: cvs_log(LP_ERR,
966: "unexpected token `%s' in RCS admin section",
967: RCS_TOKSTR(rfp));
968: return (-1);
969: }
970: }
971:
972: return (0);
973: }
974:
975:
976: /*
977: * rcs_parse_delta()
978: *
979: * Parse an RCS delta section and allocate the structure to store that delta's
980: * information in the <rfp> delta list.
981: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
982: * -1 on error.
983: */
984:
985: static int
986: rcs_parse_delta(RCSFILE *rfp)
987: {
988: int ret, tok, ntok, hmask;
989: u_int i;
990: char *tokstr;
1.3 vincent 991: RCSNUM *datenum;
1.1 jfb 992: struct rcs_delta *rdp;
993: struct rcs_key *rk;
994:
995: rdp = (struct rcs_delta *)malloc(sizeof(*rdp));
996: if (rdp == NULL) {
997: cvs_log(LP_ERRNO, "failed to allocate RCS delta structure");
998: return (-1);
999: }
1000: memset(rdp, 0, sizeof(*rdp));
1001:
1002: rdp->rd_num = rcsnum_alloc();
1.11 joris 1003: if (rdp->rd_num == NULL) {
1004: rcs_freedelta(rdp);
1005: return (-1);
1006: }
1.1 jfb 1007: rdp->rd_next = rcsnum_alloc();
1.11 joris 1008: if (rdp->rd_next == NULL) {
1009: rcs_freedelta(rdp);
1010: return (-1);
1011: }
1.1 jfb 1012:
1013: TAILQ_INIT(&(rdp->rd_branches));
1014:
1015: tok = rcs_gettok(rfp);
1016: if (tok != RCS_TOK_NUM) {
1017: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1018: RCS_TOKSTR(rfp));
1019: rcs_freedelta(rdp);
1020: return (-1);
1021: }
1022: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1023:
1024: hmask = 0;
1025: ret = 0;
1026: tokstr = NULL;
1027:
1028: for (;;) {
1029: tok = rcs_gettok(rfp);
1030: if (tok == RCS_TOK_ERR) {
1031: cvs_log(LP_ERR, "parse error in RCS delta section");
1032: rcs_freedelta(rdp);
1033: return (-1);
1.14 deraadt 1034: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1035: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1036: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1037: break;
1038: }
1039:
1040: rk = NULL;
1.18 jfb 1041: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1042: if (rcs_keys[i].rk_id == tok)
1043: rk = &(rcs_keys[i]);
1044:
1045: if (hmask & (1 << tok)) {
1046: cvs_log(LP_ERR, "duplicate RCS key");
1047: rcs_freedelta(rdp);
1048: return (-1);
1049: }
1050: hmask |= (1 << tok);
1051:
1052: switch (tok) {
1053: case RCS_TOK_DATE:
1054: case RCS_TOK_AUTHOR:
1055: case RCS_TOK_STATE:
1056: case RCS_TOK_NEXT:
1057: ntok = rcs_gettok(rfp);
1058: if (ntok == RCS_TOK_SCOLON) {
1059: if (rk->rk_flags & RCS_VOPT)
1060: break;
1061: else {
1062: cvs_log(LP_ERR, "missing mandatory "
1063: "value to RCS key `%s'",
1064: rk->rk_str);
1065: rcs_freedelta(rdp);
1066: return (-1);
1067: }
1068: }
1069:
1070: if (ntok != rk->rk_val) {
1071: cvs_log(LP_ERR,
1072: "invalid value type for RCS key `%s'",
1073: rk->rk_str);
1074: rcs_freedelta(rdp);
1075: return (-1);
1076: }
1077:
1078: if (tokstr != NULL)
1079: free(tokstr);
1080: tokstr = strdup(RCS_TOKSTR(rfp));
1.10 joris 1081: if (tokstr == NULL) {
1.15 tedu 1082: cvs_log(LP_ERRNO,
1.10 joris 1083: "failed to duplicate rcs token");
1084: rcs_freedelta(rdp);
1085: return (-1);
1086: }
1.1 jfb 1087:
1088: /* now get the expected semi-colon */
1089: ntok = rcs_gettok(rfp);
1090: if (ntok != RCS_TOK_SCOLON) {
1091: cvs_log(LP_ERR,
1092: "missing semi-colon after RCS `%s' key",
1093: rk->rk_str);
1094: rcs_freedelta(rdp);
1095: return (-1);
1096: }
1097:
1098: if (tok == RCS_TOK_DATE) {
1.3 vincent 1099: datenum = rcsnum_alloc();
1.11 joris 1100: if (datenum == NULL) {
1101: rcs_freedelta(rdp);
1102: return (-1);
1103: }
1.3 vincent 1104: rcsnum_aton(tokstr, NULL, datenum);
1105: if (datenum->rn_len != 6) {
1.1 jfb 1106: cvs_log(LP_ERR,
1107: "RCS date specification has %s "
1108: "fields",
1.3 vincent 1109: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 1110: "missing");
1111: rcs_freedelta(rdp);
1112: }
1.3 vincent 1113: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 1114: if (rdp->rd_date.tm_year >= 1900)
1115: rdp->rd_date.tm_year -= 1900;
1.3 vincent 1116: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1117: rdp->rd_date.tm_mday = datenum->rn_id[2];
1118: rdp->rd_date.tm_hour = datenum->rn_id[3];
1119: rdp->rd_date.tm_min = datenum->rn_id[4];
1120: rdp->rd_date.tm_sec = datenum->rn_id[5];
1121: rcsnum_free(datenum);
1.14 deraadt 1122: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 1123: rdp->rd_author = tokstr;
1124: tokstr = NULL;
1.14 deraadt 1125: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 1126: rdp->rd_state = tokstr;
1127: tokstr = NULL;
1.14 deraadt 1128: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 1129: rcsnum_aton(tokstr, NULL, rdp->rd_next);
1130: }
1131: break;
1132: case RCS_TOK_BRANCHES:
1133: rcs_parse_branches(rfp, rdp);
1134: break;
1135: default:
1136: cvs_log(LP_ERR,
1137: "unexpected token `%s' in RCS delta",
1138: RCS_TOKSTR(rfp));
1139: rcs_freedelta(rdp);
1140: return (-1);
1141: }
1142: }
1143:
1.13 jfb 1144: if (tokstr != NULL)
1145: free(tokstr);
1146:
1.1 jfb 1147: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1148:
1149: return (ret);
1150: }
1151:
1152:
1153: /*
1154: * rcs_parse_deltatext()
1155: *
1156: * Parse an RCS delta text section and fill in the log and text field of the
1157: * appropriate delta section.
1158: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1159: * -1 on error.
1160: */
1161:
1162: static int
1163: rcs_parse_deltatext(RCSFILE *rfp)
1164: {
1165: int tok;
1166: RCSNUM *tnum;
1167: struct rcs_delta *rdp;
1168:
1169: tok = rcs_gettok(rfp);
1170: if (tok == RCS_TOK_EOF)
1171: return (0);
1172:
1173: if (tok != RCS_TOK_NUM) {
1174: cvs_log(LP_ERR,
1175: "unexpected token `%s' at start of RCS delta text",
1176: RCS_TOKSTR(rfp));
1177: return (-1);
1178: }
1.13 jfb 1179:
1180: tnum = rcsnum_alloc();
1181: if (tnum == NULL)
1182: return (-1);
1.1 jfb 1183: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1184:
1185: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1186: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1187: break;
1188: }
1.13 jfb 1189: rcsnum_free(tnum);
1190:
1.1 jfb 1191: if (rdp == NULL) {
1192: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1193: RCS_TOKSTR(rfp));
1194: return (-1);
1195: }
1196:
1197: tok = rcs_gettok(rfp);
1198: if (tok != RCS_TOK_LOG) {
1199: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1200: RCS_TOKSTR(rfp));
1201: return (-1);
1202: }
1203:
1204: tok = rcs_gettok(rfp);
1205: if (tok != RCS_TOK_STRING) {
1206: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1207: RCS_TOKSTR(rfp));
1208: return (-1);
1209: }
1210: rdp->rd_log = strdup(RCS_TOKSTR(rfp));
1211: if (rdp->rd_log == NULL) {
1212: cvs_log(LP_ERRNO, "failed to copy RCS deltatext log");
1213: return (-1);
1214: }
1215:
1216: tok = rcs_gettok(rfp);
1217: if (tok != RCS_TOK_TEXT) {
1218: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1219: RCS_TOKSTR(rfp));
1220: return (-1);
1221: }
1222:
1223: tok = rcs_gettok(rfp);
1224: if (tok != RCS_TOK_STRING) {
1225: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1226: RCS_TOKSTR(rfp));
1227: return (-1);
1228: }
1229:
1230: rdp->rd_text = strdup(RCS_TOKSTR(rfp));
1231: if (rdp->rd_text == NULL) {
1232: cvs_log(LP_ERRNO, "failed to copy RCS delta text");
1233: return (-1);
1234: }
1235:
1236: return (1);
1237: }
1238:
1239:
1240: /*
1241: * rcs_parse_access()
1242: *
1243: * Parse the access list given as value to the `access' keyword.
1244: * Returns 0 on success, or -1 on failure.
1245: */
1246:
1247: static int
1248: rcs_parse_access(RCSFILE *rfp)
1249: {
1250: int type;
1251:
1252: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
1253: if (type != RCS_TOK_ID) {
1254: cvs_log(LP_ERR, "unexpected token `%s' in access list",
1255: RCS_TOKSTR(rfp));
1256: return (-1);
1257: }
1258: }
1259:
1260: return (0);
1261: }
1262:
1263:
1264: /*
1265: * rcs_parse_symbols()
1266: *
1267: * Parse the symbol list given as value to the `symbols' keyword.
1268: * Returns 0 on success, or -1 on failure.
1269: */
1270:
1271: static int
1272: rcs_parse_symbols(RCSFILE *rfp)
1273: {
1274: int type;
1275: struct rcs_sym *symp;
1276:
1277: for (;;) {
1278: type = rcs_gettok(rfp);
1279: if (type == RCS_TOK_SCOLON)
1280: break;
1281:
1282: if (type != RCS_TOK_STRING) {
1283: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1284: RCS_TOKSTR(rfp));
1285: return (-1);
1286: }
1287:
1288: symp = (struct rcs_sym *)malloc(sizeof(*symp));
1289: if (symp == NULL) {
1290: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
1291: return (-1);
1292: }
1293: symp->rs_name = strdup(RCS_TOKSTR(rfp));
1.10 joris 1294: if (symp->rs_name == NULL) {
1295: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
1296: free(symp);
1297: return (-1);
1298: }
1299:
1.1 jfb 1300: symp->rs_num = rcsnum_alloc();
1.11 joris 1301: if (symp->rs_num == NULL) {
1302: cvs_log(LP_ERRNO, "failed to allocate rcsnum info");
1303: free(symp);
1304: return (-1);
1305: }
1.1 jfb 1306:
1307: type = rcs_gettok(rfp);
1308: if (type != RCS_TOK_COLON) {
1309: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1310: RCS_TOKSTR(rfp));
1.11 joris 1311: rcsnum_free(symp->rs_num);
1.1 jfb 1312: free(symp->rs_name);
1313: free(symp);
1314: return (-1);
1315: }
1316:
1317: type = rcs_gettok(rfp);
1318: if (type != RCS_TOK_NUM) {
1319: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1320: RCS_TOKSTR(rfp));
1.11 joris 1321: rcsnum_free(symp->rs_num);
1.1 jfb 1322: free(symp->rs_name);
1323: free(symp);
1324: return (-1);
1325: }
1326:
1327: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
1328: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1329: RCS_TOKSTR(rfp));
1.11 joris 1330: rcsnum_free(symp->rs_num);
1.1 jfb 1331: free(symp->rs_name);
1332: free(symp);
1333: return (-1);
1334: }
1335:
1336: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
1337: }
1338:
1339: return (0);
1340: }
1341:
1342:
1343: /*
1344: * rcs_parse_locks()
1345: *
1346: * Parse the lock list given as value to the `locks' keyword.
1347: * Returns 0 on success, or -1 on failure.
1348: */
1349:
1350: static int
1351: rcs_parse_locks(RCSFILE *rfp)
1352: {
1353: int type;
1354: struct rcs_lock *lkp;
1355:
1356: for (;;) {
1357: type = rcs_gettok(rfp);
1358: if (type == RCS_TOK_SCOLON)
1359: break;
1360:
1361: if (type != RCS_TOK_ID) {
1362: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
1363: RCS_TOKSTR(rfp));
1364: return (-1);
1365: }
1366:
1367: lkp = (struct rcs_lock *)malloc(sizeof(*lkp));
1368: if (lkp == NULL) {
1369: cvs_log(LP_ERRNO, "failed to allocate RCS lock");
1370: return (-1);
1371: }
1372: lkp->rl_num = rcsnum_alloc();
1.11 joris 1373: if (lkp->rl_num == NULL) {
1374: free(lkp);
1375: return (-1);
1376: }
1.1 jfb 1377:
1378: type = rcs_gettok(rfp);
1379: if (type != RCS_TOK_COLON) {
1380: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1381: RCS_TOKSTR(rfp));
1382: free(lkp);
1383: return (-1);
1384: }
1385:
1386: type = rcs_gettok(rfp);
1387: if (type != RCS_TOK_NUM) {
1388: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1389: RCS_TOKSTR(rfp));
1390: free(lkp);
1391: return (-1);
1392: }
1393:
1394: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
1395: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1396: RCS_TOKSTR(rfp));
1397: free(lkp);
1398: return (-1);
1399: }
1400:
1401: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
1402: }
1403:
1404: /* check if we have a `strict' */
1405: type = rcs_gettok(rfp);
1406: if (type != RCS_TOK_STRICT) {
1407: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 1408: } else {
1.1 jfb 1409: rfp->rf_flags |= RCS_RF_SLOCK;
1410:
1411: type = rcs_gettok(rfp);
1412: if (type != RCS_TOK_SCOLON) {
1413: cvs_log(LP_ERR,
1414: "missing semi-colon after `strict' keyword");
1415: return (-1);
1416: }
1417: }
1418:
1419: return (0);
1420: }
1421:
1422: /*
1423: * rcs_parse_branches()
1424: *
1425: * Parse the list of branches following a `branches' keyword in a delta.
1426: * Returns 0 on success, or -1 on failure.
1427: */
1428:
1429: static int
1430: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
1431: {
1432: int type;
1433: struct rcs_branch *brp;
1434:
1435: for (;;) {
1436: type = rcs_gettok(rfp);
1437: if (type == RCS_TOK_SCOLON)
1438: break;
1439:
1440: if (type != RCS_TOK_NUM) {
1441: cvs_log(LP_ERR,
1442: "unexpected token `%s' in list of branches",
1443: RCS_TOKSTR(rfp));
1444: return (-1);
1445: }
1446:
1447: brp = (struct rcs_branch *)malloc(sizeof(*brp));
1448: if (brp == NULL) {
1449: cvs_log(LP_ERRNO, "failed to allocate RCS branch");
1450: return (-1);
1451: }
1452: brp->rb_num = rcsnum_alloc();
1.11 joris 1453: if (brp->rb_num == NULL) {
1454: free(brp);
1455: return (-1);
1456: }
1457:
1.1 jfb 1458: rcsnum_aton(RCS_TOKSTR(rfp), NULL, brp->rb_num);
1459:
1460: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
1461: }
1462:
1463: return (0);
1464: }
1465:
1466:
1467: /*
1468: * rcs_freedelta()
1469: *
1470: * Free the contents of a delta structure.
1471: */
1472:
1.18 jfb 1473: static void
1.1 jfb 1474: rcs_freedelta(struct rcs_delta *rdp)
1475: {
1.12 jfb 1476: struct rcs_branch *rb;
1.1 jfb 1477: struct rcs_delta *crdp;
1478:
1.12 jfb 1479: if (rdp->rd_num != NULL)
1480: rcsnum_free(rdp->rd_num);
1481: if (rdp->rd_next != NULL)
1482: rcsnum_free(rdp->rd_next);
1483:
1.1 jfb 1484: if (rdp->rd_author != NULL)
1485: free(rdp->rd_author);
1486: if (rdp->rd_state != NULL)
1487: free(rdp->rd_state);
1488: if (rdp->rd_log != NULL)
1489: free(rdp->rd_log);
1490: if (rdp->rd_text != NULL)
1491: free(rdp->rd_text);
1.12 jfb 1492:
1493: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1494: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
1495: rcsnum_free(rb->rb_num);
1496: free(rb);
1497: }
1.1 jfb 1498:
1499: while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) {
1500: TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list);
1501: rcs_freedelta(crdp);
1502: }
1503:
1504: free(rdp);
1505: }
1506:
1507:
1508: /*
1509: * rcs_freepdata()
1510: *
1511: * Free the contents of the parser data structure.
1512: */
1513:
1514: static void
1515: rcs_freepdata(struct rcs_pdata *pd)
1516: {
1517: if (pd->rp_file != NULL)
1518: (void)fclose(pd->rp_file);
1519: if (pd->rp_buf != NULL)
1520: free(pd->rp_buf);
1521: free(pd);
1522: }
1523:
1524:
1525: /*
1526: * rcs_gettok()
1527: *
1528: * Get the next RCS token from the string <str>.
1529: */
1530:
1531: static int
1532: rcs_gettok(RCSFILE *rfp)
1533: {
1534: u_int i;
1535: int ch, last, type;
1.18 jfb 1536: size_t len;
1537: char *bp;
1.1 jfb 1538: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1539:
1540: type = RCS_TOK_ERR;
1541: bp = pdp->rp_buf;
1542: *bp = '\0';
1543:
1544: if (pdp->rp_pttype != RCS_TOK_ERR) {
1545: type = pdp->rp_pttype;
1546: strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen);
1547: pdp->rp_pttype = RCS_TOK_ERR;
1548: return (type);
1549: }
1550:
1551: /* skip leading whitespace */
1552: /* XXX we must skip backspace too for compatibility, should we? */
1553: do {
1554: ch = getc(pdp->rp_file);
1555: if (ch == '\n')
1.18 jfb 1556: pdp->rp_lines++;
1.1 jfb 1557: } while (isspace(ch));
1558:
1559: if (ch == EOF) {
1560: type = RCS_TOK_EOF;
1.14 deraadt 1561: } else if (ch == ';') {
1.1 jfb 1562: type = RCS_TOK_SCOLON;
1.14 deraadt 1563: } else if (ch == ':') {
1.1 jfb 1564: type = RCS_TOK_COLON;
1.14 deraadt 1565: } else if (isalpha(ch)) {
1.18 jfb 1566: type = RCS_TOK_STRING;
1.1 jfb 1567: *(bp++) = ch;
1.18 jfb 1568: for (;;) {
1.1 jfb 1569: ch = getc(pdp->rp_file);
1.11 joris 1570: if (!isalnum(ch) && ch != '_' && ch != '-') {
1.1 jfb 1571: ungetc(ch, pdp->rp_file);
1572: break;
1573: }
1574: *(bp++) = ch;
1.18 jfb 1575: if (bp == pdp->rp_bufend - 1) {
1576: len = bp - pdp->rp_buf;
1577: if (rcs_growbuf(rfp) < 0) {
1578: type = RCS_TOK_ERR;
1579: break;
1580: }
1581: bp = pdp->rp_buf + len;
1582: }
1.1 jfb 1583: }
1584: *bp = '\0';
1585:
1.18 jfb 1586: if (type != RCS_TOK_ERR) {
1587: for (i = 0; i < RCS_NKEYS; i++) {
1588: if (strcmp(rcs_keys[i].rk_str,
1589: pdp->rp_buf) == 0) {
1590: type = rcs_keys[i].rk_id;
1591: break;
1592: }
1.1 jfb 1593: }
1594: }
1.14 deraadt 1595: } else if (ch == '@') {
1.1 jfb 1596: /* we have a string */
1.18 jfb 1597: type = RCS_TOK_STRING;
1.1 jfb 1598: for (;;) {
1599: ch = getc(pdp->rp_file);
1600: if (ch == '@') {
1601: ch = getc(pdp->rp_file);
1602: if (ch != '@') {
1603: ungetc(ch, pdp->rp_file);
1604: break;
1605: }
1.14 deraadt 1606: } else if (ch == '\n')
1.18 jfb 1607: pdp->rp_lines++;
1.1 jfb 1608:
1609: *(bp++) = ch;
1.18 jfb 1610: if (bp == pdp->rp_bufend - 1) {
1611: len = bp - pdp->rp_buf;
1612: if (rcs_growbuf(rfp) < 0) {
1613: type = RCS_TOK_ERR;
1614: break;
1615: }
1616: bp = pdp->rp_buf + len;
1617: }
1.1 jfb 1618: }
1619:
1620: *bp = '\0';
1.14 deraadt 1621: } else if (isdigit(ch)) {
1.1 jfb 1622: *(bp++) = ch;
1623: last = ch;
1624: type = RCS_TOK_NUM;
1625:
1626: for (;;) {
1627: ch = getc(pdp->rp_file);
1.18 jfb 1628: if (bp == pdp->rp_bufend)
1.1 jfb 1629: break;
1630: if (!isdigit(ch) && ch != '.') {
1631: ungetc(ch, pdp->rp_file);
1632: break;
1633: }
1634:
1635: if (last == '.' && ch == '.') {
1636: type = RCS_TOK_ERR;
1637: break;
1638: }
1639: last = ch;
1640: *(bp++) = ch;
1641: }
1.18 jfb 1642: *bp = '\0';
1.1 jfb 1643: }
1644:
1645: return (type);
1646: }
1647:
1648:
1649: /*
1650: * rcs_pushtok()
1651: *
1652: * Push a token back in the parser's token buffer.
1653: */
1654:
1655: static int
1656: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
1657: {
1658: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1659:
1660: if (pdp->rp_pttype != RCS_TOK_ERR)
1661: return (-1);
1662:
1663: pdp->rp_pttype = type;
1664: strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok));
1665: return (0);
1666: }
1667:
1668:
1669: /*
1670: * rcs_stresc()
1671: *
1672: * Performs either escaping or unescaping of the string stored in <str>.
1673: * The operation is to escape special RCS characters if the <esc> argument
1674: * is 1, or unescape otherwise. The result is stored in the <buf> destination
1675: * buffer, and <blen> must originally point to the size of <buf>.
1676: * Returns the number of bytes which have been read from the source <str> and
1677: * operated on. The <blen> parameter will contain the number of bytes
1678: * actually copied in <buf>.
1679: */
1680:
1681: size_t
1682: rcs_stresc(int esc, const char *str, char *buf, size_t *blen)
1683: {
1684: size_t rlen;
1685: const char *sp;
1686: char *bp, *bep;
1687:
1688: rlen = 0;
1689: bp = buf;
1690: bep = buf + *blen - 1;
1691:
1692: for (sp = str; (*sp != '\0') && (bp <= (bep - 1)); sp++) {
1693: if (*sp == '@') {
1694: if (esc) {
1695: if (bp > (bep - 2))
1696: break;
1697: *(bp++) = '@';
1.14 deraadt 1698: } else {
1.1 jfb 1699: sp++;
1700: if (*sp != '@') {
1701: cvs_log(LP_WARN,
1702: "unknown escape character `%c' in "
1703: "RCS file", *sp);
1704: if (*sp == '\0')
1705: break;
1706: }
1707: }
1708: }
1709:
1710: *(bp++) = *sp;
1711: }
1712:
1713: *bp = '\0';
1714: *blen = (bp - buf);
1715: return (sp - str);
1716: }
1717:
1718:
1719: /*
1720: * rcs_splitlines()
1721: *
1722: * Split the contents of a file into a list of lines.
1723: */
1724:
1725: static struct rcs_foo*
1726: rcs_splitlines(const char *fcont)
1727: {
1728: char *dcp;
1729: struct rcs_foo *foo;
1730: struct rcs_line *lp;
1731:
1732: foo = (struct rcs_foo *)malloc(sizeof(*foo));
1733: if (foo == NULL) {
1734: cvs_log(LP_ERR, "failed to allocate line structure");
1735: return (NULL);
1736: }
1737: TAILQ_INIT(&(foo->rl_lines));
1738: foo->rl_nblines = 0;
1739: foo->rl_data = strdup(fcont);
1740: if (foo->rl_data == NULL) {
1741: cvs_log(LP_ERRNO, "failed to copy file contents");
1742: free(foo);
1743: return (NULL);
1744: }
1745:
1746: /*
1747: * Add a first bogus line with line number 0. This is used so we
1748: * can position the line pointer before 1 when changing the first line
1749: * in rcs_patch().
1750: */
1751: lp = (struct rcs_line *)malloc(sizeof(*lp));
1.5 vincent 1752: if (lp == NULL)
1.1 jfb 1753: return (NULL);
1.5 vincent 1754:
1.1 jfb 1755: lp->rl_line = NULL;
1756: lp->rl_lineno = 0;
1757: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1758:
1759:
1760: for (dcp = foo->rl_data; *dcp != '\0';) {
1761: lp = (struct rcs_line *)malloc(sizeof(*lp));
1762: if (lp == NULL) {
1763: cvs_log(LP_ERR, "failed to allocate line entry");
1764: return (NULL);
1765: }
1766:
1767: lp->rl_line = dcp;
1768: lp->rl_lineno = ++(foo->rl_nblines);
1769: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1770:
1771: dcp = strchr(dcp, '\n');
1772: if (dcp == NULL) {
1773: break;
1774: }
1775: *(dcp++) = '\0';
1776: }
1777:
1778: return (foo);
1.5 vincent 1779: }
1780:
1781: static void
1782: rcs_freefoo(struct rcs_foo *fp)
1783: {
1784: struct rcs_line *lp;
1785:
1786: while ((lp = TAILQ_FIRST(&fp->rl_lines)) != NULL) {
1787: TAILQ_REMOVE(&fp->rl_lines, lp, rl_list);
1788: free(lp);
1789: }
1790: free(fp->rl_data);
1791: free(fp);
1.18 jfb 1792: }
1793:
1794: /*
1795: * rcs_growbuf()
1796: *
1797: * Attempt to grow the internal parse buffer for the RCS file <rf> by
1798: * RCS_BUFEXTSIZE.
1799: * In case of failure, the original buffer is left unmodified.
1800: * Returns 0 on success, or -1 on failure.
1801: */
1802:
1803: static int
1804: rcs_growbuf(RCSFILE *rf)
1805: {
1806: void *tmp;
1807: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
1808:
1809: tmp = realloc(pdp->rp_buf, pdp->rp_blen + RCS_BUFEXTSIZE);
1810: if (tmp == NULL) {
1811: cvs_log(LP_ERRNO, "failed to grow RCS parse buffer");
1812: return (-1);
1813: }
1814:
1815: pdp->rp_buf = (char *)tmp;
1816: pdp->rp_blen += RCS_BUFEXTSIZE;
1817: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1818:
1819: return (0);
1.1 jfb 1820: }