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