Annotation of src/usr.bin/rcs/rcsparse.c, Revision 1.2
1.2 ! tobias 1: /* $OpenBSD: rcsparse.c,v 1.1 2010/10/15 08:44:12 tobias Exp $ */
1.1 tobias 2: /*
3: * Copyright (c) 2010 Tobias Stoeckmann <tobias@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <sys/queue.h>
19:
20: #include <ctype.h>
21: #include <err.h>
22: #include <pwd.h>
23: #include <stdarg.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28:
29: #include "rcs.h"
30: #include "rcsparse.h"
31: #include "xmalloc.h"
32:
33: #define RCS_BUFSIZE 16384
34: #define RCS_BUFEXTSIZE 8192
35:
36: /* RCS token types */
37: #define RCS_TOK_HEAD (1 << 0)
38: #define RCS_TOK_BRANCH (1 << 1)
39: #define RCS_TOK_ACCESS (1 << 2)
40: #define RCS_TOK_SYMBOLS (1 << 3)
41: #define RCS_TOK_LOCKS (1 << 4)
42: #define RCS_TOK_STRICT (1 << 5)
43: #define RCS_TOK_COMMENT (1 << 6)
44: #define RCS_TOK_COMMITID (1 << 7)
45: #define RCS_TOK_EXPAND (1 << 8)
46: #define RCS_TOK_DESC (1 << 9)
47: #define RCS_TOK_DATE (1 << 10)
48: #define RCS_TOK_AUTHOR (1 << 11)
49: #define RCS_TOK_STATE (1 << 12)
50: #define RCS_TOK_BRANCHES (1 << 13)
51: #define RCS_TOK_NEXT (1 << 14)
52: #define RCS_TOK_LOG (1 << 15)
53: #define RCS_TOK_TEXT (1 << 16)
54: #define RCS_TOK_COLON (1 << 17)
55: #define RCS_TOK_COMMA (1 << 18)
56: #define RCS_TOK_SCOLON (1 << 19)
57:
58: #define RCS_TYPE_STRING (1 << 20)
59: #define RCS_TYPE_NUMBER (1 << 21)
60: #define RCS_TYPE_BRANCH (1 << 22)
61: #define RCS_TYPE_REVISION (1 << 23)
62: #define RCS_TYPE_LOGIN (1 << 24)
63: #define RCS_TYPE_STATE (1 << 25)
64: #define RCS_TYPE_SYMBOL (1 << 26)
65: #define RCS_TYPE_DATE (1 << 27)
66: #define RCS_TYPE_KEYWORD (1 << 28)
67: #define RCS_TYPE_COMMITID (1 << 29)
68:
69: #define MANDATORY 0
70: #define OPTIONAL 1
71:
72: /* opaque parse data */
73: struct rcs_pdata {
74: char *rp_buf;
75: size_t rp_blen;
76: char *rp_bufend;
77: size_t rp_tlen;
78:
79: struct rcs_delta *rp_delta;
80: FILE *rp_file;
81: int rp_lineno;
82: int rp_msglineno;
83: int rp_token;
84:
85: union {
86: RCSNUM *rev;
87: char *str;
88: struct tm date;
89: } rp_value;
90: };
91:
92: struct rcs_keyword {
93: const char *k_name;
94: int k_val;
95: };
96:
97: struct rcs_section {
98: int token;
99: int (*parse)(RCSFILE *, struct rcs_pdata *);
100: int opt;
101: };
102:
103: /* this has to be sorted always */
104: static const struct rcs_keyword keywords[] = {
105: { "access", RCS_TOK_ACCESS},
106: { "author", RCS_TOK_AUTHOR},
107: { "branch", RCS_TOK_BRANCH},
108: { "branches", RCS_TOK_BRANCHES},
109: { "comment", RCS_TOK_COMMENT},
110: { "date", RCS_TOK_DATE},
111: { "desc", RCS_TOK_DESC},
112: { "expand", RCS_TOK_EXPAND},
113: { "head", RCS_TOK_HEAD},
114: { "locks", RCS_TOK_LOCKS},
115: { "log", RCS_TOK_LOG},
116: { "next", RCS_TOK_NEXT},
117: { "state", RCS_TOK_STATE},
118: { "strict", RCS_TOK_STRICT},
119: { "symbols", RCS_TOK_SYMBOLS},
120: { "text", RCS_TOK_TEXT}
121: };
122:
123: /* parser functions specified in rcs_section structs */
124: static int rcsparse_head(RCSFILE *, struct rcs_pdata *);
125: static int rcsparse_branch(RCSFILE *, struct rcs_pdata *);
126: static int rcsparse_access(RCSFILE *, struct rcs_pdata *);
127: static int rcsparse_symbols(RCSFILE *, struct rcs_pdata *);
128: static int rcsparse_locks(RCSFILE *, struct rcs_pdata *);
129: static int rcsparse_strict(RCSFILE *, struct rcs_pdata *);
130: static int rcsparse_comment(RCSFILE *, struct rcs_pdata *);
131: static int rcsparse_commitid(RCSFILE *, struct rcs_pdata *);
132: static int rcsparse_expand(RCSFILE *, struct rcs_pdata *);
133: static int rcsparse_deltarevision(RCSFILE *, struct rcs_pdata *);
134: static int rcsparse_date(RCSFILE *, struct rcs_pdata *);
135: static int rcsparse_author(RCSFILE *, struct rcs_pdata *);
136: static int rcsparse_state(RCSFILE *, struct rcs_pdata *);
137: static int rcsparse_branches(RCSFILE *, struct rcs_pdata *);
138: static int rcsparse_next(RCSFILE *, struct rcs_pdata *);
139: static int rcsparse_textrevision(RCSFILE *, struct rcs_pdata *);
140: static int rcsparse_log(RCSFILE *, struct rcs_pdata *);
141: static int rcsparse_text(RCSFILE *, struct rcs_pdata *);
142:
143: static int rcsparse_delta(RCSFILE *);
144: static int rcsparse_deltatext(RCSFILE *);
145: static int rcsparse_desc(RCSFILE *);
146:
147: static int kw_cmp(const void *, const void *);
148: static int rcsparse(RCSFILE *, struct rcs_section *);
149: static void rcsparse_growbuf(RCSFILE *);
150: static int rcsparse_string(RCSFILE *, int);
151: static int rcsparse_token(RCSFILE *, int);
152: static void rcsparse_warnx(RCSFILE *, char *, ...);
153: static int valid_login(char *);
154:
155: /*
156: * head [REVISION];
157: * [branch BRANCH];
158: * access [LOGIN ...];
159: * symbols [SYMBOL:REVISION ...];
160: * locks [LOGIN:REVISION ...];
161: * [strict;]
162: * [comment [@[...]@];]
163: * [expand [@[...]@];]
164: */
165: static struct rcs_section sec_admin[] = {
166: { RCS_TOK_HEAD, rcsparse_head, MANDATORY },
167: { RCS_TOK_BRANCH, rcsparse_branch, OPTIONAL },
168: { RCS_TOK_ACCESS, rcsparse_access, MANDATORY },
169: { RCS_TOK_SYMBOLS, rcsparse_symbols, MANDATORY },
170: { RCS_TOK_LOCKS, rcsparse_locks, MANDATORY },
171: { RCS_TOK_STRICT, rcsparse_strict, OPTIONAL },
172: { RCS_TOK_COMMENT, rcsparse_comment, OPTIONAL },
173: { RCS_TOK_EXPAND, rcsparse_expand, OPTIONAL },
174: { 0, NULL, 0 }
175: };
176:
177: /*
178: * REVISION
179: * date [YY]YY.MM.DD.HH.MM.SS;
180: * author LOGIN;
181: * state STATE;
182: * branches [REVISION ...];
183: * next [REVISION];
184: * [commitid ID;]
185: */
186: static struct rcs_section sec_delta[] = {
187: { RCS_TYPE_REVISION, rcsparse_deltarevision, MANDATORY },
188: { RCS_TOK_DATE, rcsparse_date, MANDATORY },
189: { RCS_TOK_AUTHOR, rcsparse_author, MANDATORY },
190: { RCS_TOK_STATE, rcsparse_state, MANDATORY },
191: { RCS_TOK_BRANCHES, rcsparse_branches, MANDATORY },
192: { RCS_TOK_NEXT, rcsparse_next, MANDATORY },
193: { RCS_TOK_COMMITID, rcsparse_commitid, OPTIONAL },
194: { 0, NULL, 0 }
195: };
196:
197: /*
198: * REVISION
199: * log @[...]@
200: * text @[...]@
201: */
202: static struct rcs_section sec_deltatext[] = {
203: { RCS_TYPE_REVISION, rcsparse_textrevision, MANDATORY },
204: { RCS_TOK_LOG, rcsparse_log, MANDATORY },
205: { RCS_TOK_TEXT, rcsparse_text, MANDATORY },
206: { 0, NULL, 0 }
207: };
208:
209: /*
210: * rcsparse_init()
211: *
212: * Initializes the parsing data structure and parses the admin section of
213: * RCS file <rfp>.
214: *
215: * Returns 0 on success or 1 on failure.
216: */
217: int
218: rcsparse_init(RCSFILE *rfp)
219: {
220: struct rcs_pdata *pdp;
221:
1.2 ! tobias 222: if (rfp->rf_flags & RCS_PARSED)
1.1 tobias 223: return (0);
224:
225: pdp = xmalloc(sizeof(*pdp));
226: pdp->rp_buf = xmalloc(RCS_BUFSIZE);
227: pdp->rp_blen = RCS_BUFSIZE;
228: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
229: pdp->rp_token = -1;
230: pdp->rp_lineno = 1;
231: pdp->rp_msglineno = 1;
232: if ((pdp->rp_file = fdopen(rfp->rf_fd, "r")) == NULL) {
233: xfree(pdp);
234: return (1);
235: }
236: /* ditch the strict lock */
237: rfp->rf_flags &= ~RCS_SLOCK;
238: rfp->rf_pdata = pdp;
239:
240: if (rcsparse(rfp, sec_admin)) {
241: rcsparse_free(rfp);
242: return (1);
243: }
244:
245: if ((rfp->rf_flags & RCS_PARSE_FULLY) &&
246: rcsparse_deltatexts(rfp, NULL)) {
247: rcsparse_free(rfp);
248: return (1);
249: }
250:
251: rfp->rf_flags |= RCS_SYNCED;
252: return (0);
253: }
254:
255: /*
256: * rcsparse_deltas()
257: *
258: * Parse deltas. If <rev> is not NULL, parse only as far as that
259: * revision. If <rev> is NULL, parse all deltas.
260: *
261: * Returns 0 on success or 1 on error.
262: */
263: int
264: rcsparse_deltas(RCSFILE *rfp, RCSNUM *rev)
265: {
266: int ret;
267: struct rcs_delta *enddelta;
268:
269: if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
270: return (0);
271:
272: for (;;) {
273: ret = rcsparse_delta(rfp);
274: if (rev != NULL) {
275: enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
276: if (enddelta == NULL)
277: return (1);
278:
279: if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
280: break;
281: }
282:
283: if (ret == 0) {
284: rfp->rf_flags |= PARSED_DELTAS;
285: break;
286: }
287: else if (ret == -1)
288: return (1);
289: }
290:
291: return (0);
292: }
293:
294: /*
295: * rcsparse_deltatexts()
296: *
297: * Parse deltatexts. If <rev> is not NULL, parse only as far as that
298: * revision. If <rev> is NULL, parse everything.
299: *
300: * Returns 0 on success or 1 on error.
301: */
302: int
303: rcsparse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
304: {
305: int ret;
306: struct rcs_delta *rdp;
307:
308: if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
309: (rfp->rf_flags & RCS_CREATE))
310: return (0);
311:
312: if (!(rfp->rf_flags & PARSED_DESC))
313: if (rcsparse_desc(rfp))
314: return (1);
315: for (;;) {
316: if (rev != NULL) {
317: rdp = rcs_findrev(rfp, rev);
318: if (rdp->rd_text != NULL)
319: break;
320: else
321: ret = rcsparse_deltatext(rfp);
322: } else
323: ret = rcsparse_deltatext(rfp);
324: if (ret == 0) {
325: rfp->rf_flags |= PARSED_DELTATEXTS;
326: break;
327: }
328: else if (ret == -1)
329: return (1);
330: }
331:
332: return (0);
333: }
334:
335: /*
336: * rcsparse_free()
337: *
338: * Free the contents of the <rfp>'s parser data structure.
339: */
340: void
341: rcsparse_free(RCSFILE *rfp)
342: {
343: struct rcs_pdata *pdp;
344:
345: pdp = rfp->rf_pdata;
346:
347: if (pdp->rp_file != NULL)
348: (void)fclose(pdp->rp_file);
349: if (pdp->rp_buf != NULL)
350: xfree(pdp->rp_buf);
351: if (pdp->rp_token == RCS_TYPE_REVISION)
352: rcsnum_free(pdp->rp_value.rev);
353: xfree(pdp);
354: }
355:
356: /*
357: * rcsparse_desc()
358: *
359: * Parse desc of the RCS file <rfp>. By calling rcsparse_desc, all deltas
360: * will be parsed in order to proceed the reading cursor to the desc keyword.
361: *
362: * desc @[...]@;
363: *
364: * Returns 0 on success or 1 on error.
365: */
366: static int
367: rcsparse_desc(RCSFILE *rfp)
368: {
369: struct rcs_pdata *pdp;
370:
371: if (rfp->rf_flags & PARSED_DESC)
372: return (0);
373:
374: if (!(rfp->rf_flags & PARSED_DELTAS) && rcsparse_deltas(rfp, NULL))
375: return (1);
376:
377: pdp = (struct rcs_pdata *)rfp->rf_pdata;
378:
379: if (rcsparse_token(rfp, RCS_TOK_DESC) != RCS_TOK_DESC ||
380: rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
381: return (1);
382:
383: rfp->rf_desc = pdp->rp_value.str;
384: rfp->rf_flags |= PARSED_DESC;
385:
386: return (0);
387: }
388:
389: /*
390: * rcsparse_deltarevision()
391: *
392: * Called upon reaching a new REVISION entry in the delta section.
393: * A new rcs_delta structure will be prepared in pdp->rp_delta for further
394: * parsing.
395: *
396: * REVISION
397: *
398: * Always returns 0.
399: */
400: static int
401: rcsparse_deltarevision(RCSFILE *rfp, struct rcs_pdata *pdp)
402: {
403: struct rcs_delta *rdp;
404:
405: rdp = xcalloc(1, sizeof(*rdp));
406: TAILQ_INIT(&rdp->rd_branches);
407: rdp->rd_num = pdp->rp_value.rev;
408: pdp->rp_delta = rdp;
409:
410: return (0);
411: }
412:
413: /*
414: * rcsparse_date()
415: *
416: * Parses the specified date of current delta pdp->rp_delta.
417: *
418: * date YYYY.MM.DD.HH.MM.SS;
419: *
420: * Returns 0 on success or 1 on failure.
421: */
422: static int
423: rcsparse_date(RCSFILE *rfp, struct rcs_pdata *pdp)
424: {
425: if (rcsparse_token(rfp, RCS_TYPE_DATE) != RCS_TYPE_DATE)
426: return (1);
427:
428: pdp->rp_delta->rd_date = pdp->rp_value.date;
429:
430: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
431: }
432:
433: /*
434: * rcsparse_author()
435: *
436: * Parses the specified author of current delta pdp->rp_delta.
437: *
438: * author LOGIN;
439: *
440: * Returns 0 on success or 1 on failure.
441: */
442: static int
443: rcsparse_author(RCSFILE *rfp, struct rcs_pdata *pdp)
444: {
445: if (rcsparse_token(rfp, RCS_TYPE_LOGIN) != RCS_TYPE_LOGIN)
446: return (1);
447:
448: pdp->rp_delta->rd_author = pdp->rp_value.str;
449:
450: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
451: }
452:
453: /*
454: * rcsparse_state()
455: *
456: * Parses the specified state of current delta pdp->rp_delta.
457: *
458: * state STATE;
459: *
460: * Returns 0 on success or 1 on failure.
461: */
462: static int
463: rcsparse_state(RCSFILE *rfp, struct rcs_pdata *pdp)
464: {
465: if (rcsparse_token(rfp, RCS_TYPE_STATE) != RCS_TYPE_STATE)
466: return (1);
467:
468: pdp->rp_delta->rd_state = pdp->rp_value.str;
469:
470: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
471: }
472:
473: /*
474: * rcsparse_branches()
475: *
476: * Parses the specified branches of current delta pdp->rp_delta.
477: *
478: * branches [REVISION ...];
479: *
480: * Returns 0 on success or 1 on failure.
481: */
482: static int
483: rcsparse_branches(RCSFILE *rfp, struct rcs_pdata *pdp)
484: {
485: struct rcs_branch *rb;
486: int type;
487:
488: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_REVISION))
489: == RCS_TYPE_REVISION) {
490: rb = xmalloc(sizeof(*rb));
491: rb->rb_num = pdp->rp_value.rev;
492: TAILQ_INSERT_TAIL(&(pdp->rp_delta->rd_branches), rb, rb_list);
493: }
494:
495: return (type != RCS_TOK_SCOLON);
496: }
497:
498: /*
499: * rcsparse_next()
500: *
501: * Parses the specified next revision of current delta pdp->rp_delta.
502: *
503: * next [REVISION];
504: *
505: * Returns 0 on success or 1 on failure.
506: */
507: static int
508: rcsparse_next(RCSFILE *rfp, struct rcs_pdata *pdp)
509: {
510: int type;
511:
512: type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
513: if (type == RCS_TYPE_REVISION) {
514: pdp->rp_delta->rd_next = pdp->rp_value.rev;
515: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
516: } else
517: pdp->rp_delta->rd_next = rcsnum_alloc();
518:
519: return (type != RCS_TOK_SCOLON);
520: }
521:
522: /*
523: * rcsparse_commitid()
524: *
525: * Parses the specified commit id of current delta pdp->rp_delta. The
526: * commitid keyword is optional and can be omitted.
527: *
528: * [commitid ID;]
529: *
530: * Returns 0 on success or 1 on failure.
531: */
532: static int
533: rcsparse_commitid(RCSFILE *rfp, struct rcs_pdata *pdp)
534: {
535: if (rcsparse_token(rfp, RCS_TYPE_COMMITID) != RCS_TYPE_COMMITID)
536: return (1);
537:
538: /* XXX - do something with commitid */
539:
540: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
541: }
542:
543: /*
544: * rcsparse_textrevision()
545: *
546: * Called upon reaching a new REVISION entry in the delta text section.
547: * pdp->rp_delta will be set to REVISION's delta (created in delta section)
548: * for further parsing.
549: *
550: * REVISION
551: *
552: * Returns 0 on success or 1 on failure.
553: */
554: static int
555: rcsparse_textrevision(RCSFILE *rfp, struct rcs_pdata *pdp)
556: {
557: struct rcs_delta *rdp;
558:
559: TAILQ_FOREACH(rdp, &rfp->rf_delta, rd_list) {
560: if (rcsnum_cmp(rdp->rd_num, pdp->rp_value.rev, 0) == 0)
561: break;
562: }
563: if (rdp == NULL) {
564: rcsparse_warnx(rfp, "delta for revision \"%s\" not found",
565: pdp->rp_buf);
566: rcsnum_free(pdp->rp_value.rev);
567: return (1);
568: }
569: pdp->rp_delta = rdp;
570:
571: rcsnum_free(pdp->rp_value.rev);
572: return (0);
573: }
574:
575: /*
576: * rcsparse_log()
577: *
578: * Parses the specified log of current deltatext pdp->rp_delta.
579: *
580: * log @[...]@
581: *
582: * Returns 0 on success or 1 on failure.
583: */
584: static int
585: rcsparse_log(RCSFILE *rfp, struct rcs_pdata *pdp)
586: {
587: if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
588: return (1);
589:
590: pdp->rp_delta->rd_log = pdp->rp_value.str;
591:
592: return (0);
593: }
594:
595: /*
596: * rcsparse_text()
597: *
598: * Parses the specified text of current deltatext pdp->rp_delta.
599: *
600: * text @[...]@
601: *
602: * Returns 0 on success or 1 on failure.
603: */
604: static int
605: rcsparse_text(RCSFILE *rfp, struct rcs_pdata *pdp)
606: {
607: if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
608: return (1);
609:
610: pdp->rp_delta->rd_tlen = pdp->rp_tlen - 1;
611: if (pdp->rp_delta->rd_tlen == 0) {
612: pdp->rp_delta->rd_text = xstrdup("");
613: } else {
614: pdp->rp_delta->rd_text = xmalloc(pdp->rp_delta->rd_tlen);
615: memcpy(pdp->rp_delta->rd_text, pdp->rp_buf,
616: pdp->rp_delta->rd_tlen);
617: }
618: xfree(pdp->rp_value.str);
619:
620: return (0);
621: }
622:
623: /*
624: * rcsparse_head()
625: *
626: * Parses the head revision of RCS file <rfp>.
627: *
628: * head [REVISION];
629: *
630: * Returns 0 on success or 1 on failure.
631: */
632: static int
633: rcsparse_head(RCSFILE *rfp, struct rcs_pdata *pdp)
634: {
635: int type;
636:
637: type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
638: if (type == RCS_TYPE_REVISION) {
639: rfp->rf_head = pdp->rp_value.rev;
640: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
641: }
642:
643: return (type != RCS_TOK_SCOLON);
644: }
645:
646: /*
647: * rcsparse_branch()
648: *
649: * Parses the default branch of RCS file <rfp>. The branch keyword is
650: * optional and can be omitted.
651: *
652: * [branch BRANCH;]
653: *
654: * Returns 0 on success or 1 on failure.
655: */
656: static int
657: rcsparse_branch(RCSFILE *rfp, struct rcs_pdata *pdp)
658: {
659: int type;
660:
661: type = rcsparse_token(rfp, RCS_TYPE_BRANCH|RCS_TOK_SCOLON);
662: if (type == RCS_TYPE_BRANCH) {
663: rfp->rf_branch = pdp->rp_value.rev;
664: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
665: }
666:
667: return (type != RCS_TOK_SCOLON);
668: }
669:
670: /*
671: * rcsparse_access()
672: *
673: * Parses the access list of RCS file <rfp>.
674: *
675: * access [LOGIN ...];
676: *
677: * Returns 0 on success or 1 on failure.
678: */
679: static int
680: rcsparse_access(RCSFILE *rfp, struct rcs_pdata *pdp)
681: {
682: struct rcs_access *ap;
683: int type;
684:
685: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN))
686: == RCS_TYPE_LOGIN) {
687: ap = xmalloc(sizeof(*ap));
688: ap->ra_name = pdp->rp_value.str;
689: TAILQ_INSERT_TAIL(&(rfp->rf_access), ap, ra_list);
690: }
691:
692: return (type != RCS_TOK_SCOLON);
693: }
694:
695: /*
696: * rcsparse_symbols()
697: *
698: * Parses the symbol list of RCS file <rfp>.
699: *
700: * symbols [SYMBOL:REVISION ...];
701: *
702: * Returns 0 on success or 1 on failure.
703: */
704: static int
705: rcsparse_symbols(RCSFILE *rfp, struct rcs_pdata *pdp)
706: {
707: struct rcs_sym *symp;
708: char *name;
709: int type;
710:
711: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
712: RCS_TYPE_SYMBOL) {
713: name = pdp->rp_value.str;
714: if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
715: rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
716: xfree(name);
717: return (1);
718: }
719: symp = xmalloc(sizeof(*symp));
720: symp->rs_name = name;
721: symp->rs_num = pdp->rp_value.rev;
722: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
723: }
724:
725: return (type != RCS_TOK_SCOLON);
726: }
727:
728: /*
729: * rcsparse_locks()
730: *
731: * Parses the lock list of RCS file <rfp>.
732: *
733: * locks [SYMBOL:REVISION ...];
734: *
735: * Returns 0 on success or 1 on failure.
736: */
737: static int
738: rcsparse_locks(RCSFILE *rfp, struct rcs_pdata *pdp)
739: {
740: struct rcs_lock *lkp;
741: char *name;
742: int type;
743:
744: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN)) ==
745: RCS_TYPE_LOGIN) {
746: name = pdp->rp_value.str;
747: if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
748: rcsparse_token(rfp, RCS_TYPE_REVISION) !=
749: RCS_TYPE_REVISION) {
750: xfree(name);
751: return (1);
752: }
753: lkp = xmalloc(sizeof(*lkp));
754: lkp->rl_name = name;
755: lkp->rl_num = pdp->rp_value.rev;
756: TAILQ_INSERT_TAIL(&(rfp->rf_locks), lkp, rl_list);
757: }
758:
759: return (type != RCS_TOK_SCOLON);
760: }
761:
762: /*
763: * rcsparse_locks()
764: *
765: * Parses the strict keyword of RCS file <rfp>. The strict keyword is
766: * optional and can be omitted.
767: *
768: * [strict;]
769: *
770: * Returns 0 on success or 1 on failure.
771: */
772: static int
773: rcsparse_strict(RCSFILE *rfp, struct rcs_pdata *pdp)
774: {
775: rfp->rf_flags |= RCS_SLOCK;
776:
777: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
778: }
779:
780: /*
781: * rcsparse_comment()
782: *
783: * Parses the comment of RCS file <rfp>. The comment keyword is optional
784: * and can be omitted.
785: *
786: * [comment [@[...]@];]
787: *
788: * Returns 0 on success or 1 on failure.
789: */
790: static int
791: rcsparse_comment(RCSFILE *rfp, struct rcs_pdata *pdp)
792: {
793: int type;
794:
795: type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
796: if (type == RCS_TYPE_STRING) {
797: rfp->rf_comment = pdp->rp_value.str;
798: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
799: }
800:
801: return (type != RCS_TOK_SCOLON);
802: }
803:
804: /*
805: * rcsparse_expand()
806: *
807: * Parses expand of RCS file <rfp>. The expand keyword is optional and
808: * can be omitted.
809: *
810: * [expand [@[...]@];]
811: *
812: * Returns 0 on success or 1 on failure.
813: */
814: static int
815: rcsparse_expand(RCSFILE *rfp, struct rcs_pdata *pdp)
816: {
817: int type;
818:
819: type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
820: if (type == RCS_TYPE_STRING) {
821: rfp->rf_expand = pdp->rp_value.str;
822: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
823: }
824:
825: return (type != RCS_TOK_SCOLON);
826: }
827:
828: #define RBUF_PUTC(ch) \
829: do { \
830: if (bp == pdp->rp_bufend - 1) { \
831: len = bp - pdp->rp_buf; \
832: rcsparse_growbuf(rfp); \
833: bp = pdp->rp_buf + len; \
834: } \
835: *(bp++) = (ch); \
836: pdp->rp_tlen++; \
837: } while (0);
838:
839: static int
840: rcsparse_string(RCSFILE *rfp, int allowed)
841: {
842: struct rcs_pdata *pdp;
843: int c;
844: size_t len;
845: char *bp;
846:
847: pdp = (struct rcs_pdata *)rfp->rf_pdata;
848:
849: bp = pdp->rp_buf;
850: pdp->rp_tlen = 0;
851: *bp = '\0';
852:
853: for (;;) {
854: c = getc(pdp->rp_file);
855: if (c == '@') {
856: c = getc(pdp->rp_file);
857: if (c == EOF) {
858: return (EOF);
859: } else if (c != '@') {
860: ungetc(c, pdp->rp_file);
861: break;
862: }
863: }
864:
865: if (c == EOF) {
866: return (EOF);
867: } else if (c == '\n')
868: pdp->rp_lineno++;
869:
870: RBUF_PUTC(c);
871: }
872:
873: bp = pdp->rp_buf + pdp->rp_tlen;
874: RBUF_PUTC('\0');
875:
876: if (!(allowed & RCS_TYPE_STRING)) {
877: rcsparse_warnx(rfp, "unexpected RCS string");
878: return (0);
879: }
880:
881: pdp->rp_value.str = xstrdup(pdp->rp_buf);
882:
883: return (RCS_TYPE_STRING);
884: }
885:
886: static int
887: rcsparse_token(RCSFILE *rfp, int allowed)
888: {
889: const struct rcs_keyword *p;
890: struct rcs_pdata *pdp;
891: int c, pre, ret, type;
892: char *bp;
893: size_t len;
894: RCSNUM *datenum;
895:
896: pdp = (struct rcs_pdata *)rfp->rf_pdata;
897:
898: if (pdp->rp_token != -1) {
899: /* no need to check for allowed here */
900: type = pdp->rp_token;
901: pdp->rp_token = -1;
902: return (type);
903: }
904:
905: /* skip whitespaces */
906: c = EOF;
907: do {
908: pre = c;
909: c = getc(pdp->rp_file);
910: if (c == EOF) {
911: if (ferror(pdp->rp_file)) {
912: rcsparse_warnx(rfp, "error during parsing");
913: return (0);
914: }
915: if (pre != '\n')
916: rcsparse_warnx(rfp,
917: "no newline at end of file");
918: return (EOF);
919: } else if (c == '\n')
920: pdp->rp_lineno++;
921: } while (isspace(c));
922:
923: pdp->rp_msglineno = pdp->rp_lineno;
924: type = 0;
925: switch (c) {
926: case '@':
927: ret = rcsparse_string(rfp, allowed);
928: if (ret == EOF && ferror(pdp->rp_file)) {
929: rcsparse_warnx(rfp, "error during parsing");
930: return (0);
931: }
932: return (ret);
933: /* NOTREACHED */
934: case ':':
935: type = RCS_TOK_COLON;
936: if (type & allowed)
937: return (type);
938: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
939: return (0);
940: /* NOTREACHED */
941: case ';':
942: type = RCS_TOK_SCOLON;
943: if (type & allowed)
944: return (type);
945: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
946: return (0);
947: /* NOTREACHED */
948: case ',':
949: type = RCS_TOK_COMMA;
950: if (type & allowed)
951: return (type);
952: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
953: return (0);
954: /* NOTREACHED */
955: default:
956: if (!isgraph(c)) {
957: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
958: return (0);
959: }
960: break;
961: }
962: allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
963:
964: bp = pdp->rp_buf;
965: pdp->rp_tlen = 0;
966: *bp = '\0';
967:
968: for (;;) {
969: if (c == EOF) {
970: if (ferror(pdp->rp_file))
971: rcsparse_warnx(rfp, "error during parsing");
972: else
973: rcsparse_warnx(rfp, "unexpected end of file");
974: return (0);
975: } else if (c == '\n')
976: pdp->rp_lineno++;
977:
978: RBUF_PUTC(c);
979:
980: c = getc(pdp->rp_file);
981:
982: if (isspace(c)) {
983: if (c == '\n')
984: pdp->rp_lineno++;
985: RBUF_PUTC('\0');
986: break;
987: } else if (c == ';' || c == ':' || c == ',') {
988: ungetc(c, pdp->rp_file);
989: RBUF_PUTC('\0');
990: break;
991: } else if (!isgraph(c)) {
992: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
993: return (0);
994: }
995: }
996:
997: switch (allowed) {
998: case RCS_TYPE_COMMITID:
999: /* XXX validate commitd */
1000: break;
1001: case RCS_TYPE_LOGIN:
1002: if (!valid_login(pdp->rp_buf)) {
1003: rcsparse_warnx(rfp, "invalid login \"%s\"",
1004: pdp->rp_buf);
1005: return (0);
1006: }
1007: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1008: break;
1009: case RCS_TYPE_SYMBOL:
1010: if (!rcs_sym_check(pdp->rp_buf)) {
1011: rcsparse_warnx(rfp, "invalid symbol \"%s\"",
1012: pdp->rp_buf);
1013: return (0);
1014: }
1015: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1016: break;
1017: /* FALLTHROUGH */
1018: case RCS_TYPE_STATE:
1019: if (rcs_state_check(pdp->rp_buf)) {
1020: rcsparse_warnx(rfp, "invalid state \"%s\"",
1021: pdp->rp_buf);
1022: return (0);
1023: }
1024: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1025: break;
1026: case RCS_TYPE_DATE:
1027: if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
1028: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1029: return (0);
1030: }
1031: if (datenum->rn_len != 6) {
1032: rcsnum_free(datenum);
1033: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1034: return (0);
1035: }
1036: pdp->rp_value.date.tm_year = datenum->rn_id[0];
1037: if (pdp->rp_value.date.tm_year >= 1900)
1038: pdp->rp_value.date.tm_year -= 1900;
1039: pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
1040: pdp->rp_value.date.tm_mday = datenum->rn_id[2];
1041: pdp->rp_value.date.tm_hour = datenum->rn_id[3];
1042: pdp->rp_value.date.tm_min = datenum->rn_id[4];
1043: pdp->rp_value.date.tm_sec = datenum->rn_id[5];
1044: rcsnum_free(datenum);
1045: break;
1046: case RCS_TYPE_NUMBER:
1047: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1048: if (pdp->rp_value.rev == NULL) {
1049: rcsparse_warnx(rfp, "invalid number \"%s\"",
1050: pdp->rp_buf);
1051: return (0);
1052: }
1053: break;
1054: case RCS_TYPE_BRANCH:
1055: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1056: if (pdp->rp_value.rev == NULL) {
1057: rcsparse_warnx(rfp, "invalid branch \"%s\"",
1058: pdp->rp_buf);
1059: return (0);
1060: }
1061: if (!RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1062: rcsnum_free(pdp->rp_value.rev);
1063: rcsparse_warnx(rfp, "expected branch, got \"%s\"",
1064: pdp->rp_buf);
1065: return (0);
1066: }
1067: break;
1068: case RCS_TYPE_KEYWORD:
1069: if (islower(*pdp->rp_buf)) {
1070: p = bsearch(pdp->rp_buf, keywords,
1071: sizeof(keywords) / sizeof(keywords[0]),
1072: sizeof(keywords[0]), kw_cmp);
1073: if (p != NULL)
1074: return (p->k_val);
1075: }
1076: allowed = RCS_TYPE_REVISION;
1077: /* FALLTHROUGH */
1078: case RCS_TYPE_REVISION:
1079: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1080: if (pdp->rp_value.rev != NULL) {
1081: if (RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1082: rcsnum_free(pdp->rp_value.rev);
1083: rcsparse_warnx(rfp,
1084: "expected revision, got \"%s\"",
1085: pdp->rp_buf);
1086: return (0);
1087: }
1088: break;
1089: }
1090: /* FALLTHROUGH */
1091: default:
1092: RBUF_PUTC('\0');
1093: rcsparse_warnx(rfp, "unexpected token \"%s\"", pdp->rp_buf);
1094: return (0);
1095: /* NOTREACHED */
1096: }
1097:
1098: return (allowed);
1099: }
1100:
1101: static int
1102: rcsparse(RCSFILE *rfp, struct rcs_section *sec)
1103: {
1104: struct rcs_pdata *pdp;
1105: int i, token;
1106:
1107: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1108: i = 0;
1109:
1110: token = 0;
1111: for (i = 0; sec[i].token != 0; i++) {
1112: token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
1113: if (token == 0)
1114: return (1);
1115:
1116: while (token != sec[i].token) {
1117: if (sec[i].parse == NULL)
1118: goto end;
1119: if (sec[i].opt) {
1120: i++;
1121: continue;
1122: }
1123: if (token == EOF || (!(rfp->rf_flags & PARSED_DELTAS) &&
1124: token == RCS_TOK_DESC))
1125: goto end;
1126: rcsparse_warnx(rfp, "unexpected token \"%s\"",
1127: pdp->rp_buf);
1128: return (1);
1129: }
1130:
1131: if (sec[i].parse(rfp, pdp))
1132: return (1);
1133: }
1134: end:
1135: if (token == RCS_TYPE_REVISION)
1136: pdp->rp_token = token;
1137: else if (token == RCS_TOK_DESC)
1138: pdp->rp_token = RCS_TOK_DESC;
1139: else if (token == EOF)
1140: rfp->rf_flags |= RCS_PARSED;
1141:
1142: return (0);
1143: }
1144:
1145: static int
1146: rcsparse_deltatext(RCSFILE *rfp)
1147: {
1148: struct rcs_pdata *pdp;
1149: int ret;
1150:
1151: if (rfp->rf_flags & PARSED_DELTATEXTS)
1152: return (0);
1153:
1154: if (!(rfp->rf_flags & PARSED_DESC))
1155: if ((ret = rcsparse_desc(rfp)))
1156: return (ret);
1157:
1158: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1159:
1160: if (rcsparse(rfp, sec_deltatext))
1161: return (-1);
1162:
1.2 ! tobias 1163: if (rfp->rf_flags & RCS_PARSED)
1.1 tobias 1164: rfp->rf_flags |= PARSED_DELTATEXTS;
1165:
1166: return (1);
1167: }
1168:
1169: static int
1170: rcsparse_delta(RCSFILE *rfp)
1171: {
1172: struct rcs_pdata *pdp;
1173:
1174: if (rfp->rf_flags & PARSED_DELTAS)
1175: return (0);
1176:
1177: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1178: if (pdp->rp_token == RCS_TOK_DESC) {
1179: rfp->rf_flags |= PARSED_DELTAS;
1180: return (0);
1181: }
1182:
1183: if (rcsparse(rfp, sec_delta))
1184: return (-1);
1185:
1186: if (pdp->rp_delta != NULL) {
1187: TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
1188: pdp->rp_delta = NULL;
1189: rfp->rf_ndelta++;
1190: return (1);
1191: }
1192:
1193: return (0);
1194: }
1195:
1196: /*
1197: * rcsparse_growbuf()
1198: *
1199: * Attempt to grow the internal parse buffer for the RCS file <rf> by
1200: * RCS_BUFEXTSIZE.
1201: * In case of failure, the original buffer is left unmodified.
1202: */
1203: static void
1204: rcsparse_growbuf(RCSFILE *rfp)
1205: {
1206: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1207:
1208: pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
1209: pdp->rp_blen + RCS_BUFEXTSIZE);
1210: pdp->rp_blen += RCS_BUFEXTSIZE;
1211: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1212: }
1213:
1214: /*
1215: * Borrowed from src/usr.sbin/user/user.c:
1216: * return 1 if `login' is a valid login name
1217: */
1218: static int
1219: valid_login(char *login_name)
1220: {
1221: unsigned char *cp;
1222:
1223: /* The first character cannot be a hyphen */
1224: if (*login_name == '-')
1225: return 0;
1226:
1227: for (cp = login_name ; *cp ; cp++) {
1228: /* We allow '$' as the last character for samba */
1229: if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
1230: !(*cp == '$' && *(cp + 1) == '\0')) {
1231: return 0;
1232: }
1233: }
1234: if ((char *)cp - login_name > _PW_NAME_LEN)
1235: return 0;
1236: return 1;
1237: }
1238:
1239: static int
1240: kw_cmp(const void *k, const void *e)
1241: {
1242: return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
1243: }
1244:
1245: static void
1246: rcsparse_warnx(RCSFILE *rfp, char *fmt, ...)
1247: {
1248: struct rcs_pdata *pdp;
1249: va_list ap;
1250: char *nfmt;
1251:
1252: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1253: va_start(ap, fmt);
1254: if (asprintf(&nfmt, "%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, fmt)
1255: == -1)
1256: nfmt = fmt;
1257: vwarnx(nfmt, ap);
1258: va_end(ap);
1259: if (nfmt != fmt)
1260: free(nfmt);
1261: }