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