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