xref: /openbsd-src/usr.bin/cvs/rcs.c (revision 4deeb87832e5d00dbfea5b08ed9c463296b435ef)
1 /*	$OpenBSD: rcs.c,v 1.9 2004/08/12 21:02:20 jfb Exp $	*/
2 /*
3  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/queue.h>
29 #include <sys/stat.h>
30 
31 #include <errno.h>
32 #include <stdio.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "rcs.h"
38 #include "log.h"
39 
40 #define RCS_BUFSIZE   8192
41 
42 
43 /* RCS token types */
44 #define RCS_TOK_ERR     -1
45 #define RCS_TOK_EOF      0
46 #define RCS_TOK_NUM      1
47 #define RCS_TOK_ID       2
48 #define RCS_TOK_STRING   3
49 #define RCS_TOK_SCOLON   4
50 #define RCS_TOK_COLON    5
51 
52 
53 #define RCS_TOK_HEAD     8
54 #define RCS_TOK_BRANCH   9
55 #define RCS_TOK_ACCESS   10
56 #define RCS_TOK_SYMBOLS  11
57 #define RCS_TOK_LOCKS    12
58 #define RCS_TOK_COMMENT  13
59 #define RCS_TOK_EXPAND   14
60 #define RCS_TOK_DATE     15
61 #define RCS_TOK_AUTHOR   16
62 #define RCS_TOK_STATE    17
63 #define RCS_TOK_NEXT     18
64 #define RCS_TOK_BRANCHES 19
65 #define RCS_TOK_DESC     20
66 #define RCS_TOK_LOG      21
67 #define RCS_TOK_TEXT     22
68 #define RCS_TOK_STRICT   23
69 
70 #define RCS_ISKEY(t)    (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
71 
72 
73 #define RCS_NOSCOL   0x01   /* no terminating semi-colon */
74 #define RCS_VOPT     0x02   /* value is optional */
75 
76 
77 
78 /* opaque parse data */
79 struct rcs_pdata {
80 	u_int  rp_line;
81 
82 	char  *rp_buf;
83 	size_t rp_blen;
84 
85 	/* pushback token buffer */
86 	char   rp_ptok[128];
87 	int    rp_pttype;       /* token type, RCS_TOK_ERR if no token */
88 
89 	FILE  *rp_file;
90 };
91 
92 
93 struct rcs_line {
94 	char *rl_line;
95 	int   rl_lineno;
96 	TAILQ_ENTRY(rcs_line) rl_list;
97 };
98 TAILQ_HEAD(rcs_tqh, rcs_line);
99 
100 struct rcs_foo {
101 	int       rl_nblines;
102 	char     *rl_data;
103 	struct rcs_tqh rl_lines;
104 };
105 
106 static int  rcs_parse_admin     (RCSFILE *);
107 static int  rcs_parse_delta     (RCSFILE *);
108 static int  rcs_parse_deltatext (RCSFILE *);
109 
110 static int      rcs_parse_access    (RCSFILE *);
111 static int      rcs_parse_symbols   (RCSFILE *);
112 static int      rcs_parse_locks     (RCSFILE *);
113 static int      rcs_parse_branches  (RCSFILE *, struct rcs_delta *);
114 static void     rcs_freedelta       (struct rcs_delta *);
115 static void     rcs_freepdata       (struct rcs_pdata *);
116 static int      rcs_gettok          (RCSFILE *);
117 static int      rcs_pushtok         (RCSFILE *, const char *, int);
118 static int      rcs_patch_lines     (struct rcs_foo *, struct rcs_foo *);
119 
120 static struct rcs_delta*  rcs_findrev    (RCSFILE *, RCSNUM *);
121 static struct rcs_foo*    rcs_splitlines (const char *);
122 static void               rcs_freefoo    (struct rcs_foo *);
123 
124 #define RCS_TOKSTR(rfp)   ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
125 #define RCS_TOKLEN(rfp)   ((struct rcs_pdata *)rfp->rf_pdata)->rp_blen
126 
127 
128 static struct rcs_key {
129 	char  rk_str[16];
130 	int   rk_id;
131 	int   rk_val;
132 	int   rk_flags;
133 } rcs_keys[] = {
134 	{ "access",   RCS_TOK_ACCESS,   RCS_TOK_ID,     RCS_VOPT     },
135 	{ "author",   RCS_TOK_AUTHOR,   RCS_TOK_STRING, 0            },
136 	{ "branch",   RCS_TOK_BRANCH,   RCS_TOK_NUM,    RCS_VOPT     },
137 	{ "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM,    RCS_VOPT     },
138 	{ "comment",  RCS_TOK_COMMENT,  RCS_TOK_STRING, RCS_VOPT     },
139 	{ "date",     RCS_TOK_DATE,     RCS_TOK_NUM,    0            },
140 	{ "desc",     RCS_TOK_DESC,     RCS_TOK_STRING, RCS_NOSCOL   },
141 	{ "expand",   RCS_TOK_EXPAND,   RCS_TOK_STRING, RCS_VOPT     },
142 	{ "head",     RCS_TOK_HEAD,     RCS_TOK_NUM,    RCS_VOPT     },
143 	{ "locks",    RCS_TOK_LOCKS,    RCS_TOK_ID,     0            },
144 	{ "log",      RCS_TOK_LOG,      RCS_TOK_STRING, RCS_NOSCOL   },
145 	{ "next",     RCS_TOK_NEXT,     RCS_TOK_NUM,    RCS_VOPT     },
146 	{ "state",    RCS_TOK_STATE,    RCS_TOK_STRING, RCS_VOPT     },
147 	{ "strict",   RCS_TOK_STRICT,   0,              0,           },
148 	{ "symbols",  RCS_TOK_SYMBOLS,  0,              0            },
149 	{ "text",     RCS_TOK_TEXT,     RCS_TOK_STRING, RCS_NOSCOL   },
150 };
151 
152 
153 
154 /*
155  * rcs_open()
156  *
157  * Open a file containing RCS-formatted information.  The file's path is
158  * given in <path>, and the opening mode is given in <mode>, which is either
159  * RCS_MODE_READ, RCS_MODE_WRITE, or RCS_MODE_RDWR.  If the mode requests write
160  * access and the file does not exist, it will be created.
161  * The file isn't actually parsed by rcs_open(); parsing is delayed until the
162  * first operation that requires information from the file.
163  * Returns a handle to the opened file on success, or NULL on failure.
164  */
165 
166 RCSFILE*
167 rcs_open(const char *path, u_int mode)
168 {
169 	RCSFILE *rfp;
170 	struct stat st;
171 
172 	if ((stat(path, &st) == -1) && (errno == ENOENT) &&
173 	   !(mode & RCS_MODE_WRITE)) {
174 		cvs_log(LP_ERRNO, "cannot open RCS file `%s'", path);
175 		return (NULL);
176 	}
177 
178 	rfp = (RCSFILE *)malloc(sizeof(*rfp));
179 	if (rfp == NULL) {
180 		cvs_log(LP_ERRNO, "failed to allocate RCS file structure");
181 		return (NULL);
182 	}
183 	memset(rfp, 0, sizeof(*rfp));
184 
185 	rfp->rf_head = rcsnum_alloc();
186 	if (rfp->rf_head == NULL) {
187 		free(rfp);
188 		return (NULL);
189 	}
190 
191 	rfp->rf_path = strdup(path);
192 	if (rfp->rf_path == NULL) {
193 		cvs_log(LP_ERRNO, "failed to duplicate RCS file path");
194 		rcs_close(rfp);
195 		return (NULL);
196 	}
197 
198 	rcsnum_aton(RCS_HEAD_INIT, NULL, rfp->rf_head);
199 
200 	rfp->rf_ref = 1;
201 	rfp->rf_flags |= RCS_RF_SLOCK;
202 	rfp->rf_mode = mode;
203 
204 	TAILQ_INIT(&(rfp->rf_delta));
205 	TAILQ_INIT(&(rfp->rf_symbols));
206 	TAILQ_INIT(&(rfp->rf_locks));
207 
208 	if (rcs_parse(rfp) < 0) {
209 		rcs_close(rfp);
210 		return (NULL);
211 	}
212 
213 	return (rfp);
214 }
215 
216 
217 /*
218  * rcs_close()
219  *
220  * Close an RCS file handle.
221  */
222 
223 void
224 rcs_close(RCSFILE *rfp)
225 {
226 	struct rcs_delta *rdp;
227 
228 	if (rfp->rf_ref > 1) {
229 		rfp->rf_ref--;
230 		return;
231 	}
232 
233 	while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
234 		rdp = TAILQ_FIRST(&(rfp->rf_delta));
235 		TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
236 		rcs_freedelta(rdp);
237 	}
238 
239 	if (rfp->rf_head != NULL)
240 		rcsnum_free(rfp->rf_head);
241 
242 	if (rfp->rf_path != NULL)
243 		free(rfp->rf_path);
244 	if (rfp->rf_comment != NULL)
245 		free(rfp->rf_comment);
246 	if (rfp->rf_expand != NULL)
247 		free(rfp->rf_expand);
248 	if (rfp->rf_desc != NULL)
249 		free(rfp->rf_desc);
250 	free(rfp);
251 }
252 
253 
254 /*
255  * rcs_write()
256  *
257  * Write the contents of the RCS file handle <rfp> to disk in the file whose
258  * path is in <rf_path>.
259  * Returns 0 on success, or -1 on failure.
260  */
261 
262 int
263 rcs_write(RCSFILE *rfp)
264 {
265 	FILE *fp;
266 	char buf[1024], numbuf[64], *cp;
267 	size_t rlen, len;
268 	struct rcs_sym *symp;
269 	struct rcs_delta *rdp;
270 
271 	if (rfp->rf_flags & RCS_RF_SYNCED)
272 		return (0);
273 
274 	fp = fopen(rfp->rf_path, "w");
275 	if (fp == NULL) {
276 		cvs_log(LP_ERRNO, "failed to open RCS output file `%s'",
277 		    rfp->rf_path);
278 		return (-1);
279 	}
280 
281 	rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
282 	fprintf(fp, "head\t%s;\n", numbuf);
283 	fprintf(fp, "access;\n");
284 
285 	fprintf(fp, "symbols\n");
286 	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
287 		rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
288 		snprintf(buf, sizeof(buf), "%s:%s", symp->rs_name, numbuf);
289 		fprintf(fp, "\t%s", buf);
290 		if (symp != TAILQ_LAST(&(rfp->rf_symbols), rcs_slist))
291 			fputc('\n', fp);
292 	}
293 	fprintf(fp, ";\n");
294 
295 	fprintf(fp, "locks;");
296 
297 	if (rfp->rf_flags & RCS_RF_SLOCK)
298 		fprintf(fp, " strict;");
299 	fputc('\n', fp);
300 
301 	if (rfp->rf_comment != NULL)
302 		fprintf(fp, "comment\t@%s@;\n", rfp->rf_comment);
303 
304 	if (rfp->rf_expand != NULL)
305 		fprintf(fp, "expand @ %s @;\n", rfp->rf_expand);
306 
307 	fprintf(fp, "\n\n");
308 
309 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
310 		fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
311 		    sizeof(numbuf)));
312 		fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
313 		    rdp->rd_date.tm_year, rdp->rd_date.tm_mon + 1,
314 		    rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
315 		    rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
316 		fprintf(fp, "\tauthor %s;\tstate %s;\n",
317 		    rdp->rd_author, rdp->rd_state);
318 		fprintf(fp, "branches;\n");
319 		fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
320 		    numbuf, sizeof(numbuf)));
321 	}
322 
323 	fprintf(fp, "\ndesc\n@%s@\n\n", rfp->rf_desc);
324 
325 	/* deltatexts */
326 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
327 		fprintf(fp, "\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
328 		    sizeof(numbuf)));
329 		fprintf(fp, "log\n@%s@\ntext\n@", rdp->rd_log);
330 
331 		cp = rdp->rd_text;
332 		do {
333 			len = sizeof(buf);
334 			rlen = rcs_stresc(1, cp, buf, &len);
335 			fprintf(fp, "%s", buf);
336 			cp += rlen;
337 		} while (len != 0);
338 		fprintf(fp, "@\n\n");
339 	}
340 	fclose(fp);
341 
342 	rfp->rf_flags |= RCS_RF_SYNCED;
343 
344 	return (0);
345 }
346 
347 
348 /*
349  * rcs_addsym()
350  *
351  * Add a symbol to the list of symbols for the RCS file <rfp>.  The new symbol
352  * is named <sym> and is bound to the RCS revision <snum>.
353  * Returns 0 on success, or -1 on failure.
354  */
355 
356 int
357 rcs_addsym(RCSFILE *rfp, const char *sym, RCSNUM *snum)
358 {
359 	struct rcs_sym *symp;
360 
361 	/* first look for duplication */
362 	TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
363 		if (strcmp(symp->rs_name, sym) == 0) {
364 			return (-1);
365 		}
366 	}
367 
368 	symp = (struct rcs_sym *)malloc(sizeof(*symp));
369 	if (symp == NULL) {
370 		cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
371 		return (-1);
372 	}
373 
374 	symp->rs_name = strdup(sym);
375 	symp->rs_num = rcsnum_alloc();
376 	rcsnum_cpy(snum, symp->rs_num, 0);
377 
378 	TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
379 
380 	/* not synced anymore */
381 	rfp->rf_flags &= ~RCS_RF_SYNCED;
382 
383 	return (0);
384 }
385 
386 
387 /*
388  * rcs_patch()
389  *
390  * Apply an RCS-format patch pointed to by <patch> to the file contents
391  * found in <data>.
392  * Returns 0 on success, or -1 on failure.
393  */
394 
395 BUF*
396 rcs_patch(const char *data, const char *patch)
397 {
398 	struct rcs_foo *dlines, *plines;
399 	struct rcs_line *lp;
400 	size_t len;
401 	int lineno;
402 	BUF *res;
403 
404 	len = strlen(data);
405 	res = cvs_buf_alloc(len, BUF_AUTOEXT);
406 	if (res == NULL)
407 		return (NULL);
408 
409 	dlines = rcs_splitlines(data);
410 	if (dlines == NULL)
411 		return (NULL);
412 
413 	plines = rcs_splitlines(patch);
414 	if (plines == NULL) {
415 		rcs_freefoo(dlines);
416 		return (NULL);
417 	}
418 
419 	if (rcs_patch_lines(dlines, plines) < 0) {
420 		rcs_freefoo(plines);
421 		rcs_freefoo(dlines);
422 		return (NULL);
423 	}
424 
425 	lineno = 0;
426 	TAILQ_FOREACH(lp, &dlines->rl_lines, rl_list) {
427 		if (lineno != 0)
428 			cvs_buf_fappend(res, "%s\n", lp->rl_line);
429 		lineno++;
430 	}
431 
432 	rcs_freefoo(dlines);
433 	rcs_freefoo(plines);
434 	return (res);
435 }
436 
437 static int
438 rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
439 {
440 	char op, *ep;
441 	struct rcs_line *lp, *dlp, *ndlp;
442 	int i, lineno, nbln;
443 
444 	dlp = TAILQ_FIRST(&(dlines->rl_lines));
445 	lp = TAILQ_FIRST(&(plines->rl_lines));
446 
447 	/* skip first bogus line */
448 	for (lp = TAILQ_NEXT(lp, rl_list); lp != NULL;
449 	    lp = TAILQ_NEXT(lp, rl_list)) {
450 		op = *(lp->rl_line);
451 		lineno = (int)strtol((lp->rl_line + 1), &ep, 10);
452 		if ((lineno > dlines->rl_nblines) || (lineno <= 0) ||
453 		    (*ep != ' ')) {
454 			cvs_log(LP_ERR,
455 			    "invalid line specification in RCS patch");
456 			return (NULL);
457 		}
458 		ep++;
459 		nbln = (int)strtol(ep, &ep, 10);
460 		if ((nbln <= 0) || (*ep != '\0')) {
461 			cvs_log(LP_ERR,
462 			    "invalid line number specification in RCS patch");
463 			return (NULL);
464 		}
465 
466 		/* find the appropriate line */
467 		for (;;) {
468 			if (dlp == NULL)
469 				break;
470 			if (dlp->rl_lineno == lineno)
471 				break;
472 			if (dlp->rl_lineno > lineno) {
473 				dlp = TAILQ_PREV(dlp, rcs_tqh, rl_list);
474 			}
475 			else if (dlp->rl_lineno < lineno) {
476 				ndlp = TAILQ_NEXT(dlp, rl_list);
477 				if (ndlp->rl_lineno > lineno)
478 					break;
479 				dlp = ndlp;
480 			}
481 		}
482 		if (dlp == NULL) {
483 			cvs_log(LP_ERR,
484 			    "can't find referenced line in RCS patch");
485 			return (NULL);
486 		}
487 
488 		if (op == 'd') {
489 			for (i = 0; (i < nbln) && (dlp != NULL); i++) {
490 				ndlp = TAILQ_NEXT(dlp, rl_list);
491 				TAILQ_REMOVE(&(dlines->rl_lines), dlp, rl_list);
492 				dlp = ndlp;
493 			}
494 		}
495 		else if (op == 'a') {
496 			for (i = 0; i < nbln; i++) {
497 				ndlp = lp;
498 				lp = TAILQ_NEXT(lp, rl_list);
499 				if (lp == NULL) {
500 					cvs_log(LP_ERR, "truncated RCS patch");
501 					return (-1);
502 				}
503 				TAILQ_REMOVE(&(plines->rl_lines), lp, rl_list);
504 				TAILQ_INSERT_AFTER(&(dlines->rl_lines), dlp,
505 				    lp, rl_list);
506 				dlp = lp;
507 
508 				/* we don't want lookup to block on those */
509 				lp->rl_lineno = lineno;
510 
511 				lp = ndlp;
512 			}
513 		}
514 		else {
515 			cvs_log(LP_ERR, "unknown RCS patch operation `%c'", op);
516 			return (-1);
517 		}
518 
519 		/* last line of the patch, done */
520 		if (lp->rl_lineno == plines->rl_nblines)
521 			break;
522 	}
523 
524 	/* once we're done patching, rebuild the line numbers */
525 	lineno = 0;
526 	TAILQ_FOREACH(lp, &(dlines->rl_lines), rl_list)
527 		lp->rl_lineno = lineno++;
528 	dlines->rl_nblines = lineno - 1;
529 
530 	return (0);
531 }
532 
533 
534 /*
535  * rcs_getrev()
536  *
537  * Get the whole contents of revision <rev> from the RCSFILE <rfp>.  The
538  * returned buffer is dynamically allocated and should be released using
539  * cvs_buf_free() once the caller is done using it.
540  */
541 
542 BUF*
543 rcs_getrev(RCSFILE *rfp, RCSNUM *rev)
544 {
545 	int res;
546 	size_t len;
547 	void *bp;
548 	RCSNUM *crev;
549 	BUF *rbuf;
550 	struct rcs_delta *rdp = NULL;
551 
552 	res = rcsnum_cmp(rfp->rf_head, rev, 0);
553 	if (res == 1) {
554 		cvs_log(LP_ERR, "sorry, can't travel in the future yet");
555 		return (NULL);
556 	}
557 	else {
558 		rdp = rcs_findrev(rfp, rfp->rf_head);
559 		if (rdp == NULL) {
560 			cvs_log(LP_ERR, "failed to get RCS HEAD revision");
561 			return (NULL);
562 		}
563 
564 		len = strlen(rdp->rd_text);
565 		rbuf = cvs_buf_alloc(len, BUF_AUTOEXT);
566 		if (rbuf == NULL)
567 			return (NULL);
568 		cvs_buf_append(rbuf, rdp->rd_text, len);
569 
570 		if (res != 0) {
571 			/* Apply patches backwards to get the right version.
572 			 * This will need some rework to support sub branches.
573 			 */
574 			crev = rcsnum_alloc();
575 
576 			rcsnum_cpy(rfp->rf_head, crev, 0);
577 			do {
578 				crev->rn_id[crev->rn_len - 1]--;
579 				rdp = rcs_findrev(rfp, crev);
580 				if (rdp == NULL)
581 					return (NULL);
582 
583 				cvs_buf_putc(rbuf, '\0');
584 				bp = cvs_buf_release(rbuf);
585 				rbuf = rcs_patch((char *)bp, rdp->rd_text);
586 				if (rbuf == NULL)
587 					break;
588 			} while (rcsnum_cmp(crev, rev, 0) != 0);
589 
590 			rcsnum_free(crev);
591 		}
592 	}
593 
594 
595 	return (rbuf);
596 }
597 
598 
599 /*
600  * rcs_getrevbydate()
601  *
602  * Get an RCS revision by a specific date.
603  */
604 
605 RCSNUM*
606 rcs_getrevbydate(RCSFILE *rfp, struct tm *date)
607 {
608 	return (NULL);
609 }
610 
611 
612 /*
613  * rcs_findrev()
614  *
615  * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
616  * The revision number is given in <rev>.
617  * Returns a pointer to the delta on success, or NULL on failure.
618  */
619 
620 static struct rcs_delta*
621 rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
622 {
623 	u_int cmplen;
624 	struct rcs_delta *rdp;
625 	struct rcs_dlist *hp;
626 	int found;
627 
628 	cmplen = 2;
629 	hp = &(rfp->rf_delta);
630 
631 	do {
632 		found = 0;
633 		TAILQ_FOREACH(rdp, hp, rd_list) {
634 			if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) {
635 				if (cmplen == rev->rn_len)
636 					return (rdp);
637 
638 				hp = &(rdp->rd_snodes);
639 				cmplen += 2;
640 				found = 1;
641 				break;
642 			}
643 		}
644 	} while (found && cmplen < rev->rn_len);
645 
646 	return (NULL);
647 }
648 
649 
650 /*
651  * rcs_parse()
652  *
653  * Parse the contents of file <path>, which are in the RCS format.
654  * Returns 0 on success, or -1 on failure.
655  */
656 
657 int
658 rcs_parse(RCSFILE *rfp)
659 {
660 	int ret;
661 	struct rcs_pdata *pdp;
662 
663 	if (rfp->rf_flags & RCS_RF_PARSED)
664 		return (0);
665 
666 	pdp = (struct rcs_pdata *)malloc(sizeof(*pdp));
667 	if (pdp == NULL) {
668 		cvs_log(LP_ERRNO, "failed to allocate RCS parser data");
669 		return (-1);
670 	}
671 	memset(pdp, 0, sizeof(*pdp));
672 
673 	pdp->rp_line = 1;
674 	pdp->rp_pttype = RCS_TOK_ERR;
675 
676 	pdp->rp_file = fopen(rfp->rf_path, "r");
677 	if (pdp->rp_file == NULL) {
678 		cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path);
679 		rcs_freepdata(pdp);
680 		return (-1);
681 	}
682 
683 	pdp->rp_buf = (char *)malloc(RCS_BUFSIZE);
684 	if (pdp->rp_buf == NULL) {
685 		cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer");
686 		rcs_freepdata(pdp);
687 		return (-1);
688 	}
689 	pdp->rp_blen = RCS_BUFSIZE;
690 
691 	/* ditch the strict lock */
692 	rfp->rf_flags &= ~RCS_RF_SLOCK;
693 	rfp->rf_pdata = pdp;
694 
695 	if (rcs_parse_admin(rfp) < 0) {
696 		rcs_freepdata(pdp);
697 		return (-1);
698 	}
699 
700 	for (;;) {
701 		ret = rcs_parse_delta(rfp);
702 		if (ret == 0)
703 			break;
704 		else if (ret == -1) {
705 			rcs_freepdata(pdp);
706 			return (-1);
707 		}
708 	}
709 
710 	ret = rcs_gettok(rfp);
711 	if (ret != RCS_TOK_DESC) {
712 		cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
713 		    RCS_TOKSTR(rfp));
714 		rcs_freepdata(pdp);
715 		return (-1);
716 	}
717 
718 	ret = rcs_gettok(rfp);
719 	if (ret != RCS_TOK_STRING) {
720 		cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
721 		    RCS_TOKSTR(rfp));
722 		rcs_freepdata(pdp);
723 		return (-1);
724 	}
725 
726 	rfp->rf_desc = strdup(RCS_TOKSTR(rfp));
727 
728 	for (;;) {
729 		ret = rcs_parse_deltatext(rfp);
730 		if (ret == 0)
731 			break;
732 		else if (ret == -1) {
733 			rcs_freepdata(pdp);
734 			return (-1);
735 		}
736 	}
737 
738 	cvs_log(LP_DEBUG, "RCS file `%s' parsed OK (%u lines)", rfp->rf_path,
739 	    pdp->rp_line);
740 
741 	rcs_freepdata(pdp);
742 
743 	rfp->rf_pdata = NULL;
744 	rfp->rf_flags |= RCS_RF_PARSED|RCS_RF_SYNCED;
745 
746 	return (0);
747 }
748 
749 
750 /*
751  * rcs_parse_admin()
752  *
753  * Parse the administrative portion of an RCS file.
754  * Returns 0 on success, or -1 on failure.
755  */
756 
757 static int
758 rcs_parse_admin(RCSFILE *rfp)
759 {
760 	u_int i;
761 	int tok, ntok, hmask;
762 	struct rcs_key *rk;
763 
764 	/* hmask is a mask of the headers already encountered */
765 	hmask = 0;
766 	for (;;) {
767 		tok = rcs_gettok(rfp);
768 		if (tok == RCS_TOK_ERR) {
769 			cvs_log(LP_ERR, "parse error in RCS admin section");
770 			return (-1);
771 		}
772 		else if (tok == RCS_TOK_NUM) {
773 			/* assume this is the start of the first delta */
774 			rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
775 			return (0);
776 		}
777 
778 		rk = NULL;
779 		for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++)
780 			if (rcs_keys[i].rk_id == tok)
781 				rk = &(rcs_keys[i]);
782 
783 		if (hmask & (1 << tok)) {
784 			cvs_log(LP_ERR, "duplicate RCS key");
785 			return (-1);
786 		}
787 		hmask |= (1 << tok);
788 
789 		switch (tok) {
790 		case RCS_TOK_HEAD:
791 		case RCS_TOK_BRANCH:
792 		case RCS_TOK_COMMENT:
793 		case RCS_TOK_EXPAND:
794 			ntok = rcs_gettok(rfp);
795 			if (ntok == RCS_TOK_SCOLON)
796 				break;
797 			if (ntok != rk->rk_val) {
798 				cvs_log(LP_ERR,
799 				    "invalid value type for RCS key `%s'",
800 				    rk->rk_str);
801 			}
802 
803 			if (tok == RCS_TOK_HEAD) {
804 				rcsnum_aton(RCS_TOKSTR(rfp), NULL,
805 				    rfp->rf_head);
806 			}
807 			else if (tok == RCS_TOK_BRANCH) {
808 				rcsnum_aton(RCS_TOKSTR(rfp), NULL,
809 				    rfp->rf_branch);
810 			}
811 			else if (tok == RCS_TOK_COMMENT) {
812 				rfp->rf_comment = strdup(RCS_TOKSTR(rfp));
813 			}
814 			else if (tok == RCS_TOK_EXPAND) {
815 				rfp->rf_expand = strdup(RCS_TOKSTR(rfp));
816 			}
817 
818 			/* now get the expected semi-colon */
819 			ntok = rcs_gettok(rfp);
820 			if (ntok != RCS_TOK_SCOLON) {
821 				cvs_log(LP_ERR,
822 				    "missing semi-colon after RCS `%s' key",
823 				    rk->rk_str);
824 				return (-1);
825 			}
826 			break;
827 		case RCS_TOK_ACCESS:
828 			rcs_parse_access(rfp);
829 			break;
830 		case RCS_TOK_SYMBOLS:
831 			rcs_parse_symbols(rfp);
832 			break;
833 		case RCS_TOK_LOCKS:
834 			rcs_parse_locks(rfp);
835 			break;
836 		default:
837 			cvs_log(LP_ERR,
838 			    "unexpected token `%s' in RCS admin section",
839 			    RCS_TOKSTR(rfp));
840 			return (-1);
841 		}
842 	}
843 
844 	return (0);
845 }
846 
847 
848 /*
849  * rcs_parse_delta()
850  *
851  * Parse an RCS delta section and allocate the structure to store that delta's
852  * information in the <rfp> delta list.
853  * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
854  * -1 on error.
855  */
856 
857 static int
858 rcs_parse_delta(RCSFILE *rfp)
859 {
860 	int ret, tok, ntok, hmask;
861 	u_int i;
862 	char *tokstr;
863 	RCSNUM *datenum;
864 	struct rcs_delta *rdp;
865 	struct rcs_key *rk;
866 
867 	rdp = (struct rcs_delta *)malloc(sizeof(*rdp));
868 	if (rdp == NULL) {
869 		cvs_log(LP_ERRNO, "failed to allocate RCS delta structure");
870 		return (-1);
871 	}
872 	memset(rdp, 0, sizeof(*rdp));
873 
874 	rdp->rd_num = rcsnum_alloc();
875 	rdp->rd_next = rcsnum_alloc();
876 
877 	TAILQ_INIT(&(rdp->rd_branches));
878 
879 	tok = rcs_gettok(rfp);
880 	if (tok != RCS_TOK_NUM) {
881 		cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
882 		    RCS_TOKSTR(rfp));
883 		rcs_freedelta(rdp);
884 		return (-1);
885 	}
886 	rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
887 
888 	hmask = 0;
889 	ret = 0;
890 	tokstr = NULL;
891 
892 	for (;;) {
893 		tok = rcs_gettok(rfp);
894 		if (tok == RCS_TOK_ERR) {
895 			cvs_log(LP_ERR, "parse error in RCS delta section");
896 			rcs_freedelta(rdp);
897 			return (-1);
898 		}
899 		else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
900 			rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
901 			ret = (tok == RCS_TOK_NUM ? 1 : 0);
902 			break;
903 		}
904 
905 		rk = NULL;
906 		for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++)
907 			if (rcs_keys[i].rk_id == tok)
908 				rk = &(rcs_keys[i]);
909 
910 		if (hmask & (1 << tok)) {
911 			cvs_log(LP_ERR, "duplicate RCS key");
912 			rcs_freedelta(rdp);
913 			return (-1);
914 		}
915 		hmask |= (1 << tok);
916 
917 		switch (tok) {
918 		case RCS_TOK_DATE:
919 		case RCS_TOK_AUTHOR:
920 		case RCS_TOK_STATE:
921 		case RCS_TOK_NEXT:
922 			ntok = rcs_gettok(rfp);
923 			if (ntok == RCS_TOK_SCOLON) {
924 				if (rk->rk_flags & RCS_VOPT)
925 					break;
926 				else {
927 					cvs_log(LP_ERR, "missing mandatory "
928 					    "value to RCS key `%s'",
929 					    rk->rk_str);
930 					rcs_freedelta(rdp);
931 					return (-1);
932 				}
933 			}
934 
935 			if (ntok != rk->rk_val) {
936 				cvs_log(LP_ERR,
937 				    "invalid value type for RCS key `%s'",
938 				    rk->rk_str);
939 				rcs_freedelta(rdp);
940 				return (-1);
941 			}
942 
943 			if (tokstr != NULL)
944 				free(tokstr);
945 			tokstr = strdup(RCS_TOKSTR(rfp));
946 
947 
948 			/* now get the expected semi-colon */
949 			ntok = rcs_gettok(rfp);
950 			if (ntok != RCS_TOK_SCOLON) {
951 				cvs_log(LP_ERR,
952 				    "missing semi-colon after RCS `%s' key",
953 				    rk->rk_str);
954 				rcs_freedelta(rdp);
955 				return (-1);
956 			}
957 
958 			if (tok == RCS_TOK_DATE) {
959 				datenum = rcsnum_alloc();
960 				rcsnum_aton(tokstr, NULL, datenum);
961 				if (datenum->rn_len != 6) {
962 					cvs_log(LP_ERR,
963 					    "RCS date specification has %s "
964 					    "fields",
965 					    (datenum->rn_len > 6) ? "too many" :
966 					    "missing");
967 					rcs_freedelta(rdp);
968 				}
969 				rdp->rd_date.tm_year = datenum->rn_id[0];
970 				rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
971 				rdp->rd_date.tm_mday = datenum->rn_id[2];
972 				rdp->rd_date.tm_hour = datenum->rn_id[3];
973 				rdp->rd_date.tm_min = datenum->rn_id[4];
974 				rdp->rd_date.tm_sec = datenum->rn_id[5];
975 				rcsnum_free(datenum);
976 			}
977 			else if (tok == RCS_TOK_AUTHOR) {
978 				rdp->rd_author = tokstr;
979 				tokstr = NULL;
980 			}
981 			else if (tok == RCS_TOK_STATE) {
982 				rdp->rd_state = tokstr;
983 				tokstr = NULL;
984 			}
985 			else if (tok == RCS_TOK_NEXT) {
986 				rcsnum_aton(tokstr, NULL, rdp->rd_next);
987 			}
988 			break;
989 		case RCS_TOK_BRANCHES:
990 			rcs_parse_branches(rfp, rdp);
991 			break;
992 		default:
993 			cvs_log(LP_ERR,
994 			    "unexpected token `%s' in RCS delta",
995 			    RCS_TOKSTR(rfp));
996 			rcs_freedelta(rdp);
997 			return (-1);
998 		}
999 	}
1000 
1001 	TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1002 
1003 	return (ret);
1004 }
1005 
1006 
1007 /*
1008  * rcs_parse_deltatext()
1009  *
1010  * Parse an RCS delta text section and fill in the log and text field of the
1011  * appropriate delta section.
1012  * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1013  * -1 on error.
1014  */
1015 
1016 static int
1017 rcs_parse_deltatext(RCSFILE *rfp)
1018 {
1019 	int tok;
1020 	RCSNUM *tnum;
1021 	struct rcs_delta *rdp;
1022 
1023 	tnum = rcsnum_alloc();
1024 	if (tnum == NULL)
1025 		return (-1);
1026 
1027 	tok = rcs_gettok(rfp);
1028 	if (tok == RCS_TOK_EOF)
1029 		return (0);
1030 
1031 	if (tok != RCS_TOK_NUM) {
1032 		cvs_log(LP_ERR,
1033 		    "unexpected token `%s' at start of RCS delta text",
1034 		    RCS_TOKSTR(rfp));
1035 		return (-1);
1036 	}
1037 	rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1038 
1039 	TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1040 		if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1041 			break;
1042 	}
1043 	if (rdp == NULL) {
1044 		cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1045 		    RCS_TOKSTR(rfp));
1046 		return (-1);
1047 	}
1048 
1049 	tok = rcs_gettok(rfp);
1050 	if (tok != RCS_TOK_LOG) {
1051 		cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1052 		    RCS_TOKSTR(rfp));
1053 		return (-1);
1054 	}
1055 
1056 	tok = rcs_gettok(rfp);
1057 	if (tok != RCS_TOK_STRING) {
1058 		cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1059 		    RCS_TOKSTR(rfp));
1060 		return (-1);
1061 	}
1062 	rdp->rd_log = strdup(RCS_TOKSTR(rfp));
1063 	if (rdp->rd_log == NULL) {
1064 		cvs_log(LP_ERRNO, "failed to copy RCS deltatext log");
1065 		return (-1);
1066 	}
1067 
1068 	tok = rcs_gettok(rfp);
1069 	if (tok != RCS_TOK_TEXT) {
1070 		cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1071 		    RCS_TOKSTR(rfp));
1072 		return (-1);
1073 	}
1074 
1075 	tok = rcs_gettok(rfp);
1076 	if (tok != RCS_TOK_STRING) {
1077 		cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1078 		    RCS_TOKSTR(rfp));
1079 		return (-1);
1080 	}
1081 
1082 	rdp->rd_text = strdup(RCS_TOKSTR(rfp));
1083 	if (rdp->rd_text == NULL) {
1084 		cvs_log(LP_ERRNO, "failed to copy RCS delta text");
1085 		return (-1);
1086 	}
1087 
1088 	return (1);
1089 }
1090 
1091 
1092 /*
1093  * rcs_parse_access()
1094  *
1095  * Parse the access list given as value to the `access' keyword.
1096  * Returns 0 on success, or -1 on failure.
1097  */
1098 
1099 static int
1100 rcs_parse_access(RCSFILE *rfp)
1101 {
1102 	int type;
1103 
1104 	while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
1105 		if (type != RCS_TOK_ID) {
1106 			cvs_log(LP_ERR, "unexpected token `%s' in access list",
1107 			    RCS_TOKSTR(rfp));
1108 			return (-1);
1109 		}
1110 	}
1111 
1112 	return (0);
1113 }
1114 
1115 
1116 /*
1117  * rcs_parse_symbols()
1118  *
1119  * Parse the symbol list given as value to the `symbols' keyword.
1120  * Returns 0 on success, or -1 on failure.
1121  */
1122 
1123 static int
1124 rcs_parse_symbols(RCSFILE *rfp)
1125 {
1126 	int type;
1127 	struct rcs_sym *symp;
1128 
1129 	for (;;) {
1130 		type = rcs_gettok(rfp);
1131 		if (type == RCS_TOK_SCOLON)
1132 			break;
1133 
1134 		if (type != RCS_TOK_STRING) {
1135 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1136 			    RCS_TOKSTR(rfp));
1137 			return (-1);
1138 		}
1139 
1140 		symp = (struct rcs_sym *)malloc(sizeof(*symp));
1141 		if (symp == NULL) {
1142 			cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
1143 			return (-1);
1144 		}
1145 		symp->rs_name = strdup(RCS_TOKSTR(rfp));
1146 		symp->rs_num = rcsnum_alloc();
1147 
1148 		type = rcs_gettok(rfp);
1149 		if (type != RCS_TOK_COLON) {
1150 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1151 			    RCS_TOKSTR(rfp));
1152 			free(symp->rs_name);
1153 			free(symp);
1154 			return (-1);
1155 		}
1156 
1157 		type = rcs_gettok(rfp);
1158 		if (type != RCS_TOK_NUM) {
1159 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1160 			    RCS_TOKSTR(rfp));
1161 			free(symp->rs_name);
1162 			free(symp);
1163 			return (-1);
1164 		}
1165 
1166 		if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
1167 			cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1168 			    RCS_TOKSTR(rfp));
1169 			free(symp->rs_name);
1170 			free(symp);
1171 			return (-1);
1172 		}
1173 
1174 		TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
1175 	}
1176 
1177 	return (0);
1178 }
1179 
1180 
1181 /*
1182  * rcs_parse_locks()
1183  *
1184  * Parse the lock list given as value to the `locks' keyword.
1185  * Returns 0 on success, or -1 on failure.
1186  */
1187 
1188 static int
1189 rcs_parse_locks(RCSFILE *rfp)
1190 {
1191 	int type;
1192 	struct rcs_lock *lkp;
1193 
1194 	for (;;) {
1195 		type = rcs_gettok(rfp);
1196 		if (type == RCS_TOK_SCOLON)
1197 			break;
1198 
1199 		if (type != RCS_TOK_ID) {
1200 			cvs_log(LP_ERR, "unexpected token `%s' in lock list",
1201 			    RCS_TOKSTR(rfp));
1202 			return (-1);
1203 		}
1204 
1205 		lkp = (struct rcs_lock *)malloc(sizeof(*lkp));
1206 		if (lkp == NULL) {
1207 			cvs_log(LP_ERRNO, "failed to allocate RCS lock");
1208 			return (-1);
1209 		}
1210 		lkp->rl_num = rcsnum_alloc();
1211 
1212 		type = rcs_gettok(rfp);
1213 		if (type != RCS_TOK_COLON) {
1214 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1215 			    RCS_TOKSTR(rfp));
1216 			free(lkp);
1217 			return (-1);
1218 		}
1219 
1220 		type = rcs_gettok(rfp);
1221 		if (type != RCS_TOK_NUM) {
1222 			cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1223 			    RCS_TOKSTR(rfp));
1224 			free(lkp);
1225 			return (-1);
1226 		}
1227 
1228 		if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
1229 			cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1230 			    RCS_TOKSTR(rfp));
1231 			free(lkp);
1232 			return (-1);
1233 		}
1234 
1235 		TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
1236 	}
1237 
1238 	/* check if we have a `strict' */
1239 	type = rcs_gettok(rfp);
1240 	if (type != RCS_TOK_STRICT) {
1241 		rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1242 	}
1243 	else {
1244 		rfp->rf_flags |= RCS_RF_SLOCK;
1245 
1246 		type = rcs_gettok(rfp);
1247 		if (type != RCS_TOK_SCOLON) {
1248 			cvs_log(LP_ERR,
1249 			    "missing semi-colon after `strict' keyword");
1250 			return (-1);
1251 		}
1252 	}
1253 
1254 	return (0);
1255 }
1256 
1257 /*
1258  * rcs_parse_branches()
1259  *
1260  * Parse the list of branches following a `branches' keyword in a delta.
1261  * Returns 0 on success, or -1 on failure.
1262  */
1263 
1264 static int
1265 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
1266 {
1267 	int type;
1268 	struct rcs_branch *brp;
1269 
1270 	for (;;) {
1271 		type = rcs_gettok(rfp);
1272 		if (type == RCS_TOK_SCOLON)
1273 			break;
1274 
1275 		if (type != RCS_TOK_NUM) {
1276 			cvs_log(LP_ERR,
1277 			    "unexpected token `%s' in list of branches",
1278 			    RCS_TOKSTR(rfp));
1279 			return (-1);
1280 		}
1281 
1282 		brp = (struct rcs_branch *)malloc(sizeof(*brp));
1283 		if (brp == NULL) {
1284 			cvs_log(LP_ERRNO, "failed to allocate RCS branch");
1285 			return (-1);
1286 		}
1287 		brp->rb_num = rcsnum_alloc();
1288 		rcsnum_aton(RCS_TOKSTR(rfp), NULL, brp->rb_num);
1289 
1290 		TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
1291 	}
1292 
1293 	return (0);
1294 }
1295 
1296 
1297 /*
1298  * rcs_freedelta()
1299  *
1300  * Free the contents of a delta structure.
1301  */
1302 
1303 void
1304 rcs_freedelta(struct rcs_delta *rdp)
1305 {
1306 	struct rcs_delta *crdp;
1307 
1308 	if (rdp->rd_author != NULL)
1309 		free(rdp->rd_author);
1310 	if (rdp->rd_state != NULL)
1311 		free(rdp->rd_state);
1312 	if (rdp->rd_log != NULL)
1313 		free(rdp->rd_log);
1314 	if (rdp->rd_text != NULL)
1315 		free(rdp->rd_text);
1316 
1317 	while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) {
1318 		TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list);
1319 		rcs_freedelta(crdp);
1320 	}
1321 
1322 	free(rdp);
1323 }
1324 
1325 
1326 /*
1327  * rcs_freepdata()
1328  *
1329  * Free the contents of the parser data structure.
1330  */
1331 
1332 static void
1333 rcs_freepdata(struct rcs_pdata *pd)
1334 {
1335 	if (pd->rp_file != NULL)
1336 		(void)fclose(pd->rp_file);
1337 	if (pd->rp_buf != NULL)
1338 		free(pd->rp_buf);
1339 	free(pd);
1340 }
1341 
1342 
1343 /*
1344  * rcs_gettok()
1345  *
1346  * Get the next RCS token from the string <str>.
1347  */
1348 
1349 static int
1350 rcs_gettok(RCSFILE *rfp)
1351 {
1352 	u_int i;
1353 	int ch, last, type;
1354 	char *bp, *bep;
1355 	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1356 
1357 	type = RCS_TOK_ERR;
1358 	bp = pdp->rp_buf;
1359 	bep = pdp->rp_buf + pdp->rp_blen - 1;
1360 	*bp = '\0';
1361 
1362 	if (pdp->rp_pttype != RCS_TOK_ERR) {
1363 		type = pdp->rp_pttype;
1364 		strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen);
1365 		pdp->rp_pttype = RCS_TOK_ERR;
1366 		return (type);
1367 	}
1368 
1369 	/* skip leading whitespace */
1370 	/* XXX we must skip backspace too for compatibility, should we? */
1371 	do {
1372 		ch = getc(pdp->rp_file);
1373 		if (ch == '\n')
1374 			pdp->rp_line++;
1375 	} while (isspace(ch));
1376 
1377 	if (ch == EOF) {
1378 		type = RCS_TOK_EOF;
1379 	}
1380 	else if (ch == ';') {
1381 		type = RCS_TOK_SCOLON;
1382 	}
1383 	else if (ch == ':') {
1384 		type = RCS_TOK_COLON;
1385 	}
1386 	else if (isalpha(ch)) {
1387 		*(bp++) = ch;
1388 		while (bp <= bep - 1) {
1389 			ch = getc(pdp->rp_file);
1390 			if (!isalnum(ch)) {
1391 				ungetc(ch, pdp->rp_file);
1392 				break;
1393 			}
1394 			*(bp++) = ch;
1395 		}
1396 		*bp = '\0';
1397 
1398 		for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++) {
1399 			if (strcmp(rcs_keys[i].rk_str, pdp->rp_buf) == 0) {
1400 				type = rcs_keys[i].rk_id;
1401 				break;
1402 			}
1403 		}
1404 
1405 		/* not a keyword, assume it's just a string */
1406 		if (type == RCS_TOK_ERR)
1407 			type = RCS_TOK_STRING;
1408 	}
1409 	else if (ch == '@') {
1410 		/* we have a string */
1411 		for (;;) {
1412 			ch = getc(pdp->rp_file);
1413 			if (ch == '@') {
1414 				ch = getc(pdp->rp_file);
1415 				if (ch != '@') {
1416 					ungetc(ch, pdp->rp_file);
1417 					break;
1418 				}
1419 			}
1420 			else if (ch == '\n')
1421 				pdp->rp_line++;
1422 
1423 			*(bp++) = ch;
1424 			if (bp == bep)
1425 				break;
1426 		}
1427 
1428 		*bp = '\0';
1429 		type = RCS_TOK_STRING;
1430 	}
1431 	else if (isdigit(ch)) {
1432 		*(bp++) = ch;
1433 		last = ch;
1434 		type = RCS_TOK_NUM;
1435 
1436 		for (;;) {
1437 			ch = getc(pdp->rp_file);
1438 			if (bp == bep)
1439 				break;
1440 			if (!isdigit(ch) && ch != '.') {
1441 				ungetc(ch, pdp->rp_file);
1442 				break;
1443 			}
1444 
1445 			if (last == '.' && ch == '.') {
1446 				type = RCS_TOK_ERR;
1447 				break;
1448 			}
1449 			last = ch;
1450 			*(bp++) = ch;
1451 		}
1452 		*(bp) = '\0';
1453 	}
1454 
1455 	return (type);
1456 }
1457 
1458 
1459 /*
1460  * rcs_pushtok()
1461  *
1462  * Push a token back in the parser's token buffer.
1463  */
1464 
1465 static int
1466 rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
1467 {
1468 	struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1469 
1470 	if (pdp->rp_pttype != RCS_TOK_ERR)
1471 		return (-1);
1472 
1473 	pdp->rp_pttype = type;
1474 	strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok));
1475 	return (0);
1476 }
1477 
1478 
1479 /*
1480  * rcs_stresc()
1481  *
1482  * Performs either escaping or unescaping of the string stored in <str>.
1483  * The operation is to escape special RCS characters if the <esc> argument
1484  * is 1, or unescape otherwise.  The result is stored in the <buf> destination
1485  * buffer, and <blen> must originally point to the size of <buf>.
1486  * Returns the number of bytes which have been read from the source <str> and
1487  * operated on.  The <blen> parameter will contain the number of bytes
1488  * actually copied in <buf>.
1489  */
1490 
1491 size_t
1492 rcs_stresc(int esc, const char *str, char *buf, size_t *blen)
1493 {
1494 	size_t rlen;
1495 	const char *sp;
1496 	char *bp, *bep;
1497 
1498 	rlen = 0;
1499 	bp = buf;
1500 	bep = buf + *blen - 1;
1501 
1502 	for (sp = str; (*sp != '\0') && (bp <= (bep - 1)); sp++) {
1503 		if (*sp == '@') {
1504 			if (esc) {
1505 				if (bp > (bep - 2))
1506 					break;
1507 				*(bp++) = '@';
1508 			}
1509 			else {
1510 				sp++;
1511 				if (*sp != '@') {
1512 					cvs_log(LP_WARN,
1513 					    "unknown escape character `%c' in "
1514 					    "RCS file", *sp);
1515 					if (*sp == '\0')
1516 						break;
1517 				}
1518 			}
1519 		}
1520 
1521 		*(bp++) = *sp;
1522 	}
1523 
1524 	*bp = '\0';
1525 	*blen = (bp - buf);
1526 	return (sp - str);
1527 }
1528 
1529 
1530 /*
1531  * rcs_splitlines()
1532  *
1533  * Split the contents of a file into a list of lines.
1534  */
1535 
1536 static struct rcs_foo*
1537 rcs_splitlines(const char *fcont)
1538 {
1539 	char *dcp;
1540 	struct rcs_foo *foo;
1541 	struct rcs_line *lp;
1542 
1543 	foo = (struct rcs_foo *)malloc(sizeof(*foo));
1544 	if (foo == NULL) {
1545 		cvs_log(LP_ERR, "failed to allocate line structure");
1546 		return (NULL);
1547 	}
1548 	TAILQ_INIT(&(foo->rl_lines));
1549 	foo->rl_nblines = 0;
1550 	foo->rl_data = strdup(fcont);
1551 	if (foo->rl_data == NULL) {
1552 		cvs_log(LP_ERRNO, "failed to copy file contents");
1553 		free(foo);
1554 		return (NULL);
1555 	}
1556 
1557 	/*
1558 	 * Add a first bogus line with line number 0.  This is used so we
1559 	 * can position the line pointer before 1 when changing the first line
1560 	 * in rcs_patch().
1561 	 */
1562 	lp = (struct rcs_line *)malloc(sizeof(*lp));
1563 	if (lp == NULL)
1564 		return (NULL);
1565 
1566 	lp->rl_line = NULL;
1567 	lp->rl_lineno = 0;
1568 	TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1569 
1570 
1571 	for (dcp = foo->rl_data; *dcp != '\0';) {
1572 		lp = (struct rcs_line *)malloc(sizeof(*lp));
1573 		if (lp == NULL) {
1574 			cvs_log(LP_ERR, "failed to allocate line entry");
1575 			return (NULL);
1576 		}
1577 
1578 		lp->rl_line = dcp;
1579 		lp->rl_lineno = ++(foo->rl_nblines);
1580 		TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
1581 
1582 		dcp = strchr(dcp, '\n');
1583 		if (dcp == NULL) {
1584 			break;
1585 		}
1586 		*(dcp++) = '\0';
1587 	}
1588 
1589 	return (foo);
1590 }
1591 
1592 static void
1593 rcs_freefoo(struct rcs_foo *fp)
1594 {
1595 	struct rcs_line *lp;
1596 
1597 	while ((lp = TAILQ_FIRST(&fp->rl_lines)) != NULL) {
1598 		TAILQ_REMOVE(&fp->rl_lines, lp, rl_list);
1599 		free(lp);
1600 	}
1601 	free(fp->rl_data);
1602 	free(fp);
1603 }
1604