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