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