1 /* $OpenBSD: rcs.c,v 1.53 2009/02/25 23:16:20 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 */ 83 struct 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 */ 102 static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; 103 104 /* comment leaders, depending on the file's suffix */ 105 static 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 173 struct 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 189 static 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 216 static 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 227 int rcs_errno = RCS_ERR_NOERR; 228 char *timezone_flag = NULL; 229 230 int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *); 231 static int rcs_movefile(char *, char *, mode_t, u_int); 232 static void rcs_parse_init(RCSFILE *); 233 static int rcs_parse_admin(RCSFILE *); 234 static int rcs_parse_delta(RCSFILE *); 235 static void rcs_parse_deltas(RCSFILE *, RCSNUM *); 236 static int rcs_parse_deltatext(RCSFILE *); 237 static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *); 238 static void rcs_parse_desc(RCSFILE *, RCSNUM *); 239 240 static int rcs_parse_access(RCSFILE *); 241 static int rcs_parse_symbols(RCSFILE *); 242 static int rcs_parse_locks(RCSFILE *); 243 static int rcs_parse_branches(RCSFILE *, struct rcs_delta *); 244 static void rcs_freedelta(struct rcs_delta *); 245 static void rcs_freepdata(struct rcs_pdata *); 246 static int rcs_gettok(RCSFILE *); 247 static int rcs_pushtok(RCSFILE *, const char *, int); 248 static void rcs_growbuf(RCSFILE *); 249 static void rcs_strprint(const u_char *, size_t, FILE *); 250 251 static BUF *rcs_expand_keywords(char *, struct rcs_delta *, BUF *, int); 252 253 RCSFILE * 254 rcs_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 */ 307 void 308 rcs_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 */ 371 void 372 rcs_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 (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 */ 527 static int 528 rcs_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 589 out: 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 */ 600 const RCSNUM * 601 rcs_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 */ 612 int 613 rcs_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 */ 634 const RCSNUM * 635 rcs_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 */ 646 int 647 rcs_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 */ 663 int 664 rcs_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 */ 692 int 693 rcs_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 */ 722 int 723 rcs_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 */ 760 int 761 rcs_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 */ 797 RCSNUM * 798 rcs_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 */ 829 int 830 rcs_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 */ 854 int 855 rcs_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 */ 867 int 868 rcs_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 */ 890 int 891 rcs_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 */ 923 int 924 rcs_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 */ 954 const char * 955 rcs_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 */ 965 void 966 rcs_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 */ 983 const char * 984 rcs_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 */ 1006 const char * 1007 rcs_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 */ 1017 void 1018 rcs_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 */ 1035 RCSNUM * 1036 rcs_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 1047 int 1048 rcs_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 */ 1150 BUF* 1151 rcs_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're going to need up the the description parsed */ 1179 rcs_parse_desc(rfp, NULL); 1180 1181 rdp = rcs_findrev(rfp, rfp->rf_head); 1182 if (rdp == NULL) { 1183 warnx("failed to get RCS HEAD revision"); 1184 return (NULL); 1185 } 1186 1187 if (rdp->rd_tlen == 0) 1188 rcs_parse_deltatexts(rfp, rfp->rf_head); 1189 1190 len = rdp->rd_tlen; 1191 if (len == 0) { 1192 rbuf = rcs_buf_alloc(1, 0); 1193 rcs_buf_empty(rbuf); 1194 return (rbuf); 1195 } 1196 1197 rbuf = rcs_buf_alloc(len, BUF_AUTOEXT); 1198 rcs_buf_append(rbuf, rdp->rd_text, len); 1199 1200 isbranch = 0; 1201 brev = NULL; 1202 1203 /* 1204 * If a branch was passed, get the latest revision on it. 1205 */ 1206 if (RCSNUM_ISBRANCH(rev)) { 1207 brev = rev; 1208 rdp = rcs_findrev(rfp, rev); 1209 if (rdp == NULL) 1210 return (NULL); 1211 1212 rev = rdp->rd_num; 1213 } else { 1214 if (RCSNUM_ISBRANCHREV(rev)) { 1215 brev = rcsnum_revtobr(rev); 1216 isbranch = 1; 1217 } 1218 } 1219 1220 lookonbranch = 0; 1221 crev = NULL; 1222 1223 /* Apply patches backwards to get the right version. 1224 */ 1225 do { 1226 found = 0; 1227 1228 if (rcsnum_cmp(rfp->rf_head, rev, 0) == 0) 1229 break; 1230 1231 if (isbranch == 1 && rdp->rd_num->rn_len < rev->rn_len && 1232 !TAILQ_EMPTY(&(rdp->rd_branches))) 1233 lookonbranch = 1; 1234 1235 if (isbranch && lookonbranch == 1) { 1236 lookonbranch = 0; 1237 TAILQ_FOREACH(rb, &(rdp->rd_branches), rb_list) { 1238 /* XXX rcsnum_cmp() is totally broken for 1239 * this purpose. 1240 */ 1241 numlen = MIN(brev->rn_len, 1242 rb->rb_num->rn_len - 1); 1243 for (i = 0; i < numlen; i++) { 1244 if (rb->rb_num->rn_id[i] != 1245 brev->rn_id[i]) 1246 break; 1247 } 1248 1249 if (i == numlen) { 1250 crev = rb->rb_num; 1251 found = 1; 1252 break; 1253 } 1254 } 1255 if (found == 0) 1256 crev = rdp->rd_next; 1257 } else { 1258 crev = rdp->rd_next; 1259 } 1260 1261 rdp = rcs_findrev(rfp, crev); 1262 if (rdp == NULL) { 1263 rcs_buf_free(rbuf); 1264 return (NULL); 1265 } 1266 1267 plen = rdp->rd_tlen; 1268 dlen = rcs_buf_len(rbuf); 1269 patch = rdp->rd_text; 1270 data = rcs_buf_release(rbuf); 1271 /* check if we have parsed this rev's deltatext */ 1272 if (rdp->rd_tlen == 0) 1273 rcs_parse_deltatexts(rfp, rdp->rd_num); 1274 1275 rbuf = rcs_patchfile(data, dlen, patch, plen, rcs_patch_lines); 1276 1277 if (rbuf == NULL) 1278 break; 1279 } while (rcsnum_cmp(crev, rev, 0) != 0); 1280 1281 return (rbuf); 1282 } 1283 1284 void 1285 rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved) 1286 { 1287 struct rcs_lines *plines; 1288 struct rcs_line *lp; 1289 int added, i, lineno, nbln, removed; 1290 char op, *ep; 1291 u_char tmp; 1292 1293 added = removed = 0; 1294 1295 plines = rcs_splitlines(rdp->rd_text, rdp->rd_tlen); 1296 lp = TAILQ_FIRST(&(plines->l_lines)); 1297 1298 /* skip first bogus line */ 1299 for (lp = TAILQ_NEXT(lp, l_list); lp != NULL; 1300 lp = TAILQ_NEXT(lp, l_list)) { 1301 if (lp->l_len < 2) 1302 errx(1, 1303 "line too short, RCS patch seems broken"); 1304 op = *(lp->l_line); 1305 /* NUL-terminate line buffer for strtol() safety. */ 1306 tmp = lp->l_line[lp->l_len - 1]; 1307 lp->l_line[lp->l_len - 1] = '\0'; 1308 lineno = (int)strtol((lp->l_line + 1), &ep, 10); 1309 ep++; 1310 nbln = (int)strtol(ep, &ep, 10); 1311 /* Restore the last byte of the buffer */ 1312 lp->l_line[lp->l_len - 1] = tmp; 1313 if (nbln < 0) 1314 errx(1, "invalid line number specification " 1315 "in RCS patch"); 1316 1317 if (op == 'a') { 1318 added += nbln; 1319 for (i = 0; i < nbln; i++) { 1320 lp = TAILQ_NEXT(lp, l_list); 1321 if (lp == NULL) 1322 errx(1, "truncated RCS patch"); 1323 } 1324 } else if (op == 'd') 1325 removed += nbln; 1326 else 1327 errx(1, "unknown RCS patch operation '%c'", op); 1328 } 1329 1330 rcs_freelines(plines); 1331 1332 *ladded = added; 1333 *lremoved = removed; 1334 } 1335 1336 1337 /* 1338 * rcs_rev_add() 1339 * 1340 * Add a revision to the RCS file <rf>. The new revision's number can be 1341 * specified in <rev> (which can also be RCS_HEAD_REV, in which case the 1342 * new revision will have a number equal to the previous head revision plus 1343 * one). The <msg> argument specifies the log message for that revision, and 1344 * <date> specifies the revision's date (a value of -1 is 1345 * equivalent to using the current time). 1346 * If <username> is NULL, set the author for this revision to the current user. 1347 * Otherwise, set it to <username>. 1348 * Returns 0 on success, or -1 on failure. 1349 */ 1350 int 1351 rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date, 1352 const char *username) 1353 { 1354 time_t now; 1355 struct passwd *pw; 1356 struct rcs_delta *ordp, *rdp; 1357 uid_t uid; 1358 1359 if (rev == RCS_HEAD_REV) { 1360 if (rf->rf_flags & RCS_CREATE) { 1361 if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL) 1362 return (-1); 1363 rf->rf_head = rcsnum_alloc(); 1364 rcsnum_cpy(rev, rf->rf_head, 0); 1365 } else { 1366 rev = rcsnum_inc(rf->rf_head); 1367 } 1368 } else { 1369 if ((rdp = rcs_findrev(rf, rev)) != NULL) { 1370 rcs_errno = RCS_ERR_DUPENT; 1371 return (-1); 1372 } 1373 } 1374 1375 uid = getuid(); 1376 if ((pw = getpwuid(uid)) == NULL) 1377 errx(1, "getpwuid failed"); 1378 1379 rdp = xcalloc(1, sizeof(*rdp)); 1380 1381 TAILQ_INIT(&(rdp->rd_branches)); 1382 1383 rdp->rd_num = rcsnum_alloc(); 1384 rcsnum_cpy(rev, rdp->rd_num, 0); 1385 1386 rdp->rd_next = rcsnum_alloc(); 1387 1388 if (!(rf->rf_flags & RCS_CREATE)) { 1389 /* next should point to the previous HEAD */ 1390 ordp = TAILQ_FIRST(&(rf->rf_delta)); 1391 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0); 1392 } 1393 1394 if (uid == 0) 1395 username = getlogin(); 1396 if (username == NULL || *username == '\0') 1397 username = pw->pw_name; 1398 1399 rdp->rd_author = xstrdup(username); 1400 rdp->rd_state = xstrdup(RCS_STATE_EXP); 1401 rdp->rd_log = xstrdup(msg); 1402 1403 if (date != (time_t)(-1)) 1404 now = date; 1405 else 1406 time(&now); 1407 gmtime_r(&now, &(rdp->rd_date)); 1408 1409 TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list); 1410 rf->rf_ndelta++; 1411 1412 /* not synced anymore */ 1413 rf->rf_flags &= ~RCS_SYNCED; 1414 1415 return (0); 1416 } 1417 1418 /* 1419 * rcs_rev_remove() 1420 * 1421 * Remove the revision whose number is <rev> from the RCS file <rf>. 1422 */ 1423 int 1424 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) 1425 { 1426 char *path_tmp1, *path_tmp2; 1427 struct rcs_delta *rdp, *prevrdp, *nextrdp; 1428 BUF *newdeltatext, *nextbuf, *prevbuf, *newdiff; 1429 1430 nextrdp = prevrdp = NULL; 1431 path_tmp1 = path_tmp2 = NULL; 1432 1433 if (rev == RCS_HEAD_REV) 1434 rev = rf->rf_head; 1435 1436 /* do we actually have that revision? */ 1437 if ((rdp = rcs_findrev(rf, rev)) == NULL) { 1438 rcs_errno = RCS_ERR_NOENT; 1439 return (-1); 1440 } 1441 1442 /* 1443 * This is confusing, the previous delta is next in the TAILQ list. 1444 * the next delta is the previous one in the TAILQ list. 1445 * 1446 * When the HEAD revision got specified, nextrdp will be NULL. 1447 * When the first revision got specified, prevrdp will be NULL. 1448 */ 1449 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list); 1450 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, rcs_tqh, rd_list); 1451 1452 newdeltatext = prevbuf = nextbuf = NULL; 1453 1454 if (prevrdp != NULL) { 1455 if ((prevbuf = rcs_getrev(rf, prevrdp->rd_num)) == NULL) 1456 errx(1, "error getting revision"); 1457 } 1458 1459 if (prevrdp != NULL && nextrdp != NULL) { 1460 if ((nextbuf = rcs_getrev(rf, nextrdp->rd_num)) == NULL) 1461 errx(1, "error getting revision"); 1462 1463 newdiff = rcs_buf_alloc(64, BUF_AUTOEXT); 1464 1465 /* calculate new diff */ 1466 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); 1467 rcs_buf_write_stmp(nextbuf, path_tmp1); 1468 rcs_buf_free(nextbuf); 1469 1470 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); 1471 rcs_buf_write_stmp(prevbuf, path_tmp2); 1472 rcs_buf_free(prevbuf); 1473 1474 diff_format = D_RCSDIFF; 1475 if (diffreg(path_tmp1, path_tmp2, newdiff, D_FORCEASCII) == D_ERROR) 1476 errx(1, "diffreg failed"); 1477 1478 newdeltatext = newdiff; 1479 } else if (nextrdp == NULL && prevrdp != NULL) { 1480 newdeltatext = prevbuf; 1481 } 1482 1483 if (newdeltatext != NULL) { 1484 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0) 1485 errx(1, "error setting new deltatext"); 1486 } 1487 1488 TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list); 1489 1490 /* update pointers */ 1491 if (prevrdp != NULL && nextrdp != NULL) { 1492 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0); 1493 } else if (prevrdp != NULL) { 1494 if (rcs_head_set(rf, prevrdp->rd_num) < 0) 1495 errx(1, "rcs_head_set failed"); 1496 } else if (nextrdp != NULL) { 1497 rcsnum_free(nextrdp->rd_next); 1498 nextrdp->rd_next = rcsnum_alloc(); 1499 } else { 1500 rcsnum_free(rf->rf_head); 1501 rf->rf_head = NULL; 1502 } 1503 1504 rf->rf_ndelta--; 1505 rf->rf_flags &= ~RCS_SYNCED; 1506 1507 rcs_freedelta(rdp); 1508 1509 if (path_tmp1 != NULL) 1510 xfree(path_tmp1); 1511 if (path_tmp2 != NULL) 1512 xfree(path_tmp2); 1513 1514 return (0); 1515 } 1516 1517 /* 1518 * rcs_findrev() 1519 * 1520 * Find a specific revision's delta entry in the tree of the RCS file <rfp>. 1521 * The revision number is given in <rev>. 1522 * 1523 * If the given revision is a branch number, we translate it into the latest 1524 * revision on the branch. 1525 * 1526 * Returns a pointer to the delta on success, or NULL on failure. 1527 */ 1528 struct rcs_delta * 1529 rcs_findrev(RCSFILE *rfp, RCSNUM *rev) 1530 { 1531 u_int cmplen; 1532 struct rcs_delta *rdp; 1533 RCSNUM *brev, *frev; 1534 1535 /* 1536 * We need to do more parsing if the last revision in the linked list 1537 * is greater than the requested revision. 1538 */ 1539 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1540 if (rdp == NULL || 1541 rcsnum_cmp(rdp->rd_num, rev, 0) == -1) { 1542 rcs_parse_deltas(rfp, rev); 1543 } 1544 1545 /* 1546 * Translate a branch into the latest revision on the branch itself. 1547 */ 1548 if (RCSNUM_ISBRANCH(rev)) { 1549 brev = rcsnum_brtorev(rev); 1550 frev = brev; 1551 for (;;) { 1552 rdp = rcs_findrev(rfp, frev); 1553 if (rdp == NULL) 1554 return (NULL); 1555 1556 if (rdp->rd_next->rn_len == 0) 1557 break; 1558 1559 frev = rdp->rd_next; 1560 } 1561 1562 rcsnum_free(brev); 1563 return (rdp); 1564 } 1565 1566 cmplen = rev->rn_len; 1567 1568 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1569 if (rcsnum_cmp(rdp->rd_num, rev, cmplen) == 0) 1570 return (rdp); 1571 } 1572 1573 return (NULL); 1574 } 1575 1576 /* 1577 * rcs_kwexp_set() 1578 * 1579 * Set the keyword expansion mode to use on the RCS file <file> to <mode>. 1580 */ 1581 void 1582 rcs_kwexp_set(RCSFILE *file, int mode) 1583 { 1584 int i; 1585 char *tmp, buf[8] = ""; 1586 1587 if (RCS_KWEXP_INVAL(mode)) 1588 return; 1589 1590 i = 0; 1591 if (mode == RCS_KWEXP_NONE) 1592 buf[0] = 'b'; 1593 else if (mode == RCS_KWEXP_OLD) 1594 buf[0] = 'o'; 1595 else { 1596 if (mode & RCS_KWEXP_NAME) 1597 buf[i++] = 'k'; 1598 if (mode & RCS_KWEXP_VAL) 1599 buf[i++] = 'v'; 1600 if (mode & RCS_KWEXP_LKR) 1601 buf[i++] = 'l'; 1602 } 1603 1604 tmp = xstrdup(buf); 1605 if (file->rf_expand != NULL) 1606 xfree(file->rf_expand); 1607 file->rf_expand = tmp; 1608 /* not synced anymore */ 1609 file->rf_flags &= ~RCS_SYNCED; 1610 } 1611 1612 /* 1613 * rcs_kwexp_get() 1614 * 1615 * Retrieve the keyword expansion mode to be used for the RCS file <file>. 1616 */ 1617 int 1618 rcs_kwexp_get(RCSFILE *file) 1619 { 1620 return rcs_kflag_get(file->rf_expand); 1621 } 1622 1623 /* 1624 * rcs_kflag_get() 1625 * 1626 * Get the keyword expansion mode from a set of character flags given in 1627 * <flags> and return the appropriate flag mask. In case of an error, the 1628 * returned mask will have the RCS_KWEXP_ERR bit set to 1. 1629 */ 1630 int 1631 rcs_kflag_get(const char *flags) 1632 { 1633 int fl; 1634 size_t len; 1635 const char *fp; 1636 1637 fl = 0; 1638 len = strlen(flags); 1639 1640 for (fp = flags; *fp != '\0'; fp++) { 1641 if (*fp == 'k') 1642 fl |= RCS_KWEXP_NAME; 1643 else if (*fp == 'v') 1644 fl |= RCS_KWEXP_VAL; 1645 else if (*fp == 'l') 1646 fl |= RCS_KWEXP_LKR; 1647 else if (*fp == 'o') { 1648 if (len != 1) 1649 fl |= RCS_KWEXP_ERR; 1650 fl |= RCS_KWEXP_OLD; 1651 } else if (*fp == 'b') { 1652 if (len != 1) 1653 fl |= RCS_KWEXP_ERR; 1654 } else /* unknown letter */ 1655 fl |= RCS_KWEXP_ERR; 1656 } 1657 1658 return (fl); 1659 } 1660 1661 /* 1662 * rcs_errstr() 1663 * 1664 * Get the error string matching the RCS error code <code>. 1665 */ 1666 const char * 1667 rcs_errstr(int code) 1668 { 1669 const char *esp; 1670 1671 if (code < 0 || (code >= (int)RCS_NERR && code != RCS_ERR_ERRNO)) 1672 esp = NULL; 1673 else if (code == RCS_ERR_ERRNO) 1674 esp = strerror(errno); 1675 else 1676 esp = rcs_errstrs[code]; 1677 return (esp); 1678 } 1679 1680 /* rcs_parse_deltas() 1681 * 1682 * Parse deltas. If <rev> is not NULL, parse only as far as that 1683 * revision. If <rev> is NULL, parse all deltas. 1684 */ 1685 static void 1686 rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev) 1687 { 1688 int ret; 1689 struct rcs_delta *enddelta; 1690 1691 if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE)) 1692 return; 1693 1694 for (;;) { 1695 ret = rcs_parse_delta(rfp); 1696 if (rev != NULL) { 1697 enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1698 if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0) 1699 break; 1700 } 1701 if (ret == 0) { 1702 rfp->rf_flags |= PARSED_DELTAS; 1703 break; 1704 } 1705 else if (ret == -1) 1706 errx(1, "error parsing deltas"); 1707 } 1708 } 1709 1710 /* rcs_parse_deltatexts() 1711 * 1712 * Parse deltatexts. If <rev> is not NULL, parse only as far as that 1713 * revision. If <rev> is NULL, parse everything. 1714 */ 1715 static void 1716 rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev) 1717 { 1718 int ret; 1719 struct rcs_delta *rdp; 1720 1721 if ((rfp->rf_flags & PARSED_DELTATEXTS) || 1722 (rfp->rf_flags & RCS_CREATE)) 1723 return; 1724 1725 if (!(rfp->rf_flags & PARSED_DESC)) 1726 rcs_parse_desc(rfp, rev); 1727 for (;;) { 1728 if (rev != NULL) { 1729 rdp = rcs_findrev(rfp, rev); 1730 if (rdp->rd_text != NULL) 1731 break; 1732 else 1733 ret = rcs_parse_deltatext(rfp); 1734 } else 1735 ret = rcs_parse_deltatext(rfp); 1736 if (ret == 0) { 1737 rfp->rf_flags |= PARSED_DELTATEXTS; 1738 break; 1739 } 1740 else if (ret == -1) 1741 errx(1, "problem parsing deltatexts"); 1742 } 1743 } 1744 1745 /* rcs_parse_desc() 1746 * 1747 * Parse RCS description. 1748 */ 1749 static void 1750 rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev) 1751 { 1752 int ret = 0; 1753 1754 if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE)) 1755 return; 1756 if (!(rfp->rf_flags & PARSED_DELTAS)) 1757 rcs_parse_deltas(rfp, rev); 1758 /* do parsing */ 1759 ret = rcs_gettok(rfp); 1760 if (ret != RCS_TOK_DESC) 1761 errx(1, "token `%s' found where RCS desc expected", 1762 RCS_TOKSTR(rfp)); 1763 1764 ret = rcs_gettok(rfp); 1765 if (ret != RCS_TOK_STRING) 1766 errx(1, "token `%s' found where RCS desc expected", 1767 RCS_TOKSTR(rfp)); 1768 1769 rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp)); 1770 rfp->rf_flags |= PARSED_DESC; 1771 } 1772 1773 /* 1774 * rcs_parse_init() 1775 * 1776 * Initial parsing of file <path>, which are in the RCS format. 1777 * Just does admin section. 1778 */ 1779 static void 1780 rcs_parse_init(RCSFILE *rfp) 1781 { 1782 struct rcs_pdata *pdp; 1783 1784 if (rfp->rf_flags & RCS_PARSED) 1785 return; 1786 1787 pdp = xcalloc(1, sizeof(*pdp)); 1788 1789 pdp->rp_lines = 0; 1790 pdp->rp_pttype = RCS_TOK_ERR; 1791 1792 if ((pdp->rp_file = fdopen(rfp->rf_fd, "r")) == NULL) 1793 err(1, "fdopen: `%s'", rfp->rf_path); 1794 1795 pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); 1796 pdp->rp_blen = RCS_BUFSIZE; 1797 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 1798 1799 /* ditch the strict lock */ 1800 rfp->rf_flags &= ~RCS_SLOCK; 1801 rfp->rf_pdata = pdp; 1802 1803 if (rcs_parse_admin(rfp) < 0) { 1804 rcs_freepdata(pdp); 1805 errx(1, "could not parse admin data"); 1806 } 1807 1808 if (rfp->rf_flags & RCS_PARSE_FULLY) 1809 rcs_parse_deltatexts(rfp, NULL); 1810 1811 rfp->rf_flags |= RCS_SYNCED; 1812 } 1813 1814 /* 1815 * rcs_parse_admin() 1816 * 1817 * Parse the administrative portion of an RCS file. 1818 * Returns the type of the first token found after the admin section on 1819 * success, or -1 on failure. 1820 */ 1821 static int 1822 rcs_parse_admin(RCSFILE *rfp) 1823 { 1824 u_int i; 1825 int tok, ntok, hmask; 1826 struct rcs_key *rk; 1827 1828 /* hmask is a mask of the headers already encountered */ 1829 hmask = 0; 1830 for (;;) { 1831 tok = rcs_gettok(rfp); 1832 if (tok == RCS_TOK_ERR) { 1833 rcs_errno = RCS_ERR_PARSE; 1834 warnx("parse error in RCS admin section"); 1835 goto fail; 1836 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1837 /* 1838 * Assume this is the start of the first delta or 1839 * that we are dealing with an empty RCS file and 1840 * we just found the description. 1841 */ 1842 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1843 return (tok); 1844 } 1845 1846 rk = NULL; 1847 for (i = 0; i < RCS_NKEYS; i++) 1848 if (rcs_keys[i].rk_id == tok) 1849 rk = &(rcs_keys[i]); 1850 1851 if (hmask & (1 << tok)) { 1852 rcs_errno = RCS_ERR_PARSE; 1853 warnx("duplicate RCS key"); 1854 goto fail; 1855 } 1856 hmask |= (1 << tok); 1857 1858 switch (tok) { 1859 case RCS_TOK_HEAD: 1860 case RCS_TOK_BRANCH: 1861 case RCS_TOK_COMMENT: 1862 case RCS_TOK_EXPAND: 1863 ntok = rcs_gettok(rfp); 1864 if (ntok == RCS_TOK_SCOLON) 1865 break; 1866 if (ntok != rk->rk_val) { 1867 rcs_errno = RCS_ERR_PARSE; 1868 warnx("invalid value type for RCS key `%s'", 1869 rk->rk_str); 1870 } 1871 1872 if (tok == RCS_TOK_HEAD) { 1873 if (rfp->rf_head == NULL) 1874 rfp->rf_head = rcsnum_alloc(); 1875 rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1876 rfp->rf_head); 1877 } else if (tok == RCS_TOK_BRANCH) { 1878 if (rfp->rf_branch == NULL) 1879 rfp->rf_branch = rcsnum_alloc(); 1880 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1881 rfp->rf_branch) < 0) 1882 goto fail; 1883 } else if (tok == RCS_TOK_COMMENT) { 1884 rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp)); 1885 } else if (tok == RCS_TOK_EXPAND) { 1886 rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp)); 1887 } 1888 1889 /* now get the expected semi-colon */ 1890 ntok = rcs_gettok(rfp); 1891 if (ntok != RCS_TOK_SCOLON) { 1892 rcs_errno = RCS_ERR_PARSE; 1893 warnx("missing semi-colon after RCS `%s' key", 1894 rk->rk_str); 1895 goto fail; 1896 } 1897 break; 1898 case RCS_TOK_ACCESS: 1899 if (rcs_parse_access(rfp) < 0) 1900 goto fail; 1901 break; 1902 case RCS_TOK_SYMBOLS: 1903 if (rcs_parse_symbols(rfp) < 0) 1904 goto fail; 1905 break; 1906 case RCS_TOK_LOCKS: 1907 if (rcs_parse_locks(rfp) < 0) 1908 goto fail; 1909 break; 1910 default: 1911 rcs_errno = RCS_ERR_PARSE; 1912 warnx("unexpected token `%s' in RCS admin section", 1913 RCS_TOKSTR(rfp)); 1914 goto fail; 1915 } 1916 } 1917 1918 fail: 1919 return (-1); 1920 } 1921 1922 /* 1923 * rcs_parse_delta() 1924 * 1925 * Parse an RCS delta section and allocate the structure to store that delta's 1926 * information in the <rfp> delta list. 1927 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1928 * -1 on error. 1929 */ 1930 static int 1931 rcs_parse_delta(RCSFILE *rfp) 1932 { 1933 int ret, tok, ntok, hmask; 1934 u_int i; 1935 char *tokstr; 1936 RCSNUM *datenum; 1937 struct rcs_delta *rdp; 1938 struct rcs_key *rk; 1939 1940 tok = rcs_gettok(rfp); 1941 if (tok == RCS_TOK_DESC) { 1942 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1943 return (0); 1944 } else if (tok != RCS_TOK_NUM) { 1945 rcs_errno = RCS_ERR_PARSE; 1946 warnx("unexpected token `%s' at start of delta", 1947 RCS_TOKSTR(rfp)); 1948 return (-1); 1949 } 1950 1951 rdp = xcalloc(1, sizeof(*rdp)); 1952 1953 rdp->rd_num = rcsnum_alloc(); 1954 rdp->rd_next = rcsnum_alloc(); 1955 1956 TAILQ_INIT(&(rdp->rd_branches)); 1957 1958 rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num); 1959 1960 hmask = 0; 1961 ret = 0; 1962 tokstr = NULL; 1963 1964 for (;;) { 1965 tok = rcs_gettok(rfp); 1966 if (tok == RCS_TOK_ERR) { 1967 rcs_errno = RCS_ERR_PARSE; 1968 warnx("parse error in RCS delta section"); 1969 rcs_freedelta(rdp); 1970 return (-1); 1971 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1972 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1973 ret = (tok == RCS_TOK_NUM ? 1 : 0); 1974 break; 1975 } 1976 1977 rk = NULL; 1978 for (i = 0; i < RCS_NKEYS; i++) 1979 if (rcs_keys[i].rk_id == tok) 1980 rk = &(rcs_keys[i]); 1981 1982 if (hmask & (1 << tok)) { 1983 rcs_errno = RCS_ERR_PARSE; 1984 warnx("duplicate RCS key"); 1985 rcs_freedelta(rdp); 1986 return (-1); 1987 } 1988 hmask |= (1 << tok); 1989 1990 switch (tok) { 1991 case RCS_TOK_DATE: 1992 case RCS_TOK_AUTHOR: 1993 case RCS_TOK_STATE: 1994 case RCS_TOK_NEXT: 1995 case RCS_TOK_COMMITID: 1996 ntok = rcs_gettok(rfp); 1997 if (ntok == RCS_TOK_SCOLON) { 1998 if (rk->rk_flags & RCS_VOPT) 1999 break; 2000 else { 2001 rcs_errno = RCS_ERR_PARSE; 2002 warnx("missing mandatory " 2003 "value to RCS key `%s'", 2004 rk->rk_str); 2005 rcs_freedelta(rdp); 2006 return (-1); 2007 } 2008 } 2009 2010 if (ntok != rk->rk_val) { 2011 rcs_errno = RCS_ERR_PARSE; 2012 warnx("invalid value type for RCS key `%s'", 2013 rk->rk_str); 2014 rcs_freedelta(rdp); 2015 return (-1); 2016 } 2017 2018 if (tokstr != NULL) 2019 xfree(tokstr); 2020 tokstr = xstrdup(RCS_TOKSTR(rfp)); 2021 /* now get the expected semi-colon */ 2022 ntok = rcs_gettok(rfp); 2023 if (ntok != RCS_TOK_SCOLON) { 2024 rcs_errno = RCS_ERR_PARSE; 2025 warnx("missing semi-colon after RCS `%s' key", 2026 rk->rk_str); 2027 xfree(tokstr); 2028 rcs_freedelta(rdp); 2029 return (-1); 2030 } 2031 2032 if (tok == RCS_TOK_DATE) { 2033 if ((datenum = rcsnum_parse(tokstr)) == NULL) { 2034 xfree(tokstr); 2035 rcs_freedelta(rdp); 2036 return (-1); 2037 } 2038 if (datenum->rn_len != 6) { 2039 rcs_errno = RCS_ERR_PARSE; 2040 warnx("RCS date specification has %s " 2041 "fields", 2042 (datenum->rn_len > 6) ? "too many" : 2043 "missing"); 2044 xfree(tokstr); 2045 rcs_freedelta(rdp); 2046 rcsnum_free(datenum); 2047 return (-1); 2048 } 2049 rdp->rd_date.tm_year = datenum->rn_id[0]; 2050 if (rdp->rd_date.tm_year >= 1900) 2051 rdp->rd_date.tm_year -= 1900; 2052 rdp->rd_date.tm_mon = datenum->rn_id[1] - 1; 2053 rdp->rd_date.tm_mday = datenum->rn_id[2]; 2054 rdp->rd_date.tm_hour = datenum->rn_id[3]; 2055 rdp->rd_date.tm_min = datenum->rn_id[4]; 2056 rdp->rd_date.tm_sec = datenum->rn_id[5]; 2057 rcsnum_free(datenum); 2058 } else if (tok == RCS_TOK_AUTHOR) { 2059 rdp->rd_author = tokstr; 2060 tokstr = NULL; 2061 } else if (tok == RCS_TOK_STATE) { 2062 rdp->rd_state = tokstr; 2063 tokstr = NULL; 2064 } else if (tok == RCS_TOK_NEXT) { 2065 rcsnum_aton(tokstr, NULL, rdp->rd_next); 2066 } else if (tok == RCS_TOK_COMMITID) { 2067 /* XXX just parse it, no action yet */ 2068 } 2069 break; 2070 case RCS_TOK_BRANCHES: 2071 if (rcs_parse_branches(rfp, rdp) < 0) { 2072 rcs_freedelta(rdp); 2073 return (-1); 2074 } 2075 break; 2076 default: 2077 rcs_errno = RCS_ERR_PARSE; 2078 warnx("unexpected token `%s' in RCS delta", 2079 RCS_TOKSTR(rfp)); 2080 rcs_freedelta(rdp); 2081 return (-1); 2082 } 2083 } 2084 2085 if (tokstr != NULL) 2086 xfree(tokstr); 2087 2088 TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); 2089 rfp->rf_ndelta++; 2090 2091 return (ret); 2092 } 2093 2094 /* 2095 * rcs_parse_deltatext() 2096 * 2097 * Parse an RCS delta text section and fill in the log and text field of the 2098 * appropriate delta section. 2099 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 2100 * -1 on error. 2101 */ 2102 static int 2103 rcs_parse_deltatext(RCSFILE *rfp) 2104 { 2105 int tok; 2106 RCSNUM *tnum; 2107 struct rcs_delta *rdp; 2108 2109 tok = rcs_gettok(rfp); 2110 if (tok == RCS_TOK_EOF) 2111 return (0); 2112 2113 if (tok != RCS_TOK_NUM) { 2114 rcs_errno = RCS_ERR_PARSE; 2115 warnx("unexpected token `%s' at start of RCS delta text", 2116 RCS_TOKSTR(rfp)); 2117 return (-1); 2118 } 2119 2120 tnum = rcsnum_alloc(); 2121 rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum); 2122 2123 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 2124 if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0) 2125 break; 2126 } 2127 rcsnum_free(tnum); 2128 2129 if (rdp == NULL) { 2130 warnx("RCS delta text `%s' has no matching delta", 2131 RCS_TOKSTR(rfp)); 2132 return (-1); 2133 } 2134 2135 tok = rcs_gettok(rfp); 2136 if (tok != RCS_TOK_LOG) { 2137 rcs_errno = RCS_ERR_PARSE; 2138 warnx("unexpected token `%s' where RCS log expected", 2139 RCS_TOKSTR(rfp)); 2140 return (-1); 2141 } 2142 2143 tok = rcs_gettok(rfp); 2144 if (tok != RCS_TOK_STRING) { 2145 rcs_errno = RCS_ERR_PARSE; 2146 warnx("unexpected token `%s' where RCS log expected", 2147 RCS_TOKSTR(rfp)); 2148 return (-1); 2149 } 2150 rdp->rd_log = xstrdup(RCS_TOKSTR(rfp)); 2151 tok = rcs_gettok(rfp); 2152 if (tok != RCS_TOK_TEXT) { 2153 rcs_errno = RCS_ERR_PARSE; 2154 warnx("unexpected token `%s' where RCS text expected", 2155 RCS_TOKSTR(rfp)); 2156 return (-1); 2157 } 2158 2159 tok = rcs_gettok(rfp); 2160 if (tok != RCS_TOK_STRING) { 2161 rcs_errno = RCS_ERR_PARSE; 2162 warnx("unexpected token `%s' where RCS text expected", 2163 RCS_TOKSTR(rfp)); 2164 return (-1); 2165 } 2166 2167 if (RCS_TOKLEN(rfp) == 0) { 2168 rdp->rd_text = xmalloc(1); 2169 rdp->rd_text[0] = '\0'; 2170 rdp->rd_tlen = 0; 2171 } else { 2172 rdp->rd_text = xmalloc(RCS_TOKLEN(rfp)); 2173 memcpy(rdp->rd_text, RCS_TOKSTR(rfp), (RCS_TOKLEN(rfp))); 2174 rdp->rd_tlen = RCS_TOKLEN(rfp); 2175 } 2176 2177 return (1); 2178 } 2179 2180 /* 2181 * rcs_parse_access() 2182 * 2183 * Parse the access list given as value to the `access' keyword. 2184 * Returns 0 on success, or -1 on failure. 2185 */ 2186 static int 2187 rcs_parse_access(RCSFILE *rfp) 2188 { 2189 int type; 2190 2191 while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) { 2192 if (type != RCS_TOK_ID) { 2193 rcs_errno = RCS_ERR_PARSE; 2194 warnx("unexpected token `%s' in access list", 2195 RCS_TOKSTR(rfp)); 2196 return (-1); 2197 } 2198 2199 if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0) 2200 return (-1); 2201 } 2202 2203 return (0); 2204 } 2205 2206 /* 2207 * rcs_parse_symbols() 2208 * 2209 * Parse the symbol list given as value to the `symbols' keyword. 2210 * Returns 0 on success, or -1 on failure. 2211 */ 2212 static int 2213 rcs_parse_symbols(RCSFILE *rfp) 2214 { 2215 int type; 2216 struct rcs_sym *symp; 2217 2218 for (;;) { 2219 type = rcs_gettok(rfp); 2220 if (type == RCS_TOK_SCOLON) 2221 break; 2222 2223 if (type != RCS_TOK_ID) { 2224 rcs_errno = RCS_ERR_PARSE; 2225 warnx("unexpected token `%s' in symbol list", 2226 RCS_TOKSTR(rfp)); 2227 return (-1); 2228 } 2229 2230 symp = xmalloc(sizeof(*symp)); 2231 symp->rs_name = xstrdup(RCS_TOKSTR(rfp)); 2232 symp->rs_num = rcsnum_alloc(); 2233 2234 type = rcs_gettok(rfp); 2235 if (type != RCS_TOK_COLON) { 2236 rcs_errno = RCS_ERR_PARSE; 2237 warnx("unexpected token `%s' in symbol list", 2238 RCS_TOKSTR(rfp)); 2239 rcsnum_free(symp->rs_num); 2240 xfree(symp->rs_name); 2241 xfree(symp); 2242 return (-1); 2243 } 2244 2245 type = rcs_gettok(rfp); 2246 if (type != RCS_TOK_NUM) { 2247 rcs_errno = RCS_ERR_PARSE; 2248 warnx("unexpected token `%s' in symbol list", 2249 RCS_TOKSTR(rfp)); 2250 rcsnum_free(symp->rs_num); 2251 xfree(symp->rs_name); 2252 xfree(symp); 2253 return (-1); 2254 } 2255 2256 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) { 2257 warnx("failed to parse RCS NUM `%s'", 2258 RCS_TOKSTR(rfp)); 2259 rcsnum_free(symp->rs_num); 2260 xfree(symp->rs_name); 2261 xfree(symp); 2262 return (-1); 2263 } 2264 2265 TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list); 2266 } 2267 2268 return (0); 2269 } 2270 2271 /* 2272 * rcs_parse_locks() 2273 * 2274 * Parse the lock list given as value to the `locks' keyword. 2275 * Returns 0 on success, or -1 on failure. 2276 */ 2277 static int 2278 rcs_parse_locks(RCSFILE *rfp) 2279 { 2280 int type; 2281 struct rcs_lock *lkp; 2282 2283 for (;;) { 2284 type = rcs_gettok(rfp); 2285 if (type == RCS_TOK_SCOLON) 2286 break; 2287 2288 if (type != RCS_TOK_ID) { 2289 rcs_errno = RCS_ERR_PARSE; 2290 warnx("unexpected token `%s' in lock list", 2291 RCS_TOKSTR(rfp)); 2292 return (-1); 2293 } 2294 2295 lkp = xmalloc(sizeof(*lkp)); 2296 lkp->rl_name = xstrdup(RCS_TOKSTR(rfp)); 2297 lkp->rl_num = rcsnum_alloc(); 2298 2299 type = rcs_gettok(rfp); 2300 if (type != RCS_TOK_COLON) { 2301 rcs_errno = RCS_ERR_PARSE; 2302 warnx("unexpected token `%s' in symbol list", 2303 RCS_TOKSTR(rfp)); 2304 rcsnum_free(lkp->rl_num); 2305 xfree(lkp->rl_name); 2306 xfree(lkp); 2307 return (-1); 2308 } 2309 2310 type = rcs_gettok(rfp); 2311 if (type != RCS_TOK_NUM) { 2312 rcs_errno = RCS_ERR_PARSE; 2313 warnx("unexpected token `%s' in symbol list", 2314 RCS_TOKSTR(rfp)); 2315 rcsnum_free(lkp->rl_num); 2316 xfree(lkp->rl_name); 2317 xfree(lkp); 2318 return (-1); 2319 } 2320 2321 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) { 2322 warnx("failed to parse RCS NUM `%s'", 2323 RCS_TOKSTR(rfp)); 2324 rcsnum_free(lkp->rl_num); 2325 xfree(lkp->rl_name); 2326 xfree(lkp); 2327 return (-1); 2328 } 2329 2330 TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list); 2331 } 2332 2333 /* check if we have a `strict' */ 2334 type = rcs_gettok(rfp); 2335 if (type != RCS_TOK_STRICT) { 2336 rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); 2337 } else { 2338 rfp->rf_flags |= RCS_SLOCK; 2339 2340 type = rcs_gettok(rfp); 2341 if (type != RCS_TOK_SCOLON) { 2342 rcs_errno = RCS_ERR_PARSE; 2343 warnx("missing semi-colon after `strict' keyword"); 2344 return (-1); 2345 } 2346 } 2347 2348 return (0); 2349 } 2350 2351 /* 2352 * rcs_parse_branches() 2353 * 2354 * Parse the list of branches following a `branches' keyword in a delta. 2355 * Returns 0 on success, or -1 on failure. 2356 */ 2357 static int 2358 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp) 2359 { 2360 int type; 2361 struct rcs_branch *brp; 2362 2363 for (;;) { 2364 type = rcs_gettok(rfp); 2365 if (type == RCS_TOK_SCOLON) 2366 break; 2367 2368 if (type != RCS_TOK_NUM) { 2369 rcs_errno = RCS_ERR_PARSE; 2370 warnx("unexpected token `%s' in list of branches", 2371 RCS_TOKSTR(rfp)); 2372 return (-1); 2373 } 2374 2375 brp = xmalloc(sizeof(*brp)); 2376 brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp)); 2377 if (brp->rb_num == NULL) { 2378 xfree(brp); 2379 return (-1); 2380 } 2381 2382 TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); 2383 } 2384 2385 return (0); 2386 } 2387 2388 /* 2389 * rcs_freedelta() 2390 * 2391 * Free the contents of a delta structure. 2392 */ 2393 static void 2394 rcs_freedelta(struct rcs_delta *rdp) 2395 { 2396 struct rcs_branch *rb; 2397 2398 if (rdp->rd_num != NULL) 2399 rcsnum_free(rdp->rd_num); 2400 if (rdp->rd_next != NULL) 2401 rcsnum_free(rdp->rd_next); 2402 2403 if (rdp->rd_author != NULL) 2404 xfree(rdp->rd_author); 2405 if (rdp->rd_locker != NULL) 2406 xfree(rdp->rd_locker); 2407 if (rdp->rd_state != NULL) 2408 xfree(rdp->rd_state); 2409 if (rdp->rd_log != NULL) 2410 xfree(rdp->rd_log); 2411 if (rdp->rd_text != NULL) 2412 xfree(rdp->rd_text); 2413 2414 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) { 2415 TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list); 2416 rcsnum_free(rb->rb_num); 2417 xfree(rb); 2418 } 2419 2420 xfree(rdp); 2421 } 2422 2423 /* 2424 * rcs_freepdata() 2425 * 2426 * Free the contents of the parser data structure. 2427 */ 2428 static void 2429 rcs_freepdata(struct rcs_pdata *pd) 2430 { 2431 if (pd->rp_file != NULL) 2432 (void)fclose(pd->rp_file); 2433 if (pd->rp_buf != NULL) 2434 xfree(pd->rp_buf); 2435 xfree(pd); 2436 } 2437 2438 /* 2439 * rcs_gettok() 2440 * 2441 * Get the next RCS token from the string <str>. 2442 */ 2443 static int 2444 rcs_gettok(RCSFILE *rfp) 2445 { 2446 u_int i; 2447 int ch, last, type; 2448 size_t len; 2449 char *bp; 2450 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2451 2452 type = RCS_TOK_ERR; 2453 bp = pdp->rp_buf; 2454 pdp->rp_tlen = 0; 2455 *bp = '\0'; 2456 2457 if (pdp->rp_pttype != RCS_TOK_ERR) { 2458 type = pdp->rp_pttype; 2459 if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >= 2460 pdp->rp_blen) 2461 errx(1, "rcs_gettok: strlcpy"); 2462 pdp->rp_pttype = RCS_TOK_ERR; 2463 return (type); 2464 } 2465 2466 /* skip leading whitespace */ 2467 /* XXX we must skip backspace too for compatibility, should we? */ 2468 do { 2469 ch = getc(pdp->rp_file); 2470 if (ch == '\n') 2471 pdp->rp_lines++; 2472 } while (isspace(ch)); 2473 2474 if (ch == EOF) { 2475 type = RCS_TOK_EOF; 2476 } else if (ch == ';') { 2477 type = RCS_TOK_SCOLON; 2478 } else if (ch == ':') { 2479 type = RCS_TOK_COLON; 2480 } else if (isalpha(ch) || ch == '_' || ch == '$' || ch == '.' || ch == '-') { 2481 type = RCS_TOK_ID; 2482 *(bp++) = ch; 2483 for (;;) { 2484 ch = getc(pdp->rp_file); 2485 if (ch == EOF) { 2486 type = RCS_TOK_EOF; 2487 break; 2488 } else if (!isalnum(ch) && ch != '_' && ch != '-' && 2489 ch != '/' && ch != '.' && ch != '$') { 2490 ungetc(ch, pdp->rp_file); 2491 break; 2492 } 2493 *(bp++) = ch; 2494 pdp->rp_tlen++; 2495 if (bp == pdp->rp_bufend - 1) { 2496 len = bp - pdp->rp_buf; 2497 rcs_growbuf(rfp); 2498 bp = pdp->rp_buf + len; 2499 } 2500 } 2501 *bp = '\0'; 2502 2503 if (type != RCS_TOK_ERR) { 2504 for (i = 0; i < RCS_NKEYS; i++) { 2505 if (strcmp(rcs_keys[i].rk_str, 2506 pdp->rp_buf) == 0) { 2507 type = rcs_keys[i].rk_id; 2508 break; 2509 } 2510 } 2511 } 2512 } else if (ch == '@') { 2513 /* we have a string */ 2514 type = RCS_TOK_STRING; 2515 for (;;) { 2516 ch = getc(pdp->rp_file); 2517 if (ch == EOF) { 2518 type = RCS_TOK_EOF; 2519 break; 2520 } else if (ch == '@') { 2521 ch = getc(pdp->rp_file); 2522 if (ch != '@') { 2523 ungetc(ch, pdp->rp_file); 2524 break; 2525 } 2526 } else if (ch == '\n') 2527 pdp->rp_lines++; 2528 2529 *(bp++) = ch; 2530 pdp->rp_tlen++; 2531 if (bp == pdp->rp_bufend - 1) { 2532 len = bp - pdp->rp_buf; 2533 rcs_growbuf(rfp); 2534 bp = pdp->rp_buf + len; 2535 } 2536 } 2537 2538 *bp = '\0'; 2539 } else if (isdigit(ch)) { 2540 *(bp++) = ch; 2541 last = ch; 2542 type = RCS_TOK_NUM; 2543 2544 for (;;) { 2545 ch = getc(pdp->rp_file); 2546 if (ch == EOF) { 2547 type = RCS_TOK_EOF; 2548 break; 2549 } 2550 if (bp == pdp->rp_bufend) 2551 break; 2552 if (isalpha(ch) && ch != '.') { 2553 type = RCS_TOK_ID; 2554 } else if (!isdigit(ch) && ch != '.') { 2555 ungetc(ch, pdp->rp_file); 2556 break; 2557 } 2558 2559 if (last == '.' && ch == '.') { 2560 type = RCS_TOK_ERR; 2561 break; 2562 } 2563 last = ch; 2564 *(bp++) = ch; 2565 pdp->rp_tlen++; 2566 } 2567 *bp = '\0'; 2568 } 2569 2570 return (type); 2571 } 2572 2573 /* 2574 * rcs_pushtok() 2575 * 2576 * Push a token back in the parser's token buffer. 2577 */ 2578 static int 2579 rcs_pushtok(RCSFILE *rfp, const char *tok, int type) 2580 { 2581 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2582 2583 if (pdp->rp_pttype != RCS_TOK_ERR) 2584 return (-1); 2585 2586 pdp->rp_pttype = type; 2587 if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >= 2588 sizeof(pdp->rp_ptok)) 2589 errx(1, "rcs_pushtok: strlcpy"); 2590 return (0); 2591 } 2592 2593 2594 /* 2595 * rcs_growbuf() 2596 * 2597 * Attempt to grow the internal parse buffer for the RCS file <rf> by 2598 * RCS_BUFEXTSIZE. 2599 * In case of failure, the original buffer is left unmodified. 2600 */ 2601 static void 2602 rcs_growbuf(RCSFILE *rf) 2603 { 2604 struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata; 2605 2606 pdp->rp_buf = xrealloc(pdp->rp_buf, 1, 2607 pdp->rp_blen + RCS_BUFEXTSIZE); 2608 pdp->rp_blen += RCS_BUFEXTSIZE; 2609 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 2610 } 2611 2612 /* 2613 * rcs_strprint() 2614 * 2615 * Output an RCS string <str> of size <slen> to the stream <stream>. Any 2616 * '@' characters are escaped. Otherwise, the string can contain arbitrary 2617 * binary data. 2618 */ 2619 static void 2620 rcs_strprint(const u_char *str, size_t slen, FILE *stream) 2621 { 2622 const u_char *ap, *ep, *sp; 2623 2624 if (slen == 0) 2625 return; 2626 2627 ep = str + slen - 1; 2628 2629 for (sp = str; sp <= ep;) { 2630 ap = memchr(sp, '@', ep - sp); 2631 if (ap == NULL) 2632 ap = ep; 2633 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream); 2634 2635 if (*ap == '@') 2636 putc('@', stream); 2637 sp = ap + 1; 2638 } 2639 } 2640 2641 /* 2642 * rcs_expand_keywords() 2643 * 2644 * Return expansion any RCS keywords in <data> 2645 * 2646 * On error, return NULL. 2647 */ 2648 static BUF * 2649 rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, BUF *bp, int mode) 2650 { 2651 BUF *newbuf; 2652 int kwtype; 2653 u_int j, found; 2654 u_char *c, *kwstr, *start, *end, *fin; 2655 char expbuf[256], buf[256]; 2656 struct tm tb; 2657 char *fmt; 2658 size_t len; 2659 2660 kwtype = 0; 2661 kwstr = NULL; 2662 2663 /* 2664 * -z support for RCS 2665 */ 2666 tb = rdp->rd_date; 2667 if (timezone_flag != NULL) 2668 rcs_set_tz(timezone_flag, rdp, &tb); 2669 2670 len = rcs_buf_len(bp); 2671 2672 c = rcs_buf_get(bp); 2673 found = 0; 2674 /* Final character in buffer. */ 2675 fin = c + len - 1; 2676 2677 /* If no keywords are found, return original buffer. */ 2678 newbuf = bp; 2679 2680 /* 2681 * Keyword formats: 2682 * $Keyword$ 2683 * $Keyword: value$ 2684 */ 2685 for (; c < fin; c++) { 2686 if (*c == '$') { 2687 BUF *tmpbuf; 2688 size_t clen; 2689 2690 /* remember start of this possible keyword */ 2691 start = c; 2692 2693 /* first following character has to be alphanumeric */ 2694 c++; 2695 if (!isalpha(*c)) { 2696 c = start; 2697 continue; 2698 } 2699 2700 /* Number of characters between c and fin, inclusive. */ 2701 clen = fin - c + 1; 2702 2703 /* look for any matching keywords */ 2704 found = 0; 2705 for (j = 0; j < RCS_NKWORDS; j++) { 2706 size_t kwlen; 2707 2708 kwlen = strlen(rcs_expkw[j].kw_str); 2709 /* 2710 * kwlen must be less than clen since clen 2711 * includes either a terminating `$' or a `:'. 2712 */ 2713 if (kwlen < clen && 2714 memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 && 2715 (c[kwlen] == '$' || c[kwlen] == ':')) { 2716 found = 1; 2717 kwstr = rcs_expkw[j].kw_str; 2718 kwtype = rcs_expkw[j].kw_type; 2719 c += kwlen; 2720 break; 2721 } 2722 } 2723 2724 /* unknown keyword, continue looking */ 2725 if (found == 0) { 2726 c = start; 2727 continue; 2728 } 2729 2730 /* 2731 * if the next character was ':' we need to look for 2732 * an '$' before the end of the line to be sure it is 2733 * in fact a keyword. 2734 */ 2735 if (*c == ':') { 2736 for (; c <= fin; ++c) { 2737 if (*c == '$' || *c == '\n') 2738 break; 2739 } 2740 2741 if (*c != '$') { 2742 c = start; 2743 continue; 2744 } 2745 } 2746 end = c + 1; 2747 2748 /* start constructing the expansion */ 2749 expbuf[0] = '\0'; 2750 2751 if (mode & RCS_KWEXP_NAME) { 2752 char *tmp; 2753 2754 (void)xasprintf(&tmp, "$%s%s", kwstr, 2755 (mode & RCS_KWEXP_VAL) ? ": " : ""); 2756 if (strlcat(expbuf, tmp, sizeof(expbuf)) >= sizeof(expbuf)) 2757 errx(1, "rcs_expand_keywords: string truncated"); 2758 xfree(tmp); 2759 } 2760 2761 /* 2762 * order matters because of RCS_KW_ID and 2763 * RCS_KW_HEADER here 2764 */ 2765 if (mode & RCS_KWEXP_VAL) { 2766 if (kwtype & RCS_KW_RCSFILE) { 2767 char *tmp; 2768 2769 (void)xasprintf(&tmp, "%s ", 2770 (kwtype & RCS_KW_FULLPATH) ? rcsfile : basename(rcsfile)); 2771 if (strlcat(expbuf, tmp, sizeof(expbuf)) >= sizeof(expbuf)) 2772 errx(1, "rcs_expand_keywords: string truncated"); 2773 xfree(tmp); 2774 } 2775 2776 if (kwtype & RCS_KW_REVISION) { 2777 char *tmp; 2778 2779 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 2780 (void)xasprintf(&tmp, "%s ", buf); 2781 if (strlcat(expbuf, tmp, sizeof(expbuf)) >= sizeof(buf)) 2782 errx(1, "rcs_expand_keywords: string truncated"); 2783 xfree(tmp); 2784 } 2785 2786 if (kwtype & RCS_KW_DATE) { 2787 if (timezone_flag != NULL) 2788 fmt = "%Y/%m/%d %H:%M:%S%z "; 2789 else 2790 fmt = "%Y/%m/%d %H:%M:%S "; 2791 2792 strftime(buf, sizeof(buf), fmt, &tb); 2793 if (strlcat(expbuf, buf, sizeof(expbuf)) >= sizeof(expbuf)) 2794 errx(1, "rcs_expand_keywords: string truncated"); 2795 } 2796 2797 if (kwtype & RCS_KW_AUTHOR) { 2798 char *tmp; 2799 2800 (void)xasprintf(&tmp, "%s ", rdp->rd_author); 2801 if (strlcat(expbuf, tmp, sizeof(expbuf)) >= sizeof(expbuf)) 2802 errx(1, "rcs_expand_keywords: string truncated"); 2803 xfree(tmp); 2804 } 2805 2806 if (kwtype & RCS_KW_STATE) { 2807 char *tmp; 2808 2809 (void)xasprintf(&tmp, "%s ", rdp->rd_state); 2810 if (strlcat(expbuf, tmp, sizeof(expbuf)) >= sizeof(expbuf)) 2811 errx(1, "rcs_expand_keywords: string truncated"); 2812 xfree(tmp); 2813 } 2814 2815 /* order does not matter anymore below */ 2816 if (kwtype & RCS_KW_LOG) 2817 if (strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 2818 errx(1, "rcs_expand_keywords: string truncated"); 2819 2820 if (kwtype & RCS_KW_SOURCE) { 2821 char *tmp; 2822 2823 (void)xasprintf(&tmp, "%s ", rcsfile); 2824 if (strlcat(expbuf, tmp, sizeof(expbuf)) >= sizeof(expbuf)) 2825 errx(1, "rcs_expand_keywords: string truncated"); 2826 xfree(tmp); 2827 } 2828 2829 if (kwtype & RCS_KW_NAME) 2830 if (strlcat(expbuf, " ", sizeof(expbuf)) >= sizeof(expbuf)) 2831 errx(1, "rcs_expand_keywords: string truncated"); 2832 } 2833 2834 /* end the expansion */ 2835 if (mode & RCS_KWEXP_NAME) 2836 if (strlcat(expbuf, "$", sizeof(expbuf)) >= sizeof(expbuf)) 2837 errx(1, "rcs_expand_keywords: string truncated"); 2838 2839 /* Concatenate everything together. */ 2840 tmpbuf = rcs_buf_alloc(len + strlen(expbuf), BUF_AUTOEXT); 2841 /* Append everything before keyword. */ 2842 rcs_buf_append(tmpbuf, rcs_buf_get(newbuf), 2843 start - (unsigned char *)rcs_buf_get(newbuf)); 2844 /* Append keyword. */ 2845 rcs_buf_append(tmpbuf, expbuf, strlen(expbuf)); 2846 /* Point c to end of keyword. */ 2847 c = rcs_buf_get(tmpbuf) + rcs_buf_len(tmpbuf) - 1; 2848 /* Append everything after keyword. */ 2849 rcs_buf_append(tmpbuf, end, 2850 ((unsigned char *)rcs_buf_get(newbuf) + rcs_buf_len(newbuf)) - end); 2851 /* Point fin to end of data. */ 2852 fin = rcs_buf_get(tmpbuf) + rcs_buf_len(tmpbuf) - 1; 2853 /* Recalculate new length. */ 2854 len = rcs_buf_len(tmpbuf); 2855 2856 /* tmpbuf is now ready, free old newbuf if allocated here. */ 2857 if (newbuf != bp) 2858 rcs_buf_free(newbuf); 2859 newbuf = tmpbuf; 2860 } 2861 } 2862 2863 return (newbuf); 2864 } 2865 2866 /* 2867 * rcs_deltatext_set() 2868 * 2869 * Set deltatext for <rev> in RCS file <rfp> to <dtext> 2870 * Returns -1 on error, 0 on success. 2871 */ 2872 int 2873 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp) 2874 { 2875 size_t len; 2876 u_char *dtext; 2877 struct rcs_delta *rdp; 2878 2879 /* Write operations require full parsing */ 2880 rcs_parse_deltatexts(rfp, NULL); 2881 2882 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2883 return (-1); 2884 2885 if (rdp->rd_text != NULL) 2886 xfree(rdp->rd_text); 2887 2888 len = rcs_buf_len(bp); 2889 dtext = rcs_buf_release(bp); 2890 bp = NULL; 2891 if (len != 0) { 2892 rdp->rd_text = xmalloc(len); 2893 rdp->rd_tlen = len; 2894 (void)memcpy(rdp->rd_text, dtext, len); 2895 xfree(dtext); 2896 } else { 2897 rdp->rd_text = NULL; 2898 rdp->rd_tlen = 0; 2899 } 2900 2901 return (0); 2902 } 2903 2904 /* 2905 * rcs_rev_setlog() 2906 * 2907 * Sets the log message of revision <rev> to <logtext>. 2908 */ 2909 int 2910 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext) 2911 { 2912 struct rcs_delta *rdp; 2913 2914 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2915 return (-1); 2916 2917 if (rdp->rd_log != NULL) 2918 xfree(rdp->rd_log); 2919 2920 rdp->rd_log = xstrdup(logtext); 2921 rfp->rf_flags &= ~RCS_SYNCED; 2922 return (0); 2923 } 2924 /* 2925 * rcs_rev_getdate() 2926 * 2927 * Get the date corresponding to a given revision. 2928 * Returns the date on success, -1 on failure. 2929 */ 2930 time_t 2931 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev) 2932 { 2933 struct rcs_delta *rdp; 2934 2935 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2936 return (-1); 2937 2938 return (mktime(&rdp->rd_date)); 2939 } 2940 2941 /* 2942 * rcs_state_set() 2943 * 2944 * Sets the state of revision <rev> to <state> 2945 * NOTE: default state is 'Exp'. States may not contain spaces. 2946 * 2947 * Returns -1 on failure, 0 on success. 2948 */ 2949 int 2950 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state) 2951 { 2952 struct rcs_delta *rdp; 2953 2954 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2955 return (-1); 2956 2957 if (rdp->rd_state != NULL) 2958 xfree(rdp->rd_state); 2959 2960 rdp->rd_state = xstrdup(state); 2961 2962 rfp->rf_flags &= ~RCS_SYNCED; 2963 2964 return (0); 2965 } 2966 2967 /* 2968 * rcs_state_check() 2969 * 2970 * Check if string <state> is valid. 2971 * 2972 * Returns 0 if the string is valid, -1 otherwise. 2973 */ 2974 int 2975 rcs_state_check(const char *state) 2976 { 2977 if (strchr(state, ' ') != NULL) 2978 return (-1); 2979 2980 return (0); 2981 } 2982 2983 /* 2984 * rcs_state_get() 2985 * 2986 * Get the state for a given revision of a specified RCSFILE. 2987 * 2988 * Returns NULL on failure. 2989 */ 2990 const char * 2991 rcs_state_get(RCSFILE *rfp, RCSNUM *rev) 2992 { 2993 struct rcs_delta *rdp; 2994 2995 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2996 return (NULL); 2997 2998 return (rdp->rd_state); 2999 } 3000 3001 /* 3002 * rcs_kwexp_buf() 3003 * 3004 * Do keyword expansion on a buffer if necessary 3005 * 3006 */ 3007 BUF * 3008 rcs_kwexp_buf(BUF *bp, RCSFILE *rf, RCSNUM *rev) 3009 { 3010 struct rcs_delta *rdp; 3011 int expmode; 3012 3013 /* 3014 * Do keyword expansion if required. 3015 */ 3016 if (rf->rf_expand != NULL) 3017 expmode = rcs_kwexp_get(rf); 3018 else 3019 expmode = RCS_KWEXP_DEFAULT; 3020 3021 if (!(expmode & RCS_KWEXP_NONE)) { 3022 if ((rdp = rcs_findrev(rf, rev)) == NULL) 3023 errx(1, "could not fetch revision"); 3024 return (rcs_expand_keywords(rf->rf_path, rdp, bp, expmode)); 3025 } 3026 return (bp); 3027 } 3028