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