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