1/*- 2 * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <assert.h> 30#include <err.h> 31#include <errno.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35 36#include "diff.h" 37#include "keyword.h" 38#include "misc.h" 39#include "proto.h" 40#include "queue.h" 41#include "rcsfile.h" 42#include "rcsparse.h" 43#include "stream.h" 44 45#define BUF_SIZE_DEFAULT 128 46 47/* 48 * RCS parser library. This is the part of the library that handles the 49 * importing, editing and exporting of RCS files. It currently supports only the 50 * part of the RCS file specification that is needed for csup (for instance, 51 * newphrases are not supported), and assumes that you can store the whole RCS 52 * file in memory. 53 */ 54 55/* 56 * Linked list for string tokens. 57 */ 58struct string { 59 char *str; 60 STAILQ_ENTRY(string) string_next; 61}; 62 63/* 64 * Linked list of tags and revision numbers, in the RCS file header. 65 */ 66struct tag { 67 char *tag; 68 char *revnum; 69 STAILQ_ENTRY(tag) tag_next; 70}; 71 72/* 73 * A RCS delta. The delta is identified by a revision number, and contains the 74 * most important RCS attributes that is needed by csup. It also contains 75 * pointers to other nodes in the RCS file delta structure. 76 */ 77struct delta { 78 char *revdate; 79 char *revnum; 80 char *author; 81 char *state; 82 struct buf *log; 83 struct buf *text; 84 int placeholder; 85 struct delta *diffbase; 86 struct delta *prev; 87 88 LIST_ENTRY(delta) delta_next; 89 STAILQ_ENTRY(delta) delta_prev; 90 LIST_ENTRY(delta) table_next; 91 STAILQ_ENTRY(delta) stack_next; 92 LIST_HEAD(, branch) branchlist; 93 LIST_ENTRY(delta) branch_next_date; 94}; 95 96/* 97 * A branch data structure containing information about deltas in the branch as 98 * well as a base revision number. 99 */ 100struct branch { 101 char *revnum; 102 LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */ 103 LIST_ENTRY(branch) branch_next; 104}; 105 106/* 107 * The rcsfile structure is the "main" structure of the RCS parser library. It 108 * contains administrative data as well as pointers to the deltas within the 109 * file. 110 */ 111struct rcsfile { 112 char *name; 113 char *head; 114 char *branch; /* Default branch. */ 115 char *cvsroot; 116 char *colltag; 117 STAILQ_HEAD(, string) accesslist; 118 STAILQ_HEAD(, tag) taglist; 119 int strictlock; 120 char *comment; 121 int expand; 122 int ro; 123 struct branch *trunk; /* The tip delta. */ 124 125 LIST_HEAD(, delta) deltatable; 126 127 char *desc; 128}; 129 130static void rcsfile_freedelta(struct delta *); 131static void rcsfile_insertdelta(struct branch *, struct delta *, 132 int); 133static struct delta *rcsfile_createdelta(char *); 134static int rcsfile_write_deltatext(struct rcsfile *, 135 struct stream *); 136static int rcsfile_puttext(struct rcsfile *, struct stream *, 137 struct delta *, struct delta *); 138static struct branch *rcsfile_getbranch(struct rcsfile *, char *); 139static void rcsfile_insertsorteddelta(struct rcsfile *, 140 struct delta *); 141static struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *, 142 struct buf **); 143static int rcsdelta_writestring(char *, size_t, struct stream *); 144static void rcsdelta_insertbranch(struct delta *, struct branch *); 145 146/* Space formatting of RCS file. */ 147const char *head_space = "\t"; 148const char *branch_space = "\t"; 149const char *tag_space = "\t"; 150const char *date_space = "\t"; 151const char *auth_space = "\t"; 152const char *state_space = "\t"; 153const char *next_space = "\t"; 154const char *branches_space = "\t"; 155const char *comment_space ="\t"; 156const char *expand_space = "\t"; 157 158void print_stream(struct stream *); 159 160/* Print the contents of a stream, for debugging. */ 161void 162print_stream(struct stream *s) 163{ 164 char *line; 165 166 line = stream_getln(s, NULL); 167 while (line != NULL) { 168 lprintf(-1, "%s\n", line); 169 line = stream_getln(s, NULL); 170 } 171 lprintf(-1, "\n"); 172} 173 174/* 175 * Parse rcsfile from path and return a pointer to it. 176 */ 177struct rcsfile * 178rcsfile_frompath(const char *path, const char *name, const char *cvsroot, 179 const char *colltag, int ro) 180{ 181 struct rcsfile *rf; 182 FILE *infp; 183 int error; 184 185 if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL) 186 return (NULL); 187 188 rf = xmalloc(sizeof(struct rcsfile)); 189 rf->name = xstrdup(name); 190 rf->cvsroot = xstrdup(cvsroot); 191 rf->colltag = xstrdup(colltag); 192 193 /* Initialize head branch. */ 194 rf->trunk = xmalloc(sizeof(struct branch)); 195 rf->trunk->revnum = xstrdup("1"); 196 LIST_INIT(&rf->trunk->deltalist); 197 /* Initialize delta list. */ 198 LIST_INIT(&rf->deltatable); 199 /* Initialize tag list. */ 200 STAILQ_INIT(&rf->taglist); 201 /* Initialize accesslist. */ 202 STAILQ_INIT(&rf->accesslist); 203 204 /* Initialize all fields. */ 205 rf->head = NULL; 206 rf->branch = NULL; 207 rf->strictlock = 0; 208 rf->comment = NULL; 209 rf->expand = EXPAND_DEFAULT; 210 rf->desc = NULL; 211 rf->ro = ro; 212 213 infp = fopen(path, "r"); 214 if (infp == NULL) { 215 lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno)); 216 rcsfile_free(rf); 217 return (NULL); 218 } 219 error = rcsparse_run(rf, infp, ro); 220 fclose(infp); 221 if (error) { 222 lprintf(-1, "Error parsing \"%s\"\n", name); 223 rcsfile_free(rf); 224 return (NULL); 225 } 226 return (rf); 227} 228 229/* 230 * Write content of rcsfile to server. Assumes we have a complete RCS file 231 * loaded. 232 */ 233int 234rcsfile_send_details(struct rcsfile *rf, struct stream *wr) 235{ 236 struct delta *d; 237 struct tag *t; 238 const char *keyword; 239 int error; 240 241 assert(rf != NULL); 242 243 error = proto_printf(wr, "V %s\n", rf->name); 244 if (error) 245 return(error); 246 247 /* Write default branch. */ 248 if (rf->branch == NULL) 249 error = proto_printf(wr, "b\n"); 250 else 251 error = proto_printf(wr, "B %s\n", rf->branch); 252 if (error) 253 return(error); 254 255 /* Write deltas to server. */ 256 error = proto_printf(wr, "D\n"); 257 if (error) 258 return(error); 259 260 LIST_FOREACH(d, &rf->deltatable, table_next) { 261 error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate); 262 if (error) 263 return(error); 264 } 265 error = proto_printf(wr, ".\n"); 266 267 if (error) 268 return(error); 269 /* Write expand. */ 270 if (rf->expand != EXPAND_DEFAULT) { 271 keyword = keyword_encode_expand(rf->expand); 272 if (keyword != NULL) { 273 error = proto_printf(wr, "E %s\n", 274 keyword_encode_expand(rf->expand)); 275 if (error) 276 return(error); 277 } 278 } 279 280 /* Write tags to server. */ 281 error = proto_printf(wr, "T\n"); 282 if (error) 283 return(error); 284 STAILQ_FOREACH(t, &rf->taglist, tag_next) { 285 error = proto_printf(wr, "%s %s\n", t->tag, t->revnum); 286 if (error) 287 return(error); 288 } 289 error = proto_printf(wr, ".\n"); 290 if (error) 291 return(error); 292 error = proto_printf(wr, ".\n"); 293 return (error); 294} 295 296/* 297 * Write a RCS file to disk represented by the destination stream. Keep track of 298 * deltas with a stack and an inverted stack. 299 */ 300int 301rcsfile_write(struct rcsfile *rf, struct stream *dest) 302{ 303 STAILQ_HEAD(, delta) deltastack; 304 STAILQ_HEAD(, delta) deltalist_inverted; 305 struct tag *t; 306 struct branch *b; 307 struct delta *d, *d_tmp, *d_next; 308 int error; 309 310 /* First write head. */ 311 d = LIST_FIRST(&rf->trunk->deltalist); 312 if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0) 313 return (-1); 314 315 /* Write branch, if we have. */ 316 if (rf->branch != NULL) { 317 if (stream_printf(dest, "branch%s%s;\n", branch_space, 318 rf->branch) < 0) 319 return (-1); 320 } 321 322 /* Write access. */ 323 if (stream_printf(dest, "access") < 0) 324 return (-1); 325#if 0 326 if (!STAILQ_EMPTY(&rf->accesslist)) { 327 /* 328 * XXX: Write out access. This doesn't seem to be necessary for 329 * the time being. 330 */ 331 } 332#endif 333 if (stream_printf(dest, ";\n") < 0) 334 return (-1); 335 336 /* Write out taglist. */ 337 if (stream_printf(dest, "symbols") < 0) 338 return (-1); 339 if (!STAILQ_EMPTY(&rf->taglist)) { 340 STAILQ_FOREACH(t, &rf->taglist, tag_next) { 341 if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag, 342 t->revnum) < 0) 343 return (-1); 344 } 345 } 346 347 /* Write out locks and strict. */ 348 if (stream_printf(dest, ";\nlocks;") < 0) 349 return (-1); 350 if (rf->strictlock) { 351 if (stream_printf(dest, " strict;") < 0) 352 return (-1); 353 } 354 if (stream_printf(dest, "\n") < 0) 355 return (-1); 356 357 /* Write out the comment. */ 358 if (rf->comment != NULL) { 359 if (stream_printf(dest, "comment%s%s;\n", comment_space, 360 rf->comment) < 0) 361 return (-1); 362 } 363 if (rf->expand != EXPAND_DEFAULT) { 364 if (stream_printf(dest, "expand%s@%s@;\n", expand_space, 365 keyword_encode_expand(rf->expand)) < 0) 366 return (-1); 367 } 368 369 if (stream_printf(dest, "\n\n") < 0) 370 return (-1); 371 372 /* 373 * Write out deltas. We use a stack where we push the appropriate deltas 374 * that is to be written out during the loop. 375 */ 376 STAILQ_INIT(&deltastack); 377 d = LIST_FIRST(&rf->trunk->deltalist); 378 STAILQ_INSERT_HEAD(&deltastack, d, stack_next); 379 while (!STAILQ_EMPTY(&deltastack)) { 380 d = STAILQ_FIRST(&deltastack); 381 STAILQ_REMOVE_HEAD(&deltastack, stack_next); 382 /* Do not write out placeholders just to be safe. */ 383 if (d->placeholder) 384 continue; 385 if (stream_printf(dest, "%s\n", d->revnum) < 0) 386 return (-1); 387 if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate", 388 date_space, d->revdate, auth_space, d->author, 389 state_space) < 0) 390 return (-1); 391 if (d->state != NULL) { 392 if (stream_printf(dest, " %s", d->state) < 0) 393 return (-1); 394 } 395 if (stream_printf(dest, ";\nbranches") < 0) 396 return (-1); 397 /* 398 * Write out our branches. Add them to a reversed list for use 399 * later when we write out the text. 400 */ 401 STAILQ_INIT(&deltalist_inverted); 402 LIST_FOREACH(b, &d->branchlist, branch_next) { 403 d_tmp = LIST_FIRST(&b->deltalist); 404 STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev); 405 STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); 406 } 407 408 /* Push branch heads on stack. */ 409 STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) { 410 if (d_tmp == NULL) { 411 lprintf(2, "Empty branch!\n"); 412 return (-1); 413 } 414 if (stream_printf(dest, "\n%s%s", branches_space, 415 d_tmp->revnum) < 0) 416 return (-1); 417 } 418 419 if (stream_printf(dest, ";\nnext%s", next_space) < 0) 420 return (-1); 421 /* Push next delta on stack. */ 422 d_next = LIST_NEXT(d, delta_next); 423 if (d_next != NULL) { 424 if (stream_printf(dest, "%s", d_next->revnum) < 0) 425 return (-1); 426 STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next); 427 } 428 if (stream_printf(dest, ";\n\n") < 0) 429 return (-1); 430 } 431 /* Write out desc. */ 432 if (stream_printf(dest, "\ndesc\n@@") < 0) 433 return (-1); 434 d = LIST_FIRST(&rf->trunk->deltalist); 435 436 /* Write out deltatexts. */ 437 error = rcsfile_write_deltatext(rf, dest); 438 if (stream_printf(dest, "\n") < 0) 439 return (-1); 440 return (error); 441} 442 443/* 444 * Write out deltatexts of a delta and it's subbranches recursively. 445 */ 446int 447rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest) 448{ 449 STAILQ_HEAD(, delta) deltastack; 450 LIST_HEAD(, delta) branchlist_datesorted; 451 struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3; 452 struct stream *in; 453 struct branch *b; 454 size_t size; 455 char *line; 456 int error; 457 458 error = 0; 459 STAILQ_INIT(&deltastack); 460 d = LIST_FIRST(&rf->trunk->deltalist); 461 d->prev = NULL; 462 STAILQ_INSERT_HEAD(&deltastack, d, stack_next); 463 while (!STAILQ_EMPTY(&deltastack)) { 464 d = STAILQ_FIRST(&deltastack); 465 STAILQ_REMOVE_HEAD(&deltastack, stack_next); 466 /* Do not write out placeholders just to be safe. */ 467 if (d->placeholder) 468 return (0); 469 if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0) 470 return (-1); 471 if (stream_printf(dest, "log\n@") < 0) 472 return (-1); 473 in = stream_open_buf(d->log); 474 line = stream_getln(in, &size); 475 while (line != NULL) { 476 if (stream_write(dest, line, size) == -1) 477 return (-1); 478 line = stream_getln(in, &size); 479 } 480 stream_close(in); 481 if (stream_printf(dest, "@\ntext\n@") < 0) 482 return (-1); 483 error = rcsfile_puttext(rf, dest, d, d->prev); 484 if (error) 485 return (error); 486 if (stream_printf(dest, "@") < 0) 487 return (-1); 488 489 LIST_INIT(&branchlist_datesorted); 490 d_next = LIST_NEXT(d, delta_next); 491 if (d_next != NULL) { 492 d_next->prev = d; 493 /* 494 * If it's trunk, treat it like the oldest, if not treat 495 * it like a child. 496 */ 497 if (rcsrev_istrunk(d_next->revnum)) 498 STAILQ_INSERT_HEAD(&deltastack, d_next, 499 stack_next); 500 else 501 LIST_INSERT_HEAD(&branchlist_datesorted, d_next, 502 branch_next_date); 503 } 504 505 /* 506 * First, we need to sort our branches based on their date to 507 * take into account some self-hacked RCS files. 508 */ 509 LIST_FOREACH(b, &d->branchlist, branch_next) { 510 d_tmp = LIST_FIRST(&b->deltalist); 511 if (LIST_EMPTY(&branchlist_datesorted)) { 512 LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp, 513 branch_next_date); 514 continue; 515 } 516 517 d_tmp2 = LIST_FIRST(&branchlist_datesorted); 518 if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) { 519 LIST_INSERT_BEFORE(d_tmp2, d_tmp, 520 branch_next_date); 521 continue; 522 } 523 while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date)) 524 != NULL) { 525 if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate) 526 <= 0) 527 break; 528 d_tmp2 = d_tmp3; 529 } 530 LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date); 531 } 532 /* 533 * Invert the deltalist of a branch, since we're writing them 534 * the opposite way. 535 */ 536 LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) { 537 d_tmp->prev = d; 538 STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); 539 } 540 } 541 return (0); 542} 543 544/* 545 * Generates text given a delta and a diffbase. 546 */ 547static int 548rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d, 549 struct delta *diffbase) 550{ 551 struct stream *in, *rd, *orig; 552 struct keyword *k; 553 struct diffinfo dibuf, *di; 554 struct buf *b; 555 size_t size; 556 char *line; 557 int error; 558 559 di = &dibuf; 560 b = NULL; 561 error = 0; 562 563 /* Write if the diffbase is the previous */ 564 if (d->diffbase == diffbase) { 565 566 /* Write out the text. */ 567 in = stream_open_buf(d->text); 568 line = stream_getln(in, &size); 569 while (line != NULL) { 570 if (stream_write(dest, line, size) == -1) { 571 error = -1; 572 goto cleanup; 573 } 574 line = stream_getln(in, &size); 575 } 576 stream_close(in); 577 /* We need to apply diff to produce text, this is probably HEAD. */ 578 } else if (diffbase == NULL) { 579 /* Apply diff. */ 580 orig = rcsfile_getdeltatext(rf, d, &b); 581 if (orig == NULL) { 582 error = -1; 583 goto cleanup; 584 } 585 line = stream_getln(orig, &size); 586 while (line != NULL) { 587 if (stream_write(dest, line, size) == -1) { 588 error = -1; 589 goto cleanup; 590 } 591 line = stream_getln(orig, &size); 592 } 593 stream_close(orig); 594 /* 595 * A new head was probably added, and now the previous HEAD must be 596 * changed to include the diff instead. 597 */ 598 } else if (diffbase->diffbase == d) { 599 /* Get reverse diff. */ 600 orig = rcsfile_getdeltatext(rf, d, &b); 601 if (orig == NULL) { 602 error = -1; 603 goto cleanup; 604 } 605 di->di_rcsfile = rf->name; 606 di->di_cvsroot = rf->cvsroot; 607 di->di_revnum = d->revnum; 608 di->di_revdate = d->revdate; 609 di->di_author = d->author; 610 di->di_tag = rf->colltag; 611 di->di_state = d->state; 612 di->di_expand = EXPAND_OLD; 613 k = keyword_new(); 614 615 rd = stream_open_buf(diffbase->text); 616 error = diff_reverse(rd, orig, dest, k, di); 617 if (error) { 618 lprintf(-1, "Error applying reverse diff: %d\n", error); 619 goto cleanup; 620 } 621 keyword_free(k); 622 stream_close(rd); 623 stream_close(orig); 624 } 625cleanup: 626 if (b != NULL) 627 buf_free(b); 628 return (error); 629} 630 631/* 632 * Return a stream with an applied diff of a delta. 633 * XXX: extra overhead on the last apply. Could write directly to file, but 634 * makes things complicated though. 635 */ 636static struct stream * 637rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest) 638{ 639 struct diffinfo dibuf, *di; 640 struct stream *orig, *dest, *rd; 641 struct buf *buf_orig; 642 struct keyword *k; 643 int error; 644 645 buf_orig = NULL; 646 error = 0; 647 648 /* 649 * If diffbase is NULL or we are head (the old head), we have a normal 650 * complete deltatext. 651 */ 652 if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) { 653 orig = stream_open_buf(d->text); 654 return (orig); 655 } 656 657 di = &dibuf; 658 /* If not, we need to apply our diff to that of our diffbase. */ 659 orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig); 660 if (orig == NULL) 661 return (NULL); 662 663 /* 664 * Now that we are sure we have a complete deltatext in ret, let's apply 665 * our diff to it. 666 */ 667 *buf_dest = buf_new(BUF_SIZE_DEFAULT); 668 dest = stream_open_buf(*buf_dest); 669 670 di->di_rcsfile = rf->name; 671 di->di_cvsroot = rf->cvsroot; 672 di->di_revnum = d->revnum; 673 di->di_revdate = d->revdate; 674 di->di_author = d->author; 675 di->di_tag = rf->colltag; 676 di->di_state = d->state; 677 di->di_expand = EXPAND_OLD; 678 rd = stream_open_buf(d->text); 679 k = keyword_new(); 680 error = diff_apply(rd, orig, dest, k, di, 0); 681 stream_flush(dest); 682 stream_close(rd); 683 stream_close(orig); 684 stream_close(dest); 685 keyword_free(k); 686 if (buf_orig != NULL) 687 buf_free(buf_orig); 688 if (error) { 689 lprintf(-1, "Error applying diff: %d\n", error); 690 return (NULL); 691 } 692 693 /* Now reopen the stream for the reading. */ 694 dest = stream_open_buf(*buf_dest); 695 return (dest); 696} 697 698/* Print content of rcsfile. Useful for debugging. */ 699void 700rcsfile_print(struct rcsfile *rf) 701{ 702 struct delta *d; 703 struct tag *t; 704 struct string *s; 705 struct stream *in; 706 char *line; 707 708 lprintf(1, "\n"); 709 if (rf->name != NULL) 710 lprintf(1, "name: '%s'\n", rf->name); 711 if (rf->head != NULL) 712 lprintf(1, "head: '%s'\n", rf->head); 713 if (rf->branch != NULL) 714 lprintf(1, "branch: '%s'\n", rf->branch); 715 lprintf(1, "Access: "); 716 STAILQ_FOREACH(s, &rf->accesslist, string_next) 717 lprintf(1, "'%s' ", s->str); 718 lprintf(1, "\n"); 719 720 /* Print all tags. */ 721 STAILQ_FOREACH(t, &rf->taglist, tag_next) { 722 lprintf(1, "Tag: "); 723 if (t->tag != NULL) 724 lprintf(1, "name: %s ", t->tag); 725 if (t->revnum != NULL) 726 lprintf(1, "rev: %s", t->revnum); 727 lprintf(1, "\n"); 728 } 729 730 if (rf->strictlock) 731 lprintf(1, "Strict!\n"); 732 if (rf->comment != NULL) 733 lprintf(1, "comment: '%s'\n", rf->comment); 734 if (rf->expand != EXPAND_DEFAULT) 735 lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand)); 736 737 /* Print all deltas. */ 738 LIST_FOREACH(d, &rf->deltatable, table_next) { 739 lprintf(1, "Delta: "); 740 if (d->revdate != NULL) 741 lprintf(1, "date: %s ", d->revdate); 742 if (d->revnum != NULL) 743 lprintf(1, "rev: %s", d->revnum); 744 if (d->author != NULL) 745 lprintf(1, "author: %s", d->author); 746 if (d->state != NULL) 747 lprintf(1, "state: %s", d->state); 748 749 lprintf(1, "Text:\n"); 750 in = stream_open_buf(d->text); 751 line = stream_getln(in, NULL); 752 while (line != NULL) { 753 lprintf(1, "TEXT: %s\n", line); 754 line = stream_getln(in, NULL); 755 } 756 stream_close(in); 757 lprintf(1, "\n"); 758 } 759 760 if (rf->desc != NULL) 761 lprintf(1, "desc: '%s'\n", rf->desc); 762} 763 764/* Free all memory associated with a struct rcsfile. */ 765void 766rcsfile_free(struct rcsfile *rf) 767{ 768 struct delta *d; 769 struct tag *t; 770 struct string *s; 771 772 if (rf->name != NULL) 773 free(rf->name); 774 if (rf->head != NULL) 775 free(rf->head); 776 if (rf->branch != NULL) 777 free(rf->branch); 778 if (rf->cvsroot != NULL) 779 free(rf->cvsroot); 780 if (rf->colltag != NULL) 781 free(rf->colltag); 782 783 /* Free all access ids. */ 784 while (!STAILQ_EMPTY(&rf->accesslist)) { 785 s = STAILQ_FIRST(&rf->accesslist); 786 STAILQ_REMOVE_HEAD(&rf->accesslist, string_next); 787 if (s->str != NULL) 788 free(s->str); 789 free(s); 790 } 791 792 /* Free all tags. */ 793 while (!STAILQ_EMPTY(&rf->taglist)) { 794 t = STAILQ_FIRST(&rf->taglist); 795 STAILQ_REMOVE_HEAD(&rf->taglist, tag_next); 796 if (t->tag != NULL) 797 free(t->tag); 798 if (t->revnum != NULL) 799 free(t->revnum); 800 free(t); 801 } 802 803 if (rf->comment != NULL) 804 free(rf->comment); 805 806 /* Free all deltas in global list */ 807 while (!LIST_EMPTY(&rf->deltatable)) { 808 d = LIST_FIRST(&rf->deltatable); 809 if (!rf->ro) 810 LIST_REMOVE(d, delta_next); 811 LIST_REMOVE(d, table_next); 812 rcsfile_freedelta(d); 813 } 814 815 /* Free global branch. */ 816 if (rf->trunk->revnum != NULL) 817 free(rf->trunk->revnum); 818 free(rf->trunk); 819 820 if (rf->desc != NULL) 821 free(rf->desc); 822 823 free(rf); 824} 825 826/* 827 * Free a RCS delta. 828 */ 829static void 830rcsfile_freedelta(struct delta *d) 831{ 832 struct branch *b; 833 834 if (d->revdate != NULL) 835 free(d->revdate); 836 if (d->revnum != NULL) 837 free(d->revnum); 838 if (d->author != NULL) 839 free(d->author); 840 if (d->state != NULL) 841 free(d->state); 842 if (d->log != NULL) 843 buf_free(d->log); 844 if (d->text != NULL) 845 buf_free(d->text); 846 847 /* Free all subbranches of a delta. */ 848 while (!LIST_EMPTY(&d->branchlist)) { 849 b = LIST_FIRST(&d->branchlist); 850 LIST_REMOVE(b, branch_next); 851 free(b->revnum); 852 free(b); 853 } 854 free(d); 855} 856 857/* 858 * Functions for editing RCS deltas. 859 */ 860 861/* Add a new entry to the access list. */ 862void 863rcsfile_addaccess(struct rcsfile *rf, char *id) 864{ 865 struct string *s; 866 867 s = xmalloc(sizeof(struct string)); 868 s->str = xstrdup(id); 869 STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next); 870} 871 872/* Add a tag to a RCS file. */ 873void 874rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum) 875{ 876 struct tag *t; 877 878 t = xmalloc(sizeof(struct tag)); 879 t->tag = xstrdup(tag); 880 t->revnum = xstrdup(revnum); 881 882 STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next); 883} 884 885/* Import a tag to a RCS file. */ 886void 887rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum) 888{ 889 struct tag *t; 890 891 t = xmalloc(sizeof(struct tag)); 892 t->tag = xstrdup(tag); 893 t->revnum = xstrdup(revnum); 894 895 STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next); 896} 897 898/* 899 * Delete a revision from the global delta list and the branch it is in. Csup 900 * will tell us to delete the tags involved. 901 */ 902void 903rcsfile_deleterev(struct rcsfile *rf, char *revname) 904{ 905 struct delta *d; 906 907 d = rcsfile_getdelta(rf, revname); 908 if (!rf->ro) 909 LIST_REMOVE(d, delta_next); 910 LIST_REMOVE(d, table_next); 911 rcsfile_freedelta(d); 912} 913 914/* Delete a tag from the tag list. */ 915void 916rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum) 917{ 918 struct tag *t; 919 920 STAILQ_FOREACH(t, &rf->taglist, tag_next) { 921 if ((strcmp(tag, t->tag) == 0) && 922 (strcmp(revnum, t->revnum) == 0)) { 923 STAILQ_REMOVE(&rf->taglist, t, tag, tag_next); 924 free(t->tag); 925 free(t->revnum); 926 free(t); 927 return; 928 } 929 } 930} 931 932/* 933 * Searches the global deltalist for a delta. 934 */ 935struct delta * 936rcsfile_getdelta(struct rcsfile *rf, char *revnum) 937{ 938 struct delta *d; 939 940 LIST_FOREACH(d, &rf->deltatable, table_next) { 941 if (strcmp(revnum, d->revnum) == 0) 942 return (d); 943 } 944 return (NULL); 945} 946 947/* Set rcsfile head. */ 948void 949rcsfile_setval(struct rcsfile *rf, int field, char *val) 950{ 951 size_t len; 952 953 switch (field) { 954 case RCSFILE_HEAD: 955 if (rf->head != NULL) 956 free(rf->head); 957 rf->head = xstrdup(val); 958 break; 959 case RCSFILE_BRANCH: 960 if (rf->branch != NULL) 961 free(rf->branch); 962 rf->branch = (val == NULL) ? NULL : xstrdup(val); 963 break; 964 case RCSFILE_STRICT: 965 if (val != NULL) 966 rf->strictlock = 1; 967 break; 968 case RCSFILE_COMMENT: 969 if (rf->comment != NULL) 970 free(rf->comment); 971 rf->comment = xstrdup(val); 972 break; 973 case RCSFILE_EXPAND: 974 len = strlen(val) - 1; 975 val++; 976 val[len - 1] = '\0'; 977 rf->expand = keyword_decode_expand(val); 978 break; 979 case RCSFILE_DESC: 980 if (rf->desc != NULL) 981 free(rf->desc); 982 rf->desc = xstrdup(val); 983 break; 984 default: 985 lprintf(-1, "Setting invalid RCSfile value.\n"); 986 break; 987 } 988} 989 990/* Create and initialize a delta. */ 991static struct delta * 992rcsfile_createdelta(char *revnum) 993{ 994 struct delta *d; 995 996 d = xmalloc(sizeof(struct delta)); 997 d->revnum = xstrdup(revnum); 998 d->revdate = NULL; 999 d->state = NULL; 1000 d->author = NULL; 1001 d->log = buf_new(BUF_SIZE_DEFAULT); 1002 d->text = buf_new(BUF_SIZE_DEFAULT); 1003 d->diffbase = NULL; 1004 1005 LIST_INIT(&d->branchlist); 1006 return (d); 1007} 1008 1009/* Add a delta to a imported delta tree. Used by the updater. */ 1010struct delta * 1011rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, 1012 char *diffbase) 1013{ 1014 struct branch *b; 1015 struct delta *d, *d_bp, *d_next; 1016 char *brev, *bprev; 1017 int trunk; 1018 1019 d_next = NULL; 1020 d = rcsfile_getdelta(rf, revnum); 1021 if (d != NULL) { 1022 lprintf(-1, "Delta %s already exists!\n", revnum); 1023 return (NULL); 1024 } 1025 d = rcsfile_createdelta(revnum); 1026 d->placeholder = 0; 1027 d->revdate = xstrdup(revdate); 1028 d->author = xstrdup(author); 1029 d->diffbase = rcsfile_getdelta(rf, diffbase); 1030 1031 /* If it's trunk, insert it in the head branch list. */ 1032 b = rcsrev_istrunk(d->revnum) ? rf->trunk : 1033 rcsfile_getbranch(rf, d->revnum); 1034 1035 /* 1036 * We didn't find a branch, check if we can find a branchpoint and 1037 * create a branch there. 1038 */ 1039 if (b == NULL) { 1040 brev = rcsrev_prefix(d->revnum); 1041 bprev = rcsrev_prefix(brev); 1042 1043 d_bp = rcsfile_getdelta(rf, bprev); 1044 free(bprev); 1045 if (d_bp == NULL) { 1046 lprintf(-1, "No branch point for adding delta %s\n", 1047 d->revnum); 1048 return (NULL); 1049 } 1050 1051 /* Create the branch and insert in delta. */ 1052 b = xmalloc(sizeof(struct branch)); 1053 b->revnum = brev; 1054 LIST_INIT(&b->deltalist); 1055 rcsdelta_insertbranch(d_bp, b); 1056 } 1057 1058 /* Insert both into the tree, and into the lookup list. */ 1059 trunk = rcsrev_istrunk(d->revnum); 1060 rcsfile_insertdelta(b, d, trunk); 1061 rcsfile_insertsorteddelta(rf, d); 1062 return (d); 1063} 1064 1065/* Adds a delta to a rcsfile struct. Used by the parser. */ 1066void 1067rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, 1068 char *state, char *next) 1069{ 1070 struct branch *b; 1071 struct delta *d, *d_bp, *d_next; 1072 char *brev, *bprev; 1073 int trunk; 1074 1075 d_next = NULL; 1076 d = rcsfile_getdelta(rf, revnum); 1077 1078 if (d == NULL) { 1079 /* If not, we'll just create a new entry. */ 1080 d = rcsfile_createdelta(revnum); 1081 d->placeholder = 0; 1082 } else { 1083 if (d->placeholder == 0) { 1084 lprintf(-1, "Trying to import already existing delta\n"); 1085 return; 1086 } 1087 } 1088 /* 1089 * If already exists, assume that only revnum is filled out, and set the 1090 * rest of the fields. This should be an OK assumption given that we can 1091 * be sure internally that the structure is sufficiently initialized so 1092 * we won't have any unfreed memory. 1093 */ 1094 d->revdate = xstrdup(revdate); 1095 d->author = xstrdup(author); 1096 if (state != NULL) 1097 d->state = xstrdup(state); 1098 1099 /* If we have a next, create a placeholder for it. */ 1100 if (next != NULL) { 1101 d_next = rcsfile_createdelta(next); 1102 d_next->placeholder = 1; 1103 /* Diffbase should be the previous. */ 1104 d_next->diffbase = d; 1105 } 1106 1107 /* If we're opening read-only, do minimal work. */ 1108 if (rf->ro) { 1109 if (!d->placeholder) 1110 rcsfile_insertsorteddelta(rf, d); 1111 else 1112 d->placeholder = 0; 1113 if (d_next != NULL) 1114 rcsfile_insertsorteddelta(rf, d_next); 1115 return; 1116 } 1117 1118 /* If it's trunk, insert it in the head branch list. */ 1119 b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf, 1120 d->revnum); 1121 1122 /* 1123 * We didn't find a branch, check if we can find a branchpoint and 1124 * create a branch there. 1125 */ 1126 if (b == NULL) { 1127 brev = rcsrev_prefix(d->revnum); 1128 bprev = rcsrev_prefix(brev); 1129 1130 d_bp = rcsfile_getdelta(rf, bprev); 1131 free(bprev); 1132 if (d_bp == NULL) { 1133 lprintf(-1, "No branch point for adding delta %s\n", 1134 d->revnum); 1135 return; 1136 } 1137 1138 /* Create the branch and insert in delta. */ 1139 b = xmalloc(sizeof(struct branch)); 1140 b->revnum = brev; 1141 LIST_INIT(&b->deltalist); 1142 rcsdelta_insertbranch(d_bp, b); 1143 } 1144 1145 /* Insert if not a placeholder. */ 1146 if (!d->placeholder) { 1147 /* Insert both into the tree, and into the lookup list. */ 1148 if (rcsrev_istrunk(d->revnum)) 1149 rcsfile_insertdelta(b, d, 1); 1150 else { 1151 rcsfile_insertdelta(b, d, 0); 1152 /* 1153 * On import we need to set the diffbase to our 1154 * branchpoint for writing out later. 1155 */ 1156 if (LIST_FIRST(&b->deltalist) == d) { 1157 brev = rcsrev_prefix(d->revnum); 1158 bprev = rcsrev_prefix(brev); 1159 d_bp = rcsfile_getdelta(rf, bprev); 1160 /* This should really not happen. */ 1161 assert(d_bp != NULL); 1162 d->diffbase = d_bp; 1163 free(brev); 1164 free(bprev); 1165 } 1166 } 1167 rcsfile_insertsorteddelta(rf, d); 1168 } else /* Not a placeholder anymore. */ { 1169 d->placeholder = 0; 1170 /* Put it into the tree. */ 1171 trunk = rcsrev_istrunk(d->revnum); 1172 rcsfile_insertdelta(b, d, trunk); 1173 } 1174 1175 /* If we have a next, insert the placeholder into the lookup list. */ 1176 if (d_next != NULL) 1177 rcsfile_insertsorteddelta(rf, d_next); 1178} 1179 1180/* 1181 * Find the branch of a revision number. 1182 */ 1183static struct branch * 1184rcsfile_getbranch(struct rcsfile *rf, char *revnum) 1185{ 1186 struct branch *b; 1187 struct delta *d; 1188 char *branchrev, *bprev; 1189 1190 branchrev = rcsrev_prefix(revnum); 1191 bprev = rcsrev_prefix(branchrev); 1192 d = rcsfile_getdelta(rf, bprev); 1193 free(bprev); 1194 LIST_FOREACH(b, &d->branchlist, branch_next) { 1195 if(rcsnum_cmp(b->revnum, branchrev) == 0) { 1196 free(branchrev); 1197 return (b); 1198 } 1199 } 1200 free(branchrev); 1201 return (NULL); 1202} 1203 1204/* Insert a branch into a delta, sorted by branch revision date. */ 1205static void 1206rcsdelta_insertbranch(struct delta *d, struct branch *b) 1207{ 1208 struct branch *b_iter; 1209 1210 /* If it's empty, insert into head. */ 1211 if (LIST_EMPTY(&d->branchlist)) { 1212 LIST_INSERT_HEAD(&d->branchlist, b, branch_next); 1213 return; 1214 } 1215 1216 /* Just put it in before the revdate that is lower. */ 1217 LIST_FOREACH(b_iter, &d->branchlist, branch_next) { 1218 if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) { 1219 LIST_INSERT_BEFORE(b_iter, b, branch_next); 1220 return; 1221 } 1222 if (LIST_NEXT(b_iter, branch_next) == NULL) 1223 break; 1224 } 1225 /* Insert after last element. */ 1226 LIST_INSERT_AFTER(b_iter, b, branch_next); 1227} 1228 1229/* Insert a delta into the correct place in the table of the rcsfile. */ 1230static void 1231rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d) 1232{ 1233 struct delta *d2; 1234 1235 /* If it's empty, insert into head. */ 1236 if (LIST_EMPTY(&rf->deltatable)) { 1237 LIST_INSERT_HEAD(&rf->deltatable, d, table_next); 1238 return; 1239 } 1240 1241 /* Just put it in before the revdate that is lower. */ 1242 LIST_FOREACH(d2, &rf->deltatable, table_next) { 1243 if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) { 1244 LIST_INSERT_BEFORE(d2, d, table_next); 1245 return; 1246 } 1247 if (LIST_NEXT(d2, table_next) == NULL) 1248 break; 1249 } 1250 /* Insert after last element. */ 1251 LIST_INSERT_AFTER(d2, d, table_next); 1252} 1253 1254/* 1255 * Insert a delta into the correct place in branch. A trunk branch will have 1256 * different ordering scheme and be sorted by revision number, but a normal 1257 * branch will be sorted by date to maintain compatibility with branches that 1258 * is "hand-hacked". 1259 */ 1260static void 1261rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk) 1262{ 1263 struct delta *d2; 1264 1265 /* If it's empty, insert into head. */ 1266 if (LIST_EMPTY(&b->deltalist)) { 1267 LIST_INSERT_HEAD(&b->deltalist, d, delta_next); 1268 return; 1269 } 1270 1271 /* 1272 * Just put it in before the revnum that is lower. Sort trunk branch by 1273 * branchnum but the subbranches after deltadate. 1274 */ 1275 LIST_FOREACH(d2, &b->deltalist, delta_next) { 1276 if (trunk) { 1277 if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) { 1278 LIST_INSERT_BEFORE(d2, d, delta_next); 1279 return; 1280 } 1281 } else { 1282 /* XXX: here we depend on the date being set, but it 1283 * should be before this is called anyway. */ 1284 if (rcsnum_cmp(d->revnum, d2->revnum) < 0) { 1285 LIST_INSERT_BEFORE(d2, d, delta_next); 1286 return; 1287 } 1288 } 1289 if (LIST_NEXT(d2, delta_next) == NULL) 1290 break; 1291 } 1292 /* Insert after last element. */ 1293 LIST_INSERT_AFTER(d2, d, delta_next); 1294} 1295 1296 1297/* Add logtext to a delta. Assume the delta already exists. */ 1298int 1299rcsdelta_addlog(struct delta *d, char *log, int len) 1300{ 1301 struct stream *dest; 1302 int nbytes; 1303 1304 assert(d != NULL); 1305 /* Strip away '@' at beginning and end. */ 1306 log++; 1307 len--; 1308 log[len - 1] = '\0'; 1309 dest = stream_open_buf(d->log); 1310 nbytes = stream_write(dest, log, len - 1); 1311 stream_close(dest); 1312 return ((nbytes == -1) ? -1 : 0); 1313} 1314 1315/* Add deltatext to a delta. Assume the delta already exists. */ 1316int 1317rcsdelta_addtext(struct delta *d, char *text, int len) 1318{ 1319 struct stream *dest; 1320 int nbytes; 1321 1322 assert(d != NULL); 1323 /* Strip away '@' at beginning and end. */ 1324 text++; 1325 len--; 1326 text[len - 1] = '\0'; 1327 1328 dest = stream_open_buf(d->text); 1329 nbytes = stream_write(dest, text, len - 1); 1330 stream_close(dest); 1331 return ((nbytes == -1) ? -1 : 0); 1332} 1333 1334/* Add a deltatext logline to a delta. */ 1335int 1336rcsdelta_appendlog(struct delta *d, char *logline, size_t size) 1337{ 1338 struct stream *dest; 1339 int error; 1340 1341 assert(d != NULL); 1342 dest = stream_open_buf(d->log); 1343 error = rcsdelta_writestring(logline, size, dest); 1344 stream_close(dest); 1345 return (error); 1346} 1347 1348/* Add a deltatext textline to a delta. */ 1349int 1350rcsdelta_appendtext(struct delta *d, char *textline, size_t size) 1351{ 1352 struct stream *dest; 1353 int error; 1354 1355 assert(d != NULL); 1356 dest = stream_open_buf(d->text); 1357 error = rcsdelta_writestring(textline, size, dest); 1358 stream_close(dest); 1359 return (error); 1360} 1361 1362static int 1363rcsdelta_writestring(char *textline, size_t size, struct stream *dest) 1364{ 1365 char buf[3]; 1366 size_t i; 1367 int count; 1368 1369 for (i = 0; i < size; i++) { 1370 buf[0] = textline[i]; 1371 buf[1] = '\0'; 1372 count = 1; 1373 /* Expand @'s */ 1374 if (buf[0] == '@') { 1375 buf[1] = '@'; 1376 buf[2] = '\0'; 1377 count = 2; 1378 } 1379 if (stream_write(dest, buf, count) == -1) 1380 return (-1); 1381 } 1382 return (0); 1383} 1384 1385/* Set delta state. */ 1386void 1387rcsdelta_setstate(struct delta *d, char *state) 1388{ 1389 1390 if (d->state != NULL) 1391 free(state); 1392 if (state != NULL) { 1393 d->state = xstrdup(state); 1394 return; 1395 } 1396 d->state = NULL; 1397} 1398 1399/* Truncate the deltalog with a certain offset. */ 1400void 1401rcsdelta_truncatelog(struct delta *d, off_t offset) 1402{ 1403 1404 stream_truncate_buf(d->log, offset); 1405} 1406 1407/* Truncate the deltatext with a certain offset. */ 1408void 1409rcsdelta_truncatetext(struct delta *d, off_t offset) 1410{ 1411 1412 stream_truncate_buf(d->text, offset); 1413} 1414