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