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