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