1 /* $OpenBSD: rcs.c,v 1.277 2008/06/15 04:44:06 joris 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 <errno.h> 31 #include <libgen.h> 32 #include <pwd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "atomicio.h" 38 #include "cvs.h" 39 #include "diff.h" 40 #include "rcs.h" 41 42 #define RCS_BUFSIZE 16384 43 #define RCS_BUFEXTSIZE 8192 44 #define RCS_KWEXP_SIZE 1024 45 46 /* RCS token types */ 47 #define RCS_TOK_ERR -1 48 #define RCS_TOK_EOF 0 49 #define RCS_TOK_NUM 1 50 #define RCS_TOK_ID 2 51 #define RCS_TOK_STRING 3 52 #define RCS_TOK_SCOLON 4 53 #define RCS_TOK_COLON 5 54 55 #define RCS_TOK_HEAD 8 56 #define RCS_TOK_BRANCH 9 57 #define RCS_TOK_ACCESS 10 58 #define RCS_TOK_SYMBOLS 11 59 #define RCS_TOK_LOCKS 12 60 #define RCS_TOK_COMMENT 13 61 #define RCS_TOK_EXPAND 14 62 #define RCS_TOK_DATE 15 63 #define RCS_TOK_AUTHOR 16 64 #define RCS_TOK_STATE 17 65 #define RCS_TOK_NEXT 18 66 #define RCS_TOK_BRANCHES 19 67 #define RCS_TOK_DESC 20 68 #define RCS_TOK_LOG 21 69 #define RCS_TOK_TEXT 22 70 #define RCS_TOK_STRICT 23 71 72 #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES)) 73 74 #define RCS_NOSCOL 0x01 /* no terminating semi-colon */ 75 #define RCS_VOPT 0x02 /* value is optional */ 76 77 #define ANNOTATE_NEVER 0 78 #define ANNOTATE_NOW 1 79 #define ANNOTATE_LATER 2 80 81 /* opaque parse data */ 82 struct rcs_pdata { 83 u_int rp_lines; 84 85 char *rp_buf; 86 size_t rp_blen; 87 char *rp_bufend; 88 size_t rp_tlen; 89 90 /* pushback token buffer */ 91 char rp_ptok[128]; 92 int rp_pttype; /* token type, RCS_TOK_ERR if no token */ 93 94 FILE *rp_file; 95 }; 96 97 #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf 98 #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen 99 100 /* invalid characters in RCS symbol names */ 101 static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; 102 103 /* comment leaders, depending on the file's suffix */ 104 static const struct rcs_comment { 105 const char *rc_suffix; 106 const char *rc_cstr; 107 } rcs_comments[] = { 108 { "1", ".\\\" " }, 109 { "2", ".\\\" " }, 110 { "3", ".\\\" " }, 111 { "4", ".\\\" " }, 112 { "5", ".\\\" " }, 113 { "6", ".\\\" " }, 114 { "7", ".\\\" " }, 115 { "8", ".\\\" " }, 116 { "9", ".\\\" " }, 117 { "a", "-- " }, /* Ada */ 118 { "ada", "-- " }, 119 { "adb", "-- " }, 120 { "asm", ";; " }, /* assembler (MS-DOS) */ 121 { "ads", "-- " }, /* Ada */ 122 { "bat", ":: " }, /* batch (MS-DOS) */ 123 { "body", "-- " }, /* Ada */ 124 { "c", " * " }, /* C */ 125 { "c++", "// " }, /* C++ */ 126 { "cc", "// " }, 127 { "cpp", "// " }, 128 { "cxx", "// " }, 129 { "m", "// " }, /* Objective-C */ 130 { "cl", ";;; " }, /* Common Lisp */ 131 { "cmd", ":: " }, /* command (OS/2) */ 132 { "cmf", "c " }, /* CM Fortran */ 133 { "csh", "# " }, /* shell */ 134 { "e", "# " }, /* efl */ 135 { "epsf", "% " }, /* encapsulated postscript */ 136 { "epsi", "% " }, /* encapsulated postscript */ 137 { "el", "; " }, /* Emacs Lisp */ 138 { "f", "c " }, /* Fortran */ 139 { "for", "c " }, 140 { "h", " * " }, /* C-header */ 141 { "hh", "// " }, /* C++ header */ 142 { "hpp", "// " }, 143 { "hxx", "// " }, 144 { "in", "# " }, /* for Makefile.in */ 145 { "l", " * " }, /* lex */ 146 { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ 147 { "mak", "# " }, /* makefile, e.g. Visual C++ */ 148 { "me", ".\\\" " }, /* me-macros t/nroff */ 149 { "ml", "; " }, /* mocklisp */ 150 { "mm", ".\\\" " }, /* mm-macros t/nroff */ 151 { "ms", ".\\\" " }, /* ms-macros t/nroff */ 152 { "man", ".\\\" " }, /* man-macros t/nroff */ 153 { "p", " * " }, /* pascal */ 154 { "pas", " * " }, 155 { "pl", "# " }, /* Perl (conflict with Prolog) */ 156 { "pm", "# " }, /* Perl module */ 157 { "ps", "% " }, /* postscript */ 158 { "psw", "% " }, /* postscript wrap */ 159 { "pswm", "% " }, /* postscript wrap */ 160 { "r", "# " }, /* ratfor */ 161 { "rc", " * " }, /* Microsoft Windows resource file */ 162 { "red", "% " }, /* psl/rlisp */ 163 { "sh", "# " }, /* shell */ 164 { "sl", "% " }, /* psl */ 165 { "spec", "-- " }, /* Ada */ 166 { "tex", "% " }, /* tex */ 167 { "y", " * " }, /* yacc */ 168 { "ye", " * " }, /* yacc-efl */ 169 { "yr", " * " }, /* yacc-ratfor */ 170 }; 171 172 struct rcs_kw rcs_expkw[] = { 173 { "Author", RCS_KW_AUTHOR }, 174 { "Date", RCS_KW_DATE }, 175 { "Header", RCS_KW_HEADER }, 176 { "Id", RCS_KW_ID }, 177 { "Locker", RCS_KW_LOCKER }, 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 { "Mdocdate", RCS_KW_MDOCDATE }, 185 }; 186 187 #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0])) 188 189 static struct rcs_key { 190 char rk_str[16]; 191 int rk_id; 192 int rk_val; 193 int rk_flags; 194 } rcs_keys[] = { 195 { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT }, 196 { "author", RCS_TOK_AUTHOR, RCS_TOK_ID, 0 }, 197 { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT }, 198 { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT }, 199 { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT }, 200 { "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 RCSNUM *rcs_get_revision(const char *, RCSFILE *); 216 int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *, 217 struct cvs_line **, struct rcs_delta *); 218 static void rcs_parse_init(RCSFILE *); 219 static int rcs_parse_admin(RCSFILE *); 220 static int rcs_parse_delta(RCSFILE *); 221 static void rcs_parse_deltas(RCSFILE *, RCSNUM *); 222 static int rcs_parse_deltatext(RCSFILE *); 223 static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *); 224 static void rcs_parse_desc(RCSFILE *, RCSNUM *); 225 226 static int rcs_parse_access(RCSFILE *); 227 static int rcs_parse_symbols(RCSFILE *); 228 static int rcs_parse_locks(RCSFILE *); 229 static int rcs_parse_branches(RCSFILE *, struct rcs_delta *); 230 static void rcs_freedelta(struct rcs_delta *); 231 static void rcs_freepdata(struct rcs_pdata *); 232 static int rcs_gettok(RCSFILE *); 233 static int rcs_pushtok(RCSFILE *, const char *, int); 234 static void rcs_growbuf(RCSFILE *); 235 static void rcs_strprint(const u_char *, size_t, FILE *); 236 237 static void rcs_kwexp_line(char *, struct rcs_delta *, struct cvs_lines *, 238 struct cvs_line *, int mode); 239 240 static int rcs_ignore_keys = 0; 241 242 RCSFILE * 243 rcs_open(const char *path, int fd, int flags, ...) 244 { 245 int mode; 246 mode_t fmode; 247 RCSFILE *rfp; 248 va_list vap; 249 struct stat st; 250 struct rcs_delta *rdp; 251 struct rcs_lock *lkr; 252 253 fmode = S_IRUSR|S_IRGRP|S_IROTH; 254 flags &= 0xffff; /* ditch any internal flags */ 255 256 if (flags & RCS_CREATE) { 257 va_start(vap, flags); 258 mode = va_arg(vap, int); 259 va_end(vap); 260 fmode = (mode_t)mode; 261 } else { 262 if (fstat(fd, &st) == -1) 263 fatal("rcs_open: %s: fstat: %s", path, strerror(errno)); 264 fmode = st.st_mode; 265 } 266 267 fmode &= ~cvs_umask; 268 269 rfp = xcalloc(1, sizeof(*rfp)); 270 271 rfp->rf_path = xstrdup(path); 272 rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED; 273 rfp->rf_mode = fmode; 274 rfp->fd = fd; 275 rfp->rf_dead = 0; 276 277 TAILQ_INIT(&(rfp->rf_delta)); 278 TAILQ_INIT(&(rfp->rf_access)); 279 TAILQ_INIT(&(rfp->rf_symbols)); 280 TAILQ_INIT(&(rfp->rf_locks)); 281 282 if (!(rfp->rf_flags & RCS_CREATE)) 283 rcs_parse_init(rfp); 284 285 /* fill in rd_locker */ 286 TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) { 287 if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) { 288 rcs_close(rfp); 289 return (NULL); 290 } 291 292 rdp->rd_locker = xstrdup(lkr->rl_name); 293 } 294 295 return (rfp); 296 } 297 298 /* 299 * rcs_close() 300 * 301 * Close an RCS file handle. 302 */ 303 void 304 rcs_close(RCSFILE *rfp) 305 { 306 struct rcs_delta *rdp; 307 struct rcs_access *rap; 308 struct rcs_lock *rlp; 309 struct rcs_sym *rsp; 310 311 if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED)) 312 rcs_write(rfp); 313 314 while (!TAILQ_EMPTY(&(rfp->rf_delta))) { 315 rdp = TAILQ_FIRST(&(rfp->rf_delta)); 316 TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list); 317 rcs_freedelta(rdp); 318 } 319 320 while (!TAILQ_EMPTY(&(rfp->rf_access))) { 321 rap = TAILQ_FIRST(&(rfp->rf_access)); 322 TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list); 323 xfree(rap->ra_name); 324 xfree(rap); 325 } 326 327 while (!TAILQ_EMPTY(&(rfp->rf_symbols))) { 328 rsp = TAILQ_FIRST(&(rfp->rf_symbols)); 329 TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list); 330 rcsnum_free(rsp->rs_num); 331 xfree(rsp->rs_name); 332 xfree(rsp); 333 } 334 335 while (!TAILQ_EMPTY(&(rfp->rf_locks))) { 336 rlp = TAILQ_FIRST(&(rfp->rf_locks)); 337 TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list); 338 rcsnum_free(rlp->rl_num); 339 xfree(rlp->rl_name); 340 xfree(rlp); 341 } 342 343 if (rfp->rf_head != NULL) 344 rcsnum_free(rfp->rf_head); 345 if (rfp->rf_branch != NULL) 346 rcsnum_free(rfp->rf_branch); 347 348 if (rfp->rf_path != NULL) 349 xfree(rfp->rf_path); 350 if (rfp->rf_comment != NULL) 351 xfree(rfp->rf_comment); 352 if (rfp->rf_expand != NULL) 353 xfree(rfp->rf_expand); 354 if (rfp->rf_desc != NULL) 355 xfree(rfp->rf_desc); 356 if (rfp->rf_pdata != NULL) 357 rcs_freepdata(rfp->rf_pdata); 358 xfree(rfp); 359 } 360 361 /* 362 * rcs_write() 363 * 364 * Write the contents of the RCS file handle <rfp> to disk in the file whose 365 * path is in <rf_path>. 366 */ 367 void 368 rcs_write(RCSFILE *rfp) 369 { 370 FILE *fp; 371 char buf[1024], numbuf[CVS_REV_BUFSZ], *fn, tmpdir[MAXPATHLEN]; 372 struct rcs_access *ap; 373 struct rcs_sym *symp; 374 struct rcs_branch *brp; 375 struct rcs_delta *rdp; 376 struct rcs_lock *lkp; 377 size_t len; 378 int fd, saved_errno; 379 380 fd = -1; 381 382 if (rfp->rf_flags & RCS_SYNCED) 383 return; 384 385 if (cvs_noexec == 1) 386 return; 387 388 /* Write operations need the whole file parsed */ 389 rcs_parse_deltatexts(rfp, NULL); 390 391 if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir)) 392 fatal("rcs_write: truncation"); 393 (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir)); 394 395 if ((fd = mkstemp(fn)) == -1) 396 fatal("%s", fn); 397 398 if ((fp = fdopen(fd, "w")) == NULL) { 399 saved_errno = errno; 400 (void)unlink(fn); 401 fatal("fdopen %s: %s", fn, strerror(saved_errno)); 402 } 403 404 if (rfp->rf_head != NULL) 405 rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf)); 406 else 407 numbuf[0] = '\0'; 408 409 fprintf(fp, "head\t%s;\n", numbuf); 410 411 if (rfp->rf_branch != NULL) { 412 rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf)); 413 fprintf(fp, "branch\t%s;\n", numbuf); 414 } 415 416 fputs("access", fp); 417 TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) { 418 fprintf(fp, "\n\t%s", ap->ra_name); 419 } 420 fputs(";\n", fp); 421 422 fprintf(fp, "symbols"); 423 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 424 if (RCSNUM_ISBRANCH(symp->rs_num)) 425 rcsnum_addmagic(symp->rs_num); 426 rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); 427 if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) || 428 strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) || 429 strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf)) 430 fatal("rcs_write: string overflow"); 431 fprintf(fp, "\n\t%s", buf); 432 } 433 fprintf(fp, ";\n"); 434 435 fprintf(fp, "locks"); 436 TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) { 437 rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf)); 438 fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf); 439 } 440 441 fprintf(fp, ";"); 442 443 if (rfp->rf_flags & RCS_SLOCK) 444 fprintf(fp, " strict;"); 445 fputc('\n', fp); 446 447 fputs("comment\t@", fp); 448 if (rfp->rf_comment != NULL) { 449 rcs_strprint((const u_char *)rfp->rf_comment, 450 strlen(rfp->rf_comment), fp); 451 fputs("@;\n", fp); 452 } else 453 fputs("# @;\n", fp); 454 455 if (rfp->rf_expand != NULL) { 456 fputs("expand @", fp); 457 rcs_strprint((const u_char *)rfp->rf_expand, 458 strlen(rfp->rf_expand), fp); 459 fputs("@;\n", fp); 460 } 461 462 fputs("\n\n", fp); 463 464 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 465 fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 466 sizeof(numbuf))); 467 fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;", 468 rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1, 469 rdp->rd_date.tm_mday, rdp->rd_date.tm_hour, 470 rdp->rd_date.tm_min, rdp->rd_date.tm_sec); 471 fprintf(fp, "\tauthor %s;\tstate %s;\n", 472 rdp->rd_author, rdp->rd_state); 473 fputs("branches", fp); 474 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 475 fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf, 476 sizeof(numbuf))); 477 } 478 fputs(";\n", fp); 479 fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next, 480 numbuf, sizeof(numbuf))); 481 } 482 483 fputs("\ndesc\n@", fp); 484 if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) { 485 rcs_strprint((const u_char *)rfp->rf_desc, len, fp); 486 if (rfp->rf_desc[len-1] != '\n') 487 fputc('\n', fp); 488 } 489 fputs("@\n", fp); 490 491 /* deltatexts */ 492 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 493 fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf, 494 sizeof(numbuf))); 495 fputs("log\n@", fp); 496 if (rdp->rd_log != NULL) { 497 len = strlen(rdp->rd_log); 498 rcs_strprint((const u_char *)rdp->rd_log, len, fp); 499 if (rdp->rd_log[len-1] != '\n') 500 fputc('\n', fp); 501 } 502 fputs("@\ntext\n@", fp); 503 if (rdp->rd_text != NULL) { 504 rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp); 505 } 506 fputs("@\n", fp); 507 } 508 509 if (fchmod(fd, rfp->rf_mode) == -1) { 510 saved_errno = errno; 511 (void)unlink(fn); 512 fatal("fchmod %s: %s", fn, strerror(saved_errno)); 513 } 514 515 (void)fclose(fp); 516 517 if (rename(fn, rfp->rf_path) == -1) { 518 saved_errno = errno; 519 (void)unlink(fn); 520 fatal("rename(%s, %s): %s", fn, rfp->rf_path, 521 strerror(saved_errno)); 522 } 523 524 rfp->rf_flags |= RCS_SYNCED; 525 526 if (fn != NULL) 527 xfree(fn); 528 } 529 530 /* 531 * rcs_head_get() 532 * 533 * Retrieve the revision number of the head revision for the RCS file <file>. 534 */ 535 RCSNUM * 536 rcs_head_get(RCSFILE *file) 537 { 538 struct rcs_branch *brp; 539 struct rcs_delta *rdp; 540 RCSNUM *rev, *rootrev; 541 542 if (file->rf_head == NULL) 543 return NULL; 544 545 rev = rcsnum_alloc(); 546 if (file->rf_branch != NULL) { 547 /* we have a default branch, use that to calculate the 548 * real HEAD*/ 549 rootrev = rcsnum_alloc(); 550 rcsnum_cpy(file->rf_branch, rootrev, 2); 551 if ((rdp = rcs_findrev(file, rootrev)) == NULL) 552 fatal("rcs_head_get: could not find root revision"); 553 554 /* HEAD should be the last revision on the default branch */ 555 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 556 if (TAILQ_NEXT(brp, rb_list) == NULL) 557 break; 558 } 559 rcsnum_free(rootrev); 560 561 if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL) 562 fatal("rcs_head_get: could not find branch revision"); 563 while (rdp->rd_next->rn_len != 0) 564 if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL) 565 fatal("rcs_head_get: could not find " 566 "next branch revision"); 567 568 rcsnum_cpy(rdp->rd_num, rev, 0); 569 } else { 570 rcsnum_cpy(file->rf_head, rev, 0); 571 } 572 573 return (rev); 574 } 575 576 /* 577 * rcs_head_set() 578 * 579 * Set the revision number of the head revision for the RCS file <file> to 580 * <rev>, which must reference a valid revision within the file. 581 */ 582 int 583 rcs_head_set(RCSFILE *file, RCSNUM *rev) 584 { 585 if (rcs_findrev(file, rev) == NULL) 586 return (-1); 587 588 if (file->rf_head == NULL) 589 file->rf_head = rcsnum_alloc(); 590 591 rcsnum_cpy(rev, file->rf_head, 0); 592 file->rf_flags &= ~RCS_SYNCED; 593 return (0); 594 } 595 596 /* 597 * rcs_branch_new() 598 * 599 * Create a new branch out of supplied revision for the RCS file <file>. 600 */ 601 RCSNUM * 602 rcs_branch_new(RCSFILE *file, RCSNUM *rev) 603 { 604 RCSNUM *brev; 605 struct rcs_sym *sym; 606 607 if ((brev = rcsnum_new_branch(rev)) == NULL) 608 return (NULL); 609 610 for (;;) { 611 TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list) 612 if (!rcsnum_cmp(sym->rs_num, brev, 0)) 613 break; 614 615 if (sym != NULL) { 616 if (rcsnum_inc(brev) == NULL || 617 rcsnum_inc(brev) == NULL) 618 return (NULL); 619 } else 620 break; 621 } 622 623 return (brev); 624 } 625 626 /* 627 * rcs_branch_get() 628 * 629 * Retrieve the default branch number for the RCS file <file>. 630 * Returns the number on success. If NULL is returned, then there is no 631 * default branch for this file. 632 */ 633 const RCSNUM * 634 rcs_branch_get(RCSFILE *file) 635 { 636 return (file->rf_branch); 637 } 638 639 /* 640 * rcs_branch_set() 641 * 642 * Set the default branch for the RCS file <file> to <bnum>. 643 * Returns 0 on success, -1 on failure. 644 */ 645 int 646 rcs_branch_set(RCSFILE *file, const RCSNUM *bnum) 647 { 648 if (file->rf_branch == NULL) 649 file->rf_branch = rcsnum_alloc(); 650 651 rcsnum_cpy(bnum, file->rf_branch, 0); 652 file->rf_flags &= ~RCS_SYNCED; 653 return (0); 654 } 655 656 /* 657 * rcs_access_add() 658 * 659 * Add the login name <login> to the access list for the RCS file <file>. 660 * Returns 0 on success, or -1 on failure. 661 */ 662 int 663 rcs_access_add(RCSFILE *file, const char *login) 664 { 665 struct rcs_access *ap; 666 667 /* first look for duplication */ 668 TAILQ_FOREACH(ap, &(file->rf_access), ra_list) { 669 if (strcmp(ap->ra_name, login) == 0) 670 return (-1); 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 return (-1); 700 701 TAILQ_REMOVE(&(file->rf_access), ap, ra_list); 702 xfree(ap->ra_name); 703 xfree(ap); 704 705 /* not synced anymore */ 706 file->rf_flags &= ~RCS_SYNCED; 707 return (0); 708 } 709 710 /* 711 * rcs_sym_add() 712 * 713 * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol 714 * is named <sym> and is bound to the RCS revision <snum>. 715 */ 716 int 717 rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum) 718 { 719 struct rcs_sym *symp; 720 721 if (!rcs_sym_check(sym)) 722 return (-1); 723 724 /* first look for duplication */ 725 TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { 726 if (strcmp(symp->rs_name, sym) == 0) 727 return (1); 728 } 729 730 symp = xmalloc(sizeof(*symp)); 731 symp->rs_name = xstrdup(sym); 732 symp->rs_num = rcsnum_alloc(); 733 rcsnum_cpy(snum, symp->rs_num, 0); 734 735 TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list); 736 737 /* not synced anymore */ 738 rfp->rf_flags &= ~RCS_SYNCED; 739 return (0); 740 } 741 742 /* 743 * rcs_sym_remove() 744 * 745 * Remove the symbol with name <sym> from the symbol list for the RCS file 746 * <file>. If no such symbol is found, the call fails and returns with an 747 * error. 748 * Returns 0 on success, or -1 on failure. 749 */ 750 int 751 rcs_sym_remove(RCSFILE *file, const char *sym) 752 { 753 struct rcs_sym *symp; 754 755 if (!rcs_sym_check(sym)) 756 return (-1); 757 758 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 759 if (strcmp(symp->rs_name, sym) == 0) 760 break; 761 762 if (symp == NULL) 763 return (-1); 764 765 TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list); 766 xfree(symp->rs_name); 767 rcsnum_free(symp->rs_num); 768 xfree(symp); 769 770 /* not synced anymore */ 771 file->rf_flags &= ~RCS_SYNCED; 772 return (0); 773 } 774 775 /* 776 * rcs_sym_get() 777 * 778 * Find a specific symbol <sym> entry in the tree of the RCS file <file>. 779 * 780 * Returns a pointer to the symbol on success, or NULL on failure. 781 */ 782 struct rcs_sym * 783 rcs_sym_get(RCSFILE *file, const char *sym) 784 { 785 struct rcs_sym *symp; 786 787 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 788 if (strcmp(symp->rs_name, sym) == 0) 789 return (symp); 790 791 return (NULL); 792 } 793 794 /* 795 * rcs_sym_getrev() 796 * 797 * Retrieve the RCS revision number associated with the symbol <sym> for the 798 * RCS file <file>. The returned value is a dynamically-allocated copy and 799 * should be freed by the caller once they are done with it. 800 * Returns the RCSNUM on success, or NULL on failure. 801 */ 802 RCSNUM * 803 rcs_sym_getrev(RCSFILE *file, const char *sym) 804 { 805 RCSNUM *num; 806 struct rcs_sym *symp; 807 808 if (!rcs_sym_check(sym) || file->rf_head == NULL) 809 return (NULL); 810 811 if (!strcmp(sym, RCS_HEAD_BRANCH)) { 812 num = rcsnum_alloc(); 813 rcsnum_cpy(file->rf_head, num, 0); 814 return (num); 815 } 816 817 num = NULL; 818 TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list) 819 if (strcmp(symp->rs_name, sym) == 0) 820 break; 821 822 if (symp != NULL) { 823 num = rcsnum_alloc(); 824 rcsnum_cpy(symp->rs_num, num, 0); 825 } 826 827 return (num); 828 } 829 830 /* 831 * rcs_sym_check() 832 * 833 * Check the RCS symbol name <sym> for any unsupported characters. 834 * Returns 1 if the tag is correct, 0 if it isn't valid. 835 */ 836 int 837 rcs_sym_check(const char *sym) 838 { 839 int ret; 840 const char *cp; 841 842 ret = 1; 843 cp = sym; 844 if (!isalpha(*cp++)) 845 return (0); 846 847 for (; *cp != '\0'; cp++) 848 if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) { 849 ret = 0; 850 break; 851 } 852 853 return (ret); 854 } 855 856 /* 857 * rcs_lock_getmode() 858 * 859 * Retrieve the locking mode of the RCS file <file>. 860 */ 861 int 862 rcs_lock_getmode(RCSFILE *file) 863 { 864 return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE; 865 } 866 867 /* 868 * rcs_lock_setmode() 869 * 870 * Set the locking mode of the RCS file <file> to <mode>, which must either 871 * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT. 872 * Returns the previous mode on success, or -1 on failure. 873 */ 874 int 875 rcs_lock_setmode(RCSFILE *file, int mode) 876 { 877 int pmode; 878 pmode = rcs_lock_getmode(file); 879 880 if (mode == RCS_LOCK_STRICT) 881 file->rf_flags |= RCS_SLOCK; 882 else if (mode == RCS_LOCK_LOOSE) 883 file->rf_flags &= ~RCS_SLOCK; 884 else 885 fatal("rcs_lock_setmode: invalid mode `%d'", mode); 886 887 file->rf_flags &= ~RCS_SYNCED; 888 return (pmode); 889 } 890 891 /* 892 * rcs_lock_add() 893 * 894 * Add an RCS lock for the user <user> on revision <rev>. 895 * Returns 0 on success, or -1 on failure. 896 */ 897 int 898 rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev) 899 { 900 struct rcs_lock *lkp; 901 902 /* first look for duplication */ 903 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { 904 if (strcmp(lkp->rl_name, user) == 0 && 905 rcsnum_cmp(rev, lkp->rl_num, 0) == 0) 906 return (-1); 907 } 908 909 lkp = xmalloc(sizeof(*lkp)); 910 lkp->rl_name = xstrdup(user); 911 lkp->rl_num = rcsnum_alloc(); 912 rcsnum_cpy(rev, lkp->rl_num, 0); 913 914 TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list); 915 916 /* not synced anymore */ 917 file->rf_flags &= ~RCS_SYNCED; 918 return (0); 919 } 920 921 922 /* 923 * rcs_lock_remove() 924 * 925 * Remove the RCS lock on revision <rev>. 926 * Returns 0 on success, or -1 on failure. 927 */ 928 int 929 rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev) 930 { 931 struct rcs_lock *lkp; 932 933 TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) { 934 if (strcmp(lkp->rl_name, user) == 0 && 935 rcsnum_cmp(lkp->rl_num, rev, 0) == 0) 936 break; 937 } 938 939 if (lkp == NULL) 940 return (-1); 941 942 TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list); 943 rcsnum_free(lkp->rl_num); 944 xfree(lkp->rl_name); 945 xfree(lkp); 946 947 /* not synced anymore */ 948 file->rf_flags &= ~RCS_SYNCED; 949 return (0); 950 } 951 952 /* 953 * rcs_desc_get() 954 * 955 * Retrieve the description for the RCS file <file>. 956 */ 957 const char * 958 rcs_desc_get(RCSFILE *file) 959 { 960 return (file->rf_desc); 961 } 962 963 /* 964 * rcs_desc_set() 965 * 966 * Set the description for the RCS file <file>. 967 */ 968 void 969 rcs_desc_set(RCSFILE *file, const char *desc) 970 { 971 char *tmp; 972 973 tmp = xstrdup(desc); 974 if (file->rf_desc != NULL) 975 xfree(file->rf_desc); 976 file->rf_desc = tmp; 977 file->rf_flags &= ~RCS_SYNCED; 978 } 979 980 /* 981 * rcs_comment_lookup() 982 * 983 * Lookup the assumed comment leader based on a file's suffix. 984 * Returns a pointer to the string on success, or NULL on failure. 985 */ 986 const char * 987 rcs_comment_lookup(const char *filename) 988 { 989 int i; 990 const char *sp; 991 992 if ((sp = strrchr(filename, '.')) == NULL) 993 return (NULL); 994 sp++; 995 996 for (i = 0; i < (int)NB_COMTYPES; i++) 997 if (strcmp(rcs_comments[i].rc_suffix, sp) == 0) 998 return (rcs_comments[i].rc_cstr); 999 return (NULL); 1000 } 1001 1002 /* 1003 * rcs_comment_get() 1004 * 1005 * Retrieve the comment leader for the RCS file <file>. 1006 */ 1007 const char * 1008 rcs_comment_get(RCSFILE *file) 1009 { 1010 return (file->rf_comment); 1011 } 1012 1013 /* 1014 * rcs_comment_set() 1015 * 1016 * Set the comment leader for the RCS file <file>. 1017 */ 1018 void 1019 rcs_comment_set(RCSFILE *file, const char *comment) 1020 { 1021 char *tmp; 1022 1023 tmp = xstrdup(comment); 1024 if (file->rf_comment != NULL) 1025 xfree(file->rf_comment); 1026 file->rf_comment = tmp; 1027 file->rf_flags &= ~RCS_SYNCED; 1028 } 1029 1030 int 1031 rcs_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines, 1032 struct cvs_line **alines, struct rcs_delta *rdp) 1033 { 1034 u_char op; 1035 char *ep; 1036 struct cvs_line *lp, *dlp, *ndlp; 1037 int i, lineno, nbln; 1038 u_char tmp; 1039 1040 dlp = TAILQ_FIRST(&(dlines->l_lines)); 1041 lp = TAILQ_FIRST(&(plines->l_lines)); 1042 1043 /* skip first bogus line */ 1044 for (lp = TAILQ_NEXT(lp, l_list); lp != NULL; 1045 lp = TAILQ_NEXT(lp, l_list)) { 1046 if (lp->l_len < 2) 1047 fatal("line too short, RCS patch seems broken"); 1048 op = *(lp->l_line); 1049 /* NUL-terminate line buffer for strtol() safety. */ 1050 tmp = lp->l_line[lp->l_len - 1]; 1051 lp->l_line[lp->l_len - 1] = '\0'; 1052 lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10); 1053 if (lineno - 1 > dlines->l_nblines || lineno < 0) { 1054 fatal("invalid line specification in RCS patch"); 1055 } 1056 ep++; 1057 nbln = (int)strtol(ep, &ep, 10); 1058 /* Restore the last byte of the buffer */ 1059 lp->l_line[lp->l_len - 1] = tmp; 1060 if (nbln < 0) 1061 fatal("invalid line number specification in RCS patch"); 1062 1063 /* find the appropriate line */ 1064 for (;;) { 1065 if (dlp == NULL) 1066 break; 1067 if (dlp->l_lineno == lineno) 1068 break; 1069 if (dlp->l_lineno > lineno) { 1070 dlp = TAILQ_PREV(dlp, cvs_tqh, l_list); 1071 } else if (dlp->l_lineno < lineno) { 1072 if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) || 1073 ndlp->l_lineno > lineno) 1074 break; 1075 dlp = ndlp; 1076 } 1077 } 1078 if (dlp == NULL) 1079 fatal("can't find referenced line in RCS patch"); 1080 1081 if (op == 'd') { 1082 for (i = 0; (i < nbln) && (dlp != NULL); i++) { 1083 ndlp = TAILQ_NEXT(dlp, l_list); 1084 TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list); 1085 if (alines != NULL && dlp->l_line != NULL) { 1086 dlp->l_delta = rdp; 1087 alines[dlp->l_lineno_orig - 1] = 1088 dlp; 1089 } else 1090 xfree(dlp); 1091 dlp = ndlp; 1092 /* last line is gone - reset dlp */ 1093 if (dlp == NULL) { 1094 ndlp = TAILQ_LAST(&(dlines->l_lines), 1095 cvs_tqh); 1096 dlp = ndlp; 1097 } 1098 } 1099 } else if (op == 'a') { 1100 for (i = 0; i < nbln; i++) { 1101 ndlp = lp; 1102 lp = TAILQ_NEXT(lp, l_list); 1103 if (lp == NULL) 1104 fatal("truncated RCS patch"); 1105 TAILQ_REMOVE(&(plines->l_lines), lp, l_list); 1106 if (alines != NULL) { 1107 if (lp->l_needsfree == 1) 1108 xfree(lp->l_line); 1109 lp->l_line = NULL; 1110 lp->l_needsfree = 0; 1111 } 1112 lp->l_delta = rdp; 1113 TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp, 1114 lp, l_list); 1115 dlp = lp; 1116 1117 /* we don't want lookup to block on those */ 1118 lp->l_lineno = lineno; 1119 1120 lp = ndlp; 1121 } 1122 } else 1123 fatal("unknown RCS patch operation `%c'", op); 1124 1125 /* last line of the patch, done */ 1126 if (lp->l_lineno == plines->l_nblines) 1127 break; 1128 } 1129 1130 /* once we're done patching, rebuild the line numbers */ 1131 lineno = 0; 1132 TAILQ_FOREACH(lp, &(dlines->l_lines), l_list) 1133 lp->l_lineno = lineno++; 1134 dlines->l_nblines = lineno - 1; 1135 1136 return (0); 1137 } 1138 1139 void 1140 rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved) 1141 { 1142 struct cvs_lines *plines; 1143 struct cvs_line *lp; 1144 int added, i, lineno, nbln, removed; 1145 char op, *ep; 1146 u_char tmp; 1147 1148 added = removed = 0; 1149 1150 plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen); 1151 lp = TAILQ_FIRST(&(plines->l_lines)); 1152 1153 /* skip first bogus line */ 1154 for (lp = TAILQ_NEXT(lp, l_list); lp != NULL; 1155 lp = TAILQ_NEXT(lp, l_list)) { 1156 if (lp->l_len < 2) 1157 fatal("line too short, RCS patch seems broken"); 1158 op = *(lp->l_line); 1159 /* NUL-terminate line buffer for strtol() safety. */ 1160 tmp = lp->l_line[lp->l_len - 1]; 1161 lp->l_line[lp->l_len - 1] = '\0'; 1162 lineno = (int)strtol((lp->l_line + 1), &ep, 10); 1163 ep++; 1164 nbln = (int)strtol(ep, &ep, 10); 1165 /* Restore the last byte of the buffer */ 1166 lp->l_line[lp->l_len - 1] = tmp; 1167 if (nbln < 0) 1168 fatal("invalid line number specification in RCS patch"); 1169 1170 if (op == 'a') { 1171 added += nbln; 1172 for (i = 0; i < nbln; i++) { 1173 lp = TAILQ_NEXT(lp, l_list); 1174 if (lp == NULL) 1175 fatal("truncated RCS patch"); 1176 } 1177 } 1178 else if (op == 'd') 1179 removed += nbln; 1180 else 1181 fatal("unknown RCS patch operation '%c'", op); 1182 } 1183 1184 cvs_freelines(plines); 1185 1186 *ladded = added; 1187 *lremoved = removed; 1188 } 1189 1190 /* 1191 * rcs_rev_add() 1192 * 1193 * Add a revision to the RCS file <rf>. The new revision's number can be 1194 * specified in <rev> (which can also be RCS_HEAD_REV, in which case the 1195 * new revision will have a number equal to the previous head revision plus 1196 * one). The <msg> argument specifies the log message for that revision, and 1197 * <date> specifies the revision's date (a value of -1 is 1198 * equivalent to using the current time). 1199 * If <username> is NULL, set the author for this revision to the current user. 1200 * Otherwise, set it to <username>. 1201 * Returns 0 on success, or -1 on failure. 1202 */ 1203 int 1204 rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date, 1205 const char *username) 1206 { 1207 time_t now; 1208 RCSNUM *root = NULL; 1209 struct passwd *pw; 1210 struct rcs_branch *brp, *obrp; 1211 struct rcs_delta *ordp, *rdp; 1212 1213 if (rev == RCS_HEAD_REV) { 1214 if (rf->rf_flags & RCS_CREATE) { 1215 if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL) 1216 return (-1); 1217 if (rf->rf_head != NULL) 1218 xfree(rf->rf_head); 1219 rf->rf_head = rcsnum_alloc(); 1220 rcsnum_cpy(rev, rf->rf_head, 0); 1221 } else if (rf->rf_head == NULL) { 1222 return (-1); 1223 } else { 1224 rev = rcsnum_inc(rf->rf_head); 1225 } 1226 } else { 1227 if ((rdp = rcs_findrev(rf, rev)) != NULL) 1228 return (-1); 1229 } 1230 1231 if ((pw = getpwuid(getuid())) == NULL) 1232 fatal("getpwuid failed"); 1233 1234 rdp = xcalloc(1, sizeof(*rdp)); 1235 1236 TAILQ_INIT(&(rdp->rd_branches)); 1237 1238 rdp->rd_num = rcsnum_alloc(); 1239 rcsnum_cpy(rev, rdp->rd_num, 0); 1240 1241 rdp->rd_next = rcsnum_alloc(); 1242 1243 if (username == NULL) 1244 username = pw->pw_name; 1245 1246 rdp->rd_author = xstrdup(username); 1247 rdp->rd_state = xstrdup(RCS_STATE_EXP); 1248 rdp->rd_log = xstrdup(msg); 1249 1250 if (date != (time_t)(-1)) 1251 now = date; 1252 else 1253 time(&now); 1254 gmtime_r(&now, &(rdp->rd_date)); 1255 1256 if (RCSNUM_ISBRANCHREV(rev)) 1257 TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list); 1258 else 1259 TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list); 1260 rf->rf_ndelta++; 1261 1262 if (!(rf->rf_flags & RCS_CREATE)) { 1263 if (RCSNUM_ISBRANCHREV(rev)) { 1264 if (rev->rn_id[rev->rn_len - 1] == 1) { 1265 /* a new branch */ 1266 root = rcsnum_branch_root(rev); 1267 brp = xmalloc(sizeof(*brp)); 1268 brp->rb_num = rcsnum_alloc(); 1269 rcsnum_cpy(rdp->rd_num, brp->rb_num, 0); 1270 1271 if ((ordp = rcs_findrev(rf, root)) == NULL) 1272 fatal("root node not found"); 1273 1274 TAILQ_FOREACH(obrp, &(ordp->rd_branches), 1275 rb_list) { 1276 if (!rcsnum_cmp(obrp->rb_num, 1277 brp->rb_num, 1278 brp->rb_num->rn_len - 1)) 1279 break; 1280 } 1281 1282 if (obrp == NULL) { 1283 TAILQ_INSERT_TAIL(&(ordp->rd_branches), 1284 brp, rb_list); 1285 } 1286 } else { 1287 root = rcsnum_alloc(); 1288 rcsnum_cpy(rev, root, 0); 1289 rcsnum_dec(root); 1290 if ((ordp = rcs_findrev(rf, root)) == NULL) 1291 fatal("previous revision not found"); 1292 rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0); 1293 } 1294 } else { 1295 ordp = TAILQ_NEXT(rdp, rd_list); 1296 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0); 1297 } 1298 } 1299 1300 if (root != NULL) 1301 rcsnum_free(root); 1302 1303 /* not synced anymore */ 1304 rf->rf_flags &= ~RCS_SYNCED; 1305 1306 return (0); 1307 } 1308 1309 /* 1310 * rcs_rev_remove() 1311 * 1312 * Remove the revision whose number is <rev> from the RCS file <rf>. 1313 */ 1314 int 1315 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) 1316 { 1317 int fd1, fd2; 1318 char *path_tmp1, *path_tmp2; 1319 struct rcs_delta *rdp, *prevrdp, *nextrdp; 1320 BUF *prevbuf, *newdiff, *newdeltatext; 1321 1322 if (rev == RCS_HEAD_REV) 1323 rev = rf->rf_head; 1324 1325 if (rev == NULL) 1326 return (-1); 1327 1328 /* do we actually have that revision? */ 1329 if ((rdp = rcs_findrev(rf, rev)) == NULL) 1330 return (-1); 1331 1332 /* 1333 * This is confusing, the previous delta is next in the TAILQ list. 1334 * the next delta is the previous one in the TAILQ list. 1335 * 1336 * When the HEAD revision got specified, nextrdp will be NULL. 1337 * When the first revision got specified, prevrdp will be NULL. 1338 */ 1339 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list); 1340 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list); 1341 1342 newdeltatext = NULL; 1343 prevbuf = NULL; 1344 path_tmp1 = path_tmp2 = NULL; 1345 1346 if (prevrdp != NULL && nextrdp != NULL) { 1347 newdiff = cvs_buf_alloc(64); 1348 1349 /* calculate new diff */ 1350 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 1351 fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0); 1352 1353 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 1354 fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0); 1355 1356 diff_format = D_RCSDIFF; 1357 if (cvs_diffreg(path_tmp1, path_tmp2, 1358 fd1, fd2, newdiff) == D_ERROR) 1359 fatal("rcs_diffreg failed"); 1360 1361 close(fd1); 1362 close(fd2); 1363 1364 newdeltatext = newdiff; 1365 } else if (nextrdp == NULL && prevrdp != NULL) { 1366 newdeltatext = prevbuf; 1367 } 1368 1369 if (newdeltatext != NULL) { 1370 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0) 1371 fatal("error setting new deltatext"); 1372 } 1373 1374 TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list); 1375 1376 /* update pointers */ 1377 if (prevrdp != NULL && nextrdp != NULL) { 1378 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0); 1379 } else if (prevrdp != NULL) { 1380 if (rcs_head_set(rf, prevrdp->rd_num) < 0) 1381 fatal("rcs_head_set failed"); 1382 } else if (nextrdp != NULL) { 1383 rcsnum_free(nextrdp->rd_next); 1384 nextrdp->rd_next = rcsnum_alloc(); 1385 } else { 1386 rcsnum_free(rf->rf_head); 1387 rf->rf_head = NULL; 1388 } 1389 1390 rf->rf_ndelta--; 1391 rf->rf_flags &= ~RCS_SYNCED; 1392 1393 rcs_freedelta(rdp); 1394 1395 if (newdeltatext != NULL) 1396 xfree(newdeltatext); 1397 1398 if (path_tmp1 != NULL) 1399 xfree(path_tmp1); 1400 if (path_tmp2 != NULL) 1401 xfree(path_tmp2); 1402 1403 return (0); 1404 } 1405 1406 /* 1407 * rcs_findrev() 1408 * 1409 * Find a specific revision's delta entry in the tree of the RCS file <rfp>. 1410 * The revision number is given in <rev>. 1411 * 1412 * Returns a pointer to the delta on success, or NULL on failure. 1413 */ 1414 struct rcs_delta * 1415 rcs_findrev(RCSFILE *rfp, RCSNUM *rev) 1416 { 1417 int isbrev; 1418 struct rcs_delta *rdp; 1419 1420 if (rev == NULL) 1421 return NULL; 1422 1423 isbrev = RCSNUM_ISBRANCHREV(rev); 1424 1425 /* 1426 * We need to do more parsing if the last revision in the linked list 1427 * is greater than the requested revision. 1428 */ 1429 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1430 if (rdp == NULL || 1431 (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) || 1432 ((isbrev && rdp->rd_num->rn_len < 4) || 1433 (isbrev && rcsnum_differ(rev, rdp->rd_num)))) { 1434 rcs_parse_deltas(rfp, rev); 1435 } 1436 1437 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1438 if (rcsnum_differ(rdp->rd_num, rev)) 1439 continue; 1440 else 1441 return (rdp); 1442 } 1443 1444 return (NULL); 1445 } 1446 1447 /* 1448 * rcs_kwexp_set() 1449 * 1450 * Set the keyword expansion mode to use on the RCS file <file> to <mode>. 1451 */ 1452 void 1453 rcs_kwexp_set(RCSFILE *file, int mode) 1454 { 1455 int i; 1456 char *tmp, buf[8] = ""; 1457 1458 if (RCS_KWEXP_INVAL(mode)) 1459 return; 1460 1461 i = 0; 1462 if (mode == RCS_KWEXP_NONE) 1463 buf[0] = 'b'; 1464 else if (mode == RCS_KWEXP_OLD) 1465 buf[0] = 'o'; 1466 else { 1467 if (mode & RCS_KWEXP_NAME) 1468 buf[i++] = 'k'; 1469 if (mode & RCS_KWEXP_VAL) 1470 buf[i++] = 'v'; 1471 if (mode & RCS_KWEXP_LKR) 1472 buf[i++] = 'l'; 1473 } 1474 1475 tmp = xstrdup(buf); 1476 if (file->rf_expand != NULL) 1477 xfree(file->rf_expand); 1478 file->rf_expand = tmp; 1479 /* not synced anymore */ 1480 file->rf_flags &= ~RCS_SYNCED; 1481 } 1482 1483 /* 1484 * rcs_kwexp_get() 1485 * 1486 * Retrieve the keyword expansion mode to be used for the RCS file <file>. 1487 */ 1488 int 1489 rcs_kwexp_get(RCSFILE *file) 1490 { 1491 return rcs_kflag_get(file->rf_expand); 1492 } 1493 1494 /* 1495 * rcs_kflag_get() 1496 * 1497 * Get the keyword expansion mode from a set of character flags given in 1498 * <flags> and return the appropriate flag mask. In case of an error, the 1499 * returned mask will have the RCS_KWEXP_ERR bit set to 1. 1500 */ 1501 int 1502 rcs_kflag_get(const char *flags) 1503 { 1504 int fl; 1505 size_t len; 1506 const char *fp; 1507 1508 if (flags == NULL) 1509 return 0; 1510 1511 fl = 0; 1512 if (!(len = strlen(flags))) 1513 return RCS_KWEXP_ERR; 1514 1515 for (fp = flags; *fp != '\0'; fp++) { 1516 if (*fp == 'k') 1517 fl |= RCS_KWEXP_NAME; 1518 else if (*fp == 'v') 1519 fl |= RCS_KWEXP_VAL; 1520 else if (*fp == 'l') 1521 fl |= RCS_KWEXP_LKR; 1522 else if (*fp == 'o') { 1523 if (len != 1) 1524 fl |= RCS_KWEXP_ERR; 1525 fl |= RCS_KWEXP_OLD; 1526 } else if (*fp == 'b') { 1527 if (len != 1) 1528 fl |= RCS_KWEXP_ERR; 1529 fl |= RCS_KWEXP_NONE; 1530 } else /* unknown letter */ 1531 fl |= RCS_KWEXP_ERR; 1532 } 1533 1534 return (fl); 1535 } 1536 1537 /* rcs_parse_deltas() 1538 * 1539 * Parse deltas. If <rev> is not NULL, parse only as far as that 1540 * revision. If <rev> is NULL, parse all deltas. 1541 */ 1542 static void 1543 rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev) 1544 { 1545 int ret; 1546 struct rcs_delta *enddelta; 1547 1548 if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE)) 1549 return; 1550 1551 for (;;) { 1552 ret = rcs_parse_delta(rfp); 1553 if (rev != NULL) { 1554 enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1555 if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0) 1556 break; 1557 } 1558 1559 if (ret == 0) { 1560 rfp->rf_flags |= PARSED_DELTAS; 1561 break; 1562 } 1563 else if (ret == -1) 1564 fatal("error parsing deltas"); 1565 } 1566 } 1567 1568 /* rcs_parse_deltatexts() 1569 * 1570 * Parse deltatexts. If <rev> is not NULL, parse only as far as that 1571 * revision. If <rev> is NULL, parse everything. 1572 */ 1573 static void 1574 rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev) 1575 { 1576 int ret; 1577 struct rcs_delta *rdp; 1578 1579 if ((rfp->rf_flags & PARSED_DELTATEXTS) || 1580 (rfp->rf_flags & RCS_CREATE)) 1581 return; 1582 1583 if (!(rfp->rf_flags & PARSED_DESC)) 1584 rcs_parse_desc(rfp, rev); 1585 for (;;) { 1586 if (rev != NULL) { 1587 rdp = rcs_findrev(rfp, rev); 1588 if (rdp->rd_text != NULL) 1589 break; 1590 else 1591 ret = rcs_parse_deltatext(rfp); 1592 } else 1593 ret = rcs_parse_deltatext(rfp); 1594 if (ret == 0) { 1595 rfp->rf_flags |= PARSED_DELTATEXTS; 1596 break; 1597 } 1598 else if (ret == -1) 1599 fatal("problem parsing deltatexts"); 1600 } 1601 } 1602 1603 /* rcs_parse_desc() 1604 * 1605 * Parse RCS description. 1606 */ 1607 static void 1608 rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev) 1609 { 1610 int ret = 0; 1611 1612 if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE)) 1613 return; 1614 if (!(rfp->rf_flags & PARSED_DELTAS)) 1615 rcs_parse_deltas(rfp, rev); 1616 /* do parsing */ 1617 ret = rcs_gettok(rfp); 1618 if (ret != RCS_TOK_DESC) 1619 fatal("token `%s' found where RCS desc expected", 1620 RCS_TOKSTR(rfp)); 1621 1622 ret = rcs_gettok(rfp); 1623 if (ret != RCS_TOK_STRING) 1624 fatal("token `%s' found where RCS desc expected", 1625 RCS_TOKSTR(rfp)); 1626 1627 rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp)); 1628 rfp->rf_flags |= PARSED_DESC; 1629 } 1630 1631 /* 1632 * rcs_parse_init() 1633 * 1634 * Initial parsing of file <path>, which are in the RCS format. 1635 * Just does admin section. 1636 */ 1637 static void 1638 rcs_parse_init(RCSFILE *rfp) 1639 { 1640 struct rcs_pdata *pdp; 1641 1642 if (rfp->rf_flags & RCS_PARSED) 1643 return; 1644 1645 pdp = xcalloc(1, sizeof(*pdp)); 1646 1647 pdp->rp_lines = 0; 1648 pdp->rp_pttype = RCS_TOK_ERR; 1649 1650 if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL) 1651 fatal("fdopen: `%s'", rfp->rf_path); 1652 1653 pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); 1654 pdp->rp_blen = RCS_BUFSIZE; 1655 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 1656 1657 /* ditch the strict lock */ 1658 rfp->rf_flags &= ~RCS_SLOCK; 1659 rfp->rf_pdata = pdp; 1660 1661 if (rcs_parse_admin(rfp) < 0) { 1662 rcs_freepdata(pdp); 1663 fatal("could not parse admin data"); 1664 } 1665 1666 if (rfp->rf_flags & RCS_PARSE_FULLY) { 1667 rcs_parse_deltatexts(rfp, NULL); 1668 (void)close(rfp->fd); 1669 rfp->fd = -1; 1670 } 1671 1672 rfp->rf_flags |= RCS_SYNCED; 1673 } 1674 1675 /* 1676 * rcs_parse_admin() 1677 * 1678 * Parse the administrative portion of an RCS file. 1679 * Returns the type of the first token found after the admin section on 1680 * success, or -1 on failure. 1681 */ 1682 static int 1683 rcs_parse_admin(RCSFILE *rfp) 1684 { 1685 u_int i; 1686 int tok, ntok, hmask; 1687 struct rcs_key *rk; 1688 1689 rfp->rf_head = NULL; 1690 rfp->rf_branch = NULL; 1691 1692 /* hmask is a mask of the headers already encountered */ 1693 hmask = 0; 1694 for (;;) { 1695 tok = rcs_gettok(rfp); 1696 if (tok == RCS_TOK_ERR) { 1697 cvs_log(LP_ERR, "parse error in RCS admin section"); 1698 goto fail; 1699 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1700 /* 1701 * Assume this is the start of the first delta or 1702 * that we are dealing with an empty RCS file and 1703 * we just found the description. 1704 */ 1705 if (!(hmask & (1 << RCS_TOK_HEAD))) { 1706 cvs_log(LP_ERR, "head missing"); 1707 goto fail; 1708 } 1709 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1710 return (tok); 1711 } 1712 1713 rk = NULL; 1714 for (i = 0; i < RCS_NKEYS; i++) 1715 if (rcs_keys[i].rk_id == tok) 1716 rk = &(rcs_keys[i]); 1717 1718 if (hmask & (1 << tok)) { 1719 cvs_log(LP_ERR, "duplicate RCS key"); 1720 goto fail; 1721 } 1722 hmask |= (1 << tok); 1723 1724 switch (tok) { 1725 case RCS_TOK_HEAD: 1726 case RCS_TOK_BRANCH: 1727 case RCS_TOK_COMMENT: 1728 case RCS_TOK_EXPAND: 1729 ntok = rcs_gettok(rfp); 1730 if (ntok == RCS_TOK_SCOLON) 1731 break; 1732 if (ntok != rk->rk_val) { 1733 cvs_log(LP_ERR, 1734 "invalid value type for RCS key `%s'", 1735 rk->rk_str); 1736 } 1737 1738 if (tok == RCS_TOK_HEAD) { 1739 if (rfp->rf_head == NULL) 1740 rfp->rf_head = rcsnum_alloc(); 1741 if (RCS_TOKSTR(rfp)[0] == '\0' || 1742 rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1743 rfp->rf_head) < 0) { 1744 rcsnum_free(rfp->rf_head); 1745 rfp->rf_head = NULL; 1746 } 1747 } else if (tok == RCS_TOK_BRANCH) { 1748 if (rfp->rf_branch == NULL) 1749 rfp->rf_branch = rcsnum_alloc(); 1750 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1751 rfp->rf_branch) < 0) 1752 goto fail; 1753 } else if (tok == RCS_TOK_COMMENT) { 1754 rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp)); 1755 } else if (tok == RCS_TOK_EXPAND) { 1756 rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp)); 1757 } 1758 1759 /* now get the expected semi-colon */ 1760 ntok = rcs_gettok(rfp); 1761 if (ntok != RCS_TOK_SCOLON) { 1762 cvs_log(LP_ERR, 1763 "missing semi-colon after RCS `%s' key", 1764 rk->rk_str); 1765 goto fail; 1766 } 1767 break; 1768 case RCS_TOK_ACCESS: 1769 if (rcs_parse_access(rfp) < 0) 1770 goto fail; 1771 break; 1772 case RCS_TOK_SYMBOLS: 1773 rcs_ignore_keys = 1; 1774 if (rcs_parse_symbols(rfp) < 0) 1775 goto fail; 1776 break; 1777 case RCS_TOK_LOCKS: 1778 if (rcs_parse_locks(rfp) < 0) 1779 goto fail; 1780 break; 1781 default: 1782 cvs_log(LP_ERR, 1783 "unexpected token `%s' in RCS admin section", 1784 RCS_TOKSTR(rfp)); 1785 goto fail; 1786 } 1787 1788 rcs_ignore_keys = 0; 1789 1790 } 1791 1792 fail: 1793 return (-1); 1794 } 1795 1796 /* 1797 * rcs_parse_delta() 1798 * 1799 * Parse an RCS delta section and allocate the structure to store that delta's 1800 * information in the <rfp> delta list. 1801 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1802 * -1 on error. 1803 */ 1804 static int 1805 rcs_parse_delta(RCSFILE *rfp) 1806 { 1807 int ret, tok, ntok, hmask; 1808 u_int i; 1809 char *tokstr; 1810 RCSNUM *datenum; 1811 struct rcs_delta *rdp; 1812 struct rcs_key *rk; 1813 1814 tok = rcs_gettok(rfp); 1815 if (tok == RCS_TOK_DESC) { 1816 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1817 return (0); 1818 } else if (tok != RCS_TOK_NUM) { 1819 cvs_log(LP_ERR, "unexpected token `%s' at start of delta", 1820 RCS_TOKSTR(rfp)); 1821 return (-1); 1822 } 1823 1824 rdp = xcalloc(1, sizeof(*rdp)); 1825 1826 rdp->rd_num = rcsnum_alloc(); 1827 rdp->rd_next = rcsnum_alloc(); 1828 1829 TAILQ_INIT(&(rdp->rd_branches)); 1830 1831 rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num); 1832 1833 hmask = 0; 1834 ret = 0; 1835 tokstr = NULL; 1836 1837 for (;;) { 1838 tok = rcs_gettok(rfp); 1839 if (tok == RCS_TOK_ERR) { 1840 cvs_log(LP_ERR, "parse error in RCS delta section"); 1841 rcs_freedelta(rdp); 1842 return (-1); 1843 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1844 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1845 ret = (tok == RCS_TOK_NUM ? 1 : 0); 1846 break; 1847 } 1848 1849 rk = NULL; 1850 for (i = 0; i < RCS_NKEYS; i++) 1851 if (rcs_keys[i].rk_id == tok) 1852 rk = &(rcs_keys[i]); 1853 1854 if (hmask & (1 << tok)) { 1855 cvs_log(LP_ERR, "duplicate RCS key"); 1856 rcs_freedelta(rdp); 1857 return (-1); 1858 } 1859 hmask |= (1 << tok); 1860 1861 switch (tok) { 1862 case RCS_TOK_DATE: 1863 case RCS_TOK_AUTHOR: 1864 case RCS_TOK_STATE: 1865 case RCS_TOK_NEXT: 1866 ntok = rcs_gettok(rfp); 1867 if (ntok == RCS_TOK_SCOLON) { 1868 if (rk->rk_flags & RCS_VOPT) 1869 break; 1870 else { 1871 cvs_log(LP_ERR, "missing mandatory " 1872 "value to RCS key `%s'", 1873 rk->rk_str); 1874 rcs_freedelta(rdp); 1875 return (-1); 1876 } 1877 } 1878 1879 if (ntok != rk->rk_val) { 1880 cvs_log(LP_ERR, 1881 "invalid value type for RCS key `%s'", 1882 rk->rk_str); 1883 rcs_freedelta(rdp); 1884 return (-1); 1885 } 1886 1887 if (tokstr != NULL) 1888 xfree(tokstr); 1889 tokstr = xstrdup(RCS_TOKSTR(rfp)); 1890 /* now get the expected semi-colon */ 1891 ntok = rcs_gettok(rfp); 1892 if (ntok != RCS_TOK_SCOLON) { 1893 cvs_log(LP_ERR, 1894 "missing semi-colon after RCS `%s' key", 1895 rk->rk_str); 1896 xfree(tokstr); 1897 rcs_freedelta(rdp); 1898 return (-1); 1899 } 1900 1901 if (tok == RCS_TOK_DATE) { 1902 if ((datenum = rcsnum_parse(tokstr)) == NULL) { 1903 xfree(tokstr); 1904 rcs_freedelta(rdp); 1905 return (-1); 1906 } 1907 if (datenum->rn_len != 6) { 1908 cvs_log(LP_ERR, 1909 "RCS date specification has %s " 1910 "fields", 1911 (datenum->rn_len > 6) ? "too many" : 1912 "missing"); 1913 xfree(tokstr); 1914 rcs_freedelta(rdp); 1915 rcsnum_free(datenum); 1916 return (-1); 1917 } 1918 rdp->rd_date.tm_year = datenum->rn_id[0]; 1919 if (rdp->rd_date.tm_year >= 1900) 1920 rdp->rd_date.tm_year -= 1900; 1921 rdp->rd_date.tm_mon = datenum->rn_id[1] - 1; 1922 rdp->rd_date.tm_mday = datenum->rn_id[2]; 1923 rdp->rd_date.tm_hour = datenum->rn_id[3]; 1924 rdp->rd_date.tm_min = datenum->rn_id[4]; 1925 rdp->rd_date.tm_sec = datenum->rn_id[5]; 1926 rcsnum_free(datenum); 1927 } else if (tok == RCS_TOK_AUTHOR) { 1928 rdp->rd_author = tokstr; 1929 tokstr = NULL; 1930 } else if (tok == RCS_TOK_STATE) { 1931 rdp->rd_state = tokstr; 1932 tokstr = NULL; 1933 } else if (tok == RCS_TOK_NEXT) { 1934 rcsnum_aton(tokstr, NULL, rdp->rd_next); 1935 } 1936 break; 1937 case RCS_TOK_BRANCHES: 1938 if (rcs_parse_branches(rfp, rdp) < 0) { 1939 rcs_freedelta(rdp); 1940 return (-1); 1941 } 1942 break; 1943 default: 1944 cvs_log(LP_ERR, "unexpected token `%s' in RCS delta", 1945 RCS_TOKSTR(rfp)); 1946 rcs_freedelta(rdp); 1947 return (-1); 1948 } 1949 } 1950 1951 if (tokstr != NULL) 1952 xfree(tokstr); 1953 1954 TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); 1955 rfp->rf_ndelta++; 1956 1957 return (ret); 1958 } 1959 1960 /* 1961 * rcs_parse_deltatext() 1962 * 1963 * Parse an RCS delta text section and fill in the log and text field of the 1964 * appropriate delta section. 1965 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1966 * -1 on error. 1967 */ 1968 static int 1969 rcs_parse_deltatext(RCSFILE *rfp) 1970 { 1971 int tok; 1972 RCSNUM *tnum; 1973 struct rcs_delta *rdp; 1974 1975 tok = rcs_gettok(rfp); 1976 if (tok == RCS_TOK_EOF) 1977 return (0); 1978 1979 if (tok != RCS_TOK_NUM) { 1980 cvs_log(LP_ERR, 1981 "unexpected token `%s' at start of RCS delta text", 1982 RCS_TOKSTR(rfp)); 1983 return (-1); 1984 } 1985 1986 tnum = rcsnum_alloc(); 1987 rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum); 1988 1989 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1990 if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0) 1991 break; 1992 } 1993 rcsnum_free(tnum); 1994 1995 if (rdp == NULL) { 1996 cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta", 1997 RCS_TOKSTR(rfp)); 1998 return (-1); 1999 } 2000 2001 tok = rcs_gettok(rfp); 2002 if (tok != RCS_TOK_LOG) { 2003 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 2004 RCS_TOKSTR(rfp)); 2005 return (-1); 2006 } 2007 2008 tok = rcs_gettok(rfp); 2009 if (tok != RCS_TOK_STRING) { 2010 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 2011 RCS_TOKSTR(rfp)); 2012 return (-1); 2013 } 2014 rdp->rd_log = xstrdup(RCS_TOKSTR(rfp)); 2015 tok = rcs_gettok(rfp); 2016 if (tok != RCS_TOK_TEXT) { 2017 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 2018 RCS_TOKSTR(rfp)); 2019 return (-1); 2020 } 2021 2022 tok = rcs_gettok(rfp); 2023 if (tok != RCS_TOK_STRING) { 2024 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 2025 RCS_TOKSTR(rfp)); 2026 return (-1); 2027 } 2028 2029 if (RCS_TOKLEN(rfp) == 0) { 2030 rdp->rd_text = xmalloc(1); 2031 rdp->rd_text[0] = '\0'; 2032 rdp->rd_tlen = 0; 2033 } else { 2034 rdp->rd_text = xmalloc(RCS_TOKLEN(rfp)); 2035 memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp)); 2036 rdp->rd_tlen = RCS_TOKLEN(rfp); 2037 } 2038 2039 return (1); 2040 } 2041 2042 /* 2043 * rcs_parse_access() 2044 * 2045 * Parse the access list given as value to the `access' keyword. 2046 * Returns 0 on success, or -1 on failure. 2047 */ 2048 static int 2049 rcs_parse_access(RCSFILE *rfp) 2050 { 2051 int type; 2052 2053 while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) { 2054 if (type != RCS_TOK_ID) { 2055 cvs_log(LP_ERR, "unexpected token `%s' in access list", 2056 RCS_TOKSTR(rfp)); 2057 return (-1); 2058 } 2059 2060 if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0) 2061 return (-1); 2062 } 2063 2064 return (0); 2065 } 2066 2067 /* 2068 * rcs_parse_symbols() 2069 * 2070 * Parse the symbol list given as value to the `symbols' keyword. 2071 * Returns 0 on success, or -1 on failure. 2072 */ 2073 static int 2074 rcs_parse_symbols(RCSFILE *rfp) 2075 { 2076 int type; 2077 struct rcs_sym *symp; 2078 2079 for (;;) { 2080 type = rcs_gettok(rfp); 2081 if (type == RCS_TOK_SCOLON) 2082 break; 2083 2084 if (type != RCS_TOK_ID) { 2085 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2086 RCS_TOKSTR(rfp)); 2087 return (-1); 2088 } 2089 2090 symp = xmalloc(sizeof(*symp)); 2091 symp->rs_name = xstrdup(RCS_TOKSTR(rfp)); 2092 symp->rs_num = rcsnum_alloc(); 2093 2094 type = rcs_gettok(rfp); 2095 if (type != RCS_TOK_COLON) { 2096 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2097 RCS_TOKSTR(rfp)); 2098 rcsnum_free(symp->rs_num); 2099 xfree(symp->rs_name); 2100 xfree(symp); 2101 return (-1); 2102 } 2103 2104 type = rcs_gettok(rfp); 2105 if (type != RCS_TOK_NUM) { 2106 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2107 RCS_TOKSTR(rfp)); 2108 rcsnum_free(symp->rs_num); 2109 xfree(symp->rs_name); 2110 xfree(symp); 2111 return (-1); 2112 } 2113 2114 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) { 2115 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 2116 RCS_TOKSTR(rfp)); 2117 rcsnum_free(symp->rs_num); 2118 xfree(symp->rs_name); 2119 xfree(symp); 2120 return (-1); 2121 } 2122 2123 TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list); 2124 } 2125 2126 return (0); 2127 } 2128 2129 /* 2130 * rcs_parse_locks() 2131 * 2132 * Parse the lock list given as value to the `locks' keyword. 2133 * Returns 0 on success, or -1 on failure. 2134 */ 2135 static int 2136 rcs_parse_locks(RCSFILE *rfp) 2137 { 2138 int type; 2139 struct rcs_lock *lkp; 2140 2141 for (;;) { 2142 type = rcs_gettok(rfp); 2143 if (type == RCS_TOK_SCOLON) 2144 break; 2145 2146 if (type != RCS_TOK_ID) { 2147 cvs_log(LP_ERR, "unexpected token `%s' in lock list", 2148 RCS_TOKSTR(rfp)); 2149 return (-1); 2150 } 2151 2152 lkp = xmalloc(sizeof(*lkp)); 2153 lkp->rl_name = xstrdup(RCS_TOKSTR(rfp)); 2154 lkp->rl_num = rcsnum_alloc(); 2155 2156 type = rcs_gettok(rfp); 2157 if (type != RCS_TOK_COLON) { 2158 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2159 RCS_TOKSTR(rfp)); 2160 rcsnum_free(lkp->rl_num); 2161 xfree(lkp->rl_name); 2162 xfree(lkp); 2163 return (-1); 2164 } 2165 2166 type = rcs_gettok(rfp); 2167 if (type != RCS_TOK_NUM) { 2168 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2169 RCS_TOKSTR(rfp)); 2170 rcsnum_free(lkp->rl_num); 2171 xfree(lkp->rl_name); 2172 xfree(lkp); 2173 return (-1); 2174 } 2175 2176 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) { 2177 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 2178 RCS_TOKSTR(rfp)); 2179 rcsnum_free(lkp->rl_num); 2180 xfree(lkp->rl_name); 2181 xfree(lkp); 2182 return (-1); 2183 } 2184 2185 TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list); 2186 } 2187 2188 /* check if we have a `strict' */ 2189 type = rcs_gettok(rfp); 2190 if (type != RCS_TOK_STRICT) { 2191 rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); 2192 } else { 2193 rfp->rf_flags |= RCS_SLOCK; 2194 2195 type = rcs_gettok(rfp); 2196 if (type != RCS_TOK_SCOLON) { 2197 cvs_log(LP_ERR, 2198 "missing semi-colon after `strict' keyword"); 2199 return (-1); 2200 } 2201 } 2202 2203 return (0); 2204 } 2205 2206 /* 2207 * rcs_parse_branches() 2208 * 2209 * Parse the list of branches following a `branches' keyword in a delta. 2210 * Returns 0 on success, or -1 on failure. 2211 */ 2212 static int 2213 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp) 2214 { 2215 int type; 2216 struct rcs_branch *brp; 2217 2218 for (;;) { 2219 type = rcs_gettok(rfp); 2220 if (type == RCS_TOK_SCOLON) 2221 break; 2222 2223 if (type != RCS_TOK_NUM) { 2224 cvs_log(LP_ERR, 2225 "unexpected token `%s' in list of branches", 2226 RCS_TOKSTR(rfp)); 2227 return (-1); 2228 } 2229 2230 brp = xmalloc(sizeof(*brp)); 2231 brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp)); 2232 if (brp->rb_num == NULL) { 2233 xfree(brp); 2234 return (-1); 2235 } 2236 2237 TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); 2238 } 2239 2240 return (0); 2241 } 2242 2243 /* 2244 * rcs_freedelta() 2245 * 2246 * Free the contents of a delta structure. 2247 */ 2248 static void 2249 rcs_freedelta(struct rcs_delta *rdp) 2250 { 2251 struct rcs_branch *rb; 2252 2253 if (rdp->rd_num != NULL) 2254 rcsnum_free(rdp->rd_num); 2255 if (rdp->rd_next != NULL) 2256 rcsnum_free(rdp->rd_next); 2257 2258 if (rdp->rd_author != NULL) 2259 xfree(rdp->rd_author); 2260 if (rdp->rd_locker != NULL) 2261 xfree(rdp->rd_locker); 2262 if (rdp->rd_state != NULL) 2263 xfree(rdp->rd_state); 2264 if (rdp->rd_log != NULL) 2265 xfree(rdp->rd_log); 2266 if (rdp->rd_text != NULL) 2267 xfree(rdp->rd_text); 2268 2269 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) { 2270 TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list); 2271 rcsnum_free(rb->rb_num); 2272 xfree(rb); 2273 } 2274 2275 xfree(rdp); 2276 } 2277 2278 /* 2279 * rcs_freepdata() 2280 * 2281 * Free the contents of the parser data structure. 2282 */ 2283 static void 2284 rcs_freepdata(struct rcs_pdata *pd) 2285 { 2286 if (pd->rp_file != NULL) 2287 (void)fclose(pd->rp_file); 2288 if (pd->rp_buf != NULL) 2289 xfree(pd->rp_buf); 2290 xfree(pd); 2291 } 2292 2293 /* 2294 * rcs_gettok() 2295 * 2296 * Get the next RCS token from the string <str>. 2297 */ 2298 static int 2299 rcs_gettok(RCSFILE *rfp) 2300 { 2301 u_int i; 2302 int ch, last, type; 2303 size_t len; 2304 char *bp; 2305 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2306 2307 type = RCS_TOK_ERR; 2308 bp = pdp->rp_buf; 2309 pdp->rp_tlen = 0; 2310 *bp = '\0'; 2311 2312 if (pdp->rp_pttype != RCS_TOK_ERR) { 2313 type = pdp->rp_pttype; 2314 if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >= 2315 pdp->rp_blen) 2316 fatal("rcs_gettok: strlcpy"); 2317 pdp->rp_pttype = RCS_TOK_ERR; 2318 return (type); 2319 } 2320 2321 /* skip leading whitespace */ 2322 /* XXX we must skip backspace too for compatibility, should we? */ 2323 do { 2324 ch = getc(pdp->rp_file); 2325 if (ch == '\n') 2326 pdp->rp_lines++; 2327 } while (isspace(ch)); 2328 2329 if (ch == EOF) { 2330 type = RCS_TOK_EOF; 2331 } else if (ch == ';') { 2332 type = RCS_TOK_SCOLON; 2333 } else if (ch == ':') { 2334 type = RCS_TOK_COLON; 2335 } else if (isalpha(ch)) { 2336 type = RCS_TOK_ID; 2337 *(bp++) = ch; 2338 for (;;) { 2339 ch = getc(pdp->rp_file); 2340 if (ch == EOF) { 2341 type = RCS_TOK_EOF; 2342 break; 2343 } else if (!isalnum(ch) && ch != '_' && ch != '-' && 2344 ch != '/' && ch != '+' && ch != '|') { 2345 ungetc(ch, pdp->rp_file); 2346 break; 2347 } 2348 *(bp++) = ch; 2349 pdp->rp_tlen++; 2350 if (bp == pdp->rp_bufend - 1) { 2351 len = bp - pdp->rp_buf; 2352 rcs_growbuf(rfp); 2353 bp = pdp->rp_buf + len; 2354 } 2355 } 2356 *bp = '\0'; 2357 2358 if (type != RCS_TOK_ERR && rcs_ignore_keys != 1) { 2359 for (i = 0; i < RCS_NKEYS; i++) { 2360 if (strcmp(rcs_keys[i].rk_str, 2361 pdp->rp_buf) == 0) { 2362 type = rcs_keys[i].rk_id; 2363 break; 2364 } 2365 } 2366 } 2367 } else if (ch == '@') { 2368 /* we have a string */ 2369 type = RCS_TOK_STRING; 2370 for (;;) { 2371 ch = getc(pdp->rp_file); 2372 if (ch == EOF) { 2373 type = RCS_TOK_EOF; 2374 break; 2375 } else if (ch == '@') { 2376 ch = getc(pdp->rp_file); 2377 if (ch != '@') { 2378 ungetc(ch, pdp->rp_file); 2379 break; 2380 } 2381 } else if (ch == '\n') 2382 pdp->rp_lines++; 2383 2384 *(bp++) = ch; 2385 pdp->rp_tlen++; 2386 if (bp == pdp->rp_bufend - 1) { 2387 len = bp - pdp->rp_buf; 2388 rcs_growbuf(rfp); 2389 bp = pdp->rp_buf + len; 2390 } 2391 } 2392 2393 *bp = '\0'; 2394 } else if (isdigit(ch)) { 2395 *(bp++) = ch; 2396 last = ch; 2397 type = RCS_TOK_NUM; 2398 2399 for (;;) { 2400 ch = getc(pdp->rp_file); 2401 if (ch == EOF) { 2402 type = RCS_TOK_EOF; 2403 break; 2404 } 2405 if (bp == pdp->rp_bufend) 2406 break; 2407 if (!isdigit(ch) && ch != '.') { 2408 ungetc(ch, pdp->rp_file); 2409 break; 2410 } 2411 2412 if (last == '.' && ch == '.') { 2413 type = RCS_TOK_ERR; 2414 break; 2415 } 2416 last = ch; 2417 *(bp++) = ch; 2418 pdp->rp_tlen++; 2419 } 2420 *bp = '\0'; 2421 } 2422 2423 return (type); 2424 } 2425 2426 /* 2427 * rcs_pushtok() 2428 * 2429 * Push a token back in the parser's token buffer. 2430 */ 2431 static int 2432 rcs_pushtok(RCSFILE *rfp, const char *tok, int type) 2433 { 2434 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2435 2436 if (pdp->rp_pttype != RCS_TOK_ERR) 2437 return (-1); 2438 2439 pdp->rp_pttype = type; 2440 if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >= 2441 sizeof(pdp->rp_ptok)) 2442 fatal("rcs_pushtok: strlcpy"); 2443 return (0); 2444 } 2445 2446 2447 /* 2448 * rcs_growbuf() 2449 * 2450 * Attempt to grow the internal parse buffer for the RCS file <rf> by 2451 * RCS_BUFEXTSIZE. 2452 * In case of failure, the original buffer is left unmodified. 2453 */ 2454 static void 2455 rcs_growbuf(RCSFILE *rf) 2456 { 2457 struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata; 2458 2459 pdp->rp_buf = xrealloc(pdp->rp_buf, 1, 2460 pdp->rp_blen + RCS_BUFEXTSIZE); 2461 pdp->rp_blen += RCS_BUFEXTSIZE; 2462 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 2463 } 2464 2465 /* 2466 * rcs_strprint() 2467 * 2468 * Output an RCS string <str> of size <slen> to the stream <stream>. Any 2469 * '@' characters are escaped. Otherwise, the string can contain arbitrary 2470 * binary data. 2471 */ 2472 static void 2473 rcs_strprint(const u_char *str, size_t slen, FILE *stream) 2474 { 2475 const u_char *ap, *ep, *sp; 2476 2477 if (slen == 0) 2478 return; 2479 2480 ep = str + slen - 1; 2481 2482 for (sp = str; sp <= ep;) { 2483 ap = memchr(sp, '@', ep - sp); 2484 if (ap == NULL) 2485 ap = ep; 2486 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream); 2487 2488 if (*ap == '@') 2489 putc('@', stream); 2490 sp = ap + 1; 2491 } 2492 } 2493 2494 /* 2495 * rcs_deltatext_set() 2496 * 2497 * Set deltatext for <rev> in RCS file <rfp> to <dtext> 2498 * Returns -1 on error, 0 on success. 2499 */ 2500 int 2501 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp) 2502 { 2503 size_t len; 2504 u_char *dtext; 2505 struct rcs_delta *rdp; 2506 2507 /* Write operations require full parsing */ 2508 rcs_parse_deltatexts(rfp, NULL); 2509 2510 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2511 return (-1); 2512 2513 if (rdp->rd_text != NULL) 2514 xfree(rdp->rd_text); 2515 2516 len = cvs_buf_len(bp); 2517 dtext = cvs_buf_release(bp); 2518 bp = NULL; 2519 2520 if (len != 0) { 2521 rdp->rd_text = xmalloc(len); 2522 rdp->rd_tlen = len; 2523 (void)memcpy(rdp->rd_text, dtext, len); 2524 } else { 2525 rdp->rd_text = NULL; 2526 rdp->rd_tlen = 0; 2527 } 2528 2529 if (dtext != NULL) 2530 xfree(dtext); 2531 2532 return (0); 2533 } 2534 2535 /* 2536 * rcs_rev_setlog() 2537 * 2538 * Sets the log message of revision <rev> to <logtext> 2539 */ 2540 int 2541 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext) 2542 { 2543 struct rcs_delta *rdp; 2544 char buf[CVS_REV_BUFSZ]; 2545 2546 rcsnum_tostr(rev, buf, sizeof(buf)); 2547 2548 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2549 return (-1); 2550 2551 if (rdp->rd_log != NULL) 2552 xfree(rdp->rd_log); 2553 2554 rdp->rd_log = xstrdup(logtext); 2555 rfp->rf_flags &= ~RCS_SYNCED; 2556 return (0); 2557 } 2558 /* 2559 * rcs_rev_getdate() 2560 * 2561 * Get the date corresponding to a given revision. 2562 * Returns the date on success, -1 on failure. 2563 */ 2564 time_t 2565 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev) 2566 { 2567 struct rcs_delta *rdp; 2568 2569 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2570 return (-1); 2571 2572 return (timegm(&rdp->rd_date)); 2573 } 2574 2575 /* 2576 * rcs_state_set() 2577 * 2578 * Sets the state of revision <rev> to <state> 2579 * NOTE: default state is 'Exp'. States may not contain spaces. 2580 * 2581 * Returns -1 on failure, 0 on success. 2582 */ 2583 int 2584 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state) 2585 { 2586 struct rcs_delta *rdp; 2587 2588 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2589 return (-1); 2590 2591 if (rdp->rd_state != NULL) 2592 xfree(rdp->rd_state); 2593 2594 rdp->rd_state = xstrdup(state); 2595 2596 rfp->rf_flags &= ~RCS_SYNCED; 2597 2598 return (0); 2599 } 2600 2601 /* 2602 * rcs_state_check() 2603 * 2604 * Check if string <state> is valid. 2605 * 2606 * Returns 0 if the string is valid, -1 otherwise. 2607 */ 2608 int 2609 rcs_state_check(const char *state) 2610 { 2611 if (strchr(state, ' ') != NULL) 2612 return (-1); 2613 2614 return (0); 2615 } 2616 2617 /* 2618 * rcs_state_get() 2619 * 2620 * Get the state for a given revision of a specified RCSFILE. 2621 * 2622 * Returns NULL on failure. 2623 */ 2624 const char * 2625 rcs_state_get(RCSFILE *rfp, RCSNUM *rev) 2626 { 2627 struct rcs_delta *rdp; 2628 2629 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2630 return (NULL); 2631 2632 return (rdp->rd_state); 2633 } 2634 2635 /* rcs_get_revision() */ 2636 static RCSNUM * 2637 rcs_get_revision(const char *revstr, RCSFILE *rfp) 2638 { 2639 RCSNUM *rev, *brev, *frev; 2640 struct rcs_branch *brp; 2641 struct rcs_delta *rdp; 2642 size_t i; 2643 2644 rdp = NULL; 2645 2646 if (!strcmp(revstr, RCS_HEAD_BRANCH)) { 2647 if (rfp->rf_head == NULL) 2648 return NULL; 2649 2650 frev = rcsnum_alloc(); 2651 rcsnum_cpy(rfp->rf_head, frev, 0); 2652 return (frev); 2653 } 2654 2655 /* Possibly we could be passed a version number */ 2656 if ((rev = rcsnum_parse(revstr)) != NULL) { 2657 /* Do not return if it is not in RCS file */ 2658 if ((rdp = rcs_findrev(rfp, rev)) != NULL) { 2659 frev = rcsnum_alloc(); 2660 rcsnum_cpy(rev, frev, 0); 2661 return (frev); 2662 } 2663 } else { 2664 /* More likely we will be passed a symbol */ 2665 rev = rcs_sym_getrev(rfp, revstr); 2666 } 2667 2668 if (rev == NULL) 2669 return (NULL); 2670 2671 /* 2672 * If it was not a branch, thats ok the symbolic 2673 * name refered to a revision, so return the resolved 2674 * revision for the given name. */ 2675 if (!RCSNUM_ISBRANCH(rev)) { 2676 /* Sanity check: The first two elements of any 2677 * revision (be it on a branch or on trunk) cannot 2678 * be greater than HEAD. 2679 * 2680 * XXX: To avoid comparing to uninitialized memory, 2681 * the minimum of both revision lengths is taken 2682 * instead of just 2. 2683 */ 2684 if (rfp->rf_head == NULL) 2685 return NULL; 2686 2687 if (rcsnum_cmp(rev, rfp->rf_head, 2688 MIN(rfp->rf_head->rn_len, rev->rn_len)) < 0) { 2689 rcsnum_free(rev); 2690 return NULL; 2691 } 2692 return (rev); 2693 } 2694 2695 brev = rcsnum_alloc(); 2696 rcsnum_cpy(rev, brev, rev->rn_len - 1); 2697 2698 if ((rdp = rcs_findrev(rfp, brev)) == NULL) 2699 fatal("rcs_get_revision: tag `%s' does not exist", revstr); 2700 rcsnum_free(brev); 2701 2702 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2703 for (i = 0; i < rev->rn_len; i++) 2704 if (brp->rb_num->rn_id[i] != rev->rn_id[i]) 2705 break; 2706 if (i != rev->rn_len) 2707 continue; 2708 break; 2709 } 2710 2711 rcsnum_free(rev); 2712 frev = rcsnum_alloc(); 2713 if (brp == NULL) { 2714 rcsnum_cpy(rdp->rd_num, frev, 0); 2715 return (frev); 2716 } else { 2717 /* Fetch the delta with the correct branch num */ 2718 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2719 fatal("rcs_get_revision: could not fetch branch " 2720 "delta"); 2721 rcsnum_cpy(rdp->rd_num, frev, 0); 2722 return (frev); 2723 } 2724 } 2725 2726 /* 2727 * rcs_rev_getlines() 2728 * 2729 * Get the entire contents of revision <frev> from the RCSFILE <rfp> and 2730 * return it as a pointer to a struct cvs_lines. 2731 */ 2732 struct cvs_lines * 2733 rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines) 2734 { 2735 size_t plen; 2736 int annotate, done, i, nextroot; 2737 RCSNUM *tnum, *bnum; 2738 struct rcs_branch *brp; 2739 struct rcs_delta *hrdp, *prdp, *rdp, *trdp; 2740 u_char *patch; 2741 struct cvs_line *line, *nline; 2742 struct cvs_lines *dlines, *plines; 2743 2744 if (rfp->rf_head == NULL || 2745 (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL) 2746 fatal("rcs_rev_getlines: no HEAD revision"); 2747 2748 tnum = frev; 2749 rcs_parse_deltatexts(rfp, hrdp->rd_num); 2750 2751 /* revision on branch, get the branch root */ 2752 nextroot = 2; 2753 if (RCSNUM_ISBRANCHREV(tnum)) { 2754 bnum = rcsnum_alloc(); 2755 rcsnum_cpy(tnum, bnum, nextroot); 2756 } else { 2757 bnum = tnum; 2758 } 2759 2760 if (alines != NULL) { 2761 /* start with annotate first at requested revision */ 2762 annotate = ANNOTATE_LATER; 2763 *alines = NULL; 2764 } else 2765 annotate = ANNOTATE_NEVER; 2766 2767 dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen); 2768 2769 done = 0; 2770 2771 rdp = hrdp; 2772 if (!rcsnum_differ(rdp->rd_num, bnum)) { 2773 if (annotate == ANNOTATE_LATER) { 2774 /* found requested revision for annotate */ 2775 i = 0; 2776 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 2777 line->l_lineno_orig = line->l_lineno; 2778 i++; 2779 } 2780 2781 *alines = xcalloc(i + 1, sizeof(struct cvs_line *)); 2782 (*alines)[i] = NULL; 2783 annotate = ANNOTATE_NOW; 2784 2785 /* annotate down to 1.1 from where we are */ 2786 if (bnum == tnum) 2787 bnum = rcsnum_alloc(); 2788 bnum = rcsnum_parse("1.1"); 2789 if (!rcsnum_differ(rdp->rd_num, bnum)) { 2790 goto next; 2791 } 2792 } else 2793 goto next; 2794 } 2795 2796 prdp = hrdp; 2797 if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL) 2798 goto done; 2799 2800 again: 2801 for (;;) { 2802 if (rdp->rd_next->rn_len != 0) { 2803 trdp = rcs_findrev(rfp, rdp->rd_next); 2804 if (trdp == NULL) 2805 fatal("failed to grab next revision"); 2806 } 2807 2808 if (rdp->rd_tlen == 0) { 2809 rcs_parse_deltatexts(rfp, rdp->rd_num); 2810 if (rdp->rd_tlen == 0) { 2811 if (!rcsnum_differ(rdp->rd_num, bnum)) 2812 break; 2813 rdp = trdp; 2814 continue; 2815 } 2816 } 2817 2818 plen = rdp->rd_tlen; 2819 patch = rdp->rd_text; 2820 plines = cvs_splitlines(patch, plen); 2821 if (annotate == ANNOTATE_NOW) 2822 rcs_patch_lines(dlines, plines, *alines, prdp); 2823 else 2824 rcs_patch_lines(dlines, plines, NULL, NULL); 2825 cvs_freelines(plines); 2826 2827 if (!rcsnum_differ(rdp->rd_num, bnum)) { 2828 if (annotate != ANNOTATE_LATER) 2829 break; 2830 2831 /* found requested revision for annotate */ 2832 i = 0; 2833 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 2834 line->l_lineno_orig = line->l_lineno; 2835 i++; 2836 } 2837 2838 *alines = xcalloc(i + 1, sizeof(struct cvs_line *)); 2839 (*alines)[i] = NULL; 2840 annotate = ANNOTATE_NOW; 2841 2842 /* annotate down to 1.1 from where we are */ 2843 if (bnum == tnum) 2844 bnum = rcsnum_alloc(); 2845 bnum = rcsnum_parse("1.1"); 2846 2847 if (!rcsnum_differ(rdp->rd_num, bnum)) 2848 break; 2849 } 2850 2851 prdp = rdp; 2852 rdp = trdp; 2853 } 2854 2855 next: 2856 if (!rcsnum_differ(rdp->rd_num, frev)) 2857 done = 1; 2858 2859 if (RCSNUM_ISBRANCHREV(frev) && done != 1) { 2860 nextroot += 2; 2861 rcsnum_cpy(frev, bnum, nextroot); 2862 2863 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2864 for (i = 0; i < nextroot - 1; i++) 2865 if (brp->rb_num->rn_id[i] != bnum->rn_id[i]) 2866 break; 2867 if (i == nextroot - 1) 2868 break; 2869 } 2870 2871 if (brp == NULL) { 2872 if (annotate != ANNOTATE_NEVER) { 2873 if (*alines != NULL) 2874 xfree(*alines); 2875 *alines = NULL; 2876 cvs_freelines(dlines); 2877 if (bnum != tnum) 2878 rcsnum_free(bnum); 2879 return (NULL); 2880 } 2881 fatal("expected branch not found on branch list"); 2882 } 2883 2884 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2885 fatal("rcs_rev_getlines: failed to get delta for target rev"); 2886 2887 goto again; 2888 } 2889 done: 2890 /* put remaining lines into annotate buffer */ 2891 if (annotate == ANNOTATE_NOW) { 2892 for (line = TAILQ_FIRST(&(dlines->l_lines)); 2893 line != NULL; line = nline) { 2894 nline = TAILQ_NEXT(line, l_list); 2895 TAILQ_REMOVE(&(dlines->l_lines), line, l_list); 2896 if (line->l_line == NULL) { 2897 xfree(line); 2898 continue; 2899 } 2900 2901 line->l_delta = rdp; 2902 (*alines)[line->l_lineno_orig - 1] = line; 2903 } 2904 2905 cvs_freelines(dlines); 2906 dlines = NULL; 2907 } 2908 2909 if (bnum != tnum) 2910 rcsnum_free(bnum); 2911 2912 return (dlines); 2913 } 2914 2915 void 2916 rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines) 2917 { 2918 size_t plen; 2919 int i, nextroot; 2920 RCSNUM *bnum; 2921 struct rcs_branch *brp; 2922 struct rcs_delta *rdp, *trdp; 2923 u_char *patch; 2924 struct cvs_line *line; 2925 struct cvs_lines *dlines, *plines; 2926 2927 if (!RCSNUM_ISBRANCHREV(frev)) 2928 fatal("rcs_annotate_getlines: branch revision expected"); 2929 2930 /* revision on branch, get the branch root */ 2931 nextroot = 2; 2932 bnum = rcsnum_alloc(); 2933 rcsnum_cpy(frev, bnum, nextroot); 2934 2935 /* 2936 * Going from HEAD to 1.1 enables the use of an array, which is 2937 * much faster. Unfortunately this is not possible with branch 2938 * revisions, so copy over our alines (array) into dlines (tailq). 2939 */ 2940 dlines = xcalloc(1, sizeof(*dlines)); 2941 TAILQ_INIT(&(dlines->l_lines)); 2942 line = xcalloc(1, sizeof(*line)); 2943 TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list); 2944 2945 for (i = 0; (*alines)[i] != NULL; i++) { 2946 line = (*alines)[i]; 2947 line->l_lineno = i + 1; 2948 TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list); 2949 } 2950 2951 rdp = rcs_findrev(rfp, bnum); 2952 if (rdp == NULL) 2953 fatal("failed to grab branch root revision"); 2954 2955 do { 2956 nextroot += 2; 2957 rcsnum_cpy(frev, bnum, nextroot); 2958 2959 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2960 for (i = 0; i < nextroot - 1; i++) 2961 if (brp->rb_num->rn_id[i] != bnum->rn_id[i]) 2962 break; 2963 if (i == nextroot - 1) 2964 break; 2965 } 2966 2967 if (brp == NULL) 2968 fatal("expected branch not found on branch list"); 2969 2970 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2971 fatal("failed to get delta for target rev"); 2972 2973 for (;;) { 2974 if (rdp->rd_next->rn_len != 0) { 2975 trdp = rcs_findrev(rfp, rdp->rd_next); 2976 if (trdp == NULL) 2977 fatal("failed to grab next revision"); 2978 } 2979 2980 if (rdp->rd_tlen == 0) { 2981 rcs_parse_deltatexts(rfp, rdp->rd_num); 2982 if (rdp->rd_tlen == 0) { 2983 if (!rcsnum_differ(rdp->rd_num, bnum)) 2984 break; 2985 rdp = trdp; 2986 continue; 2987 } 2988 } 2989 2990 plen = rdp->rd_tlen; 2991 patch = rdp->rd_text; 2992 plines = cvs_splitlines(patch, plen); 2993 rcs_patch_lines(dlines, plines, NULL, rdp); 2994 cvs_freelines(plines); 2995 2996 if (!rcsnum_differ(rdp->rd_num, bnum)) 2997 break; 2998 2999 rdp = trdp; 3000 } 3001 } while (rcsnum_differ(rdp->rd_num, frev)); 3002 3003 if (bnum != frev) 3004 rcsnum_free(bnum); 3005 3006 /* 3007 * All lines have been parsed, now they must be copied over 3008 * into alines (array) again. 3009 */ 3010 xfree(*alines); 3011 3012 i = 0; 3013 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 3014 if (line->l_line != NULL) 3015 i++; 3016 } 3017 *alines = xcalloc(i + 1, sizeof(struct cvs_line *)); 3018 (*alines)[i] = NULL; 3019 3020 i = 0; 3021 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 3022 if (line->l_line != NULL) 3023 (*alines)[i++] = line; 3024 } 3025 } 3026 3027 /* 3028 * rcs_rev_getbuf() 3029 * 3030 * XXX: This is really really slow and should be avoided if at all possible! 3031 * 3032 * Get the entire contents of revision <rev> from the RCSFILE <rfp> and 3033 * return it as a BUF pointer. 3034 */ 3035 BUF * 3036 rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode) 3037 { 3038 int expmode, expand; 3039 struct rcs_delta *rdp; 3040 struct cvs_lines *lines; 3041 struct cvs_line *lp, *nlp; 3042 BUF *bp; 3043 3044 expand = 0; 3045 lines = rcs_rev_getlines(rfp, rev, NULL); 3046 bp = cvs_buf_alloc(1024 * 16); 3047 3048 if (!(mode & RCS_KWEXP_NONE)) { 3049 if (rfp->rf_expand != NULL) 3050 expmode = rcs_kwexp_get(rfp); 3051 else 3052 expmode = RCS_KWEXP_DEFAULT; 3053 3054 if (!(expmode & RCS_KWEXP_NONE)) { 3055 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 3056 fatal("could not fetch revision"); 3057 expand = 1; 3058 } 3059 } 3060 3061 for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) { 3062 nlp = TAILQ_NEXT(lp, l_list); 3063 3064 if (lp->l_line == NULL) { 3065 lp = nlp; 3066 continue; 3067 } 3068 3069 if (expand) 3070 rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode); 3071 3072 do { 3073 cvs_buf_append(bp, lp->l_line, lp->l_len); 3074 } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp); 3075 } 3076 3077 cvs_freelines(lines); 3078 3079 return (bp); 3080 } 3081 3082 /* 3083 * rcs_rev_write_fd() 3084 * 3085 * Write the entire contents of revision <frev> from the rcsfile <rfp> to 3086 * file descriptor <fd>. 3087 */ 3088 void 3089 rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode) 3090 { 3091 int fd; 3092 FILE *fp; 3093 size_t ret; 3094 int expmode, expand; 3095 struct rcs_delta *rdp; 3096 struct cvs_lines *lines; 3097 struct cvs_line *lp, *nlp; 3098 extern int print_stdout; 3099 3100 expand = 0; 3101 lines = rcs_rev_getlines(rfp, rev, NULL); 3102 3103 if (!(mode & RCS_KWEXP_NONE)) { 3104 if (rfp->rf_expand != NULL) 3105 expmode = rcs_kwexp_get(rfp); 3106 else 3107 expmode = RCS_KWEXP_DEFAULT; 3108 3109 if (!(expmode & RCS_KWEXP_NONE)) { 3110 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 3111 fatal("could not fetch revision"); 3112 expand = 1; 3113 } 3114 } 3115 3116 fd = dup(_fd); 3117 if (fd == -1) 3118 fatal("rcs_rev_write_fd: dup: %s", strerror(errno)); 3119 3120 if ((fp = fdopen(fd, "w")) == NULL) 3121 fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno)); 3122 3123 for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) { 3124 nlp = TAILQ_NEXT(lp, l_list); 3125 3126 if (lp->l_line == NULL) { 3127 lp = nlp; 3128 continue; 3129 } 3130 3131 if (expand) 3132 rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode); 3133 3134 do { 3135 /* 3136 * Solely for the checkout and update -p options. 3137 */ 3138 if (cvs_server_active == 1 && 3139 (cvs_cmdop == CVS_OP_CHECKOUT || 3140 cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) { 3141 ret = fwrite("M ", 1, 2, fp); 3142 if (ret != 2) 3143 fatal("rcs_rev_write_fd: %s", 3144 strerror(errno)); 3145 } 3146 3147 ret = fwrite(lp->l_line, 1, lp->l_len, fp); 3148 if (ret != lp->l_len) 3149 fatal("rcs_rev_write_fd: %s", strerror(errno)); 3150 } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp); 3151 } 3152 3153 cvs_freelines(lines); 3154 (void)fclose(fp); 3155 } 3156 3157 /* 3158 * rcs_rev_write_stmp() 3159 * 3160 * Write the contents of the rev <rev> to a temporary file whose path is 3161 * specified using <template> (see mkstemp(3)). NB. This function will modify 3162 * <template>, as per mkstemp. 3163 */ 3164 int 3165 rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode) 3166 { 3167 int fd; 3168 3169 if ((fd = mkstemp(template)) == -1) 3170 fatal("mkstemp: `%s': %s", template, strerror(errno)); 3171 3172 cvs_worklist_add(template, &temp_files); 3173 rcs_rev_write_fd(rfp, rev, fd, mode); 3174 3175 if (lseek(fd, 0, SEEK_SET) < 0) 3176 fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno)); 3177 3178 return (fd); 3179 } 3180 3181 static void 3182 rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines, 3183 struct cvs_line *line, int mode) 3184 { 3185 BUF *tmpbuf; 3186 int kwtype; 3187 u_int j, found; 3188 const u_char *c, *start, *fin, *end; 3189 char *kwstr; 3190 char expbuf[256], buf[256]; 3191 char *fmt; 3192 size_t clen, kwlen, len, tlen; 3193 3194 kwtype = 0; 3195 kwstr = NULL; 3196 3197 if (mode & RCS_KWEXP_OLD) 3198 return; 3199 3200 len = line->l_len; 3201 if (len == 0) 3202 return; 3203 3204 c = line->l_line; 3205 found = 0; 3206 /* Final character in buffer. */ 3207 fin = c + len - 1; 3208 3209 /* 3210 * Keyword formats: 3211 * $Keyword$ 3212 * $Keyword: value$ 3213 */ 3214 for (; c < fin; c++) { 3215 if (*c != '$') 3216 continue; 3217 3218 /* remember start of this possible keyword */ 3219 start = c; 3220 3221 /* first following character has to be alphanumeric */ 3222 c++; 3223 if (!isalpha(*c)) { 3224 c = start; 3225 continue; 3226 } 3227 3228 /* Number of characters between c and fin, inclusive. */ 3229 clen = fin - c + 1; 3230 3231 /* look for any matching keywords */ 3232 found = 0; 3233 for (j = 0; j < RCS_NKWORDS; j++) { 3234 kwlen = strlen(rcs_expkw[j].kw_str); 3235 /* 3236 * kwlen must be less than clen since clen 3237 * includes either a terminating `$' or a `:'. 3238 */ 3239 if (kwlen < clen && 3240 memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 && 3241 (c[kwlen] == '$' || c[kwlen] == ':')) { 3242 found = 1; 3243 kwstr = rcs_expkw[j].kw_str; 3244 kwtype = rcs_expkw[j].kw_type; 3245 c += kwlen; 3246 break; 3247 } 3248 } 3249 3250 if (found == 0 && cvs_tagname != NULL) { 3251 kwlen = strlen(cvs_tagname); 3252 if (kwlen < clen && 3253 memcmp(c, cvs_tagname, kwlen) == 0 && 3254 (c[kwlen] == '$' || c[kwlen] == ':')) { 3255 found = 1; 3256 kwstr = cvs_tagname; 3257 kwtype = RCS_KW_ID; 3258 c += kwlen; 3259 } 3260 } 3261 3262 /* unknown keyword, continue looking */ 3263 if (found == 0) { 3264 c = start; 3265 continue; 3266 } 3267 3268 /* 3269 * if the next character was ':' we need to look for 3270 * an '$' before the end of the line to be sure it is 3271 * in fact a keyword. 3272 */ 3273 if (*c == ':') { 3274 for (; c <= fin; ++c) { 3275 if (*c == '$' || *c == '\n') 3276 break; 3277 } 3278 3279 if (*c != '$') { 3280 c = start; 3281 continue; 3282 } 3283 } 3284 end = c + 1; 3285 3286 /* start constructing the expansion */ 3287 expbuf[0] = '\0'; 3288 3289 if (mode & RCS_KWEXP_NAME) { 3290 if (strlcat(expbuf, "$", sizeof(expbuf)) >= 3291 sizeof(expbuf) || strlcat(expbuf, kwstr, 3292 sizeof(expbuf)) >= sizeof(expbuf)) 3293 fatal("rcs_kwexp_line: truncated"); 3294 if ((mode & RCS_KWEXP_VAL) && 3295 strlcat(expbuf, ": ", sizeof(expbuf)) >= 3296 sizeof(expbuf)) 3297 fatal("rcs_kwexp_line: truncated"); 3298 } 3299 3300 /* 3301 * order matters because of RCS_KW_ID and 3302 * RCS_KW_HEADER here 3303 */ 3304 if (mode & RCS_KWEXP_VAL) { 3305 if (kwtype & RCS_KW_RCSFILE) { 3306 if (!(kwtype & RCS_KW_FULLPATH)) 3307 (void)strlcat(expbuf, basename(rcsfile), 3308 sizeof(expbuf)); 3309 else 3310 (void)strlcat(expbuf, rcsfile, 3311 sizeof(expbuf)); 3312 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3313 sizeof(expbuf)) 3314 fatal("rcs_kwexp_line: truncated"); 3315 } 3316 3317 if (kwtype & RCS_KW_REVISION) { 3318 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 3319 if (strlcat(buf, " ", sizeof(buf)) >= 3320 sizeof(buf) || strlcat(expbuf, buf, 3321 sizeof(expbuf)) >= sizeof(buf)) 3322 fatal("rcs_kwexp_line: truncated"); 3323 } 3324 3325 if (kwtype & RCS_KW_DATE) { 3326 fmt = "%Y/%m/%d %H:%M:%S "; 3327 3328 if (strftime(buf, sizeof(buf), fmt, 3329 &rdp->rd_date) == 0) 3330 fatal("rcs_kwexp_line: strftime " 3331 "failure"); 3332 if (strlcat(expbuf, buf, sizeof(expbuf)) >= 3333 sizeof(expbuf)) 3334 fatal("rcs_kwexp_line: string " 3335 "truncated"); 3336 } 3337 3338 if (kwtype & RCS_KW_MDOCDATE) { 3339 /* 3340 * Do not prepend ' ' for a single 3341 * digit, %e would do so and there is 3342 * no better format for strftime(). 3343 */ 3344 if (rdp->rd_date.tm_mday < 10) 3345 fmt = "%B%e %Y "; 3346 else 3347 fmt = "%B %e %Y "; 3348 3349 if (strftime(buf, sizeof(buf), fmt, 3350 &rdp->rd_date) == 0) 3351 fatal("rcs_kwexp_line: strftime " 3352 "failure"); 3353 if (strlcat(expbuf, buf, sizeof(expbuf)) >= 3354 sizeof(expbuf)) 3355 fatal("rcs_kwexp_line: string " 3356 "truncated"); 3357 } 3358 3359 if (kwtype & RCS_KW_AUTHOR) { 3360 if (strlcat(expbuf, rdp->rd_author, 3361 sizeof(expbuf)) >= sizeof(expbuf) || 3362 strlcat(expbuf, " ", sizeof(expbuf)) >= 3363 sizeof(expbuf)) 3364 fatal("rcs_kwexp_line: string " 3365 "truncated"); 3366 } 3367 3368 if (kwtype & RCS_KW_STATE) { 3369 if (strlcat(expbuf, rdp->rd_state, 3370 sizeof(expbuf)) >= sizeof(expbuf) || 3371 strlcat(expbuf, " ", sizeof(expbuf)) >= 3372 sizeof(expbuf)) 3373 fatal("rcs_kwexp_line: string " 3374 "truncated"); 3375 } 3376 3377 /* order does not matter anymore below */ 3378 if (kwtype & RCS_KW_LOG) { 3379 char linebuf[256]; 3380 struct cvs_line *cur, *lp; 3381 char *logp, *l_line, *prefix, *q, *sprefix; 3382 size_t i; 3383 3384 /* Log line */ 3385 if (!(kwtype & RCS_KW_FULLPATH)) 3386 (void)strlcat(expbuf, 3387 basename(rcsfile), sizeof(expbuf)); 3388 else 3389 (void)strlcat(expbuf, rcsfile, 3390 sizeof(expbuf)); 3391 3392 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3393 sizeof(expbuf)) 3394 fatal("rcs_kwexp_line: string " 3395 "truncated"); 3396 3397 cur = line; 3398 3399 /* copy rdp->rd_log for strsep */ 3400 logp = xstrdup(rdp->rd_log); 3401 3402 /* copy our prefix for later processing */ 3403 prefix = xmalloc(start - line->l_line + 1); 3404 memcpy(prefix, line->l_line, 3405 start - line->l_line); 3406 prefix[start - line->l_line] = '\0'; 3407 3408 /* copy also prefix without trailing blanks. */ 3409 sprefix = xstrdup(prefix); 3410 for (i = strlen(sprefix); i > 0 && 3411 sprefix[i - 1] == ' '; i--) 3412 sprefix[i - 1] = '\0'; 3413 3414 /* new line: revision + date + author */ 3415 linebuf[0] = '\0'; 3416 if (strlcat(linebuf, "Revision ", 3417 sizeof(linebuf)) >= sizeof(linebuf)) 3418 fatal("rcs_kwexp_line: truncated"); 3419 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 3420 if (strlcat(linebuf, buf, sizeof(linebuf)) 3421 >= sizeof(buf)) 3422 fatal("rcs_kwexp_line: truncated"); 3423 fmt = " %Y/%m/%d %H:%M:%S "; 3424 if (strftime(buf, sizeof(buf), fmt, 3425 &rdp->rd_date) == 0) 3426 fatal("rcs_kwexp_line: strftime " 3427 "failure"); 3428 if (strlcat(linebuf, buf, sizeof(linebuf)) 3429 >= sizeof(linebuf)) 3430 fatal("rcs_kwexp_line: string " 3431 "truncated"); 3432 if (strlcat(linebuf, rdp->rd_author, 3433 sizeof(linebuf)) >= sizeof(linebuf)) 3434 fatal("rcs_kwexp_line: string " 3435 "truncated"); 3436 3437 lp = xcalloc(1, sizeof(*lp)); 3438 xasprintf(&(lp->l_line), "%s%s\n", 3439 prefix, linebuf); 3440 lp->l_len = strlen(lp->l_line); 3441 TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp, 3442 l_list); 3443 cur = lp; 3444 3445 /* Log message */ 3446 q = logp; 3447 while ((l_line = strsep(&q, "\n")) != NULL && 3448 q != NULL) { 3449 lp = xcalloc(1, sizeof(*lp)); 3450 3451 if (l_line[0] == '\0') { 3452 xasprintf(&(lp->l_line), "%s\n", 3453 sprefix); 3454 } else { 3455 xasprintf(&(lp->l_line), 3456 "%s%s\n", prefix, l_line); 3457 } 3458 3459 lp->l_len = strlen(lp->l_line); 3460 TAILQ_INSERT_AFTER(&(lines->l_lines), 3461 cur, lp, l_list); 3462 cur = lp; 3463 } 3464 xfree(logp); 3465 3466 /* 3467 * This is just another hairy mess, but it must 3468 * be done: All characters behind Log will be 3469 * written in a new line next to log messages. 3470 * But that's not enough, we have to strip all 3471 * trailing whitespaces of our prefix. 3472 */ 3473 lp = xcalloc(1, sizeof(*lp)); 3474 lp->l_line = xcalloc(strlen(sprefix) + 3475 line->l_line + line->l_len - end + 1, 1); 3476 strlcpy(lp->l_line, sprefix, 3477 strlen(sprefix) + 1); 3478 memcpy(lp->l_line + strlen(sprefix), 3479 end, line->l_line + line->l_len - end); 3480 lp->l_len = strlen(lp->l_line); 3481 TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp, 3482 l_list); 3483 cur = lp; 3484 3485 end = line->l_line + line->l_len - 1; 3486 3487 xfree(prefix); 3488 xfree(sprefix); 3489 3490 } 3491 3492 if (kwtype & RCS_KW_SOURCE) { 3493 if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >= 3494 sizeof(expbuf) || strlcat(expbuf, " ", 3495 sizeof(expbuf)) >= sizeof(expbuf)) 3496 fatal("rcs_kwexp_line: string " 3497 "truncated"); 3498 } 3499 3500 if (kwtype & RCS_KW_NAME) 3501 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3502 sizeof(expbuf)) 3503 fatal("rcs_kwexp_line: string " 3504 "truncated"); 3505 3506 if (kwtype & RCS_KW_LOCKER) 3507 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3508 sizeof(expbuf)) 3509 fatal("rcs_kwexp_line: string " 3510 "truncated"); 3511 } 3512 3513 /* end the expansion */ 3514 if (mode & RCS_KWEXP_NAME) 3515 if (strlcat(expbuf, "$", 3516 sizeof(expbuf)) >= sizeof(expbuf)) 3517 fatal("rcs_kwexp_line: truncated"); 3518 3519 /* Concatenate everything together. */ 3520 tmpbuf = cvs_buf_alloc(len + strlen(expbuf)); 3521 /* Append everything before keyword. */ 3522 cvs_buf_append(tmpbuf, line->l_line, 3523 start - line->l_line); 3524 /* Append keyword. */ 3525 cvs_buf_puts(tmpbuf, expbuf); 3526 /* Point c to end of keyword. */ 3527 tlen = cvs_buf_len(tmpbuf) - 1; 3528 /* Append everything after keyword. */ 3529 cvs_buf_append(tmpbuf, end, 3530 line->l_line + line->l_len - end); 3531 c = cvs_buf_get(tmpbuf) + tlen; 3532 /* Point fin to end of data. */ 3533 fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1; 3534 /* Recalculate new length. */ 3535 len = cvs_buf_len(tmpbuf); 3536 3537 /* tmpbuf is now ready, convert to string */ 3538 if (line->l_needsfree) 3539 xfree(line->l_line); 3540 line->l_len = len; 3541 line->l_line = cvs_buf_release(tmpbuf); 3542 line->l_needsfree = 1; 3543 } 3544 } 3545 3546 /* rcs_translate_tag() */ 3547 RCSNUM * 3548 rcs_translate_tag(const char *revstr, RCSFILE *rfp) 3549 { 3550 int follow; 3551 time_t deltatime; 3552 char branch[CVS_REV_BUFSZ]; 3553 RCSNUM *brev, *frev, *rev, *rrev; 3554 struct rcs_delta *rdp, *trdp; 3555 time_t cdate; 3556 3557 brev = frev = rrev = NULL; 3558 3559 if (revstr == NULL) { 3560 if (rfp->rf_branch != NULL) { 3561 rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch)); 3562 revstr = branch; 3563 } else { 3564 revstr = RCS_HEAD_BRANCH; 3565 } 3566 } 3567 3568 if ((rev = rcs_get_revision(revstr, rfp)) == NULL) 3569 return (NULL); 3570 3571 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 3572 return (NULL); 3573 3574 /* let's see if we must follow a branch */ 3575 if (!strcmp(revstr, RCS_HEAD_BRANCH)) 3576 follow = 1; 3577 else { 3578 frev = rcs_sym_getrev(rfp, revstr); 3579 if (frev == NULL) 3580 frev = rrev = rcsnum_parse(revstr); 3581 3582 brev = rcsnum_alloc(); 3583 rcsnum_cpy(rev, brev, rev->rn_len - 1); 3584 3585 if (frev != NULL && RCSNUM_ISBRANCH(frev) && 3586 !rcsnum_cmp(frev, brev, 0)) { 3587 follow = 1; 3588 } else 3589 follow = 0; 3590 3591 rcsnum_free(brev); 3592 if (rrev != NULL) 3593 rcsnum_free(rrev); 3594 } 3595 3596 if (cvs_specified_date != -1) 3597 cdate = cvs_specified_date; 3598 else 3599 cdate = cvs_directory_date; 3600 3601 if (cdate == -1) { 3602 /* XXX */ 3603 if (rev->rn_len < 4 || !follow) { 3604 return (rev); 3605 } 3606 3607 /* Find the latest delta on that branch */ 3608 rcsnum_free(rev); 3609 for (;;) { 3610 if (rdp->rd_next->rn_len == 0) 3611 break; 3612 if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL) 3613 fatal("rcs_translate_tag: could not fetch " 3614 "branch delta"); 3615 } 3616 3617 rev = rcsnum_alloc(); 3618 rcsnum_cpy(rdp->rd_num, rev, 0); 3619 return (rev); 3620 } 3621 3622 if (frev != NULL) 3623 rcsnum_tostr(frev, branch, sizeof(branch)); 3624 3625 if (frev != NULL) { 3626 brev = rcsnum_revtobr(frev); 3627 brev->rn_len = rev->rn_len - 1; 3628 } 3629 3630 rcsnum_free(rev); 3631 3632 do { 3633 deltatime = timelocal(&(rdp->rd_date)); 3634 3635 if (RCSNUM_ISBRANCHREV(rdp->rd_num)) { 3636 if (deltatime > cdate) { 3637 trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list); 3638 if (trdp == NULL) 3639 trdp = rdp; 3640 3641 if (trdp->rd_num->rn_len != rdp->rd_num->rn_len) 3642 return (NULL); 3643 3644 rev = rcsnum_alloc(); 3645 rcsnum_cpy(trdp->rd_num, rev, 0); 3646 return (rev); 3647 } 3648 3649 if (rdp->rd_next->rn_len == 0) { 3650 rev = rcsnum_alloc(); 3651 rcsnum_cpy(rdp->rd_num, rev, 0); 3652 return (rev); 3653 } 3654 } else { 3655 if (deltatime < cdate) { 3656 rev = rcsnum_alloc(); 3657 rcsnum_cpy(rdp->rd_num, rev, 0); 3658 return (rev); 3659 } 3660 } 3661 3662 if (follow && rdp->rd_next->rn_len != 0) { 3663 if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0)) 3664 break; 3665 3666 trdp = rcs_findrev(rfp, rdp->rd_next); 3667 if (trdp == NULL) 3668 fatal("failed to grab next revision"); 3669 rdp = trdp; 3670 } else 3671 follow = 0; 3672 } while (follow); 3673 3674 return (NULL); 3675 } 3676