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