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