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(const char *path, const char *name, const char *cvsroot,
179 const char *colltag, int ro)
185 if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
188 rf = xmalloc(sizeof(struct rcsfile));
189 rf->name = xstrdup(name);
190 rf->cvsroot = xstrdup(cvsroot);
191 rf->colltag = xstrdup(colltag);
193 /* Initialize head branch. */
194 rf->trunk = xmalloc(sizeof(struct branch));
195 rf->trunk->revnum = xstrdup("1");
196 LIST_INIT(&rf->trunk->deltalist);
197 /* Initialize delta list. */
198 LIST_INIT(&rf->deltatable);
199 /* Initialize tag list. */
200 STAILQ_INIT(&rf->taglist);
201 /* Initialize accesslist. */
202 STAILQ_INIT(&rf->accesslist);
204 /* Initialize all fields. */
209 rf->expand = EXPAND_DEFAULT;
213 infp = fopen(path, "r");
215 lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
219 error = rcsparse_run(rf, infp, ro);
222 lprintf(-1, "Error parsing \"%s\"\n", name);
230 * Write content of rcsfile to server. Assumes we have a complete RCS file
234 rcsfile_send_details(struct rcsfile *rf, struct stream *wr)
243 error = proto_printf(wr, "V %s\n", rf->name);
247 /* Write default branch. */
248 if (rf->branch == NULL)
249 error = proto_printf(wr, "b\n");
251 error = proto_printf(wr, "B %s\n", rf->branch);
255 /* Write deltas to server. */
256 error = proto_printf(wr, "D\n");
260 LIST_FOREACH(d, &rf->deltatable, table_next) {
261 error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
265 error = proto_printf(wr, ".\n");
270 if (rf->expand != EXPAND_DEFAULT) {
271 keyword = keyword_encode_expand(rf->expand);
272 if (keyword != NULL) {
273 error = proto_printf(wr, "E %s\n",
274 keyword_encode_expand(rf->expand));
280 /* Write tags to server. */
281 error = proto_printf(wr, "T\n");
284 STAILQ_FOREACH(t, &rf->taglist, tag_next) {
285 error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
289 error = proto_printf(wr, ".\n");
292 error = proto_printf(wr, ".\n");
297 * Write a RCS file to disk represented by the destination stream. Keep track of
298 * deltas with a stack and an inverted stack.
301 rcsfile_write(struct rcsfile *rf, struct stream *dest)
303 STAILQ_HEAD(, delta) deltastack;
304 STAILQ_HEAD(, delta) deltalist_inverted;
307 struct delta *d, *d_tmp, *d_next;
310 /* First write head. */
311 d = LIST_FIRST(&rf->trunk->deltalist);
312 if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0)
315 /* Write branch, if we have. */
316 if (rf->branch != NULL) {
317 if (stream_printf(dest, "branch%s%s;\n", branch_space,
323 if (stream_printf(dest, "access") < 0)
326 if (!STAILQ_EMPTY(&rf->accesslist)) {
328 * XXX: Write out access. This doesn't seem to be necessary for
333 if (stream_printf(dest, ";\n") < 0)
336 /* Write out taglist. */
337 if (stream_printf(dest, "symbols") < 0)
339 if (!STAILQ_EMPTY(&rf->taglist)) {
340 STAILQ_FOREACH(t, &rf->taglist, tag_next) {
341 if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag,
347 /* Write out locks and strict. */
348 if (stream_printf(dest, ";\nlocks;") < 0)
350 if (rf->strictlock) {
351 if (stream_printf(dest, " strict;") < 0)
354 if (stream_printf(dest, "\n") < 0)
357 /* Write out the comment. */
358 if (rf->comment != NULL) {
359 if (stream_printf(dest, "comment%s%s;\n", comment_space,
363 if (rf->expand != EXPAND_DEFAULT) {
364 if (stream_printf(dest, "expand%s@%s@;\n", expand_space,
365 keyword_encode_expand(rf->expand)) < 0)
369 if (stream_printf(dest, "\n\n") < 0)
373 * Write out deltas. We use a stack where we push the appropriate deltas
374 * that is to be written out during the loop.
376 STAILQ_INIT(&deltastack);
377 d = LIST_FIRST(&rf->trunk->deltalist);
378 STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
379 while (!STAILQ_EMPTY(&deltastack)) {
380 d = STAILQ_FIRST(&deltastack);
381 STAILQ_REMOVE_HEAD(&deltastack, stack_next);
382 /* Do not write out placeholders just to be safe. */
385 if (stream_printf(dest, "%s\n", d->revnum) < 0)
387 if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
388 date_space, d->revdate, auth_space, d->author,
391 if (d->state != NULL) {
392 if (stream_printf(dest, " %s", d->state) < 0)
395 if (stream_printf(dest, ";\nbranches") < 0)
398 * Write out our branches. Add them to a reversed list for use
399 * later when we write out the text.
401 STAILQ_INIT(&deltalist_inverted);
402 LIST_FOREACH(b, &d->branchlist, branch_next) {
403 d_tmp = LIST_FIRST(&b->deltalist);
404 STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);
405 STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
408 /* Push branch heads on stack. */
409 STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
411 lprintf(2, "Empty branch!\n");
414 if (stream_printf(dest, "\n%s%s", branches_space,
419 if (stream_printf(dest, ";\nnext%s", next_space) < 0)
421 /* Push next delta on stack. */
422 d_next = LIST_NEXT(d, delta_next);
423 if (d_next != NULL) {
424 if (stream_printf(dest, "%s", d_next->revnum) < 0)
426 STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
428 if (stream_printf(dest, ";\n\n") < 0)
431 /* Write out desc. */
432 if (stream_printf(dest, "\ndesc\n@@") < 0)
434 d = LIST_FIRST(&rf->trunk->deltalist);
436 /* Write out deltatexts. */
437 error = rcsfile_write_deltatext(rf, dest);
438 if (stream_printf(dest, "\n") < 0)
444 * Write out deltatexts of a delta and it's subbranches recursively.
447 rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
449 STAILQ_HEAD(, delta) deltastack;
450 LIST_HEAD(, delta) branchlist_datesorted;
451 struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
459 STAILQ_INIT(&deltastack);
460 d = LIST_FIRST(&rf->trunk->deltalist);
462 STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
463 while (!STAILQ_EMPTY(&deltastack)) {
464 d = STAILQ_FIRST(&deltastack);
465 STAILQ_REMOVE_HEAD(&deltastack, stack_next);
466 /* Do not write out placeholders just to be safe. */
469 if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0)
471 if (stream_printf(dest, "log\n@") < 0)
473 in = stream_open_buf(d->log);
474 line = stream_getln(in, &size);
475 while (line != NULL) {
476 if (stream_write(dest, line, size) == -1)
478 line = stream_getln(in, &size);
481 if (stream_printf(dest, "@\ntext\n@") < 0)
483 error = rcsfile_puttext(rf, dest, d, d->prev);
486 if (stream_printf(dest, "@") < 0)
489 LIST_INIT(&branchlist_datesorted);
490 d_next = LIST_NEXT(d, delta_next);
491 if (d_next != NULL) {
494 * If it's trunk, treat it like the oldest, if not treat
497 if (rcsrev_istrunk(d_next->revnum))
498 STAILQ_INSERT_HEAD(&deltastack, d_next,
501 LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
506 * First, we need to sort our branches based on their date to
507 * take into account some self-hacked RCS files.
509 LIST_FOREACH(b, &d->branchlist, branch_next) {
510 d_tmp = LIST_FIRST(&b->deltalist);
511 if (LIST_EMPTY(&branchlist_datesorted)) {
512 LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp,
517 d_tmp2 = LIST_FIRST(&branchlist_datesorted);
518 if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) {
519 LIST_INSERT_BEFORE(d_tmp2, d_tmp,
523 while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
525 if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
530 LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
533 * Invert the deltalist of a branch, since we're writing them
536 LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
538 STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
545 * Generates text given a delta and a diffbase.
548 rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
549 struct delta *diffbase)
551 struct stream *in, *rd, *orig;
553 struct diffinfo dibuf, *di;
563 /* Write if the diffbase is the previous */
564 if (d->diffbase == diffbase) {
566 /* Write out the text. */
567 in = stream_open_buf(d->text);
568 line = stream_getln(in, &size);
569 while (line != NULL) {
570 if (stream_write(dest, line, size) == -1) {
574 line = stream_getln(in, &size);
577 /* We need to apply diff to produce text, this is probably HEAD. */
578 } else if (diffbase == NULL) {
580 orig = rcsfile_getdeltatext(rf, d, &b);
585 line = stream_getln(orig, &size);
586 while (line != NULL) {
587 if (stream_write(dest, line, size) == -1) {
591 line = stream_getln(orig, &size);
595 * A new head was probably added, and now the previous HEAD must be
596 * changed to include the diff instead.
598 } else if (diffbase->diffbase == d) {
599 /* Get reverse diff. */
600 orig = rcsfile_getdeltatext(rf, d, &b);
605 di->di_rcsfile = rf->name;
606 di->di_cvsroot = rf->cvsroot;
607 di->di_revnum = d->revnum;
608 di->di_revdate = d->revdate;
609 di->di_author = d->author;
610 di->di_tag = rf->colltag;
611 di->di_state = d->state;
612 di->di_expand = EXPAND_OLD;
615 rd = stream_open_buf(diffbase->text);
616 error = diff_reverse(rd, orig, dest, k, di);
618 lprintf(-1, "Error applying reverse diff: %d\n", error);
632 * Return a stream with an applied diff of a delta.
633 * XXX: extra overhead on the last apply. Could write directly to file, but
634 * makes things complicated though.
636 static struct stream *
637 rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
639 struct diffinfo dibuf, *di;
640 struct stream *orig, *dest, *rd;
641 struct buf *buf_orig;
649 * If diffbase is NULL or we are head (the old head), we have a normal
650 * complete deltatext.
652 if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
653 orig = stream_open_buf(d->text);
658 /* If not, we need to apply our diff to that of our diffbase. */
659 orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
664 * Now that we are sure we have a complete deltatext in ret, let's apply
667 *buf_dest = buf_new(BUF_SIZE_DEFAULT);
668 dest = stream_open_buf(*buf_dest);
670 di->di_rcsfile = rf->name;
671 di->di_cvsroot = rf->cvsroot;
672 di->di_revnum = d->revnum;
673 di->di_revdate = d->revdate;
674 di->di_author = d->author;
675 di->di_tag = rf->colltag;
676 di->di_state = d->state;
677 di->di_expand = EXPAND_OLD;
678 rd = stream_open_buf(d->text);
680 error = diff_apply(rd, orig, dest, k, di, 0);
686 if (buf_orig != NULL)
689 lprintf(-1, "Error applying diff: %d\n", error);
693 /* Now reopen the stream for the reading. */
694 dest = stream_open_buf(*buf_dest);
698 /* Print content of rcsfile. Useful for debugging. */
700 rcsfile_print(struct rcsfile *rf)
709 if (rf->name != NULL)
710 lprintf(1, "name: '%s'\n", rf->name);
711 if (rf->head != NULL)
712 lprintf(1, "head: '%s'\n", rf->head);
713 if (rf->branch != NULL)
714 lprintf(1, "branch: '%s'\n", rf->branch);
715 lprintf(1, "Access: ");
716 STAILQ_FOREACH(s, &rf->accesslist, string_next)
717 lprintf(1, "'%s' ", s->str);
720 /* Print all tags. */
721 STAILQ_FOREACH(t, &rf->taglist, tag_next) {
724 lprintf(1, "name: %s ", t->tag);
725 if (t->revnum != NULL)
726 lprintf(1, "rev: %s", t->revnum);
731 lprintf(1, "Strict!\n");
732 if (rf->comment != NULL)
733 lprintf(1, "comment: '%s'\n", rf->comment);
734 if (rf->expand != EXPAND_DEFAULT)
735 lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand));
737 /* Print all deltas. */
738 LIST_FOREACH(d, &rf->deltatable, table_next) {
739 lprintf(1, "Delta: ");
740 if (d->revdate != NULL)
741 lprintf(1, "date: %s ", d->revdate);
742 if (d->revnum != NULL)
743 lprintf(1, "rev: %s", d->revnum);
744 if (d->author != NULL)
745 lprintf(1, "author: %s", d->author);
746 if (d->state != NULL)
747 lprintf(1, "state: %s", d->state);
749 lprintf(1, "Text:\n");
750 in = stream_open_buf(d->text);
751 line = stream_getln(in, NULL);
752 while (line != NULL) {
753 lprintf(1, "TEXT: %s\n", line);
754 line = stream_getln(in, NULL);
760 if (rf->desc != NULL)
761 lprintf(1, "desc: '%s'\n", rf->desc);
764 /* Free all memory associated with a struct rcsfile. */
766 rcsfile_free(struct rcsfile *rf)
772 if (rf->name != NULL)
774 if (rf->head != NULL)
776 if (rf->branch != NULL)
778 if (rf->cvsroot != NULL)
780 if (rf->colltag != NULL)
783 /* Free all access ids. */
784 while (!STAILQ_EMPTY(&rf->accesslist)) {
785 s = STAILQ_FIRST(&rf->accesslist);
786 STAILQ_REMOVE_HEAD(&rf->accesslist, string_next);
793 while (!STAILQ_EMPTY(&rf->taglist)) {
794 t = STAILQ_FIRST(&rf->taglist);
795 STAILQ_REMOVE_HEAD(&rf->taglist, tag_next);
798 if (t->revnum != NULL)
803 if (rf->comment != NULL)
806 /* Free all deltas in global list */
807 while (!LIST_EMPTY(&rf->deltatable)) {
808 d = LIST_FIRST(&rf->deltatable);
810 LIST_REMOVE(d, delta_next);
811 LIST_REMOVE(d, table_next);
812 rcsfile_freedelta(d);
815 /* Free global branch. */
816 if (rf->trunk->revnum != NULL)
817 free(rf->trunk->revnum);
820 if (rf->desc != NULL)
830 rcsfile_freedelta(struct delta *d)
834 if (d->revdate != NULL)
836 if (d->revnum != NULL)
838 if (d->author != NULL)
840 if (d->state != NULL)
847 /* Free all subbranches of a delta. */
848 while (!LIST_EMPTY(&d->branchlist)) {
849 b = LIST_FIRST(&d->branchlist);
850 LIST_REMOVE(b, branch_next);
858 * Functions for editing RCS deltas.
861 /* Add a new entry to the access list. */
863 rcsfile_addaccess(struct rcsfile *rf, char *id)
867 s = xmalloc(sizeof(struct string));
868 s->str = xstrdup(id);
869 STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next);
872 /* Add a tag to a RCS file. */
874 rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum)
878 t = xmalloc(sizeof(struct tag));
879 t->tag = xstrdup(tag);
880 t->revnum = xstrdup(revnum);
882 STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next);
885 /* Import a tag to a RCS file. */
887 rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum)
891 t = xmalloc(sizeof(struct tag));
892 t->tag = xstrdup(tag);
893 t->revnum = xstrdup(revnum);
895 STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next);
899 * Delete a revision from the global delta list and the branch it is in. Csup
900 * will tell us to delete the tags involved.
903 rcsfile_deleterev(struct rcsfile *rf, char *revname)
907 d = rcsfile_getdelta(rf, revname);
909 LIST_REMOVE(d, delta_next);
910 LIST_REMOVE(d, table_next);
911 rcsfile_freedelta(d);
914 /* Delete a tag from the tag list. */
916 rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum)
920 STAILQ_FOREACH(t, &rf->taglist, tag_next) {
921 if ((strcmp(tag, t->tag) == 0) &&
922 (strcmp(revnum, t->revnum) == 0)) {
923 STAILQ_REMOVE(&rf->taglist, t, tag, tag_next);
933 * Searches the global deltalist for a delta.
936 rcsfile_getdelta(struct rcsfile *rf, char *revnum)
940 LIST_FOREACH(d, &rf->deltatable, table_next) {
941 if (strcmp(revnum, d->revnum) == 0)
947 /* Set rcsfile head. */
949 rcsfile_setval(struct rcsfile *rf, int field, char *val)
955 if (rf->head != NULL)
957 rf->head = xstrdup(val);
960 if (rf->branch != NULL)
962 rf->branch = (val == NULL) ? NULL : xstrdup(val);
968 case RCSFILE_COMMENT:
969 if (rf->comment != NULL)
971 rf->comment = xstrdup(val);
974 len = strlen(val) - 1;
977 rf->expand = keyword_decode_expand(val);
980 if (rf->desc != NULL)
982 rf->desc = xstrdup(val);
985 lprintf(-1, "Setting invalid RCSfile value.\n");
990 /* Create and initialize a delta. */
991 static struct delta *
992 rcsfile_createdelta(char *revnum)
996 d = xmalloc(sizeof(struct delta));
997 d->revnum = xstrdup(revnum);
1001 d->log = buf_new(BUF_SIZE_DEFAULT);
1002 d->text = buf_new(BUF_SIZE_DEFAULT);
1005 LIST_INIT(&d->branchlist);
1009 /* Add a delta to a imported delta tree. Used by the updater. */
1011 rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1015 struct delta *d, *d_bp, *d_next;
1020 d = rcsfile_getdelta(rf, revnum);
1022 lprintf(-1, "Delta %s already exists!\n", revnum);
1025 d = rcsfile_createdelta(revnum);
1027 d->revdate = xstrdup(revdate);
1028 d->author = xstrdup(author);
1029 d->diffbase = rcsfile_getdelta(rf, diffbase);
1031 /* If it's trunk, insert it in the head branch list. */
1032 b = rcsrev_istrunk(d->revnum) ? rf->trunk :
1033 rcsfile_getbranch(rf, d->revnum);
1036 * We didn't find a branch, check if we can find a branchpoint and
1037 * create a branch there.
1040 brev = rcsrev_prefix(d->revnum);
1041 bprev = rcsrev_prefix(brev);
1043 d_bp = rcsfile_getdelta(rf, bprev);
1046 lprintf(-1, "No branch point for adding delta %s\n",
1051 /* Create the branch and insert in delta. */
1052 b = xmalloc(sizeof(struct branch));
1054 LIST_INIT(&b->deltalist);
1055 rcsdelta_insertbranch(d_bp, b);
1058 /* Insert both into the tree, and into the lookup list. */
1059 trunk = rcsrev_istrunk(d->revnum);
1060 rcsfile_insertdelta(b, d, trunk);
1061 rcsfile_insertsorteddelta(rf, d);
1065 /* Adds a delta to a rcsfile struct. Used by the parser. */
1067 rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1068 char *state, char *next)
1071 struct delta *d, *d_bp, *d_next;
1076 d = rcsfile_getdelta(rf, revnum);
1079 /* If not, we'll just create a new entry. */
1080 d = rcsfile_createdelta(revnum);
1083 if (d->placeholder == 0) {
1084 lprintf(-1, "Trying to import already existing delta\n");
1089 * If already exists, assume that only revnum is filled out, and set the
1090 * rest of the fields. This should be an OK assumption given that we can
1091 * be sure internally that the structure is sufficiently initialized so
1092 * we won't have any unfreed memory.
1094 d->revdate = xstrdup(revdate);
1095 d->author = xstrdup(author);
1097 d->state = xstrdup(state);
1099 /* If we have a next, create a placeholder for it. */
1101 d_next = rcsfile_createdelta(next);
1102 d_next->placeholder = 1;
1103 /* Diffbase should be the previous. */
1104 d_next->diffbase = d;
1107 /* If we're opening read-only, do minimal work. */
1109 if (!d->placeholder)
1110 rcsfile_insertsorteddelta(rf, d);
1114 rcsfile_insertsorteddelta(rf, d_next);
1118 /* If it's trunk, insert it in the head branch list. */
1119 b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
1123 * We didn't find a branch, check if we can find a branchpoint and
1124 * create a branch there.
1127 brev = rcsrev_prefix(d->revnum);
1128 bprev = rcsrev_prefix(brev);
1130 d_bp = rcsfile_getdelta(rf, bprev);
1133 lprintf(-1, "No branch point for adding delta %s\n",
1138 /* Create the branch and insert in delta. */
1139 b = xmalloc(sizeof(struct branch));
1141 LIST_INIT(&b->deltalist);
1142 rcsdelta_insertbranch(d_bp, b);
1145 /* Insert if not a placeholder. */
1146 if (!d->placeholder) {
1147 /* Insert both into the tree, and into the lookup list. */
1148 if (rcsrev_istrunk(d->revnum))
1149 rcsfile_insertdelta(b, d, 1);
1151 rcsfile_insertdelta(b, d, 0);
1153 * On import we need to set the diffbase to our
1154 * branchpoint for writing out later.
1156 if (LIST_FIRST(&b->deltalist) == d) {
1157 brev = rcsrev_prefix(d->revnum);
1158 bprev = rcsrev_prefix(brev);
1159 d_bp = rcsfile_getdelta(rf, bprev);
1160 /* This should really not happen. */
1161 assert(d_bp != NULL);
1167 rcsfile_insertsorteddelta(rf, d);
1168 } else /* Not a placeholder anymore. */ {
1170 /* Put it into the tree. */
1171 trunk = rcsrev_istrunk(d->revnum);
1172 rcsfile_insertdelta(b, d, trunk);
1175 /* If we have a next, insert the placeholder into the lookup list. */
1177 rcsfile_insertsorteddelta(rf, d_next);
1181 * Find the branch of a revision number.
1183 static struct branch *
1184 rcsfile_getbranch(struct rcsfile *rf, char *revnum)
1188 char *branchrev, *bprev;
1190 branchrev = rcsrev_prefix(revnum);
1191 bprev = rcsrev_prefix(branchrev);
1192 d = rcsfile_getdelta(rf, bprev);
1194 LIST_FOREACH(b, &d->branchlist, branch_next) {
1195 if(rcsnum_cmp(b->revnum, branchrev) == 0) {
1204 /* Insert a branch into a delta, sorted by branch revision date. */
1206 rcsdelta_insertbranch(struct delta *d, struct branch *b)
1208 struct branch *b_iter;
1210 /* If it's empty, insert into head. */
1211 if (LIST_EMPTY(&d->branchlist)) {
1212 LIST_INSERT_HEAD(&d->branchlist, b, branch_next);
1216 /* Just put it in before the revdate that is lower. */
1217 LIST_FOREACH(b_iter, &d->branchlist, branch_next) {
1218 if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) {
1219 LIST_INSERT_BEFORE(b_iter, b, branch_next);
1222 if (LIST_NEXT(b_iter, branch_next) == NULL)
1225 /* Insert after last element. */
1226 LIST_INSERT_AFTER(b_iter, b, branch_next);
1229 /* Insert a delta into the correct place in the table of the rcsfile. */
1231 rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d)
1235 /* If it's empty, insert into head. */
1236 if (LIST_EMPTY(&rf->deltatable)) {
1237 LIST_INSERT_HEAD(&rf->deltatable, d, table_next);
1241 /* Just put it in before the revdate that is lower. */
1242 LIST_FOREACH(d2, &rf->deltatable, table_next) {
1243 if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) {
1244 LIST_INSERT_BEFORE(d2, d, table_next);
1247 if (LIST_NEXT(d2, table_next) == NULL)
1250 /* Insert after last element. */
1251 LIST_INSERT_AFTER(d2, d, table_next);
1255 * Insert a delta into the correct place in branch. A trunk branch will have
1256 * different ordering scheme and be sorted by revision number, but a normal
1257 * branch will be sorted by date to maintain compability with branches that is
1261 rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk)
1265 /* If it's empty, insert into head. */
1266 if (LIST_EMPTY(&b->deltalist)) {
1267 LIST_INSERT_HEAD(&b->deltalist, d, delta_next);
1272 * Just put it in before the revnum that is lower. Sort trunk branch by
1273 * branchnum but the subbranches after deltadate.
1275 LIST_FOREACH(d2, &b->deltalist, delta_next) {
1277 if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) {
1278 LIST_INSERT_BEFORE(d2, d, delta_next);
1282 /* XXX: here we depend on the date being set, but it
1283 * should be before this is called anyway. */
1284 if (rcsnum_cmp(d->revnum, d2->revnum) < 0) {
1285 LIST_INSERT_BEFORE(d2, d, delta_next);
1289 if (LIST_NEXT(d2, delta_next) == NULL)
1292 /* Insert after last element. */
1293 LIST_INSERT_AFTER(d2, d, delta_next);
1297 /* Add logtext to a delta. Assume the delta already exists. */
1299 rcsdelta_addlog(struct delta *d, char *log, int len)
1301 struct stream *dest;
1305 /* Strip away '@' at beginning and end. */
1308 log[len - 1] = '\0';
1309 dest = stream_open_buf(d->log);
1310 nbytes = stream_write(dest, log, len - 1);
1312 return ((nbytes == -1) ? -1 : 0);
1315 /* Add deltatext to a delta. Assume the delta already exists. */
1317 rcsdelta_addtext(struct delta *d, char *text, int len)
1319 struct stream *dest;
1323 /* Strip away '@' at beginning and end. */
1326 text[len - 1] = '\0';
1328 dest = stream_open_buf(d->text);
1329 nbytes = stream_write(dest, text, len - 1);
1331 return ((nbytes == -1) ? -1 : 0);
1334 /* Add a deltatext logline to a delta. */
1336 rcsdelta_appendlog(struct delta *d, char *logline, size_t size)
1338 struct stream *dest;
1342 dest = stream_open_buf(d->log);
1343 error = rcsdelta_writestring(logline, size, dest);
1348 /* Add a deltatext textline to a delta. */
1350 rcsdelta_appendtext(struct delta *d, char *textline, size_t size)
1352 struct stream *dest;
1356 dest = stream_open_buf(d->text);
1357 error = rcsdelta_writestring(textline, size, dest);
1363 rcsdelta_writestring(char *textline, size_t size, struct stream *dest)
1369 for (i = 0; i < size; i++) {
1370 buf[0] = textline[i];
1374 if (buf[0] == '@') {
1379 if (stream_write(dest, buf, count) == -1)
1385 /* Set delta state. */
1387 rcsdelta_setstate(struct delta *d, char *state)
1390 if (d->state != NULL)
1392 if (state != NULL) {
1393 d->state = xstrdup(state);
1399 /* Truncate the deltalog with a certain offset. */
1401 rcsdelta_truncatelog(struct delta *d, off_t offset)
1404 stream_truncate_buf(d->log, offset);
1407 /* Truncate the deltatext with a certain offset. */
1409 rcsdelta_truncatetext(struct delta *d, off_t offset)
1412 stream_truncate_buf(d->text, offset);