Annotation of src/usr.bin/cvs/rcs.c, Revision 1.1.1.1
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 */
490: lineno = 1;
491: TAILQ_FOREACH(lp, &(dlines->rl_lines), rl_list) {
492: if (lineno == 1) {
493: lineno++;
494: continue;
495: }
496: cvs_buf_fappend(res, "%s\n", lp->rl_line);
497: lp->rl_lineno = lineno++;
498: }
499:
500: dlines->rl_nblines = lineno - 1;
501:
502: return (res);
503: }
504:
505:
506: /*
507: * rcs_getrev()
508: *
509: * Get the whole contents of revision <rev> from the RCSFILE <rfp>. The
510: * returned buffer is dynamically allocated and should be released using free()
511: * once the caller is done using it.
512: */
513:
514: BUF*
515: rcs_getrev(RCSFILE *rfp, RCSNUM *rev)
516: {
517: int res;
518: size_t len;
519: void *bp;
520: RCSNUM *crev;
521: BUF *rbuf;
522: struct rcs_delta *rdp = NULL;
523:
524: res = rcsnum_cmp(rfp->rf_head, rev, 0);
525: if (res == 1) {
526: cvs_log(LP_ERR, "sorry, can't travel in the future yet");
527: return (NULL);
528: }
529: else {
530: rdp = rcs_findrev(rfp, rfp->rf_head);
531: if (rdp == NULL) {
532: cvs_log(LP_ERR, "failed to get RCS HEAD revision");
533: return (NULL);
534: }
535:
536: len = strlen(rdp->rd_text);
537: rbuf = cvs_buf_alloc(len, BUF_AUTOEXT);
538: if (rbuf == NULL)
539: return (NULL);
540: cvs_buf_append(rbuf, rdp->rd_text, len);
541:
542: if (res != 0) {
543: /* Apply patches backwards to get the right version.
544: * This will need some rework to support sub branches.
545: */
546: crev = rcsnum_alloc();
547:
548: rcsnum_cpy(rfp->rf_head, crev, 0);
549: do {
550: crev->rn_id[crev->rn_len - 1]--;
551: rdp = rcs_findrev(rfp, crev);
552: if (rdp == NULL)
553: return (NULL);
554:
555: cvs_buf_putc(rbuf, '\0');
556: bp = cvs_buf_release(rbuf);
557: rbuf = rcs_patch((char *)bp, rdp->rd_text);
558: if (rbuf == NULL)
559: break;
560: } while (rcsnum_cmp(crev, rev, 0) != 0);
561:
562: rcsnum_free(crev);
563: }
564: }
565:
566:
567: return (rbuf);
568: }
569:
570:
571: /*
572: * rcs_getrevbydate()
573: *
574: * Get an RCS revision by a specific date.
575: */
576:
577: RCSNUM*
578: rcs_getrevbydate(RCSFILE *rfp, struct tm *date)
579: {
580: return (NULL);
581: }
582:
583:
584: /*
585: * rcs_findrev()
586: *
587: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
588: * The revision number is given in <rev>.
589: * Returns a pointer to the delta on success, or NULL on failure.
590: */
591:
592: static struct rcs_delta*
593: rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
594: {
595: u_int cmplen;
596: struct rcs_delta *rdp;
597: struct rcs_dlist *hp;
598:
599: cmplen = 2;
600: hp = &(rfp->rf_delta);
601:
602: TAILQ_FOREACH(rdp, hp, rd_list) {
603: if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) {
604: if (cmplen == rev->rn_len)
605: return (rdp);
606:
607: hp = &(rdp->rd_snodes);
608: cmplen += 2;
609: }
610: }
611:
612: return (NULL);
613: }
614:
615:
616: /*
617: * rcs_parse()
618: *
619: * Parse the contents of file <path>, which are in the RCS format.
620: * Returns 0 on success, or -1 on failure.
621: */
622:
623: int
624: rcs_parse(RCSFILE *rfp)
625: {
626: int ret;
627: struct rcs_pdata *pdp;
628:
629: if (rfp->rf_flags & RCS_RF_PARSED)
630: return (0);
631:
632: pdp = (struct rcs_pdata *)malloc(sizeof(*pdp));
633: if (pdp == NULL) {
634: cvs_log(LP_ERRNO, "failed to allocate RCS parser data");
635: return (-1);
636: }
637: memset(pdp, 0, sizeof(*pdp));
638:
639: pdp->rp_line = 1;
640: pdp->rp_pttype = RCS_TOK_ERR;
641:
642: pdp->rp_file = fopen(rfp->rf_path, "r");
643: if (pdp->rp_file == NULL) {
644: cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path);
645: rcs_freepdata(pdp);
646: return (-1);
647: }
648:
649: pdp->rp_buf = (char *)malloc(RCS_BUFSIZE);
650: if (pdp->rp_buf == NULL) {
651: cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer");
652: rcs_freepdata(pdp);
653: return (-1);
654: }
655: pdp->rp_blen = RCS_BUFSIZE;
656:
657: /* ditch the strict lock */
658: rfp->rf_flags &= ~RCS_RF_SLOCK;
659: rfp->rf_pdata = pdp;
660:
661: if (rcs_parse_admin(rfp) < 0) {
662: rcs_freepdata(pdp);
663: return (-1);
664: }
665:
666: for (;;) {
667: ret = rcs_parse_delta(rfp);
668: if (ret == 0)
669: break;
670: else if (ret == -1) {
671: rcs_freepdata(pdp);
672: return (-1);
673: }
674: }
675:
676: ret = rcs_gettok(rfp);
677: if (ret != RCS_TOK_DESC) {
678: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
679: RCS_TOKSTR(rfp));
680: rcs_freepdata(pdp);
681: return (-1);
682: }
683:
684: ret = rcs_gettok(rfp);
685: if (ret != RCS_TOK_STRING) {
686: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
687: RCS_TOKSTR(rfp));
688: rcs_freepdata(pdp);
689: return (-1);
690: }
691:
692: rfp->rf_desc = strdup(RCS_TOKSTR(rfp));
693:
694: for (;;) {
695: ret = rcs_parse_deltatext(rfp);
696: if (ret == 0)
697: break;
698: else if (ret == -1) {
699: rcs_freepdata(pdp);
700: return (-1);
701: }
702: }
703:
704: cvs_log(LP_DEBUG, "RCS file `%s' parsed OK (%u lines)", rfp->rf_path,
705: pdp->rp_line);
706:
707: rcs_freepdata(pdp);
708:
709: rfp->rf_pdata = NULL;
710: rfp->rf_flags |= RCS_RF_PARSED|RCS_RF_SYNCED;
711:
712: return (0);
713: }
714:
715:
716: /*
717: * rcs_parse_admin()
718: *
719: * Parse the administrative portion of an RCS file.
720: * Returns 0 on success, or -1 on failure.
721: */
722:
723: static int
724: rcs_parse_admin(RCSFILE *rfp)
725: {
726: u_int i;
727: int tok, ntok, hmask;
728: struct rcs_key *rk;
729:
730: /* hmask is a mask of the headers already encountered */
731: hmask = 0;
732: for (;;) {
733: tok = rcs_gettok(rfp);
734: if (tok == RCS_TOK_ERR) {
735: cvs_log(LP_ERR, "parse error in RCS admin section");
736: return (-1);
737: }
738: else if (tok == RCS_TOK_NUM) {
739: /* assume this is the start of the first delta */
740: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
741: return (0);
742: }
743:
744: rk = NULL;
745: for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++)
746: if (rcs_keys[i].rk_id == tok)
747: rk = &(rcs_keys[i]);
748:
749: if (hmask & (1 << tok)) {
750: cvs_log(LP_ERR, "duplicate RCS key");
751: return (-1);
752: }
753: hmask |= (1 << tok);
754:
755: switch (tok) {
756: case RCS_TOK_HEAD:
757: case RCS_TOK_BRANCH:
758: case RCS_TOK_COMMENT:
759: case RCS_TOK_EXPAND:
760: ntok = rcs_gettok(rfp);
761: if (ntok == RCS_TOK_SCOLON)
762: break;
763: if (ntok != rk->rk_val) {
764: cvs_log(LP_ERR,
765: "invalid value type for RCS key `%s'",
766: rk->rk_str);
767: }
768:
769: if (tok == RCS_TOK_HEAD) {
770: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
771: rfp->rf_head);
772: }
773: else if (tok == RCS_TOK_BRANCH) {
774: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
775: rfp->rf_branch);
776: }
777: else if (tok == RCS_TOK_COMMENT) {
778: rfp->rf_comment = strdup(RCS_TOKSTR(rfp));
779: }
780: else if (tok == RCS_TOK_EXPAND) {
781: rfp->rf_expand = strdup(RCS_TOKSTR(rfp));
782: }
783:
784: /* now get the expected semi-colon */
785: ntok = rcs_gettok(rfp);
786: if (ntok != RCS_TOK_SCOLON) {
787: cvs_log(LP_ERR,
788: "missing semi-colon after RCS `%s' key",
789: rk->rk_str);
790: return (-1);
791: }
792: break;
793: case RCS_TOK_ACCESS:
794: rcs_parse_access(rfp);
795: break;
796: case RCS_TOK_SYMBOLS:
797: rcs_parse_symbols(rfp);
798: break;
799: case RCS_TOK_LOCKS:
800: rcs_parse_locks(rfp);
801: break;
802: default:
803: cvs_log(LP_ERR,
804: "unexpected token `%s' in RCS admin section",
805: RCS_TOKSTR(rfp));
806: return (-1);
807: }
808: }
809:
810: return (0);
811: }
812:
813:
814: /*
815: * rcs_parse_delta()
816: *
817: * Parse an RCS delta section and allocate the structure to store that delta's
818: * information in the <rfp> delta list.
819: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
820: * -1 on error.
821: */
822:
823: static int
824: rcs_parse_delta(RCSFILE *rfp)
825: {
826: int ret, tok, ntok, hmask;
827: u_int i;
828: char *tokstr;
829: RCSNUM datenum;
830: struct rcs_delta *rdp;
831: struct rcs_key *rk;
832:
833: rdp = (struct rcs_delta *)malloc(sizeof(*rdp));
834: if (rdp == NULL) {
835: cvs_log(LP_ERRNO, "failed to allocate RCS delta structure");
836: return (-1);
837: }
838: memset(rdp, 0, sizeof(*rdp));
839:
840: rdp->rd_num = rcsnum_alloc();
841: rdp->rd_next = rcsnum_alloc();
842:
843: TAILQ_INIT(&(rdp->rd_branches));
844:
845: tok = rcs_gettok(rfp);
846: if (tok != RCS_TOK_NUM) {
847: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
848: RCS_TOKSTR(rfp));
849: rcs_freedelta(rdp);
850: return (-1);
851: }
852: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
853:
854: hmask = 0;
855: ret = 0;
856: tokstr = NULL;
857:
858: for (;;) {
859: tok = rcs_gettok(rfp);
860: if (tok == RCS_TOK_ERR) {
861: cvs_log(LP_ERR, "parse error in RCS delta section");
862: rcs_freedelta(rdp);
863: return (-1);
864: }
865: else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
866: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
867: ret = (tok == RCS_TOK_NUM ? 1 : 0);
868: break;
869: }
870:
871: rk = NULL;
872: for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++)
873: if (rcs_keys[i].rk_id == tok)
874: rk = &(rcs_keys[i]);
875:
876: if (hmask & (1 << tok)) {
877: cvs_log(LP_ERR, "duplicate RCS key");
878: rcs_freedelta(rdp);
879: return (-1);
880: }
881: hmask |= (1 << tok);
882:
883: switch (tok) {
884: case RCS_TOK_DATE:
885: case RCS_TOK_AUTHOR:
886: case RCS_TOK_STATE:
887: case RCS_TOK_NEXT:
888: ntok = rcs_gettok(rfp);
889: if (ntok == RCS_TOK_SCOLON) {
890: if (rk->rk_flags & RCS_VOPT)
891: break;
892: else {
893: cvs_log(LP_ERR, "missing mandatory "
894: "value to RCS key `%s'",
895: rk->rk_str);
896: rcs_freedelta(rdp);
897: return (-1);
898: }
899: }
900:
901: if (ntok != rk->rk_val) {
902: cvs_log(LP_ERR,
903: "invalid value type for RCS key `%s'",
904: rk->rk_str);
905: rcs_freedelta(rdp);
906: return (-1);
907: }
908:
909: if (tokstr != NULL)
910: free(tokstr);
911: tokstr = strdup(RCS_TOKSTR(rfp));
912:
913:
914: /* now get the expected semi-colon */
915: ntok = rcs_gettok(rfp);
916: if (ntok != RCS_TOK_SCOLON) {
917: cvs_log(LP_ERR,
918: "missing semi-colon after RCS `%s' key",
919: rk->rk_str);
920: rcs_freedelta(rdp);
921: return (-1);
922: }
923:
924: if (tok == RCS_TOK_DATE) {
925: rcsnum_aton(tokstr, NULL, &datenum);
926: if (datenum.rn_len != 6) {
927: cvs_log(LP_ERR,
928: "RCS date specification has %s "
929: "fields",
930: (datenum.rn_len > 6) ? "too many" :
931: "missing");
932: rcs_freedelta(rdp);
933: }
934: rdp->rd_date.tm_year = datenum.rn_id[0];
935: rdp->rd_date.tm_mon = datenum.rn_id[1] - 1;
936: rdp->rd_date.tm_mday = datenum.rn_id[2];
937: rdp->rd_date.tm_hour = datenum.rn_id[3];
938: rdp->rd_date.tm_min = datenum.rn_id[4];
939: rdp->rd_date.tm_sec = datenum.rn_id[5];
940: }
941: else if (tok == RCS_TOK_AUTHOR) {
942: rdp->rd_author = tokstr;
943: tokstr = NULL;
944: }
945: else if (tok == RCS_TOK_STATE) {
946: rdp->rd_state = tokstr;
947: tokstr = NULL;
948: }
949: else if (tok == RCS_TOK_NEXT) {
950: rcsnum_aton(tokstr, NULL, rdp->rd_next);
951: }
952: break;
953: case RCS_TOK_BRANCHES:
954: rcs_parse_branches(rfp, rdp);
955: break;
956: default:
957: cvs_log(LP_ERR,
958: "unexpected token `%s' in RCS delta",
959: RCS_TOKSTR(rfp));
960: rcs_freedelta(rdp);
961: return (-1);
962: }
963: }
964:
965: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
966:
967: return (ret);
968: }
969:
970:
971: /*
972: * rcs_parse_deltatext()
973: *
974: * Parse an RCS delta text section and fill in the log and text field of the
975: * appropriate delta section.
976: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
977: * -1 on error.
978: */
979:
980: static int
981: rcs_parse_deltatext(RCSFILE *rfp)
982: {
983: int tok;
984: RCSNUM *tnum;
985: struct rcs_delta *rdp;
986:
987: tnum = rcsnum_alloc();
988: if (tnum == NULL)
989: return (-1);
990:
991: tok = rcs_gettok(rfp);
992: if (tok == RCS_TOK_EOF)
993: return (0);
994:
995: if (tok != RCS_TOK_NUM) {
996: cvs_log(LP_ERR,
997: "unexpected token `%s' at start of RCS delta text",
998: RCS_TOKSTR(rfp));
999: return (-1);
1000: }
1001: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1002:
1003: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1004: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1005: break;
1006: }
1007: if (rdp == NULL) {
1008: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1009: RCS_TOKSTR(rfp));
1010: return (-1);
1011: }
1012:
1013: tok = rcs_gettok(rfp);
1014: if (tok != RCS_TOK_LOG) {
1015: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1016: RCS_TOKSTR(rfp));
1017: return (-1);
1018: }
1019:
1020: tok = rcs_gettok(rfp);
1021: if (tok != RCS_TOK_STRING) {
1022: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1023: RCS_TOKSTR(rfp));
1024: return (-1);
1025: }
1026: rdp->rd_log = strdup(RCS_TOKSTR(rfp));
1027: if (rdp->rd_log == NULL) {
1028: cvs_log(LP_ERRNO, "failed to copy RCS deltatext log");
1029: return (-1);
1030: }
1031:
1032: tok = rcs_gettok(rfp);
1033: if (tok != RCS_TOK_TEXT) {
1034: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1035: RCS_TOKSTR(rfp));
1036: return (-1);
1037: }
1038:
1039: tok = rcs_gettok(rfp);
1040: if (tok != RCS_TOK_STRING) {
1041: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1042: RCS_TOKSTR(rfp));
1043: return (-1);
1044: }
1045:
1046: rdp->rd_text = strdup(RCS_TOKSTR(rfp));
1047: if (rdp->rd_text == NULL) {
1048: cvs_log(LP_ERRNO, "failed to copy RCS delta text");
1049: return (-1);
1050: }
1051:
1052: return (1);
1053: }
1054:
1055:
1056: /*
1057: * rcs_parse_access()
1058: *
1059: * Parse the access list given as value to the `access' keyword.
1060: * Returns 0 on success, or -1 on failure.
1061: */
1062:
1063: static int
1064: rcs_parse_access(RCSFILE *rfp)
1065: {
1066: int type;
1067:
1068: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
1069: if (type != RCS_TOK_ID) {
1070: cvs_log(LP_ERR, "unexpected token `%s' in access list",
1071: RCS_TOKSTR(rfp));
1072: return (-1);
1073: }
1074: }
1075:
1076: return (0);
1077: }
1078:
1079:
1080: /*
1081: * rcs_parse_symbols()
1082: *
1083: * Parse the symbol list given as value to the `symbols' keyword.
1084: * Returns 0 on success, or -1 on failure.
1085: */
1086:
1087: static int
1088: rcs_parse_symbols(RCSFILE *rfp)
1089: {
1090: int type;
1091: struct rcs_sym *symp;
1092:
1093: for (;;) {
1094: type = rcs_gettok(rfp);
1095: if (type == RCS_TOK_SCOLON)
1096: break;
1097:
1098: if (type != RCS_TOK_STRING) {
1099: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1100: RCS_TOKSTR(rfp));
1101: return (-1);
1102: }
1103:
1104: symp = (struct rcs_sym *)malloc(sizeof(*symp));
1105: if (symp == NULL) {
1106: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
1107: return (-1);
1108: }
1109: symp->rs_name = strdup(RCS_TOKSTR(rfp));
1110: symp->rs_num = rcsnum_alloc();
1111:
1112: type = rcs_gettok(rfp);
1113: if (type != RCS_TOK_COLON) {
1114: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1115: RCS_TOKSTR(rfp));
1116: free(symp->rs_name);
1117: free(symp);
1118: return (-1);
1119: }
1120:
1121: type = rcs_gettok(rfp);
1122: if (type != RCS_TOK_NUM) {
1123: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1124: RCS_TOKSTR(rfp));
1125: free(symp->rs_name);
1126: free(symp);
1127: return (-1);
1128: }
1129:
1130: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
1131: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1132: RCS_TOKSTR(rfp));
1133: free(symp->rs_name);
1134: free(symp);
1135: return (-1);
1136: }
1137:
1138: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
1139: }
1140:
1141: return (0);
1142: }
1143:
1144:
1145: /*
1146: * rcs_parse_locks()
1147: *
1148: * Parse the lock list given as value to the `locks' keyword.
1149: * Returns 0 on success, or -1 on failure.
1150: */
1151:
1152: static int
1153: rcs_parse_locks(RCSFILE *rfp)
1154: {
1155: int type;
1156: struct rcs_lock *lkp;
1157:
1158: for (;;) {
1159: type = rcs_gettok(rfp);
1160: if (type == RCS_TOK_SCOLON)
1161: break;
1162:
1163: if (type != RCS_TOK_ID) {
1164: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
1165: RCS_TOKSTR(rfp));
1166: return (-1);
1167: }
1168:
1169: lkp = (struct rcs_lock *)malloc(sizeof(*lkp));
1170: if (lkp == NULL) {
1171: cvs_log(LP_ERRNO, "failed to allocate RCS lock");
1172: return (-1);
1173: }
1174: lkp->rl_num = rcsnum_alloc();
1175:
1176: type = rcs_gettok(rfp);
1177: if (type != RCS_TOK_COLON) {
1178: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1179: RCS_TOKSTR(rfp));
1180: free(lkp);
1181: return (-1);
1182: }
1183:
1184: type = rcs_gettok(rfp);
1185: if (type != RCS_TOK_NUM) {
1186: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1187: RCS_TOKSTR(rfp));
1188: free(lkp);
1189: return (-1);
1190: }
1191:
1192: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
1193: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1194: RCS_TOKSTR(rfp));
1195: free(lkp);
1196: return (-1);
1197: }
1198:
1199: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
1200: }
1201:
1202: /* check if we have a `strict' */
1203: type = rcs_gettok(rfp);
1204: if (type != RCS_TOK_STRICT) {
1205: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1206: }
1207: else {
1208: rfp->rf_flags |= RCS_RF_SLOCK;
1209:
1210: type = rcs_gettok(rfp);
1211: if (type != RCS_TOK_SCOLON) {
1212: cvs_log(LP_ERR,
1213: "missing semi-colon after `strict' keyword");
1214: return (-1);
1215: }
1216: }
1217:
1218: return (0);
1219: }
1220:
1221: /*
1222: * rcs_parse_branches()
1223: *
1224: * Parse the list of branches following a `branches' keyword in a delta.
1225: * Returns 0 on success, or -1 on failure.
1226: */
1227:
1228: static int
1229: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
1230: {
1231: int type;
1232: struct rcs_branch *brp;
1233:
1234: for (;;) {
1235: type = rcs_gettok(rfp);
1236: if (type == RCS_TOK_SCOLON)
1237: break;
1238:
1239: if (type != RCS_TOK_NUM) {
1240: cvs_log(LP_ERR,
1241: "unexpected token `%s' in list of branches",
1242: RCS_TOKSTR(rfp));
1243: return (-1);
1244: }
1245:
1246: brp = (struct rcs_branch *)malloc(sizeof(*brp));
1247: if (brp == NULL) {
1248: cvs_log(LP_ERRNO, "failed to allocate RCS branch");
1249: return (-1);
1250: }
1251: brp->rb_num = rcsnum_alloc();
1252: rcsnum_aton(RCS_TOKSTR(rfp), NULL, brp->rb_num);
1253:
1254: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
1255: }
1256:
1257: return (0);
1258: }
1259:
1260:
1261: /*
1262: * rcs_freedelta()
1263: *
1264: * Free the contents of a delta structure.
1265: */
1266:
1267: void
1268: rcs_freedelta(struct rcs_delta *rdp)
1269: {
1270: struct rcs_delta *crdp;
1271:
1272: if (rdp->rd_author != NULL)
1273: free(rdp->rd_author);
1274: if (rdp->rd_state != NULL)
1275: free(rdp->rd_state);
1276: if (rdp->rd_log != NULL)
1277: free(rdp->rd_log);
1278: if (rdp->rd_text != NULL)
1279: free(rdp->rd_text);
1280:
1281: while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) {
1282: TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list);
1283: rcs_freedelta(crdp);
1284: }
1285:
1286: free(rdp);
1287: }
1288:
1289:
1290: /*
1291: * rcs_freepdata()
1292: *
1293: * Free the contents of the parser data structure.
1294: */
1295:
1296: static void
1297: rcs_freepdata(struct rcs_pdata *pd)
1298: {
1299: if (pd->rp_file != NULL)
1300: (void)fclose(pd->rp_file);
1301: if (pd->rp_buf != NULL)
1302: free(pd->rp_buf);
1303: free(pd);
1304: }
1305:
1306:
1307: /*
1308: * rcs_gettok()
1309: *
1310: * Get the next RCS token from the string <str>.
1311: */
1312:
1313: static int
1314: rcs_gettok(RCSFILE *rfp)
1315: {
1316: u_int i;
1317: int ch, last, type;
1318: char *bp, *bep;
1319: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1320:
1321: type = RCS_TOK_ERR;
1322: bp = pdp->rp_buf;
1323: bep = pdp->rp_buf + pdp->rp_blen - 1;
1324: *bp = '\0';
1325:
1326: if (pdp->rp_pttype != RCS_TOK_ERR) {
1327: type = pdp->rp_pttype;
1328: strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen);
1329: pdp->rp_pttype = RCS_TOK_ERR;
1330: return (type);
1331: }
1332:
1333: /* skip leading whitespace */
1334: /* XXX we must skip backspace too for compatibility, should we? */
1335: do {
1336: ch = getc(pdp->rp_file);
1337: if (ch == '\n')
1338: pdp->rp_line++;
1339: } while (isspace(ch));
1340:
1341: if (ch == EOF) {
1342: type = RCS_TOK_EOF;
1343: }
1344: else if (ch == ';') {
1345: type = RCS_TOK_SCOLON;
1346: }
1347: else if (ch == ':') {
1348: type = RCS_TOK_COLON;
1349: }
1350: else if (isalpha(ch)) {
1351: *(bp++) = ch;
1352: while (bp <= bep - 1) {
1353: ch = getc(pdp->rp_file);
1354: if (!isalnum(ch)) {
1355: ungetc(ch, pdp->rp_file);
1356: break;
1357: }
1358: *(bp++) = ch;
1359: }
1360: *bp = '\0';
1361:
1362: for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++) {
1363: if (strcmp(rcs_keys[i].rk_str, pdp->rp_buf) == 0) {
1364: type = rcs_keys[i].rk_id;
1365: break;
1366: }
1367: }
1368:
1369: /* not a keyword, assume it's just a string */
1370: if (type == RCS_TOK_ERR)
1371: type = RCS_TOK_STRING;
1372: }
1373: else if (ch == '@') {
1374: /* we have a string */
1375: for (;;) {
1376: ch = getc(pdp->rp_file);
1377: if (ch == '@') {
1378: ch = getc(pdp->rp_file);
1379: if (ch != '@') {
1380: ungetc(ch, pdp->rp_file);
1381: break;
1382: }
1383: }
1384: else if (ch == '\n')
1385: pdp->rp_line++;
1386:
1387: *(bp++) = ch;
1388: if (bp == bep)
1389: break;
1390: }
1391:
1392: *bp = '\0';
1393: type = RCS_TOK_STRING;
1394: }
1395: else if (isdigit(ch)) {
1396: *(bp++) = ch;
1397: last = ch;
1398: type = RCS_TOK_NUM;
1399:
1400: for (;;) {
1401: ch = getc(pdp->rp_file);
1402: if (bp == bep)
1403: break;
1404: if (!isdigit(ch) && ch != '.') {
1405: ungetc(ch, pdp->rp_file);
1406: break;
1407: }
1408:
1409: if (last == '.' && ch == '.') {
1410: type = RCS_TOK_ERR;
1411: break;
1412: }
1413: last = ch;
1414: *(bp++) = ch;
1415: }
1416: *(bp) = '\0';
1417: }
1418:
1419: return (type);
1420: }
1421:
1422:
1423: /*
1424: * rcs_pushtok()
1425: *
1426: * Push a token back in the parser's token buffer.
1427: */
1428:
1429: static int
1430: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
1431: {
1432: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1433:
1434: if (pdp->rp_pttype != RCS_TOK_ERR)
1435: return (-1);
1436:
1437: pdp->rp_pttype = type;
1438: strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok));
1439: return (0);
1440: }
1441:
1442:
1443: /*
1444: * rcs_stresc()
1445: *
1446: * Performs either escaping or unescaping of the string stored in <str>.
1447: * The operation is to escape special RCS characters if the <esc> argument
1448: * is 1, or unescape otherwise. The result is stored in the <buf> destination
1449: * buffer, and <blen> must originally point to the size of <buf>.
1450: * Returns the number of bytes which have been read from the source <str> and
1451: * operated on. The <blen> parameter will contain the number of bytes
1452: * actually copied in <buf>.
1453: */
1454:
1455: size_t
1456: rcs_stresc(int esc, const char *str, char *buf, size_t *blen)
1457: {
1458: size_t rlen;
1459: const char *sp;
1460: char *bp, *bep;
1461:
1462: if (!esc)
1463: printf("unescaping `%s'\n", str);
1464:
1465: rlen = 0;
1466: bp = buf;
1467: bep = buf + *blen - 1;
1468:
1469: for (sp = str; (*sp != '\0') && (bp <= (bep - 1)); sp++) {
1470: if (*sp == '@') {
1471: if (esc) {
1472: if (bp > (bep - 2))
1473: break;
1474: *(bp++) = '@';
1475: }
1476: else {
1477: sp++;
1478: if (*sp != '@') {
1479: cvs_log(LP_WARN,
1480: "unknown escape character `%c' in "
1481: "RCS file", *sp);
1482: if (*sp == '\0')
1483: break;
1484: }
1485: }
1486: }
1487:
1488: *(bp++) = *sp;
1489: }
1490:
1491: *bp = '\0';
1492: *blen = (bp - buf);
1493: return (sp - str);
1494: }
1495:
1496:
1497: /*
1498: * rcs_splitlines()
1499: *
1500: * Split the contents of a file into a list of lines.
1501: */
1502:
1503: static struct rcs_foo*
1504: rcs_splitlines(const char *fcont)
1505: {
1506: char *dcp;
1507: struct rcs_foo *foo;
1508: struct rcs_line *lp;
1509:
1510: foo = (struct rcs_foo *)malloc(sizeof(*foo));
1511: if (foo == NULL) {
1512: cvs_log(LP_ERR, "failed to allocate line structure");
1513: return (NULL);
1514: }
1515: TAILQ_INIT(&(foo->rl_lines));
1516: foo->rl_nblines = 0;
1517: foo->rl_data = strdup(fcont);
1518: if (foo->rl_data == NULL) {
1519: cvs_log(LP_ERRNO, "failed to copy file contents");
1520: free(foo);
1521: return (NULL);
1522: }
1523:
1524: /*
1525: * Add a first bogus line with line number 0. This is used so we
1526: * can position the line pointer before 1 when changing the first line
1527: * in rcs_patch().
1528: */
1529: lp = (struct rcs_line *)malloc(sizeof(*lp));
1530: if (lp == NULL) {
1531: return (NULL);
1532: }
1533: lp->rl_line = NULL;
1534: lp->rl_lineno = 0;
1535: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1536:
1537:
1538: for (dcp = foo->rl_data; *dcp != '\0';) {
1539: lp = (struct rcs_line *)malloc(sizeof(*lp));
1540: if (lp == NULL) {
1541: cvs_log(LP_ERR, "failed to allocate line entry");
1542: return (NULL);
1543: }
1544:
1545: lp->rl_line = dcp;
1546: lp->rl_lineno = ++(foo->rl_nblines);
1547: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1548:
1549: dcp = strchr(dcp, '\n');
1550: if (dcp == NULL) {
1551: break;
1552: }
1553: *(dcp++) = '\0';
1554: }
1555:
1556: return (foo);
1557: }