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