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