]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/csup/rcsfile.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / csup / rcsfile.c
1 /*-
2  * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <assert.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "diff.h"
37 #include "keyword.h"
38 #include "misc.h"
39 #include "proto.h"
40 #include "queue.h"
41 #include "rcsfile.h"
42 #include "rcsparse.h"
43 #include "stream.h"
44
45 #define BUF_SIZE_DEFAULT        128
46
47 /*
48  * RCS parser library. This is the part of the library that handles the
49  * importing, editing and exporting of RCS files. It currently supports only the
50  * part of the RCS file specification that is needed for csup (for instance,
51  * newphrases are not supported), and assumes that you can store the whole RCS
52  * file in memory.
53  */
54
55 /*
56  * Linked list for string tokens.
57  */
58 struct 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  */
66 struct 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  */
77 struct 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  */
100 struct 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  */
111 struct 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
130 static void              rcsfile_freedelta(struct delta *);
131 static void              rcsfile_insertdelta(struct branch *, struct delta *,
132                              int);
133 static struct delta     *rcsfile_createdelta(char *);
134 static int               rcsfile_write_deltatext(struct rcsfile *,
135                              struct stream *);
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 *,
140                              struct delta *);
141 static struct stream    *rcsfile_getdeltatext(struct rcsfile *, struct delta *,
142                              struct buf **);
143 static int               rcsdelta_writestring(char *, size_t, struct stream *);
144 static void              rcsdelta_insertbranch(struct delta *, struct branch *);
145
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";
157
158 void print_stream(struct stream *);
159
160 /* Print the contents of a stream, for debugging. */
161 void
162 print_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  */
177 struct rcsfile *
178 rcsfile_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  */
232 int
233 rcsfile_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  */
299 int
300 rcsfile_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  */
445 int
446 rcsfile_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  */
546 static int
547 rcsfile_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         }
624 cleanup:
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  */
635 static struct stream *
636 rcsfile_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. */
698 void
699 rcsfile_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. */
764 void
765 rcsfile_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  */
828 static void
829 rcsfile_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. */
861 void
862 rcsfile_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. */
872 void
873 rcsfile_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. */
885 void
886 rcsfile_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  */
901 void
902 rcsfile_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. */
914 void
915 rcsfile_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  */
934 struct delta *
935 rcsfile_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. */
947 void
948 rcsfile_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. */
990 static struct delta *
991 rcsfile_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. */
1009 struct delta *
1010 rcsfile_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. */
1065 void
1066 rcsfile_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  */
1182 static struct branch *
1183 rcsfile_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. */
1204 static void
1205 rcsdelta_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. */
1229 static void
1230 rcsfile_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  */
1259 static void
1260 rcsfile_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. */
1297 int
1298 rcsdelta_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. */
1315 int
1316 rcsdelta_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. */
1334 int
1335 rcsdelta_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. */
1348 int
1349 rcsdelta_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
1361 static int 
1362 rcsdelta_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. */
1385 void
1386 rcsdelta_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. */
1399 void
1400 rcsdelta_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. */
1407 void
1408 rcsdelta_truncatetext(struct delta *d, off_t offset)
1409 {
1410
1411         stream_truncate_buf(d->text, offset);
1412 }