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