Annotation of src/usr.bin/rcs/rcsparse.c, Revision 1.3
1.3 ! tobias 1: /* $OpenBSD: rcsparse.c,v 1.2 2010/10/15 09:58:25 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);
1.3 ! tobias 315:
! 316: rdp = (rev != NULL) ? rcs_findrev(rfp, rev) : NULL;
! 317:
1.1 tobias 318: for (;;) {
1.3 ! tobias 319: if (rdp != NULL && rdp->rd_text != NULL)
! 320: break;
! 321: ret = rcsparse_deltatext(rfp);
1.1 tobias 322: if (ret == 0) {
323: rfp->rf_flags |= PARSED_DELTATEXTS;
324: break;
325: }
326: else if (ret == -1)
327: return (1);
328: }
329:
330: return (0);
331: }
332:
333: /*
334: * rcsparse_free()
335: *
336: * Free the contents of the <rfp>'s parser data structure.
337: */
338: void
339: rcsparse_free(RCSFILE *rfp)
340: {
341: struct rcs_pdata *pdp;
342:
343: pdp = rfp->rf_pdata;
344:
345: if (pdp->rp_file != NULL)
346: (void)fclose(pdp->rp_file);
347: if (pdp->rp_buf != NULL)
348: xfree(pdp->rp_buf);
349: if (pdp->rp_token == RCS_TYPE_REVISION)
350: rcsnum_free(pdp->rp_value.rev);
351: xfree(pdp);
352: }
353:
354: /*
355: * rcsparse_desc()
356: *
357: * Parse desc of the RCS file <rfp>. By calling rcsparse_desc, all deltas
358: * will be parsed in order to proceed the reading cursor to the desc keyword.
359: *
360: * desc @[...]@;
361: *
362: * Returns 0 on success or 1 on error.
363: */
364: static int
365: rcsparse_desc(RCSFILE *rfp)
366: {
367: struct rcs_pdata *pdp;
368:
369: if (rfp->rf_flags & PARSED_DESC)
370: return (0);
371:
372: if (!(rfp->rf_flags & PARSED_DELTAS) && rcsparse_deltas(rfp, NULL))
373: return (1);
374:
375: pdp = (struct rcs_pdata *)rfp->rf_pdata;
376:
377: if (rcsparse_token(rfp, RCS_TOK_DESC) != RCS_TOK_DESC ||
378: rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
379: return (1);
380:
381: rfp->rf_desc = pdp->rp_value.str;
382: rfp->rf_flags |= PARSED_DESC;
383:
384: return (0);
385: }
386:
387: /*
388: * rcsparse_deltarevision()
389: *
390: * Called upon reaching a new REVISION entry in the delta section.
391: * A new rcs_delta structure will be prepared in pdp->rp_delta for further
392: * parsing.
393: *
394: * REVISION
395: *
396: * Always returns 0.
397: */
398: static int
399: rcsparse_deltarevision(RCSFILE *rfp, struct rcs_pdata *pdp)
400: {
401: struct rcs_delta *rdp;
402:
403: rdp = xcalloc(1, sizeof(*rdp));
404: TAILQ_INIT(&rdp->rd_branches);
405: rdp->rd_num = pdp->rp_value.rev;
406: pdp->rp_delta = rdp;
407:
408: return (0);
409: }
410:
411: /*
412: * rcsparse_date()
413: *
414: * Parses the specified date of current delta pdp->rp_delta.
415: *
416: * date YYYY.MM.DD.HH.MM.SS;
417: *
418: * Returns 0 on success or 1 on failure.
419: */
420: static int
421: rcsparse_date(RCSFILE *rfp, struct rcs_pdata *pdp)
422: {
423: if (rcsparse_token(rfp, RCS_TYPE_DATE) != RCS_TYPE_DATE)
424: return (1);
425:
426: pdp->rp_delta->rd_date = pdp->rp_value.date;
427:
428: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
429: }
430:
431: /*
432: * rcsparse_author()
433: *
434: * Parses the specified author of current delta pdp->rp_delta.
435: *
436: * author LOGIN;
437: *
438: * Returns 0 on success or 1 on failure.
439: */
440: static int
441: rcsparse_author(RCSFILE *rfp, struct rcs_pdata *pdp)
442: {
443: if (rcsparse_token(rfp, RCS_TYPE_LOGIN) != RCS_TYPE_LOGIN)
444: return (1);
445:
446: pdp->rp_delta->rd_author = pdp->rp_value.str;
447:
448: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
449: }
450:
451: /*
452: * rcsparse_state()
453: *
454: * Parses the specified state of current delta pdp->rp_delta.
455: *
456: * state STATE;
457: *
458: * Returns 0 on success or 1 on failure.
459: */
460: static int
461: rcsparse_state(RCSFILE *rfp, struct rcs_pdata *pdp)
462: {
463: if (rcsparse_token(rfp, RCS_TYPE_STATE) != RCS_TYPE_STATE)
464: return (1);
465:
466: pdp->rp_delta->rd_state = pdp->rp_value.str;
467:
468: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
469: }
470:
471: /*
472: * rcsparse_branches()
473: *
474: * Parses the specified branches of current delta pdp->rp_delta.
475: *
476: * branches [REVISION ...];
477: *
478: * Returns 0 on success or 1 on failure.
479: */
480: static int
481: rcsparse_branches(RCSFILE *rfp, struct rcs_pdata *pdp)
482: {
483: struct rcs_branch *rb;
484: int type;
485:
486: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_REVISION))
487: == RCS_TYPE_REVISION) {
488: rb = xmalloc(sizeof(*rb));
489: rb->rb_num = pdp->rp_value.rev;
490: TAILQ_INSERT_TAIL(&(pdp->rp_delta->rd_branches), rb, rb_list);
491: }
492:
493: return (type != RCS_TOK_SCOLON);
494: }
495:
496: /*
497: * rcsparse_next()
498: *
499: * Parses the specified next revision of current delta pdp->rp_delta.
500: *
501: * next [REVISION];
502: *
503: * Returns 0 on success or 1 on failure.
504: */
505: static int
506: rcsparse_next(RCSFILE *rfp, struct rcs_pdata *pdp)
507: {
508: int type;
509:
510: type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
511: if (type == RCS_TYPE_REVISION) {
512: pdp->rp_delta->rd_next = pdp->rp_value.rev;
513: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
514: } else
515: pdp->rp_delta->rd_next = rcsnum_alloc();
516:
517: return (type != RCS_TOK_SCOLON);
518: }
519:
520: /*
521: * rcsparse_commitid()
522: *
523: * Parses the specified commit id of current delta pdp->rp_delta. The
524: * commitid keyword is optional and can be omitted.
525: *
526: * [commitid ID;]
527: *
528: * Returns 0 on success or 1 on failure.
529: */
530: static int
531: rcsparse_commitid(RCSFILE *rfp, struct rcs_pdata *pdp)
532: {
533: if (rcsparse_token(rfp, RCS_TYPE_COMMITID) != RCS_TYPE_COMMITID)
534: return (1);
535:
536: /* XXX - do something with commitid */
537:
538: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
539: }
540:
541: /*
542: * rcsparse_textrevision()
543: *
544: * Called upon reaching a new REVISION entry in the delta text section.
545: * pdp->rp_delta will be set to REVISION's delta (created in delta section)
546: * for further parsing.
547: *
548: * REVISION
549: *
550: * Returns 0 on success or 1 on failure.
551: */
552: static int
553: rcsparse_textrevision(RCSFILE *rfp, struct rcs_pdata *pdp)
554: {
555: struct rcs_delta *rdp;
556:
557: TAILQ_FOREACH(rdp, &rfp->rf_delta, rd_list) {
558: if (rcsnum_cmp(rdp->rd_num, pdp->rp_value.rev, 0) == 0)
559: break;
560: }
561: if (rdp == NULL) {
562: rcsparse_warnx(rfp, "delta for revision \"%s\" not found",
563: pdp->rp_buf);
564: rcsnum_free(pdp->rp_value.rev);
565: return (1);
566: }
567: pdp->rp_delta = rdp;
568:
569: rcsnum_free(pdp->rp_value.rev);
570: return (0);
571: }
572:
573: /*
574: * rcsparse_log()
575: *
576: * Parses the specified log of current deltatext pdp->rp_delta.
577: *
578: * log @[...]@
579: *
580: * Returns 0 on success or 1 on failure.
581: */
582: static int
583: rcsparse_log(RCSFILE *rfp, struct rcs_pdata *pdp)
584: {
585: if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
586: return (1);
587:
588: pdp->rp_delta->rd_log = pdp->rp_value.str;
589:
590: return (0);
591: }
592:
593: /*
594: * rcsparse_text()
595: *
596: * Parses the specified text of current deltatext pdp->rp_delta.
597: *
598: * text @[...]@
599: *
600: * Returns 0 on success or 1 on failure.
601: */
602: static int
603: rcsparse_text(RCSFILE *rfp, struct rcs_pdata *pdp)
604: {
605: if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
606: return (1);
607:
608: pdp->rp_delta->rd_tlen = pdp->rp_tlen - 1;
609: if (pdp->rp_delta->rd_tlen == 0) {
610: pdp->rp_delta->rd_text = xstrdup("");
611: } else {
612: pdp->rp_delta->rd_text = xmalloc(pdp->rp_delta->rd_tlen);
613: memcpy(pdp->rp_delta->rd_text, pdp->rp_buf,
614: pdp->rp_delta->rd_tlen);
615: }
616: xfree(pdp->rp_value.str);
617:
618: return (0);
619: }
620:
621: /*
622: * rcsparse_head()
623: *
624: * Parses the head revision of RCS file <rfp>.
625: *
626: * head [REVISION];
627: *
628: * Returns 0 on success or 1 on failure.
629: */
630: static int
631: rcsparse_head(RCSFILE *rfp, struct rcs_pdata *pdp)
632: {
633: int type;
634:
635: type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
636: if (type == RCS_TYPE_REVISION) {
637: rfp->rf_head = pdp->rp_value.rev;
638: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
639: }
640:
641: return (type != RCS_TOK_SCOLON);
642: }
643:
644: /*
645: * rcsparse_branch()
646: *
647: * Parses the default branch of RCS file <rfp>. The branch keyword is
648: * optional and can be omitted.
649: *
650: * [branch BRANCH;]
651: *
652: * Returns 0 on success or 1 on failure.
653: */
654: static int
655: rcsparse_branch(RCSFILE *rfp, struct rcs_pdata *pdp)
656: {
657: int type;
658:
659: type = rcsparse_token(rfp, RCS_TYPE_BRANCH|RCS_TOK_SCOLON);
660: if (type == RCS_TYPE_BRANCH) {
661: rfp->rf_branch = pdp->rp_value.rev;
662: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
663: }
664:
665: return (type != RCS_TOK_SCOLON);
666: }
667:
668: /*
669: * rcsparse_access()
670: *
671: * Parses the access list of RCS file <rfp>.
672: *
673: * access [LOGIN ...];
674: *
675: * Returns 0 on success or 1 on failure.
676: */
677: static int
678: rcsparse_access(RCSFILE *rfp, struct rcs_pdata *pdp)
679: {
680: struct rcs_access *ap;
681: int type;
682:
683: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN))
684: == RCS_TYPE_LOGIN) {
685: ap = xmalloc(sizeof(*ap));
686: ap->ra_name = pdp->rp_value.str;
687: TAILQ_INSERT_TAIL(&(rfp->rf_access), ap, ra_list);
688: }
689:
690: return (type != RCS_TOK_SCOLON);
691: }
692:
693: /*
694: * rcsparse_symbols()
695: *
696: * Parses the symbol list of RCS file <rfp>.
697: *
698: * symbols [SYMBOL:REVISION ...];
699: *
700: * Returns 0 on success or 1 on failure.
701: */
702: static int
703: rcsparse_symbols(RCSFILE *rfp, struct rcs_pdata *pdp)
704: {
705: struct rcs_sym *symp;
706: char *name;
707: int type;
708:
709: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
710: RCS_TYPE_SYMBOL) {
711: name = pdp->rp_value.str;
712: if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
713: rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
714: xfree(name);
715: return (1);
716: }
717: symp = xmalloc(sizeof(*symp));
718: symp->rs_name = name;
719: symp->rs_num = pdp->rp_value.rev;
720: TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
721: }
722:
723: return (type != RCS_TOK_SCOLON);
724: }
725:
726: /*
727: * rcsparse_locks()
728: *
729: * Parses the lock list of RCS file <rfp>.
730: *
731: * locks [SYMBOL:REVISION ...];
732: *
733: * Returns 0 on success or 1 on failure.
734: */
735: static int
736: rcsparse_locks(RCSFILE *rfp, struct rcs_pdata *pdp)
737: {
738: struct rcs_lock *lkp;
739: char *name;
740: int type;
741:
742: while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN)) ==
743: RCS_TYPE_LOGIN) {
744: name = pdp->rp_value.str;
745: if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
746: rcsparse_token(rfp, RCS_TYPE_REVISION) !=
747: RCS_TYPE_REVISION) {
748: xfree(name);
749: return (1);
750: }
751: lkp = xmalloc(sizeof(*lkp));
752: lkp->rl_name = name;
753: lkp->rl_num = pdp->rp_value.rev;
754: TAILQ_INSERT_TAIL(&(rfp->rf_locks), lkp, rl_list);
755: }
756:
757: return (type != RCS_TOK_SCOLON);
758: }
759:
760: /*
761: * rcsparse_locks()
762: *
763: * Parses the strict keyword of RCS file <rfp>. The strict keyword is
764: * optional and can be omitted.
765: *
766: * [strict;]
767: *
768: * Returns 0 on success or 1 on failure.
769: */
770: static int
771: rcsparse_strict(RCSFILE *rfp, struct rcs_pdata *pdp)
772: {
773: rfp->rf_flags |= RCS_SLOCK;
774:
775: return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
776: }
777:
778: /*
779: * rcsparse_comment()
780: *
781: * Parses the comment of RCS file <rfp>. The comment keyword is optional
782: * and can be omitted.
783: *
784: * [comment [@[...]@];]
785: *
786: * Returns 0 on success or 1 on failure.
787: */
788: static int
789: rcsparse_comment(RCSFILE *rfp, struct rcs_pdata *pdp)
790: {
791: int type;
792:
793: type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
794: if (type == RCS_TYPE_STRING) {
795: rfp->rf_comment = pdp->rp_value.str;
796: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
797: }
798:
799: return (type != RCS_TOK_SCOLON);
800: }
801:
802: /*
803: * rcsparse_expand()
804: *
805: * Parses expand of RCS file <rfp>. The expand keyword is optional and
806: * can be omitted.
807: *
808: * [expand [@[...]@];]
809: *
810: * Returns 0 on success or 1 on failure.
811: */
812: static int
813: rcsparse_expand(RCSFILE *rfp, struct rcs_pdata *pdp)
814: {
815: int type;
816:
817: type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
818: if (type == RCS_TYPE_STRING) {
819: rfp->rf_expand = pdp->rp_value.str;
820: type = rcsparse_token(rfp, RCS_TOK_SCOLON);
821: }
822:
823: return (type != RCS_TOK_SCOLON);
824: }
825:
826: #define RBUF_PUTC(ch) \
827: do { \
828: if (bp == pdp->rp_bufend - 1) { \
829: len = bp - pdp->rp_buf; \
830: rcsparse_growbuf(rfp); \
831: bp = pdp->rp_buf + len; \
832: } \
833: *(bp++) = (ch); \
834: pdp->rp_tlen++; \
835: } while (0);
836:
837: static int
838: rcsparse_string(RCSFILE *rfp, int allowed)
839: {
840: struct rcs_pdata *pdp;
841: int c;
842: size_t len;
843: char *bp;
844:
845: pdp = (struct rcs_pdata *)rfp->rf_pdata;
846:
847: bp = pdp->rp_buf;
848: pdp->rp_tlen = 0;
849: *bp = '\0';
850:
851: for (;;) {
852: c = getc(pdp->rp_file);
853: if (c == '@') {
854: c = getc(pdp->rp_file);
855: if (c == EOF) {
856: return (EOF);
857: } else if (c != '@') {
858: ungetc(c, pdp->rp_file);
859: break;
860: }
861: }
862:
863: if (c == EOF) {
864: return (EOF);
865: } else if (c == '\n')
866: pdp->rp_lineno++;
867:
868: RBUF_PUTC(c);
869: }
870:
871: bp = pdp->rp_buf + pdp->rp_tlen;
872: RBUF_PUTC('\0');
873:
874: if (!(allowed & RCS_TYPE_STRING)) {
875: rcsparse_warnx(rfp, "unexpected RCS string");
876: return (0);
877: }
878:
879: pdp->rp_value.str = xstrdup(pdp->rp_buf);
880:
881: return (RCS_TYPE_STRING);
882: }
883:
884: static int
885: rcsparse_token(RCSFILE *rfp, int allowed)
886: {
887: const struct rcs_keyword *p;
888: struct rcs_pdata *pdp;
889: int c, pre, ret, type;
890: char *bp;
891: size_t len;
892: RCSNUM *datenum;
893:
894: pdp = (struct rcs_pdata *)rfp->rf_pdata;
895:
896: if (pdp->rp_token != -1) {
897: /* no need to check for allowed here */
898: type = pdp->rp_token;
899: pdp->rp_token = -1;
900: return (type);
901: }
902:
903: /* skip whitespaces */
904: c = EOF;
905: do {
906: pre = c;
907: c = getc(pdp->rp_file);
908: if (c == EOF) {
909: if (ferror(pdp->rp_file)) {
910: rcsparse_warnx(rfp, "error during parsing");
911: return (0);
912: }
913: if (pre != '\n')
914: rcsparse_warnx(rfp,
915: "no newline at end of file");
916: return (EOF);
917: } else if (c == '\n')
918: pdp->rp_lineno++;
919: } while (isspace(c));
920:
921: pdp->rp_msglineno = pdp->rp_lineno;
922: type = 0;
923: switch (c) {
924: case '@':
925: ret = rcsparse_string(rfp, allowed);
926: if (ret == EOF && ferror(pdp->rp_file)) {
927: rcsparse_warnx(rfp, "error during parsing");
928: return (0);
929: }
930: return (ret);
931: /* NOTREACHED */
932: case ':':
933: type = RCS_TOK_COLON;
934: if (type & allowed)
935: return (type);
936: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
937: return (0);
938: /* NOTREACHED */
939: case ';':
940: type = RCS_TOK_SCOLON;
941: if (type & allowed)
942: return (type);
943: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
944: return (0);
945: /* NOTREACHED */
946: case ',':
947: type = RCS_TOK_COMMA;
948: if (type & allowed)
949: return (type);
950: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
951: return (0);
952: /* NOTREACHED */
953: default:
954: if (!isgraph(c)) {
955: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
956: return (0);
957: }
958: break;
959: }
960: allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
961:
962: bp = pdp->rp_buf;
963: pdp->rp_tlen = 0;
964: *bp = '\0';
965:
966: for (;;) {
967: if (c == EOF) {
968: if (ferror(pdp->rp_file))
969: rcsparse_warnx(rfp, "error during parsing");
970: else
971: rcsparse_warnx(rfp, "unexpected end of file");
972: return (0);
973: } else if (c == '\n')
974: pdp->rp_lineno++;
975:
976: RBUF_PUTC(c);
977:
978: c = getc(pdp->rp_file);
979:
980: if (isspace(c)) {
981: if (c == '\n')
982: pdp->rp_lineno++;
983: RBUF_PUTC('\0');
984: break;
985: } else if (c == ';' || c == ':' || c == ',') {
986: ungetc(c, pdp->rp_file);
987: RBUF_PUTC('\0');
988: break;
989: } else if (!isgraph(c)) {
990: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
991: return (0);
992: }
993: }
994:
995: switch (allowed) {
996: case RCS_TYPE_COMMITID:
997: /* XXX validate commitd */
998: break;
999: case RCS_TYPE_LOGIN:
1000: if (!valid_login(pdp->rp_buf)) {
1001: rcsparse_warnx(rfp, "invalid login \"%s\"",
1002: pdp->rp_buf);
1003: return (0);
1004: }
1005: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1006: break;
1007: case RCS_TYPE_SYMBOL:
1008: if (!rcs_sym_check(pdp->rp_buf)) {
1009: rcsparse_warnx(rfp, "invalid symbol \"%s\"",
1010: pdp->rp_buf);
1011: return (0);
1012: }
1013: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1014: break;
1015: /* FALLTHROUGH */
1016: case RCS_TYPE_STATE:
1017: if (rcs_state_check(pdp->rp_buf)) {
1018: rcsparse_warnx(rfp, "invalid state \"%s\"",
1019: pdp->rp_buf);
1020: return (0);
1021: }
1022: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1023: break;
1024: case RCS_TYPE_DATE:
1025: if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
1026: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1027: return (0);
1028: }
1029: if (datenum->rn_len != 6) {
1030: rcsnum_free(datenum);
1031: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1032: return (0);
1033: }
1034: pdp->rp_value.date.tm_year = datenum->rn_id[0];
1035: if (pdp->rp_value.date.tm_year >= 1900)
1036: pdp->rp_value.date.tm_year -= 1900;
1037: pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
1038: pdp->rp_value.date.tm_mday = datenum->rn_id[2];
1039: pdp->rp_value.date.tm_hour = datenum->rn_id[3];
1040: pdp->rp_value.date.tm_min = datenum->rn_id[4];
1041: pdp->rp_value.date.tm_sec = datenum->rn_id[5];
1042: rcsnum_free(datenum);
1043: break;
1044: case RCS_TYPE_NUMBER:
1045: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1046: if (pdp->rp_value.rev == NULL) {
1047: rcsparse_warnx(rfp, "invalid number \"%s\"",
1048: pdp->rp_buf);
1049: return (0);
1050: }
1051: break;
1052: case RCS_TYPE_BRANCH:
1053: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1054: if (pdp->rp_value.rev == NULL) {
1055: rcsparse_warnx(rfp, "invalid branch \"%s\"",
1056: pdp->rp_buf);
1057: return (0);
1058: }
1059: if (!RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1060: rcsnum_free(pdp->rp_value.rev);
1061: rcsparse_warnx(rfp, "expected branch, got \"%s\"",
1062: pdp->rp_buf);
1063: return (0);
1064: }
1065: break;
1066: case RCS_TYPE_KEYWORD:
1067: if (islower(*pdp->rp_buf)) {
1068: p = bsearch(pdp->rp_buf, keywords,
1069: sizeof(keywords) / sizeof(keywords[0]),
1070: sizeof(keywords[0]), kw_cmp);
1071: if (p != NULL)
1072: return (p->k_val);
1073: }
1074: allowed = RCS_TYPE_REVISION;
1075: /* FALLTHROUGH */
1076: case RCS_TYPE_REVISION:
1077: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1078: if (pdp->rp_value.rev != NULL) {
1079: if (RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1080: rcsnum_free(pdp->rp_value.rev);
1081: rcsparse_warnx(rfp,
1082: "expected revision, got \"%s\"",
1083: pdp->rp_buf);
1084: return (0);
1085: }
1086: break;
1087: }
1088: /* FALLTHROUGH */
1089: default:
1090: RBUF_PUTC('\0');
1091: rcsparse_warnx(rfp, "unexpected token \"%s\"", pdp->rp_buf);
1092: return (0);
1093: /* NOTREACHED */
1094: }
1095:
1096: return (allowed);
1097: }
1098:
1099: static int
1100: rcsparse(RCSFILE *rfp, struct rcs_section *sec)
1101: {
1102: struct rcs_pdata *pdp;
1103: int i, token;
1104:
1105: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1106: i = 0;
1107:
1108: token = 0;
1109: for (i = 0; sec[i].token != 0; i++) {
1110: token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
1111: if (token == 0)
1112: return (1);
1113:
1114: while (token != sec[i].token) {
1115: if (sec[i].parse == NULL)
1116: goto end;
1117: if (sec[i].opt) {
1118: i++;
1119: continue;
1120: }
1121: if (token == EOF || (!(rfp->rf_flags & PARSED_DELTAS) &&
1122: token == RCS_TOK_DESC))
1123: goto end;
1124: rcsparse_warnx(rfp, "unexpected token \"%s\"",
1125: pdp->rp_buf);
1126: return (1);
1127: }
1128:
1129: if (sec[i].parse(rfp, pdp))
1130: return (1);
1131: }
1132: end:
1133: if (token == RCS_TYPE_REVISION)
1134: pdp->rp_token = token;
1135: else if (token == RCS_TOK_DESC)
1136: pdp->rp_token = RCS_TOK_DESC;
1137: else if (token == EOF)
1138: rfp->rf_flags |= RCS_PARSED;
1139:
1140: return (0);
1141: }
1142:
1143: static int
1144: rcsparse_deltatext(RCSFILE *rfp)
1145: {
1146: struct rcs_pdata *pdp;
1147: int ret;
1148:
1149: if (rfp->rf_flags & PARSED_DELTATEXTS)
1150: return (0);
1151:
1152: if (!(rfp->rf_flags & PARSED_DESC))
1153: if ((ret = rcsparse_desc(rfp)))
1154: return (ret);
1155:
1156: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1157:
1158: if (rcsparse(rfp, sec_deltatext))
1159: return (-1);
1160:
1.2 tobias 1161: if (rfp->rf_flags & RCS_PARSED)
1.1 tobias 1162: rfp->rf_flags |= PARSED_DELTATEXTS;
1163:
1164: return (1);
1165: }
1166:
1167: static int
1168: rcsparse_delta(RCSFILE *rfp)
1169: {
1170: struct rcs_pdata *pdp;
1171:
1172: if (rfp->rf_flags & PARSED_DELTAS)
1173: return (0);
1174:
1175: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1176: if (pdp->rp_token == RCS_TOK_DESC) {
1177: rfp->rf_flags |= PARSED_DELTAS;
1178: return (0);
1179: }
1180:
1181: if (rcsparse(rfp, sec_delta))
1182: return (-1);
1183:
1184: if (pdp->rp_delta != NULL) {
1185: TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
1186: pdp->rp_delta = NULL;
1187: rfp->rf_ndelta++;
1188: return (1);
1189: }
1190:
1191: return (0);
1192: }
1193:
1194: /*
1195: * rcsparse_growbuf()
1196: *
1197: * Attempt to grow the internal parse buffer for the RCS file <rf> by
1198: * RCS_BUFEXTSIZE.
1199: * In case of failure, the original buffer is left unmodified.
1200: */
1201: static void
1202: rcsparse_growbuf(RCSFILE *rfp)
1203: {
1204: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1205:
1206: pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
1207: pdp->rp_blen + RCS_BUFEXTSIZE);
1208: pdp->rp_blen += RCS_BUFEXTSIZE;
1209: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1210: }
1211:
1212: /*
1213: * Borrowed from src/usr.sbin/user/user.c:
1214: * return 1 if `login' is a valid login name
1215: */
1216: static int
1217: valid_login(char *login_name)
1218: {
1219: unsigned char *cp;
1220:
1221: /* The first character cannot be a hyphen */
1222: if (*login_name == '-')
1223: return 0;
1224:
1225: for (cp = login_name ; *cp ; cp++) {
1226: /* We allow '$' as the last character for samba */
1227: if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
1228: !(*cp == '$' && *(cp + 1) == '\0')) {
1229: return 0;
1230: }
1231: }
1232: if ((char *)cp - login_name > _PW_NAME_LEN)
1233: return 0;
1234: return 1;
1235: }
1236:
1237: static int
1238: kw_cmp(const void *k, const void *e)
1239: {
1240: return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
1241: }
1242:
1243: static void
1244: rcsparse_warnx(RCSFILE *rfp, char *fmt, ...)
1245: {
1246: struct rcs_pdata *pdp;
1247: va_list ap;
1248: char *nfmt;
1249:
1250: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1251: va_start(ap, fmt);
1252: if (asprintf(&nfmt, "%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, fmt)
1253: == -1)
1254: nfmt = fmt;
1255: vwarnx(nfmt, ap);
1256: va_end(ap);
1257: if (nfmt != fmt)
1258: free(nfmt);
1259: }