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