rcsfile.c revision 272461
1120945Snectar/*-
2120945Snectar * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
3120945Snectar * All rights reserved.
4120945Snectar *
5120945Snectar * Redistribution and use in source and binary forms, with or without
6120945Snectar * modification, are permitted provided that the following conditions
7120945Snectar * are met:
8120945Snectar * 1. Redistributions of source code must retain the above copyright
9120945Snectar *    notice, this list of conditions and the following disclaimer.
10120945Snectar * 2. Redistributions in binary form must reproduce the above copyright
11120945Snectar *    notice, this list of conditions and the following disclaimer in the
12120945Snectar *    documentation and/or other materials provided with the distribution.
13120945Snectar *
14120945Snectar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15120945Snectar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16120945Snectar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17120945Snectar * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18120945Snectar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19120945Snectar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20120945Snectar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21120945Snectar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22120945Snectar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23120945Snectar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24120945Snectar * SUCH DAMAGE.
25120945Snectar *
26120945Snectar * $FreeBSD: releng/10.1/usr.bin/csup/rcsfile.c 228992 2011-12-30 11:02:40Z uqs $
27120945Snectar */
28120945Snectar
29120945Snectar#include <assert.h>
30120945Snectar#include <err.h>
31120945Snectar#include <errno.h>
32120945Snectar#include <stdio.h>
33120945Snectar#include <stdlib.h>
34120945Snectar#include <string.h>
35120945Snectar
36120945Snectar#include "diff.h"
37120945Snectar#include "keyword.h"
38120945Snectar#include "misc.h"
39120945Snectar#include "proto.h"
40120945Snectar#include "queue.h"
41120945Snectar#include "rcsfile.h"
42120945Snectar#include "rcsparse.h"
43120945Snectar#include "stream.h"
44120945Snectar
45120945Snectar#define BUF_SIZE_DEFAULT	128
46120945Snectar
47120945Snectar/*
48120945Snectar * RCS parser library. This is the part of the library that handles the
49120945Snectar * importing, editing and exporting of RCS files. It currently supports only the
50120945Snectar * part of the RCS file specification that is needed for csup (for instance,
51120945Snectar * newphrases are not supported), and assumes that you can store the whole RCS
52120945Snectar * file in memory.
53120945Snectar */
54120945Snectar
55120945Snectar/*
56120945Snectar * Linked list for string tokens.
57120945Snectar */
58120945Snectarstruct string {
59120945Snectar	char *str;
60120945Snectar	STAILQ_ENTRY(string) string_next;
61120945Snectar};
62120945Snectar
63120945Snectar/*
64120945Snectar * Linked list of tags and revision numbers, in the RCS file header.
65120945Snectar */
66120945Snectarstruct tag {
67120945Snectar	char *tag;
68120945Snectar	char *revnum;
69120945Snectar	STAILQ_ENTRY(tag) tag_next;
70120945Snectar};
71120945Snectar
72120945Snectar/*
73120945Snectar * A RCS delta. The delta is identified by a revision number, and contains the
74120945Snectar * most important RCS attributes that is needed by csup. It also contains
75120945Snectar * pointers to other nodes in the RCS file delta structure.
76120945Snectar */
77120945Snectarstruct delta {
78120945Snectar	char *revdate;
79120945Snectar	char *revnum;
80120945Snectar	char *author;
81120945Snectar	char *state;
82120945Snectar	struct buf *log;
83120945Snectar	struct buf *text;
84120945Snectar	int placeholder;
85120945Snectar	struct delta *diffbase;
86120945Snectar	struct delta *prev;
87120945Snectar
88120945Snectar	LIST_ENTRY(delta) delta_next;
89120945Snectar	STAILQ_ENTRY(delta) delta_prev;
90120945Snectar	LIST_ENTRY(delta) table_next;
91120945Snectar	STAILQ_ENTRY(delta) stack_next;
92120945Snectar	LIST_HEAD(, branch) branchlist;
93120945Snectar	LIST_ENTRY(delta) branch_next_date;
94120945Snectar};
95120945Snectar
96120945Snectar/*
97120945Snectar * A branch data structure containing information about deltas in the branch as
98120945Snectar * well as a base revision number.
99120945Snectar */
100120945Snectarstruct branch {
101120945Snectar	char *revnum;
102120945Snectar	LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */
103120945Snectar	LIST_ENTRY(branch) branch_next;
104120945Snectar};
105120945Snectar
106120945Snectar/*
107120945Snectar * The rcsfile structure is the "main" structure of the RCS parser library. It
108120945Snectar * contains administrative data as well as pointers to the deltas within the
109120945Snectar * file.
110120945Snectar */
111120945Snectarstruct rcsfile {
112120945Snectar	char *name;
113120945Snectar	char *head;
114120945Snectar	char *branch;	/* Default branch. */
115120945Snectar	char *cvsroot;
116120945Snectar	char *colltag;
117120945Snectar	STAILQ_HEAD(, string) accesslist;
118120945Snectar	STAILQ_HEAD(, tag) taglist;
119120945Snectar	int strictlock;
120120945Snectar	char *comment;
121120945Snectar	int expand;
122120945Snectar	int ro;
123120945Snectar	struct branch *trunk; /* The tip delta. */
124120945Snectar
125120945Snectar	LIST_HEAD(, delta) deltatable;
126120945Snectar
127120945Snectar	char *desc;
128120945Snectar};
129120945Snectar
130120945Snectarstatic void		 rcsfile_freedelta(struct delta *);
131120945Snectarstatic void		 rcsfile_insertdelta(struct branch *, struct delta *,
132120945Snectar			     int);
133120945Snectarstatic struct delta	*rcsfile_createdelta(char *);
134120945Snectarstatic int		 rcsfile_write_deltatext(struct rcsfile *,
135120945Snectar			     struct stream *);
136120945Snectarstatic int		 rcsfile_puttext(struct rcsfile *, struct stream *,
137120945Snectar			     struct delta *, struct delta *);
138120945Snectarstatic struct branch	*rcsfile_getbranch(struct rcsfile *, char *);
139120945Snectarstatic void		 rcsfile_insertsorteddelta(struct rcsfile *,
140120945Snectar			     struct delta *);
141120945Snectarstatic struct stream 	*rcsfile_getdeltatext(struct rcsfile *, struct delta *,
142120945Snectar			     struct buf **);
143120945Snectarstatic int		 rcsdelta_writestring(char *, size_t, struct stream *);
144120945Snectarstatic void		 rcsdelta_insertbranch(struct delta *, struct branch *);
145120945Snectar
146120945Snectar/* Space formatting of RCS file. */
147120945Snectarconst char *head_space = "\t";
148120945Snectarconst char *branch_space = "\t";
149120945Snectarconst char *tag_space = "\t";
150120945Snectarconst char *date_space = "\t";
151120945Snectarconst char *auth_space = "\t";
152120945Snectarconst char *state_space = "\t";
153120945Snectarconst char *next_space = "\t";
154120945Snectarconst char *branches_space = "\t";
155120945Snectarconst char *comment_space ="\t";
156120945Snectarconst char *expand_space = "\t";
157120945Snectar
158120945Snectarvoid print_stream(struct stream *);
159120945Snectar
160120945Snectar/* Print the contents of a stream, for debugging. */
161120945Snectarvoid
162120945Snectarprint_stream(struct stream *s)
163120945Snectar{
164120945Snectar	char *line;
165120945Snectar
166120945Snectar	line = stream_getln(s, NULL);
167120945Snectar	while (line != NULL) {
168120945Snectar		lprintf(-1, "%s\n", line);
169120945Snectar		line = stream_getln(s, NULL);
170120945Snectar	}
171120945Snectar	lprintf(-1, "\n");
172120945Snectar}
173120945Snectar
174120945Snectar/*
175120945Snectar * Parse rcsfile from path and return a pointer to it.
176120945Snectar */
177120945Snectarstruct rcsfile *
178120945Snectarrcsfile_frompath(const char *path, const char *name, const char *cvsroot,
179120945Snectar    const char *colltag, int ro)
180120945Snectar{
181120945Snectar	struct rcsfile *rf;
182120945Snectar	FILE *infp;
183120945Snectar	int error;
184120945Snectar
185120945Snectar	if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
186120945Snectar		return (NULL);
187120945Snectar
188120945Snectar	rf = xmalloc(sizeof(struct rcsfile));
189120945Snectar	rf->name = xstrdup(name);
190120945Snectar	rf->cvsroot = xstrdup(cvsroot);
191120945Snectar	rf->colltag = xstrdup(colltag);
192120945Snectar
193120945Snectar	/* Initialize head branch. */
194120945Snectar	rf->trunk = xmalloc(sizeof(struct branch));
195120945Snectar	rf->trunk->revnum = xstrdup("1");
196120945Snectar	LIST_INIT(&rf->trunk->deltalist);
197120945Snectar	/* Initialize delta list. */
198120945Snectar	LIST_INIT(&rf->deltatable);
199120945Snectar	/* Initialize tag list. */
200120945Snectar	STAILQ_INIT(&rf->taglist);
201120945Snectar	/* Initialize accesslist. */
202120945Snectar	STAILQ_INIT(&rf->accesslist);
203120945Snectar
204120945Snectar	/* Initialize all fields. */
205120945Snectar	rf->head = NULL;
206120945Snectar	rf->branch = NULL;
207120945Snectar	rf->strictlock = 0;
208120945Snectar	rf->comment = NULL;
209120945Snectar	rf->expand = EXPAND_DEFAULT;
210120945Snectar	rf->desc = NULL;
211120945Snectar	rf->ro = ro;
212120945Snectar
213120945Snectar	infp = fopen(path, "r");
214120945Snectar	if (infp == NULL) {
215120945Snectar		lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
216120945Snectar		rcsfile_free(rf);
217120945Snectar		return (NULL);
218120945Snectar	}
219120945Snectar	error = rcsparse_run(rf, infp, ro);
220120945Snectar	fclose(infp);
221120945Snectar	if (error) {
222120945Snectar		lprintf(-1, "Error parsing \"%s\"\n", name);
223120945Snectar		rcsfile_free(rf);
224120945Snectar		return (NULL);
225120945Snectar	}
226120945Snectar	return (rf);
227120945Snectar}
228120945Snectar
229120945Snectar/*
230120945Snectar * Write content of rcsfile to server. Assumes we have a complete RCS file
231120945Snectar * loaded.
232120945Snectar */
233120945Snectarint
234120945Snectarrcsfile_send_details(struct rcsfile *rf, struct stream *wr)
235120945Snectar{
236120945Snectar	struct delta *d;
237120945Snectar	struct tag *t;
238120945Snectar	const char *keyword;
239120945Snectar	int error;
240120945Snectar
241120945Snectar	assert(rf != NULL);
242120945Snectar
243120945Snectar	error = proto_printf(wr, "V %s\n", rf->name);
244120945Snectar	if (error)
245120945Snectar		return(error);
246120945Snectar
247120945Snectar	/* Write default branch. */
248120945Snectar	if (rf->branch == NULL)
249120945Snectar		error = proto_printf(wr, "b\n");
250120945Snectar	else
251120945Snectar		error = proto_printf(wr, "B %s\n", rf->branch);
252120945Snectar	if (error)
253120945Snectar		return(error);
254120945Snectar
255120945Snectar	/* Write deltas to server. */
256120945Snectar	error = proto_printf(wr, "D\n");
257120945Snectar	if (error)
258120945Snectar		return(error);
259120945Snectar
260120945Snectar	LIST_FOREACH(d, &rf->deltatable, table_next) {
261120945Snectar		error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
262120945Snectar		if (error)
263120945Snectar			return(error);
264120945Snectar	}
265120945Snectar	error = proto_printf(wr, ".\n");
266120945Snectar
267120945Snectar	if (error)
268120945Snectar		return(error);
269120945Snectar	/* Write expand. */
270120945Snectar	if (rf->expand != EXPAND_DEFAULT) {
271120945Snectar		keyword = keyword_encode_expand(rf->expand);
272120945Snectar		if (keyword != NULL) {
273120945Snectar			error = proto_printf(wr, "E %s\n",
274120945Snectar			    keyword_encode_expand(rf->expand));
275120945Snectar			if (error)
276120945Snectar				return(error);
277120945Snectar		}
278120945Snectar	}
279120945Snectar
280120945Snectar	/* Write tags to server. */
281120945Snectar	error = proto_printf(wr, "T\n");
282120945Snectar	if (error)
283120945Snectar		return(error);
284120945Snectar	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
285120945Snectar		error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
286120945Snectar		if (error)
287120945Snectar			return(error);
288120945Snectar	}
289120945Snectar	error = proto_printf(wr, ".\n");
290120945Snectar	if (error)
291120945Snectar		return(error);
292120945Snectar	error = proto_printf(wr, ".\n");
293120945Snectar	return (error);
294120945Snectar}
295120945Snectar
296120945Snectar/*
297120945Snectar * Write a RCS file to disk represented by the destination stream. Keep track of
298120945Snectar * deltas with a stack and an inverted stack.
299120945Snectar */
300120945Snectarint
301120945Snectarrcsfile_write(struct rcsfile *rf, struct stream *dest)
302120945Snectar{
303120945Snectar	STAILQ_HEAD(, delta) deltastack;
304120945Snectar	STAILQ_HEAD(, delta) deltalist_inverted;
305120945Snectar	struct tag *t;
306120945Snectar	struct branch *b;
307120945Snectar	struct delta *d, *d_tmp, *d_next;
308120945Snectar	int error;
309120945Snectar
310120945Snectar	/* First write head. */
311120945Snectar	d = LIST_FIRST(&rf->trunk->deltalist);
312120945Snectar	if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0)
313120945Snectar		return (-1);
314120945Snectar
315120945Snectar	/* Write branch, if we have. */
316120945Snectar	if (rf->branch != NULL) {
317120945Snectar		if (stream_printf(dest, "branch%s%s;\n", branch_space,
318120945Snectar		    rf->branch) < 0)
319120945Snectar			return (-1);
320120945Snectar	}
321120945Snectar
322120945Snectar	/* Write access. */
323120945Snectar	if (stream_printf(dest, "access") < 0)
324120945Snectar		return (-1);
325120945Snectar#if 0
326120945Snectar	if (!STAILQ_EMPTY(&rf->accesslist)) {
327120945Snectar		/*
328120945Snectar		 * XXX: Write out access. This doesn't seem to be necessary for
329120945Snectar		 * the time being.
330120945Snectar		 */
331120945Snectar	}
332120945Snectar#endif
333120945Snectar	if (stream_printf(dest, ";\n") < 0)
334120945Snectar		return (-1);
335120945Snectar
336120945Snectar	/* Write out taglist. */
337120945Snectar	if (stream_printf(dest, "symbols") < 0)
338120945Snectar		return (-1);
339120945Snectar	if (!STAILQ_EMPTY(&rf->taglist)) {
340120945Snectar		STAILQ_FOREACH(t, &rf->taglist, tag_next) {
341120945Snectar			if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag,
342120945Snectar			    t->revnum) < 0)
343120945Snectar				return (-1);
344120945Snectar		}
345120945Snectar	}
346120945Snectar
347120945Snectar	/* Write out locks and strict. */
348120945Snectar	if (stream_printf(dest, ";\nlocks;") < 0)
349120945Snectar		return (-1);
350120945Snectar	if (rf->strictlock) {
351120945Snectar		if (stream_printf(dest, " strict;") < 0)
352120945Snectar			return (-1);
353120945Snectar	}
354120945Snectar	if (stream_printf(dest, "\n") < 0)
355120945Snectar		return (-1);
356120945Snectar
357120945Snectar	/* Write out the comment. */
358120945Snectar	if (rf->comment != NULL) {
359120945Snectar		if (stream_printf(dest, "comment%s%s;\n", comment_space,
360120945Snectar		    rf->comment) < 0)
361120945Snectar			return (-1);
362120945Snectar	}
363120945Snectar	if (rf->expand != EXPAND_DEFAULT) {
364120945Snectar		if (stream_printf(dest, "expand%s@%s@;\n", expand_space,
365120945Snectar		    keyword_encode_expand(rf->expand)) < 0)
366120945Snectar			return (-1);
367120945Snectar	}
368120945Snectar
369120945Snectar	if (stream_printf(dest, "\n\n") < 0)
370120945Snectar		return (-1);
371120945Snectar
372120945Snectar	/*
373120945Snectar	 * Write out deltas. We use a stack where we push the appropriate deltas
374120945Snectar	 * that is to be written out during the loop.
375120945Snectar	 */
376120945Snectar	STAILQ_INIT(&deltastack);
377120945Snectar	d = LIST_FIRST(&rf->trunk->deltalist);
378120945Snectar	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
379120945Snectar	while (!STAILQ_EMPTY(&deltastack)) {
380120945Snectar		d = STAILQ_FIRST(&deltastack);
381120945Snectar		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
382120945Snectar		/* Do not write out placeholders just to be safe. */
383120945Snectar		if (d->placeholder)
384120945Snectar			continue;
385120945Snectar		if (stream_printf(dest, "%s\n", d->revnum) < 0)
386120945Snectar			return (-1);
387120945Snectar		if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
388120945Snectar		    date_space, d->revdate, auth_space, d->author,
389120945Snectar		    state_space) < 0)
390120945Snectar			return (-1);
391120945Snectar		if (d->state != NULL) {
392120945Snectar			if (stream_printf(dest, " %s", d->state) < 0)
393120945Snectar				return (-1);
394120945Snectar		}
395120945Snectar		if (stream_printf(dest, ";\nbranches") < 0)
396120945Snectar			return (-1);
397120945Snectar		/*
398120945Snectar		 * Write out our branches. Add them to a reversed list for use
399120945Snectar		 * later when we write out the text.
400120945Snectar		 */
401120945Snectar		STAILQ_INIT(&deltalist_inverted);
402120945Snectar		LIST_FOREACH(b, &d->branchlist, branch_next) {
403120945Snectar			d_tmp = LIST_FIRST(&b->deltalist);
404120945Snectar			STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);
405120945Snectar			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
406120945Snectar		}
407120945Snectar
408120945Snectar		/* Push branch heads on stack. */
409120945Snectar		STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
410120945Snectar			if (d_tmp == NULL) {
411120945Snectar				lprintf(2, "Empty branch!\n");
412120945Snectar				return (-1);
413120945Snectar			}
414120945Snectar			if (stream_printf(dest, "\n%s%s", branches_space,
415120945Snectar			    d_tmp->revnum) < 0)
416120945Snectar				return (-1);
417120945Snectar		}
418120945Snectar
419120945Snectar		if (stream_printf(dest, ";\nnext%s", next_space) < 0)
420120945Snectar			return (-1);
421120945Snectar		/* Push next delta on stack. */
422120945Snectar		d_next = LIST_NEXT(d, delta_next);
423120945Snectar		if (d_next != NULL) {
424120945Snectar			if (stream_printf(dest, "%s", d_next->revnum) < 0)
425120945Snectar				return (-1);
426120945Snectar			STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
427120945Snectar		}
428120945Snectar		if (stream_printf(dest, ";\n\n") < 0)
429120945Snectar			return (-1);
430120945Snectar	}
431120945Snectar	/* Write out desc. */
432120945Snectar	if (stream_printf(dest, "\ndesc\n@@") < 0)
433120945Snectar		return (-1);
434120945Snectar	d = LIST_FIRST(&rf->trunk->deltalist);
435120945Snectar
436120945Snectar	/* Write out deltatexts. */
437120945Snectar	error = rcsfile_write_deltatext(rf, dest);
438120945Snectar	if (stream_printf(dest, "\n") < 0)
439120945Snectar		return (-1);
440120945Snectar	return (error);
441120945Snectar}
442120945Snectar
443120945Snectar/*
444120945Snectar * Write out deltatexts of a delta and it's subbranches recursively.
445120945Snectar */
446120945Snectarint
447120945Snectarrcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
448120945Snectar{
449120945Snectar	STAILQ_HEAD(, delta) deltastack;
450120945Snectar	LIST_HEAD(, delta) branchlist_datesorted;
451120945Snectar	struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
452120945Snectar	struct stream *in;
453120945Snectar	struct branch *b;
454120945Snectar	size_t size;
455120945Snectar	char *line;
456120945Snectar	int error;
457120945Snectar
458120945Snectar	error = 0;
459120945Snectar	STAILQ_INIT(&deltastack);
460120945Snectar	d = LIST_FIRST(&rf->trunk->deltalist);
461120945Snectar	d->prev = NULL;
462120945Snectar	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
463120945Snectar	while (!STAILQ_EMPTY(&deltastack)) {
464120945Snectar		d = STAILQ_FIRST(&deltastack);
465120945Snectar		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
466120945Snectar		/* Do not write out placeholders just to be safe. */
467120945Snectar		if (d->placeholder)
468120945Snectar			return (0);
469120945Snectar		if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0)
470120945Snectar			return (-1);
471120945Snectar		if (stream_printf(dest, "log\n@") < 0)
472120945Snectar			return (-1);
473120945Snectar		in = stream_open_buf(d->log);
474120945Snectar		line = stream_getln(in, &size);
475120945Snectar		while (line != NULL) {
476120945Snectar			if (stream_write(dest, line, size) == -1)
477120945Snectar				return (-1);
478120945Snectar			line = stream_getln(in, &size);
479120945Snectar		}
480120945Snectar		stream_close(in);
481120945Snectar		if (stream_printf(dest, "@\ntext\n@") < 0)
482120945Snectar			return (-1);
483120945Snectar		error = rcsfile_puttext(rf, dest, d, d->prev);
484120945Snectar		if (error)
485120945Snectar			return (error);
486120945Snectar		if (stream_printf(dest, "@") < 0)
487120945Snectar			return (-1);
488120945Snectar
489120945Snectar		LIST_INIT(&branchlist_datesorted);
490120945Snectar		d_next = LIST_NEXT(d, delta_next);
491120945Snectar		if (d_next != NULL) {
492120945Snectar			d_next->prev = d;
493120945Snectar			/*
494120945Snectar			 * If it's trunk, treat it like the oldest, if not treat
495120945Snectar			 * it like a child.
496120945Snectar			 */
497120945Snectar			if (rcsrev_istrunk(d_next->revnum))
498120945Snectar				STAILQ_INSERT_HEAD(&deltastack, d_next,
499120945Snectar				    stack_next);
500120945Snectar			else
501120945Snectar				LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
502120945Snectar				    branch_next_date);
503120945Snectar		}
504120945Snectar
505120945Snectar		/*
506120945Snectar		 * First, we need to sort our branches based on their date to
507120945Snectar		 * take into account some self-hacked RCS files.
508120945Snectar		 */
509120945Snectar		LIST_FOREACH(b, &d->branchlist, branch_next) {
510120945Snectar			d_tmp = LIST_FIRST(&b->deltalist);
511120945Snectar			if (LIST_EMPTY(&branchlist_datesorted)) {
512120945Snectar				LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp,
513120945Snectar				    branch_next_date);
514120945Snectar				continue;
515120945Snectar			}
516120945Snectar
517120945Snectar			d_tmp2 = LIST_FIRST(&branchlist_datesorted);
518120945Snectar			if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) {
519120945Snectar				LIST_INSERT_BEFORE(d_tmp2, d_tmp,
520120945Snectar				    branch_next_date);
521120945Snectar				continue;
522120945Snectar			}
523120945Snectar			while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
524120945Snectar			    != NULL) {
525120945Snectar				if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
526120945Snectar				    <= 0)
527120945Snectar					break;
528120945Snectar				d_tmp2 = d_tmp3;
529120945Snectar			}
530120945Snectar			LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
531120945Snectar		}
532120945Snectar		/*
533120945Snectar		 * Invert the deltalist of a branch, since we're writing them
534120945Snectar		 * the opposite way.
535120945Snectar		 */
536120945Snectar		LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
537120945Snectar                        d_tmp->prev = d;
538120945Snectar			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
539120945Snectar		}
540120945Snectar	}
541120945Snectar	return (0);
542120945Snectar}
543120945Snectar
544120945Snectar/*
545120945Snectar * Generates text given a delta and a diffbase.
546120945Snectar */
547120945Snectarstatic int
548120945Snectarrcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
549120945Snectar    struct delta *diffbase)
550120945Snectar{
551120945Snectar	struct stream *in, *rd, *orig;
552120945Snectar	struct keyword *k;
553120945Snectar	struct diffinfo dibuf, *di;
554120945Snectar	struct buf *b;
555120945Snectar	size_t size;
556120945Snectar	char *line;
557120945Snectar	int error;
558120945Snectar
559120945Snectar	di = &dibuf;
560120945Snectar	b = NULL;
561120945Snectar	error = 0;
562120945Snectar
563120945Snectar	/* Write if the diffbase is the previous */
564120945Snectar	if (d->diffbase == diffbase) {
565120945Snectar
566120945Snectar		/* Write out the text. */
567120945Snectar		in = stream_open_buf(d->text);
568120945Snectar		line = stream_getln(in, &size);
569120945Snectar		while (line != NULL) {
570120945Snectar			if (stream_write(dest, line, size) == -1) {
571120945Snectar				error = -1;
572120945Snectar				goto cleanup;
573120945Snectar			}
574120945Snectar			line = stream_getln(in, &size);
575120945Snectar		}
576120945Snectar		stream_close(in);
577120945Snectar	/* We need to apply diff to produce text, this is probably HEAD. */
578120945Snectar	} else if (diffbase == NULL) {
579120945Snectar		/* Apply diff. */
580120945Snectar		orig = rcsfile_getdeltatext(rf, d, &b);
581120945Snectar		if (orig == NULL) {
582120945Snectar			error = -1;
583120945Snectar			goto cleanup;
584120945Snectar		}
585120945Snectar		line = stream_getln(orig, &size);
586120945Snectar		while (line != NULL) {
587120945Snectar			if (stream_write(dest, line, size) == -1) {
588120945Snectar				error = -1;
589120945Snectar				goto cleanup;
590120945Snectar			}
591120945Snectar			line = stream_getln(orig, &size);
592120945Snectar		}
593120945Snectar		stream_close(orig);
594120945Snectar	/*
595120945Snectar	 * A new head was probably added, and now the previous HEAD must be
596120945Snectar	 * changed to include the diff instead.
597120945Snectar	 */
598120945Snectar	} else if (diffbase->diffbase == d) {
599120945Snectar		/* Get reverse diff. */
600120945Snectar		orig = rcsfile_getdeltatext(rf, d, &b);
601120945Snectar		if (orig == NULL) {
602120945Snectar			error = -1;
603120945Snectar			goto cleanup;
604120945Snectar		}
605120945Snectar		di->di_rcsfile = rf->name;
606120945Snectar		di->di_cvsroot = rf->cvsroot;
607120945Snectar		di->di_revnum = d->revnum;
608120945Snectar		di->di_revdate = d->revdate;
609120945Snectar		di->di_author = d->author;
610120945Snectar		di->di_tag = rf->colltag;
611120945Snectar		di->di_state = d->state;
612120945Snectar		di->di_expand = EXPAND_OLD;
613120945Snectar		k = keyword_new();
614120945Snectar
615120945Snectar		rd = stream_open_buf(diffbase->text);
616120945Snectar		error = diff_reverse(rd, orig, dest, k, di);
617120945Snectar		if (error) {
618120945Snectar			lprintf(-1, "Error applying reverse diff: %d\n", error);
619120945Snectar			goto cleanup;
620120945Snectar		}
621120945Snectar		keyword_free(k);
622120945Snectar		stream_close(rd);
623120945Snectar		stream_close(orig);
624120945Snectar	}
625120945Snectarcleanup:
626120945Snectar	if (b != NULL)
627120945Snectar		buf_free(b);
628120945Snectar	return (error);
629120945Snectar}
630120945Snectar
631120945Snectar/*
632120945Snectar * Return a stream with an applied diff of a delta.
633120945Snectar * XXX: extra overhead on the last apply. Could write directly to file, but
634120945Snectar * makes things complicated though.
635120945Snectar */
636120945Snectarstatic struct stream *
637120945Snectarrcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
638120945Snectar{
639120945Snectar	struct diffinfo dibuf, *di;
640120945Snectar	struct stream *orig, *dest, *rd;
641120945Snectar	struct buf *buf_orig;
642120945Snectar	struct keyword *k;
643120945Snectar	int error;
644120945Snectar
645120945Snectar	buf_orig = NULL;
646120945Snectar	error = 0;
647120945Snectar
648120945Snectar	/*
649120945Snectar	 * If diffbase is NULL or we are head (the old head), we have a normal
650120945Snectar	 * complete deltatext.
651120945Snectar	 */
652120945Snectar	if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
653120945Snectar		orig = stream_open_buf(d->text);
654120945Snectar		return (orig);
655120945Snectar	}
656120945Snectar
657120945Snectar	di = &dibuf;
658120945Snectar	/* If not, we need to apply our diff to that of our diffbase. */
659120945Snectar	orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
660120945Snectar	if (orig == NULL)
661120945Snectar		return (NULL);
662120945Snectar
663120945Snectar	/*
664120945Snectar	 * Now that we are sure we have a complete deltatext in ret, let's apply
665120945Snectar	 * our diff to it.
666233294Sstas	 */
667120945Snectar	*buf_dest = buf_new(BUF_SIZE_DEFAULT);
668120945Snectar	dest = stream_open_buf(*buf_dest);
669120945Snectar
670120945Snectar	di->di_rcsfile = rf->name;
671120945Snectar	di->di_cvsroot = rf->cvsroot;
672120945Snectar	di->di_revnum = d->revnum;
673120945Snectar	di->di_revdate = d->revdate;
674120945Snectar	di->di_author = d->author;
675120945Snectar	di->di_tag = rf->colltag;
676120945Snectar	di->di_state = d->state;
677120945Snectar	di->di_expand = EXPAND_OLD;
678120945Snectar	rd = stream_open_buf(d->text);
679120945Snectar	k = keyword_new();
680120945Snectar	error = diff_apply(rd, orig, dest, k, di, 0);
681120945Snectar	stream_flush(dest);
682120945Snectar	stream_close(rd);
683120945Snectar	stream_close(orig);
684120945Snectar	stream_close(dest);
685120945Snectar	keyword_free(k);
686120945Snectar	if (buf_orig != NULL)
687120945Snectar		buf_free(buf_orig);
688120945Snectar	if (error) {
689120945Snectar		lprintf(-1, "Error applying diff: %d\n", error);
690120945Snectar		return (NULL);
691120945Snectar	}
692120945Snectar
693120945Snectar	/* Now reopen the stream for the reading. */
694120945Snectar	dest = stream_open_buf(*buf_dest);
695120945Snectar	return (dest);
696120945Snectar}
697120945Snectar
698120945Snectar/* Print content of rcsfile. Useful for debugging. */
699120945Snectarvoid
700120945Snectarrcsfile_print(struct rcsfile *rf)
701120945Snectar{
702120945Snectar	struct delta *d;
703120945Snectar	struct tag *t;
704120945Snectar	struct string *s;
705120945Snectar	struct stream *in;
706120945Snectar	char *line;
707120945Snectar
708120945Snectar	lprintf(1, "\n");
709120945Snectar	if (rf->name != NULL)
710120945Snectar		lprintf(1, "name: '%s'\n", rf->name);
711120945Snectar	if (rf->head != NULL)
712120945Snectar		lprintf(1, "head: '%s'\n", rf->head);
713120945Snectar	if (rf->branch != NULL)
714120945Snectar		lprintf(1, "branch: '%s'\n", rf->branch);
715120945Snectar	lprintf(1, "Access: ");
716120945Snectar	STAILQ_FOREACH(s, &rf->accesslist, string_next)
717120945Snectar		lprintf(1, "'%s' ", s->str);
718120945Snectar	lprintf(1, "\n");
719120945Snectar
720120945Snectar	/* Print all tags. */
721120945Snectar	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
722120945Snectar		lprintf(1, "Tag: ");
723120945Snectar		if (t->tag != NULL)
724120945Snectar			lprintf(1, "name: %s ", t->tag);
725120945Snectar		if (t->revnum != NULL)
726120945Snectar			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. */
765void
766rcsfile_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 */
829static void
830rcsfile_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. */
862void
863rcsfile_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. */
873void
874rcsfile_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. */
886void
887rcsfile_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 */
902void
903rcsfile_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. */
915void
916rcsfile_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 */
935struct delta *
936rcsfile_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. */
948void
949rcsfile_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. */
991static struct delta *
992rcsfile_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. */
1010struct delta *
1011rcsfile_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. */
1066void
1067rcsfile_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 */
1183static struct branch *
1184rcsfile_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. */
1205static void
1206rcsdelta_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. */
1230static void
1231rcsfile_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 */
1260static void
1261rcsfile_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. */
1298int
1299rcsdelta_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. */
1316int
1317rcsdelta_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. */
1335int
1336rcsdelta_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. */
1349int
1350rcsdelta_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
1362static int
1363rcsdelta_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. */
1386void
1387rcsdelta_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. */
1400void
1401rcsdelta_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. */
1408void
1409rcsdelta_truncatetext(struct delta *d, off_t offset)
1410{
1411
1412	stream_truncate_buf(d->text, offset);
1413}
1414