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