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