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