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