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