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