Annotation of src/usr.bin/cvs/rcs.c, Revision 1.32
1.31 jfb 1: /* $OpenBSD: rcs.c,v 1.30 2005/03/05 03:30:29 jfb Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.15 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.15 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.15 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.15 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 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
1.15 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 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>
1.26 jfb 35: #include <stdarg.h>
1.1 jfb 36: #include <string.h>
37:
38: #include "rcs.h"
39: #include "log.h"
40:
1.26 jfb 41: #define RCS_BUFSIZE 16384
1.18 jfb 42: #define RCS_BUFEXTSIZE 8192
1.1 jfb 43:
44:
45: /* RCS token types */
46: #define RCS_TOK_ERR -1
47: #define RCS_TOK_EOF 0
48: #define RCS_TOK_NUM 1
49: #define RCS_TOK_ID 2
50: #define RCS_TOK_STRING 3
51: #define RCS_TOK_SCOLON 4
52: #define RCS_TOK_COLON 5
53:
54:
55: #define RCS_TOK_HEAD 8
56: #define RCS_TOK_BRANCH 9
57: #define RCS_TOK_ACCESS 10
58: #define RCS_TOK_SYMBOLS 11
59: #define RCS_TOK_LOCKS 12
60: #define RCS_TOK_COMMENT 13
61: #define RCS_TOK_EXPAND 14
62: #define RCS_TOK_DATE 15
63: #define RCS_TOK_AUTHOR 16
64: #define RCS_TOK_STATE 17
65: #define RCS_TOK_NEXT 18
66: #define RCS_TOK_BRANCHES 19
67: #define RCS_TOK_DESC 20
68: #define RCS_TOK_LOG 21
69: #define RCS_TOK_TEXT 22
70: #define RCS_TOK_STRICT 23
71:
72: #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
73:
74:
75: #define RCS_NOSCOL 0x01 /* no terminating semi-colon */
76: #define RCS_VOPT 0x02 /* value is optional */
77:
78:
79: /* opaque parse data */
80: struct rcs_pdata {
1.18 jfb 81: u_int rp_lines;
1.1 jfb 82:
83: char *rp_buf;
84: size_t rp_blen;
1.18 jfb 85: char *rp_bufend;
1.1 jfb 86:
87: /* pushback token buffer */
88: char rp_ptok[128];
89: int rp_pttype; /* token type, RCS_TOK_ERR if no token */
90:
91: FILE *rp_file;
92: };
93:
94:
95: struct rcs_line {
96: char *rl_line;
97: int rl_lineno;
98: TAILQ_ENTRY(rcs_line) rl_list;
99: };
1.5 vincent 100: TAILQ_HEAD(rcs_tqh, rcs_line);
1.1 jfb 101:
102: struct rcs_foo {
103: int rl_nblines;
104: char *rl_data;
1.5 vincent 105: struct rcs_tqh rl_lines;
1.1 jfb 106: };
107:
108: #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
109: #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_blen
110:
111:
1.20 jfb 112: static struct rcs_kfl {
113: char rk_char;
114: int rk_val;
115: } rcs_kflags[] = {
116: { 'k', RCS_KWEXP_NAME },
117: { 'v', RCS_KWEXP_VAL },
118: { 'l', RCS_KWEXP_LKR },
119: { 'o', RCS_KWEXP_OLD },
120: { 'b', RCS_KWEXP_NONE },
121: };
122:
1.1 jfb 123: static struct rcs_key {
124: char rk_str[16];
125: int rk_id;
126: int rk_val;
127: int rk_flags;
128: } rcs_keys[] = {
129: { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT },
130: { "author", RCS_TOK_AUTHOR, RCS_TOK_STRING, 0 },
131: { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT },
132: { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT },
133: { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT },
134: { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 },
135: { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL },
136: { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT },
137: { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT },
138: { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 },
139: { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL },
140: { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT },
141: { "state", RCS_TOK_STATE, RCS_TOK_STRING, RCS_VOPT },
142: { "strict", RCS_TOK_STRICT, 0, 0, },
143: { "symbols", RCS_TOK_SYMBOLS, 0, 0 },
144: { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL },
145: };
146:
1.18 jfb 147: #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0]))
1.1 jfb 148:
1.32 ! jfb 149: static const char *rcs_errstrs[] = {
! 150: "No error",
! 151: "No such entry",
! 152: "Duplicate entry found",
! 153: "Bad RCS number",
! 154: };
! 155:
! 156: #define RCS_NERR (sizeof(rcs_errstrs)/sizeof(rcs_errstrs[0]))
! 157:
! 158:
! 159: int rcs_errno = RCS_ERR_NOERR;
! 160:
1.1 jfb 161:
1.27 jfb 162: static int rcs_write (RCSFILE *);
1.26 jfb 163: static int rcs_parse (RCSFILE *);
164: static int rcs_parse_admin (RCSFILE *);
165: static int rcs_parse_delta (RCSFILE *);
166: static int rcs_parse_deltatext (RCSFILE *);
167:
168: static int rcs_parse_access (RCSFILE *);
169: static int rcs_parse_symbols (RCSFILE *);
170: static int rcs_parse_locks (RCSFILE *);
171: static int rcs_parse_branches (RCSFILE *, struct rcs_delta *);
172: static void rcs_freedelta (struct rcs_delta *);
173: static void rcs_freepdata (struct rcs_pdata *);
174: static int rcs_gettok (RCSFILE *);
175: static int rcs_pushtok (RCSFILE *, const char *, int);
176: static int rcs_growbuf (RCSFILE *);
177: static int rcs_patch_lines (struct rcs_foo *, struct rcs_foo *);
178:
179: static struct rcs_delta* rcs_findrev (RCSFILE *, RCSNUM *);
180: static struct rcs_foo* rcs_splitlines (const char *);
181: static void rcs_freefoo (struct rcs_foo *);
182:
183:
1.1 jfb 184: /*
185: * rcs_open()
186: *
187: * Open a file containing RCS-formatted information. The file's path is
1.26 jfb 188: * given in <path>, and the opening flags are given in <flags>, which is either
189: * RCS_READ, RCS_WRITE, or RCS_RDWR. If the open requests write access and
190: * the file does not exist, the RCS_CREATE flag must also be given, in which
191: * case it will be created with the mode specified in a third argument of
192: * type mode_t. If the file exists and RCS_CREATE is passed, the open will
193: * fail.
1.1 jfb 194: * Returns a handle to the opened file on success, or NULL on failure.
195: */
196: RCSFILE*
1.26 jfb 197: rcs_open(const char *path, int flags, ...)
1.1 jfb 198: {
1.26 jfb 199: int ret;
200: mode_t fmode;
1.1 jfb 201: RCSFILE *rfp;
202: struct stat st;
1.26 jfb 203: va_list vap;
204:
205: fmode = 0;
206: flags &= 0xffff; /* ditch any internal flags */
1.1 jfb 207:
1.26 jfb 208: if (((ret = stat(path, &st)) == -1) && (errno == ENOENT)) {
209: if (flags & RCS_CREATE) {
210: va_start(vap, flags);
211: fmode = va_arg(vap, mode_t);
212: va_end(vap);
213: } else {
214: cvs_log(LP_ERR, "RCS file `%s' does not exist", path);
215: return (NULL);
216: }
217: } else if ((ret == 0) && (flags & RCS_CREATE)) {
218: cvs_log(LP_ERR, "RCS file `%s' exists", path);
1.1 jfb 219: return (NULL);
220: }
221:
1.26 jfb 222: if ((rfp = (RCSFILE *)malloc(sizeof(*rfp))) == NULL) {
1.1 jfb 223: cvs_log(LP_ERRNO, "failed to allocate RCS file structure");
224: return (NULL);
225: }
226: memset(rfp, 0, sizeof(*rfp));
227:
1.26 jfb 228: if ((rfp->rf_branch = rcsnum_alloc()) == NULL) {
229: free(rfp);
1.11 joris 230: return (NULL);
231: }
232:
1.26 jfb 233: if ((rfp->rf_path = strdup(path)) == NULL) {
1.1 jfb 234: cvs_log(LP_ERRNO, "failed to duplicate RCS file path");
1.26 jfb 235: rcsnum_free(rfp->rf_branch);
236: free(rfp);
1.1 jfb 237: return (NULL);
238: }
239:
240: rfp->rf_ref = 1;
1.26 jfb 241: rfp->rf_flags = flags | RCS_SLOCK;
242: rfp->rf_mode = fmode;
1.1 jfb 243:
244: TAILQ_INIT(&(rfp->rf_delta));
1.29 jfb 245: TAILQ_INIT(&(rfp->rf_access));
1.1 jfb 246: TAILQ_INIT(&(rfp->rf_symbols));
247: TAILQ_INIT(&(rfp->rf_locks));
248:
1.26 jfb 249: if (rfp->rf_flags & RCS_CREATE) {
250: } else if (rcs_parse(rfp) < 0) {
1.1 jfb 251: rcs_close(rfp);
252: return (NULL);
253: }
254:
255: return (rfp);
256: }
257:
258:
259: /*
260: * rcs_close()
261: *
262: * Close an RCS file handle.
263: */
264: void
265: rcs_close(RCSFILE *rfp)
266: {
267: struct rcs_delta *rdp;
1.13 jfb 268: struct rcs_lock *rlp;
269: struct rcs_sym *rsp;
1.1 jfb 270:
271: if (rfp->rf_ref > 1) {
272: rfp->rf_ref--;
273: return;
274: }
275:
1.26 jfb 276: if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
277: rcs_write(rfp);
278:
1.1 jfb 279: while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
280: rdp = TAILQ_FIRST(&(rfp->rf_delta));
281: TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
282: rcs_freedelta(rdp);
283: }
284:
1.13 jfb 285: while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
286: rsp = TAILQ_FIRST(&(rfp->rf_symbols));
287: TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
288: rcsnum_free(rsp->rs_num);
289: free(rsp->rs_name);
290: free(rsp);
291: }
292:
293: while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
294: rlp = TAILQ_FIRST(&(rfp->rf_locks));
295: TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
296: rcsnum_free(rlp->rl_num);
297: free(rlp);
298: }
299:
1.1 jfb 300: if (rfp->rf_head != NULL)
301: rcsnum_free(rfp->rf_head);
1.11 joris 302: if (rfp->rf_branch != NULL)
303: rcsnum_free(rfp->rf_branch);
1.1 jfb 304:
305: if (rfp->rf_path != NULL)
306: free(rfp->rf_path);
307: if (rfp->rf_comment != NULL)
308: free(rfp->rf_comment);
309: if (rfp->rf_expand != NULL)
310: free(rfp->rf_expand);
311: if (rfp->rf_desc != NULL)
312: free(rfp->rf_desc);
313: free(rfp);
314: }
315:
316:
317: /*
318: * rcs_write()
319: *
320: * Write the contents of the RCS file handle <rfp> to disk in the file whose
321: * path is in <rf_path>.
322: * Returns 0 on success, or -1 on failure.
323: */
1.27 jfb 324: static int
1.1 jfb 325: rcs_write(RCSFILE *rfp)
326: {
327: FILE *fp;
1.7 jfb 328: char buf[1024], numbuf[64], *cp;
329: size_t rlen, len;
1.29 jfb 330: struct rcs_access *ap;
1.1 jfb 331: struct rcs_sym *symp;
332: struct rcs_delta *rdp;
333:
1.28 jfb 334: if (rfp->rf_flags & RCS_SYNCED)
1.1 jfb 335: return (0);
336:
1.29 jfb 337: if ((fp = fopen(rfp->rf_path, "w")) == NULL) {
1.1 jfb 338: cvs_log(LP_ERRNO, "failed to open RCS output file `%s'",
339: rfp->rf_path);
340: return (-1);
341: }
342:
1.28 jfb 343: if (rfp->rf_head != NULL)
344: rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
345: else
346: numbuf[0] = '\0';
347:
1.1 jfb 348: fprintf(fp, "head\t%s;\n", numbuf);
1.29 jfb 349: fputs("access", fp);
350: TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
351: fprintf(fp, "\n\t%s", ap->ra_name);
352: }
353: fputs(";\n", fp);
1.1 jfb 354:
355: fprintf(fp, "symbols\n");
356: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
357: rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
358: snprintf(buf, sizeof(buf), "%s:%s", symp->rs_name, numbuf);
359: fprintf(fp, "\t%s", buf);
360: if (symp != TAILQ_LAST(&(rfp->rf_symbols), rcs_slist))
361: fputc('\n', fp);
362: }
363: fprintf(fp, ";\n");
364:
365: fprintf(fp, "locks;");
366:
1.26 jfb 367: if (rfp->rf_flags & RCS_SLOCK)
1.1 jfb 368: fprintf(fp, " strict;");
369: fputc('\n', fp);
370:
371: if (rfp->rf_comment != NULL)
372: fprintf(fp, "comment\t@%s@;\n", rfp->rf_comment);
373:
374: if (rfp->rf_expand != NULL)
375: fprintf(fp, "expand @ %s @;\n", rfp->rf_expand);
376:
377: fprintf(fp, "\n\n");
378:
379: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
380: fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
381: sizeof(numbuf)));
382: fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
383: rdp->rd_date.tm_year, rdp->rd_date.tm_mon + 1,
384: rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
385: rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
386: fprintf(fp, "\tauthor %s;\tstate %s;\n",
387: rdp->rd_author, rdp->rd_state);
388: fprintf(fp, "branches;\n");
389: fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
390: numbuf, sizeof(numbuf)));
391: }
392:
1.26 jfb 393: fprintf(fp, "\ndesc\n@%s@\n\n",
394: (rfp->rf_desc == NULL) ? "" : rfp->rf_desc);
1.1 jfb 395:
396: /* deltatexts */
397: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
398: fprintf(fp, "\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
399: sizeof(numbuf)));
1.7 jfb 400: fprintf(fp, "log\n@%s@\ntext\n@", rdp->rd_log);
401:
402: cp = rdp->rd_text;
403: do {
404: len = sizeof(buf);
405: rlen = rcs_stresc(1, cp, buf, &len);
406: fprintf(fp, "%s", buf);
407: cp += rlen;
408: } while (len != 0);
409: fprintf(fp, "@\n\n");
1.1 jfb 410: }
411: fclose(fp);
412:
1.26 jfb 413: rfp->rf_flags |= RCS_SYNCED;
1.1 jfb 414:
415: return (0);
416: }
417:
418:
419: /*
1.29 jfb 420: * rcs_access_add()
421: *
422: * Add the login name <login> to the access list for the RCS file <file>.
423: * Returns 0 on success, or -1 on failure.
424: */
425: int
426: rcs_access_add(RCSFILE *file, const char *login)
427: {
428: struct rcs_access *ap;
429:
430: /* first look for duplication */
431: TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
432: if (strcmp(ap->ra_name, login) == 0) {
1.32 ! jfb 433: rcs_errno = RCS_ERR_DUPENT;
1.29 jfb 434: return (-1);
435: }
436: }
437:
438: ap = (struct rcs_access *)malloc(sizeof(*ap));
439: if (ap == NULL) {
440: cvs_log(LP_ERRNO, "failed to allocate RCS access entry");
441: return (-1);
442: }
443:
444: ap->ra_name = strdup(login);
445: if (ap->ra_name == NULL) {
446: cvs_log(LP_ERRNO, "failed to duplicate user name");
447: free(ap);
448: return (-1);
449: }
450:
451: TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
452:
453: /* not synced anymore */
454: file->rf_flags &= ~RCS_SYNCED;
455: return (0);
456:
457:
458: }
459:
460:
461: /*
462: * rcs_access_remove()
463: *
464: * Remove an entry with login name <login> from the access list of the RCS
465: * file <file>.
466: * Returns 0 on success, or -1 on failure.
467: */
468: int
469: rcs_access_remove(RCSFILE *file, const char *login)
470: {
471: struct rcs_access *ap;
472:
473: TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
474: if (strcmp(ap->ra_name, login) == 0)
475: break;
476:
477: if (ap == NULL) {
1.32 ! jfb 478: rcs_errno = RCS_ERR_NOENT;
1.29 jfb 479: return (-1);
480: }
481:
482: TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
483: free(ap->ra_name);
484: free(ap);
485:
486: /* not synced anymore */
487: file->rf_flags &= ~RCS_SYNCED;
488: return (0);
489: }
490:
491:
492: /*
1.26 jfb 493: * rcs_sym_add()
1.1 jfb 494: *
495: * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
496: * is named <sym> and is bound to the RCS revision <snum>.
497: * Returns 0 on success, or -1 on failure.
498: */
499: int
1.26 jfb 500: rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
1.1 jfb 501: {
502: struct rcs_sym *symp;
503:
504: /* first look for duplication */
505: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
506: if (strcmp(symp->rs_name, sym) == 0) {
1.32 ! jfb 507: rcs_errno = RCS_ERR_DUPENT;
1.1 jfb 508: return (-1);
509: }
510: }
511:
512: symp = (struct rcs_sym *)malloc(sizeof(*symp));
513: if (symp == NULL) {
514: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
515: return (-1);
516: }
517:
518: symp->rs_name = strdup(sym);
1.10 joris 519: if (symp->rs_name == NULL) {
520: cvs_log(LP_ERRNO, "failed to duplicate symbol");
521: free(symp);
522: return (-1);
523: }
524:
1.1 jfb 525: symp->rs_num = rcsnum_alloc();
1.11 joris 526: if (symp->rs_num == NULL) {
1.17 jfb 527: free(symp->rs_name);
1.11 joris 528: free(symp);
529: return (-1);
530: }
1.1 jfb 531: rcsnum_cpy(snum, symp->rs_num, 0);
532:
533: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
534:
535: /* not synced anymore */
1.26 jfb 536: rfp->rf_flags &= ~RCS_SYNCED;
1.1 jfb 537:
538: return (0);
539: }
540:
541:
542: /*
1.27 jfb 543: * rcs_sym_remove()
544: *
545: * Remove the symbol with name <sym> from the symbol list for the RCS file
546: * <file>. If no such symbol is found, the call fails and returns with an
547: * error.
548: * Returns 0 on success, or -1 on failure.
549: */
550: int
551: rcs_sym_remove(RCSFILE *file, const char *sym)
552: {
553: struct rcs_sym *symp;
554:
555: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
556: if (strcmp(symp->rs_name, sym) == 0)
557: break;
558:
559: if (symp == NULL) {
1.32 ! jfb 560: rcs_errno = RCS_ERR_NOENT;
1.27 jfb 561: return (-1);
562: }
563:
564: TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
565: free(symp->rs_name);
566: rcsnum_free(symp->rs_num);
567: free(symp);
568:
569: /* not synced anymore */
570: file->rf_flags &= ~RCS_SYNCED;
571:
572: return (0);
573: }
574:
575:
576: /*
577: * rcs_sym_getrev()
578: *
579: * Retrieve the RCS revision number associated with the symbol <sym> for the
580: * RCS file <file>. The returned value is a dynamically-allocated copy and
581: * should be freed by the caller once they are done with it.
582: * Returns the RCSNUM on success, or NULL on failure.
583: */
584: RCSNUM*
585: rcs_sym_getrev(RCSFILE *file, const char *sym)
586: {
587: RCSNUM *num;
588: struct rcs_sym *symp;
589:
590: num = NULL;
591:
592: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
593: if (strcmp(symp->rs_name, sym) == 0)
594: break;
595:
596: if (symp == NULL) {
597: /* XXX set error */
598: } else if (((num = rcsnum_alloc()) != NULL) &&
599: (rcsnum_cpy(symp->rs_num, num, 0) < 0)) {
600: rcsnum_free(num);
601: num = NULL;
602: }
603:
604: return (num);
1.30 jfb 605: }
606:
607:
608: /*
609: * rcs_lock_getmode()
610: *
611: * Retrieve the locking mode of the RCS file <file>.
612: */
613: int
614: rcs_lock_getmode(RCSFILE *file)
615: {
616: return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
617: }
618:
619:
620: /*
621: * rcs_lock_setmode()
622: *
623: * Set the locking mode of the RCS file <file> to <mode>, which must either
624: * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
625: * Returns the previous mode on success, or -1 on failure.
626: */
627: int
628: rcs_lock_setmode(RCSFILE *file, int mode)
629: {
630: int pmode;
631: pmode = rcs_lock_getmode(file);
632:
633: if (mode == RCS_LOCK_STRICT)
634: file->rf_flags |= RCS_SLOCK;
635: else if (mode == RCS_LOCK_LOOSE)
636: file->rf_flags &= ~RCS_SLOCK;
637: else {
638: cvs_log(LP_ERRNO, "invalid lock mode %d", mode);
639: return (-1);
640: }
641:
642: return (pmode);
1.27 jfb 643: }
644:
645:
646: /*
647: * rcs_desc_get()
648: *
649: * Retrieve the description for the RCS file <file>.
650: */
651: const char*
652: rcs_desc_get(RCSFILE *file)
653: {
654: return (file->rf_desc);
655: }
656:
657:
658: /*
659: * rcs_desc_set()
660: *
661: * Set the description for the RCS file <file>.
662: * Returns 0 on success, or -1 on failure.
663: */
664: int
665: rcs_desc_set(RCSFILE *file, const char *desc)
666: {
667: char *tmp;
668:
669: if ((tmp = strdup(desc)) == NULL)
670: return (-1);
671:
672: if (file->rf_desc != NULL)
673: free(file->rf_desc);
674: file->rf_desc = tmp;
675: file->rf_flags &= ~RCS_SYNCED;
676:
677: return (0);
678: }
679:
680:
681: /*
1.1 jfb 682: * rcs_patch()
683: *
684: * Apply an RCS-format patch pointed to by <patch> to the file contents
685: * found in <data>.
686: * Returns 0 on success, or -1 on failure.
687: */
688: BUF*
689: rcs_patch(const char *data, const char *patch)
690: {
1.5 vincent 691: struct rcs_foo *dlines, *plines;
692: struct rcs_line *lp;
1.1 jfb 693: size_t len;
1.5 vincent 694: int lineno;
1.1 jfb 695: BUF *res;
696:
697: len = strlen(data);
698: res = cvs_buf_alloc(len, BUF_AUTOEXT);
699: if (res == NULL)
700: return (NULL);
701:
702: dlines = rcs_splitlines(data);
1.17 jfb 703: if (dlines == NULL) {
704: cvs_buf_free(res);
1.1 jfb 705: return (NULL);
1.17 jfb 706: }
1.5 vincent 707:
1.1 jfb 708: plines = rcs_splitlines(patch);
1.5 vincent 709: if (plines == NULL) {
1.17 jfb 710: cvs_buf_free(res);
1.5 vincent 711: rcs_freefoo(dlines);
1.1 jfb 712: return (NULL);
1.5 vincent 713: }
714:
715: if (rcs_patch_lines(dlines, plines) < 0) {
1.17 jfb 716: cvs_buf_free(res);
1.5 vincent 717: rcs_freefoo(plines);
718: rcs_freefoo(dlines);
719: return (NULL);
720: }
721:
722: lineno = 0;
723: TAILQ_FOREACH(lp, &dlines->rl_lines, rl_list) {
724: if (lineno != 0)
725: cvs_buf_fappend(res, "%s\n", lp->rl_line);
726: lineno++;
727: }
728:
729: rcs_freefoo(dlines);
730: rcs_freefoo(plines);
731: return (res);
732: }
733:
1.7 jfb 734: static int
1.5 vincent 735: rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
736: {
737: char op, *ep;
738: struct rcs_line *lp, *dlp, *ndlp;
739: int i, lineno, nbln;
1.1 jfb 740:
741: dlp = TAILQ_FIRST(&(dlines->rl_lines));
742: lp = TAILQ_FIRST(&(plines->rl_lines));
743:
744: /* skip first bogus line */
745: for (lp = TAILQ_NEXT(lp, rl_list); lp != NULL;
746: lp = TAILQ_NEXT(lp, rl_list)) {
747: op = *(lp->rl_line);
748: lineno = (int)strtol((lp->rl_line + 1), &ep, 10);
749: if ((lineno > dlines->rl_nblines) || (lineno <= 0) ||
750: (*ep != ' ')) {
751: cvs_log(LP_ERR,
752: "invalid line specification in RCS patch");
753: return (NULL);
754: }
755: ep++;
756: nbln = (int)strtol(ep, &ep, 10);
757: if ((nbln <= 0) || (*ep != '\0')) {
758: cvs_log(LP_ERR,
759: "invalid line number specification in RCS patch");
760: return (NULL);
761: }
762:
763: /* find the appropriate line */
764: for (;;) {
765: if (dlp == NULL)
766: break;
767: if (dlp->rl_lineno == lineno)
768: break;
769: if (dlp->rl_lineno > lineno) {
770: dlp = TAILQ_PREV(dlp, rcs_tqh, rl_list);
1.14 deraadt 771: } else if (dlp->rl_lineno < lineno) {
1.1 jfb 772: ndlp = TAILQ_NEXT(dlp, rl_list);
773: if (ndlp->rl_lineno > lineno)
774: break;
775: dlp = ndlp;
776: }
777: }
778: if (dlp == NULL) {
779: cvs_log(LP_ERR,
780: "can't find referenced line in RCS patch");
781: return (NULL);
782: }
783:
784: if (op == 'd') {
785: for (i = 0; (i < nbln) && (dlp != NULL); i++) {
786: ndlp = TAILQ_NEXT(dlp, rl_list);
787: TAILQ_REMOVE(&(dlines->rl_lines), dlp, rl_list);
788: dlp = ndlp;
789: }
1.14 deraadt 790: } else if (op == 'a') {
1.1 jfb 791: for (i = 0; i < nbln; i++) {
792: ndlp = lp;
793: lp = TAILQ_NEXT(lp, rl_list);
794: if (lp == NULL) {
795: cvs_log(LP_ERR, "truncated RCS patch");
1.5 vincent 796: return (-1);
1.1 jfb 797: }
798: TAILQ_REMOVE(&(plines->rl_lines), lp, rl_list);
799: TAILQ_INSERT_AFTER(&(dlines->rl_lines), dlp,
800: lp, rl_list);
801: dlp = lp;
802:
803: /* we don't want lookup to block on those */
804: lp->rl_lineno = lineno;
805:
806: lp = ndlp;
807: }
1.14 deraadt 808: } else {
1.1 jfb 809: cvs_log(LP_ERR, "unknown RCS patch operation `%c'", op);
1.5 vincent 810: return (-1);
1.1 jfb 811: }
812:
813: /* last line of the patch, done */
814: if (lp->rl_lineno == plines->rl_nblines)
815: break;
816: }
817:
818: /* once we're done patching, rebuild the line numbers */
1.2 vincent 819: lineno = 0;
1.5 vincent 820: TAILQ_FOREACH(lp, &(dlines->rl_lines), rl_list)
1.1 jfb 821: lp->rl_lineno = lineno++;
822: dlines->rl_nblines = lineno - 1;
823:
1.5 vincent 824: return (0);
1.1 jfb 825: }
826:
827:
828: /*
829: * rcs_getrev()
830: *
831: * Get the whole contents of revision <rev> from the RCSFILE <rfp>. The
1.4 vincent 832: * returned buffer is dynamically allocated and should be released using
833: * cvs_buf_free() once the caller is done using it.
1.1 jfb 834: */
835: BUF*
836: rcs_getrev(RCSFILE *rfp, RCSNUM *rev)
837: {
838: int res;
839: size_t len;
840: void *bp;
841: RCSNUM *crev;
842: BUF *rbuf;
843: struct rcs_delta *rdp = NULL;
844:
1.28 jfb 845: if (rfp->rf_head == NULL)
846: return (NULL);
847:
1.1 jfb 848: res = rcsnum_cmp(rfp->rf_head, rev, 0);
849: if (res == 1) {
1.32 ! jfb 850: rcs_errno = RCS_ERR_NOENT;
1.1 jfb 851: return (NULL);
1.26 jfb 852: }
853:
854: rdp = rcs_findrev(rfp, rfp->rf_head);
855: if (rdp == NULL) {
856: cvs_log(LP_ERR, "failed to get RCS HEAD revision");
857: return (NULL);
858: }
859:
860: len = strlen(rdp->rd_text);
861: if ((rbuf = cvs_buf_alloc(len, BUF_AUTOEXT)) == NULL)
862: return (NULL);
863:
864: cvs_buf_append(rbuf, rdp->rd_text, len);
865:
866: if (res != 0) {
867: /* Apply patches backwards to get the right version.
868: * This will need some rework to support sub branches.
869: */
870: if ((crev = rcsnum_alloc()) == NULL) {
871: cvs_buf_free(rbuf);
1.1 jfb 872: return (NULL);
873: }
1.26 jfb 874: rcsnum_cpy(rfp->rf_head, crev, 0);
875: do {
876: crev->rn_id[crev->rn_len - 1]--;
877: rdp = rcs_findrev(rfp, crev);
878: if (rdp == NULL) {
879: rcsnum_free(crev);
880: cvs_buf_free(rbuf);
881: return (NULL);
882: }
1.1 jfb 883:
1.26 jfb 884: if (cvs_buf_putc(rbuf, '\0') < 0) {
885: rcsnum_free(crev);
1.17 jfb 886: cvs_buf_free(rbuf);
1.11 joris 887: return (NULL);
1.17 jfb 888: }
1.26 jfb 889: bp = cvs_buf_release(rbuf);
890: rbuf = rcs_patch((char *)bp, rdp->rd_text);
891: if (rbuf == NULL)
892: break;
893: } while (rcsnum_cmp(crev, rev, 0) != 0);
1.1 jfb 894:
1.26 jfb 895: rcsnum_free(crev);
1.1 jfb 896: }
897:
898: return (rbuf);
1.16 jfb 899: }
900:
901:
902: /*
903: * rcs_gethead()
904: *
905: * Get the head revision for the RCS file <rf>.
906: */
907: BUF*
908: rcs_gethead(RCSFILE *rf)
909: {
910: return rcs_getrev(rf, rf->rf_head);
1.1 jfb 911: }
912:
913:
914: /*
915: * rcs_getrevbydate()
916: *
917: * Get an RCS revision by a specific date.
918: */
919: RCSNUM*
920: rcs_getrevbydate(RCSFILE *rfp, struct tm *date)
921: {
922: return (NULL);
923: }
924:
925:
926: /*
927: * rcs_findrev()
928: *
929: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
930: * The revision number is given in <rev>.
931: * Returns a pointer to the delta on success, or NULL on failure.
932: */
933: static struct rcs_delta*
934: rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
935: {
936: u_int cmplen;
937: struct rcs_delta *rdp;
938: struct rcs_dlist *hp;
1.6 vincent 939: int found;
1.26 jfb 940:
1.1 jfb 941: cmplen = 2;
942: hp = &(rfp->rf_delta);
943:
1.6 vincent 944: do {
945: found = 0;
946: TAILQ_FOREACH(rdp, hp, rd_list) {
947: if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) {
948: if (cmplen == rev->rn_len)
949: return (rdp);
1.1 jfb 950:
1.6 vincent 951: hp = &(rdp->rd_snodes);
952: cmplen += 2;
953: found = 1;
954: break;
955: }
1.1 jfb 956: }
1.6 vincent 957: } while (found && cmplen < rev->rn_len);
1.1 jfb 958:
959: return (NULL);
1.20 jfb 960: }
961:
962:
963: /*
1.26 jfb 964: * rcs_kwexp_set()
965: *
966: * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
967: * Returns 0 on success, or -1 on failure.
968: */
969: int
970: rcs_kwexp_set(RCSFILE *file, int mode)
971: {
972: int i;
973: char *tmp, buf[8] = "";
974:
975: if (RCS_KWEXP_INVAL(mode))
976: return (-1);
977:
978: i = 0;
979: if (mode == RCS_KWEXP_NONE)
980: buf[0] = 'b';
981: else if (mode == RCS_KWEXP_OLD)
982: buf[0] = 'o';
983: else {
984: if (mode & RCS_KWEXP_NAME)
985: buf[i++] = 'k';
986: if (mode & RCS_KWEXP_VAL)
987: buf[i++] = 'v';
988: if (mode & RCS_KWEXP_LKR)
989: buf[i++] = 'l';
990: }
991:
992: if ((tmp = strdup(buf)) == NULL) {
993: cvs_log(LP_ERRNO, "%s: failed to copy expansion mode",
994: file->rf_path);
995: return (-1);
996: }
997:
1.27 jfb 998: if (file->rf_expand != NULL)
999: free(file->rf_expand);
1.26 jfb 1000: file->rf_expand = tmp;
1001:
1002: return (0);
1003: }
1004:
1005: /*
1006: * rcs_kwexp_get()
1007: *
1008: * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1009: */
1010: int
1011: rcs_kwexp_get(RCSFILE *file)
1012: {
1013: return rcs_kflag_get(file->rf_expand);
1014: }
1015:
1016: /*
1.20 jfb 1017: * rcs_kflag_get()
1018: *
1019: * Get the keyword expansion mode from a set of character flags given in
1020: * <flags> and return the appropriate flag mask. In case of an error, the
1021: * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1022: */
1023: int
1024: rcs_kflag_get(const char *flags)
1025: {
1026: int fl;
1027: size_t len;
1028: const char *fp;
1029:
1030: fl = 0;
1031: len = strlen(flags);
1032:
1033: for (fp = flags; *fp != '\0'; fp++) {
1034: if (*fp == 'k')
1035: fl |= RCS_KWEXP_NAME;
1036: else if (*fp == 'v')
1037: fl |= RCS_KWEXP_VAL;
1038: else if (*fp == 'l')
1039: fl |= RCS_KWEXP_LKR;
1040: else if (*fp == 'o') {
1041: if (len != 1)
1042: fl |= RCS_KWEXP_ERR;
1043: fl |= RCS_KWEXP_OLD;
1044: } else if (*fp == 'b') {
1045: if (len != 1)
1046: fl |= RCS_KWEXP_ERR;
1047: } else /* unknown letter */
1048: fl |= RCS_KWEXP_ERR;
1049: }
1050:
1051: return (fl);
1.32 ! jfb 1052: }
! 1053:
! 1054: /*
! 1055: * rcs_errstr()
! 1056: *
! 1057: * Get the error string matching the RCS error code <code>.
! 1058: */
! 1059: const char*
! 1060: rcs_errstr(int code)
! 1061: {
! 1062: if ((code < 0) || (code > (int)RCS_NERR))
! 1063: return (NULL);
! 1064: return (rcs_errstrs[code]);
1.1 jfb 1065: }
1066:
1.21 jfb 1067: void
1068: rcs_kflag_usage(void)
1069: {
1070: fprintf(stderr, "Valid expansion modes include:\n"
1.22 jfb 1071: "\t-kkv\tGenerate keywords using the default form.\n"
1072: "\t-kkvl\tLike -kkv, except locker's name inserted.\n"
1073: "\t-kk\tGenerate only keyword names in keyword strings.\n"
1074: "\t-kv\tGenerate only keyword values in keyword strings.\n"
1075: "\t-ko\tGenerate old keyword string "
1.21 jfb 1076: "(no changes from checked in file).\n"
1.22 jfb 1077: "\t-kb\tGenerate binary file unmodified (merges not allowed).\n");
1.21 jfb 1078: }
1.1 jfb 1079:
1.26 jfb 1080:
1.1 jfb 1081: /*
1082: * rcs_parse()
1083: *
1084: * Parse the contents of file <path>, which are in the RCS format.
1085: * Returns 0 on success, or -1 on failure.
1086: */
1.26 jfb 1087: static int
1.1 jfb 1088: rcs_parse(RCSFILE *rfp)
1089: {
1090: int ret;
1091: struct rcs_pdata *pdp;
1092:
1.26 jfb 1093: if (rfp->rf_flags & RCS_PARSED)
1.1 jfb 1094: return (0);
1095:
1.26 jfb 1096: if ((pdp = (struct rcs_pdata *)malloc(sizeof(*pdp))) == NULL) {
1.1 jfb 1097: cvs_log(LP_ERRNO, "failed to allocate RCS parser data");
1098: return (-1);
1099: }
1100: memset(pdp, 0, sizeof(*pdp));
1101:
1.18 jfb 1102: pdp->rp_lines = 0;
1.1 jfb 1103: pdp->rp_pttype = RCS_TOK_ERR;
1104:
1105: pdp->rp_file = fopen(rfp->rf_path, "r");
1106: if (pdp->rp_file == NULL) {
1107: cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path);
1108: rcs_freepdata(pdp);
1109: return (-1);
1110: }
1111:
1112: pdp->rp_buf = (char *)malloc(RCS_BUFSIZE);
1113: if (pdp->rp_buf == NULL) {
1114: cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer");
1115: rcs_freepdata(pdp);
1116: return (-1);
1117: }
1118: pdp->rp_blen = RCS_BUFSIZE;
1.18 jfb 1119: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
1.1 jfb 1120:
1121: /* ditch the strict lock */
1.26 jfb 1122: rfp->rf_flags &= ~RCS_SLOCK;
1.1 jfb 1123: rfp->rf_pdata = pdp;
1124:
1.31 jfb 1125: if ((ret = rcs_parse_admin(rfp)) < 0) {
1.1 jfb 1126: rcs_freepdata(pdp);
1127: return (-1);
1.31 jfb 1128: } else if (ret == RCS_TOK_NUM) {
1129: for (;;) {
1130: ret = rcs_parse_delta(rfp);
1131: if (ret == 0)
1132: break;
1133: else if (ret == -1) {
1134: rcs_freepdata(pdp);
1135: return (-1);
1136: }
1.1 jfb 1137: }
1138: }
1139:
1140: ret = rcs_gettok(rfp);
1141: if (ret != RCS_TOK_DESC) {
1142: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
1143: RCS_TOKSTR(rfp));
1144: rcs_freepdata(pdp);
1145: return (-1);
1146: }
1147:
1148: ret = rcs_gettok(rfp);
1149: if (ret != RCS_TOK_STRING) {
1150: cvs_log(LP_ERR, "token `%s' found where RCS desc expected",
1151: RCS_TOKSTR(rfp));
1152: rcs_freepdata(pdp);
1153: return (-1);
1154: }
1155:
1156: rfp->rf_desc = strdup(RCS_TOKSTR(rfp));
1.10 joris 1157: if (rfp->rf_desc == NULL) {
1158: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
1159: rcs_freepdata(pdp);
1160: return (-1);
1161: }
1.1 jfb 1162:
1163: for (;;) {
1164: ret = rcs_parse_deltatext(rfp);
1165: if (ret == 0)
1166: break;
1167: else if (ret == -1) {
1168: rcs_freepdata(pdp);
1169: return (-1);
1170: }
1171: }
1172:
1173: cvs_log(LP_DEBUG, "RCS file `%s' parsed OK (%u lines)", rfp->rf_path,
1.18 jfb 1174: pdp->rp_lines);
1.1 jfb 1175:
1176: rcs_freepdata(pdp);
1177:
1178: rfp->rf_pdata = NULL;
1.26 jfb 1179: rfp->rf_flags |= RCS_PARSED | RCS_SYNCED;
1.1 jfb 1180:
1181: return (0);
1182: }
1183:
1184:
1185: /*
1186: * rcs_parse_admin()
1187: *
1188: * Parse the administrative portion of an RCS file.
1.31 jfb 1189: * Returns the type of the first token found after the admin section on
1190: * success, or -1 on failure.
1.1 jfb 1191: */
1192: static int
1193: rcs_parse_admin(RCSFILE *rfp)
1194: {
1195: u_int i;
1196: int tok, ntok, hmask;
1197: struct rcs_key *rk;
1198:
1199: /* hmask is a mask of the headers already encountered */
1200: hmask = 0;
1201: for (;;) {
1202: tok = rcs_gettok(rfp);
1203: if (tok == RCS_TOK_ERR) {
1204: cvs_log(LP_ERR, "parse error in RCS admin section");
1205: return (-1);
1.31 jfb 1206: } else if ((tok == RCS_TOK_NUM) || (tok == RCS_TOK_DESC)) {
1207: /*
1208: * Assume this is the start of the first delta or
1209: * that we are dealing with an empty RCS file and
1210: * we just found the description.
1211: */
1.1 jfb 1212: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.31 jfb 1213: return (tok);
1.1 jfb 1214: }
1215:
1216: rk = NULL;
1.18 jfb 1217: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1218: if (rcs_keys[i].rk_id == tok)
1219: rk = &(rcs_keys[i]);
1220:
1221: if (hmask & (1 << tok)) {
1222: cvs_log(LP_ERR, "duplicate RCS key");
1223: return (-1);
1224: }
1225: hmask |= (1 << tok);
1226:
1227: switch (tok) {
1228: case RCS_TOK_HEAD:
1229: case RCS_TOK_BRANCH:
1230: case RCS_TOK_COMMENT:
1231: case RCS_TOK_EXPAND:
1232: ntok = rcs_gettok(rfp);
1233: if (ntok == RCS_TOK_SCOLON)
1234: break;
1235: if (ntok != rk->rk_val) {
1236: cvs_log(LP_ERR,
1237: "invalid value type for RCS key `%s'",
1238: rk->rk_str);
1239: }
1240:
1241: if (tok == RCS_TOK_HEAD) {
1.28 jfb 1242: if (rfp->rf_head == NULL) {
1243: rfp->rf_head = rcsnum_alloc();
1244: if (rfp->rf_head == NULL)
1245: return (-1);
1246: }
1.1 jfb 1247: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1248: rfp->rf_head);
1.14 deraadt 1249: } else if (tok == RCS_TOK_BRANCH) {
1.1 jfb 1250: rcsnum_aton(RCS_TOKSTR(rfp), NULL,
1251: rfp->rf_branch);
1.14 deraadt 1252: } else if (tok == RCS_TOK_COMMENT) {
1.1 jfb 1253: rfp->rf_comment = strdup(RCS_TOKSTR(rfp));
1.10 joris 1254: if (rfp->rf_comment == NULL) {
1255: cvs_log(LP_ERRNO,
1256: "failed to duplicate rcs token");
1257: return (-1);
1258: }
1.14 deraadt 1259: } else if (tok == RCS_TOK_EXPAND) {
1.1 jfb 1260: rfp->rf_expand = strdup(RCS_TOKSTR(rfp));
1.10 joris 1261: if (rfp->rf_expand == NULL) {
1262: cvs_log(LP_ERRNO,
1263: "failed to duplicate rcs token");
1264: return (-1);
1265: }
1.1 jfb 1266: }
1267:
1268: /* now get the expected semi-colon */
1269: ntok = rcs_gettok(rfp);
1270: if (ntok != RCS_TOK_SCOLON) {
1271: cvs_log(LP_ERR,
1272: "missing semi-colon after RCS `%s' key",
1.26 jfb 1273: rk->rk_str);
1.1 jfb 1274: return (-1);
1275: }
1276: break;
1277: case RCS_TOK_ACCESS:
1.29 jfb 1278: if (rcs_parse_access(rfp) < 0)
1279: return (-1);
1.1 jfb 1280: break;
1281: case RCS_TOK_SYMBOLS:
1.29 jfb 1282: if (rcs_parse_symbols(rfp) < 0)
1283: return (-1);
1.1 jfb 1284: break;
1285: case RCS_TOK_LOCKS:
1.29 jfb 1286: if (rcs_parse_locks(rfp) < 0)
1287: return (-1);
1.1 jfb 1288: break;
1289: default:
1290: cvs_log(LP_ERR,
1291: "unexpected token `%s' in RCS admin section",
1292: RCS_TOKSTR(rfp));
1293: return (-1);
1294: }
1295: }
1296:
1297: return (0);
1298: }
1299:
1300:
1301: /*
1302: * rcs_parse_delta()
1303: *
1304: * Parse an RCS delta section and allocate the structure to store that delta's
1305: * information in the <rfp> delta list.
1306: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1307: * -1 on error.
1308: */
1309: static int
1310: rcs_parse_delta(RCSFILE *rfp)
1311: {
1312: int ret, tok, ntok, hmask;
1313: u_int i;
1314: char *tokstr;
1.3 vincent 1315: RCSNUM *datenum;
1.1 jfb 1316: struct rcs_delta *rdp;
1317: struct rcs_key *rk;
1318:
1319: rdp = (struct rcs_delta *)malloc(sizeof(*rdp));
1320: if (rdp == NULL) {
1321: cvs_log(LP_ERRNO, "failed to allocate RCS delta structure");
1322: return (-1);
1323: }
1324: memset(rdp, 0, sizeof(*rdp));
1325:
1326: rdp->rd_num = rcsnum_alloc();
1.11 joris 1327: if (rdp->rd_num == NULL) {
1328: rcs_freedelta(rdp);
1329: return (-1);
1330: }
1.1 jfb 1331: rdp->rd_next = rcsnum_alloc();
1.11 joris 1332: if (rdp->rd_next == NULL) {
1333: rcs_freedelta(rdp);
1334: return (-1);
1335: }
1.1 jfb 1336:
1337: TAILQ_INIT(&(rdp->rd_branches));
1338:
1339: tok = rcs_gettok(rfp);
1340: if (tok != RCS_TOK_NUM) {
1341: cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
1342: RCS_TOKSTR(rfp));
1343: rcs_freedelta(rdp);
1344: return (-1);
1345: }
1346: rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
1347:
1348: hmask = 0;
1349: ret = 0;
1350: tokstr = NULL;
1351:
1352: for (;;) {
1353: tok = rcs_gettok(rfp);
1354: if (tok == RCS_TOK_ERR) {
1355: cvs_log(LP_ERR, "parse error in RCS delta section");
1356: rcs_freedelta(rdp);
1357: return (-1);
1.14 deraadt 1358: } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
1.15 tedu 1359: rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
1.1 jfb 1360: ret = (tok == RCS_TOK_NUM ? 1 : 0);
1361: break;
1362: }
1363:
1364: rk = NULL;
1.18 jfb 1365: for (i = 0; i < RCS_NKEYS; i++)
1.1 jfb 1366: if (rcs_keys[i].rk_id == tok)
1367: rk = &(rcs_keys[i]);
1368:
1369: if (hmask & (1 << tok)) {
1370: cvs_log(LP_ERR, "duplicate RCS key");
1371: rcs_freedelta(rdp);
1372: return (-1);
1373: }
1374: hmask |= (1 << tok);
1375:
1376: switch (tok) {
1377: case RCS_TOK_DATE:
1378: case RCS_TOK_AUTHOR:
1379: case RCS_TOK_STATE:
1380: case RCS_TOK_NEXT:
1381: ntok = rcs_gettok(rfp);
1382: if (ntok == RCS_TOK_SCOLON) {
1383: if (rk->rk_flags & RCS_VOPT)
1384: break;
1385: else {
1386: cvs_log(LP_ERR, "missing mandatory "
1387: "value to RCS key `%s'",
1388: rk->rk_str);
1389: rcs_freedelta(rdp);
1390: return (-1);
1391: }
1392: }
1393:
1394: if (ntok != rk->rk_val) {
1395: cvs_log(LP_ERR,
1396: "invalid value type for RCS key `%s'",
1397: rk->rk_str);
1398: rcs_freedelta(rdp);
1399: return (-1);
1400: }
1401:
1402: if (tokstr != NULL)
1403: free(tokstr);
1404: tokstr = strdup(RCS_TOKSTR(rfp));
1.10 joris 1405: if (tokstr == NULL) {
1.15 tedu 1406: cvs_log(LP_ERRNO,
1.10 joris 1407: "failed to duplicate rcs token");
1408: rcs_freedelta(rdp);
1409: return (-1);
1410: }
1.1 jfb 1411:
1412: /* now get the expected semi-colon */
1413: ntok = rcs_gettok(rfp);
1414: if (ntok != RCS_TOK_SCOLON) {
1415: cvs_log(LP_ERR,
1416: "missing semi-colon after RCS `%s' key",
1.26 jfb 1417: rk->rk_str);
1.1 jfb 1418: rcs_freedelta(rdp);
1419: return (-1);
1420: }
1421:
1422: if (tok == RCS_TOK_DATE) {
1.25 jfb 1423: if ((datenum = rcsnum_parse(tokstr)) == NULL) {
1.11 joris 1424: rcs_freedelta(rdp);
1425: return (-1);
1426: }
1.3 vincent 1427: if (datenum->rn_len != 6) {
1.1 jfb 1428: cvs_log(LP_ERR,
1429: "RCS date specification has %s "
1430: "fields",
1.3 vincent 1431: (datenum->rn_len > 6) ? "too many" :
1.1 jfb 1432: "missing");
1433: rcs_freedelta(rdp);
1434: }
1.3 vincent 1435: rdp->rd_date.tm_year = datenum->rn_id[0];
1.19 jfb 1436: if (rdp->rd_date.tm_year >= 1900)
1437: rdp->rd_date.tm_year -= 1900;
1.3 vincent 1438: rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
1439: rdp->rd_date.tm_mday = datenum->rn_id[2];
1440: rdp->rd_date.tm_hour = datenum->rn_id[3];
1441: rdp->rd_date.tm_min = datenum->rn_id[4];
1442: rdp->rd_date.tm_sec = datenum->rn_id[5];
1443: rcsnum_free(datenum);
1.14 deraadt 1444: } else if (tok == RCS_TOK_AUTHOR) {
1.1 jfb 1445: rdp->rd_author = tokstr;
1446: tokstr = NULL;
1.14 deraadt 1447: } else if (tok == RCS_TOK_STATE) {
1.1 jfb 1448: rdp->rd_state = tokstr;
1449: tokstr = NULL;
1.14 deraadt 1450: } else if (tok == RCS_TOK_NEXT) {
1.1 jfb 1451: rcsnum_aton(tokstr, NULL, rdp->rd_next);
1452: }
1453: break;
1454: case RCS_TOK_BRANCHES:
1455: rcs_parse_branches(rfp, rdp);
1456: break;
1457: default:
1458: cvs_log(LP_ERR,
1459: "unexpected token `%s' in RCS delta",
1460: RCS_TOKSTR(rfp));
1461: rcs_freedelta(rdp);
1462: return (-1);
1463: }
1464: }
1465:
1.13 jfb 1466: if (tokstr != NULL)
1467: free(tokstr);
1468:
1.1 jfb 1469: TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
1.26 jfb 1470: rfp->rf_ndelta++;
1.1 jfb 1471:
1472: return (ret);
1473: }
1474:
1475:
1476: /*
1477: * rcs_parse_deltatext()
1478: *
1479: * Parse an RCS delta text section and fill in the log and text field of the
1480: * appropriate delta section.
1481: * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
1482: * -1 on error.
1483: */
1484: static int
1485: rcs_parse_deltatext(RCSFILE *rfp)
1486: {
1487: int tok;
1488: RCSNUM *tnum;
1489: struct rcs_delta *rdp;
1490:
1491: tok = rcs_gettok(rfp);
1492: if (tok == RCS_TOK_EOF)
1493: return (0);
1494:
1495: if (tok != RCS_TOK_NUM) {
1496: cvs_log(LP_ERR,
1497: "unexpected token `%s' at start of RCS delta text",
1498: RCS_TOKSTR(rfp));
1499: return (-1);
1500: }
1.13 jfb 1501:
1502: tnum = rcsnum_alloc();
1503: if (tnum == NULL)
1504: return (-1);
1.1 jfb 1505: rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
1506:
1507: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1508: if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
1509: break;
1510: }
1.13 jfb 1511: rcsnum_free(tnum);
1512:
1.1 jfb 1513: if (rdp == NULL) {
1514: cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
1515: RCS_TOKSTR(rfp));
1516: return (-1);
1517: }
1518:
1519: tok = rcs_gettok(rfp);
1520: if (tok != RCS_TOK_LOG) {
1521: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1522: RCS_TOKSTR(rfp));
1523: return (-1);
1524: }
1525:
1526: tok = rcs_gettok(rfp);
1527: if (tok != RCS_TOK_STRING) {
1528: cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
1529: RCS_TOKSTR(rfp));
1530: return (-1);
1531: }
1532: rdp->rd_log = strdup(RCS_TOKSTR(rfp));
1533: if (rdp->rd_log == NULL) {
1534: cvs_log(LP_ERRNO, "failed to copy RCS deltatext log");
1535: return (-1);
1536: }
1537:
1538: tok = rcs_gettok(rfp);
1539: if (tok != RCS_TOK_TEXT) {
1540: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1541: RCS_TOKSTR(rfp));
1542: return (-1);
1543: }
1544:
1545: tok = rcs_gettok(rfp);
1546: if (tok != RCS_TOK_STRING) {
1547: cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
1548: RCS_TOKSTR(rfp));
1549: return (-1);
1550: }
1551:
1552: rdp->rd_text = strdup(RCS_TOKSTR(rfp));
1553: if (rdp->rd_text == NULL) {
1554: cvs_log(LP_ERRNO, "failed to copy RCS delta text");
1555: return (-1);
1556: }
1557:
1558: return (1);
1559: }
1560:
1561:
1562: /*
1563: * rcs_parse_access()
1564: *
1565: * Parse the access list given as value to the `access' keyword.
1566: * Returns 0 on success, or -1 on failure.
1567: */
1568: static int
1569: rcs_parse_access(RCSFILE *rfp)
1570: {
1571: int type;
1572:
1573: while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
1574: if (type != RCS_TOK_ID) {
1575: cvs_log(LP_ERR, "unexpected token `%s' in access list",
1576: RCS_TOKSTR(rfp));
1577: return (-1);
1578: }
1.29 jfb 1579:
1580: if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
1581: return (-1);
1.1 jfb 1582: }
1583:
1584: return (0);
1585: }
1586:
1587:
1588: /*
1589: * rcs_parse_symbols()
1590: *
1591: * Parse the symbol list given as value to the `symbols' keyword.
1592: * Returns 0 on success, or -1 on failure.
1593: */
1594: static int
1595: rcs_parse_symbols(RCSFILE *rfp)
1596: {
1597: int type;
1598: struct rcs_sym *symp;
1599:
1600: for (;;) {
1601: type = rcs_gettok(rfp);
1602: if (type == RCS_TOK_SCOLON)
1603: break;
1604:
1605: if (type != RCS_TOK_STRING) {
1606: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1607: RCS_TOKSTR(rfp));
1608: return (-1);
1609: }
1610:
1611: symp = (struct rcs_sym *)malloc(sizeof(*symp));
1612: if (symp == NULL) {
1613: cvs_log(LP_ERRNO, "failed to allocate RCS symbol");
1614: return (-1);
1615: }
1616: symp->rs_name = strdup(RCS_TOKSTR(rfp));
1.10 joris 1617: if (symp->rs_name == NULL) {
1618: cvs_log(LP_ERRNO, "failed to duplicate rcs token");
1619: free(symp);
1620: return (-1);
1621: }
1622:
1.1 jfb 1623: symp->rs_num = rcsnum_alloc();
1.11 joris 1624: if (symp->rs_num == NULL) {
1625: cvs_log(LP_ERRNO, "failed to allocate rcsnum info");
1626: free(symp);
1627: return (-1);
1628: }
1.1 jfb 1629:
1630: type = rcs_gettok(rfp);
1631: if (type != RCS_TOK_COLON) {
1632: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1633: RCS_TOKSTR(rfp));
1.11 joris 1634: rcsnum_free(symp->rs_num);
1.1 jfb 1635: free(symp->rs_name);
1636: free(symp);
1637: return (-1);
1638: }
1639:
1640: type = rcs_gettok(rfp);
1641: if (type != RCS_TOK_NUM) {
1642: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1643: RCS_TOKSTR(rfp));
1.11 joris 1644: rcsnum_free(symp->rs_num);
1.1 jfb 1645: free(symp->rs_name);
1646: free(symp);
1647: return (-1);
1648: }
1649:
1650: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
1651: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1652: RCS_TOKSTR(rfp));
1.11 joris 1653: rcsnum_free(symp->rs_num);
1.1 jfb 1654: free(symp->rs_name);
1655: free(symp);
1656: return (-1);
1657: }
1658:
1659: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
1660: }
1661:
1662: return (0);
1663: }
1664:
1665:
1666: /*
1667: * rcs_parse_locks()
1668: *
1669: * Parse the lock list given as value to the `locks' keyword.
1670: * Returns 0 on success, or -1 on failure.
1671: */
1672: static int
1673: rcs_parse_locks(RCSFILE *rfp)
1674: {
1675: int type;
1676: struct rcs_lock *lkp;
1677:
1678: for (;;) {
1679: type = rcs_gettok(rfp);
1680: if (type == RCS_TOK_SCOLON)
1681: break;
1682:
1683: if (type != RCS_TOK_ID) {
1684: cvs_log(LP_ERR, "unexpected token `%s' in lock list",
1685: RCS_TOKSTR(rfp));
1686: return (-1);
1687: }
1688:
1689: lkp = (struct rcs_lock *)malloc(sizeof(*lkp));
1690: if (lkp == NULL) {
1691: cvs_log(LP_ERRNO, "failed to allocate RCS lock");
1692: return (-1);
1693: }
1694: lkp->rl_num = rcsnum_alloc();
1.11 joris 1695: if (lkp->rl_num == NULL) {
1696: free(lkp);
1697: return (-1);
1698: }
1.1 jfb 1699:
1700: type = rcs_gettok(rfp);
1701: if (type != RCS_TOK_COLON) {
1702: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1703: RCS_TOKSTR(rfp));
1704: free(lkp);
1705: return (-1);
1706: }
1707:
1708: type = rcs_gettok(rfp);
1709: if (type != RCS_TOK_NUM) {
1710: cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
1711: RCS_TOKSTR(rfp));
1712: free(lkp);
1713: return (-1);
1714: }
1715:
1716: if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
1717: cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
1718: RCS_TOKSTR(rfp));
1719: free(lkp);
1720: return (-1);
1721: }
1722:
1723: TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
1724: }
1725:
1726: /* check if we have a `strict' */
1727: type = rcs_gettok(rfp);
1728: if (type != RCS_TOK_STRICT) {
1729: rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
1.14 deraadt 1730: } else {
1.26 jfb 1731: rfp->rf_flags |= RCS_SLOCK;
1.1 jfb 1732:
1733: type = rcs_gettok(rfp);
1734: if (type != RCS_TOK_SCOLON) {
1735: cvs_log(LP_ERR,
1736: "missing semi-colon after `strict' keyword");
1737: return (-1);
1738: }
1739: }
1740:
1741: return (0);
1742: }
1743:
1744: /*
1745: * rcs_parse_branches()
1746: *
1747: * Parse the list of branches following a `branches' keyword in a delta.
1748: * Returns 0 on success, or -1 on failure.
1749: */
1750: static int
1751: rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
1752: {
1753: int type;
1754: struct rcs_branch *brp;
1755:
1756: for (;;) {
1757: type = rcs_gettok(rfp);
1758: if (type == RCS_TOK_SCOLON)
1759: break;
1760:
1761: if (type != RCS_TOK_NUM) {
1762: cvs_log(LP_ERR,
1763: "unexpected token `%s' in list of branches",
1764: RCS_TOKSTR(rfp));
1765: return (-1);
1766: }
1767:
1768: brp = (struct rcs_branch *)malloc(sizeof(*brp));
1769: if (brp == NULL) {
1770: cvs_log(LP_ERRNO, "failed to allocate RCS branch");
1771: return (-1);
1772: }
1773: brp->rb_num = rcsnum_alloc();
1.11 joris 1774: if (brp->rb_num == NULL) {
1775: free(brp);
1776: return (-1);
1777: }
1778:
1.1 jfb 1779: rcsnum_aton(RCS_TOKSTR(rfp), NULL, brp->rb_num);
1780:
1781: TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
1782: }
1783:
1784: return (0);
1785: }
1786:
1787:
1788: /*
1789: * rcs_freedelta()
1790: *
1791: * Free the contents of a delta structure.
1792: */
1.18 jfb 1793: static void
1.1 jfb 1794: rcs_freedelta(struct rcs_delta *rdp)
1795: {
1.12 jfb 1796: struct rcs_branch *rb;
1.1 jfb 1797: struct rcs_delta *crdp;
1798:
1.12 jfb 1799: if (rdp->rd_num != NULL)
1800: rcsnum_free(rdp->rd_num);
1801: if (rdp->rd_next != NULL)
1802: rcsnum_free(rdp->rd_next);
1803:
1.1 jfb 1804: if (rdp->rd_author != NULL)
1805: free(rdp->rd_author);
1806: if (rdp->rd_state != NULL)
1807: free(rdp->rd_state);
1808: if (rdp->rd_log != NULL)
1809: free(rdp->rd_log);
1810: if (rdp->rd_text != NULL)
1811: free(rdp->rd_text);
1.12 jfb 1812:
1813: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1814: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
1815: rcsnum_free(rb->rb_num);
1816: free(rb);
1817: }
1.1 jfb 1818:
1819: while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) {
1820: TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list);
1821: rcs_freedelta(crdp);
1822: }
1823:
1824: free(rdp);
1825: }
1826:
1827:
1828: /*
1829: * rcs_freepdata()
1830: *
1831: * Free the contents of the parser data structure.
1832: */
1833: static void
1834: rcs_freepdata(struct rcs_pdata *pd)
1835: {
1836: if (pd->rp_file != NULL)
1837: (void)fclose(pd->rp_file);
1838: if (pd->rp_buf != NULL)
1839: free(pd->rp_buf);
1840: free(pd);
1841: }
1842:
1843:
1844: /*
1845: * rcs_gettok()
1846: *
1847: * Get the next RCS token from the string <str>.
1848: */
1849: static int
1850: rcs_gettok(RCSFILE *rfp)
1851: {
1852: u_int i;
1853: int ch, last, type;
1.18 jfb 1854: size_t len;
1855: char *bp;
1.1 jfb 1856: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1857:
1858: type = RCS_TOK_ERR;
1859: bp = pdp->rp_buf;
1860: *bp = '\0';
1861:
1862: if (pdp->rp_pttype != RCS_TOK_ERR) {
1863: type = pdp->rp_pttype;
1864: strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen);
1865: pdp->rp_pttype = RCS_TOK_ERR;
1866: return (type);
1867: }
1868:
1869: /* skip leading whitespace */
1870: /* XXX we must skip backspace too for compatibility, should we? */
1871: do {
1872: ch = getc(pdp->rp_file);
1873: if (ch == '\n')
1.18 jfb 1874: pdp->rp_lines++;
1.1 jfb 1875: } while (isspace(ch));
1876:
1877: if (ch == EOF) {
1878: type = RCS_TOK_EOF;
1.14 deraadt 1879: } else if (ch == ';') {
1.1 jfb 1880: type = RCS_TOK_SCOLON;
1.14 deraadt 1881: } else if (ch == ':') {
1.1 jfb 1882: type = RCS_TOK_COLON;
1.14 deraadt 1883: } else if (isalpha(ch)) {
1.31 jfb 1884: type = RCS_TOK_ID;
1.1 jfb 1885: *(bp++) = ch;
1.18 jfb 1886: for (;;) {
1.1 jfb 1887: ch = getc(pdp->rp_file);
1.11 joris 1888: if (!isalnum(ch) && ch != '_' && ch != '-') {
1.1 jfb 1889: ungetc(ch, pdp->rp_file);
1890: break;
1891: }
1892: *(bp++) = ch;
1.18 jfb 1893: if (bp == pdp->rp_bufend - 1) {
1894: len = bp - pdp->rp_buf;
1895: if (rcs_growbuf(rfp) < 0) {
1896: type = RCS_TOK_ERR;
1897: break;
1898: }
1899: bp = pdp->rp_buf + len;
1900: }
1.1 jfb 1901: }
1902: *bp = '\0';
1903:
1.18 jfb 1904: if (type != RCS_TOK_ERR) {
1905: for (i = 0; i < RCS_NKEYS; i++) {
1906: if (strcmp(rcs_keys[i].rk_str,
1907: pdp->rp_buf) == 0) {
1908: type = rcs_keys[i].rk_id;
1909: break;
1910: }
1.1 jfb 1911: }
1912: }
1.14 deraadt 1913: } else if (ch == '@') {
1.1 jfb 1914: /* we have a string */
1.18 jfb 1915: type = RCS_TOK_STRING;
1.1 jfb 1916: for (;;) {
1917: ch = getc(pdp->rp_file);
1918: if (ch == '@') {
1919: ch = getc(pdp->rp_file);
1920: if (ch != '@') {
1921: ungetc(ch, pdp->rp_file);
1922: break;
1923: }
1.14 deraadt 1924: } else if (ch == '\n')
1.18 jfb 1925: pdp->rp_lines++;
1.1 jfb 1926:
1927: *(bp++) = ch;
1.18 jfb 1928: if (bp == pdp->rp_bufend - 1) {
1929: len = bp - pdp->rp_buf;
1930: if (rcs_growbuf(rfp) < 0) {
1931: type = RCS_TOK_ERR;
1932: break;
1933: }
1934: bp = pdp->rp_buf + len;
1935: }
1.1 jfb 1936: }
1937:
1938: *bp = '\0';
1.14 deraadt 1939: } else if (isdigit(ch)) {
1.1 jfb 1940: *(bp++) = ch;
1941: last = ch;
1942: type = RCS_TOK_NUM;
1943:
1944: for (;;) {
1945: ch = getc(pdp->rp_file);
1.18 jfb 1946: if (bp == pdp->rp_bufend)
1.1 jfb 1947: break;
1948: if (!isdigit(ch) && ch != '.') {
1949: ungetc(ch, pdp->rp_file);
1950: break;
1951: }
1952:
1953: if (last == '.' && ch == '.') {
1954: type = RCS_TOK_ERR;
1955: break;
1956: }
1957: last = ch;
1958: *(bp++) = ch;
1959: }
1.18 jfb 1960: *bp = '\0';
1.1 jfb 1961: }
1962:
1963: return (type);
1964: }
1965:
1966:
1967: /*
1968: * rcs_pushtok()
1969: *
1970: * Push a token back in the parser's token buffer.
1971: */
1972: static int
1973: rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
1974: {
1975: struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
1976:
1977: if (pdp->rp_pttype != RCS_TOK_ERR)
1978: return (-1);
1979:
1980: pdp->rp_pttype = type;
1981: strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok));
1982: return (0);
1983: }
1984:
1985:
1986: /*
1987: * rcs_stresc()
1988: *
1989: * Performs either escaping or unescaping of the string stored in <str>.
1990: * The operation is to escape special RCS characters if the <esc> argument
1991: * is 1, or unescape otherwise. The result is stored in the <buf> destination
1992: * buffer, and <blen> must originally point to the size of <buf>.
1993: * Returns the number of bytes which have been read from the source <str> and
1994: * operated on. The <blen> parameter will contain the number of bytes
1995: * actually copied in <buf>.
1996: */
1997: size_t
1998: rcs_stresc(int esc, const char *str, char *buf, size_t *blen)
1999: {
2000: size_t rlen;
2001: const char *sp;
2002: char *bp, *bep;
2003:
2004: rlen = 0;
2005: bp = buf;
2006: bep = buf + *blen - 1;
2007:
2008: for (sp = str; (*sp != '\0') && (bp <= (bep - 1)); sp++) {
2009: if (*sp == '@') {
2010: if (esc) {
2011: if (bp > (bep - 2))
2012: break;
2013: *(bp++) = '@';
1.14 deraadt 2014: } else {
1.1 jfb 2015: sp++;
2016: if (*sp != '@') {
2017: cvs_log(LP_WARN,
2018: "unknown escape character `%c' in "
2019: "RCS file", *sp);
2020: if (*sp == '\0')
2021: break;
2022: }
2023: }
2024: }
2025:
2026: *(bp++) = *sp;
2027: }
2028:
2029: *bp = '\0';
2030: *blen = (bp - buf);
2031: return (sp - str);
2032: }
2033:
2034:
2035: /*
2036: * rcs_splitlines()
2037: *
2038: * Split the contents of a file into a list of lines.
2039: */
2040: static struct rcs_foo*
2041: rcs_splitlines(const char *fcont)
2042: {
2043: char *dcp;
2044: struct rcs_foo *foo;
2045: struct rcs_line *lp;
2046:
2047: foo = (struct rcs_foo *)malloc(sizeof(*foo));
2048: if (foo == NULL) {
2049: cvs_log(LP_ERR, "failed to allocate line structure");
2050: return (NULL);
2051: }
2052: TAILQ_INIT(&(foo->rl_lines));
2053: foo->rl_nblines = 0;
2054: foo->rl_data = strdup(fcont);
2055: if (foo->rl_data == NULL) {
2056: cvs_log(LP_ERRNO, "failed to copy file contents");
2057: free(foo);
2058: return (NULL);
2059: }
2060:
2061: /*
2062: * Add a first bogus line with line number 0. This is used so we
2063: * can position the line pointer before 1 when changing the first line
2064: * in rcs_patch().
2065: */
2066: lp = (struct rcs_line *)malloc(sizeof(*lp));
1.5 vincent 2067: if (lp == NULL)
1.1 jfb 2068: return (NULL);
1.5 vincent 2069:
1.1 jfb 2070: lp->rl_line = NULL;
2071: lp->rl_lineno = 0;
2072: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
2073:
2074:
2075: for (dcp = foo->rl_data; *dcp != '\0';) {
2076: lp = (struct rcs_line *)malloc(sizeof(*lp));
2077: if (lp == NULL) {
2078: cvs_log(LP_ERR, "failed to allocate line entry");
2079: return (NULL);
2080: }
2081:
2082: lp->rl_line = dcp;
2083: lp->rl_lineno = ++(foo->rl_nblines);
2084: TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
2085:
2086: dcp = strchr(dcp, '\n');
2087: if (dcp == NULL) {
2088: break;
2089: }
2090: *(dcp++) = '\0';
2091: }
2092:
2093: return (foo);
1.5 vincent 2094: }
2095:
2096: static void
2097: rcs_freefoo(struct rcs_foo *fp)
2098: {
2099: struct rcs_line *lp;
2100:
2101: while ((lp = TAILQ_FIRST(&fp->rl_lines)) != NULL) {
2102: TAILQ_REMOVE(&fp->rl_lines, lp, rl_list);
2103: free(lp);
2104: }
2105: free(fp->rl_data);
2106: free(fp);
1.18 jfb 2107: }
2108:
2109: /*
2110: * rcs_growbuf()
2111: *
2112: * Attempt to grow the internal parse buffer for the RCS file <rf> by
2113: * RCS_BUFEXTSIZE.
2114: * In case of failure, the original buffer is left unmodified.
2115: * Returns 0 on success, or -1 on failure.
2116: */
2117: static int
2118: rcs_growbuf(RCSFILE *rf)
2119: {
2120: void *tmp;
2121: struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
2122:
2123: tmp = realloc(pdp->rp_buf, pdp->rp_blen + RCS_BUFEXTSIZE);
2124: if (tmp == NULL) {
2125: cvs_log(LP_ERRNO, "failed to grow RCS parse buffer");
2126: return (-1);
2127: }
2128:
2129: pdp->rp_buf = (char *)tmp;
2130: pdp->rp_blen += RCS_BUFEXTSIZE;
2131: pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
2132:
2133: return (0);
1.1 jfb 2134: }