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