1/*	$OpenBSD: rcsparse.c,v 1.16 2016/08/26 09:02:54 guenther 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 <time.h>
28#include <unistd.h>
29
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 */
74struct 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
92struct rcs_keyword {
93	const char	*k_name;
94	int		 k_val;
95};
96
97struct rcs_section {
98	int	token;
99	int	(*parse)(RCSFILE *, struct rcs_pdata *);
100	int	opt;
101};
102
103/* this has to be sorted always */
104static const struct rcs_keyword keywords[] = {
105	{ "access",		RCS_TOK_ACCESS},
106	{ "author",		RCS_TOK_AUTHOR},
107	{ "branch",		RCS_TOK_BRANCH},
108	{ "branches",		RCS_TOK_BRANCHES},
109	{ "comment",		RCS_TOK_COMMENT},
110	{ "commitid",		RCS_TOK_COMMITID},
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 */
125static int	rcsparse_head(RCSFILE *, struct rcs_pdata *);
126static int	rcsparse_branch(RCSFILE *, struct rcs_pdata *);
127static int	rcsparse_access(RCSFILE *, struct rcs_pdata *);
128static int	rcsparse_symbols(RCSFILE *, struct rcs_pdata *);
129static int	rcsparse_locks(RCSFILE *, struct rcs_pdata *);
130static int	rcsparse_strict(RCSFILE *, struct rcs_pdata *);
131static int	rcsparse_comment(RCSFILE *, struct rcs_pdata *);
132static int	rcsparse_commitid(RCSFILE *, struct rcs_pdata *);
133static int	rcsparse_expand(RCSFILE *, struct rcs_pdata *);
134static int	rcsparse_deltarevision(RCSFILE *, struct rcs_pdata *);
135static int	rcsparse_date(RCSFILE *, struct rcs_pdata *);
136static int	rcsparse_author(RCSFILE *, struct rcs_pdata *);
137static int	rcsparse_state(RCSFILE *, struct rcs_pdata *);
138static int	rcsparse_branches(RCSFILE *, struct rcs_pdata *);
139static int	rcsparse_next(RCSFILE *, struct rcs_pdata *);
140static int	rcsparse_textrevision(RCSFILE *, struct rcs_pdata *);
141static int	rcsparse_log(RCSFILE *, struct rcs_pdata *);
142static int	rcsparse_text(RCSFILE *, struct rcs_pdata *);
143
144static int	rcsparse_delta(RCSFILE *);
145static int	rcsparse_deltatext(RCSFILE *);
146static int	rcsparse_desc(RCSFILE *);
147
148static int	kw_cmp(const void *, const void *);
149static int	rcsparse(RCSFILE *, struct rcs_section *);
150static void	rcsparse_growbuf(RCSFILE *);
151static int	rcsparse_string(RCSFILE *, int);
152static int	rcsparse_token(RCSFILE *, int);
153static void	rcsparse_warnx(RCSFILE *, char *, ...);
154static int	valid_login(char *);
155static int	valid_commitid(char *);
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 */
167static 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 */
188static 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 */
204static 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 */
219int
220rcsparse_init(RCSFILE *rfp)
221{
222	struct rcs_pdata *pdp;
223
224	if (rfp->rf_flags & RCS_PARSED)
225		return (0);
226
227	pdp = xcalloc(1, 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;
234
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 */
262int
263rcsparse_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 */
301int
302rcsparse_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);
314
315	rdp = (rev != NULL) ? rcs_findrev(rfp, rev) : NULL;
316
317	for (;;) {
318		if (rdp != NULL && rdp->rd_text != NULL)
319			break;
320		ret = rcsparse_deltatext(rfp);
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 */
337void
338rcsparse_free(RCSFILE *rfp)
339{
340	struct rcs_pdata *pdp;
341
342	pdp = rfp->rf_pdata;
343
344	free(pdp->rp_buf);
345	if (pdp->rp_token == RCS_TYPE_REVISION)
346		rcsnum_free(pdp->rp_value.rev);
347	free(pdp);
348}
349
350/*
351 * rcsparse_desc()
352 *
353 * Parse desc of the RCS file <rfp>.  By calling rcsparse_desc, all deltas
354 * will be parsed in order to proceed the reading cursor to the desc keyword.
355 *
356 * desc @[...]@;
357 *
358 * Returns 0 on success or 1 on error.
359 */
360static int
361rcsparse_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 */
394static int
395rcsparse_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 */
416static int
417rcsparse_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 */
436static int
437rcsparse_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 */
456static int
457rcsparse_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 */
476static int
477rcsparse_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 */
501static int
502rcsparse_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 */
526static int
527rcsparse_commitid(RCSFILE *rfp, struct rcs_pdata *pdp)
528{
529	if (rcsparse_token(rfp, RCS_TYPE_COMMITID) != RCS_TYPE_COMMITID)
530		return (1);
531
532	pdp->rp_delta->rd_commitid = pdp->rp_value.str;
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 */
548static int
549rcsparse_textrevision(RCSFILE *rfp, struct rcs_pdata *pdp)
550{
551	struct rcs_delta *rdp;
552
553	TAILQ_FOREACH(rdp, &rfp->rf_delta, rd_list) {
554		if (rcsnum_cmp(rdp->rd_num, pdp->rp_value.rev, 0) == 0)
555			break;
556	}
557	if (rdp == NULL) {
558		rcsparse_warnx(rfp, "delta for revision \"%s\" not found",
559		    pdp->rp_buf);
560		rcsnum_free(pdp->rp_value.rev);
561		return (1);
562	}
563	pdp->rp_delta = rdp;
564
565	rcsnum_free(pdp->rp_value.rev);
566	return (0);
567}
568
569/*
570 * rcsparse_log()
571 *
572 * Parses the specified log of current deltatext pdp->rp_delta.
573 *
574 * log @[...]@
575 *
576 * Returns 0 on success or 1 on failure.
577 */
578static int
579rcsparse_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 */
598static int
599rcsparse_text(RCSFILE *rfp, struct rcs_pdata *pdp)
600{
601	if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
602		return (1);
603
604	pdp->rp_delta->rd_tlen = pdp->rp_tlen - 1;
605	if (pdp->rp_delta->rd_tlen == 0) {
606		pdp->rp_delta->rd_text = xstrdup("");
607	} else {
608		pdp->rp_delta->rd_text = xmalloc(pdp->rp_delta->rd_tlen);
609		memcpy(pdp->rp_delta->rd_text, pdp->rp_buf,
610		    pdp->rp_delta->rd_tlen);
611	}
612	free(pdp->rp_value.str);
613
614	return (0);
615}
616
617/*
618 * rcsparse_head()
619 *
620 * Parses the head revision of RCS file <rfp>.
621 *
622 * head [REVISION];
623 *
624 * Returns 0 on success or 1 on failure.
625 */
626static int
627rcsparse_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 */
650static int
651rcsparse_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 */
673static int
674rcsparse_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 */
698static int
699rcsparse_symbols(RCSFILE *rfp, struct rcs_pdata *pdp)
700{
701	struct rcs_sym *symp;
702	char *name;
703	int type;
704
705	while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
706	    RCS_TYPE_SYMBOL) {
707		name = pdp->rp_value.str;
708		if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
709		    rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
710			free(name);
711			return (1);
712		}
713		symp = xmalloc(sizeof(*symp));
714		symp->rs_name = name;
715		symp->rs_num = pdp->rp_value.rev;
716		TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
717	}
718
719	return (type != RCS_TOK_SCOLON);
720}
721
722/*
723 * rcsparse_locks()
724 *
725 * Parses the lock list of RCS file <rfp>.
726 *
727 * locks [SYMBOL:REVISION ...];
728 *
729 * Returns 0 on success or 1 on failure.
730 */
731static int
732rcsparse_locks(RCSFILE *rfp, struct rcs_pdata *pdp)
733{
734	struct rcs_lock *lkp;
735	char *name;
736	int type;
737
738	while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN)) ==
739	    RCS_TYPE_LOGIN) {
740		name = pdp->rp_value.str;
741		if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
742		    rcsparse_token(rfp, RCS_TYPE_REVISION) !=
743		    RCS_TYPE_REVISION) {
744			free(name);
745			return (1);
746		}
747		lkp = xmalloc(sizeof(*lkp));
748		lkp->rl_name = name;
749		lkp->rl_num = pdp->rp_value.rev;
750		TAILQ_INSERT_TAIL(&(rfp->rf_locks), lkp, rl_list);
751	}
752
753	return (type != RCS_TOK_SCOLON);
754}
755
756/*
757 * rcsparse_locks()
758 *
759 * Parses the strict keyword of RCS file <rfp>. The strict keyword is
760 * optional and can be omitted.
761 *
762 * [strict;]
763 *
764 * Returns 0 on success or 1 on failure.
765 */
766static int
767rcsparse_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 */
784static int
785rcsparse_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 */
808static int
809rcsparse_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) \
823do { \
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
833static int
834rcsparse_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 (;;) {
848		c = getc(rfp->rf_file);
849		if (c == '@') {
850			c = getc(rfp->rf_file);
851			if (c == EOF) {
852				return (EOF);
853			} else if (c != '@') {
854				ungetc(c, rfp->rf_file);
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
880static int
881rcsparse_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;
903		c = getc(rfp->rf_file);
904		if (c == EOF) {
905			if (ferror(rfp->rf_file)) {
906				rcsparse_warnx(rfp, "error during parsing");
907				return (0);
908			}
909			if (pre != '\n')
910				rcsparse_warnx(rfp,
911				    "no newline at end of file");
912			return (EOF);
913		} else if (c == '\n')
914			pdp->rp_lineno++;
915	} while (isspace(c));
916
917	pdp->rp_msglineno = pdp->rp_lineno;
918	switch (c) {
919	case '@':
920		ret = rcsparse_string(rfp, allowed);
921		if (ret == EOF && ferror(rfp->rf_file)) {
922			rcsparse_warnx(rfp, "error during parsing");
923			return (0);
924		}
925		return (ret);
926		/* NOTREACHED */
927	case ':':
928		type = RCS_TOK_COLON;
929		if (type & allowed)
930			return (type);
931		rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
932		return (0);
933		/* NOTREACHED */
934	case ';':
935		type = RCS_TOK_SCOLON;
936		if (type & allowed)
937			return (type);
938		rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
939		return (0);
940		/* NOTREACHED */
941	case ',':
942		type = RCS_TOK_COMMA;
943		if (type & allowed)
944			return (type);
945		rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
946		return (0);
947		/* NOTREACHED */
948	default:
949		if (!isgraph(c)) {
950			rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
951			return (0);
952		}
953		break;
954	}
955	allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
956
957	bp = pdp->rp_buf;
958	pdp->rp_tlen = 0;
959	*bp = '\0';
960
961	for (;;) {
962		if (c == EOF) {
963			if (ferror(rfp->rf_file))
964				rcsparse_warnx(rfp, "error during parsing");
965			else
966				rcsparse_warnx(rfp, "unexpected end of file");
967			return (0);
968		} else if (c == '\n')
969			pdp->rp_lineno++;
970
971		RBUF_PUTC(c);
972
973		c = getc(rfp->rf_file);
974
975		if (isspace(c)) {
976			if (c == '\n')
977				pdp->rp_lineno++;
978			RBUF_PUTC('\0');
979			break;
980		} else if (c == ';' || c == ':' || c == ',') {
981			ungetc(c, rfp->rf_file);
982			RBUF_PUTC('\0');
983			break;
984		} else if (!isgraph(c)) {
985			rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
986			return (0);
987		}
988	}
989
990	switch (allowed) {
991	case RCS_TYPE_COMMITID:
992		if (!valid_commitid(pdp->rp_buf)) {
993			rcsparse_warnx(rfp, "invalid commitid \"%s\"",
994			    pdp->rp_buf);
995			return (0);
996		}
997		pdp->rp_value.str = xstrdup(pdp->rp_buf);
998		break;
999	case RCS_TYPE_LOGIN:
1000		if (!valid_login(pdp->rp_buf)) {
1001			rcsparse_warnx(rfp, "invalid login \"%s\"",
1002			    pdp->rp_buf);
1003			return (0);
1004		}
1005		pdp->rp_value.str = xstrdup(pdp->rp_buf);
1006		break;
1007	case RCS_TYPE_SYMBOL:
1008		if (!rcs_sym_check(pdp->rp_buf)) {
1009			rcsparse_warnx(rfp, "invalid symbol \"%s\"",
1010			    pdp->rp_buf);
1011			return (0);
1012		}
1013		pdp->rp_value.str = xstrdup(pdp->rp_buf);
1014		break;
1015		/* FALLTHROUGH */
1016	case RCS_TYPE_STATE:
1017		if (rcs_state_check(pdp->rp_buf)) {
1018			rcsparse_warnx(rfp, "invalid state \"%s\"",
1019			    pdp->rp_buf);
1020			return (0);
1021		}
1022		pdp->rp_value.str = xstrdup(pdp->rp_buf);
1023		break;
1024	case RCS_TYPE_DATE:
1025		if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
1026			rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1027			return (0);
1028		}
1029		if (datenum->rn_len != 6) {
1030			rcsnum_free(datenum);
1031			rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1032			return (0);
1033		}
1034		pdp->rp_value.date.tm_year = datenum->rn_id[0];
1035		if (pdp->rp_value.date.tm_year >= 1900)
1036			pdp->rp_value.date.tm_year -= 1900;
1037		pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
1038		pdp->rp_value.date.tm_mday = datenum->rn_id[2];
1039		pdp->rp_value.date.tm_hour = datenum->rn_id[3];
1040		pdp->rp_value.date.tm_min = datenum->rn_id[4];
1041		pdp->rp_value.date.tm_sec = datenum->rn_id[5];
1042		rcsnum_free(datenum);
1043		break;
1044	case RCS_TYPE_NUMBER:
1045		pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1046		if (pdp->rp_value.rev == NULL) {
1047			rcsparse_warnx(rfp, "invalid number \"%s\"",
1048			    pdp->rp_buf);
1049			return (0);
1050		}
1051		break;
1052	case RCS_TYPE_BRANCH:
1053		pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1054		if (pdp->rp_value.rev == NULL) {
1055			rcsparse_warnx(rfp, "invalid branch \"%s\"",
1056			    pdp->rp_buf);
1057			return (0);
1058		}
1059		if (!RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1060			rcsnum_free(pdp->rp_value.rev);
1061			rcsparse_warnx(rfp, "expected branch, got \"%s\"",
1062			    pdp->rp_buf);
1063			return (0);
1064		}
1065		break;
1066	case RCS_TYPE_KEYWORD:
1067		if (islower(*pdp->rp_buf)) {
1068			p = bsearch(pdp->rp_buf, keywords,
1069			    sizeof(keywords) / sizeof(keywords[0]),
1070			    sizeof(keywords[0]), kw_cmp);
1071			if (p != NULL)
1072				return (p->k_val);
1073		}
1074		allowed = RCS_TYPE_REVISION;
1075		/* FALLTHROUGH */
1076	case RCS_TYPE_REVISION:
1077		pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1078		if (pdp->rp_value.rev != NULL) {
1079			if (RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
1080				rcsnum_free(pdp->rp_value.rev);
1081				rcsparse_warnx(rfp,
1082				    "expected revision, got \"%s\"",
1083				    pdp->rp_buf);
1084				return (0);
1085			}
1086			break;
1087		}
1088		/* FALLTHROUGH */
1089	default:
1090		RBUF_PUTC('\0');
1091		rcsparse_warnx(rfp, "unexpected token \"%s\"", pdp->rp_buf);
1092		return (0);
1093		/* NOTREACHED */
1094	}
1095
1096	return (allowed);
1097}
1098
1099static int
1100rcsparse(RCSFILE *rfp, struct rcs_section *sec)
1101{
1102	struct rcs_pdata *pdp;
1103	int i, token;
1104
1105	pdp = (struct rcs_pdata *)rfp->rf_pdata;
1106
1107	token = 0;
1108	for (i = 0; sec[i].token != 0; i++) {
1109		token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
1110		if (token == 0)
1111			return (1);
1112
1113		while (token != sec[i].token) {
1114			if (sec[i].parse == NULL)
1115				goto end;
1116			if (sec[i].opt) {
1117				i++;
1118				continue;
1119			}
1120			if (token == EOF || (!(rfp->rf_flags & PARSED_DELTAS) &&
1121			    token == RCS_TOK_DESC))
1122				goto end;
1123			rcsparse_warnx(rfp, "unexpected token \"%s\"",
1124			    pdp->rp_buf);
1125			return (1);
1126		}
1127
1128		if (sec[i].parse(rfp, pdp))
1129			return (1);
1130	}
1131end:
1132	if (token == RCS_TYPE_REVISION)
1133		pdp->rp_token = token;
1134	else if (token == RCS_TOK_DESC)
1135		pdp->rp_token = RCS_TOK_DESC;
1136	else if (token == EOF)
1137		rfp->rf_flags |= RCS_PARSED;
1138
1139	return (0);
1140}
1141
1142static int
1143rcsparse_deltatext(RCSFILE *rfp)
1144{
1145	int ret;
1146
1147	if (rfp->rf_flags & PARSED_DELTATEXTS)
1148		return (0);
1149
1150	if (!(rfp->rf_flags & PARSED_DESC))
1151		if ((ret = rcsparse_desc(rfp)))
1152			return (ret);
1153
1154	if (rcsparse(rfp, sec_deltatext))
1155		return (-1);
1156
1157	if (rfp->rf_flags & RCS_PARSED)
1158		rfp->rf_flags |= PARSED_DELTATEXTS;
1159
1160	return (1);
1161}
1162
1163static int
1164rcsparse_delta(RCSFILE *rfp)
1165{
1166	struct rcs_pdata *pdp;
1167
1168	if (rfp->rf_flags & PARSED_DELTAS)
1169		return (0);
1170
1171	pdp = (struct rcs_pdata *)rfp->rf_pdata;
1172	if (pdp->rp_token == RCS_TOK_DESC) {
1173		rfp->rf_flags |= PARSED_DELTAS;
1174		return (0);
1175	}
1176
1177	if (rcsparse(rfp, sec_delta))
1178		return (-1);
1179
1180	if (pdp->rp_delta != NULL) {
1181		TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
1182		pdp->rp_delta = NULL;
1183		rfp->rf_ndelta++;
1184		return (1);
1185	}
1186
1187	return (0);
1188}
1189
1190/*
1191 * rcsparse_growbuf()
1192 *
1193 * Attempt to grow the internal parse buffer for the RCS file <rf> by
1194 * RCS_BUFEXTSIZE.
1195 * In case of failure, the original buffer is left unmodified.
1196 */
1197static void
1198rcsparse_growbuf(RCSFILE *rfp)
1199{
1200	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1201
1202	pdp->rp_buf = xreallocarray(pdp->rp_buf, 1,
1203		pdp->rp_blen + RCS_BUFEXTSIZE);
1204	pdp->rp_blen += RCS_BUFEXTSIZE;
1205	pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1206}
1207
1208/*
1209 * Borrowed from src/usr.sbin/user/user.c:
1210 * return 1 if `login' is a valid login name
1211 */
1212static int
1213valid_login(char *login_name)
1214{
1215	unsigned char *cp;
1216
1217	/* The first character cannot be a hyphen */
1218	if (*login_name == '-')
1219		return 0;
1220
1221	for (cp = login_name ; *cp ; cp++) {
1222		/* We allow '$' as the last character for samba */
1223		if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
1224		    !(*cp == '$' && *(cp + 1) == '\0')) {
1225			return 0;
1226		}
1227	}
1228	if ((char *)cp - login_name > _PW_NAME_LEN)
1229		return 0;
1230	return 1;
1231}
1232
1233static int
1234valid_commitid(char *commitid)
1235{
1236	unsigned char *cp;
1237
1238	/* A-Za-z0-9 */
1239	for (cp = commitid; *cp ; cp++) {
1240		if (!isalnum(*cp))
1241			return 0;
1242	}
1243	if ((char *)cp - commitid > RCS_COMMITID_MAXLEN)
1244		return 0;
1245	return 1;
1246}
1247
1248static int
1249kw_cmp(const void *k, const void *e)
1250{
1251	return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
1252}
1253
1254static void
1255rcsparse_warnx(RCSFILE *rfp, char *fmt, ...)
1256{
1257	struct rcs_pdata *pdp;
1258	va_list ap;
1259	char *msg;
1260
1261	pdp = (struct rcs_pdata *)rfp->rf_pdata;
1262	va_start(ap, fmt);
1263	if (vasprintf(&msg, fmt, ap) == -1) {
1264		warn("vasprintf");
1265		va_end(ap);
1266		return;
1267	}
1268	va_end(ap);
1269	warnx("%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, msg);
1270	free(msg);
1271}
1272