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