xref: /openbsd/usr.bin/cvs/rcsparse.c (revision 53ce2177)
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
rcsparse_init(RCSFILE * rfp)220 rcsparse_init(RCSFILE *rfp)
221 {
222 	struct rcs_pdata *pdp;
223 
224 	if (rfp->rf_flags & RCS_PARSED)
225 		return (0);
226 
227 	pdp = xcalloc(1, sizeof(*pdp));
228 	pdp->rp_buf = xmalloc(RCS_BUFSIZE);
229 	pdp->rp_blen = RCS_BUFSIZE;
230 	pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
231 	pdp->rp_token = -1;
232 	pdp->rp_lineno = 1;
233 	pdp->rp_msglineno = 1;
234 
235 	/* ditch the strict lock */
236 	rfp->rf_flags &= ~RCS_SLOCK;
237 	rfp->rf_pdata = pdp;
238 
239 	if (rcsparse(rfp, sec_admin)) {
240 		rcsparse_free(rfp);
241 		return (1);
242 	}
243 
244 	if ((rfp->rf_flags & RCS_PARSE_FULLY) &&
245 	    rcsparse_deltatexts(rfp, NULL)) {
246 		rcsparse_free(rfp);
247 		return (1);
248 	}
249 
250 	rfp->rf_flags |= RCS_SYNCED;
251 	return (0);
252 }
253 
254 /*
255  * rcsparse_deltas()
256  *
257  * Parse deltas. If <rev> is not NULL, parse only as far as that
258  * revision. If <rev> is NULL, parse all deltas.
259  *
260  * Returns 0 on success or 1 on error.
261  */
262 int
rcsparse_deltas(RCSFILE * rfp,RCSNUM * rev)263 rcsparse_deltas(RCSFILE *rfp, RCSNUM *rev)
264 {
265 	int ret;
266 	struct rcs_delta *enddelta;
267 
268 	if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
269 		return (0);
270 
271 	for (;;) {
272 		ret = rcsparse_delta(rfp);
273 		if (rev != NULL) {
274 			enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
275 			if (enddelta == NULL)
276 				return (1);
277 
278 			if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
279 				break;
280 		}
281 
282 		if (ret == 0) {
283 			rfp->rf_flags |= PARSED_DELTAS;
284 			break;
285 		}
286 		else if (ret == -1)
287 			return (1);
288 	}
289 
290 	return (0);
291 }
292 
293 /*
294  * rcsparse_deltatexts()
295  *
296  * Parse deltatexts. If <rev> is not NULL, parse only as far as that
297  * revision. If <rev> is NULL, parse everything.
298  *
299  * Returns 0 on success or 1 on error.
300  */
301 int
rcsparse_deltatexts(RCSFILE * rfp,RCSNUM * rev)302 rcsparse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
303 {
304 	int ret;
305 	struct rcs_delta *rdp;
306 
307 	if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
308 	    (rfp->rf_flags & RCS_CREATE))
309 		return (0);
310 
311 	if (!(rfp->rf_flags & PARSED_DESC))
312 		if (rcsparse_desc(rfp))
313 			return (1);
314 
315 	rdp = (rev != NULL) ? rcs_findrev(rfp, rev) : NULL;
316 
317 	for (;;) {
318 		if (rdp != NULL && rdp->rd_text != NULL)
319 			break;
320 		ret = rcsparse_deltatext(rfp);
321 		if (ret == 0) {
322 			rfp->rf_flags |= PARSED_DELTATEXTS;
323 			break;
324 		}
325 		else if (ret == -1)
326 			return (1);
327 	}
328 
329 	return (0);
330 }
331 
332 /*
333  * rcsparse_free()
334  *
335  * Free the contents of the <rfp>'s parser data structure.
336  */
337 void
rcsparse_free(RCSFILE * rfp)338 rcsparse_free(RCSFILE *rfp)
339 {
340 	struct rcs_pdata *pdp;
341 
342 	pdp = rfp->rf_pdata;
343 
344 	free(pdp->rp_buf);
345 	if (pdp->rp_token == RCS_TYPE_REVISION)
346 		free(pdp->rp_value.rev);
347 	free(pdp);
348 }
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
rcsparse_desc(RCSFILE * rfp)361 rcsparse_desc(RCSFILE *rfp)
362 {
363 	struct rcs_pdata *pdp;
364 
365 	if (rfp->rf_flags & PARSED_DESC)
366 		return (0);
367 
368 	if (!(rfp->rf_flags & PARSED_DELTAS) && rcsparse_deltas(rfp, NULL))
369 		return (1);
370 
371 	pdp = (struct rcs_pdata *)rfp->rf_pdata;
372 
373 	if (rcsparse_token(rfp, RCS_TOK_DESC) != RCS_TOK_DESC ||
374 	    rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
375 		return (1);
376 
377 	rfp->rf_desc = pdp->rp_value.str;
378 	rfp->rf_flags |= PARSED_DESC;
379 
380 	return (0);
381 }
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
rcsparse_deltarevision(RCSFILE * rfp,struct rcs_pdata * pdp)395 rcsparse_deltarevision(RCSFILE *rfp, struct rcs_pdata *pdp)
396 {
397 	struct rcs_delta *rdp;
398 
399 	rdp = xcalloc(1, sizeof(*rdp));
400 	TAILQ_INIT(&rdp->rd_branches);
401 	rdp->rd_num = pdp->rp_value.rev;
402 	pdp->rp_delta = rdp;
403 
404 	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
rcsparse_date(RCSFILE * rfp,struct rcs_pdata * pdp)417 rcsparse_date(RCSFILE *rfp, struct rcs_pdata *pdp)
418 {
419 	if (rcsparse_token(rfp, RCS_TYPE_DATE) != RCS_TYPE_DATE)
420 		return (1);
421 
422 	pdp->rp_delta->rd_date = pdp->rp_value.date;
423 
424 	return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
425 }
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
rcsparse_author(RCSFILE * rfp,struct rcs_pdata * pdp)437 rcsparse_author(RCSFILE *rfp, struct rcs_pdata *pdp)
438 {
439 	if (rcsparse_token(rfp, RCS_TYPE_LOGIN) != RCS_TYPE_LOGIN)
440 		return (1);
441 
442 	pdp->rp_delta->rd_author = pdp->rp_value.str;
443 
444 	return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
445 }
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
rcsparse_state(RCSFILE * rfp,struct rcs_pdata * pdp)457 rcsparse_state(RCSFILE *rfp, struct rcs_pdata *pdp)
458 {
459 	if (rcsparse_token(rfp, RCS_TYPE_STATE) != RCS_TYPE_STATE)
460 		return (1);
461 
462 	pdp->rp_delta->rd_state = pdp->rp_value.str;
463 
464 	return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
465 }
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
rcsparse_branches(RCSFILE * rfp,struct rcs_pdata * pdp)477 rcsparse_branches(RCSFILE *rfp, struct rcs_pdata *pdp)
478 {
479 	struct rcs_branch *rb;
480 	int type;
481 
482 	while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_REVISION))
483 	    == RCS_TYPE_REVISION) {
484 		rb = xmalloc(sizeof(*rb));
485 		rb->rb_num = pdp->rp_value.rev;
486 		TAILQ_INSERT_TAIL(&(pdp->rp_delta->rd_branches), rb, rb_list);
487 	}
488 
489 	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
rcsparse_next(RCSFILE * rfp,struct rcs_pdata * pdp)502 rcsparse_next(RCSFILE *rfp, struct rcs_pdata *pdp)
503 {
504 	int type;
505 
506 	type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
507 	if (type == RCS_TYPE_REVISION) {
508 		pdp->rp_delta->rd_next = pdp->rp_value.rev;
509 		type = rcsparse_token(rfp, RCS_TOK_SCOLON);
510 	} else
511 		pdp->rp_delta->rd_next = rcsnum_alloc();
512 
513 	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
rcsparse_commitid(RCSFILE * rfp,struct rcs_pdata * pdp)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
rcsparse_textrevision(RCSFILE * rfp,struct rcs_pdata * pdp)549 rcsparse_textrevision(RCSFILE *rfp, struct rcs_pdata *pdp)
550 {
551 	struct rcs_delta *rdp;
552 
553 	TAILQ_FOREACH(rdp, &rfp->rf_delta, rd_list) {
554 		if (rcsnum_cmp(rdp->rd_num, pdp->rp_value.rev, 0) == 0)
555 			break;
556 	}
557 	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 	pdp->rp_delta = rdp;
564 
565 	free(pdp->rp_value.rev);
566 	return (0);
567 }
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
rcsparse_log(RCSFILE * rfp,struct rcs_pdata * pdp)579 rcsparse_log(RCSFILE *rfp, struct rcs_pdata *pdp)
580 {
581 	if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
582 		return (1);
583 
584 	pdp->rp_delta->rd_log = pdp->rp_value.str;
585 
586 	return (0);
587 }
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
rcsparse_text(RCSFILE * rfp,struct rcs_pdata * pdp)599 rcsparse_text(RCSFILE *rfp, struct rcs_pdata *pdp)
600 {
601 	if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
602 		return (1);
603 
604 	pdp->rp_delta->rd_tlen = pdp->rp_tlen - 1;
605 	if (pdp->rp_delta->rd_tlen == 0) {
606 		pdp->rp_delta->rd_text = xstrdup("");
607 	} else {
608 		pdp->rp_delta->rd_text = xmalloc(pdp->rp_delta->rd_tlen);
609 		memcpy(pdp->rp_delta->rd_text, pdp->rp_buf,
610 		    pdp->rp_delta->rd_tlen);
611 	}
612 	free(pdp->rp_value.str);
613 
614 	return (0);
615 }
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
rcsparse_head(RCSFILE * rfp,struct rcs_pdata * pdp)627 rcsparse_head(RCSFILE *rfp, struct rcs_pdata *pdp)
628 {
629 	int type;
630 
631 	type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
632 	if (type == RCS_TYPE_REVISION) {
633 		rfp->rf_head = pdp->rp_value.rev;
634 		type = rcsparse_token(rfp, RCS_TOK_SCOLON);
635 	}
636 
637 	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
rcsparse_branch(RCSFILE * rfp,struct rcs_pdata * pdp)651 rcsparse_branch(RCSFILE *rfp, struct rcs_pdata *pdp)
652 {
653 	int type;
654 
655 	type = rcsparse_token(rfp, RCS_TYPE_BRANCH|RCS_TOK_SCOLON);
656 	if (type == RCS_TYPE_BRANCH) {
657 		rfp->rf_branch = pdp->rp_value.rev;
658 		type = rcsparse_token(rfp, RCS_TOK_SCOLON);
659 	}
660 
661 	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
rcsparse_access(RCSFILE * rfp,struct rcs_pdata * pdp)674 rcsparse_access(RCSFILE *rfp, struct rcs_pdata *pdp)
675 {
676 	struct rcs_access *ap;
677 	int type;
678 
679 	while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN))
680 	    == 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 	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
rcsparse_symbols(RCSFILE * rfp,struct rcs_pdata * pdp)699 rcsparse_symbols(RCSFILE *rfp, struct rcs_pdata *pdp)
700 {
701 	struct rcs_sym *symp;
702 	char *name;
703 	int type;
704 
705 	while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
706 	    RCS_TYPE_SYMBOL) {
707 		name = pdp->rp_value.str;
708 		if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
709 		    rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
710 			free(name);
711 			return (1);
712 		}
713 		symp = xmalloc(sizeof(*symp));
714 		symp->rs_name = name;
715 		symp->rs_num = pdp->rp_value.rev;
716 		TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
717 	}
718 
719 	return (type != RCS_TOK_SCOLON);
720 }
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
rcsparse_locks(RCSFILE * rfp,struct rcs_pdata * pdp)732 rcsparse_locks(RCSFILE *rfp, struct rcs_pdata *pdp)
733 {
734 	struct rcs_lock *lkp;
735 	char *name;
736 	int type;
737 
738 	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 	return (type != RCS_TOK_SCOLON);
754 }
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
rcsparse_strict(RCSFILE * rfp,struct rcs_pdata * pdp)767 rcsparse_strict(RCSFILE *rfp, struct rcs_pdata *pdp)
768 {
769 	rfp->rf_flags |= RCS_SLOCK;
770 
771 	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
rcsparse_comment(RCSFILE * rfp,struct rcs_pdata * pdp)785 rcsparse_comment(RCSFILE *rfp, struct rcs_pdata *pdp)
786 {
787 	int type;
788 
789 	type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
790 	if (type == RCS_TYPE_STRING) {
791 		rfp->rf_comment = pdp->rp_value.str;
792 		type = rcsparse_token(rfp, RCS_TOK_SCOLON);
793 	}
794 
795 	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
rcsparse_expand(RCSFILE * rfp,struct rcs_pdata * pdp)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
rcsparse_string(RCSFILE * rfp,int allowed)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 	pdp = (struct rcs_pdata *)rfp->rf_pdata;
842 
843 	bp = pdp->rp_buf;
844 	pdp->rp_tlen = 0;
845 	*bp = '\0';
846 
847 	for (;;) {
848 		c = getc(rfp->rf_file);
849 		if (c == '@') {
850 			c = getc(rfp->rf_file);
851 			if (c == EOF) {
852 				return (EOF);
853 			} else if (c != '@') {
854 				ungetc(c, rfp->rf_file);
855 				break;
856 			}
857 		}
858 
859 		if (c == EOF) {
860 			return (EOF);
861 		} else if (c == '\n')
862 			pdp->rp_lineno++;
863 
864 		RBUF_PUTC(c);
865 	}
866 
867 	bp = pdp->rp_buf + pdp->rp_tlen;
868 	RBUF_PUTC('\0');
869 
870 	if (!(allowed & RCS_TYPE_STRING)) {
871 		rcsparse_warnx(rfp, "unexpected RCS string");
872 		return (0);
873 	}
874 
875 	pdp->rp_value.str = xstrdup(pdp->rp_buf);
876 
877 	return (RCS_TYPE_STRING);
878 }
879 
880 static int
rcsparse_token(RCSFILE * rfp,int allowed)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 	pdp = (struct rcs_pdata *)rfp->rf_pdata;
891 
892 	if (pdp->rp_token != -1) {
893 		/* no need to check for allowed here */
894 		type = pdp->rp_token;
895 		pdp->rp_token = -1;
896 		return (type);
897 	}
898 
899 	/* skip whitespaces */
900 	c = EOF;
901 	do {
902 		pre = c;
903 		c = getc(rfp->rf_file);
904 		if (c == EOF) {
905 			if (ferror(rfp->rf_file)) {
906 				rcsparse_warnx(rfp, "error during parsing");
907 				return (0);
908 			}
909 			if (pre != '\n')
910 				rcsparse_warnx(rfp,
911 				    "no newline at end of file");
912 			return (EOF);
913 		} else if (c == '\n')
914 			pdp->rp_lineno++;
915 	} while (isspace(c));
916 
917 	pdp->rp_msglineno = pdp->rp_lineno;
918 	type = 0;
919 	switch (c) {
920 	case '@':
921 		ret = rcsparse_string(rfp, allowed);
922 		if (ret == EOF && ferror(rfp->rf_file)) {
923 			rcsparse_warnx(rfp, "error during parsing");
924 			return (0);
925 		}
926 		return (ret);
927 		/* NOTREACHED */
928 	case ':':
929 		type = RCS_TOK_COLON;
930 		if (type & allowed)
931 			return (type);
932 		rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
933 		return (0);
934 		/* NOTREACHED */
935 	case ';':
936 		type = RCS_TOK_SCOLON;
937 		if (type & allowed)
938 			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 		if (!isgraph(c)) {
951 			rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
952 			return (0);
953 		}
954 		break;
955 	}
956 	allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
957 
958 	bp = pdp->rp_buf;
959 	pdp->rp_tlen = 0;
960 	*bp = '\0';
961 
962 	for (;;) {
963 		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 		} else if (c == '\n')
970 			pdp->rp_lineno++;
971 
972 		RBUF_PUTC(c);
973 
974 		c = getc(rfp->rf_file);
975 
976 		if (isspace(c)) {
977 			if (c == '\n')
978 				pdp->rp_lineno++;
979 			RBUF_PUTC('\0');
980 			break;
981 		} else if (c == ';' || c == ':' || c == ',') {
982 			ungetc(c, rfp->rf_file);
983 			RBUF_PUTC('\0');
984 			break;
985 		} else if (!isgraph(c)) {
986 			rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
987 			return (0);
988 		}
989 	}
990 
991 	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 		if (!valid_login(pdp->rp_buf)) {
1002 			rcsparse_warnx(rfp, "invalid login \"%s\"",
1003 			    pdp->rp_buf);
1004 			return (0);
1005 		}
1006 		pdp->rp_value.str = xstrdup(pdp->rp_buf);
1007 		break;
1008 	case RCS_TYPE_SYMBOL:
1009 		if (!rcs_sym_check(pdp->rp_buf)) {
1010 			rcsparse_warnx(rfp, "invalid symbol \"%s\"",
1011 			    pdp->rp_buf);
1012 			return (0);
1013 		}
1014 		pdp->rp_value.str = xstrdup(pdp->rp_buf);
1015 		break;
1016 		/* FALLTHROUGH */
1017 	case RCS_TYPE_STATE:
1018 		if (rcs_state_check(pdp->rp_buf)) {
1019 			rcsparse_warnx(rfp, "invalid state \"%s\"",
1020 			    pdp->rp_buf);
1021 			return (0);
1022 		}
1023 		pdp->rp_value.str = xstrdup(pdp->rp_buf);
1024 		break;
1025 	case RCS_TYPE_DATE:
1026 		if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
1027 			rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1028 			return (0);
1029 		}
1030 		if (datenum->rn_len != 6) {
1031 			free(datenum);
1032 			rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
1033 			return (0);
1034 		}
1035 		pdp->rp_value.date.tm_year = datenum->rn_id[0];
1036 		if (pdp->rp_value.date.tm_year >= 1900)
1037 			pdp->rp_value.date.tm_year -= 1900;
1038 		pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
1039 		pdp->rp_value.date.tm_mday = datenum->rn_id[2];
1040 		pdp->rp_value.date.tm_hour = datenum->rn_id[3];
1041 		pdp->rp_value.date.tm_min = datenum->rn_id[4];
1042 		pdp->rp_value.date.tm_sec = datenum->rn_id[5];
1043 		free(datenum);
1044 		break;
1045 	case RCS_TYPE_NUMBER:
1046 		pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1047 		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 		pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1055 		if (pdp->rp_value.rev == NULL) {
1056 			rcsparse_warnx(rfp, "invalid branch \"%s\"",
1057 			    pdp->rp_buf);
1058 			return (0);
1059 		}
1060 		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 		if (islower(*pdp->rp_buf)) {
1069 			p = bsearch(pdp->rp_buf, keywords,
1070 			    sizeof(keywords) / sizeof(keywords[0]),
1071 			    sizeof(keywords[0]), kw_cmp);
1072 			if (p != NULL)
1073 				return (p->k_val);
1074 		}
1075 		allowed = RCS_TYPE_REVISION;
1076 		/* FALLTHROUGH */
1077 	case RCS_TYPE_REVISION:
1078 		pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
1079 		if (pdp->rp_value.rev != NULL) {
1080 			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 	return (allowed);
1098 }
1099 
1100 static int
rcsparse(RCSFILE * rfp,struct rcs_section * sec)1101 rcsparse(RCSFILE *rfp, struct rcs_section *sec)
1102 {
1103 	struct rcs_pdata *pdp;
1104 	int i, token;
1105 
1106 	pdp = (struct rcs_pdata *)rfp->rf_pdata;
1107 	i = 0;
1108 
1109 	token = 0;
1110 	for (i = 0; sec[i].token != 0; i++) {
1111 		token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
1112 		if (token == 0)
1113 			return (1);
1114 
1115 		while (token != sec[i].token) {
1116 			if (sec[i].parse == NULL)
1117 				goto end;
1118 			if (sec[i].opt) {
1119 				i++;
1120 				continue;
1121 			}
1122 			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 		if (sec[i].parse(rfp, pdp))
1131 			return (1);
1132 	}
1133 end:
1134 	if (token == RCS_TYPE_REVISION)
1135 		pdp->rp_token = token;
1136 	else if (token == RCS_TOK_DESC)
1137 		pdp->rp_token = RCS_TOK_DESC;
1138 	else if (token == EOF)
1139 		rfp->rf_flags |= RCS_PARSED;
1140 
1141 	return (0);
1142 }
1143 
1144 static int
rcsparse_deltatext(RCSFILE * rfp)1145 rcsparse_deltatext(RCSFILE *rfp)
1146 {
1147 	int ret;
1148 
1149 	if (rfp->rf_flags & PARSED_DELTATEXTS)
1150 		return (0);
1151 
1152 	if (!(rfp->rf_flags & PARSED_DESC))
1153 		if ((ret = rcsparse_desc(rfp)))
1154 			return (ret);
1155 
1156 	if (rcsparse(rfp, sec_deltatext))
1157 		return (-1);
1158 
1159 	if (rfp->rf_flags & RCS_PARSED)
1160 		rfp->rf_flags |= PARSED_DELTATEXTS;
1161 
1162 	return (1);
1163 }
1164 
1165 static int
rcsparse_delta(RCSFILE * rfp)1166 rcsparse_delta(RCSFILE *rfp)
1167 {
1168 	struct rcs_pdata *pdp;
1169 
1170 	if (rfp->rf_flags & PARSED_DELTAS)
1171 		return (0);
1172 
1173 	pdp = (struct rcs_pdata *)rfp->rf_pdata;
1174 	if (pdp->rp_token == RCS_TOK_DESC) {
1175 		rfp->rf_flags |= PARSED_DELTAS;
1176 		return (0);
1177 	}
1178 
1179 	if (rcsparse(rfp, sec_delta))
1180 		return (-1);
1181 
1182 	if (pdp->rp_delta != NULL) {
1183 		TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
1184 		pdp->rp_delta = NULL;
1185 		rfp->rf_ndelta++;
1186 		return (1);
1187 	}
1188 
1189 	return (0);
1190 }
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
rcsparse_growbuf(RCSFILE * rfp)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
valid_login(char * login_name)1215 valid_login(char *login_name)
1216 {
1217 	unsigned char *cp;
1218 
1219 	/* The first character cannot be a hyphen */
1220 	if (*login_name == '-')
1221 		return 0;
1222 
1223 	for (cp = login_name ; *cp ; cp++) {
1224 		/* We allow '$' as the last character for samba */
1225 		if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
1226 		    !(*cp == '$' && *(cp + 1) == '\0')) {
1227 			return 0;
1228 		}
1229 	}
1230 	if ((char *)cp - login_name > _PW_NAME_LEN)
1231 		return 0;
1232 	return 1;
1233 }
1234 
1235 static int
valid_commitid(char * commitid)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
kw_cmp(const void * k,const void * e)1251 kw_cmp(const void *k, const void *e)
1252 {
1253 	return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
1254 }
1255 
1256 static void
rcsparse_warnx(RCSFILE * rfp,char * fmt,...)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 }
1274