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