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