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