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