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