2 * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
45 #define BUF_SIZE_DEFAULT 128
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
56 * Linked list for string tokens.
60 STAILQ_ENTRY(string) string_next;
64 * Linked list of tags and revision numbers, in the RCS file header.
69 STAILQ_ENTRY(tag) tag_next;
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.
85 struct delta *diffbase;
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;
97 * A branch data structure containing information about deltas in the branch as
98 * well as a base revision number.
102 LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */
103 LIST_ENTRY(branch) branch_next;
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
114 char *branch; /* Default branch. */
117 STAILQ_HEAD(, string) accesslist;
118 STAILQ_HEAD(, tag) taglist;
123 struct branch *trunk; /* The tip delta. */
125 LIST_HEAD(, delta) deltatable;
130 static void rcsfile_freedelta(struct delta *);
131 static void rcsfile_insertdelta(struct branch *, struct delta *,
133 static struct delta *rcsfile_createdelta(char *);
134 static int rcsfile_write_deltatext(struct rcsfile *,
136 static int rcsfile_puttext(struct rcsfile *, struct stream *,
137 struct delta *, struct delta *);
138 static struct branch *rcsfile_getbranch(struct rcsfile *, char *);
139 static void rcsfile_insertsorteddelta(struct rcsfile *,
141 static struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *,
143 static int rcsdelta_writestring(char *, size_t, struct stream *);
144 static void rcsdelta_insertbranch(struct delta *, struct branch *);
146 /* Space formatting of RCS file. */
147 const char *head_space = "\t";
148 const char *branch_space = "\t";
149 const char *tag_space = "\t";
150 const char *date_space = "\t";
151 const char *auth_space = "\t";
152 const char *state_space = "\t";
153 const char *next_space = "\t";
154 const char *branches_space = "\t";
155 const char *comment_space ="\t";
156 const char *expand_space = "\t";
158 void print_stream(struct stream *);
160 /* Print the contents of a stream, for debugging. */
162 print_stream(struct stream *s)
166 line = stream_getln(s, NULL);
167 while (line != NULL) {
168 lprintf(-1, "%s\n", line);
169 line = stream_getln(s, NULL);
175 * Parse rcsfile from path and return a pointer to it.
178 rcsfile_frompath(char *path, char *name, char *cvsroot, char *colltag, int ro)
184 if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
187 rf = xmalloc(sizeof(struct rcsfile));
188 rf->name = xstrdup(name);
189 rf->cvsroot = xstrdup(cvsroot);
190 rf->colltag = xstrdup(colltag);
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);
203 /* Initialize all fields. */
208 rf->expand = EXPAND_DEFAULT;
212 infp = fopen(path, "r");
214 lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
218 error = rcsparse_run(rf, infp, ro);
221 lprintf(-1, "Error parsing \"%s\"\n", name);
229 * Write content of rcsfile to server. Assumes we have a complete RCS file
233 rcsfile_send_details(struct rcsfile *rf, struct stream *wr)
242 error = proto_printf(wr, "V %s\n", rf->name);
246 /* Write default branch. */
247 if (rf->branch == NULL)
248 error = proto_printf(wr, "b\n");
250 error = proto_printf(wr, "B %s\n", rf->branch);
254 /* Write deltas to server. */
255 error = proto_printf(wr, "D\n");
259 LIST_FOREACH(d, &rf->deltatable, table_next) {
260 error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
264 error = proto_printf(wr, ".\n");
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));
279 /* Write tags to server. */
280 error = proto_printf(wr, "T\n");
283 STAILQ_FOREACH(t, &rf->taglist, tag_next) {
284 error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
288 error = proto_printf(wr, ".\n");
291 error = proto_printf(wr, ".\n");
296 * Write a RCS file to disk represented by the destination stream. Keep track of
297 * deltas with a stack and an inverted stack.
300 rcsfile_write(struct rcsfile *rf, struct stream *dest)
302 STAILQ_HEAD(, delta) deltastack;
303 STAILQ_HEAD(, delta) deltalist_inverted;
306 struct delta *d, *d_tmp, *d_next;
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)
314 /* Write branch, if we have. */
315 if (rf->branch != NULL) {
316 if (stream_printf(dest, "branch%s%s;\n", branch_space,
322 if (stream_printf(dest, "access") < 0)
325 if (!STAILQ_EMPTY(&rf->accesslist)) {
327 * XXX: Write out access. This doesn't seem to be necessary for
332 if (stream_printf(dest, ";\n") < 0)
335 /* Write out taglist. */
336 if (stream_printf(dest, "symbols") < 0)
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,
346 /* Write out locks and strict. */
347 if (stream_printf(dest, ";\nlocks;") < 0)
349 if (rf->strictlock) {
350 if (stream_printf(dest, " strict;") < 0)
353 if (stream_printf(dest, "\n") < 0)
356 /* Write out the comment. */
357 if (rf->comment != NULL) {
358 if (stream_printf(dest, "comment%s%s;\n", comment_space,
362 if (rf->expand != EXPAND_DEFAULT) {
363 if (stream_printf(dest, "expand%s@%s@;\n", expand_space,
364 keyword_encode_expand(rf->expand)) < 0)
368 if (stream_printf(dest, "\n\n") < 0)
372 * Write out deltas. We use a stack where we push the appropriate deltas
373 * that is to be written out during the loop.
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. */
384 if (stream_printf(dest, "%s\n", d->revnum) < 0)
386 if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
387 date_space, d->revdate, auth_space, d->author,
390 if (d->state != NULL) {
391 if (stream_printf(dest, " %s", d->state) < 0)
394 if (stream_printf(dest, ";\nbranches") < 0)
397 * Write out our branches. Add them to a reversed list for use
398 * later when we write out the text.
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);
407 /* Push branch heads on stack. */
408 STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
410 lprintf(2, "Empty branch!\n");
413 if (stream_printf(dest, "\n%s%s", branches_space,
418 if (stream_printf(dest, ";\nnext%s", next_space) < 0)
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)
425 STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
427 if (stream_printf(dest, ";\n\n") < 0)
430 /* Write out desc. */
431 if (stream_printf(dest, "\ndesc\n@@") < 0)
433 d = LIST_FIRST(&rf->trunk->deltalist);
435 /* Write out deltatexts. */
436 error = rcsfile_write_deltatext(rf, dest);
437 if (stream_printf(dest, "\n") < 0)
443 * Write out deltatexts of a delta and it's subbranches recursively.
446 rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
448 STAILQ_HEAD(, delta) deltastack;
449 LIST_HEAD(, delta) branchlist_datesorted;
450 struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
458 STAILQ_INIT(&deltastack);
459 d = LIST_FIRST(&rf->trunk->deltalist);
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. */
468 if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0)
470 if (stream_printf(dest, "log\n@") < 0)
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)
477 line = stream_getln(in, &size);
480 if (stream_printf(dest, "@\ntext\n@") < 0)
482 error = rcsfile_puttext(rf, dest, d, d->prev);
485 if (stream_printf(dest, "@") < 0)
488 LIST_INIT(&branchlist_datesorted);
489 d_next = LIST_NEXT(d, delta_next);
490 if (d_next != NULL) {
493 * If it's trunk, treat it like the oldest, if not treat
496 if (rcsrev_istrunk(d_next->revnum))
497 STAILQ_INSERT_HEAD(&deltastack, d_next,
500 LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
505 * First, we need to sort our branches based on their date to
506 * take into account some self-hacked RCS files.
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,
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,
522 while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
524 if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
529 LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
532 * Invert the deltalist of a branch, since we're writing them
535 LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
537 STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
544 * Generates text given a delta and a diffbase.
547 rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
548 struct delta *diffbase)
550 struct stream *in, *rd, *orig;
552 struct diffinfo dibuf, *di;
562 /* Write if the diffbase is the previous */
563 if (d->diffbase == diffbase) {
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) {
573 line = stream_getln(in, &size);
576 /* We need to apply diff to produce text, this is probably HEAD. */
577 } else if (diffbase == NULL) {
579 orig = rcsfile_getdeltatext(rf, d, &b);
584 line = stream_getln(orig, &size);
585 while (line != NULL) {
586 if (stream_write(dest, line, size) == -1) {
590 line = stream_getln(orig, &size);
594 * A new head was probably added, and now the previous HEAD must be
595 * changed to include the diff instead.
597 } else if (diffbase->diffbase == d) {
598 /* Get reverse diff. */
599 orig = rcsfile_getdeltatext(rf, d, &b);
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;
614 rd = stream_open_buf(diffbase->text);
615 error = diff_reverse(rd, orig, dest, k, di);
617 lprintf(-1, "Error applying reverse diff: %d\n", error);
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.
635 static struct stream *
636 rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
638 struct diffinfo dibuf, *di;
639 struct stream *orig, *dest, *rd;
640 struct buf *buf_orig;
648 * If diffbase is NULL or we are head (the old head), we have a normal
649 * complete deltatext.
651 if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
652 orig = stream_open_buf(d->text);
657 /* If not, we need to apply our diff to that of our diffbase. */
658 orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
663 * Now that we are sure we have a complete deltatext in ret, let's apply
666 *buf_dest = buf_new(BUF_SIZE_DEFAULT);
667 dest = stream_open_buf(*buf_dest);
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);
679 error = diff_apply(rd, orig, dest, k, di, 0);
685 if (buf_orig != NULL)
688 lprintf(-1, "Error applying diff: %d\n", error);
692 /* Now reopen the stream for the reading. */
693 dest = stream_open_buf(*buf_dest);
697 /* Print content of rcsfile. Useful for debugging. */
699 rcsfile_print(struct rcsfile *rf)
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);
719 /* Print all tags. */
720 STAILQ_FOREACH(t, &rf->taglist, tag_next) {
723 lprintf(1, "name: %s ", t->tag);
724 if (t->revnum != NULL)
725 lprintf(1, "rev: %s", t->revnum);
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));
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);
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);
759 if (rf->desc != NULL)
760 lprintf(1, "desc: '%s'\n", rf->desc);
763 /* Free all memory associated with a struct rcsfile. */
765 rcsfile_free(struct rcsfile *rf)
771 if (rf->name != NULL)
773 if (rf->head != NULL)
775 if (rf->branch != NULL)
777 if (rf->cvsroot != NULL)
779 if (rf->colltag != NULL)
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);
792 while (!STAILQ_EMPTY(&rf->taglist)) {
793 t = STAILQ_FIRST(&rf->taglist);
794 STAILQ_REMOVE_HEAD(&rf->taglist, tag_next);
797 if (t->revnum != NULL)
802 if (rf->comment != NULL)
805 /* Free all deltas in global list */
806 while (!LIST_EMPTY(&rf->deltatable)) {
807 d = LIST_FIRST(&rf->deltatable);
809 LIST_REMOVE(d, delta_next);
810 LIST_REMOVE(d, table_next);
811 rcsfile_freedelta(d);
814 /* Free global branch. */
815 if (rf->trunk->revnum != NULL)
816 free(rf->trunk->revnum);
819 if (rf->desc != NULL)
829 rcsfile_freedelta(struct delta *d)
833 if (d->revdate != NULL)
835 if (d->revnum != NULL)
837 if (d->author != NULL)
839 if (d->state != NULL)
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);
857 * Functions for editing RCS deltas.
860 /* Add a new entry to the access list. */
862 rcsfile_addaccess(struct rcsfile *rf, char *id)
866 s = xmalloc(sizeof(struct string));
867 s->str = xstrdup(id);
868 STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next);
871 /* Add a tag to a RCS file. */
873 rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum)
877 t = xmalloc(sizeof(struct tag));
878 t->tag = xstrdup(tag);
879 t->revnum = xstrdup(revnum);
881 STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next);
884 /* Import a tag to a RCS file. */
886 rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum)
890 t = xmalloc(sizeof(struct tag));
891 t->tag = xstrdup(tag);
892 t->revnum = xstrdup(revnum);
894 STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next);
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.
902 rcsfile_deleterev(struct rcsfile *rf, char *revname)
906 d = rcsfile_getdelta(rf, revname);
908 LIST_REMOVE(d, delta_next);
909 LIST_REMOVE(d, table_next);
910 rcsfile_freedelta(d);
913 /* Delete a tag from the tag list. */
915 rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum)
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);
932 * Searches the global deltalist for a delta.
935 rcsfile_getdelta(struct rcsfile *rf, char *revnum)
939 LIST_FOREACH(d, &rf->deltatable, table_next) {
940 if (strcmp(revnum, d->revnum) == 0)
946 /* Set rcsfile head. */
948 rcsfile_setval(struct rcsfile *rf, int field, char *val)
954 if (rf->head != NULL)
956 rf->head = xstrdup(val);
959 if (rf->branch != NULL)
961 rf->branch = (val == NULL) ? NULL : xstrdup(val);
967 case RCSFILE_COMMENT:
968 if (rf->comment != NULL)
970 rf->comment = xstrdup(val);
973 len = strlen(val) - 1;
976 rf->expand = keyword_decode_expand(val);
979 if (rf->desc != NULL)
981 rf->desc = xstrdup(val);
984 lprintf(-1, "Setting invalid RCSfile value.\n");
989 /* Create and initialize a delta. */
990 static struct delta *
991 rcsfile_createdelta(char *revnum)
995 d = xmalloc(sizeof(struct delta));
996 d->revnum = xstrdup(revnum);
1000 d->log = buf_new(BUF_SIZE_DEFAULT);
1001 d->text = buf_new(BUF_SIZE_DEFAULT);
1004 LIST_INIT(&d->branchlist);
1008 /* Add a delta to a imported delta tree. Used by the updater. */
1010 rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1014 struct delta *d, *d_bp, *d_next;
1019 d = rcsfile_getdelta(rf, revnum);
1021 lprintf(-1, "Delta %s already exists!\n", revnum);
1024 d = rcsfile_createdelta(revnum);
1026 d->revdate = xstrdup(revdate);
1027 d->author = xstrdup(author);
1028 d->diffbase = rcsfile_getdelta(rf, diffbase);
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);
1035 * We didn't find a branch, check if we can find a branchpoint and
1036 * create a branch there.
1039 brev = rcsrev_prefix(d->revnum);
1040 bprev = rcsrev_prefix(brev);
1042 d_bp = rcsfile_getdelta(rf, bprev);
1045 lprintf(-1, "No branch point for adding delta %s\n",
1050 /* Create the branch and insert in delta. */
1051 b = xmalloc(sizeof(struct branch));
1053 LIST_INIT(&b->deltalist);
1054 rcsdelta_insertbranch(d_bp, b);
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);
1064 /* Adds a delta to a rcsfile struct. Used by the parser. */
1066 rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1067 char *state, char *next)
1070 struct delta *d, *d_bp, *d_next;
1075 d = rcsfile_getdelta(rf, revnum);
1078 /* If not, we'll just create a new entry. */
1079 d = rcsfile_createdelta(revnum);
1082 if (d->placeholder == 0) {
1083 lprintf(-1, "Trying to import already existing delta\n");
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.
1093 d->revdate = xstrdup(revdate);
1094 d->author = xstrdup(author);
1096 d->state = xstrdup(state);
1098 /* If we have a next, create a placeholder for it. */
1100 d_next = rcsfile_createdelta(next);
1101 d_next->placeholder = 1;
1102 /* Diffbase should be the previous. */
1103 d_next->diffbase = d;
1106 /* If we're opening read-only, do minimal work. */
1108 if (!d->placeholder)
1109 rcsfile_insertsorteddelta(rf, d);
1113 rcsfile_insertsorteddelta(rf, d_next);
1117 /* If it's trunk, insert it in the head branch list. */
1118 b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
1122 * We didn't find a branch, check if we can find a branchpoint and
1123 * create a branch there.
1126 brev = rcsrev_prefix(d->revnum);
1127 bprev = rcsrev_prefix(brev);
1129 d_bp = rcsfile_getdelta(rf, bprev);
1132 lprintf(-1, "No branch point for adding delta %s\n",
1137 /* Create the branch and insert in delta. */
1138 b = xmalloc(sizeof(struct branch));
1140 LIST_INIT(&b->deltalist);
1141 rcsdelta_insertbranch(d_bp, b);
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);
1150 rcsfile_insertdelta(b, d, 0);
1152 * On import we need to set the diffbase to our
1153 * branchpoint for writing out later.
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);
1166 rcsfile_insertsorteddelta(rf, d);
1167 } else /* Not a placeholder anymore. */ {
1169 /* Put it into the tree. */
1170 trunk = rcsrev_istrunk(d->revnum);
1171 rcsfile_insertdelta(b, d, trunk);
1174 /* If we have a next, insert the placeholder into the lookup list. */
1176 rcsfile_insertsorteddelta(rf, d_next);
1180 * Find the branch of a revision number.
1182 static struct branch *
1183 rcsfile_getbranch(struct rcsfile *rf, char *revnum)
1187 char *branchrev, *bprev;
1189 branchrev = rcsrev_prefix(revnum);
1190 bprev = rcsrev_prefix(branchrev);
1191 d = rcsfile_getdelta(rf, bprev);
1193 LIST_FOREACH(b, &d->branchlist, branch_next) {
1194 if(rcsnum_cmp(b->revnum, branchrev) == 0) {
1203 /* Insert a branch into a delta, sorted by branch revision date. */
1205 rcsdelta_insertbranch(struct delta *d, struct branch *b)
1207 struct branch *b_iter;
1209 /* If it's empty, insert into head. */
1210 if (LIST_EMPTY(&d->branchlist)) {
1211 LIST_INSERT_HEAD(&d->branchlist, b, branch_next);
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);
1221 if (LIST_NEXT(b_iter, branch_next) == NULL)
1224 /* Insert after last element. */
1225 LIST_INSERT_AFTER(b_iter, b, branch_next);
1228 /* Insert a delta into the correct place in the table of the rcsfile. */
1230 rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d)
1234 /* If it's empty, insert into head. */
1235 if (LIST_EMPTY(&rf->deltatable)) {
1236 LIST_INSERT_HEAD(&rf->deltatable, d, table_next);
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);
1246 if (LIST_NEXT(d2, table_next) == NULL)
1249 /* Insert after last element. */
1250 LIST_INSERT_AFTER(d2, d, table_next);
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
1260 rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk)
1264 /* If it's empty, insert into head. */
1265 if (LIST_EMPTY(&b->deltalist)) {
1266 LIST_INSERT_HEAD(&b->deltalist, d, delta_next);
1271 * Just put it in before the revnum that is lower. Sort trunk branch by
1272 * branchnum but the subbranches after deltadate.
1274 LIST_FOREACH(d2, &b->deltalist, delta_next) {
1276 if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) {
1277 LIST_INSERT_BEFORE(d2, d, delta_next);
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);
1288 if (LIST_NEXT(d2, delta_next) == NULL)
1291 /* Insert after last element. */
1292 LIST_INSERT_AFTER(d2, d, delta_next);
1296 /* Add logtext to a delta. Assume the delta already exists. */
1298 rcsdelta_addlog(struct delta *d, char *log, int len)
1300 struct stream *dest;
1304 /* Strip away '@' at beginning and end. */
1307 log[len - 1] = '\0';
1308 dest = stream_open_buf(d->log);
1309 nbytes = stream_write(dest, log, len - 1);
1311 return ((nbytes == -1) ? -1 : 0);
1314 /* Add deltatext to a delta. Assume the delta already exists. */
1316 rcsdelta_addtext(struct delta *d, char *text, int len)
1318 struct stream *dest;
1322 /* Strip away '@' at beginning and end. */
1325 text[len - 1] = '\0';
1327 dest = stream_open_buf(d->text);
1328 nbytes = stream_write(dest, text, len - 1);
1330 return ((nbytes == -1) ? -1 : 0);
1333 /* Add a deltatext logline to a delta. */
1335 rcsdelta_appendlog(struct delta *d, char *logline, size_t size)
1337 struct stream *dest;
1341 dest = stream_open_buf(d->log);
1342 error = rcsdelta_writestring(logline, size, dest);
1347 /* Add a deltatext textline to a delta. */
1349 rcsdelta_appendtext(struct delta *d, char *textline, size_t size)
1351 struct stream *dest;
1355 dest = stream_open_buf(d->text);
1356 error = rcsdelta_writestring(textline, size, dest);
1362 rcsdelta_writestring(char *textline, size_t size, struct stream *dest)
1368 for (i = 0; i < size; i++) {
1369 buf[0] = textline[i];
1373 if (buf[0] == '@') {
1378 if (stream_write(dest, buf, count) == -1)
1384 /* Set delta state. */
1386 rcsdelta_setstate(struct delta *d, char *state)
1389 if (d->state != NULL)
1391 if (state != NULL) {
1392 d->state = xstrdup(state);
1398 /* Truncate the deltalog with a certain offset. */
1400 rcsdelta_truncatelog(struct delta *d, off_t offset)
1403 stream_truncate_buf(d->log, offset);
1406 /* Truncate the deltatext with a certain offset. */
1408 rcsdelta_truncatetext(struct delta *d, off_t offset)
1411 stream_truncate_buf(d->text, offset);