1 /* $OpenBSD: rcs.c,v 1.281 2008/09/17 06:47:57 reyk Exp $ */ 2 /* 3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 18 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/stat.h> 28 29 #include <ctype.h> 30 #include <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 uid_t uid; 1213 1214 if (rev == RCS_HEAD_REV) { 1215 if (rf->rf_flags & RCS_CREATE) { 1216 if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL) 1217 return (-1); 1218 if (rf->rf_head != NULL) 1219 xfree(rf->rf_head); 1220 rf->rf_head = rcsnum_alloc(); 1221 rcsnum_cpy(rev, rf->rf_head, 0); 1222 } else if (rf->rf_head == NULL) { 1223 return (-1); 1224 } else { 1225 rev = rcsnum_inc(rf->rf_head); 1226 } 1227 } else { 1228 if ((rdp = rcs_findrev(rf, rev)) != NULL) 1229 return (-1); 1230 } 1231 1232 uid = getuid(); 1233 if ((pw = getpwuid(uid)) == NULL) 1234 fatal("getpwuid failed"); 1235 1236 rdp = xcalloc(1, sizeof(*rdp)); 1237 1238 TAILQ_INIT(&(rdp->rd_branches)); 1239 1240 rdp->rd_num = rcsnum_alloc(); 1241 rcsnum_cpy(rev, rdp->rd_num, 0); 1242 1243 rdp->rd_next = rcsnum_alloc(); 1244 1245 if (uid == 0) 1246 username = getlogin(); 1247 if (username == NULL || *username == '\0') 1248 username = pw->pw_name; 1249 1250 rdp->rd_author = xstrdup(username); 1251 rdp->rd_state = xstrdup(RCS_STATE_EXP); 1252 rdp->rd_log = xstrdup(msg); 1253 1254 if (date != (time_t)(-1)) 1255 now = date; 1256 else 1257 time(&now); 1258 gmtime_r(&now, &(rdp->rd_date)); 1259 1260 if (RCSNUM_ISBRANCHREV(rev)) 1261 TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list); 1262 else 1263 TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list); 1264 rf->rf_ndelta++; 1265 1266 if (!(rf->rf_flags & RCS_CREATE)) { 1267 if (RCSNUM_ISBRANCHREV(rev)) { 1268 if (rev->rn_id[rev->rn_len - 1] == 1) { 1269 /* a new branch */ 1270 root = rcsnum_branch_root(rev); 1271 brp = xmalloc(sizeof(*brp)); 1272 brp->rb_num = rcsnum_alloc(); 1273 rcsnum_cpy(rdp->rd_num, brp->rb_num, 0); 1274 1275 if ((ordp = rcs_findrev(rf, root)) == NULL) 1276 fatal("root node not found"); 1277 1278 TAILQ_FOREACH(obrp, &(ordp->rd_branches), 1279 rb_list) { 1280 if (!rcsnum_cmp(obrp->rb_num, 1281 brp->rb_num, 1282 brp->rb_num->rn_len - 1)) 1283 break; 1284 } 1285 1286 if (obrp == NULL) { 1287 TAILQ_INSERT_TAIL(&(ordp->rd_branches), 1288 brp, rb_list); 1289 } 1290 } else { 1291 root = rcsnum_alloc(); 1292 rcsnum_cpy(rev, root, 0); 1293 rcsnum_dec(root); 1294 if ((ordp = rcs_findrev(rf, root)) == NULL) 1295 fatal("previous revision not found"); 1296 rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0); 1297 } 1298 } else { 1299 ordp = TAILQ_NEXT(rdp, rd_list); 1300 rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0); 1301 } 1302 } 1303 1304 if (root != NULL) 1305 rcsnum_free(root); 1306 1307 /* not synced anymore */ 1308 rf->rf_flags &= ~RCS_SYNCED; 1309 1310 return (0); 1311 } 1312 1313 /* 1314 * rcs_rev_remove() 1315 * 1316 * Remove the revision whose number is <rev> from the RCS file <rf>. 1317 */ 1318 int 1319 rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) 1320 { 1321 int fd1, fd2; 1322 char *path_tmp1, *path_tmp2; 1323 struct rcs_delta *rdp, *prevrdp, *nextrdp; 1324 BUF *prevbuf, *newdiff, *newdeltatext; 1325 1326 if (rev == RCS_HEAD_REV) 1327 rev = rf->rf_head; 1328 1329 if (rev == NULL) 1330 return (-1); 1331 1332 /* do we actually have that revision? */ 1333 if ((rdp = rcs_findrev(rf, rev)) == NULL) 1334 return (-1); 1335 1336 /* 1337 * This is confusing, the previous delta is next in the TAILQ list. 1338 * the next delta is the previous one in the TAILQ list. 1339 * 1340 * When the HEAD revision got specified, nextrdp will be NULL. 1341 * When the first revision got specified, prevrdp will be NULL. 1342 */ 1343 prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list); 1344 nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, cvs_tqh, rd_list); 1345 1346 newdeltatext = NULL; 1347 prevbuf = NULL; 1348 path_tmp1 = path_tmp2 = NULL; 1349 1350 if (prevrdp != NULL && nextrdp != NULL) { 1351 newdiff = cvs_buf_alloc(64); 1352 1353 /* calculate new diff */ 1354 (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 1355 fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0); 1356 1357 (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 1358 fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0); 1359 1360 diff_format = D_RCSDIFF; 1361 if (cvs_diffreg(path_tmp1, path_tmp2, 1362 fd1, fd2, newdiff) == D_ERROR) 1363 fatal("rcs_diffreg failed"); 1364 1365 close(fd1); 1366 close(fd2); 1367 1368 newdeltatext = newdiff; 1369 } else if (nextrdp == NULL && prevrdp != NULL) { 1370 newdeltatext = prevbuf; 1371 } 1372 1373 if (newdeltatext != NULL) { 1374 if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0) 1375 fatal("error setting new deltatext"); 1376 } 1377 1378 TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list); 1379 1380 /* update pointers */ 1381 if (prevrdp != NULL && nextrdp != NULL) { 1382 rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0); 1383 } else if (prevrdp != NULL) { 1384 if (rcs_head_set(rf, prevrdp->rd_num) < 0) 1385 fatal("rcs_head_set failed"); 1386 } else if (nextrdp != NULL) { 1387 rcsnum_free(nextrdp->rd_next); 1388 nextrdp->rd_next = rcsnum_alloc(); 1389 } else { 1390 rcsnum_free(rf->rf_head); 1391 rf->rf_head = NULL; 1392 } 1393 1394 rf->rf_ndelta--; 1395 rf->rf_flags &= ~RCS_SYNCED; 1396 1397 rcs_freedelta(rdp); 1398 1399 if (newdeltatext != NULL) 1400 xfree(newdeltatext); 1401 1402 if (path_tmp1 != NULL) 1403 xfree(path_tmp1); 1404 if (path_tmp2 != NULL) 1405 xfree(path_tmp2); 1406 1407 return (0); 1408 } 1409 1410 /* 1411 * rcs_findrev() 1412 * 1413 * Find a specific revision's delta entry in the tree of the RCS file <rfp>. 1414 * The revision number is given in <rev>. 1415 * 1416 * Returns a pointer to the delta on success, or NULL on failure. 1417 */ 1418 struct rcs_delta * 1419 rcs_findrev(RCSFILE *rfp, RCSNUM *rev) 1420 { 1421 int isbrev; 1422 struct rcs_delta *rdp; 1423 1424 if (rev == NULL) 1425 return NULL; 1426 1427 isbrev = RCSNUM_ISBRANCHREV(rev); 1428 1429 /* 1430 * We need to do more parsing if the last revision in the linked list 1431 * is greater than the requested revision. 1432 */ 1433 rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1434 if (rdp == NULL || 1435 (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) || 1436 ((isbrev && rdp->rd_num->rn_len < 4) || 1437 (isbrev && rcsnum_differ(rev, rdp->rd_num)))) { 1438 rcs_parse_deltas(rfp, rev); 1439 } 1440 1441 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1442 if (rcsnum_differ(rdp->rd_num, rev)) 1443 continue; 1444 else 1445 return (rdp); 1446 } 1447 1448 return (NULL); 1449 } 1450 1451 /* 1452 * rcs_kwexp_set() 1453 * 1454 * Set the keyword expansion mode to use on the RCS file <file> to <mode>. 1455 */ 1456 void 1457 rcs_kwexp_set(RCSFILE *file, int mode) 1458 { 1459 int i; 1460 char *tmp, buf[8] = ""; 1461 1462 if (RCS_KWEXP_INVAL(mode)) 1463 return; 1464 1465 i = 0; 1466 if (mode == RCS_KWEXP_NONE) 1467 buf[0] = 'b'; 1468 else if (mode == RCS_KWEXP_OLD) 1469 buf[0] = 'o'; 1470 else { 1471 if (mode & RCS_KWEXP_NAME) 1472 buf[i++] = 'k'; 1473 if (mode & RCS_KWEXP_VAL) 1474 buf[i++] = 'v'; 1475 if (mode & RCS_KWEXP_LKR) 1476 buf[i++] = 'l'; 1477 } 1478 1479 tmp = xstrdup(buf); 1480 if (file->rf_expand != NULL) 1481 xfree(file->rf_expand); 1482 file->rf_expand = tmp; 1483 /* not synced anymore */ 1484 file->rf_flags &= ~RCS_SYNCED; 1485 } 1486 1487 /* 1488 * rcs_kwexp_get() 1489 * 1490 * Retrieve the keyword expansion mode to be used for the RCS file <file>. 1491 */ 1492 int 1493 rcs_kwexp_get(RCSFILE *file) 1494 { 1495 return rcs_kflag_get(file->rf_expand); 1496 } 1497 1498 /* 1499 * rcs_kflag_get() 1500 * 1501 * Get the keyword expansion mode from a set of character flags given in 1502 * <flags> and return the appropriate flag mask. In case of an error, the 1503 * returned mask will have the RCS_KWEXP_ERR bit set to 1. 1504 */ 1505 int 1506 rcs_kflag_get(const char *flags) 1507 { 1508 int fl; 1509 size_t len; 1510 const char *fp; 1511 1512 if (flags == NULL) 1513 return 0; 1514 1515 fl = 0; 1516 if (!(len = strlen(flags))) 1517 return RCS_KWEXP_ERR; 1518 1519 for (fp = flags; *fp != '\0'; fp++) { 1520 if (*fp == 'k') 1521 fl |= RCS_KWEXP_NAME; 1522 else if (*fp == 'v') 1523 fl |= RCS_KWEXP_VAL; 1524 else if (*fp == 'l') 1525 fl |= RCS_KWEXP_LKR; 1526 else if (*fp == 'o') { 1527 if (len != 1) 1528 fl |= RCS_KWEXP_ERR; 1529 fl |= RCS_KWEXP_OLD; 1530 } else if (*fp == 'b') { 1531 if (len != 1) 1532 fl |= RCS_KWEXP_ERR; 1533 fl |= RCS_KWEXP_NONE; 1534 } else /* unknown letter */ 1535 fl |= RCS_KWEXP_ERR; 1536 } 1537 1538 return (fl); 1539 } 1540 1541 /* rcs_parse_deltas() 1542 * 1543 * Parse deltas. If <rev> is not NULL, parse only as far as that 1544 * revision. If <rev> is NULL, parse all deltas. 1545 */ 1546 static void 1547 rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev) 1548 { 1549 int ret; 1550 struct rcs_delta *enddelta; 1551 1552 if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE)) 1553 return; 1554 1555 for (;;) { 1556 ret = rcs_parse_delta(rfp); 1557 if (rev != NULL) { 1558 enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist); 1559 if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0) 1560 break; 1561 } 1562 1563 if (ret == 0) { 1564 rfp->rf_flags |= PARSED_DELTAS; 1565 break; 1566 } 1567 else if (ret == -1) 1568 fatal("error parsing deltas"); 1569 } 1570 } 1571 1572 /* rcs_parse_deltatexts() 1573 * 1574 * Parse deltatexts. If <rev> is not NULL, parse only as far as that 1575 * revision. If <rev> is NULL, parse everything. 1576 */ 1577 static void 1578 rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev) 1579 { 1580 int ret; 1581 struct rcs_delta *rdp; 1582 1583 if ((rfp->rf_flags & PARSED_DELTATEXTS) || 1584 (rfp->rf_flags & RCS_CREATE)) 1585 return; 1586 1587 if (!(rfp->rf_flags & PARSED_DESC)) 1588 rcs_parse_desc(rfp, rev); 1589 for (;;) { 1590 if (rev != NULL) { 1591 rdp = rcs_findrev(rfp, rev); 1592 if (rdp->rd_text != NULL) 1593 break; 1594 else 1595 ret = rcs_parse_deltatext(rfp); 1596 } else 1597 ret = rcs_parse_deltatext(rfp); 1598 if (ret == 0) { 1599 rfp->rf_flags |= PARSED_DELTATEXTS; 1600 break; 1601 } 1602 else if (ret == -1) 1603 fatal("problem parsing deltatexts"); 1604 } 1605 } 1606 1607 /* rcs_parse_desc() 1608 * 1609 * Parse RCS description. 1610 */ 1611 static void 1612 rcs_parse_desc(RCSFILE *rfp, RCSNUM *rev) 1613 { 1614 int ret = 0; 1615 1616 if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE)) 1617 return; 1618 if (!(rfp->rf_flags & PARSED_DELTAS)) 1619 rcs_parse_deltas(rfp, rev); 1620 /* do parsing */ 1621 ret = rcs_gettok(rfp); 1622 if (ret != RCS_TOK_DESC) 1623 fatal("token `%s' found where RCS desc expected", 1624 RCS_TOKSTR(rfp)); 1625 1626 ret = rcs_gettok(rfp); 1627 if (ret != RCS_TOK_STRING) 1628 fatal("token `%s' found where RCS desc expected", 1629 RCS_TOKSTR(rfp)); 1630 1631 rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp)); 1632 rfp->rf_flags |= PARSED_DESC; 1633 } 1634 1635 /* 1636 * rcs_parse_init() 1637 * 1638 * Initial parsing of file <path>, which are in the RCS format. 1639 * Just does admin section. 1640 */ 1641 static void 1642 rcs_parse_init(RCSFILE *rfp) 1643 { 1644 struct rcs_pdata *pdp; 1645 1646 if (rfp->rf_flags & RCS_PARSED) 1647 return; 1648 1649 pdp = xcalloc(1, sizeof(*pdp)); 1650 1651 pdp->rp_lines = 0; 1652 pdp->rp_pttype = RCS_TOK_ERR; 1653 1654 if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL) 1655 fatal("fdopen: `%s'", rfp->rf_path); 1656 1657 pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); 1658 pdp->rp_blen = RCS_BUFSIZE; 1659 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 1660 1661 /* ditch the strict lock */ 1662 rfp->rf_flags &= ~RCS_SLOCK; 1663 rfp->rf_pdata = pdp; 1664 1665 if (rcs_parse_admin(rfp) < 0) { 1666 rcs_freepdata(pdp); 1667 fatal("could not parse admin data"); 1668 } 1669 1670 if (rfp->rf_flags & RCS_PARSE_FULLY) { 1671 rcs_parse_deltatexts(rfp, NULL); 1672 (void)close(rfp->fd); 1673 rfp->fd = -1; 1674 } 1675 1676 rfp->rf_flags |= RCS_SYNCED; 1677 } 1678 1679 /* 1680 * rcs_parse_admin() 1681 * 1682 * Parse the administrative portion of an RCS file. 1683 * Returns the type of the first token found after the admin section on 1684 * success, or -1 on failure. 1685 */ 1686 static int 1687 rcs_parse_admin(RCSFILE *rfp) 1688 { 1689 u_int i; 1690 int tok, ntok, hmask; 1691 struct rcs_key *rk; 1692 1693 rfp->rf_head = NULL; 1694 rfp->rf_branch = NULL; 1695 1696 /* hmask is a mask of the headers already encountered */ 1697 hmask = 0; 1698 for (;;) { 1699 tok = rcs_gettok(rfp); 1700 if (tok == RCS_TOK_ERR) { 1701 cvs_log(LP_ERR, "parse error in RCS admin section"); 1702 goto fail; 1703 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1704 /* 1705 * Assume this is the start of the first delta or 1706 * that we are dealing with an empty RCS file and 1707 * we just found the description. 1708 */ 1709 if (!(hmask & (1 << RCS_TOK_HEAD))) { 1710 cvs_log(LP_ERR, "head missing"); 1711 goto fail; 1712 } 1713 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1714 return (tok); 1715 } 1716 1717 rk = NULL; 1718 for (i = 0; i < RCS_NKEYS; i++) 1719 if (rcs_keys[i].rk_id == tok) 1720 rk = &(rcs_keys[i]); 1721 1722 if (hmask & (1 << tok)) { 1723 cvs_log(LP_ERR, "duplicate RCS key"); 1724 goto fail; 1725 } 1726 hmask |= (1 << tok); 1727 1728 switch (tok) { 1729 case RCS_TOK_HEAD: 1730 case RCS_TOK_BRANCH: 1731 case RCS_TOK_COMMENT: 1732 case RCS_TOK_EXPAND: 1733 ntok = rcs_gettok(rfp); 1734 if (ntok == RCS_TOK_SCOLON) 1735 break; 1736 if (ntok != rk->rk_val) { 1737 cvs_log(LP_ERR, 1738 "invalid value type for RCS key `%s'", 1739 rk->rk_str); 1740 } 1741 1742 if (tok == RCS_TOK_HEAD) { 1743 if (rfp->rf_head == NULL) 1744 rfp->rf_head = rcsnum_alloc(); 1745 if (RCS_TOKSTR(rfp)[0] == '\0' || 1746 rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1747 rfp->rf_head) < 0) { 1748 rcsnum_free(rfp->rf_head); 1749 rfp->rf_head = NULL; 1750 } 1751 } else if (tok == RCS_TOK_BRANCH) { 1752 if (rfp->rf_branch == NULL) 1753 rfp->rf_branch = rcsnum_alloc(); 1754 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, 1755 rfp->rf_branch) < 0) 1756 goto fail; 1757 } else if (tok == RCS_TOK_COMMENT) { 1758 rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp)); 1759 } else if (tok == RCS_TOK_EXPAND) { 1760 rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp)); 1761 } 1762 1763 /* now get the expected semi-colon */ 1764 ntok = rcs_gettok(rfp); 1765 if (ntok != RCS_TOK_SCOLON) { 1766 cvs_log(LP_ERR, 1767 "missing semi-colon after RCS `%s' key", 1768 rk->rk_str); 1769 goto fail; 1770 } 1771 break; 1772 case RCS_TOK_ACCESS: 1773 if (rcs_parse_access(rfp) < 0) 1774 goto fail; 1775 break; 1776 case RCS_TOK_SYMBOLS: 1777 rcs_ignore_keys = 1; 1778 if (rcs_parse_symbols(rfp) < 0) 1779 goto fail; 1780 break; 1781 case RCS_TOK_LOCKS: 1782 if (rcs_parse_locks(rfp) < 0) 1783 goto fail; 1784 break; 1785 default: 1786 cvs_log(LP_ERR, 1787 "unexpected token `%s' in RCS admin section", 1788 RCS_TOKSTR(rfp)); 1789 goto fail; 1790 } 1791 1792 rcs_ignore_keys = 0; 1793 1794 } 1795 1796 fail: 1797 return (-1); 1798 } 1799 1800 /* 1801 * rcs_parse_delta() 1802 * 1803 * Parse an RCS delta section and allocate the structure to store that delta's 1804 * information in the <rfp> delta list. 1805 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1806 * -1 on error. 1807 */ 1808 static int 1809 rcs_parse_delta(RCSFILE *rfp) 1810 { 1811 int ret, tok, ntok, hmask; 1812 u_int i; 1813 char *tokstr; 1814 RCSNUM *datenum; 1815 struct rcs_delta *rdp; 1816 struct rcs_key *rk; 1817 1818 tok = rcs_gettok(rfp); 1819 if (tok == RCS_TOK_DESC) { 1820 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1821 return (0); 1822 } else if (tok != RCS_TOK_NUM) { 1823 cvs_log(LP_ERR, "unexpected token `%s' at start of delta", 1824 RCS_TOKSTR(rfp)); 1825 return (-1); 1826 } 1827 1828 rdp = xcalloc(1, sizeof(*rdp)); 1829 1830 rdp->rd_num = rcsnum_alloc(); 1831 rdp->rd_next = rcsnum_alloc(); 1832 1833 TAILQ_INIT(&(rdp->rd_branches)); 1834 1835 rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num); 1836 1837 hmask = 0; 1838 ret = 0; 1839 tokstr = NULL; 1840 1841 for (;;) { 1842 tok = rcs_gettok(rfp); 1843 if (tok == RCS_TOK_ERR) { 1844 cvs_log(LP_ERR, "parse error in RCS delta section"); 1845 rcs_freedelta(rdp); 1846 return (-1); 1847 } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) { 1848 rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok); 1849 ret = (tok == RCS_TOK_NUM ? 1 : 0); 1850 break; 1851 } 1852 1853 rk = NULL; 1854 for (i = 0; i < RCS_NKEYS; i++) 1855 if (rcs_keys[i].rk_id == tok) 1856 rk = &(rcs_keys[i]); 1857 1858 if (hmask & (1 << tok)) { 1859 cvs_log(LP_ERR, "duplicate RCS key"); 1860 rcs_freedelta(rdp); 1861 return (-1); 1862 } 1863 hmask |= (1 << tok); 1864 1865 switch (tok) { 1866 case RCS_TOK_DATE: 1867 case RCS_TOK_AUTHOR: 1868 case RCS_TOK_STATE: 1869 case RCS_TOK_NEXT: 1870 ntok = rcs_gettok(rfp); 1871 if (ntok == RCS_TOK_SCOLON) { 1872 if (rk->rk_flags & RCS_VOPT) 1873 break; 1874 else { 1875 cvs_log(LP_ERR, "missing mandatory " 1876 "value to RCS key `%s'", 1877 rk->rk_str); 1878 rcs_freedelta(rdp); 1879 return (-1); 1880 } 1881 } 1882 1883 if (ntok != rk->rk_val) { 1884 cvs_log(LP_ERR, 1885 "invalid value type for RCS key `%s'", 1886 rk->rk_str); 1887 rcs_freedelta(rdp); 1888 return (-1); 1889 } 1890 1891 if (tokstr != NULL) 1892 xfree(tokstr); 1893 tokstr = xstrdup(RCS_TOKSTR(rfp)); 1894 /* now get the expected semi-colon */ 1895 ntok = rcs_gettok(rfp); 1896 if (ntok != RCS_TOK_SCOLON) { 1897 cvs_log(LP_ERR, 1898 "missing semi-colon after RCS `%s' key", 1899 rk->rk_str); 1900 xfree(tokstr); 1901 rcs_freedelta(rdp); 1902 return (-1); 1903 } 1904 1905 if (tok == RCS_TOK_DATE) { 1906 if ((datenum = rcsnum_parse(tokstr)) == NULL) { 1907 xfree(tokstr); 1908 rcs_freedelta(rdp); 1909 return (-1); 1910 } 1911 if (datenum->rn_len != 6) { 1912 cvs_log(LP_ERR, 1913 "RCS date specification has %s " 1914 "fields", 1915 (datenum->rn_len > 6) ? "too many" : 1916 "missing"); 1917 xfree(tokstr); 1918 rcs_freedelta(rdp); 1919 rcsnum_free(datenum); 1920 return (-1); 1921 } 1922 rdp->rd_date.tm_year = datenum->rn_id[0]; 1923 if (rdp->rd_date.tm_year >= 1900) 1924 rdp->rd_date.tm_year -= 1900; 1925 rdp->rd_date.tm_mon = datenum->rn_id[1] - 1; 1926 rdp->rd_date.tm_mday = datenum->rn_id[2]; 1927 rdp->rd_date.tm_hour = datenum->rn_id[3]; 1928 rdp->rd_date.tm_min = datenum->rn_id[4]; 1929 rdp->rd_date.tm_sec = datenum->rn_id[5]; 1930 rcsnum_free(datenum); 1931 } else if (tok == RCS_TOK_AUTHOR) { 1932 rdp->rd_author = tokstr; 1933 tokstr = NULL; 1934 } else if (tok == RCS_TOK_STATE) { 1935 rdp->rd_state = tokstr; 1936 tokstr = NULL; 1937 } else if (tok == RCS_TOK_NEXT) { 1938 rcsnum_aton(tokstr, NULL, rdp->rd_next); 1939 } 1940 break; 1941 case RCS_TOK_BRANCHES: 1942 if (rcs_parse_branches(rfp, rdp) < 0) { 1943 rcs_freedelta(rdp); 1944 return (-1); 1945 } 1946 break; 1947 default: 1948 cvs_log(LP_ERR, "unexpected token `%s' in RCS delta", 1949 RCS_TOKSTR(rfp)); 1950 rcs_freedelta(rdp); 1951 return (-1); 1952 } 1953 } 1954 1955 if (tokstr != NULL) 1956 xfree(tokstr); 1957 1958 TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list); 1959 rfp->rf_ndelta++; 1960 1961 return (ret); 1962 } 1963 1964 /* 1965 * rcs_parse_deltatext() 1966 * 1967 * Parse an RCS delta text section and fill in the log and text field of the 1968 * appropriate delta section. 1969 * Returns 1 if the section was parsed OK, 0 if it is the last delta, and 1970 * -1 on error. 1971 */ 1972 static int 1973 rcs_parse_deltatext(RCSFILE *rfp) 1974 { 1975 int tok; 1976 RCSNUM *tnum; 1977 struct rcs_delta *rdp; 1978 1979 tok = rcs_gettok(rfp); 1980 if (tok == RCS_TOK_EOF) 1981 return (0); 1982 1983 if (tok != RCS_TOK_NUM) { 1984 cvs_log(LP_ERR, 1985 "unexpected token `%s' at start of RCS delta text", 1986 RCS_TOKSTR(rfp)); 1987 return (-1); 1988 } 1989 1990 tnum = rcsnum_alloc(); 1991 rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum); 1992 1993 TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) { 1994 if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0) 1995 break; 1996 } 1997 rcsnum_free(tnum); 1998 1999 if (rdp == NULL) { 2000 cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta", 2001 RCS_TOKSTR(rfp)); 2002 return (-1); 2003 } 2004 2005 tok = rcs_gettok(rfp); 2006 if (tok != RCS_TOK_LOG) { 2007 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 2008 RCS_TOKSTR(rfp)); 2009 return (-1); 2010 } 2011 2012 tok = rcs_gettok(rfp); 2013 if (tok != RCS_TOK_STRING) { 2014 cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected", 2015 RCS_TOKSTR(rfp)); 2016 return (-1); 2017 } 2018 rdp->rd_log = xstrdup(RCS_TOKSTR(rfp)); 2019 tok = rcs_gettok(rfp); 2020 if (tok != RCS_TOK_TEXT) { 2021 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 2022 RCS_TOKSTR(rfp)); 2023 return (-1); 2024 } 2025 2026 tok = rcs_gettok(rfp); 2027 if (tok != RCS_TOK_STRING) { 2028 cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected", 2029 RCS_TOKSTR(rfp)); 2030 return (-1); 2031 } 2032 2033 if (RCS_TOKLEN(rfp) == 0) { 2034 rdp->rd_text = xmalloc(1); 2035 rdp->rd_text[0] = '\0'; 2036 rdp->rd_tlen = 0; 2037 } else { 2038 rdp->rd_text = xmalloc(RCS_TOKLEN(rfp)); 2039 memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp)); 2040 rdp->rd_tlen = RCS_TOKLEN(rfp); 2041 } 2042 2043 return (1); 2044 } 2045 2046 /* 2047 * rcs_parse_access() 2048 * 2049 * Parse the access list given as value to the `access' keyword. 2050 * Returns 0 on success, or -1 on failure. 2051 */ 2052 static int 2053 rcs_parse_access(RCSFILE *rfp) 2054 { 2055 int type; 2056 2057 while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) { 2058 if (type != RCS_TOK_ID) { 2059 cvs_log(LP_ERR, "unexpected token `%s' in access list", 2060 RCS_TOKSTR(rfp)); 2061 return (-1); 2062 } 2063 2064 if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0) 2065 return (-1); 2066 } 2067 2068 return (0); 2069 } 2070 2071 /* 2072 * rcs_parse_symbols() 2073 * 2074 * Parse the symbol list given as value to the `symbols' keyword. 2075 * Returns 0 on success, or -1 on failure. 2076 */ 2077 static int 2078 rcs_parse_symbols(RCSFILE *rfp) 2079 { 2080 int type; 2081 struct rcs_sym *symp; 2082 2083 for (;;) { 2084 type = rcs_gettok(rfp); 2085 if (type == RCS_TOK_SCOLON) 2086 break; 2087 2088 if (type != RCS_TOK_ID) { 2089 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2090 RCS_TOKSTR(rfp)); 2091 return (-1); 2092 } 2093 2094 symp = xmalloc(sizeof(*symp)); 2095 symp->rs_name = xstrdup(RCS_TOKSTR(rfp)); 2096 symp->rs_num = rcsnum_alloc(); 2097 2098 type = rcs_gettok(rfp); 2099 if (type != RCS_TOK_COLON) { 2100 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2101 RCS_TOKSTR(rfp)); 2102 rcsnum_free(symp->rs_num); 2103 xfree(symp->rs_name); 2104 xfree(symp); 2105 return (-1); 2106 } 2107 2108 type = rcs_gettok(rfp); 2109 if (type != RCS_TOK_NUM) { 2110 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2111 RCS_TOKSTR(rfp)); 2112 rcsnum_free(symp->rs_num); 2113 xfree(symp->rs_name); 2114 xfree(symp); 2115 return (-1); 2116 } 2117 2118 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) { 2119 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 2120 RCS_TOKSTR(rfp)); 2121 rcsnum_free(symp->rs_num); 2122 xfree(symp->rs_name); 2123 xfree(symp); 2124 return (-1); 2125 } 2126 2127 TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list); 2128 } 2129 2130 return (0); 2131 } 2132 2133 /* 2134 * rcs_parse_locks() 2135 * 2136 * Parse the lock list given as value to the `locks' keyword. 2137 * Returns 0 on success, or -1 on failure. 2138 */ 2139 static int 2140 rcs_parse_locks(RCSFILE *rfp) 2141 { 2142 int type; 2143 struct rcs_lock *lkp; 2144 2145 for (;;) { 2146 type = rcs_gettok(rfp); 2147 if (type == RCS_TOK_SCOLON) 2148 break; 2149 2150 if (type != RCS_TOK_ID) { 2151 cvs_log(LP_ERR, "unexpected token `%s' in lock list", 2152 RCS_TOKSTR(rfp)); 2153 return (-1); 2154 } 2155 2156 lkp = xmalloc(sizeof(*lkp)); 2157 lkp->rl_name = xstrdup(RCS_TOKSTR(rfp)); 2158 lkp->rl_num = rcsnum_alloc(); 2159 2160 type = rcs_gettok(rfp); 2161 if (type != RCS_TOK_COLON) { 2162 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2163 RCS_TOKSTR(rfp)); 2164 rcsnum_free(lkp->rl_num); 2165 xfree(lkp->rl_name); 2166 xfree(lkp); 2167 return (-1); 2168 } 2169 2170 type = rcs_gettok(rfp); 2171 if (type != RCS_TOK_NUM) { 2172 cvs_log(LP_ERR, "unexpected token `%s' in symbol list", 2173 RCS_TOKSTR(rfp)); 2174 rcsnum_free(lkp->rl_num); 2175 xfree(lkp->rl_name); 2176 xfree(lkp); 2177 return (-1); 2178 } 2179 2180 if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) { 2181 cvs_log(LP_ERR, "failed to parse RCS NUM `%s'", 2182 RCS_TOKSTR(rfp)); 2183 rcsnum_free(lkp->rl_num); 2184 xfree(lkp->rl_name); 2185 xfree(lkp); 2186 return (-1); 2187 } 2188 2189 TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list); 2190 } 2191 2192 /* check if we have a `strict' */ 2193 type = rcs_gettok(rfp); 2194 if (type != RCS_TOK_STRICT) { 2195 rcs_pushtok(rfp, RCS_TOKSTR(rfp), type); 2196 } else { 2197 rfp->rf_flags |= RCS_SLOCK; 2198 2199 type = rcs_gettok(rfp); 2200 if (type != RCS_TOK_SCOLON) { 2201 cvs_log(LP_ERR, 2202 "missing semi-colon after `strict' keyword"); 2203 return (-1); 2204 } 2205 } 2206 2207 return (0); 2208 } 2209 2210 /* 2211 * rcs_parse_branches() 2212 * 2213 * Parse the list of branches following a `branches' keyword in a delta. 2214 * Returns 0 on success, or -1 on failure. 2215 */ 2216 static int 2217 rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp) 2218 { 2219 int type; 2220 struct rcs_branch *brp; 2221 2222 for (;;) { 2223 type = rcs_gettok(rfp); 2224 if (type == RCS_TOK_SCOLON) 2225 break; 2226 2227 if (type != RCS_TOK_NUM) { 2228 cvs_log(LP_ERR, 2229 "unexpected token `%s' in list of branches", 2230 RCS_TOKSTR(rfp)); 2231 return (-1); 2232 } 2233 2234 brp = xmalloc(sizeof(*brp)); 2235 brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp)); 2236 if (brp->rb_num == NULL) { 2237 xfree(brp); 2238 return (-1); 2239 } 2240 2241 TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); 2242 } 2243 2244 return (0); 2245 } 2246 2247 /* 2248 * rcs_freedelta() 2249 * 2250 * Free the contents of a delta structure. 2251 */ 2252 static void 2253 rcs_freedelta(struct rcs_delta *rdp) 2254 { 2255 struct rcs_branch *rb; 2256 2257 if (rdp->rd_num != NULL) 2258 rcsnum_free(rdp->rd_num); 2259 if (rdp->rd_next != NULL) 2260 rcsnum_free(rdp->rd_next); 2261 2262 if (rdp->rd_author != NULL) 2263 xfree(rdp->rd_author); 2264 if (rdp->rd_locker != NULL) 2265 xfree(rdp->rd_locker); 2266 if (rdp->rd_state != NULL) 2267 xfree(rdp->rd_state); 2268 if (rdp->rd_log != NULL) 2269 xfree(rdp->rd_log); 2270 if (rdp->rd_text != NULL) 2271 xfree(rdp->rd_text); 2272 2273 while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) { 2274 TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list); 2275 rcsnum_free(rb->rb_num); 2276 xfree(rb); 2277 } 2278 2279 xfree(rdp); 2280 } 2281 2282 /* 2283 * rcs_freepdata() 2284 * 2285 * Free the contents of the parser data structure. 2286 */ 2287 static void 2288 rcs_freepdata(struct rcs_pdata *pd) 2289 { 2290 if (pd->rp_file != NULL) 2291 (void)fclose(pd->rp_file); 2292 if (pd->rp_buf != NULL) 2293 xfree(pd->rp_buf); 2294 xfree(pd); 2295 } 2296 2297 /* 2298 * rcs_gettok() 2299 * 2300 * Get the next RCS token from the string <str>. 2301 */ 2302 static int 2303 rcs_gettok(RCSFILE *rfp) 2304 { 2305 u_int i; 2306 int ch, last, type; 2307 size_t len; 2308 char *bp; 2309 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2310 2311 type = RCS_TOK_ERR; 2312 bp = pdp->rp_buf; 2313 pdp->rp_tlen = 0; 2314 *bp = '\0'; 2315 2316 if (pdp->rp_pttype != RCS_TOK_ERR) { 2317 type = pdp->rp_pttype; 2318 if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >= 2319 pdp->rp_blen) 2320 fatal("rcs_gettok: strlcpy"); 2321 pdp->rp_pttype = RCS_TOK_ERR; 2322 return (type); 2323 } 2324 2325 /* skip leading whitespace */ 2326 /* XXX we must skip backspace too for compatibility, should we? */ 2327 do { 2328 ch = getc(pdp->rp_file); 2329 if (ch == '\n') 2330 pdp->rp_lines++; 2331 } while (isspace(ch)); 2332 2333 if (ch == EOF) { 2334 type = RCS_TOK_EOF; 2335 } else if (ch == ';') { 2336 type = RCS_TOK_SCOLON; 2337 } else if (ch == ':') { 2338 type = RCS_TOK_COLON; 2339 } else if (isalpha(ch)) { 2340 type = RCS_TOK_ID; 2341 *(bp++) = ch; 2342 for (;;) { 2343 ch = getc(pdp->rp_file); 2344 if (ch == EOF) { 2345 type = RCS_TOK_EOF; 2346 break; 2347 } else if (!isalnum(ch) && ch != '_' && ch != '-' && 2348 ch != '/' && ch != '+' && ch != '|') { 2349 ungetc(ch, pdp->rp_file); 2350 break; 2351 } 2352 *(bp++) = ch; 2353 pdp->rp_tlen++; 2354 if (bp == pdp->rp_bufend - 1) { 2355 len = bp - pdp->rp_buf; 2356 rcs_growbuf(rfp); 2357 bp = pdp->rp_buf + len; 2358 } 2359 } 2360 *bp = '\0'; 2361 2362 if (type != RCS_TOK_ERR && rcs_ignore_keys != 1) { 2363 for (i = 0; i < RCS_NKEYS; i++) { 2364 if (strcmp(rcs_keys[i].rk_str, 2365 pdp->rp_buf) == 0) { 2366 type = rcs_keys[i].rk_id; 2367 break; 2368 } 2369 } 2370 } 2371 } else if (ch == '@') { 2372 /* we have a string */ 2373 type = RCS_TOK_STRING; 2374 for (;;) { 2375 ch = getc(pdp->rp_file); 2376 if (ch == EOF) { 2377 type = RCS_TOK_EOF; 2378 break; 2379 } else if (ch == '@') { 2380 ch = getc(pdp->rp_file); 2381 if (ch != '@') { 2382 ungetc(ch, pdp->rp_file); 2383 break; 2384 } 2385 } else if (ch == '\n') 2386 pdp->rp_lines++; 2387 2388 *(bp++) = ch; 2389 pdp->rp_tlen++; 2390 if (bp == pdp->rp_bufend - 1) { 2391 len = bp - pdp->rp_buf; 2392 rcs_growbuf(rfp); 2393 bp = pdp->rp_buf + len; 2394 } 2395 } 2396 2397 *bp = '\0'; 2398 } else if (isdigit(ch)) { 2399 *(bp++) = ch; 2400 last = ch; 2401 type = RCS_TOK_NUM; 2402 2403 for (;;) { 2404 ch = getc(pdp->rp_file); 2405 if (ch == EOF) { 2406 type = RCS_TOK_EOF; 2407 break; 2408 } 2409 if (bp == pdp->rp_bufend) 2410 break; 2411 if (!isdigit(ch) && ch != '.') { 2412 ungetc(ch, pdp->rp_file); 2413 break; 2414 } 2415 2416 if (last == '.' && ch == '.') { 2417 type = RCS_TOK_ERR; 2418 break; 2419 } 2420 last = ch; 2421 *(bp++) = ch; 2422 pdp->rp_tlen++; 2423 } 2424 *bp = '\0'; 2425 } 2426 2427 return (type); 2428 } 2429 2430 /* 2431 * rcs_pushtok() 2432 * 2433 * Push a token back in the parser's token buffer. 2434 */ 2435 static int 2436 rcs_pushtok(RCSFILE *rfp, const char *tok, int type) 2437 { 2438 struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata; 2439 2440 if (pdp->rp_pttype != RCS_TOK_ERR) 2441 return (-1); 2442 2443 pdp->rp_pttype = type; 2444 if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >= 2445 sizeof(pdp->rp_ptok)) 2446 fatal("rcs_pushtok: strlcpy"); 2447 return (0); 2448 } 2449 2450 2451 /* 2452 * rcs_growbuf() 2453 * 2454 * Attempt to grow the internal parse buffer for the RCS file <rf> by 2455 * RCS_BUFEXTSIZE. 2456 * In case of failure, the original buffer is left unmodified. 2457 */ 2458 static void 2459 rcs_growbuf(RCSFILE *rf) 2460 { 2461 struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata; 2462 2463 pdp->rp_buf = xrealloc(pdp->rp_buf, 1, 2464 pdp->rp_blen + RCS_BUFEXTSIZE); 2465 pdp->rp_blen += RCS_BUFEXTSIZE; 2466 pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1; 2467 } 2468 2469 /* 2470 * rcs_strprint() 2471 * 2472 * Output an RCS string <str> of size <slen> to the stream <stream>. Any 2473 * '@' characters are escaped. Otherwise, the string can contain arbitrary 2474 * binary data. 2475 */ 2476 static void 2477 rcs_strprint(const u_char *str, size_t slen, FILE *stream) 2478 { 2479 const u_char *ap, *ep, *sp; 2480 2481 if (slen == 0) 2482 return; 2483 2484 ep = str + slen - 1; 2485 2486 for (sp = str; sp <= ep;) { 2487 ap = memchr(sp, '@', ep - sp); 2488 if (ap == NULL) 2489 ap = ep; 2490 (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream); 2491 2492 if (*ap == '@') 2493 putc('@', stream); 2494 sp = ap + 1; 2495 } 2496 } 2497 2498 /* 2499 * rcs_deltatext_set() 2500 * 2501 * Set deltatext for <rev> in RCS file <rfp> to <dtext> 2502 * Returns -1 on error, 0 on success. 2503 */ 2504 int 2505 rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp) 2506 { 2507 size_t len; 2508 u_char *dtext; 2509 struct rcs_delta *rdp; 2510 2511 /* Write operations require full parsing */ 2512 rcs_parse_deltatexts(rfp, NULL); 2513 2514 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2515 return (-1); 2516 2517 if (rdp->rd_text != NULL) 2518 xfree(rdp->rd_text); 2519 2520 len = cvs_buf_len(bp); 2521 dtext = cvs_buf_release(bp); 2522 bp = NULL; 2523 2524 if (len != 0) { 2525 rdp->rd_text = xmalloc(len); 2526 rdp->rd_tlen = len; 2527 (void)memcpy(rdp->rd_text, dtext, len); 2528 } else { 2529 rdp->rd_text = NULL; 2530 rdp->rd_tlen = 0; 2531 } 2532 2533 if (dtext != NULL) 2534 xfree(dtext); 2535 2536 return (0); 2537 } 2538 2539 /* 2540 * rcs_rev_setlog() 2541 * 2542 * Sets the log message of revision <rev> to <logtext> 2543 */ 2544 int 2545 rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext) 2546 { 2547 struct rcs_delta *rdp; 2548 char buf[CVS_REV_BUFSZ]; 2549 2550 rcsnum_tostr(rev, buf, sizeof(buf)); 2551 2552 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2553 return (-1); 2554 2555 if (rdp->rd_log != NULL) 2556 xfree(rdp->rd_log); 2557 2558 rdp->rd_log = xstrdup(logtext); 2559 rfp->rf_flags &= ~RCS_SYNCED; 2560 return (0); 2561 } 2562 /* 2563 * rcs_rev_getdate() 2564 * 2565 * Get the date corresponding to a given revision. 2566 * Returns the date on success, -1 on failure. 2567 */ 2568 time_t 2569 rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev) 2570 { 2571 struct rcs_delta *rdp; 2572 2573 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2574 return (-1); 2575 2576 return (timegm(&rdp->rd_date)); 2577 } 2578 2579 /* 2580 * rcs_state_set() 2581 * 2582 * Sets the state of revision <rev> to <state> 2583 * NOTE: default state is 'Exp'. States may not contain spaces. 2584 * 2585 * Returns -1 on failure, 0 on success. 2586 */ 2587 int 2588 rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state) 2589 { 2590 struct rcs_delta *rdp; 2591 2592 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2593 return (-1); 2594 2595 if (rdp->rd_state != NULL) 2596 xfree(rdp->rd_state); 2597 2598 rdp->rd_state = xstrdup(state); 2599 2600 rfp->rf_flags &= ~RCS_SYNCED; 2601 2602 return (0); 2603 } 2604 2605 /* 2606 * rcs_state_check() 2607 * 2608 * Check if string <state> is valid. 2609 * 2610 * Returns 0 if the string is valid, -1 otherwise. 2611 */ 2612 int 2613 rcs_state_check(const char *state) 2614 { 2615 if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP)) 2616 return (-1); 2617 2618 return (0); 2619 } 2620 2621 /* 2622 * rcs_state_get() 2623 * 2624 * Get the state for a given revision of a specified RCSFILE. 2625 * 2626 * Returns NULL on failure. 2627 */ 2628 const char * 2629 rcs_state_get(RCSFILE *rfp, RCSNUM *rev) 2630 { 2631 struct rcs_delta *rdp; 2632 2633 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 2634 return (NULL); 2635 2636 return (rdp->rd_state); 2637 } 2638 2639 /* rcs_get_revision() */ 2640 static RCSNUM * 2641 rcs_get_revision(const char *revstr, RCSFILE *rfp) 2642 { 2643 RCSNUM *rev, *brev, *frev; 2644 struct rcs_branch *brp; 2645 struct rcs_delta *rdp; 2646 size_t i; 2647 2648 rdp = NULL; 2649 2650 if (!strcmp(revstr, RCS_HEAD_BRANCH)) { 2651 if (rfp->rf_head == NULL) 2652 return (NULL); 2653 2654 frev = rcsnum_alloc(); 2655 rcsnum_cpy(rfp->rf_head, frev, 0); 2656 return (frev); 2657 } 2658 2659 /* Possibly we could be passed a version number */ 2660 if ((rev = rcsnum_parse(revstr)) != NULL) { 2661 /* Do not return if it is not in RCS file */ 2662 if ((rdp = rcs_findrev(rfp, rev)) != NULL) 2663 return (rev); 2664 } else { 2665 /* More likely we will be passed a symbol */ 2666 rev = rcs_sym_getrev(rfp, revstr); 2667 } 2668 2669 if (rev == NULL) 2670 return (NULL); 2671 2672 /* 2673 * If it was not a branch, thats ok the symbolic 2674 * name refered to a revision, so return the resolved 2675 * revision for the given name. */ 2676 if (!RCSNUM_ISBRANCH(rev)) { 2677 /* Sanity check: The first two elements of any 2678 * revision (be it on a branch or on trunk) cannot 2679 * be greater than HEAD. 2680 * 2681 * XXX: To avoid comparing to uninitialized memory, 2682 * the minimum of both revision lengths is taken 2683 * instead of just 2. 2684 */ 2685 if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head, 2686 MIN(rfp->rf_head->rn_len, rev->rn_len)) < 0) { 2687 rcsnum_free(rev); 2688 return (NULL); 2689 } 2690 return (rev); 2691 } 2692 2693 brev = rcsnum_alloc(); 2694 rcsnum_cpy(rev, brev, rev->rn_len - 1); 2695 2696 if ((rdp = rcs_findrev(rfp, brev)) == NULL) 2697 fatal("rcs_get_revision: tag `%s' does not exist", revstr); 2698 rcsnum_free(brev); 2699 2700 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2701 for (i = 0; i < rev->rn_len; i++) 2702 if (brp->rb_num->rn_id[i] != rev->rn_id[i]) 2703 break; 2704 if (i != rev->rn_len) 2705 continue; 2706 break; 2707 } 2708 2709 rcsnum_free(rev); 2710 frev = rcsnum_alloc(); 2711 if (brp == NULL) { 2712 rcsnum_cpy(rdp->rd_num, frev, 0); 2713 return (frev); 2714 } else { 2715 /* Fetch the delta with the correct branch num */ 2716 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2717 fatal("rcs_get_revision: could not fetch branch " 2718 "delta"); 2719 rcsnum_cpy(rdp->rd_num, frev, 0); 2720 return (frev); 2721 } 2722 } 2723 2724 /* 2725 * rcs_rev_getlines() 2726 * 2727 * Get the entire contents of revision <frev> from the RCSFILE <rfp> and 2728 * return it as a pointer to a struct cvs_lines. 2729 */ 2730 struct cvs_lines * 2731 rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines) 2732 { 2733 size_t plen; 2734 int annotate, done, i, nextroot; 2735 RCSNUM *tnum, *bnum; 2736 struct rcs_branch *brp; 2737 struct rcs_delta *hrdp, *prdp, *rdp, *trdp; 2738 u_char *patch; 2739 struct cvs_line *line, *nline; 2740 struct cvs_lines *dlines, *plines; 2741 2742 if (rfp->rf_head == NULL || 2743 (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL) 2744 fatal("rcs_rev_getlines: no HEAD revision"); 2745 2746 tnum = frev; 2747 rcs_parse_deltatexts(rfp, hrdp->rd_num); 2748 2749 /* revision on branch, get the branch root */ 2750 nextroot = 2; 2751 bnum = rcsnum_alloc(); 2752 if (RCSNUM_ISBRANCHREV(tnum)) 2753 rcsnum_cpy(tnum, bnum, nextroot); 2754 else 2755 rcsnum_cpy(tnum, bnum, tnum->rn_len); 2756 2757 if (alines != NULL) { 2758 /* start with annotate first at requested revision */ 2759 annotate = ANNOTATE_LATER; 2760 *alines = NULL; 2761 } else 2762 annotate = ANNOTATE_NEVER; 2763 2764 dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen); 2765 2766 done = 0; 2767 2768 rdp = hrdp; 2769 if (!rcsnum_differ(rdp->rd_num, bnum)) { 2770 if (annotate == ANNOTATE_LATER) { 2771 /* found requested revision for annotate */ 2772 i = 0; 2773 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 2774 line->l_lineno_orig = line->l_lineno; 2775 i++; 2776 } 2777 2778 *alines = xcalloc(i + 1, sizeof(struct cvs_line *)); 2779 (*alines)[i] = NULL; 2780 annotate = ANNOTATE_NOW; 2781 2782 /* annotate down to 1.1 from where we are */ 2783 rcsnum_free(bnum); 2784 bnum = rcsnum_parse("1.1"); 2785 if (!rcsnum_differ(rdp->rd_num, bnum)) { 2786 goto next; 2787 } 2788 } else 2789 goto next; 2790 } 2791 2792 prdp = hrdp; 2793 if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL) 2794 goto done; 2795 2796 again: 2797 for (;;) { 2798 if (rdp->rd_next->rn_len != 0) { 2799 trdp = rcs_findrev(rfp, rdp->rd_next); 2800 if (trdp == NULL) 2801 fatal("failed to grab next revision"); 2802 } 2803 2804 if (rdp->rd_tlen == 0) { 2805 rcs_parse_deltatexts(rfp, rdp->rd_num); 2806 if (rdp->rd_tlen == 0) { 2807 if (!rcsnum_differ(rdp->rd_num, bnum)) 2808 break; 2809 rdp = trdp; 2810 continue; 2811 } 2812 } 2813 2814 plen = rdp->rd_tlen; 2815 patch = rdp->rd_text; 2816 plines = cvs_splitlines(patch, plen); 2817 if (annotate == ANNOTATE_NOW) 2818 rcs_patch_lines(dlines, plines, *alines, prdp); 2819 else 2820 rcs_patch_lines(dlines, plines, NULL, NULL); 2821 cvs_freelines(plines); 2822 2823 if (!rcsnum_differ(rdp->rd_num, bnum)) { 2824 if (annotate != ANNOTATE_LATER) 2825 break; 2826 2827 /* found requested revision for annotate */ 2828 i = 0; 2829 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 2830 line->l_lineno_orig = line->l_lineno; 2831 i++; 2832 } 2833 2834 *alines = xcalloc(i + 1, sizeof(struct cvs_line *)); 2835 (*alines)[i] = NULL; 2836 annotate = ANNOTATE_NOW; 2837 2838 /* annotate down to 1.1 from where we are */ 2839 rcsnum_free(bnum); 2840 bnum = rcsnum_parse("1.1"); 2841 2842 if (!rcsnum_differ(rdp->rd_num, bnum)) 2843 break; 2844 } 2845 2846 prdp = rdp; 2847 rdp = trdp; 2848 } 2849 2850 next: 2851 if (!rcsnum_differ(rdp->rd_num, frev)) 2852 done = 1; 2853 2854 if (RCSNUM_ISBRANCHREV(frev) && done != 1) { 2855 nextroot += 2; 2856 rcsnum_cpy(frev, bnum, nextroot); 2857 2858 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2859 for (i = 0; i < nextroot - 1; i++) 2860 if (brp->rb_num->rn_id[i] != bnum->rn_id[i]) 2861 break; 2862 if (i == nextroot - 1) 2863 break; 2864 } 2865 2866 if (brp == NULL) { 2867 if (annotate != ANNOTATE_NEVER) { 2868 if (*alines != NULL) 2869 xfree(*alines); 2870 *alines = NULL; 2871 cvs_freelines(dlines); 2872 rcsnum_free(bnum); 2873 return (NULL); 2874 } 2875 fatal("expected branch not found on branch list"); 2876 } 2877 2878 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2879 fatal("rcs_rev_getlines: failed to get delta for target rev"); 2880 2881 goto again; 2882 } 2883 done: 2884 /* put remaining lines into annotate buffer */ 2885 if (annotate == ANNOTATE_NOW) { 2886 for (line = TAILQ_FIRST(&(dlines->l_lines)); 2887 line != NULL; line = nline) { 2888 nline = TAILQ_NEXT(line, l_list); 2889 TAILQ_REMOVE(&(dlines->l_lines), line, l_list); 2890 if (line->l_line == NULL) { 2891 xfree(line); 2892 continue; 2893 } 2894 2895 line->l_delta = rdp; 2896 (*alines)[line->l_lineno_orig - 1] = line; 2897 } 2898 2899 cvs_freelines(dlines); 2900 dlines = NULL; 2901 } 2902 2903 if (bnum != tnum) 2904 rcsnum_free(bnum); 2905 2906 return (dlines); 2907 } 2908 2909 void 2910 rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct cvs_line ***alines) 2911 { 2912 size_t plen; 2913 int i, nextroot; 2914 RCSNUM *bnum; 2915 struct rcs_branch *brp; 2916 struct rcs_delta *rdp, *trdp; 2917 u_char *patch; 2918 struct cvs_line *line; 2919 struct cvs_lines *dlines, *plines; 2920 2921 if (!RCSNUM_ISBRANCHREV(frev)) 2922 fatal("rcs_annotate_getlines: branch revision expected"); 2923 2924 /* revision on branch, get the branch root */ 2925 nextroot = 2; 2926 bnum = rcsnum_alloc(); 2927 rcsnum_cpy(frev, bnum, nextroot); 2928 2929 /* 2930 * Going from HEAD to 1.1 enables the use of an array, which is 2931 * much faster. Unfortunately this is not possible with branch 2932 * revisions, so copy over our alines (array) into dlines (tailq). 2933 */ 2934 dlines = xcalloc(1, sizeof(*dlines)); 2935 TAILQ_INIT(&(dlines->l_lines)); 2936 line = xcalloc(1, sizeof(*line)); 2937 TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list); 2938 2939 for (i = 0; (*alines)[i] != NULL; i++) { 2940 line = (*alines)[i]; 2941 line->l_lineno = i + 1; 2942 TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list); 2943 } 2944 2945 rdp = rcs_findrev(rfp, bnum); 2946 if (rdp == NULL) 2947 fatal("failed to grab branch root revision"); 2948 2949 do { 2950 nextroot += 2; 2951 rcsnum_cpy(frev, bnum, nextroot); 2952 2953 TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) { 2954 for (i = 0; i < nextroot - 1; i++) 2955 if (brp->rb_num->rn_id[i] != bnum->rn_id[i]) 2956 break; 2957 if (i == nextroot - 1) 2958 break; 2959 } 2960 2961 if (brp == NULL) 2962 fatal("expected branch not found on branch list"); 2963 2964 if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL) 2965 fatal("failed to get delta for target rev"); 2966 2967 for (;;) { 2968 if (rdp->rd_next->rn_len != 0) { 2969 trdp = rcs_findrev(rfp, rdp->rd_next); 2970 if (trdp == NULL) 2971 fatal("failed to grab next revision"); 2972 } 2973 2974 if (rdp->rd_tlen == 0) { 2975 rcs_parse_deltatexts(rfp, rdp->rd_num); 2976 if (rdp->rd_tlen == 0) { 2977 if (!rcsnum_differ(rdp->rd_num, bnum)) 2978 break; 2979 rdp = trdp; 2980 continue; 2981 } 2982 } 2983 2984 plen = rdp->rd_tlen; 2985 patch = rdp->rd_text; 2986 plines = cvs_splitlines(patch, plen); 2987 rcs_patch_lines(dlines, plines, NULL, rdp); 2988 cvs_freelines(plines); 2989 2990 if (!rcsnum_differ(rdp->rd_num, bnum)) 2991 break; 2992 2993 rdp = trdp; 2994 } 2995 } while (rcsnum_differ(rdp->rd_num, frev)); 2996 2997 if (bnum != frev) 2998 rcsnum_free(bnum); 2999 3000 /* 3001 * All lines have been parsed, now they must be copied over 3002 * into alines (array) again. 3003 */ 3004 xfree(*alines); 3005 3006 i = 0; 3007 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 3008 if (line->l_line != NULL) 3009 i++; 3010 } 3011 *alines = xcalloc(i + 1, sizeof(struct cvs_line *)); 3012 (*alines)[i] = NULL; 3013 3014 i = 0; 3015 TAILQ_FOREACH(line, &(dlines->l_lines), l_list) { 3016 if (line->l_line != NULL) 3017 (*alines)[i++] = line; 3018 } 3019 } 3020 3021 /* 3022 * rcs_rev_getbuf() 3023 * 3024 * XXX: This is really really slow and should be avoided if at all possible! 3025 * 3026 * Get the entire contents of revision <rev> from the RCSFILE <rfp> and 3027 * return it as a BUF pointer. 3028 */ 3029 BUF * 3030 rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode) 3031 { 3032 int expmode, expand; 3033 struct rcs_delta *rdp; 3034 struct cvs_lines *lines; 3035 struct cvs_line *lp, *nlp; 3036 BUF *bp; 3037 3038 expand = 0; 3039 lines = rcs_rev_getlines(rfp, rev, NULL); 3040 bp = cvs_buf_alloc(1024 * 16); 3041 3042 if (!(mode & RCS_KWEXP_NONE)) { 3043 if (rfp->rf_expand != NULL) 3044 expmode = rcs_kwexp_get(rfp); 3045 else 3046 expmode = RCS_KWEXP_DEFAULT; 3047 3048 if (!(expmode & RCS_KWEXP_NONE)) { 3049 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 3050 fatal("could not fetch revision"); 3051 expand = 1; 3052 } 3053 } 3054 3055 for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) { 3056 nlp = TAILQ_NEXT(lp, l_list); 3057 3058 if (lp->l_line == NULL) { 3059 lp = nlp; 3060 continue; 3061 } 3062 3063 if (expand) 3064 rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode); 3065 3066 do { 3067 cvs_buf_append(bp, lp->l_line, lp->l_len); 3068 } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp); 3069 } 3070 3071 cvs_freelines(lines); 3072 3073 return (bp); 3074 } 3075 3076 /* 3077 * rcs_rev_write_fd() 3078 * 3079 * Write the entire contents of revision <frev> from the rcsfile <rfp> to 3080 * file descriptor <fd>. 3081 */ 3082 void 3083 rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode) 3084 { 3085 int fd; 3086 FILE *fp; 3087 size_t ret; 3088 int expmode, expand; 3089 struct rcs_delta *rdp; 3090 struct cvs_lines *lines; 3091 struct cvs_line *lp, *nlp; 3092 extern int print_stdout; 3093 3094 expand = 0; 3095 lines = rcs_rev_getlines(rfp, rev, NULL); 3096 3097 if (!(mode & RCS_KWEXP_NONE)) { 3098 if (rfp->rf_expand != NULL) 3099 expmode = rcs_kwexp_get(rfp); 3100 else 3101 expmode = RCS_KWEXP_DEFAULT; 3102 3103 if (!(expmode & RCS_KWEXP_NONE)) { 3104 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 3105 fatal("could not fetch revision"); 3106 expand = 1; 3107 } 3108 } 3109 3110 fd = dup(_fd); 3111 if (fd == -1) 3112 fatal("rcs_rev_write_fd: dup: %s", strerror(errno)); 3113 3114 if ((fp = fdopen(fd, "w")) == NULL) 3115 fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno)); 3116 3117 for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) { 3118 nlp = TAILQ_NEXT(lp, l_list); 3119 3120 if (lp->l_line == NULL) { 3121 lp = nlp; 3122 continue; 3123 } 3124 3125 if (expand) 3126 rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode); 3127 3128 do { 3129 /* 3130 * Solely for the checkout and update -p options. 3131 */ 3132 if (cvs_server_active == 1 && 3133 (cvs_cmdop == CVS_OP_CHECKOUT || 3134 cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) { 3135 ret = fwrite("M ", 1, 2, fp); 3136 if (ret != 2) 3137 fatal("rcs_rev_write_fd: %s", 3138 strerror(errno)); 3139 } 3140 3141 ret = fwrite(lp->l_line, 1, lp->l_len, fp); 3142 if (ret != lp->l_len) 3143 fatal("rcs_rev_write_fd: %s", strerror(errno)); 3144 } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp); 3145 } 3146 3147 cvs_freelines(lines); 3148 (void)fclose(fp); 3149 } 3150 3151 /* 3152 * rcs_rev_write_stmp() 3153 * 3154 * Write the contents of the rev <rev> to a temporary file whose path is 3155 * specified using <template> (see mkstemp(3)). NB. This function will modify 3156 * <template>, as per mkstemp. 3157 */ 3158 int 3159 rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode) 3160 { 3161 int fd; 3162 3163 if ((fd = mkstemp(template)) == -1) 3164 fatal("mkstemp: `%s': %s", template, strerror(errno)); 3165 3166 cvs_worklist_add(template, &temp_files); 3167 rcs_rev_write_fd(rfp, rev, fd, mode); 3168 3169 if (lseek(fd, 0, SEEK_SET) < 0) 3170 fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno)); 3171 3172 return (fd); 3173 } 3174 3175 static void 3176 rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct cvs_lines *lines, 3177 struct cvs_line *line, int mode) 3178 { 3179 BUF *tmpbuf; 3180 int kwtype; 3181 u_int j, found; 3182 const u_char *c, *start, *fin, *end; 3183 char *kwstr; 3184 char expbuf[256], buf[256]; 3185 char *fmt; 3186 size_t clen, kwlen, len, tlen; 3187 3188 kwtype = 0; 3189 kwstr = NULL; 3190 3191 if (mode & RCS_KWEXP_OLD) 3192 return; 3193 3194 len = line->l_len; 3195 if (len == 0) 3196 return; 3197 3198 c = line->l_line; 3199 found = 0; 3200 /* Final character in buffer. */ 3201 fin = c + len - 1; 3202 3203 /* 3204 * Keyword formats: 3205 * $Keyword$ 3206 * $Keyword: value$ 3207 */ 3208 for (; c < fin; c++) { 3209 if (*c != '$') 3210 continue; 3211 3212 /* remember start of this possible keyword */ 3213 start = c; 3214 3215 /* first following character has to be alphanumeric */ 3216 c++; 3217 if (!isalpha(*c)) { 3218 c = start; 3219 continue; 3220 } 3221 3222 /* Number of characters between c and fin, inclusive. */ 3223 clen = fin - c + 1; 3224 3225 /* look for any matching keywords */ 3226 found = 0; 3227 for (j = 0; j < RCS_NKWORDS; j++) { 3228 kwlen = strlen(rcs_expkw[j].kw_str); 3229 /* 3230 * kwlen must be less than clen since clen 3231 * includes either a terminating `$' or a `:'. 3232 */ 3233 if (kwlen < clen && 3234 memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 && 3235 (c[kwlen] == '$' || c[kwlen] == ':')) { 3236 found = 1; 3237 kwstr = rcs_expkw[j].kw_str; 3238 kwtype = rcs_expkw[j].kw_type; 3239 c += kwlen; 3240 break; 3241 } 3242 } 3243 3244 if (found == 0 && cvs_tagname != NULL) { 3245 kwlen = strlen(cvs_tagname); 3246 if (kwlen < clen && 3247 memcmp(c, cvs_tagname, kwlen) == 0 && 3248 (c[kwlen] == '$' || c[kwlen] == ':')) { 3249 found = 1; 3250 kwstr = cvs_tagname; 3251 kwtype = RCS_KW_ID; 3252 c += kwlen; 3253 } 3254 } 3255 3256 /* unknown keyword, continue looking */ 3257 if (found == 0) { 3258 c = start; 3259 continue; 3260 } 3261 3262 /* 3263 * if the next character was ':' we need to look for 3264 * an '$' before the end of the line to be sure it is 3265 * in fact a keyword. 3266 */ 3267 if (*c == ':') { 3268 for (; c <= fin; ++c) { 3269 if (*c == '$' || *c == '\n') 3270 break; 3271 } 3272 3273 if (*c != '$') { 3274 c = start; 3275 continue; 3276 } 3277 } 3278 end = c + 1; 3279 3280 /* start constructing the expansion */ 3281 expbuf[0] = '\0'; 3282 3283 if (mode & RCS_KWEXP_NAME) { 3284 if (strlcat(expbuf, "$", sizeof(expbuf)) >= 3285 sizeof(expbuf) || strlcat(expbuf, kwstr, 3286 sizeof(expbuf)) >= sizeof(expbuf)) 3287 fatal("rcs_kwexp_line: truncated"); 3288 if ((mode & RCS_KWEXP_VAL) && 3289 strlcat(expbuf, ": ", sizeof(expbuf)) >= 3290 sizeof(expbuf)) 3291 fatal("rcs_kwexp_line: truncated"); 3292 } 3293 3294 /* 3295 * order matters because of RCS_KW_ID and 3296 * RCS_KW_HEADER here 3297 */ 3298 if (mode & RCS_KWEXP_VAL) { 3299 if (kwtype & RCS_KW_RCSFILE) { 3300 if (!(kwtype & RCS_KW_FULLPATH)) 3301 (void)strlcat(expbuf, basename(rcsfile), 3302 sizeof(expbuf)); 3303 else 3304 (void)strlcat(expbuf, rcsfile, 3305 sizeof(expbuf)); 3306 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3307 sizeof(expbuf)) 3308 fatal("rcs_kwexp_line: truncated"); 3309 } 3310 3311 if (kwtype & RCS_KW_REVISION) { 3312 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 3313 if (strlcat(buf, " ", sizeof(buf)) >= 3314 sizeof(buf) || strlcat(expbuf, buf, 3315 sizeof(expbuf)) >= sizeof(buf)) 3316 fatal("rcs_kwexp_line: truncated"); 3317 } 3318 3319 if (kwtype & RCS_KW_DATE) { 3320 fmt = "%Y/%m/%d %H:%M:%S "; 3321 3322 if (strftime(buf, sizeof(buf), fmt, 3323 &rdp->rd_date) == 0) 3324 fatal("rcs_kwexp_line: strftime " 3325 "failure"); 3326 if (strlcat(expbuf, buf, sizeof(expbuf)) >= 3327 sizeof(expbuf)) 3328 fatal("rcs_kwexp_line: string " 3329 "truncated"); 3330 } 3331 3332 if (kwtype & RCS_KW_MDOCDATE) { 3333 /* 3334 * Do not prepend ' ' for a single 3335 * digit, %e would do so and there is 3336 * no better format for strftime(). 3337 */ 3338 if (rdp->rd_date.tm_mday < 10) 3339 fmt = "%B%e %Y "; 3340 else 3341 fmt = "%B %e %Y "; 3342 3343 if (strftime(buf, sizeof(buf), fmt, 3344 &rdp->rd_date) == 0) 3345 fatal("rcs_kwexp_line: strftime " 3346 "failure"); 3347 if (strlcat(expbuf, buf, sizeof(expbuf)) >= 3348 sizeof(expbuf)) 3349 fatal("rcs_kwexp_line: string " 3350 "truncated"); 3351 } 3352 3353 if (kwtype & RCS_KW_AUTHOR) { 3354 if (strlcat(expbuf, rdp->rd_author, 3355 sizeof(expbuf)) >= sizeof(expbuf) || 3356 strlcat(expbuf, " ", sizeof(expbuf)) >= 3357 sizeof(expbuf)) 3358 fatal("rcs_kwexp_line: string " 3359 "truncated"); 3360 } 3361 3362 if (kwtype & RCS_KW_STATE) { 3363 if (strlcat(expbuf, rdp->rd_state, 3364 sizeof(expbuf)) >= sizeof(expbuf) || 3365 strlcat(expbuf, " ", sizeof(expbuf)) >= 3366 sizeof(expbuf)) 3367 fatal("rcs_kwexp_line: string " 3368 "truncated"); 3369 } 3370 3371 /* order does not matter anymore below */ 3372 if (kwtype & RCS_KW_LOG) { 3373 char linebuf[256]; 3374 struct cvs_line *cur, *lp; 3375 char *logp, *l_line, *prefix, *q, *sprefix; 3376 size_t i; 3377 3378 /* Log line */ 3379 if (!(kwtype & RCS_KW_FULLPATH)) 3380 (void)strlcat(expbuf, 3381 basename(rcsfile), sizeof(expbuf)); 3382 else 3383 (void)strlcat(expbuf, rcsfile, 3384 sizeof(expbuf)); 3385 3386 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3387 sizeof(expbuf)) 3388 fatal("rcs_kwexp_line: string " 3389 "truncated"); 3390 3391 cur = line; 3392 3393 /* copy rdp->rd_log for strsep */ 3394 logp = xstrdup(rdp->rd_log); 3395 3396 /* copy our prefix for later processing */ 3397 prefix = xmalloc(start - line->l_line + 1); 3398 memcpy(prefix, line->l_line, 3399 start - line->l_line); 3400 prefix[start - line->l_line] = '\0'; 3401 3402 /* copy also prefix without trailing blanks. */ 3403 sprefix = xstrdup(prefix); 3404 for (i = strlen(sprefix); i > 0 && 3405 sprefix[i - 1] == ' '; i--) 3406 sprefix[i - 1] = '\0'; 3407 3408 /* new line: revision + date + author */ 3409 linebuf[0] = '\0'; 3410 if (strlcat(linebuf, "Revision ", 3411 sizeof(linebuf)) >= sizeof(linebuf)) 3412 fatal("rcs_kwexp_line: truncated"); 3413 rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); 3414 if (strlcat(linebuf, buf, sizeof(linebuf)) 3415 >= sizeof(buf)) 3416 fatal("rcs_kwexp_line: truncated"); 3417 fmt = " %Y/%m/%d %H:%M:%S "; 3418 if (strftime(buf, sizeof(buf), fmt, 3419 &rdp->rd_date) == 0) 3420 fatal("rcs_kwexp_line: strftime " 3421 "failure"); 3422 if (strlcat(linebuf, buf, sizeof(linebuf)) 3423 >= sizeof(linebuf)) 3424 fatal("rcs_kwexp_line: string " 3425 "truncated"); 3426 if (strlcat(linebuf, rdp->rd_author, 3427 sizeof(linebuf)) >= sizeof(linebuf)) 3428 fatal("rcs_kwexp_line: string " 3429 "truncated"); 3430 3431 lp = xcalloc(1, sizeof(*lp)); 3432 xasprintf(&(lp->l_line), "%s%s\n", 3433 prefix, linebuf); 3434 lp->l_len = strlen(lp->l_line); 3435 TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp, 3436 l_list); 3437 cur = lp; 3438 3439 /* Log message */ 3440 q = logp; 3441 while ((l_line = strsep(&q, "\n")) != NULL && 3442 q != NULL) { 3443 lp = xcalloc(1, sizeof(*lp)); 3444 3445 if (l_line[0] == '\0') { 3446 xasprintf(&(lp->l_line), "%s\n", 3447 sprefix); 3448 } else { 3449 xasprintf(&(lp->l_line), 3450 "%s%s\n", prefix, l_line); 3451 } 3452 3453 lp->l_len = strlen(lp->l_line); 3454 TAILQ_INSERT_AFTER(&(lines->l_lines), 3455 cur, lp, l_list); 3456 cur = lp; 3457 } 3458 xfree(logp); 3459 3460 /* 3461 * This is just another hairy mess, but it must 3462 * be done: All characters behind Log will be 3463 * written in a new line next to log messages. 3464 * But that's not enough, we have to strip all 3465 * trailing whitespaces of our prefix. 3466 */ 3467 lp = xcalloc(1, sizeof(*lp)); 3468 lp->l_line = xcalloc(strlen(sprefix) + 3469 line->l_line + line->l_len - end + 1, 1); 3470 strlcpy(lp->l_line, sprefix, 3471 strlen(sprefix) + 1); 3472 memcpy(lp->l_line + strlen(sprefix), 3473 end, line->l_line + line->l_len - end); 3474 lp->l_len = strlen(lp->l_line); 3475 TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp, 3476 l_list); 3477 cur = lp; 3478 3479 end = line->l_line + line->l_len - 1; 3480 3481 xfree(prefix); 3482 xfree(sprefix); 3483 3484 } 3485 3486 if (kwtype & RCS_KW_SOURCE) { 3487 if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >= 3488 sizeof(expbuf) || strlcat(expbuf, " ", 3489 sizeof(expbuf)) >= sizeof(expbuf)) 3490 fatal("rcs_kwexp_line: string " 3491 "truncated"); 3492 } 3493 3494 if (kwtype & RCS_KW_NAME) 3495 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3496 sizeof(expbuf)) 3497 fatal("rcs_kwexp_line: string " 3498 "truncated"); 3499 3500 if (kwtype & RCS_KW_LOCKER) 3501 if (strlcat(expbuf, " ", sizeof(expbuf)) >= 3502 sizeof(expbuf)) 3503 fatal("rcs_kwexp_line: string " 3504 "truncated"); 3505 } 3506 3507 /* end the expansion */ 3508 if (mode & RCS_KWEXP_NAME) 3509 if (strlcat(expbuf, "$", 3510 sizeof(expbuf)) >= sizeof(expbuf)) 3511 fatal("rcs_kwexp_line: truncated"); 3512 3513 /* Concatenate everything together. */ 3514 tmpbuf = cvs_buf_alloc(len + strlen(expbuf)); 3515 /* Append everything before keyword. */ 3516 cvs_buf_append(tmpbuf, line->l_line, 3517 start - line->l_line); 3518 /* Append keyword. */ 3519 cvs_buf_puts(tmpbuf, expbuf); 3520 /* Point c to end of keyword. */ 3521 tlen = cvs_buf_len(tmpbuf) - 1; 3522 /* Append everything after keyword. */ 3523 cvs_buf_append(tmpbuf, end, 3524 line->l_line + line->l_len - end); 3525 c = cvs_buf_get(tmpbuf) + tlen; 3526 /* Point fin to end of data. */ 3527 fin = cvs_buf_get(tmpbuf) + cvs_buf_len(tmpbuf) - 1; 3528 /* Recalculate new length. */ 3529 len = cvs_buf_len(tmpbuf); 3530 3531 /* tmpbuf is now ready, convert to string */ 3532 if (line->l_needsfree) 3533 xfree(line->l_line); 3534 line->l_len = len; 3535 line->l_line = cvs_buf_release(tmpbuf); 3536 line->l_needsfree = 1; 3537 } 3538 } 3539 3540 /* rcs_translate_tag() */ 3541 RCSNUM * 3542 rcs_translate_tag(const char *revstr, RCSFILE *rfp) 3543 { 3544 int follow; 3545 time_t deltatime; 3546 char branch[CVS_REV_BUFSZ]; 3547 RCSNUM *brev, *frev, *rev, *rrev; 3548 struct rcs_delta *rdp, *trdp; 3549 time_t cdate; 3550 3551 brev = frev = rrev = NULL; 3552 3553 if (revstr == NULL) { 3554 if (rfp->rf_branch != NULL) { 3555 rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch)); 3556 revstr = branch; 3557 } else { 3558 revstr = RCS_HEAD_BRANCH; 3559 } 3560 } 3561 3562 if ((rev = rcs_get_revision(revstr, rfp)) == NULL) 3563 return (NULL); 3564 3565 if ((rdp = rcs_findrev(rfp, rev)) == NULL) 3566 return (NULL); 3567 3568 /* let's see if we must follow a branch */ 3569 if (!strcmp(revstr, RCS_HEAD_BRANCH)) 3570 follow = 1; 3571 else { 3572 frev = rcs_sym_getrev(rfp, revstr); 3573 if (frev == NULL) 3574 frev = rrev = rcsnum_parse(revstr); 3575 3576 brev = rcsnum_alloc(); 3577 rcsnum_cpy(rev, brev, rev->rn_len - 1); 3578 3579 if (frev != NULL && RCSNUM_ISBRANCH(frev) && 3580 !rcsnum_cmp(frev, brev, 0)) { 3581 follow = 1; 3582 } else 3583 follow = 0; 3584 3585 rcsnum_free(brev); 3586 if (rrev != NULL) 3587 rcsnum_free(rrev); 3588 } 3589 3590 if (cvs_specified_date != -1) 3591 cdate = cvs_specified_date; 3592 else 3593 cdate = cvs_directory_date; 3594 3595 if (cdate == -1) { 3596 /* XXX */ 3597 if (rev->rn_len < 4 || !follow) { 3598 return (rev); 3599 } 3600 3601 /* Find the latest delta on that branch */ 3602 rcsnum_free(rev); 3603 for (;;) { 3604 if (rdp->rd_next->rn_len == 0) 3605 break; 3606 if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL) 3607 fatal("rcs_translate_tag: could not fetch " 3608 "branch delta"); 3609 } 3610 3611 rev = rcsnum_alloc(); 3612 rcsnum_cpy(rdp->rd_num, rev, 0); 3613 return (rev); 3614 } 3615 3616 if (frev != NULL) 3617 rcsnum_tostr(frev, branch, sizeof(branch)); 3618 3619 if (frev != NULL) { 3620 brev = rcsnum_revtobr(frev); 3621 brev->rn_len = rev->rn_len - 1; 3622 } 3623 3624 rcsnum_free(rev); 3625 3626 do { 3627 deltatime = timelocal(&(rdp->rd_date)); 3628 3629 if (RCSNUM_ISBRANCHREV(rdp->rd_num)) { 3630 if (deltatime > cdate) { 3631 trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list); 3632 if (trdp == NULL) 3633 trdp = rdp; 3634 3635 if (trdp->rd_num->rn_len != rdp->rd_num->rn_len) 3636 return (NULL); 3637 3638 rev = rcsnum_alloc(); 3639 rcsnum_cpy(trdp->rd_num, rev, 0); 3640 return (rev); 3641 } 3642 3643 if (rdp->rd_next->rn_len == 0) { 3644 rev = rcsnum_alloc(); 3645 rcsnum_cpy(rdp->rd_num, rev, 0); 3646 return (rev); 3647 } 3648 } else { 3649 if (deltatime < cdate) { 3650 rev = rcsnum_alloc(); 3651 rcsnum_cpy(rdp->rd_num, rev, 0); 3652 return (rev); 3653 } 3654 } 3655 3656 if (follow && rdp->rd_next->rn_len != 0) { 3657 if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0)) 3658 break; 3659 3660 trdp = rcs_findrev(rfp, rdp->rd_next); 3661 if (trdp == NULL) 3662 fatal("failed to grab next revision"); 3663 rdp = trdp; 3664 } else 3665 follow = 0; 3666 } while (follow); 3667 3668 return (NULL); 3669 } 3670