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