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