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