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