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