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