1 /* $OpenBSD: rcs.c,v 1.9 2004/08/12 21:02:20 jfb Exp $ */ 2 /* 3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 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 24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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> 35 #include <string.h> 36 37 #include "rcs.h" 38 #include "log.h" 39 40 #define RCS_BUFSIZE 8192 41 42 43 /* RCS token types */ 44 #define RCS_TOK_ERR -1 45 #define RCS_TOK_EOF 0 46 #define RCS_TOK_NUM 1 47 #define RCS_TOK_ID 2 48 #define RCS_TOK_STRING 3 49 #define RCS_TOK_SCOLON 4 50 #define RCS_TOK_COLON 5 51 52 53 #define RCS_TOK_HEAD 8 54 #define RCS_TOK_BRANCH 9 55 #define RCS_TOK_ACCESS 10 56 #define RCS_TOK_SYMBOLS 11 57 #define RCS_TOK_LOCKS 12 58 #define RCS_TOK_COMMENT 13 59 #define RCS_TOK_EXPAND 14 60 #define RCS_TOK_DATE 15 61 #define RCS_TOK_AUTHOR 16 62 #define RCS_TOK_STATE 17 63 #define RCS_TOK_NEXT 18 64 #define RCS_TOK_BRANCHES 19 65 #define RCS_TOK_DESC 20 66 #define RCS_TOK_LOG 21 67 #define RCS_TOK_TEXT 22 68 #define RCS_TOK_STRICT 23 69 70 #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES)) 71 72 73 #define RCS_NOSCOL 0x01 /* no terminating semi-colon */ 74 #define RCS_VOPT 0x02 /* value is optional */ 75 76 77 78 /* opaque parse data */ 79 struct rcs_pdata { 80 u_int rp_line; 81 82 char *rp_buf; 83 size_t rp_blen; 84 85 /* pushback token buffer */ 86 char rp_ptok[128]; 87 int rp_pttype; /* token type, RCS_TOK_ERR if no token */ 88 89 FILE *rp_file; 90 }; 91 92 93 struct rcs_line { 94 char *rl_line; 95 int rl_lineno; 96 TAILQ_ENTRY(rcs_line) rl_list; 97 }; 98 TAILQ_HEAD(rcs_tqh, rcs_line); 99 100 struct rcs_foo { 101 int rl_nblines; 102 char *rl_data; 103 struct rcs_tqh rl_lines; 104 }; 105 106 static int rcs_parse_admin (RCSFILE *); 107 static int rcs_parse_delta (RCSFILE *); 108 static int rcs_parse_deltatext (RCSFILE *); 109 110 static int rcs_parse_access (RCSFILE *); 111 static int rcs_parse_symbols (RCSFILE *); 112 static int rcs_parse_locks (RCSFILE *); 113 static int rcs_parse_branches (RCSFILE *, struct rcs_delta *); 114 static void rcs_freedelta (struct rcs_delta *); 115 static void rcs_freepdata (struct rcs_pdata *); 116 static int rcs_gettok (RCSFILE *); 117 static int rcs_pushtok (RCSFILE *, const char *, int); 118 static int rcs_patch_lines (struct rcs_foo *, struct rcs_foo *); 119 120 static struct rcs_delta* rcs_findrev (RCSFILE *, RCSNUM *); 121 static struct rcs_foo* rcs_splitlines (const char *); 122 static void rcs_freefoo (struct rcs_foo *); 123 124 #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf 125 #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_blen 126 127 128 static struct rcs_key { 129 char rk_str[16]; 130 int rk_id; 131 int rk_val; 132 int rk_flags; 133 } rcs_keys[] = { 134 { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT }, 135 { "author", RCS_TOK_AUTHOR, RCS_TOK_STRING, 0 }, 136 { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT }, 137 { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT }, 138 { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT }, 139 { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 }, 140 { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL }, 141 { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT }, 142 { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT }, 143 { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 }, 144 { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL }, 145 { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT }, 146 { "state", RCS_TOK_STATE, RCS_TOK_STRING, RCS_VOPT }, 147 { "strict", RCS_TOK_STRICT, 0, 0, }, 148 { "symbols", RCS_TOK_SYMBOLS, 0, 0 }, 149 { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL }, 150 }; 151 152 153 154 /* 155 * rcs_open() 156 * 157 * Open a file containing RCS-formatted information. The file's path is 158 * given in <path>, and the opening mode is given in <mode>, which is either 159 * RCS_MODE_READ, RCS_MODE_WRITE, or RCS_MODE_RDWR. If the mode requests write 160 * access and the file does not exist, it will be created. 161 * The file isn't actually parsed by rcs_open(); parsing is delayed until the 162 * first operation that requires information from the file. 163 * Returns a handle to the opened file on success, or NULL on failure. 164 */ 165 166 RCSFILE* 167 rcs_open(const char *path, u_int mode) 168 { 169 RCSFILE *rfp; 170 struct stat st; 171 172 if ((stat(path, &st) == -1) && (errno == ENOENT) && 173 !(mode & RCS_MODE_WRITE)) { 174 cvs_log(LP_ERRNO, "cannot open RCS file `%s'", path); 175 return (NULL); 176 } 177 178 rfp = (RCSFILE *)malloc(sizeof(*rfp)); 179 if (rfp == NULL) { 180 cvs_log(LP_ERRNO, "failed to allocate RCS file structure"); 181 return (NULL); 182 } 183 memset(rfp, 0, sizeof(*rfp)); 184 185 rfp->rf_head = rcsnum_alloc(); 186 if (rfp->rf_head == NULL) { 187 free(rfp); 188 return (NULL); 189 } 190 191 rfp->rf_path = strdup(path); 192 if (rfp->rf_path == NULL) { 193 cvs_log(LP_ERRNO, "failed to duplicate RCS file path"); 194 rcs_close(rfp); 195 return (NULL); 196 } 197 198 rcsnum_aton(RCS_HEAD_INIT, NULL, rfp->rf_head); 199 200 rfp->rf_ref = 1; 201 rfp->rf_flags |= RCS_RF_SLOCK; 202 rfp->rf_mode = mode; 203 204 TAILQ_INIT(&(rfp->rf_delta)); 205 TAILQ_INIT(&(rfp->rf_symbols)); 206 TAILQ_INIT(&(rfp->rf_locks)); 207 208 if (rcs_parse(rfp) < 0) { 209 rcs_close(rfp); 210 return (NULL); 211 } 212 213 return (rfp); 214 } 215 216 217 /* 218 * rcs_close() 219 * 220 * Close an RCS file handle. 221 */ 222 223 void 224 rcs_close(RCSFILE *rfp) 225 { 226 struct rcs_delta *rdp; 227 228 if (rfp->rf_ref > 1) { 229 rfp->rf_ref--; 230 return; 231 } 232 233 while (!TAILQ_EMPTY(&(rfp->rf_delta))) { 234 rdp = TAILQ_FIRST(&(rfp->rf_delta)); 235 TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list); 236 rcs_freedelta(rdp); 237 } 238 239 if (rfp->rf_head != NULL) 240 rcsnum_free(rfp->rf_head); 241 242 if (rfp->rf_path != NULL) 243 free(rfp->rf_path); 244 if (rfp->rf_comment != NULL) 245 free(rfp->rf_comment); 246 if (rfp->rf_expand != NULL) 247 free(rfp->rf_expand); 248 if (rfp->rf_desc != NULL) 249 free(rfp->rf_desc); 250 free(rfp); 251 } 252 253 254 /* 255 * rcs_write() 256 * 257 * Write the contents of the RCS file handle <rfp> to disk in the file whose 258 * path is in <rf_path>. 259 * Returns 0 on success, or -1 on failure. 260 */ 261 262 int 263 rcs_write(RCSFILE *rfp) 264 { 265 FILE *fp; 266 char buf[1024], numbuf[64], *cp; 267 size_t rlen, len; 268 struct rcs_sym *symp; 269 struct rcs_delta *rdp; 270 271 if (rfp->rf_flags & RCS_RF_SYNCED) 272 return (0); 273 274 fp = fopen(rfp->rf_path, "w"); 275 if (fp == NULL) { 276 cvs_log(LP_ERRNO, "failed to open RCS output file `%s'", 277 rfp->rf_path); 278 return (-1); 279 } 280 281 rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf)); 282 fprintf(fp, "head\t%s;\n", numbuf); 283 fprintf(fp, "access;\n"); 284 285 fprintf(fp, "symbols\n"); 286 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 287 rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); 288 snprintf(buf, sizeof(buf), "%s:%s", symp->rs_name, numbuf); 289 fprintf(fp, "\t%s", buf); 290 if (symp != TAILQ_LAST(&(rfp->rf_symbols), rcs_slist)) 291 fputc('\n', fp); 292 } 293 fprintf(fp, ";\n"); 294 295 fprintf(fp, "locks;"); 296 297 if (rfp->rf_flags & RCS_RF_SLOCK) 298 fprintf(fp, " strict;"); 299 fputc('\n', fp); 300 301 if (rfp->rf_comment != NULL) 302 fprintf(fp, "comment\t@%s@;\n", rfp->rf_comment); 303 304 if (rfp->rf_expand != NULL) 305 fprintf(fp, "expand @ %s @;\n", rfp->rf_expand); 306 307 fprintf(fp, "\n\n"); 308 309 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 310 fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 311 sizeof(numbuf))); 312 fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;", 313 rdp->rd_date.tm_year, rdp->rd_date.tm_mon + 1, 314 rdp->rd_date.tm_mday, rdp->rd_date.tm_hour, 315 rdp->rd_date.tm_min, rdp->rd_date.tm_sec); 316 fprintf(fp, "\tauthor %s;\tstate %s;\n", 317 rdp->rd_author, rdp->rd_state); 318 fprintf(fp, "branches;\n"); 319 fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next, 320 numbuf, sizeof(numbuf))); 321 } 322 323 fprintf(fp, "\ndesc\n@%s@\n\n", rfp->rf_desc); 324 325 /* deltatexts */ 326 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 327 fprintf(fp, "\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 328 sizeof(numbuf))); 329 fprintf(fp, "log\n@%s@\ntext\n@", rdp->rd_log); 330 331 cp = rdp->rd_text; 332 do { 333 len = sizeof(buf); 334 rlen = rcs_stresc(1, cp, buf, &len); 335 fprintf(fp, "%s", buf); 336 cp += rlen; 337 } while (len != 0); 338 fprintf(fp, "@\n\n"); 339 } 340 fclose(fp); 341 342 rfp->rf_flags |= RCS_RF_SYNCED; 343 344 return (0); 345 } 346 347 348 /* 349 * rcs_addsym() 350 * 351 * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol 352 * is named <sym> and is bound to the RCS revision <snum>. 353 * Returns 0 on success, or -1 on failure. 354 */ 355 356 int 357 rcs_addsym(RCSFILE *rfp, const char *sym, RCSNUM *snum) 358 { 359 struct rcs_sym *symp; 360 361 /* first look for duplication */ 362 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 363 if (strcmp(symp->rs_name, sym) == 0) { 364 return (-1); 365 } 366 } 367 368 symp = (struct rcs_sym *)malloc(sizeof(*symp)); 369 if (symp == NULL) { 370 cvs_log(LP_ERRNO, "failed to allocate RCS symbol"); 371 return (-1); 372 } 373 374 symp->rs_name = strdup(sym); 375 symp->rs_num = rcsnum_alloc(); 376 rcsnum_cpy(snum, symp->rs_num, 0); 377 378 TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); 379 380 /* not synced anymore */ 381 rfp->rf_flags &= ~RCS_RF_SYNCED; 382 383 return (0); 384 } 385 386 387 /* 388 * rcs_patch() 389 * 390 * Apply an RCS-format patch pointed to by <patch> to the file contents 391 * found in <data>. 392 * Returns 0 on success, or -1 on failure. 393 */ 394 395 BUF* 396 rcs_patch(const char *data, const char *patch) 397 { 398 struct rcs_foo *dlines, *plines; 399 struct rcs_line *lp; 400 size_t len; 401 int lineno; 402 BUF *res; 403 404 len = strlen(data); 405 res = cvs_buf_alloc(len, BUF_AUTOEXT); 406 if (res == NULL) 407 return (NULL); 408 409 dlines = rcs_splitlines(data); 410 if (dlines == NULL) 411 return (NULL); 412 413 plines = rcs_splitlines(patch); 414 if (plines == NULL) { 415 rcs_freefoo(dlines); 416 return (NULL); 417 } 418 419 if (rcs_patch_lines(dlines, plines) < 0) { 420 rcs_freefoo(plines); 421 rcs_freefoo(dlines); 422 return (NULL); 423 } 424 425 lineno = 0; 426 TAILQ_FOREACH(lp, &dlines->rl_lines, rl_list) { 427 if (lineno != 0) 428 cvs_buf_fappend(res, "%s\n", lp->rl_line); 429 lineno++; 430 } 431 432 rcs_freefoo(dlines); 433 rcs_freefoo(plines); 434 return (res); 435 } 436 437 static int 438 rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines) 439 { 440 char op, *ep; 441 struct rcs_line *lp, *dlp, *ndlp; 442 int i, lineno, nbln; 443 444 dlp = TAILQ_FIRST(&(dlines->rl_lines)); 445 lp = TAILQ_FIRST(&(plines->rl_lines)); 446 447 /* skip first bogus line */ 448 for (lp = TAILQ_NEXT(lp, rl_list); lp != NULL; 449 lp = TAILQ_NEXT(lp, rl_list)) { 450 op = *(lp->rl_line); 451 lineno = (int)strtol((lp->rl_line + 1), &ep, 10); 452 if ((lineno > dlines->rl_nblines) || (lineno <= 0) || 453 (*ep != ' ')) { 454 cvs_log(LP_ERR, 455 "invalid line specification in RCS patch"); 456 return (NULL); 457 } 458 ep++; 459 nbln = (int)strtol(ep, &ep, 10); 460 if ((nbln <= 0) || (*ep != '\0')) { 461 cvs_log(LP_ERR, 462 "invalid line number specification in RCS patch"); 463 return (NULL); 464 } 465 466 /* find the appropriate line */ 467 for (;;) { 468 if (dlp == NULL) 469 break; 470 if (dlp->rl_lineno == lineno) 471 break; 472 if (dlp->rl_lineno > lineno) { 473 dlp = TAILQ_PREV(dlp, rcs_tqh, rl_list); 474 } 475 else if (dlp->rl_lineno < lineno) { 476 ndlp = TAILQ_NEXT(dlp, rl_list); 477 if (ndlp->rl_lineno > lineno) 478 break; 479 dlp = ndlp; 480 } 481 } 482 if (dlp == NULL) { 483 cvs_log(LP_ERR, 484 "can't find referenced line in RCS patch"); 485 return (NULL); 486 } 487 488 if (op == 'd') { 489 for (i = 0; (i < nbln) && (dlp != NULL); i++) { 490 ndlp = TAILQ_NEXT(dlp, rl_list); 491 TAILQ_REMOVE(&(dlines->rl_lines), dlp, rl_list); 492 dlp = ndlp; 493 } 494 } 495 else if (op == 'a') { 496 for (i = 0; i < nbln; i++) { 497 ndlp = lp; 498 lp = TAILQ_NEXT(lp, rl_list); 499 if (lp == NULL) { 500 cvs_log(LP_ERR, "truncated RCS patch"); 501 return (-1); 502 } 503 TAILQ_REMOVE(&(plines->rl_lines), lp, rl_list); 504 TAILQ_INSERT_AFTER(&(dlines->rl_lines), dlp, 505 lp, rl_list); 506 dlp = lp; 507 508 /* we don't want lookup to block on those */ 509 lp->rl_lineno = lineno; 510 511 lp = ndlp; 512 } 513 } 514 else { 515 cvs_log(LP_ERR, "unknown RCS patch operation `%c'", op); 516 return (-1); 517 } 518 519 /* last line of the patch, done */ 520 if (lp->rl_lineno == plines->rl_nblines) 521 break; 522 } 523 524 /* once we're done patching, rebuild the line numbers */ 525 lineno = 0; 526 TAILQ_FOREACH(lp, &(dlines->rl_lines), rl_list) 527 lp->rl_lineno = lineno++; 528 dlines->rl_nblines = lineno - 1; 529 530 return (0); 531 } 532 533 534 /* 535 * rcs_getrev() 536 * 537 * Get the whole contents of revision <rev> from the RCSFILE <rfp>. The 538 * returned buffer is dynamically allocated and should be released using 539 * cvs_buf_free() once the caller is done using it. 540 */ 541 542 BUF* 543 rcs_getrev(RCSFILE *rfp, RCSNUM *rev) 544 { 545 int res; 546 size_t len; 547 void *bp; 548 RCSNUM *crev; 549 BUF *rbuf; 550 struct rcs_delta *rdp = NULL; 551 552 res = rcsnum_cmp(rfp->rf_head, rev, 0); 553 if (res == 1) { 554 cvs_log(LP_ERR, "sorry, can't travel in the future yet"); 555 return (NULL); 556 } 557 else { 558 rdp = rcs_findrev(rfp, rfp->rf_head); 559 if (rdp == NULL) { 560 cvs_log(LP_ERR, "failed to get RCS HEAD revision"); 561 return (NULL); 562 } 563 564 len = strlen(rdp->rd_text); 565 rbuf = cvs_buf_alloc(len, BUF_AUTOEXT); 566 if (rbuf == NULL) 567 return (NULL); 568 cvs_buf_append(rbuf, rdp->rd_text, len); 569 570 if (res != 0) { 571 /* Apply patches backwards to get the right version. 572 * This will need some rework to support sub branches. 573 */ 574 crev = rcsnum_alloc(); 575 576 rcsnum_cpy(rfp->rf_head, crev, 0); 577 do { 578 crev->rn_id[crev->rn_len - 1]--; 579 rdp = rcs_findrev(rfp, crev); 580 if (rdp == NULL) 581 return (NULL); 582 583 cvs_buf_putc(rbuf, '\0'); 584 bp = cvs_buf_release(rbuf); 585 rbuf = rcs_patch((char *)bp, rdp->rd_text); 586 if (rbuf == NULL) 587 break; 588 } while (rcsnum_cmp(crev, rev, 0) != 0); 589 590 rcsnum_free(crev); 591 } 592 } 593 594 595 return (rbuf); 596 } 597 598 599 /* 600 * rcs_getrevbydate() 601 * 602 * Get an RCS revision by a specific date. 603 */ 604 605 RCSNUM* 606 rcs_getrevbydate(RCSFILE *rfp, struct tm *date) 607 { 608 return (NULL); 609 } 610 611 612 /* 613 * rcs_findrev() 614 * 615 * Find a specific revision's delta entry in the tree of the RCS file <rfp>. 616 * The revision number is given in <rev>. 617 * Returns a pointer to the delta on success, or NULL on failure. 618 */ 619 620 static struct rcs_delta* 621 rcs_findrev(RCSFILE *rfp, RCSNUM *rev) 622 { 623 u_int cmplen; 624 struct rcs_delta *rdp; 625 struct rcs_dlist *hp; 626 int found; 627 628 cmplen = 2; 629 hp = &(rfp->rf_delta); 630 631 do { 632 found = 0; 633 TAILQ_FOREACH(rdp, hp, rd_list) { 634 if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) { 635 if (cmplen == rev->rn_len) 636 return (rdp); 637 638 hp = &(rdp->rd_snodes); 639 cmplen += 2; 640 found = 1; 641 break; 642 } 643 } 644 } while (found && cmplen < rev->rn_len); 645 646 return (NULL); 647 } 648 649 650 /* 651 * rcs_parse() 652 * 653 * Parse the contents of file <path>, which are in the RCS format. 654 * Returns 0 on success, or -1 on failure. 655 */ 656 657 int 658 rcs_parse(RCSFILE *rfp) 659 { 660 int ret; 661 struct rcs_pdata *pdp; 662 663 if (rfp->rf_flags & RCS_RF_PARSED) 664 return (0); 665 666 pdp = (struct rcs_pdata *)malloc(sizeof(*pdp)); 667 if (pdp == NULL) { 668 cvs_log(LP_ERRNO, "failed to allocate RCS parser data"); 669 return (-1); 670 } 671 memset(pdp, 0, sizeof(*pdp)); 672 673 pdp->rp_line = 1; 674 pdp->rp_pttype = RCS_TOK_ERR; 675 676 pdp->rp_file = fopen(rfp->rf_path, "r"); 677 if (pdp->rp_file == NULL) { 678 cvs_log(LP_ERRNO, "failed to open RCS file `%s'", rfp->rf_path); 679 rcs_freepdata(pdp); 680 return (-1); 681 } 682 683 pdp->rp_buf = (char *)malloc(RCS_BUFSIZE); 684 if (pdp->rp_buf == NULL) { 685 cvs_log(LP_ERRNO, "failed to allocate RCS parser buffer"); 686 rcs_freepdata(pdp); 687 return (-1); 688 } 689 pdp->rp_blen = RCS_BUFSIZE; 690 691 /* ditch the strict lock */ 692 rfp->rf_flags &= ~RCS_RF_SLOCK; 693 rfp->rf_pdata = pdp; 694 695 if (rcs_parse_admin(rfp) < 0) { 696 rcs_freepdata(pdp); 697 return (-1); 698 } 699 700 for (;;) { 701 ret = rcs_parse_delta(rfp); 702 if (ret == 0) 703 break; 704 else if (ret == -1) { 705 rcs_freepdata(pdp); 706 return (-1); 707 } 708 } 709 710 ret = rcs_gettok(rfp); 711 if (ret != RCS_TOK_DESC) { 712 cvs_log(LP_ERR, "token `%s' found where RCS desc expected", 713 RCS_TOKSTR(rfp)); 714 rcs_freepdata(pdp); 715 return (-1); 716 } 717 718 ret = rcs_gettok(rfp); 719 if (ret != RCS_TOK_STRING) { 720 cvs_log(LP_ERR, "token `%s' found where RCS desc expected", 721 RCS_TOKSTR(rfp)); 722 rcs_freepdata(pdp); 723 return (-1); 724 } 725 726 rfp->rf_desc = strdup(RCS_TOKSTR(rfp)); 727 728 for (;;) { 729 ret = rcs_parse_deltatext(rfp); 730 if (ret == 0) 731 break; 732 else if (ret == -1) { 733 rcs_freepdata(pdp); 734 return (-1); 735 } 736 } 737 738 cvs_log(LP_DEBUG, "RCS file `%s' parsed OK (%u lines)", rfp->rf_path, 739 pdp->rp_line); 740 741 rcs_freepdata(pdp); 742 743 rfp->rf_pdata = NULL; 744 rfp->rf_flags |= RCS_RF_PARSED|RCS_RF_SYNCED; 745 746 return (0); 747 } 748 749 750 /* 751 * rcs_parse_admin() 752 * 753 * Parse the administrative portion of an RCS file. 754 * Returns 0 on success, or -1 on failure. 755 */ 756 757 static int 758 rcs_parse_admin(RCSFILE *rfp) 759 { 760 u_int i; 761 int tok, ntok, hmask; 762 struct rcs_key *rk; 763 764 /* hmask is a mask of the headers already encountered */ 765 hmask = 0; 766 for (;;) { 767 tok = rcs_gettok(rfp); 768 if (tok == RCS_TOK_ERR) { 769 cvs_log(LP_ERR, "parse error in RCS admin section"); 770 return (-1); 771 } 772 else if (tok == RCS_TOK_NUM) { 773 /* assume this is the start of the first delta */ 774 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 775 return (0); 776 } 777 778 rk = NULL; 779 for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++) 780 if (rcs_keys[i].rk_id == tok) 781 rk = &(rcs_keys[i]); 782 783 if (hmask & (1 << tok)) { 784 cvs_log(LP_ERR, "duplicate RCS key"); 785 return (-1); 786 } 787 hmask |= (1 << tok); 788 789 switch (tok) { 790 case RCS_TOK_HEAD: 791 case RCS_TOK_BRANCH: 792 case RCS_TOK_COMMENT: 793 case RCS_TOK_EXPAND: 794 ntok = rcs_gettok(rfp); 795 if (ntok == RCS_TOK_SCOLON) 796 break; 797 if (ntok != rk->rk_val) { 798 cvs_log(LP_ERR, 799 "invalid value type for RCS key `%s'", 800 rk->rk_str); 801 } 802 803 if (tok == RCS_TOK_HEAD) { 804 rcsnum_aton(RCS_TOKSTR(rfp), NULL, 805 rfp->rf_head); 806 } 807 else if (tok == RCS_TOK_BRANCH) { 808 rcsnum_aton(RCS_TOKSTR(rfp), NULL, 809 rfp->rf_branch); 810 } 811 else if (tok == RCS_TOK_COMMENT) { 812 rfp->rf_comment = strdup(RCS_TOKSTR(rfp)); 813 } 814 else if (tok == RCS_TOK_EXPAND) { 815 rfp->rf_expand = strdup(RCS_TOKSTR(rfp)); 816 } 817 818 /* now get the expected semi-colon */ 819 ntok = rcs_gettok(rfp); 820 if (ntok != RCS_TOK_SCOLON) { 821 cvs_log(LP_ERR, 822 "missing semi-colon after RCS `%s' key", 823 rk->rk_str); 824 return (-1); 825 } 826 break; 827 case RCS_TOK_ACCESS: 828 rcs_parse_access(rfp); 829 break; 830 case RCS_TOK_SYMBOLS: 831 rcs_parse_symbols(rfp); 832 break; 833 case RCS_TOK_LOCKS: 834 rcs_parse_locks(rfp); 835 break; 836 default: 837 cvs_log(LP_ERR, 838 "unexpected token `%s' in RCS admin section", 839 RCS_TOKSTR(rfp)); 840 return (-1); 841 } 842 } 843 844 return (0); 845 } 846 847 848 /* 849 * rcs_parse_delta() 850 * 851 * Parse an RCS delta section and allocate the structure to store that delta's 852 * information in the <rfp> delta list. 853 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 854 * -1 on error. 855 */ 856 857 static int 858 rcs_parse_delta(RCSFILE *rfp) 859 { 860 int ret, tok, ntok, hmask; 861 u_int i; 862 char *tokstr; 863 RCSNUM *datenum; 864 struct rcs_delta *rdp; 865 struct rcs_key *rk; 866 867 rdp = (struct rcs_delta *)malloc(sizeof(*rdp)); 868 if (rdp == NULL) { 869 cvs_log(LP_ERRNO, "failed to allocate RCS delta structure"); 870 return (-1); 871 } 872 memset(rdp, 0, sizeof(*rdp)); 873 874 rdp->rd_num = rcsnum_alloc(); 875 rdp->rd_next = rcsnum_alloc(); 876 877 TAILQ_INIT(&(rdp->rd_branches)); 878 879 tok = rcs_gettok(rfp); 880 if (tok != RCS_TOK_NUM) { 881 cvs_log(LP_ERR, "unexpected token `%s' at start of delta", 882 RCS_TOKSTR(rfp)); 883 rcs_freedelta(rdp); 884 return (-1); 885 } 886 rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num); 887 888 hmask = 0; 889 ret = 0; 890 tokstr = NULL; 891 892 for (;;) { 893 tok = rcs_gettok(rfp); 894 if (tok == RCS_TOK_ERR) { 895 cvs_log(LP_ERR, "parse error in RCS delta section"); 896 rcs_freedelta(rdp); 897 return (-1); 898 } 899 else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 900 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 901 ret = (tok == RCS_TOK_NUM ? 1 : 0); 902 break; 903 } 904 905 rk = NULL; 906 for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++) 907 if (rcs_keys[i].rk_id == tok) 908 rk = &(rcs_keys[i]); 909 910 if (hmask & (1 << tok)) { 911 cvs_log(LP_ERR, "duplicate RCS key"); 912 rcs_freedelta(rdp); 913 return (-1); 914 } 915 hmask |= (1 << tok); 916 917 switch (tok) { 918 case RCS_TOK_DATE: 919 case RCS_TOK_AUTHOR: 920 case RCS_TOK_STATE: 921 case RCS_TOK_NEXT: 922 ntok = rcs_gettok(rfp); 923 if (ntok == RCS_TOK_SCOLON) { 924 if (rk->rk_flags & RCS_VOPT) 925 break; 926 else { 927 cvs_log(LP_ERR, "missing mandatory " 928 "value to RCS key `%s'", 929 rk->rk_str); 930 rcs_freedelta(rdp); 931 return (-1); 932 } 933 } 934 935 if (ntok != rk->rk_val) { 936 cvs_log(LP_ERR, 937 "invalid value type for RCS key `%s'", 938 rk->rk_str); 939 rcs_freedelta(rdp); 940 return (-1); 941 } 942 943 if (tokstr != NULL) 944 free(tokstr); 945 tokstr = strdup(RCS_TOKSTR(rfp)); 946 947 948 /* now get the expected semi-colon */ 949 ntok = rcs_gettok(rfp); 950 if (ntok != RCS_TOK_SCOLON) { 951 cvs_log(LP_ERR, 952 "missing semi-colon after RCS `%s' key", 953 rk->rk_str); 954 rcs_freedelta(rdp); 955 return (-1); 956 } 957 958 if (tok == RCS_TOK_DATE) { 959 datenum = rcsnum_alloc(); 960 rcsnum_aton(tokstr, NULL, datenum); 961 if (datenum->rn_len != 6) { 962 cvs_log(LP_ERR, 963 "RCS date specification has %s " 964 "fields", 965 (datenum->rn_len > 6) ? "too many" : 966 "missing"); 967 rcs_freedelta(rdp); 968 } 969 rdp->rd_date.tm_year = datenum->rn_id[0]; 970 rdp->rd_date.tm_mon = datenum->rn_id[1] - 1; 971 rdp->rd_date.tm_mday = datenum->rn_id[2]; 972 rdp->rd_date.tm_hour = datenum->rn_id[3]; 973 rdp->rd_date.tm_min = datenum->rn_id[4]; 974 rdp->rd_date.tm_sec = datenum->rn_id[5]; 975 rcsnum_free(datenum); 976 } 977 else if (tok == RCS_TOK_AUTHOR) { 978 rdp->rd_author = tokstr; 979 tokstr = NULL; 980 } 981 else if (tok == RCS_TOK_STATE) { 982 rdp->rd_state = tokstr; 983 tokstr = NULL; 984 } 985 else if (tok == RCS_TOK_NEXT) { 986 rcsnum_aton(tokstr, NULL, rdp->rd_next); 987 } 988 break; 989 case RCS_TOK_BRANCHES: 990 rcs_parse_branches(rfp, rdp); 991 break; 992 default: 993 cvs_log(LP_ERR, 994 "unexpected token `%s' in RCS delta", 995 RCS_TOKSTR(rfp)); 996 rcs_freedelta(rdp); 997 return (-1); 998 } 999 } 1000 1001 TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); 1002 1003 return (ret); 1004 } 1005 1006 1007 /* 1008 * rcs_parse_deltatext() 1009 * 1010 * Parse an RCS delta text section and fill in the log and text field of the 1011 * appropriate delta section. 1012 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1013 * -1 on error. 1014 */ 1015 1016 static int 1017 rcs_parse_deltatext(RCSFILE *rfp) 1018 { 1019 int tok; 1020 RCSNUM *tnum; 1021 struct rcs_delta *rdp; 1022 1023 tnum = rcsnum_alloc(); 1024 if (tnum == NULL) 1025 return (-1); 1026 1027 tok = rcs_gettok(rfp); 1028 if (tok == RCS_TOK_EOF) 1029 return (0); 1030 1031 if (tok != RCS_TOK_NUM) { 1032 cvs_log(LP_ERR, 1033 "unexpected token `%s' at start of RCS delta text", 1034 RCS_TOKSTR(rfp)); 1035 return (-1); 1036 } 1037 rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum); 1038 1039 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1040 if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0) 1041 break; 1042 } 1043 if (rdp == NULL) { 1044 cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta", 1045 RCS_TOKSTR(rfp)); 1046 return (-1); 1047 } 1048 1049 tok = rcs_gettok(rfp); 1050 if (tok != RCS_TOK_LOG) { 1051 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 1052 RCS_TOKSTR(rfp)); 1053 return (-1); 1054 } 1055 1056 tok = rcs_gettok(rfp); 1057 if (tok != RCS_TOK_STRING) { 1058 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 1059 RCS_TOKSTR(rfp)); 1060 return (-1); 1061 } 1062 rdp->rd_log = strdup(RCS_TOKSTR(rfp)); 1063 if (rdp->rd_log == NULL) { 1064 cvs_log(LP_ERRNO, "failed to copy RCS deltatext log"); 1065 return (-1); 1066 } 1067 1068 tok = rcs_gettok(rfp); 1069 if (tok != RCS_TOK_TEXT) { 1070 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 1071 RCS_TOKSTR(rfp)); 1072 return (-1); 1073 } 1074 1075 tok = rcs_gettok(rfp); 1076 if (tok != RCS_TOK_STRING) { 1077 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 1078 RCS_TOKSTR(rfp)); 1079 return (-1); 1080 } 1081 1082 rdp->rd_text = strdup(RCS_TOKSTR(rfp)); 1083 if (rdp->rd_text == NULL) { 1084 cvs_log(LP_ERRNO, "failed to copy RCS delta text"); 1085 return (-1); 1086 } 1087 1088 return (1); 1089 } 1090 1091 1092 /* 1093 * rcs_parse_access() 1094 * 1095 * Parse the access list given as value to the `access' keyword. 1096 * Returns 0 on success, or -1 on failure. 1097 */ 1098 1099 static int 1100 rcs_parse_access(RCSFILE *rfp) 1101 { 1102 int type; 1103 1104 while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) { 1105 if (type != RCS_TOK_ID) { 1106 cvs_log(LP_ERR, "unexpected token `%s' in access list", 1107 RCS_TOKSTR(rfp)); 1108 return (-1); 1109 } 1110 } 1111 1112 return (0); 1113 } 1114 1115 1116 /* 1117 * rcs_parse_symbols() 1118 * 1119 * Parse the symbol list given as value to the `symbols' keyword. 1120 * Returns 0 on success, or -1 on failure. 1121 */ 1122 1123 static int 1124 rcs_parse_symbols(RCSFILE *rfp) 1125 { 1126 int type; 1127 struct rcs_sym *symp; 1128 1129 for (;;) { 1130 type = rcs_gettok(rfp); 1131 if (type == RCS_TOK_SCOLON) 1132 break; 1133 1134 if (type != RCS_TOK_STRING) { 1135 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1136 RCS_TOKSTR(rfp)); 1137 return (-1); 1138 } 1139 1140 symp = (struct rcs_sym *)malloc(sizeof(*symp)); 1141 if (symp == NULL) { 1142 cvs_log(LP_ERRNO, "failed to allocate RCS symbol"); 1143 return (-1); 1144 } 1145 symp->rs_name = strdup(RCS_TOKSTR(rfp)); 1146 symp->rs_num = rcsnum_alloc(); 1147 1148 type = rcs_gettok(rfp); 1149 if (type != RCS_TOK_COLON) { 1150 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1151 RCS_TOKSTR(rfp)); 1152 free(symp->rs_name); 1153 free(symp); 1154 return (-1); 1155 } 1156 1157 type = rcs_gettok(rfp); 1158 if (type != RCS_TOK_NUM) { 1159 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1160 RCS_TOKSTR(rfp)); 1161 free(symp->rs_name); 1162 free(symp); 1163 return (-1); 1164 } 1165 1166 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) { 1167 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 1168 RCS_TOKSTR(rfp)); 1169 free(symp->rs_name); 1170 free(symp); 1171 return (-1); 1172 } 1173 1174 TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); 1175 } 1176 1177 return (0); 1178 } 1179 1180 1181 /* 1182 * rcs_parse_locks() 1183 * 1184 * Parse the lock list given as value to the `locks' keyword. 1185 * Returns 0 on success, or -1 on failure. 1186 */ 1187 1188 static int 1189 rcs_parse_locks(RCSFILE *rfp) 1190 { 1191 int type; 1192 struct rcs_lock *lkp; 1193 1194 for (;;) { 1195 type = rcs_gettok(rfp); 1196 if (type == RCS_TOK_SCOLON) 1197 break; 1198 1199 if (type != RCS_TOK_ID) { 1200 cvs_log(LP_ERR, "unexpected token `%s' in lock list", 1201 RCS_TOKSTR(rfp)); 1202 return (-1); 1203 } 1204 1205 lkp = (struct rcs_lock *)malloc(sizeof(*lkp)); 1206 if (lkp == NULL) { 1207 cvs_log(LP_ERRNO, "failed to allocate RCS lock"); 1208 return (-1); 1209 } 1210 lkp->rl_num = rcsnum_alloc(); 1211 1212 type = rcs_gettok(rfp); 1213 if (type != RCS_TOK_COLON) { 1214 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1215 RCS_TOKSTR(rfp)); 1216 free(lkp); 1217 return (-1); 1218 } 1219 1220 type = rcs_gettok(rfp); 1221 if (type != RCS_TOK_NUM) { 1222 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1223 RCS_TOKSTR(rfp)); 1224 free(lkp); 1225 return (-1); 1226 } 1227 1228 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) { 1229 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 1230 RCS_TOKSTR(rfp)); 1231 free(lkp); 1232 return (-1); 1233 } 1234 1235 TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list); 1236 } 1237 1238 /* check if we have a `strict' */ 1239 type = rcs_gettok(rfp); 1240 if (type != RCS_TOK_STRICT) { 1241 rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); 1242 } 1243 else { 1244 rfp->rf_flags |= RCS_RF_SLOCK; 1245 1246 type = rcs_gettok(rfp); 1247 if (type != RCS_TOK_SCOLON) { 1248 cvs_log(LP_ERR, 1249 "missing semi-colon after `strict' keyword"); 1250 return (-1); 1251 } 1252 } 1253 1254 return (0); 1255 } 1256 1257 /* 1258 * rcs_parse_branches() 1259 * 1260 * Parse the list of branches following a `branches' keyword in a delta. 1261 * Returns 0 on success, or -1 on failure. 1262 */ 1263 1264 static int 1265 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp) 1266 { 1267 int type; 1268 struct rcs_branch *brp; 1269 1270 for (;;) { 1271 type = rcs_gettok(rfp); 1272 if (type == RCS_TOK_SCOLON) 1273 break; 1274 1275 if (type != RCS_TOK_NUM) { 1276 cvs_log(LP_ERR, 1277 "unexpected token `%s' in list of branches", 1278 RCS_TOKSTR(rfp)); 1279 return (-1); 1280 } 1281 1282 brp = (struct rcs_branch *)malloc(sizeof(*brp)); 1283 if (brp == NULL) { 1284 cvs_log(LP_ERRNO, "failed to allocate RCS branch"); 1285 return (-1); 1286 } 1287 brp->rb_num = rcsnum_alloc(); 1288 rcsnum_aton(RCS_TOKSTR(rfp), NULL, brp->rb_num); 1289 1290 TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); 1291 } 1292 1293 return (0); 1294 } 1295 1296 1297 /* 1298 * rcs_freedelta() 1299 * 1300 * Free the contents of a delta structure. 1301 */ 1302 1303 void 1304 rcs_freedelta(struct rcs_delta *rdp) 1305 { 1306 struct rcs_delta *crdp; 1307 1308 if (rdp->rd_author != NULL) 1309 free(rdp->rd_author); 1310 if (rdp->rd_state != NULL) 1311 free(rdp->rd_state); 1312 if (rdp->rd_log != NULL) 1313 free(rdp->rd_log); 1314 if (rdp->rd_text != NULL) 1315 free(rdp->rd_text); 1316 1317 while ((crdp = TAILQ_FIRST(&(rdp->rd_snodes))) != NULL) { 1318 TAILQ_REMOVE(&(rdp->rd_snodes), crdp, rd_list); 1319 rcs_freedelta(crdp); 1320 } 1321 1322 free(rdp); 1323 } 1324 1325 1326 /* 1327 * rcs_freepdata() 1328 * 1329 * Free the contents of the parser data structure. 1330 */ 1331 1332 static void 1333 rcs_freepdata(struct rcs_pdata *pd) 1334 { 1335 if (pd->rp_file != NULL) 1336 (void)fclose(pd->rp_file); 1337 if (pd->rp_buf != NULL) 1338 free(pd->rp_buf); 1339 free(pd); 1340 } 1341 1342 1343 /* 1344 * rcs_gettok() 1345 * 1346 * Get the next RCS token from the string <str>. 1347 */ 1348 1349 static int 1350 rcs_gettok(RCSFILE *rfp) 1351 { 1352 u_int i; 1353 int ch, last, type; 1354 char *bp, *bep; 1355 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 1356 1357 type = RCS_TOK_ERR; 1358 bp = pdp->rp_buf; 1359 bep = pdp->rp_buf + pdp->rp_blen - 1; 1360 *bp = '\0'; 1361 1362 if (pdp->rp_pttype != RCS_TOK_ERR) { 1363 type = pdp->rp_pttype; 1364 strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen); 1365 pdp->rp_pttype = RCS_TOK_ERR; 1366 return (type); 1367 } 1368 1369 /* skip leading whitespace */ 1370 /* XXX we must skip backspace too for compatibility, should we? */ 1371 do { 1372 ch = getc(pdp->rp_file); 1373 if (ch == '\n') 1374 pdp->rp_line++; 1375 } while (isspace(ch)); 1376 1377 if (ch == EOF) { 1378 type = RCS_TOK_EOF; 1379 } 1380 else if (ch == ';') { 1381 type = RCS_TOK_SCOLON; 1382 } 1383 else if (ch == ':') { 1384 type = RCS_TOK_COLON; 1385 } 1386 else if (isalpha(ch)) { 1387 *(bp++) = ch; 1388 while (bp <= bep - 1) { 1389 ch = getc(pdp->rp_file); 1390 if (!isalnum(ch)) { 1391 ungetc(ch, pdp->rp_file); 1392 break; 1393 } 1394 *(bp++) = ch; 1395 } 1396 *bp = '\0'; 1397 1398 for (i = 0; i < sizeof(rcs_keys)/sizeof(rcs_keys[0]); i++) { 1399 if (strcmp(rcs_keys[i].rk_str, pdp->rp_buf) == 0) { 1400 type = rcs_keys[i].rk_id; 1401 break; 1402 } 1403 } 1404 1405 /* not a keyword, assume it's just a string */ 1406 if (type == RCS_TOK_ERR) 1407 type = RCS_TOK_STRING; 1408 } 1409 else if (ch == '@') { 1410 /* we have a string */ 1411 for (;;) { 1412 ch = getc(pdp->rp_file); 1413 if (ch == '@') { 1414 ch = getc(pdp->rp_file); 1415 if (ch != '@') { 1416 ungetc(ch, pdp->rp_file); 1417 break; 1418 } 1419 } 1420 else if (ch == '\n') 1421 pdp->rp_line++; 1422 1423 *(bp++) = ch; 1424 if (bp == bep) 1425 break; 1426 } 1427 1428 *bp = '\0'; 1429 type = RCS_TOK_STRING; 1430 } 1431 else if (isdigit(ch)) { 1432 *(bp++) = ch; 1433 last = ch; 1434 type = RCS_TOK_NUM; 1435 1436 for (;;) { 1437 ch = getc(pdp->rp_file); 1438 if (bp == bep) 1439 break; 1440 if (!isdigit(ch) && ch != '.') { 1441 ungetc(ch, pdp->rp_file); 1442 break; 1443 } 1444 1445 if (last == '.' && ch == '.') { 1446 type = RCS_TOK_ERR; 1447 break; 1448 } 1449 last = ch; 1450 *(bp++) = ch; 1451 } 1452 *(bp) = '\0'; 1453 } 1454 1455 return (type); 1456 } 1457 1458 1459 /* 1460 * rcs_pushtok() 1461 * 1462 * Push a token back in the parser's token buffer. 1463 */ 1464 1465 static int 1466 rcs_pushtok(RCSFILE *rfp, const char *tok, int type) 1467 { 1468 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 1469 1470 if (pdp->rp_pttype != RCS_TOK_ERR) 1471 return (-1); 1472 1473 pdp->rp_pttype = type; 1474 strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)); 1475 return (0); 1476 } 1477 1478 1479 /* 1480 * rcs_stresc() 1481 * 1482 * Performs either escaping or unescaping of the string stored in <str>. 1483 * The operation is to escape special RCS characters if the <esc> argument 1484 * is 1, or unescape otherwise. The result is stored in the <buf> destination 1485 * buffer, and <blen> must originally point to the size of <buf>. 1486 * Returns the number of bytes which have been read from the source <str> and 1487 * operated on. The <blen> parameter will contain the number of bytes 1488 * actually copied in <buf>. 1489 */ 1490 1491 size_t 1492 rcs_stresc(int esc, const char *str, char *buf, size_t *blen) 1493 { 1494 size_t rlen; 1495 const char *sp; 1496 char *bp, *bep; 1497 1498 rlen = 0; 1499 bp = buf; 1500 bep = buf + *blen - 1; 1501 1502 for (sp = str; (*sp != '\0') && (bp <= (bep - 1)); sp++) { 1503 if (*sp == '@') { 1504 if (esc) { 1505 if (bp > (bep - 2)) 1506 break; 1507 *(bp++) = '@'; 1508 } 1509 else { 1510 sp++; 1511 if (*sp != '@') { 1512 cvs_log(LP_WARN, 1513 "unknown escape character `%c' in " 1514 "RCS file", *sp); 1515 if (*sp == '\0') 1516 break; 1517 } 1518 } 1519 } 1520 1521 *(bp++) = *sp; 1522 } 1523 1524 *bp = '\0'; 1525 *blen = (bp - buf); 1526 return (sp - str); 1527 } 1528 1529 1530 /* 1531 * rcs_splitlines() 1532 * 1533 * Split the contents of a file into a list of lines. 1534 */ 1535 1536 static struct rcs_foo* 1537 rcs_splitlines(const char *fcont) 1538 { 1539 char *dcp; 1540 struct rcs_foo *foo; 1541 struct rcs_line *lp; 1542 1543 foo = (struct rcs_foo *)malloc(sizeof(*foo)); 1544 if (foo == NULL) { 1545 cvs_log(LP_ERR, "failed to allocate line structure"); 1546 return (NULL); 1547 } 1548 TAILQ_INIT(&(foo->rl_lines)); 1549 foo->rl_nblines = 0; 1550 foo->rl_data = strdup(fcont); 1551 if (foo->rl_data == NULL) { 1552 cvs_log(LP_ERRNO, "failed to copy file contents"); 1553 free(foo); 1554 return (NULL); 1555 } 1556 1557 /* 1558 * Add a first bogus line with line number 0. This is used so we 1559 * can position the line pointer before 1 when changing the first line 1560 * in rcs_patch(). 1561 */ 1562 lp = (struct rcs_line *)malloc(sizeof(*lp)); 1563 if (lp == NULL) 1564 return (NULL); 1565 1566 lp->rl_line = NULL; 1567 lp->rl_lineno = 0; 1568 TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list); 1569 1570 1571 for (dcp = foo->rl_data; *dcp != '\0';) { 1572 lp = (struct rcs_line *)malloc(sizeof(*lp)); 1573 if (lp == NULL) { 1574 cvs_log(LP_ERR, "failed to allocate line entry"); 1575 return (NULL); 1576 } 1577 1578 lp->rl_line = dcp; 1579 lp->rl_lineno = ++(foo->rl_nblines); 1580 TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list); 1581 1582 dcp = strchr(dcp, '\n'); 1583 if (dcp == NULL) { 1584 break; 1585 } 1586 *(dcp++) = '\0'; 1587 } 1588 1589 return (foo); 1590 } 1591 1592 static void 1593 rcs_freefoo(struct rcs_foo *fp) 1594 { 1595 struct rcs_line *lp; 1596 1597 while ((lp = TAILQ_FIRST(&fp->rl_lines)) != NULL) { 1598 TAILQ_REMOVE(&fp->rl_lines, lp, rl_list); 1599 free(lp); 1600 } 1601 free(fp->rl_data); 1602 free(fp); 1603 } 1604