1 /* $OpenBSD: rcs.c,v 1.210 2007/02/22 06:42:09 otto 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/stat.h> 28 29 #include <ctype.h> 30 #include <errno.h> 31 #include <libgen.h> 32 #include <pwd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "cvs.h" 38 #include "diff.h" 39 #include "rcs.h" 40 41 #define RCS_BUFSIZE 16384 42 #define RCS_BUFEXTSIZE 8192 43 #define RCS_KWEXP_SIZE 1024 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 #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 #define RCS_NOSCOL 0x01 /* no terminating semi-colon */ 74 #define RCS_VOPT 0x02 /* value is optional */ 75 76 /* opaque parse data */ 77 struct rcs_pdata { 78 u_int rp_lines; 79 80 char *rp_buf; 81 size_t rp_blen; 82 char *rp_bufend; 83 size_t rp_tlen; 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 #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf 93 #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen 94 95 /* invalid characters in RCS symbol names */ 96 static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; 97 98 /* comment leaders, depending on the file's suffix */ 99 static const struct rcs_comment { 100 const char *rc_suffix; 101 const char *rc_cstr; 102 } rcs_comments[] = { 103 { "1", ".\\\" " }, 104 { "2", ".\\\" " }, 105 { "3", ".\\\" " }, 106 { "4", ".\\\" " }, 107 { "5", ".\\\" " }, 108 { "6", ".\\\" " }, 109 { "7", ".\\\" " }, 110 { "8", ".\\\" " }, 111 { "9", ".\\\" " }, 112 { "a", "-- " }, /* Ada */ 113 { "ada", "-- " }, 114 { "adb", "-- " }, 115 { "asm", ";; " }, /* assembler (MS-DOS) */ 116 { "ads", "-- " }, /* Ada */ 117 { "bat", ":: " }, /* batch (MS-DOS) */ 118 { "body", "-- " }, /* Ada */ 119 { "c", " * " }, /* C */ 120 { "c++", "// " }, /* C++ */ 121 { "cc", "// " }, 122 { "cpp", "// " }, 123 { "cxx", "// " }, 124 { "m", "// " }, /* Objective-C */ 125 { "cl", ";;; " }, /* Common Lisp */ 126 { "cmd", ":: " }, /* command (OS/2) */ 127 { "cmf", "c " }, /* CM Fortran */ 128 { "csh", "# " }, /* shell */ 129 { "e", "# " }, /* efl */ 130 { "epsf", "% " }, /* encapsulated postscript */ 131 { "epsi", "% " }, /* encapsulated postscript */ 132 { "el", "; " }, /* Emacs Lisp */ 133 { "f", "c " }, /* Fortran */ 134 { "for", "c " }, 135 { "h", " * " }, /* C-header */ 136 { "hh", "// " }, /* C++ header */ 137 { "hpp", "// " }, 138 { "hxx", "// " }, 139 { "in", "# " }, /* for Makefile.in */ 140 { "l", " * " }, /* lex */ 141 { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ 142 { "mak", "# " }, /* makefile, e.g. Visual C++ */ 143 { "me", ".\\\" " }, /* me-macros t/nroff */ 144 { "ml", "; " }, /* mocklisp */ 145 { "mm", ".\\\" " }, /* mm-macros t/nroff */ 146 { "ms", ".\\\" " }, /* ms-macros t/nroff */ 147 { "man", ".\\\" " }, /* man-macros t/nroff */ 148 { "p", " * " }, /* pascal */ 149 { "pas", " * " }, 150 { "pl", "# " }, /* Perl (conflict with Prolog) */ 151 { "pm", "# " }, /* Perl module */ 152 { "ps", "% " }, /* postscript */ 153 { "psw", "% " }, /* postscript wrap */ 154 { "pswm", "% " }, /* postscript wrap */ 155 { "r", "# " }, /* ratfor */ 156 { "rc", " * " }, /* Microsoft Windows resource file */ 157 { "red", "% " }, /* psl/rlisp */ 158 { "sh", "# " }, /* shell */ 159 { "sl", "% " }, /* psl */ 160 { "spec", "-- " }, /* Ada */ 161 { "tex", "% " }, /* tex */ 162 { "y", " * " }, /* yacc */ 163 { "ye", " * " }, /* yacc-efl */ 164 { "yr", " * " }, /* yacc-ratfor */ 165 }; 166 167 struct rcs_kw rcs_expkw[] = { 168 { "Author", RCS_KW_AUTHOR }, 169 { "Date", RCS_KW_DATE }, 170 { "Header", RCS_KW_HEADER }, 171 { "Id", RCS_KW_ID }, 172 { "Log", RCS_KW_LOG }, 173 { "Name", RCS_KW_NAME }, 174 { "RCSfile", RCS_KW_RCSFILE }, 175 { "Revision", RCS_KW_REVISION }, 176 { "Source", RCS_KW_SOURCE }, 177 { "State", RCS_KW_STATE }, 178 }; 179 180 #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0])) 181 182 static struct rcs_key { 183 char rk_str[16]; 184 int rk_id; 185 int rk_val; 186 int rk_flags; 187 } rcs_keys[] = { 188 { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT }, 189 { "author", RCS_TOK_AUTHOR, RCS_TOK_ID, 0 }, 190 { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT }, 191 { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT }, 192 { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT }, 193 { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 }, 194 { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL }, 195 { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT }, 196 { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT }, 197 { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 }, 198 { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL }, 199 { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT }, 200 { "state", RCS_TOK_STATE, RCS_TOK_ID, RCS_VOPT }, 201 { "strict", RCS_TOK_STRICT, 0, 0, }, 202 { "symbols", RCS_TOK_SYMBOLS, 0, 0 }, 203 { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL }, 204 }; 205 206 #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0])) 207 208 static const char *rcs_errstrs[] = { 209 "No error", 210 "No such entry", 211 "Duplicate entry found", 212 "Bad RCS number", 213 "Invalid RCS symbol", 214 "Parse error", 215 }; 216 217 #define RCS_NERR (sizeof(rcs_errstrs)/sizeof(rcs_errstrs[0])) 218 219 int rcs_errno = RCS_ERR_NOERR; 220 221 int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *); 222 static void rcs_parse_init(RCSFILE *); 223 static int rcs_parse_admin(RCSFILE *); 224 static int rcs_parse_delta(RCSFILE *); 225 static void rcs_parse_deltas(RCSFILE *, RCSNUM *); 226 static int rcs_parse_deltatext(RCSFILE *); 227 static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *); 228 static void rcs_parse_desc(RCSFILE *, RCSNUM *); 229 230 static int rcs_parse_access(RCSFILE *); 231 static int rcs_parse_symbols(RCSFILE *); 232 static int rcs_parse_locks(RCSFILE *); 233 static int rcs_parse_branches(RCSFILE *, struct rcs_delta *); 234 static void rcs_freedelta(struct rcs_delta *); 235 static void rcs_freepdata(struct rcs_pdata *); 236 static int rcs_gettok(RCSFILE *); 237 static int rcs_pushtok(RCSFILE *, const char *, int); 238 static void rcs_growbuf(RCSFILE *); 239 static void rcs_strprint(const u_char *, size_t, FILE *); 240 241 static void rcs_kwexp_line(char *, struct rcs_delta *, struct cvs_line *, 242 int mode); 243 244 RCSFILE * 245 rcs_open(const char *path, int fd, int flags, ...) 246 { 247 int mode; 248 mode_t fmode; 249 RCSFILE *rfp; 250 va_list vap; 251 struct rcs_delta *rdp; 252 struct rcs_lock *lkr; 253 254 fmode = S_IRUSR|S_IRGRP|S_IROTH; 255 flags &= 0xffff; /* ditch any internal flags */ 256 257 if (flags & RCS_CREATE) { 258 va_start(vap, flags); 259 mode = va_arg(vap, int); 260 va_end(vap); 261 fmode = (mode_t)mode; 262 } 263 264 rfp = xcalloc(1, sizeof(*rfp)); 265 266 rfp->rf_path = xstrdup(path); 267 rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED; 268 rfp->rf_mode = fmode; 269 rfp->fd = fd; 270 rfp->rf_dead = 0; 271 rfp->rf_inattic = 0; 272 273 TAILQ_INIT(&(rfp->rf_delta)); 274 TAILQ_INIT(&(rfp->rf_access)); 275 TAILQ_INIT(&(rfp->rf_symbols)); 276 TAILQ_INIT(&(rfp->rf_locks)); 277 278 if (!(rfp->rf_flags & RCS_CREATE)) 279 rcs_parse_init(rfp); 280 281 /* fill in rd_locker */ 282 TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) { 283 if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) { 284 rcs_close(rfp); 285 return (NULL); 286 } 287 288 rdp->rd_locker = xstrdup(lkr->rl_name); 289 } 290 291 return (rfp); 292 } 293 294 /* 295 * rcs_close() 296 * 297 * Close an RCS file handle. 298 */ 299 void 300 rcs_close(RCSFILE *rfp) 301 { 302 struct rcs_delta *rdp; 303 struct rcs_access *rap; 304 struct rcs_lock *rlp; 305 struct rcs_sym *rsp; 306 307 if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED)) 308 rcs_write(rfp); 309 310 while (!TAILQ_EMPTY(&(rfp->rf_delta))) { 311 rdp = TAILQ_FIRST(&(rfp->rf_delta)); 312 TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list); 313 rcs_freedelta(rdp); 314 } 315 316 while (!TAILQ_EMPTY(&(rfp->rf_access))) { 317 rap = TAILQ_FIRST(&(rfp->rf_access)); 318 TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list); 319 xfree(rap->ra_name); 320 xfree(rap); 321 } 322 323 while (!TAILQ_EMPTY(&(rfp->rf_symbols))) { 324 rsp = TAILQ_FIRST(&(rfp->rf_symbols)); 325 TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list); 326 rcsnum_free(rsp->rs_num); 327 xfree(rsp->rs_name); 328 xfree(rsp); 329 } 330 331 while (!TAILQ_EMPTY(&(rfp->rf_locks))) { 332 rlp = TAILQ_FIRST(&(rfp->rf_locks)); 333 TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list); 334 rcsnum_free(rlp->rl_num); 335 xfree(rlp->rl_name); 336 xfree(rlp); 337 } 338 339 if (rfp->rf_head != NULL) 340 rcsnum_free(rfp->rf_head); 341 if (rfp->rf_branch != NULL) 342 rcsnum_free(rfp->rf_branch); 343 344 if (rfp->rf_path != NULL) 345 xfree(rfp->rf_path); 346 if (rfp->rf_comment != NULL) 347 xfree(rfp->rf_comment); 348 if (rfp->rf_expand != NULL) 349 xfree(rfp->rf_expand); 350 if (rfp->rf_desc != NULL) 351 xfree(rfp->rf_desc); 352 if (rfp->rf_pdata != NULL) 353 rcs_freepdata(rfp->rf_pdata); 354 xfree(rfp); 355 } 356 357 /* 358 * rcs_write() 359 * 360 * Write the contents of the RCS file handle <rfp> to disk in the file whose 361 * path is in <rf_path>. 362 */ 363 void 364 rcs_write(RCSFILE *rfp) 365 { 366 FILE *fp; 367 char buf[1024], numbuf[64], *fn, tmpdir[MAXPATHLEN]; 368 struct rcs_access *ap; 369 struct rcs_sym *symp; 370 struct rcs_branch *brp; 371 struct rcs_delta *rdp; 372 struct rcs_lock *lkp; 373 size_t len; 374 int fd, saved_errno; 375 376 fd = -1; 377 378 if (rfp->rf_flags & RCS_SYNCED) 379 return; 380 381 if (cvs_noexec == 1) 382 return; 383 384 /* Write operations need the whole file parsed */ 385 rcs_parse_deltatexts(rfp, NULL); 386 387 if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir)) 388 fatal("rcs_write: truncation"); 389 (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir)); 390 391 if ((fd = mkstemp(fn)) == -1) 392 fatal("%s", fn); 393 394 if ((fp = fdopen(fd, "w")) == NULL) { 395 saved_errno = errno; 396 (void)unlink(fn); 397 fatal("fdopen %s: %s", fn, strerror(saved_errno)); 398 } 399 400 if (rfp->rf_head != NULL) 401 rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf)); 402 else 403 numbuf[0] = '\0'; 404 405 fprintf(fp, "head\t%s;\n", numbuf); 406 407 if (rfp->rf_branch != NULL) { 408 rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf)); 409 fprintf(fp, "branch\t%s;\n", numbuf); 410 } 411 412 fputs("access", fp); 413 TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) { 414 fprintf(fp, "\n\t%s", ap->ra_name); 415 } 416 fputs(";\n", fp); 417 418 fprintf(fp, "symbols"); 419 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 420 rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); 421 if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) || 422 strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) || 423 strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf)) 424 fatal("rcs_write: string overflow"); 425 fprintf(fp, "\n\t%s", buf); 426 } 427 fprintf(fp, ";\n"); 428 429 fprintf(fp, "locks"); 430 TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) { 431 rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf)); 432 fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf); 433 } 434 435 fprintf(fp, ";"); 436 437 if (rfp->rf_flags & RCS_SLOCK) 438 fprintf(fp, " strict;"); 439 fputc('\n', fp); 440 441 fputs("comment\t@", fp); 442 if (rfp->rf_comment != NULL) { 443 rcs_strprint((const u_char *)rfp->rf_comment, 444 strlen(rfp->rf_comment), fp); 445 fputs("@;\n", fp); 446 } else 447 fputs("# @;\n", fp); 448 449 if (rfp->rf_expand != NULL) { 450 fputs("expand @", fp); 451 rcs_strprint((const u_char *)rfp->rf_expand, 452 strlen(rfp->rf_expand), fp); 453 fputs("@;\n", fp); 454 } 455 456 fputs("\n\n", fp); 457 458 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 459 fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 460 sizeof(numbuf))); 461 fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;", 462 rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1, 463 rdp->rd_date.tm_mday, rdp->rd_date.tm_hour, 464 rdp->rd_date.tm_min, rdp->rd_date.tm_sec); 465 fprintf(fp, "\tauthor %s;\tstate %s;\n", 466 rdp->rd_author, rdp->rd_state); 467 fputs("branches", fp); 468 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 469 fprintf(fp, " %s", rcsnum_tostr(brp->rb_num, numbuf, 470 sizeof(numbuf))); 471 } 472 fputs(";\n", fp); 473 fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next, 474 numbuf, sizeof(numbuf))); 475 } 476 477 fputs("\ndesc\n@", fp); 478 if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) { 479 rcs_strprint((const u_char *)rfp->rf_desc, len, fp); 480 if (rfp->rf_desc[len-1] != '\n') 481 fputc('\n', fp); 482 } 483 fputs("@\n", fp); 484 485 /* deltatexts */ 486 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 487 fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 488 sizeof(numbuf))); 489 fputs("log\n@", fp); 490 if (rdp->rd_log != NULL) { 491 len = strlen(rdp->rd_log); 492 rcs_strprint((const u_char *)rdp->rd_log, len, fp); 493 if (rdp->rd_log[len-1] != '\n') 494 fputc('\n', fp); 495 } 496 fputs("@\ntext\n@", fp); 497 if (rdp->rd_text != NULL) { 498 rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp); 499 } 500 fputs("@\n", fp); 501 } 502 if (fchmod(fd, rfp->rf_mode) == -1) { 503 saved_errno = errno; 504 (void)unlink(fn); 505 fatal("fchmod %s: %s", fn, strerror(saved_errno)); 506 } 507 508 (void)fclose(fp); 509 510 if (rename(fn, rfp->rf_path) == -1) { 511 saved_errno = errno; 512 (void)unlink(fn); 513 fatal("rename(%s, %s): %s", fn, rfp->rf_path, 514 strerror(saved_errno)); 515 } 516 517 rfp->rf_flags |= RCS_SYNCED; 518 519 if (fn != NULL) 520 xfree(fn); 521 } 522 523 /* 524 * rcs_head_get() 525 * 526 * Retrieve the revision number of the head revision for the RCS file <file>. 527 */ 528 RCSNUM * 529 rcs_head_get(RCSFILE *file) 530 { 531 char br[16]; 532 RCSNUM *rev; 533 534 if (file->rf_branch != NULL) { 535 rcsnum_tostr(file->rf_branch, br, sizeof(br)); 536 rev = rcs_translate_tag(br, file); 537 } else { 538 rev = rcsnum_alloc(); 539 rcsnum_cpy(file->rf_head, rev, 0); 540 } 541 542 return (rev); 543 } 544 545 /* 546 * rcs_head_set() 547 * 548 * Set the revision number of the head revision for the RCS file <file> to 549 * <rev>, which must reference a valid revision within the file. 550 */ 551 int 552 rcs_head_set(RCSFILE *file, RCSNUM *rev) 553 { 554 if (rcs_findrev(file, rev) == NULL) 555 return (-1); 556 557 if (file->rf_head == NULL) 558 file->rf_head = rcsnum_alloc(); 559 560 rcsnum_cpy(rev, file->rf_head, 0); 561 file->rf_flags &= ~RCS_SYNCED; 562 return (0); 563 } 564 565 566 /* 567 * rcs_branch_get() 568 * 569 * Retrieve the default branch number for the RCS file <file>. 570 * Returns the number on success. If NULL is returned, then there is no 571 * default branch for this file. 572 */ 573 const RCSNUM * 574 rcs_branch_get(RCSFILE *file) 575 { 576 return (file->rf_branch); 577 } 578 579 /* 580 * rcs_branch_set() 581 * 582 * Set the default branch for the RCS file <file> to <bnum>. 583 * Returns 0 on success, -1 on failure. 584 */ 585 int 586 rcs_branch_set(RCSFILE *file, const RCSNUM *bnum) 587 { 588 if (file->rf_branch == NULL) 589 file->rf_branch = rcsnum_alloc(); 590 591 rcsnum_cpy(bnum, file->rf_branch, 0); 592 file->rf_flags &= ~RCS_SYNCED; 593 return (0); 594 } 595 596 /* 597 * rcs_access_add() 598 * 599 * Add the login name <login> to the access list for the RCS file <file>. 600 * Returns 0 on success, or -1 on failure. 601 */ 602 int 603 rcs_access_add(RCSFILE *file, const char *login) 604 { 605 struct rcs_access *ap; 606 607 /* first look for duplication */ 608 TAILQ_FOREACH(ap, &(file->rf_access), ra_list) { 609 if (strcmp(ap->ra_name, login) == 0) { 610 rcs_errno = RCS_ERR_DUPENT; 611 return (-1); 612 } 613 } 614 615 ap = xmalloc(sizeof(*ap)); 616 ap->ra_name = xstrdup(login); 617 TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list); 618 619 /* not synced anymore */ 620 file->rf_flags &= ~RCS_SYNCED; 621 return (0); 622 } 623 624 /* 625 * rcs_access_remove() 626 * 627 * Remove an entry with login name <login> from the access list of the RCS 628 * file <file>. 629 * Returns 0 on success, or -1 on failure. 630 */ 631 int 632 rcs_access_remove(RCSFILE *file, const char *login) 633 { 634 struct rcs_access *ap; 635 636 TAILQ_FOREACH(ap, &(file->rf_access), ra_list) 637 if (strcmp(ap->ra_name, login) == 0) 638 break; 639 640 if (ap == NULL) { 641 rcs_errno = RCS_ERR_NOENT; 642 return (-1); 643 } 644 645 TAILQ_REMOVE(&(file->rf_access), ap, ra_list); 646 xfree(ap->ra_name); 647 xfree(ap); 648 649 /* not synced anymore */ 650 file->rf_flags &= ~RCS_SYNCED; 651 return (0); 652 } 653 654 /* 655 * rcs_sym_add() 656 * 657 * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol 658 * is named <sym> and is bound to the RCS revision <snum>. 659 * Returns 0 on success, or -1 on failure. 660 */ 661 int 662 rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum) 663 { 664 struct rcs_sym *symp; 665 666 if (!rcs_sym_check(sym)) { 667 rcs_errno = RCS_ERR_BADSYM; 668 return (-1); 669 } 670 671 /* first look for duplication */ 672 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 673 if (strcmp(symp->rs_name, sym) == 0) { 674 rcs_errno = RCS_ERR_DUPENT; 675 return (-1); 676 } 677 } 678 679 symp = xmalloc(sizeof(*symp)); 680 symp->rs_name = xstrdup(sym); 681 symp->rs_num = rcsnum_alloc(); 682 rcsnum_cpy(snum, symp->rs_num, 0); 683 684 TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); 685 686 /* not synced anymore */ 687 rfp->rf_flags &= ~RCS_SYNCED; 688 return (0); 689 } 690 691 /* 692 * rcs_sym_remove() 693 * 694 * Remove the symbol with name <sym> from the symbol list for the RCS file 695 * <file>. If no such symbol is found, the call fails and returns with an 696 * error. 697 * Returns 0 on success, or -1 on failure. 698 */ 699 int 700 rcs_sym_remove(RCSFILE *file, const char *sym) 701 { 702 struct rcs_sym *symp; 703 704 if (!rcs_sym_check(sym)) { 705 rcs_errno = RCS_ERR_BADSYM; 706 return (-1); 707 } 708 709 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 710 if (strcmp(symp->rs_name, sym) == 0) 711 break; 712 713 if (symp == NULL) { 714 rcs_errno = RCS_ERR_NOENT; 715 return (-1); 716 } 717 718 TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list); 719 xfree(symp->rs_name); 720 rcsnum_free(symp->rs_num); 721 xfree(symp); 722 723 /* not synced anymore */ 724 file->rf_flags &= ~RCS_SYNCED; 725 return (0); 726 } 727 728 /* 729 * rcs_sym_get() 730 * 731 * Find a specific symbol <sym> entry in the tree of the RCS file <file>. 732 * 733 * Returns a pointer to the symbol on success, or NULL on failure. 734 */ 735 struct rcs_sym * 736 rcs_sym_get(RCSFILE *file, const char *sym) 737 { 738 struct rcs_sym *symp; 739 740 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 741 if (strcmp(symp->rs_name, sym) == 0) 742 return (symp); 743 744 return (NULL); 745 } 746 747 /* 748 * rcs_sym_getrev() 749 * 750 * Retrieve the RCS revision number associated with the symbol <sym> for the 751 * RCS file <file>. The returned value is a dynamically-allocated copy and 752 * should be freed by the caller once they are done with it. 753 * Returns the RCSNUM on success, or NULL on failure. 754 */ 755 RCSNUM * 756 rcs_sym_getrev(RCSFILE *file, const char *sym) 757 { 758 RCSNUM *num; 759 struct rcs_sym *symp; 760 761 if (!rcs_sym_check(sym)) { 762 rcs_errno = RCS_ERR_BADSYM; 763 return (NULL); 764 } 765 766 if (!strcmp(sym, RCS_HEAD_BRANCH)) { 767 num = rcsnum_alloc(); 768 rcsnum_cpy(file->rf_head, num, 0); 769 return (num); 770 } 771 772 num = NULL; 773 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 774 if (strcmp(symp->rs_name, sym) == 0) 775 break; 776 777 if (symp == NULL) { 778 rcs_errno = RCS_ERR_NOENT; 779 } else { 780 num = rcsnum_alloc(); 781 rcsnum_cpy(symp->rs_num, num, 0); 782 } 783 784 return (num); 785 } 786 787 /* 788 * rcs_sym_check() 789 * 790 * Check the RCS symbol name <sym> for any unsupported characters. 791 * Returns 1 if the tag is correct, 0 if it isn't valid. 792 */ 793 int 794 rcs_sym_check(const char *sym) 795 { 796 int ret; 797 const char *cp; 798 799 ret = 1; 800 cp = sym; 801 if (!isalpha(*cp++)) 802 return (0); 803 804 for (; *cp != '\0'; cp++) 805 if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) { 806 ret = 0; 807 break; 808 } 809 810 return (ret); 811 } 812 813 /* 814 * rcs_lock_getmode() 815 * 816 * Retrieve the locking mode of the RCS file <file>. 817 */ 818 int 819 rcs_lock_getmode(RCSFILE *file) 820 { 821 return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE; 822 } 823 824 /* 825 * rcs_lock_setmode() 826 * 827 * Set the locking mode of the RCS file <file> to <mode>, which must either 828 * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT. 829 * Returns the previous mode on success, or -1 on failure. 830 */ 831 int 832 rcs_lock_setmode(RCSFILE *file, int mode) 833 { 834 int pmode; 835 pmode = rcs_lock_getmode(file); 836 837 if (mode == RCS_LOCK_STRICT) 838 file->rf_flags |= RCS_SLOCK; 839 else if (mode == RCS_LOCK_LOOSE) 840 file->rf_flags &= ~RCS_SLOCK; 841 else 842 fatal("rcs_lock_setmode: invalid mode `%d'", mode); 843 844 file->rf_flags &= ~RCS_SYNCED; 845 return (pmode); 846 } 847 848 /* 849 * rcs_lock_add() 850 * 851 * Add an RCS lock for the user <user> on revision <rev>. 852 * Returns 0 on success, or -1 on failure. 853 */ 854 int 855 rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev) 856 { 857 struct rcs_lock *lkp; 858 859 /* first look for duplication */ 860 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { 861 if (strcmp(lkp->rl_name, user) == 0 && 862 rcsnum_cmp(rev, lkp->rl_num, 0) == 0) { 863 rcs_errno = RCS_ERR_DUPENT; 864 return (-1); 865 } 866 } 867 868 lkp = xmalloc(sizeof(*lkp)); 869 lkp->rl_name = xstrdup(user); 870 lkp->rl_num = rcsnum_alloc(); 871 rcsnum_cpy(rev, lkp->rl_num, 0); 872 873 TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list); 874 875 /* not synced anymore */ 876 file->rf_flags &= ~RCS_SYNCED; 877 return (0); 878 } 879 880 881 /* 882 * rcs_lock_remove() 883 * 884 * Remove the RCS lock on revision <rev>. 885 * Returns 0 on success, or -1 on failure. 886 */ 887 int 888 rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev) 889 { 890 struct rcs_lock *lkp; 891 892 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { 893 if (strcmp(lkp->rl_name, user) == 0 && 894 rcsnum_cmp(lkp->rl_num, rev, 0) == 0) 895 break; 896 } 897 898 if (lkp == NULL) { 899 rcs_errno = RCS_ERR_NOENT; 900 return (-1); 901 } 902 903 TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list); 904 rcsnum_free(lkp->rl_num); 905 xfree(lkp->rl_name); 906 xfree(lkp); 907 908 /* not synced anymore */ 909 file->rf_flags &= ~RCS_SYNCED; 910 return (0); 911 } 912 913 /* 914 * rcs_desc_get() 915 * 916 * Retrieve the description for the RCS file <file>. 917 */ 918 const char * 919 rcs_desc_get(RCSFILE *file) 920 { 921 return (file->rf_desc); 922 } 923 924 /* 925 * rcs_desc_set() 926 * 927 * Set the description for the RCS file <file>. 928 */ 929 void 930 rcs_desc_set(RCSFILE *file, const char *desc) 931 { 932 char *tmp; 933 934 tmp = xstrdup(desc); 935 if (file->rf_desc != NULL) 936 xfree(file->rf_desc); 937 file->rf_desc = tmp; 938 file->rf_flags &= ~RCS_SYNCED; 939 } 940 941 /* 942 * rcs_comment_lookup() 943 * 944 * Lookup the assumed comment leader based on a file's suffix. 945 * Returns a pointer to the string on success, or NULL on failure. 946 */ 947 const char * 948 rcs_comment_lookup(const char *filename) 949 { 950 int i; 951 const char *sp; 952 953 if ((sp = strrchr(filename, '.')) == NULL) { 954 rcs_errno = RCS_ERR_NOENT; 955 return (NULL); 956 } 957 sp++; 958 959 for (i = 0; i < (int)NB_COMTYPES; i++) 960 if (strcmp(rcs_comments[i].rc_suffix, sp) == 0) 961 return (rcs_comments[i].rc_cstr); 962 return (NULL); 963 } 964 965 /* 966 * rcs_comment_get() 967 * 968 * Retrieve the comment leader for the RCS file <file>. 969 */ 970 const char * 971 rcs_comment_get(RCSFILE *file) 972 { 973 return (file->rf_comment); 974 } 975 976 /* 977 * rcs_comment_set() 978 * 979 * Set the comment leader for the RCS file <file>. 980 */ 981 void 982 rcs_comment_set(RCSFILE *file, const char *comment) 983 { 984 char *tmp; 985 986 tmp = xstrdup(comment); 987 if (file->rf_comment != NULL) 988 xfree(file->rf_comment); 989 file->rf_comment = tmp; 990 file->rf_flags &= ~RCS_SYNCED; 991 } 992 993 /* 994 * rcs_tag_resolve() 995 * 996 * Retrieve the revision number corresponding to the tag <tag> for the RCS 997 * file <file>. 998 */ 999 RCSNUM * 1000 rcs_tag_resolve(RCSFILE *file, const char *tag) 1001 { 1002 RCSNUM *num; 1003 1004 if ((num = rcsnum_parse(tag)) == NULL) { 1005 num = rcs_sym_getrev(file, tag); 1006 } 1007 1008 return (num); 1009 } 1010 1011 int 1012 rcs_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines) 1013 { 1014 u_char op; 1015 char *ep; 1016 struct cvs_line *lp, *dlp, *ndlp; 1017 int i, lineno, nbln; 1018 u_char tmp; 1019 1020 dlp = TAILQ_FIRST(&(dlines->l_lines)); 1021 lp = TAILQ_FIRST(&(plines->l_lines)); 1022 1023 /* skip first bogus line */ 1024 for (lp = TAILQ_NEXT(lp, l_list); lp != NULL; 1025 lp = TAILQ_NEXT(lp, l_list)) { 1026 if (lp->l_len < 2) 1027 fatal("line too short, RCS patch seems broken"); 1028 op = *(lp->l_line); 1029 /* NUL-terminate line buffer for strtol() safety. */ 1030 tmp = lp->l_line[lp->l_len - 1]; 1031 lp->l_line[lp->l_len - 1] = '\0'; 1032 lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10); 1033 if (lineno - 1 > dlines->l_nblines || lineno < 0) { 1034 fatal("invalid line specification in RCS patch"); 1035 } 1036 ep++; 1037 nbln = (int)strtol(ep, &ep, 10); 1038 /* Restore the last byte of the buffer */ 1039 lp->l_line[lp->l_len - 1] = tmp; 1040 if (nbln < 0) 1041 fatal("invalid line number specification in RCS patch"); 1042 1043 /* find the appropriate line */ 1044 for (;;) { 1045 if (dlp == NULL) 1046 break; 1047 if (dlp->l_lineno == lineno) 1048 break; 1049 if (dlp->l_lineno > lineno) { 1050 dlp = TAILQ_PREV(dlp, cvs_tqh, l_list); 1051 } else if (dlp->l_lineno < lineno) { 1052 if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) || 1053 ndlp->l_lineno > lineno) 1054 break; 1055 dlp = ndlp; 1056 } 1057 } 1058 if (dlp == NULL) 1059 fatal("can't find referenced line in RCS patch"); 1060 1061 if (op == 'd') { 1062 for (i = 0; (i < nbln) && (dlp != NULL); i++) { 1063 ndlp = TAILQ_NEXT(dlp, l_list); 1064 TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list); 1065 xfree(dlp); 1066 dlp = ndlp; 1067 /* last line is gone - reset dlp */ 1068 if (dlp == NULL) { 1069 ndlp = TAILQ_LAST(&(dlines->l_lines), 1070 cvs_tqh); 1071 dlp = ndlp; 1072 } 1073 } 1074 } else if (op == 'a') { 1075 for (i = 0; i < nbln; i++) { 1076 ndlp = lp; 1077 lp = TAILQ_NEXT(lp, l_list); 1078 if (lp == NULL) 1079 fatal("truncated RCS patch"); 1080 TAILQ_REMOVE(&(plines->l_lines), lp, l_list); 1081 TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp, 1082 lp, l_list); 1083 dlp = lp; 1084 1085 /* we don't want lookup to block on those */ 1086 lp->l_lineno = lineno; 1087 1088 lp = ndlp; 1089 } 1090 } else 1091 fatal("unknown RCS patch operation `%c'", op); 1092 1093 /* last line of the patch, done */ 1094 if (lp->l_lineno == plines->l_nblines) 1095 break; 1096 } 1097 1098 /* once we're done patching, rebuild the line numbers */ 1099 lineno = 0; 1100 TAILQ_FOREACH(lp, &(dlines->l_lines), l_list) 1101 lp->l_lineno = lineno++; 1102 dlines->l_nblines = lineno - 1; 1103 1104 return (0); 1105 } 1106 1107 /* 1108 * rcs_rev_add() 1109 * 1110 * Add a revision to the RCS file <rf>. The new revision's number can be 1111 * specified in <rev> (which can also be RCS_HEAD_REV, in which case the 1112 * new revision will have a number equal to the previous head revision plus 1113 * one). The <msg> argument specifies the log message for that revision, and 1114 * <date> specifies the revision's date (a value of -1 is 1115 * equivalent to using the current time). 1116 * If <username> is NULL, set the author for this revision to the current user. 1117 * Otherwise, set it to <username>. 1118 * Returns 0 on success, or -1 on failure. 1119 */ 1120 int 1121 rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date, 1122 const char *username) 1123 { 1124 time_t now; 1125 struct passwd *pw; 1126 struct rcs_branch *brp; 1127 struct rcs_delta *ordp, *rdp; 1128 1129 if (rev == RCS_HEAD_REV) { 1130 if (rf->rf_flags & RCS_CREATE) { 1131 if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL) 1132 return (-1); 1133 rf->rf_head = rcsnum_alloc(); 1134 rcsnum_cpy(rev, rf->rf_head, 0); 1135 } else { 1136 rev = rcsnum_inc(rf->rf_head); 1137 } 1138 } else { 1139 if ((rdp = rcs_findrev(rf, rev)) != NULL) { 1140 rcs_errno = RCS_ERR_DUPENT; 1141 return (-1); 1142 } 1143 } 1144 1145 if ((pw = getpwuid(getuid())) == NULL) 1146 fatal("getpwuid failed"); 1147 1148 rdp = xcalloc(1, sizeof(*rdp)); 1149 1150 TAILQ_INIT(&(rdp->rd_branches)); 1151 1152 rdp->rd_num = rcsnum_alloc(); 1153 rcsnum_cpy(rev, rdp->rd_num, 0); 1154 1155 rdp->rd_next = rcsnum_alloc(); 1156 1157 if (username == NULL) 1158 username = pw->pw_name; 1159 1160 rdp->rd_author = xstrdup(username); 1161 rdp->rd_state = xstrdup(RCS_STATE_EXP); 1162 rdp->rd_log = xstrdup(msg); 1163 1164 if (date != (time_t)(-1)) 1165 now = date; 1166 else 1167 time(&now); 1168 gmtime_r(&now, &(rdp->rd_date)); 1169 1170 if (RCSNUM_ISBRANCHREV(rev)) 1171 TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list); 1172 else 1173 TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list); 1174 rf->rf_ndelta++; 1175 1176 if (!(rf->rf_flags & RCS_CREATE)) { 1177 if (RCSNUM_ISBRANCHREV(rev)) { 1178 brp = xmalloc(sizeof(*brp)); 1179 brp->rb_num = rcsnum_alloc(); 1180 rcsnum_cpy(rdp->rd_num, brp->rb_num, 0); 1181 TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); 1182 ordp = TAILQ_PREV(rdp, rcs_dlist, rd_list); 1183 rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0); 1184 } else { 1185 ordp = TAILQ_NEXT(rdp, rd_list); 1186 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0); 1187 } 1188 } 1189 1190 /* not synced anymore */ 1191 rf->rf_flags &= ~RCS_SYNCED; 1192 1193 return (0); 1194 } 1195 1196 /* 1197 * rcs_rev_remove() 1198 * 1199 * Remove the revision whose number is <rev> from the RCS file <rf>. 1200 */ 1201 int 1202 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) 1203 { 1204 char *path_tmp1, *path_tmp2; 1205 struct rcs_delta *rdp, *prevrdp, *nextrdp; 1206 BUF *prevbuf, *newdiff, *newdeltatext; 1207 1208 if (rev == RCS_HEAD_REV) 1209 rev = rf->rf_head; 1210 1211 /* do we actually have that revision? */ 1212 if ((rdp = rcs_findrev(rf, rev)) == NULL) { 1213 rcs_errno = RCS_ERR_NOENT; 1214 return (-1); 1215 } 1216 1217 /* 1218 * This is confusing, the previous delta is next in the TAILQ list. 1219 * the next delta is the previous one in the TAILQ list. 1220 * 1221 * When the HEAD revision got specified, nextrdp will be NULL. 1222 * When the first revision got specified, prevrdp will be NULL. 1223 */ 1224 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list); 1225 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list); 1226 1227 newdeltatext = NULL; 1228 prevbuf = NULL; 1229 1230 if (prevrdp != NULL && nextrdp != NULL) { 1231 newdiff = cvs_buf_alloc(64, BUF_AUTOEXT); 1232 1233 /* calculate new diff */ 1234 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 1235 rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0); 1236 1237 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 1238 rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0); 1239 1240 diff_format = D_RCSDIFF; 1241 if (cvs_diffreg(path_tmp1, path_tmp2, newdiff) == D_ERROR) 1242 fatal("rcs_diffreg failed"); 1243 1244 newdeltatext = newdiff; 1245 } else if (nextrdp == NULL && prevrdp != NULL) { 1246 newdeltatext = prevbuf; 1247 } 1248 1249 if (newdeltatext != NULL) { 1250 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0) 1251 fatal("error setting new deltatext"); 1252 } 1253 1254 TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list); 1255 1256 /* update pointers */ 1257 if (prevrdp != NULL && nextrdp != NULL) { 1258 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0); 1259 } else if (prevrdp != NULL) { 1260 if (rcs_head_set(rf, prevrdp->rd_num) < 0) 1261 fatal("rcs_head_set failed"); 1262 } else if (nextrdp != NULL) { 1263 rcsnum_free(nextrdp->rd_next); 1264 nextrdp->rd_next = rcsnum_alloc(); 1265 } else { 1266 rcsnum_free(rf->rf_head); 1267 rf->rf_head = NULL; 1268 } 1269 1270 rf->rf_ndelta--; 1271 rf->rf_flags &= ~RCS_SYNCED; 1272 1273 rcs_freedelta(rdp); 1274 1275 if (newdeltatext != NULL) 1276 xfree(newdeltatext); 1277 1278 if (path_tmp1 != NULL) 1279 xfree(path_tmp1); 1280 if (path_tmp2 != NULL) 1281 xfree(path_tmp2); 1282 1283 return (0); 1284 } 1285 1286 /* 1287 * rcs_findrev() 1288 * 1289 * Find a specific revision's delta entry in the tree of the RCS file <rfp>. 1290 * The revision number is given in <rev>. 1291 * 1292 * Returns a pointer to the delta on success, or NULL on failure. 1293 */ 1294 struct rcs_delta * 1295 rcs_findrev(RCSFILE *rfp, RCSNUM *rev) 1296 { 1297 int isbrev; 1298 struct rcs_delta *rdp; 1299 1300 isbrev = RCSNUM_ISBRANCHREV(rev); 1301 1302 /* 1303 * We need to do more parsing if the last revision in the linked list 1304 * is greater than the requested revision. 1305 */ 1306 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1307 if (rdp == NULL || 1308 (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) || 1309 ((isbrev && rdp->rd_num->rn_len < 4) || 1310 (isbrev && rcsnum_differ(rev, rdp->rd_num)))) { 1311 rcs_parse_deltas(rfp, rev); 1312 } 1313 1314 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1315 if (rcsnum_differ(rdp->rd_num, rev)) 1316 continue; 1317 else 1318 return (rdp); 1319 } 1320 1321 return (NULL); 1322 } 1323 1324 /* 1325 * rcs_kwexp_set() 1326 * 1327 * Set the keyword expansion mode to use on the RCS file <file> to <mode>. 1328 */ 1329 void 1330 rcs_kwexp_set(RCSFILE *file, int mode) 1331 { 1332 int i; 1333 char *tmp, buf[8] = ""; 1334 1335 if (RCS_KWEXP_INVAL(mode)) 1336 return; 1337 1338 i = 0; 1339 if (mode == RCS_KWEXP_NONE) 1340 buf[0] = 'b'; 1341 else if (mode == RCS_KWEXP_OLD) 1342 buf[0] = 'o'; 1343 else { 1344 if (mode & RCS_KWEXP_NAME) 1345 buf[i++] = 'k'; 1346 if (mode & RCS_KWEXP_VAL) 1347 buf[i++] = 'v'; 1348 if (mode & RCS_KWEXP_LKR) 1349 buf[i++] = 'l'; 1350 } 1351 1352 tmp = xstrdup(buf); 1353 if (file->rf_expand != NULL) 1354 xfree(file->rf_expand); 1355 file->rf_expand = tmp; 1356 /* not synced anymore */ 1357 file->rf_flags &= ~RCS_SYNCED; 1358 } 1359 1360 /* 1361 * rcs_kwexp_get() 1362 * 1363 * Retrieve the keyword expansion mode to be used for the RCS file <file>. 1364 */ 1365 int 1366 rcs_kwexp_get(RCSFILE *file) 1367 { 1368 return rcs_kflag_get(file->rf_expand); 1369 } 1370 1371 /* 1372 * rcs_kflag_get() 1373 * 1374 * Get the keyword expansion mode from a set of character flags given in 1375 * <flags> and return the appropriate flag mask. In case of an error, the 1376 * returned mask will have the RCS_KWEXP_ERR bit set to 1. 1377 */ 1378 int 1379 rcs_kflag_get(const char *flags) 1380 { 1381 int fl; 1382 size_t len; 1383 const char *fp; 1384 1385 fl = 0; 1386 len = strlen(flags); 1387 1388 for (fp = flags; *fp != '\0'; fp++) { 1389 if (*fp == 'k') 1390 fl |= RCS_KWEXP_NAME; 1391 else if (*fp == 'v') 1392 fl |= RCS_KWEXP_VAL; 1393 else if (*fp == 'l') 1394 fl |= RCS_KWEXP_LKR; 1395 else if (*fp == 'o') { 1396 if (len != 1) 1397 fl |= RCS_KWEXP_ERR; 1398 fl |= RCS_KWEXP_OLD; 1399 } else if (*fp == 'b') { 1400 if (len != 1) 1401 fl |= RCS_KWEXP_ERR; 1402 fl |= RCS_KWEXP_NONE; 1403 } else /* unknown letter */ 1404 fl |= RCS_KWEXP_ERR; 1405 } 1406 1407 return (fl); 1408 } 1409 1410 /* 1411 * rcs_errstr() 1412 * 1413 * Get the error string matching the RCS error code <code>. 1414 */ 1415 const char * 1416 rcs_errstr(int code) 1417 { 1418 const char *esp; 1419 1420 if (code < 0 || (code >= (int)RCS_NERR && code != RCS_ERR_ERRNO)) 1421 esp = NULL; 1422 else if (code == RCS_ERR_ERRNO) 1423 esp = strerror(errno); 1424 else 1425 esp = rcs_errstrs[code]; 1426 return (esp); 1427 } 1428 1429 /* rcs_parse_deltas() 1430 * 1431 * Parse deltas. If <rev> is not NULL, parse only as far as that 1432 * revision. If <rev> is NULL, parse all deltas. 1433 */ 1434 static void 1435 rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev) 1436 { 1437 int ret; 1438 struct rcs_delta *enddelta; 1439 1440 if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE)) 1441 return; 1442 1443 for (;;) { 1444 ret = rcs_parse_delta(rfp); 1445 if (rev != NULL) { 1446 enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1447 if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0) 1448 break; 1449 } 1450 1451 if (ret == 0) { 1452 rfp->rf_flags |= PARSED_DELTAS; 1453 break; 1454 } 1455 else if (ret == -1) 1456 fatal("error parsing deltas"); 1457 } 1458 } 1459 1460 /* rcs_parse_deltatexts() 1461 * 1462 * Parse deltatexts. If <rev> is not NULL, parse only as far as that 1463 * revision. If <rev> is NULL, parse everything. 1464 */ 1465 static void 1466 rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev) 1467 { 1468 int ret; 1469 struct rcs_delta *rdp; 1470 1471 if ((rfp->rf_flags & PARSED_DELTATEXTS) || 1472 (rfp->rf_flags & RCS_CREATE)) 1473 return; 1474 1475 if (!(rfp->rf_flags & PARSED_DESC)) 1476 rcs_parse_desc(rfp, rev); 1477 for (;;) { 1478 if (rev != NULL) { 1479 rdp = rcs_findrev(rfp, rev); 1480 if (rdp->rd_text != NULL) 1481 break; 1482 else 1483 ret = rcs_parse_deltatext(rfp); 1484 } else 1485 ret = rcs_parse_deltatext(rfp); 1486 if (ret == 0) { 1487 rfp->rf_flags |= PARSED_DELTATEXTS; 1488 break; 1489 } 1490 else if (ret == -1) 1491 fatal("problem parsing deltatexts"); 1492 } 1493 } 1494 1495 /* rcs_parse_desc() 1496 * 1497 * Parse RCS description. 1498 */ 1499 static void 1500 rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev) 1501 { 1502 int ret = 0; 1503 1504 if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE)) 1505 return; 1506 if (!(rfp->rf_flags & PARSED_DELTAS)) 1507 rcs_parse_deltas(rfp, rev); 1508 /* do parsing */ 1509 ret = rcs_gettok(rfp); 1510 if (ret != RCS_TOK_DESC) 1511 fatal("token `%s' found where RCS desc expected", 1512 RCS_TOKSTR(rfp)); 1513 1514 ret = rcs_gettok(rfp); 1515 if (ret != RCS_TOK_STRING) 1516 fatal("token `%s' found where RCS desc expected", 1517 RCS_TOKSTR(rfp)); 1518 1519 rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp)); 1520 rfp->rf_flags |= PARSED_DESC; 1521 } 1522 1523 /* 1524 * rcs_parse_init() 1525 * 1526 * Initial parsing of file <path>, which are in the RCS format. 1527 * Just does admin section. 1528 */ 1529 static void 1530 rcs_parse_init(RCSFILE *rfp) 1531 { 1532 struct rcs_pdata *pdp; 1533 1534 if (rfp->rf_flags & RCS_PARSED) 1535 return; 1536 1537 pdp = xcalloc(1, sizeof(*pdp)); 1538 1539 pdp->rp_lines = 0; 1540 pdp->rp_pttype = RCS_TOK_ERR; 1541 1542 if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL) 1543 fatal("fopen: `%s'", rfp->rf_path); 1544 1545 pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); 1546 pdp->rp_blen = RCS_BUFSIZE; 1547 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 1548 1549 /* ditch the strict lock */ 1550 rfp->rf_flags &= ~RCS_SLOCK; 1551 rfp->rf_pdata = pdp; 1552 1553 if (rcs_parse_admin(rfp) < 0) { 1554 rcs_freepdata(pdp); 1555 fatal("could not parse admin data"); 1556 } 1557 1558 if (rfp->rf_flags & RCS_PARSE_FULLY) 1559 rcs_parse_deltatexts(rfp, NULL); 1560 1561 rfp->rf_flags |= RCS_SYNCED; 1562 } 1563 1564 /* 1565 * rcs_parse_admin() 1566 * 1567 * Parse the administrative portion of an RCS file. 1568 * Returns the type of the first token found after the admin section on 1569 * success, or -1 on failure. 1570 */ 1571 static int 1572 rcs_parse_admin(RCSFILE *rfp) 1573 { 1574 u_int i; 1575 int tok, ntok, hmask; 1576 struct rcs_key *rk; 1577 1578 rfp->rf_head = NULL; 1579 rfp->rf_branch = NULL; 1580 1581 /* hmask is a mask of the headers already encountered */ 1582 hmask = 0; 1583 for (;;) { 1584 tok = rcs_gettok(rfp); 1585 if (tok == RCS_TOK_ERR) { 1586 rcs_errno = RCS_ERR_PARSE; 1587 cvs_log(LP_ERR, "parse error in RCS admin section"); 1588 goto fail; 1589 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1590 /* 1591 * Assume this is the start of the first delta or 1592 * that we are dealing with an empty RCS file and 1593 * we just found the description. 1594 */ 1595 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1596 return (tok); 1597 } 1598 1599 rk = NULL; 1600 for (i = 0; i < RCS_NKEYS; i++) 1601 if (rcs_keys[i].rk_id == tok) 1602 rk = &(rcs_keys[i]); 1603 1604 if (hmask & (1 << tok)) { 1605 rcs_errno = RCS_ERR_PARSE; 1606 cvs_log(LP_ERR, "duplicate RCS key"); 1607 goto fail; 1608 } 1609 hmask |= (1 << tok); 1610 1611 switch (tok) { 1612 case RCS_TOK_HEAD: 1613 case RCS_TOK_BRANCH: 1614 case RCS_TOK_COMMENT: 1615 case RCS_TOK_EXPAND: 1616 ntok = rcs_gettok(rfp); 1617 if (ntok == RCS_TOK_SCOLON) 1618 break; 1619 if (ntok != rk->rk_val) { 1620 rcs_errno = RCS_ERR_PARSE; 1621 cvs_log(LP_ERR, 1622 "invalid value type for RCS key `%s'", 1623 rk->rk_str); 1624 } 1625 1626 if (tok == RCS_TOK_HEAD) { 1627 if (rfp->rf_head == NULL) 1628 rfp->rf_head = rcsnum_alloc(); 1629 rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1630 rfp->rf_head); 1631 } else if (tok == RCS_TOK_BRANCH) { 1632 if (rfp->rf_branch == NULL) 1633 rfp->rf_branch = rcsnum_alloc(); 1634 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1635 rfp->rf_branch) < 0) 1636 goto fail; 1637 } else if (tok == RCS_TOK_COMMENT) { 1638 rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp)); 1639 } else if (tok == RCS_TOK_EXPAND) { 1640 rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp)); 1641 } 1642 1643 /* now get the expected semi-colon */ 1644 ntok = rcs_gettok(rfp); 1645 if (ntok != RCS_TOK_SCOLON) { 1646 rcs_errno = RCS_ERR_PARSE; 1647 cvs_log(LP_ERR, 1648 "missing semi-colon after RCS `%s' key", 1649 rk->rk_str); 1650 goto fail; 1651 } 1652 break; 1653 case RCS_TOK_ACCESS: 1654 if (rcs_parse_access(rfp) < 0) 1655 goto fail; 1656 break; 1657 case RCS_TOK_SYMBOLS: 1658 if (rcs_parse_symbols(rfp) < 0) 1659 goto fail; 1660 break; 1661 case RCS_TOK_LOCKS: 1662 if (rcs_parse_locks(rfp) < 0) 1663 goto fail; 1664 break; 1665 default: 1666 rcs_errno = RCS_ERR_PARSE; 1667 cvs_log(LP_ERR, 1668 "unexpected token `%s' in RCS admin section", 1669 RCS_TOKSTR(rfp)); 1670 goto fail; 1671 } 1672 } 1673 1674 fail: 1675 return (-1); 1676 } 1677 1678 /* 1679 * rcs_parse_delta() 1680 * 1681 * Parse an RCS delta section and allocate the structure to store that delta's 1682 * information in the <rfp> delta list. 1683 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1684 * -1 on error. 1685 */ 1686 static int 1687 rcs_parse_delta(RCSFILE *rfp) 1688 { 1689 int ret, tok, ntok, hmask; 1690 u_int i; 1691 char *tokstr; 1692 RCSNUM *datenum; 1693 struct rcs_delta *rdp; 1694 struct rcs_key *rk; 1695 1696 tok = rcs_gettok(rfp); 1697 if (tok == RCS_TOK_DESC) { 1698 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1699 return (0); 1700 } else if (tok != RCS_TOK_NUM) { 1701 rcs_errno = RCS_ERR_PARSE; 1702 cvs_log(LP_ERR, "unexpected token `%s' at start of delta", 1703 RCS_TOKSTR(rfp)); 1704 return (-1); 1705 } 1706 1707 rdp = xcalloc(1, sizeof(*rdp)); 1708 1709 rdp->rd_num = rcsnum_alloc(); 1710 rdp->rd_next = rcsnum_alloc(); 1711 1712 TAILQ_INIT(&(rdp->rd_branches)); 1713 1714 rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num); 1715 1716 hmask = 0; 1717 ret = 0; 1718 tokstr = NULL; 1719 1720 for (;;) { 1721 tok = rcs_gettok(rfp); 1722 if (tok == RCS_TOK_ERR) { 1723 rcs_errno = RCS_ERR_PARSE; 1724 cvs_log(LP_ERR, "parse error in RCS delta section"); 1725 rcs_freedelta(rdp); 1726 return (-1); 1727 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1728 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1729 ret = (tok == RCS_TOK_NUM ? 1 : 0); 1730 break; 1731 } 1732 1733 rk = NULL; 1734 for (i = 0; i < RCS_NKEYS; i++) 1735 if (rcs_keys[i].rk_id == tok) 1736 rk = &(rcs_keys[i]); 1737 1738 if (hmask & (1 << tok)) { 1739 rcs_errno = RCS_ERR_PARSE; 1740 cvs_log(LP_ERR, "duplicate RCS key"); 1741 rcs_freedelta(rdp); 1742 return (-1); 1743 } 1744 hmask |= (1 << tok); 1745 1746 switch (tok) { 1747 case RCS_TOK_DATE: 1748 case RCS_TOK_AUTHOR: 1749 case RCS_TOK_STATE: 1750 case RCS_TOK_NEXT: 1751 ntok = rcs_gettok(rfp); 1752 if (ntok == RCS_TOK_SCOLON) { 1753 if (rk->rk_flags & RCS_VOPT) 1754 break; 1755 else { 1756 rcs_errno = RCS_ERR_PARSE; 1757 cvs_log(LP_ERR, "missing mandatory " 1758 "value to RCS key `%s'", 1759 rk->rk_str); 1760 rcs_freedelta(rdp); 1761 return (-1); 1762 } 1763 } 1764 1765 if (ntok != rk->rk_val) { 1766 rcs_errno = RCS_ERR_PARSE; 1767 cvs_log(LP_ERR, 1768 "invalid value type for RCS key `%s'", 1769 rk->rk_str); 1770 rcs_freedelta(rdp); 1771 return (-1); 1772 } 1773 1774 if (tokstr != NULL) 1775 xfree(tokstr); 1776 tokstr = xstrdup(RCS_TOKSTR(rfp)); 1777 /* now get the expected semi-colon */ 1778 ntok = rcs_gettok(rfp); 1779 if (ntok != RCS_TOK_SCOLON) { 1780 rcs_errno = RCS_ERR_PARSE; 1781 cvs_log(LP_ERR, 1782 "missing semi-colon after RCS `%s' key", 1783 rk->rk_str); 1784 xfree(tokstr); 1785 rcs_freedelta(rdp); 1786 return (-1); 1787 } 1788 1789 if (tok == RCS_TOK_DATE) { 1790 if ((datenum = rcsnum_parse(tokstr)) == NULL) { 1791 xfree(tokstr); 1792 rcs_freedelta(rdp); 1793 return (-1); 1794 } 1795 if (datenum->rn_len != 6) { 1796 rcs_errno = RCS_ERR_PARSE; 1797 cvs_log(LP_ERR, 1798 "RCS date specification has %s " 1799 "fields", 1800 (datenum->rn_len > 6) ? "too many" : 1801 "missing"); 1802 xfree(tokstr); 1803 rcs_freedelta(rdp); 1804 rcsnum_free(datenum); 1805 return (-1); 1806 } 1807 rdp->rd_date.tm_year = datenum->rn_id[0]; 1808 if (rdp->rd_date.tm_year >= 1900) 1809 rdp->rd_date.tm_year -= 1900; 1810 rdp->rd_date.tm_mon = datenum->rn_id[1] - 1; 1811 rdp->rd_date.tm_mday = datenum->rn_id[2]; 1812 rdp->rd_date.tm_hour = datenum->rn_id[3]; 1813 rdp->rd_date.tm_min = datenum->rn_id[4]; 1814 rdp->rd_date.tm_sec = datenum->rn_id[5]; 1815 rcsnum_free(datenum); 1816 } else if (tok == RCS_TOK_AUTHOR) { 1817 rdp->rd_author = tokstr; 1818 tokstr = NULL; 1819 } else if (tok == RCS_TOK_STATE) { 1820 rdp->rd_state = tokstr; 1821 tokstr = NULL; 1822 } else if (tok == RCS_TOK_NEXT) { 1823 rcsnum_aton(tokstr, NULL, rdp->rd_next); 1824 } 1825 break; 1826 case RCS_TOK_BRANCHES: 1827 if (rcs_parse_branches(rfp, rdp) < 0) { 1828 rcs_freedelta(rdp); 1829 return (-1); 1830 } 1831 break; 1832 default: 1833 rcs_errno = RCS_ERR_PARSE; 1834 cvs_log(LP_ERR, "unexpected token `%s' in RCS delta", 1835 RCS_TOKSTR(rfp)); 1836 rcs_freedelta(rdp); 1837 return (-1); 1838 } 1839 } 1840 1841 if (tokstr != NULL) 1842 xfree(tokstr); 1843 1844 TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); 1845 rfp->rf_ndelta++; 1846 1847 return (ret); 1848 } 1849 1850 /* 1851 * rcs_parse_deltatext() 1852 * 1853 * Parse an RCS delta text section and fill in the log and text field of the 1854 * appropriate delta section. 1855 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1856 * -1 on error. 1857 */ 1858 static int 1859 rcs_parse_deltatext(RCSFILE *rfp) 1860 { 1861 int tok; 1862 RCSNUM *tnum; 1863 struct rcs_delta *rdp; 1864 1865 tok = rcs_gettok(rfp); 1866 if (tok == RCS_TOK_EOF) 1867 return (0); 1868 1869 if (tok != RCS_TOK_NUM) { 1870 rcs_errno = RCS_ERR_PARSE; 1871 cvs_log(LP_ERR, 1872 "unexpected token `%s' at start of RCS delta text", 1873 RCS_TOKSTR(rfp)); 1874 return (-1); 1875 } 1876 1877 tnum = rcsnum_alloc(); 1878 rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum); 1879 1880 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1881 if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0) 1882 break; 1883 } 1884 rcsnum_free(tnum); 1885 1886 if (rdp == NULL) { 1887 cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta", 1888 RCS_TOKSTR(rfp)); 1889 return (-1); 1890 } 1891 1892 tok = rcs_gettok(rfp); 1893 if (tok != RCS_TOK_LOG) { 1894 rcs_errno = RCS_ERR_PARSE; 1895 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 1896 RCS_TOKSTR(rfp)); 1897 return (-1); 1898 } 1899 1900 tok = rcs_gettok(rfp); 1901 if (tok != RCS_TOK_STRING) { 1902 rcs_errno = RCS_ERR_PARSE; 1903 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 1904 RCS_TOKSTR(rfp)); 1905 return (-1); 1906 } 1907 rdp->rd_log = xstrdup(RCS_TOKSTR(rfp)); 1908 tok = rcs_gettok(rfp); 1909 if (tok != RCS_TOK_TEXT) { 1910 rcs_errno = RCS_ERR_PARSE; 1911 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 1912 RCS_TOKSTR(rfp)); 1913 return (-1); 1914 } 1915 1916 tok = rcs_gettok(rfp); 1917 if (tok != RCS_TOK_STRING) { 1918 rcs_errno = RCS_ERR_PARSE; 1919 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 1920 RCS_TOKSTR(rfp)); 1921 return (-1); 1922 } 1923 1924 if (RCS_TOKLEN(rfp) == 0) { 1925 rdp->rd_text = xmalloc(1); 1926 rdp->rd_text[0] = '\0'; 1927 rdp->rd_tlen = 0; 1928 } else { 1929 rdp->rd_text = xmalloc(RCS_TOKLEN(rfp)); 1930 memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp)); 1931 rdp->rd_tlen = RCS_TOKLEN(rfp); 1932 } 1933 1934 return (1); 1935 } 1936 1937 /* 1938 * rcs_parse_access() 1939 * 1940 * Parse the access list given as value to the `access' keyword. 1941 * Returns 0 on success, or -1 on failure. 1942 */ 1943 static int 1944 rcs_parse_access(RCSFILE *rfp) 1945 { 1946 int type; 1947 1948 while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) { 1949 if (type != RCS_TOK_ID) { 1950 rcs_errno = RCS_ERR_PARSE; 1951 cvs_log(LP_ERR, "unexpected token `%s' in access list", 1952 RCS_TOKSTR(rfp)); 1953 return (-1); 1954 } 1955 1956 if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0) 1957 return (-1); 1958 } 1959 1960 return (0); 1961 } 1962 1963 /* 1964 * rcs_parse_symbols() 1965 * 1966 * Parse the symbol list given as value to the `symbols' keyword. 1967 * Returns 0 on success, or -1 on failure. 1968 */ 1969 static int 1970 rcs_parse_symbols(RCSFILE *rfp) 1971 { 1972 int type; 1973 struct rcs_sym *symp; 1974 1975 for (;;) { 1976 type = rcs_gettok(rfp); 1977 if (type == RCS_TOK_SCOLON) 1978 break; 1979 1980 if (type != RCS_TOK_ID) { 1981 rcs_errno = RCS_ERR_PARSE; 1982 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1983 RCS_TOKSTR(rfp)); 1984 return (-1); 1985 } 1986 1987 symp = xmalloc(sizeof(*symp)); 1988 symp->rs_name = xstrdup(RCS_TOKSTR(rfp)); 1989 symp->rs_num = rcsnum_alloc(); 1990 1991 type = rcs_gettok(rfp); 1992 if (type != RCS_TOK_COLON) { 1993 rcs_errno = RCS_ERR_PARSE; 1994 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 1995 RCS_TOKSTR(rfp)); 1996 rcsnum_free(symp->rs_num); 1997 xfree(symp->rs_name); 1998 xfree(symp); 1999 return (-1); 2000 } 2001 2002 type = rcs_gettok(rfp); 2003 if (type != RCS_TOK_NUM) { 2004 rcs_errno = RCS_ERR_PARSE; 2005 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2006 RCS_TOKSTR(rfp)); 2007 rcsnum_free(symp->rs_num); 2008 xfree(symp->rs_name); 2009 xfree(symp); 2010 return (-1); 2011 } 2012 2013 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) { 2014 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 2015 RCS_TOKSTR(rfp)); 2016 rcsnum_free(symp->rs_num); 2017 xfree(symp->rs_name); 2018 xfree(symp); 2019 return (-1); 2020 } 2021 2022 TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list); 2023 } 2024 2025 return (0); 2026 } 2027 2028 /* 2029 * rcs_parse_locks() 2030 * 2031 * Parse the lock list given as value to the `locks' keyword. 2032 * Returns 0 on success, or -1 on failure. 2033 */ 2034 static int 2035 rcs_parse_locks(RCSFILE *rfp) 2036 { 2037 int type; 2038 struct rcs_lock *lkp; 2039 2040 for (;;) { 2041 type = rcs_gettok(rfp); 2042 if (type == RCS_TOK_SCOLON) 2043 break; 2044 2045 if (type != RCS_TOK_ID) { 2046 rcs_errno = RCS_ERR_PARSE; 2047 cvs_log(LP_ERR, "unexpected token `%s' in lock list", 2048 RCS_TOKSTR(rfp)); 2049 return (-1); 2050 } 2051 2052 lkp = xmalloc(sizeof(*lkp)); 2053 lkp->rl_name = xstrdup(RCS_TOKSTR(rfp)); 2054 lkp->rl_num = rcsnum_alloc(); 2055 2056 type = rcs_gettok(rfp); 2057 if (type != RCS_TOK_COLON) { 2058 rcs_errno = RCS_ERR_PARSE; 2059 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2060 RCS_TOKSTR(rfp)); 2061 rcsnum_free(lkp->rl_num); 2062 xfree(lkp->rl_name); 2063 xfree(lkp); 2064 return (-1); 2065 } 2066 2067 type = rcs_gettok(rfp); 2068 if (type != RCS_TOK_NUM) { 2069 rcs_errno = RCS_ERR_PARSE; 2070 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2071 RCS_TOKSTR(rfp)); 2072 rcsnum_free(lkp->rl_num); 2073 xfree(lkp->rl_name); 2074 xfree(lkp); 2075 return (-1); 2076 } 2077 2078 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) { 2079 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 2080 RCS_TOKSTR(rfp)); 2081 rcsnum_free(lkp->rl_num); 2082 xfree(lkp->rl_name); 2083 xfree(lkp); 2084 return (-1); 2085 } 2086 2087 TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list); 2088 } 2089 2090 /* check if we have a `strict' */ 2091 type = rcs_gettok(rfp); 2092 if (type != RCS_TOK_STRICT) { 2093 rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); 2094 } else { 2095 rfp->rf_flags |= RCS_SLOCK; 2096 2097 type = rcs_gettok(rfp); 2098 if (type != RCS_TOK_SCOLON) { 2099 rcs_errno = RCS_ERR_PARSE; 2100 cvs_log(LP_ERR, 2101 "missing semi-colon after `strict' keyword"); 2102 return (-1); 2103 } 2104 } 2105 2106 return (0); 2107 } 2108 2109 /* 2110 * rcs_parse_branches() 2111 * 2112 * Parse the list of branches following a `branches' keyword in a delta. 2113 * Returns 0 on success, or -1 on failure. 2114 */ 2115 static int 2116 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp) 2117 { 2118 int type; 2119 struct rcs_branch *brp; 2120 2121 for (;;) { 2122 type = rcs_gettok(rfp); 2123 if (type == RCS_TOK_SCOLON) 2124 break; 2125 2126 if (type != RCS_TOK_NUM) { 2127 rcs_errno = RCS_ERR_PARSE; 2128 cvs_log(LP_ERR, 2129 "unexpected token `%s' in list of branches", 2130 RCS_TOKSTR(rfp)); 2131 return (-1); 2132 } 2133 2134 brp = xmalloc(sizeof(*brp)); 2135 brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp)); 2136 if (brp->rb_num == NULL) { 2137 xfree(brp); 2138 return (-1); 2139 } 2140 2141 TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); 2142 } 2143 2144 return (0); 2145 } 2146 2147 /* 2148 * rcs_freedelta() 2149 * 2150 * Free the contents of a delta structure. 2151 */ 2152 static void 2153 rcs_freedelta(struct rcs_delta *rdp) 2154 { 2155 struct rcs_branch *rb; 2156 2157 if (rdp->rd_num != NULL) 2158 rcsnum_free(rdp->rd_num); 2159 if (rdp->rd_next != NULL) 2160 rcsnum_free(rdp->rd_next); 2161 2162 if (rdp->rd_author != NULL) 2163 xfree(rdp->rd_author); 2164 if (rdp->rd_locker != NULL) 2165 xfree(rdp->rd_locker); 2166 if (rdp->rd_state != NULL) 2167 xfree(rdp->rd_state); 2168 if (rdp->rd_log != NULL) 2169 xfree(rdp->rd_log); 2170 if (rdp->rd_text != NULL) 2171 xfree(rdp->rd_text); 2172 2173 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) { 2174 TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list); 2175 rcsnum_free(rb->rb_num); 2176 xfree(rb); 2177 } 2178 2179 xfree(rdp); 2180 } 2181 2182 /* 2183 * rcs_freepdata() 2184 * 2185 * Free the contents of the parser data structure. 2186 */ 2187 static void 2188 rcs_freepdata(struct rcs_pdata *pd) 2189 { 2190 if (pd->rp_file != NULL) 2191 (void)fclose(pd->rp_file); 2192 if (pd->rp_buf != NULL) 2193 xfree(pd->rp_buf); 2194 xfree(pd); 2195 } 2196 2197 /* 2198 * rcs_gettok() 2199 * 2200 * Get the next RCS token from the string <str>. 2201 */ 2202 static int 2203 rcs_gettok(RCSFILE *rfp) 2204 { 2205 u_int i; 2206 int ch, last, type; 2207 size_t len; 2208 char *bp; 2209 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2210 2211 type = RCS_TOK_ERR; 2212 bp = pdp->rp_buf; 2213 pdp->rp_tlen = 0; 2214 *bp = '\0'; 2215 2216 if (pdp->rp_pttype != RCS_TOK_ERR) { 2217 type = pdp->rp_pttype; 2218 if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >= 2219 pdp->rp_blen) 2220 fatal("rcs_gettok: strlcpy"); 2221 pdp->rp_pttype = RCS_TOK_ERR; 2222 return (type); 2223 } 2224 2225 /* skip leading whitespace */ 2226 /* XXX we must skip backspace too for compatibility, should we? */ 2227 do { 2228 ch = getc(pdp->rp_file); 2229 if (ch == '\n') 2230 pdp->rp_lines++; 2231 } while (isspace(ch)); 2232 2233 if (ch == EOF) { 2234 type = RCS_TOK_EOF; 2235 } else if (ch == ';') { 2236 type = RCS_TOK_SCOLON; 2237 } else if (ch == ':') { 2238 type = RCS_TOK_COLON; 2239 } else if (isalpha(ch)) { 2240 type = RCS_TOK_ID; 2241 *(bp++) = ch; 2242 for (;;) { 2243 ch = getc(pdp->rp_file); 2244 if (ch == EOF) { 2245 type = RCS_TOK_EOF; 2246 break; 2247 } else if (!isalnum(ch) && ch != '_' && ch != '-' && 2248 ch != '/' && ch != '+') { 2249 ungetc(ch, pdp->rp_file); 2250 break; 2251 } 2252 *(bp++) = ch; 2253 pdp->rp_tlen++; 2254 if (bp == pdp->rp_bufend - 1) { 2255 len = bp - pdp->rp_buf; 2256 rcs_growbuf(rfp); 2257 bp = pdp->rp_buf + len; 2258 } 2259 } 2260 *bp = '\0'; 2261 2262 if (type != RCS_TOK_ERR) { 2263 for (i = 0; i < RCS_NKEYS; i++) { 2264 if (strcmp(rcs_keys[i].rk_str, 2265 pdp->rp_buf) == 0) { 2266 type = rcs_keys[i].rk_id; 2267 break; 2268 } 2269 } 2270 } 2271 } else if (ch == '@') { 2272 /* we have a string */ 2273 type = RCS_TOK_STRING; 2274 for (;;) { 2275 ch = getc(pdp->rp_file); 2276 if (ch == EOF) { 2277 type = RCS_TOK_EOF; 2278 break; 2279 } else if (ch == '@') { 2280 ch = getc(pdp->rp_file); 2281 if (ch != '@') { 2282 ungetc(ch, pdp->rp_file); 2283 break; 2284 } 2285 } else if (ch == '\n') 2286 pdp->rp_lines++; 2287 2288 *(bp++) = ch; 2289 pdp->rp_tlen++; 2290 if (bp == pdp->rp_bufend - 1) { 2291 len = bp - pdp->rp_buf; 2292 rcs_growbuf(rfp); 2293 bp = pdp->rp_buf + len; 2294 } 2295 } 2296 2297 *bp = '\0'; 2298 } else if (isdigit(ch)) { 2299 *(bp++) = ch; 2300 last = ch; 2301 type = RCS_TOK_NUM; 2302 2303 for (;;) { 2304 ch = getc(pdp->rp_file); 2305 if (ch == EOF) { 2306 type = RCS_TOK_EOF; 2307 break; 2308 } 2309 if (bp == pdp->rp_bufend) 2310 break; 2311 if (!isdigit(ch) && ch != '.') { 2312 ungetc(ch, pdp->rp_file); 2313 break; 2314 } 2315 2316 if (last == '.' && ch == '.') { 2317 type = RCS_TOK_ERR; 2318 break; 2319 } 2320 last = ch; 2321 *(bp++) = ch; 2322 pdp->rp_tlen++; 2323 } 2324 *bp = '\0'; 2325 } 2326 2327 return (type); 2328 } 2329 2330 /* 2331 * rcs_pushtok() 2332 * 2333 * Push a token back in the parser's token buffer. 2334 */ 2335 static int 2336 rcs_pushtok(RCSFILE *rfp, const char *tok, int type) 2337 { 2338 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2339 2340 if (pdp->rp_pttype != RCS_TOK_ERR) 2341 return (-1); 2342 2343 pdp->rp_pttype = type; 2344 if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >= 2345 sizeof(pdp->rp_ptok)) 2346 fatal("rcs_pushtok: strlcpy"); 2347 return (0); 2348 } 2349 2350 2351 /* 2352 * rcs_growbuf() 2353 * 2354 * Attempt to grow the internal parse buffer for the RCS file <rf> by 2355 * RCS_BUFEXTSIZE. 2356 * In case of failure, the original buffer is left unmodified. 2357 */ 2358 static void 2359 rcs_growbuf(RCSFILE *rf) 2360 { 2361 void *tmp; 2362 struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata; 2363 2364 tmp = xrealloc(pdp->rp_buf, 1, pdp->rp_blen + RCS_BUFEXTSIZE); 2365 pdp->rp_buf = tmp; 2366 pdp->rp_blen += RCS_BUFEXTSIZE; 2367 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 2368 } 2369 2370 /* 2371 * rcs_strprint() 2372 * 2373 * Output an RCS string <str> of size <slen> to the stream <stream>. Any 2374 * '@' characters are escaped. Otherwise, the string can contain arbitrary 2375 * binary data. 2376 */ 2377 static void 2378 rcs_strprint(const u_char *str, size_t slen, FILE *stream) 2379 { 2380 const u_char *ap, *ep, *sp; 2381 2382 if (slen == 0) 2383 return; 2384 2385 ep = str + slen - 1; 2386 2387 for (sp = str; sp <= ep;) { 2388 ap = memchr(sp, '@', ep - sp); 2389 if (ap == NULL) 2390 ap = ep; 2391 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream); 2392 2393 if (*ap == '@') 2394 putc('@', stream); 2395 sp = ap + 1; 2396 } 2397 } 2398 2399 /* 2400 * rcs_deltatext_set() 2401 * 2402 * Set deltatext for <rev> in RCS file <rfp> to <dtext> 2403 * Returns -1 on error, 0 on success. 2404 */ 2405 int 2406 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp) 2407 { 2408 size_t len; 2409 u_char *dtext; 2410 struct rcs_delta *rdp; 2411 2412 /* Write operations require full parsing */ 2413 rcs_parse_deltatexts(rfp, NULL); 2414 2415 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2416 return (-1); 2417 2418 if (rdp->rd_text != NULL) 2419 xfree(rdp->rd_text); 2420 2421 len = cvs_buf_len(bp); 2422 dtext = cvs_buf_release(bp); 2423 bp = NULL; 2424 2425 if (len != 0) { 2426 rdp->rd_text = xmalloc(len); 2427 rdp->rd_tlen = len; 2428 (void)memcpy(rdp->rd_text, dtext, len); 2429 } else { 2430 rdp->rd_text = NULL; 2431 rdp->rd_tlen = 0; 2432 } 2433 2434 if (dtext != NULL) 2435 xfree(dtext); 2436 2437 return (0); 2438 } 2439 2440 /* 2441 * rcs_rev_setlog() 2442 * 2443 * Sets the log message of revision <rev> to <logtext> 2444 */ 2445 int 2446 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext) 2447 { 2448 struct rcs_delta *rdp; 2449 char buf[16]; 2450 2451 rcsnum_tostr(rev, buf, sizeof(buf)); 2452 2453 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2454 return (-1); 2455 2456 if (rdp->rd_log != NULL) 2457 xfree(rdp->rd_log); 2458 2459 rdp->rd_log = xstrdup(logtext); 2460 rfp->rf_flags &= ~RCS_SYNCED; 2461 return (0); 2462 } 2463 /* 2464 * rcs_rev_getdate() 2465 * 2466 * Get the date corresponding to a given revision. 2467 * Returns the date on success, -1 on failure. 2468 */ 2469 time_t 2470 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev) 2471 { 2472 struct rcs_delta *rdp; 2473 2474 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2475 return (-1); 2476 2477 return (timegm(&rdp->rd_date)); 2478 } 2479 2480 /* 2481 * rcs_state_set() 2482 * 2483 * Sets the state of revision <rev> to <state> 2484 * NOTE: default state is 'Exp'. States may not contain spaces. 2485 * 2486 * Returns -1 on failure, 0 on success. 2487 */ 2488 int 2489 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state) 2490 { 2491 struct rcs_delta *rdp; 2492 2493 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2494 return (-1); 2495 2496 if (rdp->rd_state != NULL) 2497 xfree(rdp->rd_state); 2498 2499 rdp->rd_state = xstrdup(state); 2500 2501 rfp->rf_flags &= ~RCS_SYNCED; 2502 2503 return (0); 2504 } 2505 2506 /* 2507 * rcs_state_check() 2508 * 2509 * Check if string <state> is valid. 2510 * 2511 * Returns 0 if the string is valid, -1 otherwise. 2512 */ 2513 int 2514 rcs_state_check(const char *state) 2515 { 2516 if (strchr(state, ' ') != NULL) 2517 return (-1); 2518 2519 return (0); 2520 } 2521 2522 /* 2523 * rcs_state_get() 2524 * 2525 * Get the state for a given revision of a specified RCSFILE. 2526 * 2527 * Returns NULL on failure. 2528 */ 2529 const char * 2530 rcs_state_get(RCSFILE *rfp, RCSNUM *rev) 2531 { 2532 struct rcs_delta *rdp; 2533 2534 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2535 return (NULL); 2536 2537 return (rdp->rd_state); 2538 } 2539 2540 RCSNUM * 2541 rcs_translate_tag(const char *revstr, RCSFILE *rfp) 2542 { 2543 size_t i; 2544 char *sdate; 2545 RCSNUM *rev, *brev; 2546 struct rcs_branch *brp; 2547 struct rcs_delta *rdp; 2548 time_t givendate, rcsdate; 2549 2550 rdp = NULL; 2551 2552 rev = rcs_sym_getrev(rfp, revstr); 2553 if (rev == NULL) { 2554 if ((rev = rcsnum_parse(revstr)) == NULL) { 2555 if ((givendate = cvs_date_parse(revstr)) == -1) 2556 fatal("tag %s does not exist (0)", revstr); 2557 2558 rcs_parse_deltas(rfp, NULL); 2559 2560 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 2561 sdate = asctime(&rdp->rd_date); 2562 if (sdate == NULL) 2563 fatal("failed to parse rcs date"); 2564 rcsdate = cvs_date_parse(sdate); 2565 if (rcsdate == -1) 2566 fatal("failed to parse %s", sdate); 2567 if (givendate <= rcsdate) 2568 continue; 2569 break; 2570 } 2571 2572 if (rdp == NULL) 2573 fatal("no revision that matches date %s", 2574 revstr); 2575 2576 rev = rdp->rd_num; 2577 } 2578 } 2579 2580 if (RCSNUM_ISBRANCH(rev)) { 2581 brev = rcsnum_alloc(); 2582 rcsnum_cpy(rev, brev, rev->rn_len - 1); 2583 } else { 2584 brev = rev; 2585 } 2586 2587 if ((rdp = rcs_findrev(rfp, brev)) == NULL) 2588 fatal("tag %s does not exist (1)", revstr); 2589 2590 if (RCSNUM_ISBRANCH(rev)) { 2591 rcsnum_free(brev); 2592 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2593 for (i = 0; i < rev->rn_len; i++) { 2594 if (brp->rb_num->rn_id[i] != rev->rn_id[i]) 2595 break; 2596 } 2597 2598 if (i != rev->rn_len) 2599 continue; 2600 2601 break; 2602 } 2603 2604 if (brp == NULL) 2605 return (NULL); 2606 2607 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2608 fatal("tag %s does not exist (3)", revstr); 2609 2610 while (rdp->rd_next->rn_len != 0) { 2611 if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL) 2612 fatal("tag %s does not exist (4)", revstr); 2613 } 2614 2615 rcsnum_cpy(rdp->rd_num, rev, 0); 2616 } 2617 2618 return (rev); 2619 } 2620 2621 /* 2622 * rcs_rev_getlines() 2623 * 2624 * Get the entire contents of revision <rev> from the RCSFILE <rfp> and 2625 * return it as a pointer to a struct cvs_lines. 2626 */ 2627 struct cvs_lines * 2628 rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev) 2629 { 2630 size_t plen; 2631 int i, done, nextroot, found; 2632 RCSNUM *tnum, *bnum; 2633 struct rcs_branch *brp; 2634 struct rcs_delta *hrdp, *trdp, *rdp; 2635 u_char *patch; 2636 struct cvs_lines *dlines, *plines; 2637 2638 if ((hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL) 2639 fatal("rcs_rev_getlines: no HEAD revision"); 2640 2641 tnum = frev; 2642 rcs_parse_deltatexts(rfp, hrdp->rd_num); 2643 2644 /* revision on branch, get the branch root */ 2645 nextroot = 2; 2646 if (RCSNUM_ISBRANCHREV(tnum)) { 2647 bnum = rcsnum_alloc(); 2648 rcsnum_cpy(tnum, bnum, nextroot); 2649 } else { 2650 bnum = tnum; 2651 } 2652 2653 dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen); 2654 2655 done = 0; 2656 2657 rdp = hrdp; 2658 if (!rcsnum_differ(rdp->rd_num, bnum)) 2659 goto next; 2660 2661 if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL) 2662 goto done; 2663 2664 again: 2665 for (;;) { 2666 if (rdp->rd_next->rn_len != 0) { 2667 trdp = rcs_findrev(rfp, rdp->rd_next); 2668 if (trdp == NULL) 2669 fatal("failed to grab next revision"); 2670 } 2671 2672 if (rdp->rd_tlen == 0) { 2673 rcs_parse_deltatexts(rfp, rdp->rd_num); 2674 if (rdp->rd_tlen == 0) { 2675 if (!rcsnum_differ(rdp->rd_num, bnum)) 2676 break; 2677 rdp = trdp; 2678 continue; 2679 } 2680 } 2681 2682 plen = rdp->rd_tlen; 2683 patch = rdp->rd_text; 2684 plines = cvs_splitlines(patch, plen); 2685 rcs_patch_lines(dlines, plines); 2686 cvs_freelines(plines); 2687 2688 if (!rcsnum_differ(rdp->rd_num, bnum)) 2689 break; 2690 2691 rdp = trdp; 2692 } 2693 2694 next: 2695 if (!rcsnum_differ(rdp->rd_num, frev)) 2696 done = 1; 2697 2698 if (RCSNUM_ISBRANCHREV(frev) && done != 1) { 2699 nextroot += 2; 2700 rcsnum_cpy(frev, bnum, nextroot); 2701 2702 /* XXX strange loop and "found" set but not used */ 2703 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2704 found = 1; 2705 for (i = 0; i < nextroot - 1; i++) { 2706 if (brp->rb_num->rn_id[i] != bnum->rn_id[i]) { 2707 found = 0; 2708 break; 2709 } 2710 } 2711 2712 break; 2713 } 2714 2715 if (brp == NULL) 2716 fatal("expected branch not found on branch list"); 2717 2718 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2719 fatal("rcs_rev_getlines: failed to get delta for target rev"); 2720 2721 goto again; 2722 } 2723 done: 2724 if (bnum != tnum) 2725 rcsnum_free(bnum); 2726 2727 return (dlines); 2728 } 2729 2730 /* 2731 * rcs_rev_getbuf() 2732 * 2733 * XXX: This is really really slow and should be avoided if at all possible! 2734 * 2735 * Get the entire contents of revision <rev> from the RCSFILE <rfp> and 2736 * return it as a BUF pointer. 2737 */ 2738 BUF * 2739 rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode) 2740 { 2741 int expmode, expand; 2742 struct rcs_delta *rdp; 2743 struct cvs_lines *lines; 2744 struct cvs_line *lp; 2745 BUF *bp; 2746 2747 expand = 0; 2748 lines = rcs_rev_getlines(rfp, rev); 2749 bp = cvs_buf_alloc(1024, BUF_AUTOEXT); 2750 2751 if (!(mode & RCS_KWEXP_NONE)) { 2752 if (rfp->rf_expand != NULL) 2753 expmode = rcs_kwexp_get(rfp); 2754 else 2755 expmode = RCS_KWEXP_DEFAULT; 2756 2757 if (!(expmode & RCS_KWEXP_NONE)) { 2758 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2759 fatal("could not fetch revision"); 2760 expand = 1; 2761 } 2762 } 2763 2764 TAILQ_FOREACH(lp, &lines->l_lines, l_list) { 2765 if (lp->l_line == NULL) 2766 continue; 2767 2768 if (expand) 2769 rcs_kwexp_line(rfp->rf_path, rdp, lp, expmode); 2770 2771 cvs_buf_append(bp, lp->l_line, lp->l_len); 2772 } 2773 2774 cvs_freelines(lines); 2775 2776 return (bp); 2777 } 2778 2779 /* 2780 * rcs_rev_write_fd() 2781 * 2782 * Write the entire contents of revision <frev> from the rcsfile <rfp> to 2783 * file descriptor <fd>. 2784 */ 2785 void 2786 rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int fd, int mode) 2787 { 2788 int expmode, expand; 2789 struct rcs_delta *rdp; 2790 struct cvs_lines *lines; 2791 struct cvs_line *lp; 2792 2793 expand = 0; 2794 lines = rcs_rev_getlines(rfp, rev); 2795 2796 if (!(mode & RCS_KWEXP_NONE)) { 2797 if (rfp->rf_expand != NULL) 2798 expmode = rcs_kwexp_get(rfp); 2799 else 2800 expmode = RCS_KWEXP_DEFAULT; 2801 2802 if (!(expmode & RCS_KWEXP_NONE)) { 2803 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2804 fatal("could not fetch revision"); 2805 expand = 1; 2806 } 2807 } 2808 2809 TAILQ_FOREACH(lp, &lines->l_lines, l_list) { 2810 if (lp->l_line == NULL) 2811 continue; 2812 2813 if (expand) 2814 rcs_kwexp_line(rfp->rf_path, rdp, lp, expmode); 2815 2816 if (write(fd, lp->l_line, lp->l_len) == -1) 2817 fatal("rcs_rev_write_fd: %s", strerror(errno)); 2818 } 2819 2820 /* XXX: do we need to call futimes(2) on the output fd? */ 2821 2822 cvs_freelines(lines); 2823 2824 } 2825 2826 /* 2827 * rcs_rev_write_stmp() 2828 * 2829 * Write the contents of the rev <rev> to a temporary file whose path is 2830 * specified using <template> (see mkstemp(3)). NB. This function will modify 2831 * <template>, as per mkstemp. 2832 */ 2833 void 2834 rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode) 2835 { 2836 int fd; 2837 2838 if ((fd = mkstemp(template)) == -1) 2839 fatal("mkstemp: `%s': %s", template, strerror(errno)); 2840 2841 cvs_worklist_add(template, &temp_files); 2842 rcs_rev_write_fd(rfp, rev, fd, mode); 2843 2844 (void)close(fd); 2845 } 2846 2847 static void 2848 rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_line *line, 2849 int mode) 2850 { 2851 int kwtype; 2852 u_int j, found; 2853 const u_char *c, *start, *fin, *end; 2854 char *kwstr; 2855 char expbuf[256], buf[256]; 2856 char *fmt; 2857 size_t len, kwlen; 2858 2859 kwtype = 0; 2860 kwstr = NULL; 2861 2862 len = line->l_len; 2863 if (len == 0) 2864 return; 2865 2866 c = line->l_line; 2867 found = 0; 2868 /* Final character in buffer. */ 2869 fin = c + len - 1; 2870 2871 /* 2872 * Keyword formats: 2873 * $Keyword$ 2874 * $Keyword: value$ 2875 */ 2876 for (; c < fin; c++) { 2877 if (*c == '$') { 2878 BUF *tmpbuf; 2879 size_t clen, tlen; 2880 2881 /* remember start of this possible keyword */ 2882 start = c; 2883 2884 /* first following character has to be alphanumeric */ 2885 c++; 2886 if (!isalpha(*c)) { 2887 c = start; 2888 continue; 2889 } 2890 2891 /* Number of characters between c and fin, inclusive. */ 2892 clen = fin - c + 1; 2893 2894 /* look for any matching keywords */ 2895 found = 0; 2896 for (j = 0; j < RCS_NKWORDS; j++) { 2897 kwlen = strlen(rcs_expkw[j].kw_str); 2898 /* 2899 * kwlen must be less than clen since clen 2900 * includes either a terminating `$' or a `:'. 2901 */ 2902 if (kwlen < clen && 2903 memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 && 2904 (c[kwlen] == '$' || c[kwlen] == ':')) { 2905 found = 1; 2906 kwstr = rcs_expkw[j].kw_str; 2907 kwtype = rcs_expkw[j].kw_type; 2908 c += kwlen; 2909 break; 2910 } 2911 } 2912 2913 if (found == 0 && cvs_tagname != NULL) { 2914 kwlen = strlen(cvs_tagname); 2915 if (kwlen < clen && 2916 memcmp(c, cvs_tagname, kwlen) == 0 && 2917 (c[kwlen] == '$' || c[kwlen] == ':')) { 2918 found = 1; 2919 kwstr = cvs_tagname; 2920 kwtype = RCS_KW_ID; 2921 c += kwlen; 2922 } 2923 } 2924 2925 /* unknown keyword, continue looking */ 2926 if (found == 0) { 2927 c = start; 2928 continue; 2929 } 2930 2931 /* 2932 * if the next character was ':' we need to look for 2933 * an '$' before the end of the line to be sure it is 2934 * in fact a keyword. 2935 */ 2936 if (*c == ':') { 2937 for (; c <= fin; ++c) { 2938 if (*c == '$' || *c == '\n') 2939 break; 2940 } 2941 2942 if (*c != '$') { 2943 c = start; 2944 continue; 2945 } 2946 } 2947 end = c + 1; 2948 2949 /* start constructing the expansion */ 2950 expbuf[0] = '\0'; 2951 2952 if (mode & RCS_KWEXP_NAME) { 2953 if (strlcat(expbuf, "$", sizeof(expbuf)) >= sizeof(expbuf) || 2954 strlcat(expbuf, kwstr, sizeof(expbuf)) >= sizeof(expbuf)) 2955 fatal("rcs_kwexp_line: truncated"); 2956 if ((mode & RCS_KWEXP_VAL) && 2957 strlcat(expbuf, ": ", sizeof(expbuf)) >= sizeof(expbuf)) 2958 fatal("rcs_kwexp_line: truncated"); 2959 } 2960 2961 /* 2962 * order matters because of RCS_KW_ID and 2963 * RCS_KW_HEADER here 2964 */ 2965 if (mode & RCS_KWEXP_VAL) { 2966 if (kwtype & RCS_KW_RCSFILE) { 2967 if (!(kwtype & RCS_KW_FULLPATH)) 2968 (void)strlcat(expbuf, basename(rcsfile), sizeof(expbuf)); 2969 else 2970 (void)strlcat(expbuf, rcsfile, sizeof(expbuf)); 2971 if (strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 2972 fatal("rcs_kwexp_line: truncated"); 2973 } 2974 2975 if (kwtype & RCS_KW_REVISION) { 2976 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 2977 if (strlcat(buf, " ", sizeof(buf)) >= sizeof(buf) || 2978 strlcat(expbuf, buf, sizeof(expbuf)) >= sizeof(buf)) 2979 fatal("rcs_kwexp_line: truncated"); 2980 } 2981 2982 if (kwtype & RCS_KW_DATE) { 2983 fmt = "%Y/%m/%d %H:%M:%S "; 2984 2985 strftime(buf, sizeof(buf), fmt, &rdp->rd_date); 2986 if (strlcat(expbuf, buf, sizeof(expbuf)) >= sizeof(expbuf)) 2987 fatal("rcs_kwexp_line: string truncated"); 2988 } 2989 2990 if (kwtype & RCS_KW_AUTHOR) { 2991 if (strlcat(expbuf, rdp->rd_author, sizeof(expbuf)) >= sizeof(expbuf) || 2992 strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 2993 fatal("rcs_kwexp_line: string truncated"); 2994 } 2995 2996 if (kwtype & RCS_KW_STATE) { 2997 if (strlcat(expbuf, rdp->rd_state, sizeof(expbuf)) >= sizeof(expbuf) || 2998 strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 2999 fatal("rcs_kwexp_line: string truncated"); 3000 } 3001 3002 /* order does not matter anymore below */ 3003 if (kwtype & RCS_KW_LOG) 3004 if (strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 3005 fatal("rcs_kwexp_line: string truncated"); 3006 3007 if (kwtype & RCS_KW_SOURCE) { 3008 if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >= sizeof(expbuf) || 3009 strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 3010 fatal("rcs_kwexp_line: string truncated"); 3011 } 3012 3013 if (kwtype & RCS_KW_NAME) 3014 if (strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 3015 fatal("rcs_kwexp_line: string truncated"); 3016 } 3017 3018 /* end the expansion */ 3019 if (mode & RCS_KWEXP_NAME) 3020 if (strlcat(expbuf, "$", 3021 sizeof(expbuf)) >= sizeof(expbuf)) 3022 fatal("rcs_kwexp_line: truncated"); 3023 3024 /* Concatenate everything together. */ 3025 tmpbuf = cvs_buf_alloc(len + strlen(expbuf), BUF_AUTOEXT); 3026 /* Append everything before keyword. */ 3027 cvs_buf_append(tmpbuf, line->l_line, 3028 start - line->l_line); 3029 /* Append keyword. */ 3030 cvs_buf_append(tmpbuf, expbuf, strlen(expbuf)); 3031 /* Point c to end of keyword. */ 3032 tlen = cvs_buf_len(tmpbuf) - 1; 3033 /* Append everything after keyword. */ 3034 cvs_buf_append(tmpbuf, end, 3035 line->l_line + line->l_len - end); 3036 c = cvs_buf_get(tmpbuf) + tlen; 3037 /* Point fin to end of data. */ 3038 fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1; 3039 /* Recalculate new length. */ 3040 len = cvs_buf_len(tmpbuf); 3041 3042 /* tmpbuf is now ready, convert to string */ 3043 if (line->l_needsfree) 3044 xfree(line->l_line); 3045 line->l_len = len; 3046 line->l_line = cvs_buf_release(tmpbuf); 3047 line->l_needsfree = 1; 3048 } 3049 } 3050 } 3051