xref: /openbsd-src/usr.bin/cvs/rcsparse.c (revision cb39b41371628601fbe4c618205356d538b9d08a)
1 /*	$OpenBSD: rcsparse.c,v 1.11 2014/12/01 21:58:46 deraadt 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 "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},
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 */
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 *);
155 static 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  */
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 = 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  */
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);
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  */
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 
533 	pdp->rp_delta->rd_commitid = pdp->rp_value.str;
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 (;;) {
849 		c = getc(rfp->rf_file);
850 		if (c == '@') {
851 			c = getc(rfp->rf_file);
852 			if (c == EOF) {
853 				return (EOF);
854 			} else if (c != '@') {
855 				ungetc(c, rfp->rf_file);
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;
904 		c = getc(rfp->rf_file);
905 		if (c == EOF) {
906 			if (ferror(rfp->rf_file)) {
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);
923 		if (ret == EOF && ferror(rfp->rf_file)) {
924 			rcsparse_warnx(rfp, "error during parsing");
925 			return (0);
926 		}
927 		return (ret);
928 		/* NOTREACHED */
929 	case ':':
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 */
936 	case ';':
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 */
943 	case ',':
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) {
965 			if (ferror(rfp->rf_file))
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 
975 		c = getc(rfp->rf_file);
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 == ',') {
983 			ungetc(c, rfp->rf_file);
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:
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);
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 = xreallocarray(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)
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)
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;
1262 	char *msg;
1263 
1264 	pdp = (struct rcs_pdata *)rfp->rf_pdata;
1265 	va_start(ap, fmt);
1266 	if (vasprintf(&msg, fmt, ap) == -1) {
1267 		cvs_log(LP_ERRNO, "vasprintf");
1268 		va_end(ap);
1269 		return;
1270 	}
1271 	va_end(ap);
1272 	cvs_log(LP_ERR, "%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, msg);
1273 	free(msg);
1274 }
1275