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