1184054Slulf/*-
2186743Slulf * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
3184054Slulf * All rights reserved.
4184054Slulf *
5184054Slulf * Redistribution and use in source and binary forms, with or without
6184054Slulf * modification, are permitted provided that the following conditions
7184054Slulf * are met:
8184054Slulf * 1. Redistributions of source code must retain the above copyright
9184054Slulf *    notice, this list of conditions and the following disclaimer.
10184054Slulf * 2. Redistributions in binary form must reproduce the above copyright
11184054Slulf *    notice, this list of conditions and the following disclaimer in the
12184054Slulf *    documentation and/or other materials provided with the distribution.
13184054Slulf *
14184054Slulf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184054Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184054Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184054Slulf * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184054Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184054Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184054Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184054Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184054Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184054Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184054Slulf * SUCH DAMAGE.
25184054Slulf *
26184054Slulf * $FreeBSD$
27184054Slulf */
28184054Slulf
29184054Slulf#include <assert.h>
30185134Slulf#include <err.h>
31185134Slulf#include <errno.h>
32184054Slulf#include <stdio.h>
33184054Slulf#include <stdlib.h>
34184054Slulf#include <string.h>
35185134Slulf
36184054Slulf#include "diff.h"
37185134Slulf#include "keyword.h"
38184054Slulf#include "misc.h"
39185134Slulf#include "proto.h"
40185134Slulf#include "queue.h"
41184054Slulf#include "rcsfile.h"
42184054Slulf#include "rcsparse.h"
43184054Slulf#include "stream.h"
44184054Slulf
45185134Slulf#define BUF_SIZE_DEFAULT	128
46185134Slulf
47184054Slulf/*
48184054Slulf * RCS parser library. This is the part of the library that handles the
49184054Slulf * importing, editing and exporting of RCS files. It currently supports only the
50184054Slulf * part of the RCS file specification that is needed for csup (for instance,
51184054Slulf * newphrases are not supported), and assumes that you can store the whole RCS
52184054Slulf * file in memory.
53184054Slulf */
54184054Slulf
55184054Slulf/*
56184054Slulf * Linked list for string tokens.
57184054Slulf */
58184054Slulfstruct string {
59184054Slulf	char *str;
60184054Slulf	STAILQ_ENTRY(string) string_next;
61184054Slulf};
62184054Slulf
63184054Slulf/*
64184054Slulf * Linked list of tags and revision numbers, in the RCS file header.
65184054Slulf */
66184054Slulfstruct tag {
67184054Slulf	char *tag;
68184054Slulf	char *revnum;
69184054Slulf	STAILQ_ENTRY(tag) tag_next;
70184054Slulf};
71184054Slulf
72184054Slulf/*
73184054Slulf * A RCS delta. The delta is identified by a revision number, and contains the
74184054Slulf * most important RCS attributes that is needed by csup. It also contains
75184054Slulf * pointers to other nodes in the RCS file delta structure.
76184054Slulf */
77184054Slulfstruct delta {
78184054Slulf	char *revdate;
79184054Slulf	char *revnum;
80184054Slulf	char *author;
81184054Slulf	char *state;
82184054Slulf	struct buf *log;
83184054Slulf	struct buf *text;
84184054Slulf	int placeholder;
85184054Slulf	struct delta *diffbase;
86184054Slulf	struct delta *prev;
87184054Slulf
88184054Slulf	LIST_ENTRY(delta) delta_next;
89184054Slulf	STAILQ_ENTRY(delta) delta_prev;
90184054Slulf	LIST_ENTRY(delta) table_next;
91184054Slulf	STAILQ_ENTRY(delta) stack_next;
92186727Slulf	LIST_HEAD(, branch) branchlist;
93184054Slulf	LIST_ENTRY(delta) branch_next_date;
94184054Slulf};
95184054Slulf
96184054Slulf/*
97184054Slulf * A branch data structure containing information about deltas in the branch as
98184054Slulf * well as a base revision number.
99184054Slulf */
100184054Slulfstruct branch {
101184054Slulf	char *revnum;
102184054Slulf	LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */
103186727Slulf	LIST_ENTRY(branch) branch_next;
104184054Slulf};
105184054Slulf
106184054Slulf/*
107184054Slulf * The rcsfile structure is the "main" structure of the RCS parser library. It
108184054Slulf * contains administrative data as well as pointers to the deltas within the
109184054Slulf * file.
110184054Slulf */
111184054Slulfstruct rcsfile {
112184054Slulf	char *name;
113184054Slulf	char *head;
114184054Slulf	char *branch;	/* Default branch. */
115184054Slulf	char *cvsroot;
116184054Slulf	char *colltag;
117184054Slulf	STAILQ_HEAD(, string) accesslist;
118184054Slulf	STAILQ_HEAD(, tag) taglist;
119184054Slulf	int strictlock;
120184054Slulf	char *comment;
121184054Slulf	int expand;
122186700Slulf	int ro;
123184054Slulf	struct branch *trunk; /* The tip delta. */
124184054Slulf
125184054Slulf	LIST_HEAD(, delta) deltatable;
126184054Slulf
127184054Slulf	char *desc;
128184054Slulf};
129184054Slulf
130184054Slulfstatic void		 rcsfile_freedelta(struct delta *);
131184054Slulfstatic void		 rcsfile_insertdelta(struct branch *, struct delta *,
132184054Slulf			     int);
133184054Slulfstatic struct delta	*rcsfile_createdelta(char *);
134184054Slulfstatic int		 rcsfile_write_deltatext(struct rcsfile *,
135184054Slulf			     struct stream *);
136184054Slulfstatic int		 rcsfile_puttext(struct rcsfile *, struct stream *,
137184054Slulf			     struct delta *, struct delta *);
138184054Slulfstatic struct branch	*rcsfile_getbranch(struct rcsfile *, char *);
139184054Slulfstatic void		 rcsfile_insertsorteddelta(struct rcsfile *,
140184054Slulf			     struct delta *);
141184054Slulfstatic struct stream 	*rcsfile_getdeltatext(struct rcsfile *, struct delta *,
142184054Slulf			     struct buf **);
143190422Slulfstatic int		 rcsdelta_writestring(char *, size_t, struct stream *);
144186727Slulfstatic void		 rcsdelta_insertbranch(struct delta *, struct branch *);
145184054Slulf
146184054Slulf/* Space formatting of RCS file. */
147184054Slulfconst char *head_space = "\t";
148184054Slulfconst char *branch_space = "\t";
149184054Slulfconst char *tag_space = "\t";
150184054Slulfconst char *date_space = "\t";
151184054Slulfconst char *auth_space = "\t";
152184054Slulfconst char *state_space = "\t";
153184054Slulfconst char *next_space = "\t";
154184054Slulfconst char *branches_space = "\t";
155184054Slulfconst char *comment_space ="\t";
156186700Slulfconst char *expand_space = "\t";
157184054Slulf
158184054Slulfvoid print_stream(struct stream *);
159184054Slulf
160184054Slulf/* Print the contents of a stream, for debugging. */
161184054Slulfvoid
162184054Slulfprint_stream(struct stream *s)
163184054Slulf{
164184054Slulf	char *line;
165184054Slulf
166184054Slulf	line = stream_getln(s, NULL);
167184054Slulf	while (line != NULL) {
168185134Slulf		lprintf(-1, "%s\n", line);
169184054Slulf		line = stream_getln(s, NULL);
170184054Slulf	}
171185134Slulf	lprintf(-1, "\n");
172184054Slulf}
173184054Slulf
174184054Slulf/*
175184054Slulf * Parse rcsfile from path and return a pointer to it.
176184054Slulf */
177184054Slulfstruct rcsfile *
178216542Slulfrcsfile_frompath(const char *path, const char *name, const char *cvsroot,
179216542Slulf    const char *colltag, int ro)
180184054Slulf{
181185134Slulf	struct rcsfile *rf;
182184054Slulf	FILE *infp;
183184054Slulf	int error;
184184054Slulf
185184054Slulf	if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
186184054Slulf		return (NULL);
187184054Slulf
188184054Slulf	rf = xmalloc(sizeof(struct rcsfile));
189184054Slulf	rf->name = xstrdup(name);
190184054Slulf	rf->cvsroot = xstrdup(cvsroot);
191184054Slulf	rf->colltag = xstrdup(colltag);
192184054Slulf
193184054Slulf	/* Initialize head branch. */
194184054Slulf	rf->trunk = xmalloc(sizeof(struct branch));
195185134Slulf	rf->trunk->revnum = xstrdup("1");
196184054Slulf	LIST_INIT(&rf->trunk->deltalist);
197184054Slulf	/* Initialize delta list. */
198184054Slulf	LIST_INIT(&rf->deltatable);
199184054Slulf	/* Initialize tag list. */
200184054Slulf	STAILQ_INIT(&rf->taglist);
201184054Slulf	/* Initialize accesslist. */
202184054Slulf	STAILQ_INIT(&rf->accesslist);
203184054Slulf
204184054Slulf	/* Initialize all fields. */
205184054Slulf	rf->head = NULL;
206184054Slulf	rf->branch = NULL;
207184054Slulf	rf->strictlock = 0;
208184054Slulf	rf->comment = NULL;
209185592Slulf	rf->expand = EXPAND_DEFAULT;
210184054Slulf	rf->desc = NULL;
211186700Slulf	rf->ro = ro;
212184054Slulf
213184054Slulf	infp = fopen(path, "r");
214184054Slulf	if (infp == NULL) {
215184054Slulf		lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
216184054Slulf		rcsfile_free(rf);
217184054Slulf		return (NULL);
218184054Slulf	}
219186700Slulf	error = rcsparse_run(rf, infp, ro);
220184054Slulf	fclose(infp);
221184054Slulf	if (error) {
222184054Slulf		lprintf(-1, "Error parsing \"%s\"\n", name);
223184054Slulf		rcsfile_free(rf);
224184054Slulf		return (NULL);
225184054Slulf	}
226184054Slulf	return (rf);
227184054Slulf}
228184054Slulf
229184054Slulf/*
230184054Slulf * Write content of rcsfile to server. Assumes we have a complete RCS file
231184054Slulf * loaded.
232184054Slulf */
233184054Slulfint
234184054Slulfrcsfile_send_details(struct rcsfile *rf, struct stream *wr)
235184054Slulf{
236184054Slulf	struct delta *d;
237184054Slulf	struct tag *t;
238185811Slulf	const char *keyword;
239184054Slulf	int error;
240184054Slulf
241184054Slulf	assert(rf != NULL);
242184054Slulf
243184054Slulf	error = proto_printf(wr, "V %s\n", rf->name);
244184054Slulf	if (error)
245184054Slulf		return(error);
246184054Slulf
247184054Slulf	/* Write default branch. */
248184054Slulf	if (rf->branch == NULL)
249184054Slulf		error = proto_printf(wr, "b\n");
250184054Slulf	else
251184054Slulf		error = proto_printf(wr, "B %s\n", rf->branch);
252184054Slulf	if (error)
253184054Slulf		return(error);
254184054Slulf
255184054Slulf	/* Write deltas to server. */
256184054Slulf	error = proto_printf(wr, "D\n");
257184054Slulf	if (error)
258184054Slulf		return(error);
259184054Slulf
260184054Slulf	LIST_FOREACH(d, &rf->deltatable, table_next) {
261184054Slulf		error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
262184054Slulf		if (error)
263184054Slulf			return(error);
264184054Slulf	}
265184054Slulf	error = proto_printf(wr, ".\n");
266184054Slulf
267184054Slulf	if (error)
268184054Slulf		return(error);
269184054Slulf	/* Write expand. */
270185592Slulf	if (rf->expand != EXPAND_DEFAULT) {
271185811Slulf		keyword = keyword_encode_expand(rf->expand);
272185811Slulf		if (keyword != NULL) {
273185811Slulf			error = proto_printf(wr, "E %s\n",
274185811Slulf			    keyword_encode_expand(rf->expand));
275185811Slulf			if (error)
276185811Slulf				return(error);
277185811Slulf		}
278184054Slulf	}
279184054Slulf
280184054Slulf	/* Write tags to server. */
281184054Slulf	error = proto_printf(wr, "T\n");
282184054Slulf	if (error)
283184054Slulf		return(error);
284184054Slulf	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
285184054Slulf		error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
286184054Slulf		if (error)
287184054Slulf			return(error);
288184054Slulf	}
289184054Slulf	error = proto_printf(wr, ".\n");
290184054Slulf	if (error)
291184054Slulf		return(error);
292184054Slulf	error = proto_printf(wr, ".\n");
293184054Slulf	return (error);
294184054Slulf}
295184054Slulf
296184054Slulf/*
297184054Slulf * Write a RCS file to disk represented by the destination stream. Keep track of
298184054Slulf * deltas with a stack and an inverted stack.
299184054Slulf */
300184054Slulfint
301184054Slulfrcsfile_write(struct rcsfile *rf, struct stream *dest)
302184054Slulf{
303184054Slulf	STAILQ_HEAD(, delta) deltastack;
304184054Slulf	STAILQ_HEAD(, delta) deltalist_inverted;
305184054Slulf	struct tag *t;
306184054Slulf	struct branch *b;
307184054Slulf	struct delta *d, *d_tmp, *d_next;
308184054Slulf	int error;
309184054Slulf
310184054Slulf	/* First write head. */
311184054Slulf	d = LIST_FIRST(&rf->trunk->deltalist);
312190422Slulf	if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0)
313190422Slulf		return (-1);
314184054Slulf
315184054Slulf	/* Write branch, if we have. */
316190422Slulf	if (rf->branch != NULL) {
317190422Slulf		if (stream_printf(dest, "branch%s%s;\n", branch_space,
318190422Slulf		    rf->branch) < 0)
319190422Slulf			return (-1);
320190422Slulf	}
321184054Slulf
322184054Slulf	/* Write access. */
323190422Slulf	if (stream_printf(dest, "access") < 0)
324190422Slulf		return (-1);
325184054Slulf#if 0
326184054Slulf	if (!STAILQ_EMPTY(&rf->accesslist)) {
327184054Slulf		/*
328184054Slulf		 * XXX: Write out access. This doesn't seem to be necessary for
329184054Slulf		 * the time being.
330184054Slulf		 */
331184054Slulf	}
332184054Slulf#endif
333190422Slulf	if (stream_printf(dest, ";\n") < 0)
334190422Slulf		return (-1);
335184054Slulf
336184054Slulf	/* Write out taglist. */
337190422Slulf	if (stream_printf(dest, "symbols") < 0)
338190422Slulf		return (-1);
339184054Slulf	if (!STAILQ_EMPTY(&rf->taglist)) {
340184054Slulf		STAILQ_FOREACH(t, &rf->taglist, tag_next) {
341190422Slulf			if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag,
342190422Slulf			    t->revnum) < 0)
343190422Slulf				return (-1);
344184054Slulf		}
345184054Slulf	}
346184054Slulf
347184054Slulf	/* Write out locks and strict. */
348190422Slulf	if (stream_printf(dest, ";\nlocks;") < 0)
349190422Slulf		return (-1);
350190422Slulf	if (rf->strictlock) {
351190422Slulf		if (stream_printf(dest, " strict;") < 0)
352190422Slulf			return (-1);
353190422Slulf	}
354190422Slulf	if (stream_printf(dest, "\n") < 0)
355190422Slulf		return (-1);
356184054Slulf
357184054Slulf	/* Write out the comment. */
358190422Slulf	if (rf->comment != NULL) {
359190422Slulf		if (stream_printf(dest, "comment%s%s;\n", comment_space,
360190422Slulf		    rf->comment) < 0)
361190422Slulf			return (-1);
362190422Slulf	}
363190422Slulf	if (rf->expand != EXPAND_DEFAULT) {
364190422Slulf		if (stream_printf(dest, "expand%s@%s@;\n", expand_space,
365190422Slulf		    keyword_encode_expand(rf->expand)) < 0)
366190422Slulf			return (-1);
367190422Slulf	}
368184054Slulf
369190422Slulf	if (stream_printf(dest, "\n\n") < 0)
370190422Slulf		return (-1);
371184054Slulf
372185134Slulf	/*
373184054Slulf	 * Write out deltas. We use a stack where we push the appropriate deltas
374184054Slulf	 * that is to be written out during the loop.
375184054Slulf	 */
376184054Slulf	STAILQ_INIT(&deltastack);
377184054Slulf	d = LIST_FIRST(&rf->trunk->deltalist);
378184054Slulf	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
379184054Slulf	while (!STAILQ_EMPTY(&deltastack)) {
380184054Slulf		d = STAILQ_FIRST(&deltastack);
381184054Slulf		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
382184054Slulf		/* Do not write out placeholders just to be safe. */
383184054Slulf		if (d->placeholder)
384184054Slulf			continue;
385190422Slulf		if (stream_printf(dest, "%s\n", d->revnum) < 0)
386190422Slulf			return (-1);
387190422Slulf		if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
388184054Slulf		    date_space, d->revdate, auth_space, d->author,
389190422Slulf		    state_space) < 0)
390190422Slulf			return (-1);
391190422Slulf		if (d->state != NULL) {
392190422Slulf			if (stream_printf(dest, " %s", d->state) < 0)
393190422Slulf				return (-1);
394190422Slulf		}
395190422Slulf		if (stream_printf(dest, ";\nbranches") < 0)
396190422Slulf			return (-1);
397184054Slulf		/*
398184054Slulf		 * Write out our branches. Add them to a reversed list for use
399184054Slulf		 * later when we write out the text.
400184054Slulf		 */
401184054Slulf		STAILQ_INIT(&deltalist_inverted);
402186727Slulf		LIST_FOREACH(b, &d->branchlist, branch_next) {
403184054Slulf			d_tmp = LIST_FIRST(&b->deltalist);
404184054Slulf			STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);
405184054Slulf			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
406184054Slulf		}
407184054Slulf
408184054Slulf		/* Push branch heads on stack. */
409184054Slulf		STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
410190422Slulf			if (d_tmp == NULL) {
411190422Slulf				lprintf(2, "Empty branch!\n");
412190422Slulf				return (-1);
413190422Slulf			}
414190422Slulf			if (stream_printf(dest, "\n%s%s", branches_space,
415190422Slulf			    d_tmp->revnum) < 0)
416190422Slulf				return (-1);
417184054Slulf		}
418184054Slulf
419190422Slulf		if (stream_printf(dest, ";\nnext%s", next_space) < 0)
420190422Slulf			return (-1);
421184054Slulf		/* Push next delta on stack. */
422184054Slulf		d_next = LIST_NEXT(d, delta_next);
423184054Slulf		if (d_next != NULL) {
424190422Slulf			if (stream_printf(dest, "%s", d_next->revnum) < 0)
425190422Slulf				return (-1);
426184054Slulf			STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
427184054Slulf		}
428190422Slulf		if (stream_printf(dest, ";\n\n") < 0)
429190422Slulf			return (-1);
430184054Slulf	}
431184054Slulf	/* Write out desc. */
432190422Slulf	if (stream_printf(dest, "\ndesc\n@@") < 0)
433190422Slulf		return (-1);
434184054Slulf	d = LIST_FIRST(&rf->trunk->deltalist);
435184054Slulf
436184054Slulf	/* Write out deltatexts. */
437184054Slulf	error = rcsfile_write_deltatext(rf, dest);
438190422Slulf	if (stream_printf(dest, "\n") < 0)
439190422Slulf		return (-1);
440184054Slulf	return (error);
441184054Slulf}
442184054Slulf
443184054Slulf/*
444184054Slulf * Write out deltatexts of a delta and it's subbranches recursively.
445184054Slulf */
446184054Slulfint
447184054Slulfrcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
448184054Slulf{
449184054Slulf	STAILQ_HEAD(, delta) deltastack;
450184054Slulf	LIST_HEAD(, delta) branchlist_datesorted;
451185134Slulf	struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
452184054Slulf	struct stream *in;
453184054Slulf	struct branch *b;
454185134Slulf	size_t size;
455184054Slulf	char *line;
456184054Slulf	int error;
457184054Slulf
458184054Slulf	error = 0;
459184054Slulf	STAILQ_INIT(&deltastack);
460184054Slulf	d = LIST_FIRST(&rf->trunk->deltalist);
461184054Slulf	d->prev = NULL;
462184054Slulf	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
463184054Slulf	while (!STAILQ_EMPTY(&deltastack)) {
464184054Slulf		d = STAILQ_FIRST(&deltastack);
465184054Slulf		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
466184054Slulf		/* Do not write out placeholders just to be safe. */
467184054Slulf		if (d->placeholder)
468184054Slulf			return (0);
469190422Slulf		if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0)
470190422Slulf			return (-1);
471190422Slulf		if (stream_printf(dest, "log\n@") < 0)
472190422Slulf			return (-1);
473184054Slulf		in = stream_open_buf(d->log);
474184054Slulf		line = stream_getln(in, &size);
475184054Slulf		while (line != NULL) {
476190422Slulf			if (stream_write(dest, line, size) == -1)
477190422Slulf				return (-1);
478184054Slulf			line = stream_getln(in, &size);
479184054Slulf		}
480184054Slulf		stream_close(in);
481190422Slulf		if (stream_printf(dest, "@\ntext\n@") < 0)
482190422Slulf			return (-1);
483184054Slulf		error = rcsfile_puttext(rf, dest, d, d->prev);
484184054Slulf		if (error)
485184054Slulf			return (error);
486190422Slulf		if (stream_printf(dest, "@") < 0)
487190422Slulf			return (-1);
488184054Slulf
489184054Slulf		LIST_INIT(&branchlist_datesorted);
490184054Slulf		d_next = LIST_NEXT(d, delta_next);
491184054Slulf		if (d_next != NULL) {
492184054Slulf			d_next->prev = d;
493184054Slulf			/*
494184054Slulf			 * If it's trunk, treat it like the oldest, if not treat
495184054Slulf			 * it like a child.
496184054Slulf			 */
497184054Slulf			if (rcsrev_istrunk(d_next->revnum))
498185134Slulf				STAILQ_INSERT_HEAD(&deltastack, d_next,
499185134Slulf				    stack_next);
500184054Slulf			else
501184054Slulf				LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
502184054Slulf				    branch_next_date);
503184054Slulf		}
504184054Slulf
505184054Slulf		/*
506184054Slulf		 * First, we need to sort our branches based on their date to
507184054Slulf		 * take into account some self-hacked RCS files.
508184054Slulf		 */
509186727Slulf		LIST_FOREACH(b, &d->branchlist, branch_next) {
510184054Slulf			d_tmp = LIST_FIRST(&b->deltalist);
511184054Slulf			if (LIST_EMPTY(&branchlist_datesorted)) {
512184054Slulf				LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp,
513184054Slulf				    branch_next_date);
514184054Slulf				continue;
515184054Slulf			}
516184054Slulf
517184054Slulf			d_tmp2 = LIST_FIRST(&branchlist_datesorted);
518186724Slulf			if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) {
519185134Slulf				LIST_INSERT_BEFORE(d_tmp2, d_tmp,
520185134Slulf				    branch_next_date);
521184054Slulf				continue;
522184054Slulf			}
523184054Slulf			while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
524184054Slulf			    != NULL) {
525184054Slulf				if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
526186724Slulf				    <= 0)
527184054Slulf					break;
528184054Slulf				d_tmp2 = d_tmp3;
529184054Slulf			}
530184054Slulf			LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
531184054Slulf		}
532185134Slulf		/*
533184054Slulf		 * Invert the deltalist of a branch, since we're writing them
534184054Slulf		 * the opposite way.
535184054Slulf		 */
536184054Slulf		LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
537184054Slulf                        d_tmp->prev = d;
538184054Slulf			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
539184054Slulf		}
540184054Slulf	}
541184054Slulf	return (0);
542184054Slulf}
543184054Slulf
544184054Slulf/*
545184054Slulf * Generates text given a delta and a diffbase.
546184054Slulf */
547184054Slulfstatic int
548184054Slulfrcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
549184054Slulf    struct delta *diffbase)
550184054Slulf{
551184054Slulf	struct stream *in, *rd, *orig;
552184054Slulf	struct keyword *k;
553184054Slulf	struct diffinfo dibuf, *di;
554184054Slulf	struct buf *b;
555185134Slulf	size_t size;
556184054Slulf	char *line;
557184054Slulf	int error;
558184054Slulf
559184054Slulf	di = &dibuf;
560184054Slulf	b = NULL;
561184054Slulf	error = 0;
562184054Slulf
563184054Slulf	/* Write if the diffbase is the previous */
564184054Slulf	if (d->diffbase == diffbase) {
565184054Slulf
566184054Slulf		/* Write out the text. */
567184054Slulf		in = stream_open_buf(d->text);
568184054Slulf		line = stream_getln(in, &size);
569184054Slulf		while (line != NULL) {
570190422Slulf			if (stream_write(dest, line, size) == -1) {
571190422Slulf				error = -1;
572190422Slulf				goto cleanup;
573190422Slulf			}
574184054Slulf			line = stream_getln(in, &size);
575184054Slulf		}
576184054Slulf		stream_close(in);
577184054Slulf	/* We need to apply diff to produce text, this is probably HEAD. */
578184054Slulf	} else if (diffbase == NULL) {
579184054Slulf		/* Apply diff. */
580184054Slulf		orig = rcsfile_getdeltatext(rf, d, &b);
581184054Slulf		if (orig == NULL) {
582184054Slulf			error = -1;
583184054Slulf			goto cleanup;
584184054Slulf		}
585184054Slulf		line = stream_getln(orig, &size);
586184054Slulf		while (line != NULL) {
587190422Slulf			if (stream_write(dest, line, size) == -1) {
588190422Slulf				error = -1;
589190422Slulf				goto cleanup;
590190422Slulf			}
591184054Slulf			line = stream_getln(orig, &size);
592184054Slulf		}
593184054Slulf		stream_close(orig);
594185134Slulf	/*
595184054Slulf	 * A new head was probably added, and now the previous HEAD must be
596184054Slulf	 * changed to include the diff instead.
597184054Slulf	 */
598184054Slulf	} else if (diffbase->diffbase == d) {
599184054Slulf		/* Get reverse diff. */
600184054Slulf		orig = rcsfile_getdeltatext(rf, d, &b);
601184054Slulf		if (orig == NULL) {
602184054Slulf			error = -1;
603184054Slulf			goto cleanup;
604184054Slulf		}
605184054Slulf		di->di_rcsfile = rf->name;
606184054Slulf		di->di_cvsroot = rf->cvsroot;
607184054Slulf		di->di_revnum = d->revnum;
608184054Slulf		di->di_revdate = d->revdate;
609184054Slulf		di->di_author = d->author;
610184054Slulf		di->di_tag = rf->colltag;
611184054Slulf		di->di_state = d->state;
612185592Slulf		di->di_expand = EXPAND_OLD;
613184054Slulf		k = keyword_new();
614185592Slulf
615184054Slulf		rd = stream_open_buf(diffbase->text);
616184054Slulf		error = diff_reverse(rd, orig, dest, k, di);
617184054Slulf		if (error) {
618185134Slulf			lprintf(-1, "Error applying reverse diff: %d\n", error);
619184054Slulf			goto cleanup;
620184054Slulf		}
621184054Slulf		keyword_free(k);
622184054Slulf		stream_close(rd);
623184054Slulf		stream_close(orig);
624184054Slulf	}
625184054Slulfcleanup:
626184054Slulf	if (b != NULL)
627184054Slulf		buf_free(b);
628184054Slulf	return (error);
629184054Slulf}
630184054Slulf
631184054Slulf/*
632184054Slulf * Return a stream with an applied diff of a delta.
633184054Slulf * XXX: extra overhead on the last apply. Could write directly to file, but
634184054Slulf * makes things complicated though.
635184054Slulf */
636184054Slulfstatic struct stream *
637184054Slulfrcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
638184054Slulf{
639184054Slulf	struct diffinfo dibuf, *di;
640184054Slulf	struct stream *orig, *dest, *rd;
641184054Slulf	struct buf *buf_orig;
642184054Slulf	struct keyword *k;
643184054Slulf	int error;
644184054Slulf
645184054Slulf	buf_orig = NULL;
646184054Slulf	error = 0;
647184054Slulf
648185134Slulf	/*
649185134Slulf	 * If diffbase is NULL or we are head (the old head), we have a normal
650185134Slulf	 * complete deltatext.
651185134Slulf	 */
652184054Slulf	if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
653184054Slulf		orig = stream_open_buf(d->text);
654184054Slulf		return (orig);
655184054Slulf	}
656184054Slulf
657184054Slulf	di = &dibuf;
658184054Slulf	/* If not, we need to apply our diff to that of our diffbase. */
659184054Slulf	orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
660184054Slulf	if (orig == NULL)
661184054Slulf		return (NULL);
662184054Slulf
663184054Slulf	/*
664184054Slulf	 * Now that we are sure we have a complete deltatext in ret, let's apply
665184054Slulf	 * our diff to it.
666184054Slulf	 */
667185134Slulf	*buf_dest = buf_new(BUF_SIZE_DEFAULT);
668184054Slulf	dest = stream_open_buf(*buf_dest);
669184054Slulf
670184054Slulf	di->di_rcsfile = rf->name;
671184054Slulf	di->di_cvsroot = rf->cvsroot;
672184054Slulf	di->di_revnum = d->revnum;
673184054Slulf	di->di_revdate = d->revdate;
674184054Slulf	di->di_author = d->author;
675184054Slulf	di->di_tag = rf->colltag;
676184054Slulf	di->di_state = d->state;
677185592Slulf	di->di_expand = EXPAND_OLD;
678184054Slulf	rd = stream_open_buf(d->text);
679184054Slulf	k = keyword_new();
680184054Slulf	error = diff_apply(rd, orig, dest, k, di, 0);
681184054Slulf	stream_flush(dest);
682184054Slulf	stream_close(rd);
683184054Slulf	stream_close(orig);
684184054Slulf	stream_close(dest);
685184054Slulf	keyword_free(k);
686184054Slulf	if (buf_orig != NULL)
687184054Slulf		buf_free(buf_orig);
688184054Slulf	if (error) {
689184054Slulf		lprintf(-1, "Error applying diff: %d\n", error);
690184054Slulf		return (NULL);
691184054Slulf	}
692184054Slulf
693184054Slulf	/* Now reopen the stream for the reading. */
694184054Slulf	dest = stream_open_buf(*buf_dest);
695184054Slulf	return (dest);
696184054Slulf}
697184054Slulf
698184054Slulf/* Print content of rcsfile. Useful for debugging. */
699184054Slulfvoid
700184054Slulfrcsfile_print(struct rcsfile *rf)
701184054Slulf{
702184054Slulf	struct delta *d;
703184054Slulf	struct tag *t;
704184054Slulf	struct string *s;
705185134Slulf	struct stream *in;
706184054Slulf	char *line;
707184054Slulf
708185134Slulf	lprintf(1, "\n");
709184054Slulf	if (rf->name != NULL)
710185134Slulf		lprintf(1, "name: '%s'\n", rf->name);
711184054Slulf	if (rf->head != NULL)
712185134Slulf		lprintf(1, "head: '%s'\n", rf->head);
713184054Slulf	if (rf->branch != NULL)
714185134Slulf		lprintf(1, "branch: '%s'\n", rf->branch);
715185134Slulf	lprintf(1, "Access: ");
716185134Slulf	STAILQ_FOREACH(s, &rf->accesslist, string_next)
717185134Slulf		lprintf(1, "'%s' ", s->str);
718185134Slulf	lprintf(1, "\n");
719184054Slulf
720184054Slulf	/* Print all tags. */
721184054Slulf	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
722185134Slulf		lprintf(1, "Tag: ");
723184054Slulf		if (t->tag != NULL)
724185134Slulf			lprintf(1, "name: %s ", t->tag);
725184054Slulf		if (t->revnum != NULL)
726185134Slulf			lprintf(1, "rev: %s", t->revnum);
727185134Slulf		lprintf(1, "\n");
728184054Slulf	}
729184054Slulf
730184054Slulf	if (rf->strictlock)
731185134Slulf		lprintf(1, "Strict!\n");
732184054Slulf	if (rf->comment != NULL)
733185134Slulf		lprintf(1, "comment: '%s'\n", rf->comment);
734194070Slulf	if (rf->expand != EXPAND_DEFAULT)
735185134Slulf		lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand));
736184054Slulf
737184054Slulf	/* Print all deltas. */
738184054Slulf	LIST_FOREACH(d, &rf->deltatable, table_next) {
739185134Slulf		lprintf(1, "Delta: ");
740184054Slulf		if (d->revdate != NULL)
741185134Slulf			lprintf(1, "date: %s ", d->revdate);
742184054Slulf		if (d->revnum != NULL)
743185134Slulf			lprintf(1, "rev: %s", d->revnum);
744184054Slulf		if (d->author != NULL)
745185134Slulf			lprintf(1, "author: %s", d->author);
746184054Slulf		if (d->state != NULL)
747185134Slulf			lprintf(1, "state: %s", d->state);
748184054Slulf
749185134Slulf		lprintf(1, "Text:\n");
750184054Slulf		in = stream_open_buf(d->text);
751184054Slulf		line = stream_getln(in, NULL);
752184054Slulf		while (line != NULL) {
753184054Slulf			lprintf(1, "TEXT: %s\n", line);
754184054Slulf			line = stream_getln(in, NULL);
755184054Slulf		}
756184054Slulf		stream_close(in);
757185134Slulf		lprintf(1, "\n");
758184054Slulf	}
759184054Slulf
760184054Slulf	if (rf->desc != NULL)
761185134Slulf		lprintf(1, "desc: '%s'\n", rf->desc);
762184054Slulf}
763184054Slulf
764184054Slulf/* Free all memory associated with a struct rcsfile. */
765184054Slulfvoid
766184054Slulfrcsfile_free(struct rcsfile *rf)
767184054Slulf{
768184054Slulf	struct delta *d;
769184054Slulf	struct tag *t;
770184054Slulf	struct string *s;
771184054Slulf
772184054Slulf	if (rf->name != NULL)
773184054Slulf		free(rf->name);
774184054Slulf	if (rf->head != NULL)
775184054Slulf		free(rf->head);
776184054Slulf	if (rf->branch != NULL)
777184054Slulf		free(rf->branch);
778184054Slulf	if (rf->cvsroot != NULL)
779184054Slulf		free(rf->cvsroot);
780184054Slulf	if (rf->colltag != NULL)
781184054Slulf		free(rf->colltag);
782184054Slulf
783184054Slulf	/* Free all access ids. */
784184054Slulf	while (!STAILQ_EMPTY(&rf->accesslist)) {
785184054Slulf		s = STAILQ_FIRST(&rf->accesslist);
786184054Slulf		STAILQ_REMOVE_HEAD(&rf->accesslist, string_next);
787184054Slulf		if (s->str != NULL)
788184054Slulf			free(s->str);
789184054Slulf		free(s);
790184054Slulf	}
791184054Slulf
792184054Slulf	/* Free all tags. */
793184054Slulf	while (!STAILQ_EMPTY(&rf->taglist)) {
794184054Slulf		t = STAILQ_FIRST(&rf->taglist);
795184054Slulf		STAILQ_REMOVE_HEAD(&rf->taglist, tag_next);
796184054Slulf		if (t->tag != NULL)
797184054Slulf			free(t->tag);
798184054Slulf		if (t->revnum != NULL)
799184054Slulf			free(t->revnum);
800184054Slulf		free(t);
801184054Slulf	}
802184054Slulf
803184054Slulf	if (rf->comment != NULL)
804184054Slulf		free(rf->comment);
805184054Slulf
806184054Slulf	/* Free all deltas in global list */
807184054Slulf	while (!LIST_EMPTY(&rf->deltatable)) {
808184054Slulf		d = LIST_FIRST(&rf->deltatable);
809186700Slulf		if (!rf->ro)
810186700Slulf			LIST_REMOVE(d, delta_next);
811184054Slulf		LIST_REMOVE(d, table_next);
812184054Slulf		rcsfile_freedelta(d);
813184054Slulf	}
814184054Slulf
815184054Slulf	/* Free global branch. */
816184054Slulf	if (rf->trunk->revnum != NULL)
817184054Slulf		free(rf->trunk->revnum);
818184054Slulf	free(rf->trunk);
819184054Slulf
820184054Slulf	if (rf->desc != NULL)
821184054Slulf		free(rf->desc);
822184054Slulf
823184054Slulf	free(rf);
824184054Slulf}
825184054Slulf
826184054Slulf/*
827184054Slulf * Free a RCS delta.
828184054Slulf */
829184054Slulfstatic void
830184054Slulfrcsfile_freedelta(struct delta *d)
831184054Slulf{
832184054Slulf	struct branch *b;
833184054Slulf
834184054Slulf	if (d->revdate != NULL)
835184054Slulf		free(d->revdate);
836184054Slulf	if (d->revnum != NULL)
837184054Slulf		free(d->revnum);
838184054Slulf	if (d->author != NULL)
839184054Slulf		free(d->author);
840184054Slulf	if (d->state != NULL)
841184054Slulf		free(d->state);
842184054Slulf	if (d->log != NULL)
843184054Slulf		buf_free(d->log);
844184054Slulf	if (d->text != NULL)
845184054Slulf		buf_free(d->text);
846184054Slulf
847184054Slulf	/* Free all subbranches of a delta. */
848186727Slulf	while (!LIST_EMPTY(&d->branchlist)) {
849186727Slulf		b = LIST_FIRST(&d->branchlist);
850186727Slulf		LIST_REMOVE(b, branch_next);
851184054Slulf		free(b->revnum);
852184054Slulf		free(b);
853184054Slulf	}
854184054Slulf	free(d);
855184054Slulf}
856184054Slulf
857184054Slulf/*
858184054Slulf * Functions for editing RCS deltas.
859184054Slulf */
860184054Slulf
861184054Slulf/* Add a new entry to the access list. */
862184054Slulfvoid
863184054Slulfrcsfile_addaccess(struct rcsfile *rf, char *id)
864184054Slulf{
865184054Slulf	struct string *s;
866184054Slulf
867184054Slulf	s = xmalloc(sizeof(struct string));
868184054Slulf	s->str = xstrdup(id);
869184054Slulf	STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next);
870184054Slulf}
871184054Slulf
872184054Slulf/* Add a tag to a RCS file. */
873184054Slulfvoid
874184054Slulfrcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum)
875184054Slulf{
876184054Slulf	struct tag *t;
877184054Slulf
878184054Slulf	t = xmalloc(sizeof(struct tag));
879184054Slulf	t->tag = xstrdup(tag);
880184054Slulf	t->revnum = xstrdup(revnum);
881184054Slulf
882184054Slulf	STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next);
883184054Slulf}
884184054Slulf
885184054Slulf/* Import a tag to a RCS file. */
886184054Slulfvoid
887184054Slulfrcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum)
888184054Slulf{
889184054Slulf	struct tag *t;
890184054Slulf
891184054Slulf	t = xmalloc(sizeof(struct tag));
892184054Slulf	t->tag = xstrdup(tag);
893184054Slulf	t->revnum = xstrdup(revnum);
894184054Slulf
895184054Slulf	STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next);
896184054Slulf}
897184054Slulf
898184054Slulf/*
899184054Slulf * Delete a revision from the global delta list and the branch it is in. Csup
900184054Slulf * will tell us to delete the tags involved.
901184054Slulf */
902184054Slulfvoid
903184054Slulfrcsfile_deleterev(struct rcsfile *rf, char *revname)
904184054Slulf{
905184054Slulf	struct delta *d;
906184054Slulf
907184054Slulf	d = rcsfile_getdelta(rf, revname);
908186700Slulf	if (!rf->ro)
909186700Slulf		LIST_REMOVE(d, delta_next);
910184054Slulf	LIST_REMOVE(d, table_next);
911184054Slulf	rcsfile_freedelta(d);
912184054Slulf}
913184054Slulf
914184054Slulf/* Delete a tag from the tag list. */
915184054Slulfvoid
916184054Slulfrcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum)
917184054Slulf{
918184054Slulf	struct tag *t;
919184054Slulf
920184054Slulf	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
921184054Slulf		if ((strcmp(tag, t->tag) == 0) &&
922184054Slulf		    (strcmp(revnum, t->revnum) == 0)) {
923184054Slulf			STAILQ_REMOVE(&rf->taglist, t, tag, tag_next);
924184054Slulf			free(t->tag);
925184054Slulf			free(t->revnum);
926184054Slulf			free(t);
927184054Slulf			return;
928184054Slulf		}
929184054Slulf	}
930184054Slulf}
931184054Slulf
932186741Slulf/*
933184054Slulf * Searches the global deltalist for a delta.
934184054Slulf */
935184054Slulfstruct delta *
936184054Slulfrcsfile_getdelta(struct rcsfile *rf, char *revnum)
937184054Slulf{
938184054Slulf	struct delta *d;
939184054Slulf
940184054Slulf	LIST_FOREACH(d, &rf->deltatable, table_next) {
941184054Slulf		if (strcmp(revnum, d->revnum) == 0)
942184054Slulf			return (d);
943184054Slulf	}
944184054Slulf	return (NULL);
945184054Slulf}
946184054Slulf
947184054Slulf/* Set rcsfile head. */
948184054Slulfvoid
949184054Slulfrcsfile_setval(struct rcsfile *rf, int field, char *val)
950184054Slulf{
951186718Slulf	size_t len;
952184054Slulf
953184054Slulf	switch (field) {
954186741Slulf	case RCSFILE_HEAD:
955186741Slulf		if (rf->head != NULL)
956186741Slulf			free(rf->head);
957186741Slulf		rf->head = xstrdup(val);
958184054Slulf		break;
959186741Slulf	case RCSFILE_BRANCH:
960186741Slulf		if (rf->branch != NULL)
961186741Slulf			free(rf->branch);
962186741Slulf		rf->branch = (val == NULL) ? NULL : xstrdup(val);
963184054Slulf		break;
964186741Slulf	case RCSFILE_STRICT:
965186741Slulf		if (val != NULL)
966186741Slulf			rf->strictlock = 1;
967184054Slulf		break;
968186741Slulf	case RCSFILE_COMMENT:
969186741Slulf		if (rf->comment != NULL)
970186741Slulf			free(rf->comment);
971186741Slulf		rf->comment = xstrdup(val);
972184054Slulf		break;
973186741Slulf	case RCSFILE_EXPAND:
974186741Slulf		len = strlen(val) - 1;
975186741Slulf		val++;
976186741Slulf		val[len - 1] = '\0';
977186741Slulf		rf->expand = keyword_decode_expand(val);
978184054Slulf		break;
979186741Slulf	case RCSFILE_DESC:
980186741Slulf		if (rf->desc != NULL)
981186741Slulf			free(rf->desc);
982186741Slulf		rf->desc = xstrdup(val);
983184054Slulf		break;
984186741Slulf	default:
985186741Slulf		lprintf(-1, "Setting invalid RCSfile value.\n");
986184054Slulf		break;
987184054Slulf	}
988184054Slulf}
989184054Slulf
990184054Slulf/* Create and initialize a delta. */
991184054Slulfstatic struct delta *
992184054Slulfrcsfile_createdelta(char *revnum)
993184054Slulf{
994184054Slulf	struct delta *d;
995184054Slulf
996184054Slulf	d = xmalloc(sizeof(struct delta));
997184054Slulf	d->revnum = xstrdup(revnum);
998184054Slulf	d->revdate = NULL;
999184054Slulf	d->state = NULL;
1000184054Slulf	d->author = NULL;
1001185134Slulf	d->log = buf_new(BUF_SIZE_DEFAULT);
1002185134Slulf	d->text = buf_new(BUF_SIZE_DEFAULT);
1003184054Slulf	d->diffbase = NULL;
1004184054Slulf
1005186727Slulf	LIST_INIT(&d->branchlist);
1006184054Slulf	return (d);
1007184054Slulf}
1008184054Slulf
1009184054Slulf/* Add a delta to a imported delta tree. Used by the updater. */
1010184054Slulfstruct delta *
1011184054Slulfrcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1012184054Slulf    char *diffbase)
1013184054Slulf{
1014184054Slulf	struct branch *b;
1015184054Slulf	struct delta *d, *d_bp, *d_next;
1016184054Slulf	char *brev, *bprev;
1017184054Slulf	int trunk;
1018184054Slulf
1019184054Slulf	d_next = NULL;
1020184054Slulf	d = rcsfile_getdelta(rf, revnum);
1021184054Slulf	if (d != NULL) {
1022184054Slulf		lprintf(-1, "Delta %s already exists!\n", revnum);
1023184054Slulf		return (NULL);
1024184054Slulf	}
1025184054Slulf	d = rcsfile_createdelta(revnum);
1026184054Slulf	d->placeholder = 0;
1027184054Slulf	d->revdate = xstrdup(revdate);
1028184054Slulf	d->author = xstrdup(author);
1029184054Slulf	d->diffbase = rcsfile_getdelta(rf, diffbase);
1030184054Slulf
1031184054Slulf	/* If it's trunk, insert it in the head branch list. */
1032185134Slulf	b = rcsrev_istrunk(d->revnum) ? rf->trunk :
1033185134Slulf	    rcsfile_getbranch(rf, d->revnum);
1034184054Slulf
1035184054Slulf	/*
1036184054Slulf	 * We didn't find a branch, check if we can find a branchpoint and
1037184054Slulf	 * create a branch there.
1038184054Slulf	 */
1039184054Slulf	if (b == NULL) {
1040184054Slulf		brev = rcsrev_prefix(d->revnum);
1041184054Slulf		bprev = rcsrev_prefix(brev);
1042184054Slulf
1043184054Slulf		d_bp = rcsfile_getdelta(rf, bprev);
1044184054Slulf		free(bprev);
1045184054Slulf		if (d_bp == NULL) {
1046184054Slulf			lprintf(-1, "No branch point for adding delta %s\n",
1047184054Slulf			    d->revnum);
1048184054Slulf			return (NULL);
1049184054Slulf		}
1050184054Slulf
1051184054Slulf		/* Create the branch and insert in delta. */
1052184054Slulf		b = xmalloc(sizeof(struct branch));
1053184054Slulf		b->revnum = brev;
1054184054Slulf		LIST_INIT(&b->deltalist);
1055186727Slulf		rcsdelta_insertbranch(d_bp, b);
1056184054Slulf	}
1057184054Slulf
1058184054Slulf	/* Insert both into the tree, and into the lookup list. */
1059184054Slulf	trunk = rcsrev_istrunk(d->revnum);
1060184054Slulf	rcsfile_insertdelta(b, d, trunk);
1061184054Slulf	rcsfile_insertsorteddelta(rf, d);
1062184054Slulf	return (d);
1063184054Slulf}
1064184054Slulf
1065184054Slulf/* Adds a delta to a rcsfile struct. Used by the parser. */
1066184054Slulfvoid
1067184054Slulfrcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1068184054Slulf    char *state, char *next)
1069184054Slulf{
1070184054Slulf	struct branch *b;
1071184054Slulf	struct delta *d, *d_bp, *d_next;
1072184054Slulf	char *brev, *bprev;
1073184054Slulf	int trunk;
1074184054Slulf
1075184054Slulf	d_next = NULL;
1076184054Slulf	d = rcsfile_getdelta(rf, revnum);
1077184054Slulf
1078184054Slulf	if (d == NULL) {
1079184054Slulf		/* If not, we'll just create a new entry. */
1080184054Slulf		d = rcsfile_createdelta(revnum);
1081184054Slulf		d->placeholder = 0;
1082184054Slulf	} else {
1083184054Slulf		if (d->placeholder == 0) {
1084184054Slulf			lprintf(-1, "Trying to import already existing delta\n");
1085184054Slulf			return;
1086184054Slulf		}
1087184054Slulf	}
1088184054Slulf	/*
1089184054Slulf	 * If already exists, assume that only revnum is filled out, and set the
1090184054Slulf	 * rest of the fields. This should be an OK assumption given that we can
1091184054Slulf	 * be sure internally that the structure is sufficiently initialized so
1092184054Slulf	 * we won't have any unfreed memory.
1093184054Slulf	 */
1094184054Slulf	d->revdate = xstrdup(revdate);
1095184054Slulf	d->author = xstrdup(author);
1096184054Slulf	if (state != NULL)
1097184054Slulf		d->state = xstrdup(state);
1098184054Slulf
1099184054Slulf	/* If we have a next, create a placeholder for it. */
1100184054Slulf	if (next != NULL) {
1101184054Slulf		d_next = rcsfile_createdelta(next);
1102184054Slulf		d_next->placeholder = 1;
1103184054Slulf		/* Diffbase should be the previous. */
1104184054Slulf		d_next->diffbase = d;
1105184054Slulf	}
1106184054Slulf
1107186700Slulf	/* If we're opening read-only, do minimal work. */
1108186700Slulf	if (rf->ro) {
1109186700Slulf		if (!d->placeholder)
1110186700Slulf			rcsfile_insertsorteddelta(rf, d);
1111186700Slulf		else
1112186700Slulf			d->placeholder = 0;
1113186700Slulf		if (d_next != NULL)
1114186700Slulf			rcsfile_insertsorteddelta(rf, d_next);
1115186700Slulf		return;
1116186700Slulf	}
1117186700Slulf
1118184054Slulf	/* If it's trunk, insert it in the head branch list. */
1119184054Slulf	b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
1120184054Slulf	    d->revnum);
1121184054Slulf
1122184054Slulf	/*
1123184054Slulf	 * We didn't find a branch, check if we can find a branchpoint and
1124184054Slulf	 * create a branch there.
1125184054Slulf	 */
1126184054Slulf	if (b == NULL) {
1127184054Slulf		brev = rcsrev_prefix(d->revnum);
1128184054Slulf		bprev = rcsrev_prefix(brev);
1129184054Slulf
1130184054Slulf		d_bp = rcsfile_getdelta(rf, bprev);
1131184054Slulf		free(bprev);
1132184054Slulf		if (d_bp == NULL) {
1133184054Slulf			lprintf(-1, "No branch point for adding delta %s\n",
1134184054Slulf			    d->revnum);
1135184054Slulf			return;
1136184054Slulf		}
1137184054Slulf
1138184054Slulf		/* Create the branch and insert in delta. */
1139184054Slulf		b = xmalloc(sizeof(struct branch));
1140184054Slulf		b->revnum = brev;
1141184054Slulf		LIST_INIT(&b->deltalist);
1142186727Slulf		rcsdelta_insertbranch(d_bp, b);
1143184054Slulf	}
1144184054Slulf
1145184054Slulf	/* Insert if not a placeholder. */
1146184054Slulf	if (!d->placeholder) {
1147184054Slulf		/* Insert both into the tree, and into the lookup list. */
1148184054Slulf		if (rcsrev_istrunk(d->revnum))
1149184054Slulf			rcsfile_insertdelta(b, d, 1);
1150184054Slulf		else {
1151184054Slulf			rcsfile_insertdelta(b, d, 0);
1152186741Slulf			/*
1153184054Slulf			 * On import we need to set the diffbase to our
1154184054Slulf			 * branchpoint for writing out later.
1155184054Slulf			 */
1156184054Slulf			if (LIST_FIRST(&b->deltalist) == d) {
1157184054Slulf				brev = rcsrev_prefix(d->revnum);
1158184054Slulf				bprev = rcsrev_prefix(brev);
1159184054Slulf				d_bp = rcsfile_getdelta(rf, bprev);
1160184054Slulf				/* This should really not happen. */
1161184054Slulf				assert(d_bp != NULL);
1162184054Slulf				d->diffbase = d_bp;
1163184054Slulf				free(brev);
1164184054Slulf				free(bprev);
1165184054Slulf			}
1166184054Slulf		}
1167184054Slulf		rcsfile_insertsorteddelta(rf, d);
1168184054Slulf	} else /* Not a placeholder anymore. */ {
1169184054Slulf		d->placeholder = 0;
1170184054Slulf		/* Put it into the tree. */
1171184054Slulf		trunk = rcsrev_istrunk(d->revnum);
1172184054Slulf		rcsfile_insertdelta(b, d, trunk);
1173184054Slulf	}
1174184054Slulf
1175184054Slulf	/* If we have a next, insert the placeholder into the lookup list. */
1176184054Slulf	if (d_next != NULL)
1177184054Slulf		rcsfile_insertsorteddelta(rf, d_next);
1178184054Slulf}
1179184054Slulf
1180184054Slulf/*
1181184054Slulf * Find the branch of a revision number.
1182184054Slulf */
1183184054Slulfstatic struct branch *
1184184054Slulfrcsfile_getbranch(struct rcsfile *rf, char *revnum)
1185184054Slulf{
1186184054Slulf	struct branch *b;
1187184054Slulf	struct delta *d;
1188185134Slulf	char *branchrev, *bprev;
1189184054Slulf
1190184054Slulf	branchrev = rcsrev_prefix(revnum);
1191184054Slulf	bprev = rcsrev_prefix(branchrev);
1192184054Slulf	d = rcsfile_getdelta(rf, bprev);
1193184054Slulf	free(bprev);
1194186727Slulf	LIST_FOREACH(b, &d->branchlist, branch_next) {
1195184054Slulf		if(rcsnum_cmp(b->revnum, branchrev) == 0) {
1196184054Slulf			free(branchrev);
1197184054Slulf			return (b);
1198184054Slulf		}
1199184054Slulf	}
1200184054Slulf	free(branchrev);
1201184054Slulf	return (NULL);
1202184054Slulf}
1203184054Slulf
1204186727Slulf/* Insert a branch into a delta, sorted by branch revision date. */
1205186727Slulfstatic void
1206186727Slulfrcsdelta_insertbranch(struct delta *d, struct branch *b)
1207186727Slulf{
1208186727Slulf	struct branch *b_iter;
1209186727Slulf
1210186727Slulf	/* If it's empty, insert into head. */
1211186727Slulf	if (LIST_EMPTY(&d->branchlist)) {
1212186727Slulf		LIST_INSERT_HEAD(&d->branchlist, b, branch_next);
1213186727Slulf		return;
1214186727Slulf	}
1215186727Slulf
1216186727Slulf	/* Just put it in before the revdate that is lower. */
1217186727Slulf	LIST_FOREACH(b_iter, &d->branchlist, branch_next) {
1218186727Slulf		if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) {
1219186727Slulf			LIST_INSERT_BEFORE(b_iter, b, branch_next);
1220186727Slulf			return;
1221186727Slulf		}
1222186727Slulf		if (LIST_NEXT(b_iter, branch_next) == NULL)
1223186727Slulf			break;
1224186727Slulf	}
1225186727Slulf	/* Insert after last element. */
1226186727Slulf	LIST_INSERT_AFTER(b_iter, b, branch_next);
1227186727Slulf}
1228186727Slulf
1229186700Slulf/* Insert a delta into the correct place in the table of the rcsfile. */
1230184054Slulfstatic void
1231184054Slulfrcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d)
1232184054Slulf{
1233184054Slulf	struct delta *d2;
1234184054Slulf
1235184054Slulf	/* If it's empty, insert into head. */
1236184054Slulf	if (LIST_EMPTY(&rf->deltatable)) {
1237184054Slulf		LIST_INSERT_HEAD(&rf->deltatable, d, table_next);
1238184054Slulf		return;
1239184054Slulf	}
1240184054Slulf
1241184054Slulf	/* Just put it in before the revdate that is lower. */
1242184054Slulf	LIST_FOREACH(d2, &rf->deltatable, table_next) {
1243184054Slulf		if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) {
1244184054Slulf			LIST_INSERT_BEFORE(d2, d, table_next);
1245184054Slulf			return;
1246184054Slulf		}
1247184054Slulf		if (LIST_NEXT(d2, table_next) == NULL)
1248184054Slulf			break;
1249184054Slulf	}
1250184054Slulf	/* Insert after last element. */
1251184054Slulf	LIST_INSERT_AFTER(d2, d, table_next);
1252184054Slulf}
1253184054Slulf
1254184054Slulf/*
1255184054Slulf * Insert a delta into the correct place in branch. A trunk branch will have
1256184054Slulf * different ordering scheme and be sorted by revision number, but a normal
1257228992Suqs * branch will be sorted by date to maintain compatibility with branches that
1258228992Suqs * is "hand-hacked".
1259184054Slulf */
1260184054Slulfstatic void
1261184054Slulfrcsfile_insertdelta(struct branch *b, struct delta *d, int trunk)
1262184054Slulf{
1263184054Slulf	struct delta *d2;
1264184054Slulf
1265184054Slulf	/* If it's empty, insert into head. */
1266184054Slulf	if (LIST_EMPTY(&b->deltalist)) {
1267184054Slulf		LIST_INSERT_HEAD(&b->deltalist, d, delta_next);
1268184054Slulf		return;
1269184054Slulf	}
1270184054Slulf
1271184054Slulf	/*
1272184054Slulf	 * Just put it in before the revnum that is lower. Sort trunk branch by
1273184054Slulf	 * branchnum but the subbranches after deltadate.
1274184054Slulf	 */
1275184054Slulf	LIST_FOREACH(d2, &b->deltalist, delta_next) {
1276184054Slulf		if (trunk) {
1277184054Slulf			if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) {
1278184054Slulf				LIST_INSERT_BEFORE(d2, d, delta_next);
1279184054Slulf				return;
1280184054Slulf			}
1281184054Slulf		} else {
1282184054Slulf			/* XXX: here we depend on the date being set, but it
1283184054Slulf			 * should be before this is called anyway. */
1284186744Slulf			if (rcsnum_cmp(d->revnum, d2->revnum) < 0) {
1285184054Slulf				LIST_INSERT_BEFORE(d2, d, delta_next);
1286184054Slulf				return;
1287184054Slulf			}
1288184054Slulf		}
1289184054Slulf		if (LIST_NEXT(d2, delta_next) == NULL)
1290184054Slulf			break;
1291184054Slulf	}
1292184054Slulf	/* Insert after last element. */
1293184054Slulf	LIST_INSERT_AFTER(d2, d, delta_next);
1294184054Slulf}
1295184054Slulf
1296184054Slulf
1297184054Slulf/* Add logtext to a delta. Assume the delta already exists. */
1298184054Slulfint
1299185592Slulfrcsdelta_addlog(struct delta *d, char *log, int len)
1300184054Slulf{
1301184054Slulf	struct stream *dest;
1302190422Slulf	int nbytes;
1303184054Slulf
1304184054Slulf	assert(d != NULL);
1305185592Slulf	/* Strip away '@' at beginning and end. */
1306184054Slulf	log++;
1307185592Slulf	len--;
1308185592Slulf	log[len - 1] = '\0';
1309184054Slulf	dest = stream_open_buf(d->log);
1310190422Slulf	nbytes = stream_write(dest, log, len - 1);
1311184054Slulf	stream_close(dest);
1312190422Slulf	return ((nbytes == -1) ? -1 : 0);
1313184054Slulf}
1314184054Slulf
1315184054Slulf/* Add deltatext to a delta. Assume the delta already exists. */
1316184054Slulfint
1317185592Slulfrcsdelta_addtext(struct delta *d, char *text, int len)
1318184054Slulf{
1319184054Slulf	struct stream *dest;
1320190422Slulf	int nbytes;
1321184054Slulf
1322184054Slulf	assert(d != NULL);
1323185592Slulf	/* Strip away '@' at beginning and end. */
1324184054Slulf	text++;
1325185592Slulf	len--;
1326185592Slulf	text[len - 1] = '\0';
1327184054Slulf
1328184054Slulf	dest = stream_open_buf(d->text);
1329190422Slulf	nbytes = stream_write(dest, text, len - 1);
1330184054Slulf	stream_close(dest);
1331190422Slulf	return ((nbytes == -1) ? -1 : 0);
1332184054Slulf}
1333184054Slulf
1334184054Slulf/* Add a deltatext logline to a delta. */
1335190422Slulfint
1336184054Slulfrcsdelta_appendlog(struct delta *d, char *logline, size_t size)
1337184054Slulf{
1338184054Slulf	struct stream *dest;
1339190422Slulf	int error;
1340184054Slulf
1341184054Slulf	assert(d != NULL);
1342184054Slulf	dest = stream_open_buf(d->log);
1343190422Slulf	error = rcsdelta_writestring(logline, size, dest);
1344184054Slulf	stream_close(dest);
1345190422Slulf	return (error);
1346184054Slulf}
1347184054Slulf
1348184054Slulf/* Add a deltatext textline to a delta. */
1349190422Slulfint
1350184054Slulfrcsdelta_appendtext(struct delta *d, char *textline, size_t size)
1351184054Slulf{
1352184054Slulf	struct stream *dest;
1353190422Slulf	int error;
1354185134Slulf
1355185134Slulf	assert(d != NULL);
1356185134Slulf	dest = stream_open_buf(d->text);
1357190422Slulf	error = rcsdelta_writestring(textline, size, dest);
1358185134Slulf	stream_close(dest);
1359190422Slulf	return (error);
1360185134Slulf}
1361185134Slulf
1362190422Slulfstatic int
1363185134Slulfrcsdelta_writestring(char *textline, size_t size, struct stream *dest)
1364185134Slulf{
1365184054Slulf	char buf[3];
1366184054Slulf	size_t i;
1367184054Slulf	int count;
1368184054Slulf
1369184054Slulf	for (i = 0; i < size; i++) {
1370184054Slulf		buf[0] = textline[i];
1371184054Slulf		buf[1] = '\0';
1372184054Slulf		count = 1;
1373184054Slulf		/* Expand @'s */
1374184054Slulf		if (buf[0] == '@') {
1375184054Slulf			buf[1] = '@';
1376184054Slulf			buf[2] = '\0';
1377184054Slulf			count = 2;
1378184054Slulf		}
1379190422Slulf		if (stream_write(dest, buf, count) == -1)
1380190422Slulf			return (-1);
1381184054Slulf	}
1382190422Slulf	return (0);
1383184054Slulf}
1384184054Slulf
1385184054Slulf/* Set delta state. */
1386184054Slulfvoid
1387184054Slulfrcsdelta_setstate(struct delta *d, char *state)
1388184054Slulf{
1389184054Slulf
1390184054Slulf	if (d->state != NULL)
1391184054Slulf		free(state);
1392184054Slulf	if (state != NULL) {
1393184054Slulf		d->state = xstrdup(state);
1394184054Slulf		return;
1395184054Slulf	}
1396184054Slulf	d->state = NULL;
1397184054Slulf}
1398184054Slulf
1399184054Slulf/* Truncate the deltalog with a certain offset. */
1400184054Slulfvoid
1401184054Slulfrcsdelta_truncatelog(struct delta *d, off_t offset)
1402184054Slulf{
1403184054Slulf
1404184054Slulf	stream_truncate_buf(d->log, offset);
1405184054Slulf}
1406184054Slulf
1407184054Slulf/* Truncate the deltatext with a certain offset. */
1408184054Slulfvoid
1409184054Slulfrcsdelta_truncatetext(struct delta *d, off_t offset)
1410184054Slulf{
1411184054Slulf
1412184054Slulf	stream_truncate_buf(d->text, offset);
1413184054Slulf}
1414