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