Annotation of src/usr.bin/cvs/rcsparse.c, Revision 1.13
1.13 ! fcambus 1: /* $OpenBSD: rcsparse.c,v 1.12 2015/11/05 09:48:21 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 "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},
1.7 jcs 110: { "commitid", RCS_TOK_COMMITID},
1.1 tobias 111: { "date", RCS_TOK_DATE},
112: { "desc", RCS_TOK_DESC},
113: { "expand", RCS_TOK_EXPAND},
114: { "head", RCS_TOK_HEAD},
115: { "locks", RCS_TOK_LOCKS},
116: { "log", RCS_TOK_LOG},
117: { "next", RCS_TOK_NEXT},
118: { "state", RCS_TOK_STATE},
119: { "strict", RCS_TOK_STRICT},
120: { "symbols", RCS_TOK_SYMBOLS},
121: { "text", RCS_TOK_TEXT}
122: };
123:
124: /* parser functions specified in rcs_section structs */
125: static int rcsparse_head(RCSFILE *, struct rcs_pdata *);
126: static int rcsparse_branch(RCSFILE *, struct rcs_pdata *);
127: static int rcsparse_access(RCSFILE *, struct rcs_pdata *);
128: static int rcsparse_symbols(RCSFILE *, struct rcs_pdata *);
129: static int rcsparse_locks(RCSFILE *, struct rcs_pdata *);
130: static int rcsparse_strict(RCSFILE *, struct rcs_pdata *);
131: static int rcsparse_comment(RCSFILE *, struct rcs_pdata *);
132: static int rcsparse_commitid(RCSFILE *, struct rcs_pdata *);
133: static int rcsparse_expand(RCSFILE *, struct rcs_pdata *);
134: static int rcsparse_deltarevision(RCSFILE *, struct rcs_pdata *);
135: static int rcsparse_date(RCSFILE *, struct rcs_pdata *);
136: static int rcsparse_author(RCSFILE *, struct rcs_pdata *);
137: static int rcsparse_state(RCSFILE *, struct rcs_pdata *);
138: static int rcsparse_branches(RCSFILE *, struct rcs_pdata *);
139: static int rcsparse_next(RCSFILE *, struct rcs_pdata *);
140: static int rcsparse_textrevision(RCSFILE *, struct rcs_pdata *);
141: static int rcsparse_log(RCSFILE *, struct rcs_pdata *);
142: static int rcsparse_text(RCSFILE *, struct rcs_pdata *);
143:
144: static int rcsparse_delta(RCSFILE *);
145: static int rcsparse_deltatext(RCSFILE *);
146: static int rcsparse_desc(RCSFILE *);
147:
148: static int kw_cmp(const void *, const void *);
149: static int rcsparse(RCSFILE *, struct rcs_section *);
150: static void rcsparse_growbuf(RCSFILE *);
151: static int rcsparse_string(RCSFILE *, int);
152: static int rcsparse_token(RCSFILE *, int);
153: static void rcsparse_warnx(RCSFILE *, char *, ...);
154: static int valid_login(char *);
1.7 jcs 155: static int valid_commitid(char *);
1.1 tobias 156:
157: /*
158: * head [REVISION];
159: * [branch BRANCH];
160: * access [LOGIN ...];
161: * symbols [SYMBOL:REVISION ...];
162: * locks [LOGIN:REVISION ...];
163: * [strict;]
164: * [comment [@[...]@];]
165: * [expand [@[...]@];]
166: */
167: static struct rcs_section sec_admin[] = {
168: { RCS_TOK_HEAD, rcsparse_head, MANDATORY },
169: { RCS_TOK_BRANCH, rcsparse_branch, OPTIONAL },
170: { RCS_TOK_ACCESS, rcsparse_access, MANDATORY },
171: { RCS_TOK_SYMBOLS, rcsparse_symbols, MANDATORY },
172: { RCS_TOK_LOCKS, rcsparse_locks, MANDATORY },
173: { RCS_TOK_STRICT, rcsparse_strict, OPTIONAL },
174: { RCS_TOK_COMMENT, rcsparse_comment, OPTIONAL },
175: { RCS_TOK_EXPAND, rcsparse_expand, OPTIONAL },
176: { 0, NULL, 0 }
177: };
178:
179: /*
180: * REVISION
181: * date [YY]YY.MM.DD.HH.MM.SS;
182: * author LOGIN;
183: * state STATE;
184: * branches [REVISION ...];
185: * next [REVISION];
186: * [commitid ID;]
187: */
188: static struct rcs_section sec_delta[] = {
189: { RCS_TYPE_REVISION, rcsparse_deltarevision, MANDATORY },
190: { RCS_TOK_DATE, rcsparse_date, MANDATORY },
191: { RCS_TOK_AUTHOR, rcsparse_author, MANDATORY },
192: { RCS_TOK_STATE, rcsparse_state, MANDATORY },
193: { RCS_TOK_BRANCHES, rcsparse_branches, MANDATORY },
194: { RCS_TOK_NEXT, rcsparse_next, MANDATORY },
195: { RCS_TOK_COMMITID, rcsparse_commitid, OPTIONAL },
196: { 0, NULL, 0 }
197: };
198:
199: /*
200: * REVISION
201: * log @[...]@
202: * text @[...]@
203: */
204: static struct rcs_section sec_deltatext[] = {
205: { RCS_TYPE_REVISION, rcsparse_textrevision, MANDATORY },
206: { RCS_TOK_LOG, rcsparse_log, MANDATORY },
207: { RCS_TOK_TEXT, rcsparse_text, MANDATORY },
208: { 0, NULL, 0 }
209: };
210:
211: /*
212: * rcsparse_init()
213: *
214: * Initializes the parsing data structure and parses the admin section of
215: * RCS file <rfp>.
216: *
217: * Returns 0 on success or 1 on failure.
218: */
219: int
220: rcsparse_init(RCSFILE *rfp)
221: {
222: struct rcs_pdata *pdp;
223:
224: if (rfp->rf_flags & RCS_PARSED)
225: return (0);
226:
1.10 deraadt 227: pdp = xcalloc(1, sizeof(*pdp));
1.1 tobias 228: pdp->rp_buf = xmalloc(RCS_BUFSIZE);
229: pdp->rp_blen = RCS_BUFSIZE;
230: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
231: pdp->rp_token = -1;
232: pdp->rp_lineno = 1;
233: pdp->rp_msglineno = 1;
1.3 tobias 234:
1.1 tobias 235: /* ditch the strict lock */
236: rfp->rf_flags &= ~RCS_SLOCK;
237: rfp->rf_pdata = pdp;
238:
239: if (rcsparse(rfp, sec_admin)) {
240: rcsparse_free(rfp);
241: return (1);
242: }
243:
244: if ((rfp->rf_flags & RCS_PARSE_FULLY) &&
245: rcsparse_deltatexts(rfp, NULL)) {
246: rcsparse_free(rfp);
247: return (1);
248: }
249:
250: rfp->rf_flags |= RCS_SYNCED;
251: return (0);
252: }
253:
254: /*
255: * rcsparse_deltas()
256: *
257: * Parse deltas. If <rev> is not NULL, parse only as far as that
258: * revision. If <rev> is NULL, parse all deltas.
259: *
260: * Returns 0 on success or 1 on error.
261: */
262: int
263: rcsparse_deltas(RCSFILE *rfp, RCSNUM *rev)
264: {
265: int ret;
266: struct rcs_delta *enddelta;
267:
268: if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
269: return (0);
270:
271: for (;;) {
272: ret = rcsparse_delta(rfp);
273: if (rev != NULL) {
274: enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
275: if (enddelta == NULL)
276: return (1);
277:
278: if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
279: break;
280: }
281:
282: if (ret == 0) {
283: rfp->rf_flags |= PARSED_DELTAS;
284: break;
285: }
286: else if (ret == -1)
287: return (1);
288: }
289:
290: return (0);
291: }
292:
293: /*
294: * rcsparse_deltatexts()
295: *
296: * Parse deltatexts. If <rev> is not NULL, parse only as far as that
297: * revision. If <rev> is NULL, parse everything.
298: *
299: * Returns 0 on success or 1 on error.
300: */
301: int
302: rcsparse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
303: {
304: int ret;
305: struct rcs_delta *rdp;
306:
307: if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
308: (rfp->rf_flags & RCS_CREATE))
309: return (0);
310:
311: if (!(rfp->rf_flags & PARSED_DESC))
312: if (rcsparse_desc(rfp))
313: return (1);
1.2 tobias 314:
315: rdp = (rev != NULL) ? rcs_findrev(rfp, rev) : NULL;
316:
1.1 tobias 317: for (;;) {
1.2 tobias 318: if (rdp != NULL && rdp->rd_text != NULL)
319: break;
320: ret = rcsparse_deltatext(rfp);
1.1 tobias 321: if (ret == 0) {
322: rfp->rf_flags |= PARSED_DELTATEXTS;
323: break;
324: }
325: else if (ret == -1)
326: return (1);
327: }
328:
329: return (0);
330: }
331:
332: /*
333: * rcsparse_free()
334: *
335: * Free the contents of the <rfp>'s parser data structure.
336: */
337: void
338: rcsparse_free(RCSFILE *rfp)
339: {
340: struct rcs_pdata *pdp;
341:
342: pdp = rfp->rf_pdata;
343:
1.12 nicm 344: free(pdp->rp_buf);
1.1 tobias 345: if (pdp->rp_token == RCS_TYPE_REVISION)
1.13 ! fcambus 346: free(pdp->rp_value.rev);
1.12 nicm 347: free(pdp);
1.1 tobias 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.7 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);
1.13 ! fcambus 560: free(pdp->rp_value.rev);
1.1 tobias 561: return (1);
562: }
563: pdp->rp_delta = rdp;
564:
1.13 ! fcambus 565: free(pdp->rp_value.rev);
1.1 tobias 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: }
1.12 nicm 612: free(pdp->rp_value.str);
1.1 tobias 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) {
1.12 nicm 710: free(name);
1.1 tobias 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) {
1.12 nicm 744: free(name);
1.1 tobias 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.3 tobias 848: c = getc(rfp->rf_file);
1.1 tobias 849: if (c == '@') {
1.3 tobias 850: c = getc(rfp->rf_file);
1.1 tobias 851: if (c == EOF) {
852: return (EOF);
853: } else if (c != '@') {
1.3 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.3 tobias 903: c = getc(rfp->rf_file);
1.1 tobias 904: if (c == EOF) {
1.3 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: type = 0;
919: switch (c) {
920: case '@':
921: ret = rcsparse_string(rfp, allowed);
1.3 tobias 922: if (ret == EOF && ferror(rfp->rf_file)) {
1.1 tobias 923: rcsparse_warnx(rfp, "error during parsing");
924: return (0);
925: }
926: return (ret);
927: /* NOTREACHED */
1.6 tobias 928: case ':':
1.1 tobias 929: type = RCS_TOK_COLON;
930: if (type & allowed)
931: return (type);
932: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
933: return (0);
934: /* NOTREACHED */
1.6 tobias 935: case ';':
1.1 tobias 936: type = RCS_TOK_SCOLON;
937: if (type & allowed)
938: return (type);
939: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
940: return (0);
941: /* NOTREACHED */
1.6 tobias 942: case ',':
1.1 tobias 943: type = RCS_TOK_COMMA;
944: if (type & allowed)
945: return (type);
946: rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
947: return (0);
948: /* NOTREACHED */
949: default:
950: if (!isgraph(c)) {
951: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
952: return (0);
953: }
954: break;
955: }
956: allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
957:
958: bp = pdp->rp_buf;
959: pdp->rp_tlen = 0;
960: *bp = '\0';
961:
962: for (;;) {
963: if (c == EOF) {
1.3 tobias 964: if (ferror(rfp->rf_file))
1.1 tobias 965: rcsparse_warnx(rfp, "error during parsing");
966: else
967: rcsparse_warnx(rfp, "unexpected end of file");
968: return (0);
969: } else if (c == '\n')
970: pdp->rp_lineno++;
971:
972: RBUF_PUTC(c);
973:
1.3 tobias 974: c = getc(rfp->rf_file);
1.1 tobias 975:
976: if (isspace(c)) {
977: if (c == '\n')
978: pdp->rp_lineno++;
979: RBUF_PUTC('\0');
980: break;
981: } else if (c == ';' || c == ':' || c == ',') {
1.3 tobias 982: ungetc(c, rfp->rf_file);
1.1 tobias 983: RBUF_PUTC('\0');
984: break;
985: } else if (!isgraph(c)) {
986: rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
987: return (0);
988: }
989: }
990:
991: switch (allowed) {
992: case RCS_TYPE_COMMITID:
1.7 jcs 993: if (!valid_commitid(pdp->rp_buf)) {
994: rcsparse_warnx(rfp, "invalid commitid \"%s\"",
995: pdp->rp_buf);
996: return (0);
997: }
998: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1.1 tobias 999: break;
1000: case RCS_TYPE_LOGIN:
1001: if (!valid_login(pdp->rp_buf)) {
1002: rcsparse_warnx(rfp, "invalid login \"%s\"",
1003: pdp->rp_buf);
1004: return (0);
1005: }
1006: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1007: break;
1008: case RCS_TYPE_SYMBOL:
1009: if (!rcs_sym_check(pdp->rp_buf)) {
1010: rcsparse_warnx(rfp, "invalid symbol \"%s\"",
1011: pdp->rp_buf);
1012: return (0);
1013: }
1014: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1015: break;
1016: /* FALLTHROUGH */
1017: case RCS_TYPE_STATE:
1018: if (rcs_state_check(pdp->rp_buf)) {
1019: rcsparse_warnx(rfp, "invalid state \"%s\"",
1020: pdp->rp_buf);
1021: return (0);
1022: }
1023: pdp->rp_value.str = xstrdup(pdp->rp_buf);
1024: break;
1025: case RCS_TYPE_DATE:
1026: if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
1027: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1028: return (0);
1029: }
1030: if (datenum->rn_len != 6) {
1.13 ! fcambus 1031: free(datenum);
1.1 tobias 1032: rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1033: return (0);
1034: }
1035: pdp->rp_value.date.tm_year = datenum->rn_id[0];
1036: if (pdp->rp_value.date.tm_year >= 1900)
1037: pdp->rp_value.date.tm_year -= 1900;
1038: pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
1039: pdp->rp_value.date.tm_mday = datenum->rn_id[2];
1040: pdp->rp_value.date.tm_hour = datenum->rn_id[3];
1041: pdp->rp_value.date.tm_min = datenum->rn_id[4];
1042: pdp->rp_value.date.tm_sec = datenum->rn_id[5];
1.13 ! fcambus 1043: free(datenum);
1.1 tobias 1044: break;
1045: case RCS_TYPE_NUMBER:
1046: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1047: if (pdp->rp_value.rev == NULL) {
1048: rcsparse_warnx(rfp, "invalid number \"%s\"",
1049: pdp->rp_buf);
1050: return (0);
1051: }
1052: break;
1053: case RCS_TYPE_BRANCH:
1054: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1055: if (pdp->rp_value.rev == NULL) {
1056: rcsparse_warnx(rfp, "invalid branch \"%s\"",
1057: pdp->rp_buf);
1058: return (0);
1059: }
1060: if (!RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1.13 ! fcambus 1061: free(pdp->rp_value.rev);
1.1 tobias 1062: rcsparse_warnx(rfp, "expected branch, got \"%s\"",
1063: pdp->rp_buf);
1064: return (0);
1065: }
1066: break;
1067: case RCS_TYPE_KEYWORD:
1068: if (islower(*pdp->rp_buf)) {
1069: p = bsearch(pdp->rp_buf, keywords,
1070: sizeof(keywords) / sizeof(keywords[0]),
1071: sizeof(keywords[0]), kw_cmp);
1072: if (p != NULL)
1073: return (p->k_val);
1074: }
1075: allowed = RCS_TYPE_REVISION;
1076: /* FALLTHROUGH */
1077: case RCS_TYPE_REVISION:
1078: pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1079: if (pdp->rp_value.rev != NULL) {
1080: if (RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1.13 ! fcambus 1081: free(pdp->rp_value.rev);
1.1 tobias 1082: rcsparse_warnx(rfp,
1083: "expected revision, got \"%s\"",
1084: pdp->rp_buf);
1085: return (0);
1086: }
1087: break;
1088: }
1089: /* FALLTHROUGH */
1090: default:
1091: RBUF_PUTC('\0');
1092: rcsparse_warnx(rfp, "unexpected token \"%s\"", pdp->rp_buf);
1093: return (0);
1094: /* NOTREACHED */
1095: }
1096:
1097: return (allowed);
1098: }
1099:
1100: static int
1101: rcsparse(RCSFILE *rfp, struct rcs_section *sec)
1102: {
1103: struct rcs_pdata *pdp;
1104: int i, token;
1105:
1106: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1107: i = 0;
1108:
1109: token = 0;
1110: for (i = 0; sec[i].token != 0; i++) {
1111: token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
1112: if (token == 0)
1113: return (1);
1114:
1115: while (token != sec[i].token) {
1116: if (sec[i].parse == NULL)
1117: goto end;
1118: if (sec[i].opt) {
1119: i++;
1120: continue;
1121: }
1122: if (token == EOF || (!(rfp->rf_flags & PARSED_DELTAS) &&
1123: token == RCS_TOK_DESC))
1124: goto end;
1125: rcsparse_warnx(rfp, "unexpected token \"%s\"",
1126: pdp->rp_buf);
1127: return (1);
1128: }
1129:
1130: if (sec[i].parse(rfp, pdp))
1131: return (1);
1132: }
1133: end:
1134: if (token == RCS_TYPE_REVISION)
1135: pdp->rp_token = token;
1136: else if (token == RCS_TOK_DESC)
1137: pdp->rp_token = RCS_TOK_DESC;
1138: else if (token == EOF)
1139: rfp->rf_flags |= RCS_PARSED;
1140:
1141: return (0);
1142: }
1143:
1144: static int
1145: rcsparse_deltatext(RCSFILE *rfp)
1146: {
1147: int ret;
1148:
1149: if (rfp->rf_flags & PARSED_DELTATEXTS)
1150: return (0);
1151:
1152: if (!(rfp->rf_flags & PARSED_DESC))
1153: if ((ret = rcsparse_desc(rfp)))
1154: return (ret);
1155:
1156: if (rcsparse(rfp, sec_deltatext))
1157: return (-1);
1158:
1159: if (rfp->rf_flags & RCS_PARSED)
1160: rfp->rf_flags |= PARSED_DELTATEXTS;
1161:
1162: return (1);
1163: }
1164:
1165: static int
1166: rcsparse_delta(RCSFILE *rfp)
1167: {
1168: struct rcs_pdata *pdp;
1169:
1170: if (rfp->rf_flags & PARSED_DELTAS)
1171: return (0);
1172:
1173: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1174: if (pdp->rp_token == RCS_TOK_DESC) {
1175: rfp->rf_flags |= PARSED_DELTAS;
1176: return (0);
1177: }
1178:
1179: if (rcsparse(rfp, sec_delta))
1180: return (-1);
1181:
1182: if (pdp->rp_delta != NULL) {
1183: TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
1184: pdp->rp_delta = NULL;
1185: rfp->rf_ndelta++;
1186: return (1);
1187: }
1188:
1189: return (0);
1190: }
1191:
1192: /*
1193: * rcsparse_growbuf()
1194: *
1195: * Attempt to grow the internal parse buffer for the RCS file <rf> by
1196: * RCS_BUFEXTSIZE.
1197: * In case of failure, the original buffer is left unmodified.
1198: */
1199: static void
1200: rcsparse_growbuf(RCSFILE *rfp)
1201: {
1202: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1203:
1.11 deraadt 1204: pdp->rp_buf = xreallocarray(pdp->rp_buf, 1,
1.1 tobias 1205: pdp->rp_blen + RCS_BUFEXTSIZE);
1206: pdp->rp_blen += RCS_BUFEXTSIZE;
1207: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1208: }
1209:
1210: /*
1211: * Borrowed from src/usr.sbin/user/user.c:
1212: * return 1 if `login' is a valid login name
1213: */
1214: static int
1215: valid_login(char *login_name)
1216: {
1217: unsigned char *cp;
1218:
1219: /* The first character cannot be a hyphen */
1220: if (*login_name == '-')
1221: return 0;
1222:
1223: for (cp = login_name ; *cp ; cp++) {
1224: /* We allow '$' as the last character for samba */
1225: if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
1226: !(*cp == '$' && *(cp + 1) == '\0')) {
1227: return 0;
1228: }
1229: }
1230: if ((char *)cp - login_name > _PW_NAME_LEN)
1.7 jcs 1231: return 0;
1232: return 1;
1233: }
1234:
1235: static int
1236: valid_commitid(char *commitid)
1237: {
1238: unsigned char *cp;
1239:
1240: /* A-Za-z0-9 */
1241: for (cp = commitid; *cp ; cp++) {
1242: if (!isalnum(*cp))
1243: return 0;
1244: }
1245: if ((char *)cp - commitid > RCS_COMMITID_MAXLEN)
1.1 tobias 1246: return 0;
1247: return 1;
1248: }
1249:
1250: static int
1251: kw_cmp(const void *k, const void *e)
1252: {
1253: return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
1254: }
1255:
1256: static void
1257: rcsparse_warnx(RCSFILE *rfp, char *fmt, ...)
1258: {
1259: struct rcs_pdata *pdp;
1260: va_list ap;
1.8 bluhm 1261: char *msg;
1.1 tobias 1262:
1263: pdp = (struct rcs_pdata *)rfp->rf_pdata;
1264: va_start(ap, fmt);
1.8 bluhm 1265: if (vasprintf(&msg, fmt, ap) == -1) {
1266: cvs_log(LP_ERRNO, "vasprintf");
1267: va_end(ap);
1268: return;
1269: }
1.1 tobias 1270: va_end(ap);
1.8 bluhm 1271: cvs_log(LP_ERR, "%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, msg);
1272: free(msg);
1.1 tobias 1273: }