1 /* $OpenBSD: rcs.c,v 1.88 2019/01/09 17:57:05 joris 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/param.h> /* MAXBSIZE */ 28 #include <sys/stat.h> 29 30 #include <ctype.h> 31 #include <err.h> 32 #include <errno.h> 33 #include <pwd.h> 34 #include <stdarg.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include "diff.h" 41 #include "rcs.h" 42 #include "rcsparse.h" 43 #include "rcsprog.h" 44 #include "rcsutil.h" 45 #include "xmalloc.h" 46 47 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 48 49 /* invalid characters in RCS states */ 50 static const char rcs_state_invch[] = RCS_STATE_INVALCHAR; 51 52 /* invalid characters in RCS symbol names */ 53 static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; 54 55 struct rcs_kw rcs_expkw[] = { 56 { "Author", RCS_KW_AUTHOR }, 57 { "Date", RCS_KW_DATE }, 58 { "Locker", RCS_KW_LOCKER }, 59 { "Header", RCS_KW_HEADER }, 60 { "Id", RCS_KW_ID }, 61 { "OpenBSD", RCS_KW_ID }, 62 { "Log", RCS_KW_LOG }, 63 { "Name", RCS_KW_NAME }, 64 { "RCSfile", RCS_KW_RCSFILE }, 65 { "Revision", RCS_KW_REVISION }, 66 { "Source", RCS_KW_SOURCE }, 67 { "State", RCS_KW_STATE }, 68 { "Mdocdate", RCS_KW_MDOCDATE }, 69 }; 70 71 int rcs_errno = RCS_ERR_NOERR; 72 char *timezone_flag = NULL; 73 74 int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *); 75 static int rcs_movefile(char *, char *, mode_t, u_int); 76 77 static void rcs_freedelta(struct rcs_delta *); 78 static void rcs_strprint(const u_char *, size_t, FILE *); 79 80 static BUF *rcs_expand_keywords(char *, struct rcs_delta *, BUF *, int); 81 82 RCSFILE * 83 rcs_open(const char *path, int fd, int flags, ...) 84 { 85 int mode; 86 mode_t fmode; 87 RCSFILE *rfp; 88 va_list vap; 89 struct rcs_delta *rdp; 90 struct rcs_lock *lkr; 91 92 fmode = S_IRUSR|S_IRGRP|S_IROTH; 93 flags &= 0xffff; /* ditch any internal flags */ 94 95 if (flags & RCS_CREATE) { 96 va_start(vap, flags); 97 mode = va_arg(vap, int); 98 va_end(vap); 99 fmode = (mode_t)mode; 100 } 101 102 rfp = xcalloc(1, sizeof(*rfp)); 103 104 rfp->rf_path = xstrdup(path); 105 rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED; 106 rfp->rf_mode = fmode; 107 if (fd == -1) 108 rfp->rf_file = NULL; 109 else if ((rfp->rf_file = fdopen(fd, "r")) == NULL) 110 err(1, "rcs_open: fdopen: `%s'", path); 111 112 TAILQ_INIT(&(rfp->rf_delta)); 113 TAILQ_INIT(&(rfp->rf_access)); 114 TAILQ_INIT(&(rfp->rf_symbols)); 115 TAILQ_INIT(&(rfp->rf_locks)); 116 117 if (!(rfp->rf_flags & RCS_CREATE)) { 118 if (rcsparse_init(rfp)) 119 errx(1, "could not parse admin data"); 120 121 /* fill in rd_locker */ 122 TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) { 123 if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) { 124 rcs_close(rfp); 125 return (NULL); 126 } 127 128 rdp->rd_locker = xstrdup(lkr->rl_name); 129 } 130 } 131 132 return (rfp); 133 } 134 135 /* 136 * rcs_close() 137 * 138 * Close an RCS file handle. 139 */ 140 void 141 rcs_close(RCSFILE *rfp) 142 { 143 struct rcs_delta *rdp; 144 struct rcs_access *rap; 145 struct rcs_lock *rlp; 146 struct rcs_sym *rsp; 147 148 if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED)) 149 rcs_write(rfp); 150 151 while (!TAILQ_EMPTY(&(rfp->rf_delta))) { 152 rdp = TAILQ_FIRST(&(rfp->rf_delta)); 153 TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list); 154 rcs_freedelta(rdp); 155 } 156 157 while (!TAILQ_EMPTY(&(rfp->rf_access))) { 158 rap = TAILQ_FIRST(&(rfp->rf_access)); 159 TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list); 160 free(rap->ra_name); 161 free(rap); 162 } 163 164 while (!TAILQ_EMPTY(&(rfp->rf_symbols))) { 165 rsp = TAILQ_FIRST(&(rfp->rf_symbols)); 166 TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list); 167 rcsnum_free(rsp->rs_num); 168 free(rsp->rs_name); 169 free(rsp); 170 } 171 172 while (!TAILQ_EMPTY(&(rfp->rf_locks))) { 173 rlp = TAILQ_FIRST(&(rfp->rf_locks)); 174 TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list); 175 rcsnum_free(rlp->rl_num); 176 free(rlp->rl_name); 177 free(rlp); 178 } 179 180 rcsnum_free(rfp->rf_head); 181 rcsnum_free(rfp->rf_branch); 182 183 if (rfp->rf_file != NULL) 184 fclose(rfp->rf_file); 185 186 free(rfp->rf_path); 187 free(rfp->rf_comment); 188 free(rfp->rf_expand); 189 free(rfp->rf_desc); 190 if (rfp->rf_pdata != NULL) 191 rcsparse_free(rfp); 192 193 free(rfp); 194 } 195 196 /* 197 * rcs_write() 198 * 199 * Write the contents of the RCS file handle <rfp> to disk in the file whose 200 * path is in <rf_path>. 201 */ 202 void 203 rcs_write(RCSFILE *rfp) 204 { 205 FILE *fp; 206 char numbuf[RCS_REV_BUFSZ], *fn; 207 struct rcs_access *ap; 208 struct rcs_sym *symp; 209 struct rcs_branch *brp; 210 struct rcs_delta *rdp; 211 struct rcs_lock *lkp; 212 size_t len; 213 int fd; 214 215 fn = NULL; 216 217 if (rfp->rf_flags & RCS_SYNCED) 218 return; 219 220 /* Write operations need the whole file parsed */ 221 if (rcsparse_deltatexts(rfp, NULL)) 222 errx(1, "problem parsing deltatexts"); 223 224 (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", rcs_tmpdir); 225 226 if ((fd = mkstemp(fn)) == -1) 227 err(1, "%s", fn); 228 229 if ((fp = fdopen(fd, "w+")) == NULL) { 230 int saved_errno; 231 232 saved_errno = errno; 233 (void)unlink(fn); 234 errno = saved_errno; 235 err(1, "%s", fn); 236 } 237 238 worklist_add(fn, &temp_files); 239 240 if (rfp->rf_head != NULL) 241 rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf)); 242 else 243 numbuf[0] = '\0'; 244 245 fprintf(fp, "head\t%s;\n", numbuf); 246 247 if (rfp->rf_branch != NULL) { 248 rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf)); 249 fprintf(fp, "branch\t%s;\n", numbuf); 250 } 251 252 fputs("access", fp); 253 TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) { 254 fprintf(fp, "\n\t%s", ap->ra_name); 255 } 256 fputs(";\n", fp); 257 258 fprintf(fp, "symbols"); 259 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 260 if (RCSNUM_ISBRANCH(symp->rs_num)) 261 rcsnum_addmagic(symp->rs_num); 262 rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); 263 fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf); 264 } 265 fprintf(fp, ";\n"); 266 267 fprintf(fp, "locks"); 268 TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) { 269 rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf)); 270 fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf); 271 } 272 273 fprintf(fp, ";"); 274 275 if (rfp->rf_flags & RCS_SLOCK) 276 fprintf(fp, " strict;"); 277 fputc('\n', fp); 278 279 fputs("comment\t@", fp); 280 if (rfp->rf_comment != NULL) { 281 rcs_strprint((const u_char *)rfp->rf_comment, 282 strlen(rfp->rf_comment), fp); 283 fputs("@;\n", fp); 284 } else 285 fputs("# @;\n", fp); 286 287 if (rfp->rf_expand != NULL) { 288 fputs("expand @", fp); 289 rcs_strprint((const u_char *)rfp->rf_expand, 290 strlen(rfp->rf_expand), fp); 291 fputs("@;\n", fp); 292 } 293 294 fputs("\n\n", fp); 295 296 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 297 fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 298 sizeof(numbuf))); 299 fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;", 300 rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1, 301 rdp->rd_date.tm_mday, rdp->rd_date.tm_hour, 302 rdp->rd_date.tm_min, rdp->rd_date.tm_sec); 303 fprintf(fp, "\tauthor %s;\tstate %s;\n", 304 rdp->rd_author, rdp->rd_state); 305 fputs("branches", fp); 306 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 307 fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf, 308 sizeof(numbuf))); 309 } 310 fputs(";\n", fp); 311 fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next, 312 numbuf, sizeof(numbuf))); 313 } 314 315 fputs("\ndesc\n@", fp); 316 if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) { 317 rcs_strprint((const u_char *)rfp->rf_desc, len, fp); 318 if (rfp->rf_desc[len-1] != '\n') 319 fputc('\n', fp); 320 } 321 fputs("@\n", fp); 322 323 /* deltatexts */ 324 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 325 fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 326 sizeof(numbuf))); 327 fputs("log\n@", fp); 328 if (rdp->rd_log != NULL) { 329 len = strlen(rdp->rd_log); 330 rcs_strprint((const u_char *)rdp->rd_log, len, fp); 331 if (len == 0 || rdp->rd_log[len-1] != '\n') 332 fputc('\n', fp); 333 } 334 fputs("@\ntext\n@", fp); 335 if (rdp->rd_text != NULL) 336 rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp); 337 fputs("@\n", fp); 338 } 339 (void)fclose(fp); 340 341 if (rcs_movefile(fn, rfp->rf_path, rfp->rf_mode, rfp->rf_flags) == -1) { 342 (void)unlink(fn); 343 errx(1, "rcs_movefile failed"); 344 } 345 346 rfp->rf_flags |= RCS_SYNCED; 347 348 free(fn); 349 } 350 351 /* 352 * rcs_movefile() 353 * 354 * Move a file using rename(2) if possible and copying if not. 355 * Returns 0 on success, -1 on failure. 356 */ 357 static int 358 rcs_movefile(char *from, char *to, mode_t perm, u_int to_flags) 359 { 360 FILE *src, *dst; 361 size_t nread, nwritten; 362 char *buf; 363 364 if (rename(from, to) == 0) { 365 if (chmod(to, perm) == -1) { 366 warn("%s", to); 367 return (-1); 368 } 369 return (0); 370 } else if (errno != EXDEV) { 371 warn("failed to access temp RCS output file"); 372 return (-1); 373 } 374 375 if ((chmod(to, S_IWUSR) == -1) && !(to_flags & RCS_CREATE)) { 376 warnx("chmod(%s, 0%o) failed", to, S_IWUSR); 377 return (-1); 378 } 379 380 /* different filesystem, have to copy the file */ 381 if ((src = fopen(from, "r")) == NULL) { 382 warn("%s", from); 383 return (-1); 384 } 385 if ((dst = fopen(to, "w")) == NULL) { 386 warn("%s", to); 387 (void)fclose(src); 388 return (-1); 389 } 390 if (fchmod(fileno(dst), perm)) { 391 warn("%s", to); 392 (void)unlink(to); 393 (void)fclose(src); 394 (void)fclose(dst); 395 return (-1); 396 } 397 398 buf = xmalloc(MAXBSIZE); 399 while ((nread = fread(buf, sizeof(char), MAXBSIZE, src)) != 0) { 400 if (ferror(src)) { 401 warnx("failed to read `%s'", from); 402 (void)unlink(to); 403 goto out; 404 } 405 nwritten = fwrite(buf, sizeof(char), nread, dst); 406 if (nwritten != nread) { 407 warnx("failed to write `%s'", to); 408 (void)unlink(to); 409 goto out; 410 } 411 } 412 413 (void)unlink(from); 414 415 out: 416 (void)fclose(src); 417 (void)fclose(dst); 418 free(buf); 419 420 return (0); 421 } 422 423 /* 424 * rcs_head_set() 425 * 426 * Set the revision number of the head revision for the RCS file <file> to 427 * <rev>, which must reference a valid revision within the file. 428 */ 429 int 430 rcs_head_set(RCSFILE *file, RCSNUM *rev) 431 { 432 if (rcs_findrev(file, rev) == NULL) 433 return (-1); 434 435 if (file->rf_head == NULL) 436 file->rf_head = rcsnum_alloc(); 437 438 rcsnum_cpy(rev, file->rf_head, 0); 439 file->rf_flags &= ~RCS_SYNCED; 440 return (0); 441 } 442 443 444 /* 445 * rcs_branch_get() 446 * 447 * Retrieve the default branch number for the RCS file <file>. 448 * Returns the number on success. If NULL is returned, then there is no 449 * default branch for this file. 450 */ 451 const RCSNUM * 452 rcs_branch_get(RCSFILE *file) 453 { 454 return (file->rf_branch); 455 } 456 457 /* 458 * rcs_access_add() 459 * 460 * Add the login name <login> to the access list for the RCS file <file>. 461 * Returns 0 on success, or -1 on failure. 462 */ 463 int 464 rcs_access_add(RCSFILE *file, const char *login) 465 { 466 struct rcs_access *ap; 467 468 /* first look for duplication */ 469 TAILQ_FOREACH(ap, &(file->rf_access), ra_list) { 470 if (strcmp(ap->ra_name, login) == 0) { 471 rcs_errno = RCS_ERR_DUPENT; 472 return (-1); 473 } 474 } 475 476 ap = xmalloc(sizeof(*ap)); 477 ap->ra_name = xstrdup(login); 478 TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list); 479 480 /* not synced anymore */ 481 file->rf_flags &= ~RCS_SYNCED; 482 return (0); 483 } 484 485 /* 486 * rcs_access_remove() 487 * 488 * Remove an entry with login name <login> from the access list of the RCS 489 * file <file>. 490 * Returns 0 on success, or -1 on failure. 491 */ 492 int 493 rcs_access_remove(RCSFILE *file, const char *login) 494 { 495 struct rcs_access *ap; 496 497 TAILQ_FOREACH(ap, &(file->rf_access), ra_list) 498 if (strcmp(ap->ra_name, login) == 0) 499 break; 500 501 if (ap == NULL) { 502 rcs_errno = RCS_ERR_NOENT; 503 return (-1); 504 } 505 506 TAILQ_REMOVE(&(file->rf_access), ap, ra_list); 507 free(ap->ra_name); 508 free(ap); 509 510 /* not synced anymore */ 511 file->rf_flags &= ~RCS_SYNCED; 512 return (0); 513 } 514 515 /* 516 * rcs_sym_add() 517 * 518 * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol 519 * is named <sym> and is bound to the RCS revision <snum>. 520 * Returns 0 on success, or -1 on failure. 521 */ 522 int 523 rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum) 524 { 525 struct rcs_sym *symp; 526 527 if (!rcs_sym_check(sym)) { 528 rcs_errno = RCS_ERR_BADSYM; 529 return (-1); 530 } 531 532 /* first look for duplication */ 533 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 534 if (strcmp(symp->rs_name, sym) == 0) { 535 rcs_errno = RCS_ERR_DUPENT; 536 return (-1); 537 } 538 } 539 540 symp = xmalloc(sizeof(*symp)); 541 symp->rs_name = xstrdup(sym); 542 symp->rs_num = rcsnum_alloc(); 543 rcsnum_cpy(snum, symp->rs_num, 0); 544 545 TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); 546 547 /* not synced anymore */ 548 rfp->rf_flags &= ~RCS_SYNCED; 549 return (0); 550 } 551 552 /* 553 * rcs_sym_remove() 554 * 555 * Remove the symbol with name <sym> from the symbol list for the RCS file 556 * <file>. If no such symbol is found, the call fails and returns with an 557 * error. 558 * Returns 0 on success, or -1 on failure. 559 */ 560 int 561 rcs_sym_remove(RCSFILE *file, const char *sym) 562 { 563 struct rcs_sym *symp; 564 565 if (!rcs_sym_check(sym)) { 566 rcs_errno = RCS_ERR_BADSYM; 567 return (-1); 568 } 569 570 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 571 if (strcmp(symp->rs_name, sym) == 0) 572 break; 573 574 if (symp == NULL) { 575 rcs_errno = RCS_ERR_NOENT; 576 return (-1); 577 } 578 579 TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list); 580 free(symp->rs_name); 581 rcsnum_free(symp->rs_num); 582 free(symp); 583 584 /* not synced anymore */ 585 file->rf_flags &= ~RCS_SYNCED; 586 return (0); 587 } 588 589 /* 590 * rcs_sym_getrev() 591 * 592 * Retrieve the RCS revision number associated with the symbol <sym> for the 593 * RCS file <file>. The returned value is a dynamically-allocated copy and 594 * should be freed by the caller once they are done with it. 595 * Returns the RCSNUM on success, or NULL on failure. 596 */ 597 RCSNUM * 598 rcs_sym_getrev(RCSFILE *file, const char *sym) 599 { 600 RCSNUM *num; 601 struct rcs_sym *symp; 602 603 if (!rcs_sym_check(sym)) { 604 rcs_errno = RCS_ERR_BADSYM; 605 return (NULL); 606 } 607 608 num = NULL; 609 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 610 if (strcmp(symp->rs_name, sym) == 0) 611 break; 612 613 if (symp == NULL) { 614 rcs_errno = RCS_ERR_NOENT; 615 } else { 616 num = rcsnum_alloc(); 617 rcsnum_cpy(symp->rs_num, num, 0); 618 } 619 620 return (num); 621 } 622 623 /* 624 * rcs_sym_check() 625 * 626 * Check the RCS symbol name <sym> for any unsupported characters. 627 * Returns 1 if the tag is correct, 0 if it isn't valid. 628 */ 629 int 630 rcs_sym_check(const char *sym) 631 { 632 int ret; 633 const unsigned char *cp; 634 635 ret = 1; 636 cp = sym; 637 if (!isalpha(*cp++)) 638 return (0); 639 640 for (; *cp != '\0'; cp++) 641 if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) { 642 ret = 0; 643 break; 644 } 645 646 return (ret); 647 } 648 649 /* 650 * rcs_lock_getmode() 651 * 652 * Retrieve the locking mode of the RCS file <file>. 653 */ 654 int 655 rcs_lock_getmode(RCSFILE *file) 656 { 657 return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE; 658 } 659 660 /* 661 * rcs_lock_setmode() 662 * 663 * Set the locking mode of the RCS file <file> to <mode>, which must either 664 * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT. 665 * Returns the previous mode on success, or -1 on failure. 666 */ 667 int 668 rcs_lock_setmode(RCSFILE *file, int mode) 669 { 670 int pmode; 671 pmode = rcs_lock_getmode(file); 672 673 if (mode == RCS_LOCK_STRICT) 674 file->rf_flags |= RCS_SLOCK; 675 else if (mode == RCS_LOCK_LOOSE) 676 file->rf_flags &= ~RCS_SLOCK; 677 else 678 errx(1, "rcs_lock_setmode: invalid mode `%d'", mode); 679 680 file->rf_flags &= ~RCS_SYNCED; 681 return (pmode); 682 } 683 684 /* 685 * rcs_lock_add() 686 * 687 * Add an RCS lock for the user <user> on revision <rev>. 688 * Returns 0 on success, or -1 on failure. 689 */ 690 int 691 rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev) 692 { 693 struct rcs_lock *lkp; 694 struct rcs_delta *rdp; 695 696 if ((rdp = rcs_findrev(file, rev)) == NULL) { 697 rcs_errno = RCS_ERR_NOENT; 698 return (-1); 699 } 700 701 /* first look for duplication */ 702 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { 703 if (strcmp(lkp->rl_name, user) == 0 && 704 rcsnum_cmp(rev, lkp->rl_num, 0) == 0) { 705 rcs_errno = RCS_ERR_DUPENT; 706 return (-1); 707 } 708 } 709 710 lkp = xmalloc(sizeof(*lkp)); 711 lkp->rl_name = xstrdup(user); 712 lkp->rl_num = rcsnum_alloc(); 713 rcsnum_cpy(rev, lkp->rl_num, 0); 714 715 free(rdp->rd_locker); 716 rdp->rd_locker = xstrdup(user); 717 718 TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list); 719 720 /* not synced anymore */ 721 file->rf_flags &= ~RCS_SYNCED; 722 return (0); 723 } 724 725 726 /* 727 * rcs_lock_remove() 728 * 729 * Remove the RCS lock on revision <rev>. 730 * Returns 0 on success, or -1 on failure. 731 */ 732 int 733 rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev) 734 { 735 struct rcs_lock *lkp; 736 struct rcs_delta *rdp; 737 738 if ((rdp = rcs_findrev(file, rev)) == NULL) { 739 rcs_errno = RCS_ERR_NOENT; 740 return (-1); 741 } 742 743 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { 744 if (strcmp(lkp->rl_name, user) == 0 && 745 rcsnum_cmp(lkp->rl_num, rev, 0) == 0) 746 break; 747 } 748 749 if (lkp == NULL) { 750 rcs_errno = RCS_ERR_NOENT; 751 return (-1); 752 } 753 754 TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list); 755 rcsnum_free(lkp->rl_num); 756 free(lkp->rl_name); 757 free(lkp); 758 759 free(rdp->rd_locker); 760 rdp->rd_locker = NULL; 761 762 /* not synced anymore */ 763 file->rf_flags &= ~RCS_SYNCED; 764 return (0); 765 } 766 767 /* 768 * rcs_desc_set() 769 * 770 * Set the description for the RCS file <file>. 771 */ 772 void 773 rcs_desc_set(RCSFILE *file, const char *desc) 774 { 775 char *tmp; 776 777 tmp = xstrdup(desc); 778 free(file->rf_desc); 779 file->rf_desc = tmp; 780 file->rf_flags &= ~RCS_SYNCED; 781 } 782 783 /* 784 * rcs_comment_set() 785 * 786 * Set the comment leader for the RCS file <file>. 787 */ 788 void 789 rcs_comment_set(RCSFILE *file, const char *comment) 790 { 791 char *tmp; 792 793 tmp = xstrdup(comment); 794 free(file->rf_comment); 795 file->rf_comment = tmp; 796 file->rf_flags &= ~RCS_SYNCED; 797 } 798 799 int 800 rcs_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines) 801 { 802 char op, *ep; 803 struct rcs_line *lp, *dlp, *ndlp; 804 int i, lineno, nbln; 805 u_char tmp; 806 807 dlp = TAILQ_FIRST(&(dlines->l_lines)); 808 lp = TAILQ_FIRST(&(plines->l_lines)); 809 810 /* skip first bogus line */ 811 for (lp = TAILQ_NEXT(lp, l_list); lp != NULL; 812 lp = TAILQ_NEXT(lp, l_list)) { 813 if (lp->l_len < 2) 814 errx(1, "line too short, RCS patch seems broken"); 815 op = *(lp->l_line); 816 /* NUL-terminate line buffer for strtol() safety. */ 817 tmp = lp->l_line[lp->l_len - 1]; 818 lp->l_line[lp->l_len - 1] = '\0'; 819 lineno = (int)strtol((lp->l_line + 1), &ep, 10); 820 if (lineno > dlines->l_nblines || lineno < 0 || 821 *ep != ' ') 822 errx(1, "invalid line specification in RCS patch"); 823 ep++; 824 nbln = (int)strtol(ep, &ep, 10); 825 /* Restore the last byte of the buffer */ 826 lp->l_line[lp->l_len - 1] = tmp; 827 if (nbln < 0) 828 errx(1, 829 "invalid line number specification in RCS patch"); 830 831 /* find the appropriate line */ 832 for (;;) { 833 if (dlp == NULL) 834 break; 835 if (dlp->l_lineno == lineno) 836 break; 837 if (dlp->l_lineno > lineno) { 838 dlp = TAILQ_PREV(dlp, tqh, l_list); 839 } else if (dlp->l_lineno < lineno) { 840 if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) || 841 ndlp->l_lineno > lineno) 842 break; 843 dlp = ndlp; 844 } 845 } 846 if (dlp == NULL) 847 errx(1, "can't find referenced line in RCS patch"); 848 849 if (op == 'd') { 850 for (i = 0; (i < nbln) && (dlp != NULL); i++) { 851 ndlp = TAILQ_NEXT(dlp, l_list); 852 TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list); 853 free(dlp); 854 dlp = ndlp; 855 /* last line is gone - reset dlp */ 856 if (dlp == NULL) { 857 ndlp = TAILQ_LAST(&(dlines->l_lines), 858 tqh); 859 dlp = ndlp; 860 } 861 } 862 } else if (op == 'a') { 863 for (i = 0; i < nbln; i++) { 864 ndlp = lp; 865 lp = TAILQ_NEXT(lp, l_list); 866 if (lp == NULL) 867 errx(1, "truncated RCS patch"); 868 TAILQ_REMOVE(&(plines->l_lines), lp, l_list); 869 TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp, 870 lp, l_list); 871 dlp = lp; 872 873 /* we don't want lookup to block on those */ 874 lp->l_lineno = lineno; 875 876 lp = ndlp; 877 } 878 } else 879 errx(1, "unknown RCS patch operation `%c'", op); 880 881 /* last line of the patch, done */ 882 if (lp->l_lineno == plines->l_nblines) 883 break; 884 } 885 886 /* once we're done patching, rebuild the line numbers */ 887 lineno = 0; 888 TAILQ_FOREACH(lp, &(dlines->l_lines), l_list) 889 lp->l_lineno = lineno++; 890 dlines->l_nblines = lineno - 1; 891 892 return (0); 893 } 894 895 /* 896 * rcs_getrev() 897 * 898 * Get the whole contents of revision <rev> from the RCSFILE <rfp>. The 899 * returned buffer is dynamically allocated and should be released using 900 * buf_free() once the caller is done using it. 901 */ 902 BUF * 903 rcs_getrev(RCSFILE *rfp, RCSNUM *frev) 904 { 905 u_int i, numlen; 906 int isbranch, lookonbranch, found; 907 size_t dlen, plen, len; 908 RCSNUM *crev, *rev, *brev; 909 BUF *rbuf; 910 struct rcs_delta *rdp = NULL; 911 struct rcs_branch *rb; 912 u_char *data, *patch; 913 914 if (rfp->rf_head == NULL) 915 return (NULL); 916 917 if (frev == RCS_HEAD_REV) 918 rev = rfp->rf_head; 919 else 920 rev = frev; 921 922 /* XXX rcsnum_cmp() */ 923 for (i = 0; i < rfp->rf_head->rn_len; i++) { 924 if (rfp->rf_head->rn_id[i] < rev->rn_id[i]) { 925 rcs_errno = RCS_ERR_NOENT; 926 return (NULL); 927 } 928 } 929 930 /* No matter what, we'll need everything parsed up until the description 931 so go for it. */ 932 if (rcsparse_deltas(rfp, NULL)) 933 return (NULL); 934 935 rdp = rcs_findrev(rfp, rfp->rf_head); 936 if (rdp == NULL) { 937 warnx("failed to get RCS HEAD revision"); 938 return (NULL); 939 } 940 941 if (rdp->rd_tlen == 0) 942 if (rcsparse_deltatexts(rfp, rfp->rf_head)) 943 return (NULL); 944 945 len = rdp->rd_tlen; 946 if (len == 0) { 947 rbuf = buf_alloc(1); 948 buf_empty(rbuf); 949 return (rbuf); 950 } 951 952 rbuf = buf_alloc(len); 953 buf_append(rbuf, rdp->rd_text, len); 954 955 isbranch = 0; 956 brev = NULL; 957 958 /* 959 * If a branch was passed, get the latest revision on it. 960 */ 961 if (RCSNUM_ISBRANCH(rev)) { 962 brev = rev; 963 rdp = rcs_findrev(rfp, rev); 964 if (rdp == NULL) { 965 buf_free(rbuf); 966 return (NULL); 967 } 968 969 rev = rdp->rd_num; 970 } else { 971 if (RCSNUM_ISBRANCHREV(rev)) { 972 brev = rcsnum_revtobr(rev); 973 isbranch = 1; 974 } 975 } 976 977 lookonbranch = 0; 978 crev = NULL; 979 980 /* Apply patches backwards to get the right version. 981 */ 982 do { 983 found = 0; 984 985 if (rcsnum_cmp(rfp->rf_head, rev, 0) == 0) 986 break; 987 988 if (isbranch == 1 && rdp->rd_num->rn_len < rev->rn_len && 989 !TAILQ_EMPTY(&(rdp->rd_branches))) 990 lookonbranch = 1; 991 992 if (isbranch && lookonbranch == 1) { 993 lookonbranch = 0; 994 TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) { 995 /* XXX rcsnum_cmp() is totally broken for 996 * this purpose. 997 */ 998 numlen = MINIMUM(brev->rn_len, 999 rb->rb_num->rn_len - 1); 1000 for (i = 0; i < numlen; i++) { 1001 if (rb->rb_num->rn_id[i] != 1002 brev->rn_id[i]) 1003 break; 1004 } 1005 1006 if (i == numlen) { 1007 crev = rb->rb_num; 1008 found = 1; 1009 break; 1010 } 1011 } 1012 if (found == 0) 1013 crev = rdp->rd_next; 1014 } else { 1015 crev = rdp->rd_next; 1016 } 1017 1018 rdp = rcs_findrev(rfp, crev); 1019 if (rdp == NULL) { 1020 buf_free(rbuf); 1021 return (NULL); 1022 } 1023 1024 plen = rdp->rd_tlen; 1025 dlen = buf_len(rbuf); 1026 patch = rdp->rd_text; 1027 data = buf_release(rbuf); 1028 /* check if we have parsed this rev's deltatext */ 1029 if (rdp->rd_tlen == 0) 1030 if (rcsparse_deltatexts(rfp, rdp->rd_num)) 1031 return (NULL); 1032 1033 rbuf = rcs_patchfile(data, dlen, patch, plen, rcs_patch_lines); 1034 free(data); 1035 1036 if (rbuf == NULL) 1037 break; 1038 } while (rcsnum_cmp(crev, rev, 0) != 0); 1039 1040 return (rbuf); 1041 } 1042 1043 void 1044 rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved) 1045 { 1046 struct rcs_lines *plines; 1047 struct rcs_line *lp; 1048 int added, i, nbln, removed; 1049 char op, *ep; 1050 u_char tmp; 1051 1052 added = removed = 0; 1053 1054 plines = rcs_splitlines(rdp->rd_text, rdp->rd_tlen); 1055 lp = TAILQ_FIRST(&(plines->l_lines)); 1056 1057 /* skip first bogus line */ 1058 for (lp = TAILQ_NEXT(lp, l_list); lp != NULL; 1059 lp = TAILQ_NEXT(lp, l_list)) { 1060 if (lp->l_len < 2) 1061 errx(1, 1062 "line too short, RCS patch seems broken"); 1063 op = *(lp->l_line); 1064 /* NUL-terminate line buffer for strtol() safety. */ 1065 tmp = lp->l_line[lp->l_len - 1]; 1066 lp->l_line[lp->l_len - 1] = '\0'; 1067 (void)strtol((lp->l_line + 1), &ep, 10); 1068 ep++; 1069 nbln = (int)strtol(ep, &ep, 10); 1070 /* Restore the last byte of the buffer */ 1071 lp->l_line[lp->l_len - 1] = tmp; 1072 if (nbln < 0) 1073 errx(1, "invalid line number specification " 1074 "in RCS patch"); 1075 1076 if (op == 'a') { 1077 added += nbln; 1078 for (i = 0; i < nbln; i++) { 1079 lp = TAILQ_NEXT(lp, l_list); 1080 if (lp == NULL) 1081 errx(1, "truncated RCS patch"); 1082 } 1083 } else if (op == 'd') 1084 removed += nbln; 1085 else 1086 errx(1, "unknown RCS patch operation '%c'", op); 1087 } 1088 1089 rcs_freelines(plines); 1090 1091 *ladded = added; 1092 *lremoved = removed; 1093 } 1094 1095 /* 1096 * rcs_rev_add() 1097 * 1098 * Add a revision to the RCS file <rf>. The new revision's number can be 1099 * specified in <rev> (which can also be RCS_HEAD_REV, in which case the 1100 * new revision will have a number equal to the previous head revision plus 1101 * one). The <msg> argument specifies the log message for that revision, and 1102 * <date> specifies the revision's date (a value of -1 is 1103 * equivalent to using the current time). 1104 * If <author> is NULL, set the author for this revision to the current user. 1105 * Returns 0 on success, or -1 on failure. 1106 */ 1107 int 1108 rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date, 1109 const char *author) 1110 { 1111 time_t now; 1112 struct passwd *pw; 1113 struct rcs_delta *ordp, *rdp; 1114 1115 if (rev == RCS_HEAD_REV) { 1116 if (rf->rf_flags & RCS_CREATE) { 1117 if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL) 1118 return (-1); 1119 rf->rf_head = rev; 1120 } else { 1121 rev = rcsnum_inc(rf->rf_head); 1122 } 1123 } else { 1124 if ((rdp = rcs_findrev(rf, rev)) != NULL) { 1125 rcs_errno = RCS_ERR_DUPENT; 1126 return (-1); 1127 } 1128 } 1129 1130 rdp = xcalloc(1, sizeof(*rdp)); 1131 1132 TAILQ_INIT(&(rdp->rd_branches)); 1133 1134 rdp->rd_num = rcsnum_alloc(); 1135 rcsnum_cpy(rev, rdp->rd_num, 0); 1136 1137 rdp->rd_next = rcsnum_alloc(); 1138 1139 if (!(rf->rf_flags & RCS_CREATE)) { 1140 /* next should point to the previous HEAD */ 1141 ordp = TAILQ_FIRST(&(rf->rf_delta)); 1142 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0); 1143 } 1144 1145 if (!author && !(author = getlogin())) { 1146 if (!(pw = getpwuid(getuid()))) 1147 errx(1, "getpwuid failed"); 1148 author = pw->pw_name; 1149 } 1150 rdp->rd_author = xstrdup(author); 1151 rdp->rd_state = xstrdup(RCS_STATE_EXP); 1152 rdp->rd_log = xstrdup(msg); 1153 1154 if (date != (time_t)(-1)) 1155 now = date; 1156 else 1157 time(&now); 1158 gmtime_r(&now, &(rdp->rd_date)); 1159 1160 TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list); 1161 rf->rf_ndelta++; 1162 1163 /* not synced anymore */ 1164 rf->rf_flags &= ~RCS_SYNCED; 1165 1166 return (0); 1167 } 1168 1169 /* 1170 * rcs_rev_remove() 1171 * 1172 * Remove the revision whose number is <rev> from the RCS file <rf>. 1173 */ 1174 int 1175 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) 1176 { 1177 char *path_tmp1, *path_tmp2; 1178 struct rcs_delta *rdp, *prevrdp, *nextrdp; 1179 BUF *newdeltatext, *nextbuf, *prevbuf, *newdiff; 1180 1181 nextrdp = prevrdp = NULL; 1182 path_tmp1 = path_tmp2 = NULL; 1183 1184 if (rev == RCS_HEAD_REV) 1185 rev = rf->rf_head; 1186 1187 /* do we actually have that revision? */ 1188 if ((rdp = rcs_findrev(rf, rev)) == NULL) { 1189 rcs_errno = RCS_ERR_NOENT; 1190 return (-1); 1191 } 1192 1193 /* 1194 * This is confusing, the previous delta is next in the TAILQ list. 1195 * the next delta is the previous one in the TAILQ list. 1196 * 1197 * When the HEAD revision got specified, nextrdp will be NULL. 1198 * When the first revision got specified, prevrdp will be NULL. 1199 */ 1200 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list); 1201 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, tqh, rd_list); 1202 1203 newdeltatext = prevbuf = nextbuf = NULL; 1204 1205 if (prevrdp != NULL) { 1206 if ((prevbuf = rcs_getrev(rf, prevrdp->rd_num)) == NULL) 1207 errx(1, "error getting revision"); 1208 } 1209 1210 if (prevrdp != NULL && nextrdp != NULL) { 1211 if ((nextbuf = rcs_getrev(rf, nextrdp->rd_num)) == NULL) 1212 errx(1, "error getting revision"); 1213 1214 newdiff = buf_alloc(64); 1215 1216 /* calculate new diff */ 1217 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); 1218 buf_write_stmp(nextbuf, path_tmp1); 1219 buf_free(nextbuf); 1220 1221 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); 1222 buf_write_stmp(prevbuf, path_tmp2); 1223 buf_free(prevbuf); 1224 1225 diff_format = D_RCSDIFF; 1226 if (diffreg(path_tmp1, path_tmp2, newdiff, D_FORCEASCII) == D_ERROR) 1227 errx(1, "diffreg failed"); 1228 1229 newdeltatext = newdiff; 1230 } else if (nextrdp == NULL && prevrdp != NULL) { 1231 newdeltatext = prevbuf; 1232 } 1233 1234 if (newdeltatext != NULL) { 1235 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0) 1236 errx(1, "error setting new deltatext"); 1237 } 1238 1239 TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list); 1240 1241 /* update pointers */ 1242 if (prevrdp != NULL && nextrdp != NULL) { 1243 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0); 1244 } else if (prevrdp != NULL) { 1245 if (rcs_head_set(rf, prevrdp->rd_num) < 0) 1246 errx(1, "rcs_head_set failed"); 1247 } else if (nextrdp != NULL) { 1248 rcsnum_free(nextrdp->rd_next); 1249 nextrdp->rd_next = rcsnum_alloc(); 1250 } else { 1251 rcsnum_free(rf->rf_head); 1252 rf->rf_head = NULL; 1253 } 1254 1255 rf->rf_ndelta--; 1256 rf->rf_flags &= ~RCS_SYNCED; 1257 1258 rcs_freedelta(rdp); 1259 1260 free(path_tmp1); 1261 free(path_tmp2); 1262 1263 return (0); 1264 } 1265 1266 /* 1267 * rcs_findrev() 1268 * 1269 * Find a specific revision's delta entry in the tree of the RCS file <rfp>. 1270 * The revision number is given in <rev>. 1271 * 1272 * If the given revision is a branch number, we translate it into the latest 1273 * revision on the branch. 1274 * 1275 * Returns a pointer to the delta on success, or NULL on failure. 1276 */ 1277 struct rcs_delta * 1278 rcs_findrev(RCSFILE *rfp, RCSNUM *rev) 1279 { 1280 u_int cmplen; 1281 struct rcs_delta *rdp; 1282 RCSNUM *brev, *frev; 1283 1284 /* 1285 * We need to do more parsing if the last revision in the linked list 1286 * is greater than the requested revision. 1287 */ 1288 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1289 if (rdp == NULL || 1290 rcsnum_cmp(rdp->rd_num, rev, 0) == -1) { 1291 if (rcsparse_deltas(rfp, rev)) 1292 return (NULL); 1293 } 1294 1295 /* 1296 * Translate a branch into the latest revision on the branch itself. 1297 */ 1298 if (RCSNUM_ISBRANCH(rev)) { 1299 brev = rcsnum_brtorev(rev); 1300 frev = brev; 1301 for (;;) { 1302 rdp = rcs_findrev(rfp, frev); 1303 if (rdp == NULL) 1304 return (NULL); 1305 1306 if (rdp->rd_next->rn_len == 0) 1307 break; 1308 1309 frev = rdp->rd_next; 1310 } 1311 1312 rcsnum_free(brev); 1313 return (rdp); 1314 } 1315 1316 cmplen = rev->rn_len; 1317 1318 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1319 if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) 1320 return (rdp); 1321 } 1322 1323 return (NULL); 1324 } 1325 1326 /* 1327 * rcs_kwexp_set() 1328 * 1329 * Set the keyword expansion mode to use on the RCS file <file> to <mode>. 1330 */ 1331 void 1332 rcs_kwexp_set(RCSFILE *file, int mode) 1333 { 1334 int i; 1335 char *tmp, buf[8] = ""; 1336 1337 if (RCS_KWEXP_INVAL(mode)) 1338 return; 1339 1340 i = 0; 1341 if (mode == RCS_KWEXP_NONE) 1342 buf[0] = 'b'; 1343 else if (mode == RCS_KWEXP_OLD) 1344 buf[0] = 'o'; 1345 else { 1346 if (mode & RCS_KWEXP_NAME) 1347 buf[i++] = 'k'; 1348 if (mode & RCS_KWEXP_VAL) 1349 buf[i++] = 'v'; 1350 if (mode & RCS_KWEXP_LKR) 1351 buf[i++] = 'l'; 1352 } 1353 1354 tmp = xstrdup(buf); 1355 free(file->rf_expand); 1356 file->rf_expand = tmp; 1357 /* not synced anymore */ 1358 file->rf_flags &= ~RCS_SYNCED; 1359 } 1360 1361 /* 1362 * rcs_kwexp_get() 1363 * 1364 * Retrieve the keyword expansion mode to be used for the RCS file <file>. 1365 */ 1366 int 1367 rcs_kwexp_get(RCSFILE *file) 1368 { 1369 if (file->rf_expand == NULL) 1370 return (RCS_KWEXP_DEFAULT); 1371 1372 return (rcs_kflag_get(file->rf_expand)); 1373 } 1374 1375 /* 1376 * rcs_kflag_get() 1377 * 1378 * Get the keyword expansion mode from a set of character flags given in 1379 * <flags> and return the appropriate flag mask. In case of an error, the 1380 * returned mask will have the RCS_KWEXP_ERR bit set to 1. 1381 */ 1382 int 1383 rcs_kflag_get(const char *flags) 1384 { 1385 int fl; 1386 size_t len; 1387 const char *fp; 1388 1389 if (flags == NULL || !(len = strlen(flags))) 1390 return (RCS_KWEXP_ERR); 1391 1392 fl = 0; 1393 for (fp = flags; *fp != '\0'; fp++) { 1394 if (*fp == 'k') 1395 fl |= RCS_KWEXP_NAME; 1396 else if (*fp == 'v') 1397 fl |= RCS_KWEXP_VAL; 1398 else if (*fp == 'l') 1399 fl |= RCS_KWEXP_LKR; 1400 else if (*fp == 'o') { 1401 if (len != 1) 1402 fl |= RCS_KWEXP_ERR; 1403 fl |= RCS_KWEXP_OLD; 1404 } else if (*fp == 'b') { 1405 if (len != 1) 1406 fl |= RCS_KWEXP_ERR; 1407 fl |= RCS_KWEXP_NONE; 1408 } else /* unknown letter */ 1409 fl |= RCS_KWEXP_ERR; 1410 } 1411 1412 return (fl); 1413 } 1414 1415 /* 1416 * rcs_freedelta() 1417 * 1418 * Free the contents of a delta structure. 1419 */ 1420 static void 1421 rcs_freedelta(struct rcs_delta *rdp) 1422 { 1423 struct rcs_branch *rb; 1424 1425 rcsnum_free(rdp->rd_num); 1426 rcsnum_free(rdp->rd_next); 1427 1428 free(rdp->rd_author); 1429 free(rdp->rd_locker); 1430 free(rdp->rd_state); 1431 free(rdp->rd_log); 1432 free(rdp->rd_text); 1433 1434 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) { 1435 TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list); 1436 rcsnum_free(rb->rb_num); 1437 free(rb); 1438 } 1439 1440 free(rdp); 1441 } 1442 1443 /* 1444 * rcs_strprint() 1445 * 1446 * Output an RCS string <str> of size <slen> to the stream <stream>. Any 1447 * '@' characters are escaped. Otherwise, the string can contain arbitrary 1448 * binary data. 1449 */ 1450 static void 1451 rcs_strprint(const u_char *str, size_t slen, FILE *stream) 1452 { 1453 const u_char *ap, *ep, *sp; 1454 1455 if (slen == 0) 1456 return; 1457 1458 ep = str + slen - 1; 1459 1460 for (sp = str; sp <= ep;) { 1461 ap = memchr(sp, '@', ep - sp); 1462 if (ap == NULL) 1463 ap = ep; 1464 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream); 1465 1466 if (*ap == '@') 1467 putc('@', stream); 1468 sp = ap + 1; 1469 } 1470 } 1471 1472 /* 1473 * rcs_expand_keywords() 1474 * 1475 * Return expansion any RCS keywords in <data> 1476 * 1477 * On error, return NULL. 1478 */ 1479 static BUF * 1480 rcs_expand_keywords(char *rcsfile_in, struct rcs_delta *rdp, BUF *bp, int mode) 1481 { 1482 BUF *newbuf; 1483 u_char *c, *kw, *fin; 1484 char buf[256], *tmpf, resolved[PATH_MAX], *rcsfile; 1485 u_char *line, *line2; 1486 u_int i, j; 1487 int kwtype; 1488 int found; 1489 struct tm tb; 1490 1491 tb = rdp->rd_date; 1492 if (timezone_flag != NULL) 1493 rcs_set_tz(timezone_flag, rdp, &tb); 1494 1495 if (realpath(rcsfile_in, resolved) == NULL) 1496 rcsfile = rcsfile_in; 1497 else 1498 rcsfile = resolved; 1499 1500 newbuf = buf_alloc(buf_len(bp)); 1501 1502 /* 1503 * Keyword formats: 1504 * $Keyword$ 1505 * $Keyword: value$ 1506 */ 1507 c = buf_get(bp); 1508 fin = c + buf_len(bp); 1509 /* Copying to newbuf is deferred until the first keyword. */ 1510 found = 0; 1511 1512 while (c < fin) { 1513 kw = memchr(c, '$', fin - c); 1514 if (kw == NULL) 1515 break; 1516 ++kw; 1517 if (found) { 1518 /* Copy everything up to and including the $. */ 1519 buf_append(newbuf, c, kw - c); 1520 } 1521 c = kw; 1522 /* c points after the $ now. */ 1523 if (c == fin) 1524 break; 1525 if (!isalpha(*c)) /* all valid keywords start with a letter */ 1526 continue; 1527 1528 for (i = 0; i < RCS_NKWORDS; ++i) { 1529 size_t kwlen; 1530 1531 kwlen = strlen(rcs_expkw[i].kw_str); 1532 /* 1533 * kwlen must be less than clen since clen includes 1534 * either a terminating `$' or a `:'. 1535 */ 1536 if (c + kwlen < fin && 1537 memcmp(c , rcs_expkw[i].kw_str, kwlen) == 0 && 1538 (c[kwlen] == '$' || c[kwlen] == ':')) { 1539 c += kwlen; 1540 break; 1541 } 1542 } 1543 if (i == RCS_NKWORDS) 1544 continue; 1545 kwtype = rcs_expkw[i].kw_type; 1546 1547 /* 1548 * If the next character is ':' we need to look for an '$' 1549 * before the end of the line to be sure it is in fact a 1550 * keyword. 1551 */ 1552 if (*c == ':') { 1553 for (; c < fin; ++c) { 1554 if (*c == '$' || *c == '\n') 1555 break; 1556 } 1557 1558 if (*c != '$') { 1559 if (found) 1560 buf_append(newbuf, kw, c - kw); 1561 continue; 1562 } 1563 } 1564 ++c; 1565 1566 if (!found) { 1567 found = 1; 1568 /* Copy everything up to and including the $. */ 1569 buf_append(newbuf, buf_get(bp), kw - buf_get(bp)); 1570 } 1571 1572 if (mode & RCS_KWEXP_NAME) { 1573 buf_puts(newbuf, rcs_expkw[i].kw_str); 1574 if (mode & RCS_KWEXP_VAL) 1575 buf_puts(newbuf, ": "); 1576 } 1577 1578 /* Order matters because of RCS_KW_ID and RCS_KW_HEADER. */ 1579 if (mode & RCS_KWEXP_VAL) { 1580 if (kwtype & (RCS_KW_RCSFILE|RCS_KW_LOG)) { 1581 if ((kwtype & RCS_KW_FULLPATH) || 1582 (tmpf = strrchr(rcsfile, '/')) == NULL) 1583 buf_puts(newbuf, rcsfile); 1584 else 1585 buf_puts(newbuf, tmpf + 1); 1586 buf_putc(newbuf, ' '); 1587 } 1588 1589 if (kwtype & RCS_KW_REVISION) { 1590 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 1591 buf_puts(newbuf, buf); 1592 buf_putc(newbuf, ' '); 1593 } 1594 1595 if (kwtype & RCS_KW_DATE) { 1596 strftime(buf, sizeof(buf), 1597 "%Y/%m/%d %H:%M:%S ", &tb); 1598 buf_puts(newbuf, buf); 1599 } 1600 1601 if (kwtype & RCS_KW_AUTHOR) { 1602 buf_puts(newbuf, rdp->rd_author); 1603 buf_putc(newbuf, ' '); 1604 } 1605 1606 if (kwtype & RCS_KW_STATE) { 1607 buf_puts(newbuf, rdp->rd_state); 1608 buf_putc(newbuf, ' '); 1609 } 1610 1611 /* Order does not matter anymore below. */ 1612 if (kwtype & RCS_KW_SOURCE) { 1613 buf_puts(newbuf, rcsfile); 1614 buf_putc(newbuf, ' '); 1615 } 1616 1617 if (kwtype & RCS_KW_MDOCDATE) { 1618 strftime(buf, sizeof(buf), "%B", &tb); 1619 buf_puts(newbuf, buf); 1620 /* Only one blank before single-digit day. */ 1621 snprintf(buf, sizeof(buf), " %d", tb.tm_mday); 1622 buf_puts(newbuf, buf); 1623 strftime(buf, sizeof(buf), " %Y ", &tb); 1624 buf_puts(newbuf, buf); 1625 } 1626 1627 if (kwtype & RCS_KW_NAME) 1628 buf_putc(newbuf, ' '); 1629 1630 if ((kwtype & RCS_KW_LOCKER)) { 1631 if (rdp->rd_locker) { 1632 buf_puts(newbuf, rdp->rd_locker); 1633 buf_putc(newbuf, ' '); 1634 } 1635 } 1636 } 1637 1638 /* End the expansion. */ 1639 if (mode & RCS_KWEXP_NAME) 1640 buf_putc(newbuf, '$'); 1641 1642 if (kwtype & RCS_KW_LOG) { 1643 line = memrchr(buf_get(bp), '\n', kw - buf_get(bp) - 1); 1644 if (line == NULL) 1645 line = buf_get(bp); 1646 else 1647 ++line; 1648 line2 = kw - 1; 1649 while (line2 > line && line2[-1] == ' ') 1650 --line2; 1651 1652 buf_putc(newbuf, '\n'); 1653 buf_append(newbuf, line, kw - 1 - line); 1654 buf_puts(newbuf, "Revision "); 1655 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 1656 buf_puts(newbuf, buf); 1657 buf_puts(newbuf, " "); 1658 strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", &tb); 1659 buf_puts(newbuf, buf); 1660 1661 buf_puts(newbuf, " "); 1662 buf_puts(newbuf, rdp->rd_author); 1663 buf_putc(newbuf, '\n'); 1664 1665 for (i = 0; rdp->rd_log[i]; i += j) { 1666 j = strcspn(rdp->rd_log + i, "\n"); 1667 if (j == 0) 1668 buf_append(newbuf, line, line2 - line); 1669 else 1670 buf_append(newbuf, line, kw - 1 - line); 1671 if (rdp->rd_log[i + j]) 1672 ++j; 1673 buf_append(newbuf, rdp->rd_log + i, j); 1674 } 1675 1676 if (i > 0 && rdp->rd_log[i - 1] != '\n') 1677 buf_putc(newbuf, '\n'); 1678 1679 buf_append(newbuf, line, line2 - line); 1680 for (j = 0; c + j < fin; ++j) { 1681 if (c[j] != ' ') 1682 break; 1683 } 1684 if (c + j == fin || c[j] == '\n') 1685 c += j; 1686 } 1687 } 1688 1689 if (found) { 1690 buf_append(newbuf, c, fin - c); 1691 buf_free(bp); 1692 return (newbuf); 1693 } else { 1694 buf_free(newbuf); 1695 return (bp); 1696 } 1697 } 1698 1699 /* 1700 * rcs_deltatext_set() 1701 * 1702 * Set deltatext for <rev> in RCS file <rfp> to <dtext> 1703 * Returns -1 on error, 0 on success. 1704 */ 1705 int 1706 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp) 1707 { 1708 size_t len; 1709 u_char *dtext; 1710 struct rcs_delta *rdp; 1711 1712 /* Write operations require full parsing */ 1713 if (rcsparse_deltatexts(rfp, NULL)) 1714 return (-1); 1715 1716 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 1717 return (-1); 1718 1719 free(rdp->rd_text); 1720 1721 len = buf_len(bp); 1722 dtext = buf_release(bp); 1723 bp = NULL; 1724 1725 if (len != 0) { 1726 rdp->rd_text = xmalloc(len); 1727 rdp->rd_tlen = len; 1728 (void)memcpy(rdp->rd_text, dtext, len); 1729 } else { 1730 rdp->rd_text = NULL; 1731 rdp->rd_tlen = 0; 1732 } 1733 1734 free(dtext); 1735 1736 return (0); 1737 } 1738 1739 /* 1740 * rcs_rev_setlog() 1741 * 1742 * Sets the log message of revision <rev> to <logtext>. 1743 */ 1744 int 1745 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext) 1746 { 1747 struct rcs_delta *rdp; 1748 1749 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 1750 return (-1); 1751 1752 free(rdp->rd_log); 1753 1754 rdp->rd_log = xstrdup(logtext); 1755 rfp->rf_flags &= ~RCS_SYNCED; 1756 return (0); 1757 } 1758 /* 1759 * rcs_rev_getdate() 1760 * 1761 * Get the date corresponding to a given revision. 1762 * Returns the date on success, -1 on failure. 1763 */ 1764 time_t 1765 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev) 1766 { 1767 struct rcs_delta *rdp; 1768 1769 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 1770 return (-1); 1771 1772 return (mktime(&rdp->rd_date)); 1773 } 1774 1775 /* 1776 * rcs_state_set() 1777 * 1778 * Sets the state of revision <rev> to <state> 1779 * NOTE: default state is 'Exp'. States may not contain spaces. 1780 * 1781 * Returns -1 on failure, 0 on success. 1782 */ 1783 int 1784 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state) 1785 { 1786 struct rcs_delta *rdp; 1787 1788 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 1789 return (-1); 1790 1791 free(rdp->rd_state); 1792 1793 rdp->rd_state = xstrdup(state); 1794 1795 rfp->rf_flags &= ~RCS_SYNCED; 1796 1797 return (0); 1798 } 1799 1800 /* 1801 * rcs_state_check() 1802 * 1803 * Check if string <state> is valid. 1804 * 1805 * Returns 0 if the string is valid, -1 otherwise. 1806 */ 1807 int 1808 rcs_state_check(const char *state) 1809 { 1810 int ret; 1811 const unsigned char *cp; 1812 1813 ret = 0; 1814 cp = state; 1815 if (!isalpha(*cp++)) 1816 return (-1); 1817 1818 for (; *cp != '\0'; cp++) 1819 if (!isgraph(*cp) || (strchr(rcs_state_invch, *cp) != NULL)) { 1820 ret = -1; 1821 break; 1822 } 1823 1824 return (ret); 1825 } 1826 1827 /* 1828 * rcs_kwexp_buf() 1829 * 1830 * Do keyword expansion on a buffer if necessary 1831 * 1832 */ 1833 BUF * 1834 rcs_kwexp_buf(BUF *bp, RCSFILE *rf, RCSNUM *rev) 1835 { 1836 struct rcs_delta *rdp; 1837 int expmode; 1838 1839 /* 1840 * Do keyword expansion if required. 1841 */ 1842 expmode = rcs_kwexp_get(rf); 1843 1844 if (!(expmode & RCS_KWEXP_NONE)) { 1845 if ((rdp = rcs_findrev(rf, rev)) == NULL) 1846 errx(1, "could not fetch revision"); 1847 return (rcs_expand_keywords(rf->rf_path, rdp, bp, expmode)); 1848 } 1849 return (bp); 1850 } 1851