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