rcsfile.c revision 190422
1/*-
2 * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/contrib/csup/rcsfile.c 190422 2009-03-25 20:15:48Z lulf $
27 */
28
29#include <assert.h>
30#include <err.h>
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include "diff.h"
37#include "keyword.h"
38#include "misc.h"
39#include "proto.h"
40#include "queue.h"
41#include "rcsfile.h"
42#include "rcsparse.h"
43#include "stream.h"
44
45#define BUF_SIZE_DEFAULT	128
46
47/*
48 * RCS parser library. This is the part of the library that handles the
49 * importing, editing and exporting of RCS files. It currently supports only the
50 * part of the RCS file specification that is needed for csup (for instance,
51 * newphrases are not supported), and assumes that you can store the whole RCS
52 * file in memory.
53 */
54
55/*
56 * Linked list for string tokens.
57 */
58struct string {
59	char *str;
60	STAILQ_ENTRY(string) string_next;
61};
62
63/*
64 * Linked list of tags and revision numbers, in the RCS file header.
65 */
66struct tag {
67	char *tag;
68	char *revnum;
69	STAILQ_ENTRY(tag) tag_next;
70};
71
72/*
73 * A RCS delta. The delta is identified by a revision number, and contains the
74 * most important RCS attributes that is needed by csup. It also contains
75 * pointers to other nodes in the RCS file delta structure.
76 */
77struct delta {
78	char *revdate;
79	char *revnum;
80	char *author;
81	char *state;
82	struct buf *log;
83	struct buf *text;
84	int placeholder;
85	struct delta *diffbase;
86	struct delta *prev;
87
88	LIST_ENTRY(delta) delta_next;
89	STAILQ_ENTRY(delta) delta_prev;
90	LIST_ENTRY(delta) table_next;
91	STAILQ_ENTRY(delta) stack_next;
92	LIST_HEAD(, branch) branchlist;
93	LIST_ENTRY(delta) branch_next_date;
94};
95
96/*
97 * A branch data structure containing information about deltas in the branch as
98 * well as a base revision number.
99 */
100struct branch {
101	char *revnum;
102	LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */
103	LIST_ENTRY(branch) branch_next;
104};
105
106/*
107 * The rcsfile structure is the "main" structure of the RCS parser library. It
108 * contains administrative data as well as pointers to the deltas within the
109 * file.
110 */
111struct rcsfile {
112	char *name;
113	char *head;
114	char *branch;	/* Default branch. */
115	char *cvsroot;
116	char *colltag;
117	STAILQ_HEAD(, string) accesslist;
118	STAILQ_HEAD(, tag) taglist;
119	int strictlock;
120	char *comment;
121	int expand;
122	int ro;
123	struct branch *trunk; /* The tip delta. */
124
125	LIST_HEAD(, delta) deltatable;
126
127	char *desc;
128};
129
130static void		 rcsfile_freedelta(struct delta *);
131static void		 rcsfile_insertdelta(struct branch *, struct delta *,
132			     int);
133static struct delta	*rcsfile_createdelta(char *);
134static int		 rcsfile_write_deltatext(struct rcsfile *,
135			     struct stream *);
136static int		 rcsfile_puttext(struct rcsfile *, struct stream *,
137			     struct delta *, struct delta *);
138static struct branch	*rcsfile_getbranch(struct rcsfile *, char *);
139static void		 rcsfile_insertsorteddelta(struct rcsfile *,
140			     struct delta *);
141static struct stream 	*rcsfile_getdeltatext(struct rcsfile *, struct delta *,
142			     struct buf **);
143static int		 rcsdelta_writestring(char *, size_t, struct stream *);
144static void		 rcsdelta_insertbranch(struct delta *, struct branch *);
145
146/* Space formatting of RCS file. */
147const char *head_space = "\t";
148const char *branch_space = "\t";
149const char *tag_space = "\t";
150const char *date_space = "\t";
151const char *auth_space = "\t";
152const char *state_space = "\t";
153const char *next_space = "\t";
154const char *branches_space = "\t";
155const char *comment_space ="\t";
156const char *expand_space = "\t";
157
158void print_stream(struct stream *);
159
160/* Print the contents of a stream, for debugging. */
161void
162print_stream(struct stream *s)
163{
164	char *line;
165
166	line = stream_getln(s, NULL);
167	while (line != NULL) {
168		lprintf(-1, "%s\n", line);
169		line = stream_getln(s, NULL);
170	}
171	lprintf(-1, "\n");
172}
173
174/*
175 * Parse rcsfile from path and return a pointer to it.
176 */
177struct rcsfile *
178rcsfile_frompath(char *path, char *name, char *cvsroot, char *colltag, int ro)
179{
180	struct rcsfile *rf;
181	FILE *infp;
182	int error;
183
184	if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
185		return (NULL);
186
187	rf = xmalloc(sizeof(struct rcsfile));
188	rf->name = xstrdup(name);
189	rf->cvsroot = xstrdup(cvsroot);
190	rf->colltag = xstrdup(colltag);
191
192	/* Initialize head branch. */
193	rf->trunk = xmalloc(sizeof(struct branch));
194	rf->trunk->revnum = xstrdup("1");
195	LIST_INIT(&rf->trunk->deltalist);
196	/* Initialize delta list. */
197	LIST_INIT(&rf->deltatable);
198	/* Initialize tag list. */
199	STAILQ_INIT(&rf->taglist);
200	/* Initialize accesslist. */
201	STAILQ_INIT(&rf->accesslist);
202
203	/* Initialize all fields. */
204	rf->head = NULL;
205	rf->branch = NULL;
206	rf->strictlock = 0;
207	rf->comment = NULL;
208	rf->expand = EXPAND_DEFAULT;
209	rf->desc = NULL;
210	rf->ro = ro;
211
212	infp = fopen(path, "r");
213	if (infp == NULL) {
214		lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
215		rcsfile_free(rf);
216		return (NULL);
217	}
218	error = rcsparse_run(rf, infp, ro);
219	fclose(infp);
220	if (error) {
221		lprintf(-1, "Error parsing \"%s\"\n", name);
222		rcsfile_free(rf);
223		return (NULL);
224	}
225	return (rf);
226}
227
228/*
229 * Write content of rcsfile to server. Assumes we have a complete RCS file
230 * loaded.
231 */
232int
233rcsfile_send_details(struct rcsfile *rf, struct stream *wr)
234{
235	struct delta *d;
236	struct tag *t;
237	const char *keyword;
238	int error;
239
240	assert(rf != NULL);
241
242	error = proto_printf(wr, "V %s\n", rf->name);
243	if (error)
244		return(error);
245
246	/* Write default branch. */
247	if (rf->branch == NULL)
248		error = proto_printf(wr, "b\n");
249	else
250		error = proto_printf(wr, "B %s\n", rf->branch);
251	if (error)
252		return(error);
253
254	/* Write deltas to server. */
255	error = proto_printf(wr, "D\n");
256	if (error)
257		return(error);
258
259	LIST_FOREACH(d, &rf->deltatable, table_next) {
260		error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
261		if (error)
262			return(error);
263	}
264	error = proto_printf(wr, ".\n");
265
266	if (error)
267		return(error);
268	/* Write expand. */
269	if (rf->expand != EXPAND_DEFAULT) {
270		keyword = keyword_encode_expand(rf->expand);
271		if (keyword != NULL) {
272			error = proto_printf(wr, "E %s\n",
273			    keyword_encode_expand(rf->expand));
274			if (error)
275				return(error);
276		}
277	}
278
279	/* Write tags to server. */
280	error = proto_printf(wr, "T\n");
281	if (error)
282		return(error);
283	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
284		error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
285		if (error)
286			return(error);
287	}
288	error = proto_printf(wr, ".\n");
289	if (error)
290		return(error);
291	error = proto_printf(wr, ".\n");
292	return (error);
293}
294
295/*
296 * Write a RCS file to disk represented by the destination stream. Keep track of
297 * deltas with a stack and an inverted stack.
298 */
299int
300rcsfile_write(struct rcsfile *rf, struct stream *dest)
301{
302	STAILQ_HEAD(, delta) deltastack;
303	STAILQ_HEAD(, delta) deltalist_inverted;
304	struct tag *t;
305	struct branch *b;
306	struct delta *d, *d_tmp, *d_next;
307	int error;
308
309	/* First write head. */
310	d = LIST_FIRST(&rf->trunk->deltalist);
311	if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0)
312		return (-1);
313
314	/* Write branch, if we have. */
315	if (rf->branch != NULL) {
316		if (stream_printf(dest, "branch%s%s;\n", branch_space,
317		    rf->branch) < 0)
318			return (-1);
319	}
320
321	/* Write access. */
322	if (stream_printf(dest, "access") < 0)
323		return (-1);
324#if 0
325	if (!STAILQ_EMPTY(&rf->accesslist)) {
326		/*
327		 * XXX: Write out access. This doesn't seem to be necessary for
328		 * the time being.
329		 */
330	}
331#endif
332	if (stream_printf(dest, ";\n") < 0)
333		return (-1);
334
335	/* Write out taglist. */
336	if (stream_printf(dest, "symbols") < 0)
337		return (-1);
338	if (!STAILQ_EMPTY(&rf->taglist)) {
339		STAILQ_FOREACH(t, &rf->taglist, tag_next) {
340			if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag,
341			    t->revnum) < 0)
342				return (-1);
343		}
344	}
345
346	/* Write out locks and strict. */
347	if (stream_printf(dest, ";\nlocks;") < 0)
348		return (-1);
349	if (rf->strictlock) {
350		if (stream_printf(dest, " strict;") < 0)
351			return (-1);
352	}
353	if (stream_printf(dest, "\n") < 0)
354		return (-1);
355
356	/* Write out the comment. */
357	if (rf->comment != NULL) {
358		if (stream_printf(dest, "comment%s%s;\n", comment_space,
359		    rf->comment) < 0)
360			return (-1);
361	}
362	if (rf->expand != EXPAND_DEFAULT) {
363		if (stream_printf(dest, "expand%s@%s@;\n", expand_space,
364		    keyword_encode_expand(rf->expand)) < 0)
365			return (-1);
366	}
367
368	if (stream_printf(dest, "\n\n") < 0)
369		return (-1);
370
371	/*
372	 * Write out deltas. We use a stack where we push the appropriate deltas
373	 * that is to be written out during the loop.
374	 */
375	STAILQ_INIT(&deltastack);
376	d = LIST_FIRST(&rf->trunk->deltalist);
377	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
378	while (!STAILQ_EMPTY(&deltastack)) {
379		d = STAILQ_FIRST(&deltastack);
380		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
381		/* Do not write out placeholders just to be safe. */
382		if (d->placeholder)
383			continue;
384		if (stream_printf(dest, "%s\n", d->revnum) < 0)
385			return (-1);
386		if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
387		    date_space, d->revdate, auth_space, d->author,
388		    state_space) < 0)
389			return (-1);
390		if (d->state != NULL) {
391			if (stream_printf(dest, " %s", d->state) < 0)
392				return (-1);
393		}
394		if (stream_printf(dest, ";\nbranches") < 0)
395			return (-1);
396		/*
397		 * Write out our branches. Add them to a reversed list for use
398		 * later when we write out the text.
399		 */
400		STAILQ_INIT(&deltalist_inverted);
401		LIST_FOREACH(b, &d->branchlist, branch_next) {
402			d_tmp = LIST_FIRST(&b->deltalist);
403			STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);
404			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
405		}
406
407		/* Push branch heads on stack. */
408		STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
409			if (d_tmp == NULL) {
410				lprintf(2, "Empty branch!\n");
411				return (-1);
412			}
413			if (stream_printf(dest, "\n%s%s", branches_space,
414			    d_tmp->revnum) < 0)
415				return (-1);
416		}
417
418		if (stream_printf(dest, ";\nnext%s", next_space) < 0)
419			return (-1);
420		/* Push next delta on stack. */
421		d_next = LIST_NEXT(d, delta_next);
422		if (d_next != NULL) {
423			if (stream_printf(dest, "%s", d_next->revnum) < 0)
424				return (-1);
425			STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
426		}
427		if (stream_printf(dest, ";\n\n") < 0)
428			return (-1);
429	}
430	/* Write out desc. */
431	if (stream_printf(dest, "\ndesc\n@@") < 0)
432		return (-1);
433	d = LIST_FIRST(&rf->trunk->deltalist);
434
435	/* Write out deltatexts. */
436	error = rcsfile_write_deltatext(rf, dest);
437	if (stream_printf(dest, "\n") < 0)
438		return (-1);
439	return (error);
440}
441
442/*
443 * Write out deltatexts of a delta and it's subbranches recursively.
444 */
445int
446rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
447{
448	STAILQ_HEAD(, delta) deltastack;
449	LIST_HEAD(, delta) branchlist_datesorted;
450	struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
451	struct stream *in;
452	struct branch *b;
453	size_t size;
454	char *line;
455	int error;
456
457	error = 0;
458	STAILQ_INIT(&deltastack);
459	d = LIST_FIRST(&rf->trunk->deltalist);
460	d->prev = NULL;
461	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
462	while (!STAILQ_EMPTY(&deltastack)) {
463		d = STAILQ_FIRST(&deltastack);
464		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
465		/* Do not write out placeholders just to be safe. */
466		if (d->placeholder)
467			return (0);
468		if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0)
469			return (-1);
470		if (stream_printf(dest, "log\n@") < 0)
471			return (-1);
472		in = stream_open_buf(d->log);
473		line = stream_getln(in, &size);
474		while (line != NULL) {
475			if (stream_write(dest, line, size) == -1)
476				return (-1);
477			line = stream_getln(in, &size);
478		}
479		stream_close(in);
480		if (stream_printf(dest, "@\ntext\n@") < 0)
481			return (-1);
482		error = rcsfile_puttext(rf, dest, d, d->prev);
483		if (error)
484			return (error);
485		if (stream_printf(dest, "@") < 0)
486			return (-1);
487
488		LIST_INIT(&branchlist_datesorted);
489		d_next = LIST_NEXT(d, delta_next);
490		if (d_next != NULL) {
491			d_next->prev = d;
492			/*
493			 * If it's trunk, treat it like the oldest, if not treat
494			 * it like a child.
495			 */
496			if (rcsrev_istrunk(d_next->revnum))
497				STAILQ_INSERT_HEAD(&deltastack, d_next,
498				    stack_next);
499			else
500				LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
501				    branch_next_date);
502		}
503
504		/*
505		 * First, we need to sort our branches based on their date to
506		 * take into account some self-hacked RCS files.
507		 */
508		LIST_FOREACH(b, &d->branchlist, branch_next) {
509			d_tmp = LIST_FIRST(&b->deltalist);
510			if (LIST_EMPTY(&branchlist_datesorted)) {
511				LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp,
512				    branch_next_date);
513				continue;
514			}
515
516			d_tmp2 = LIST_FIRST(&branchlist_datesorted);
517			if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) {
518				LIST_INSERT_BEFORE(d_tmp2, d_tmp,
519				    branch_next_date);
520				continue;
521			}
522			while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
523			    != NULL) {
524				if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
525				    <= 0)
526					break;
527				d_tmp2 = d_tmp3;
528			}
529			LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
530		}
531		/*
532		 * Invert the deltalist of a branch, since we're writing them
533		 * the opposite way.
534		 */
535		LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
536                        d_tmp->prev = d;
537			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
538		}
539	}
540	return (0);
541}
542
543/*
544 * Generates text given a delta and a diffbase.
545 */
546static int
547rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
548    struct delta *diffbase)
549{
550	struct stream *in, *rd, *orig;
551	struct keyword *k;
552	struct diffinfo dibuf, *di;
553	struct buf *b;
554	size_t size;
555	char *line;
556	int error;
557
558	di = &dibuf;
559	b = NULL;
560	error = 0;
561
562	/* Write if the diffbase is the previous */
563	if (d->diffbase == diffbase) {
564
565		/* Write out the text. */
566		in = stream_open_buf(d->text);
567		line = stream_getln(in, &size);
568		while (line != NULL) {
569			if (stream_write(dest, line, size) == -1) {
570				error = -1;
571				goto cleanup;
572			}
573			line = stream_getln(in, &size);
574		}
575		stream_close(in);
576	/* We need to apply diff to produce text, this is probably HEAD. */
577	} else if (diffbase == NULL) {
578		/* Apply diff. */
579		orig = rcsfile_getdeltatext(rf, d, &b);
580		if (orig == NULL) {
581			error = -1;
582			goto cleanup;
583		}
584		line = stream_getln(orig, &size);
585		while (line != NULL) {
586			if (stream_write(dest, line, size) == -1) {
587				error = -1;
588				goto cleanup;
589			}
590			line = stream_getln(orig, &size);
591		}
592		stream_close(orig);
593	/*
594	 * A new head was probably added, and now the previous HEAD must be
595	 * changed to include the diff instead.
596	 */
597	} else if (diffbase->diffbase == d) {
598		/* Get reverse diff. */
599		orig = rcsfile_getdeltatext(rf, d, &b);
600		if (orig == NULL) {
601			error = -1;
602			goto cleanup;
603		}
604		di->di_rcsfile = rf->name;
605		di->di_cvsroot = rf->cvsroot;
606		di->di_revnum = d->revnum;
607		di->di_revdate = d->revdate;
608		di->di_author = d->author;
609		di->di_tag = rf->colltag;
610		di->di_state = d->state;
611		di->di_expand = EXPAND_OLD;
612		k = keyword_new();
613
614		rd = stream_open_buf(diffbase->text);
615		error = diff_reverse(rd, orig, dest, k, di);
616		if (error) {
617			lprintf(-1, "Error applying reverse diff: %d\n", error);
618			goto cleanup;
619		}
620		keyword_free(k);
621		stream_close(rd);
622		stream_close(orig);
623	}
624cleanup:
625	if (b != NULL)
626		buf_free(b);
627	return (error);
628}
629
630/*
631 * Return a stream with an applied diff of a delta.
632 * XXX: extra overhead on the last apply. Could write directly to file, but
633 * makes things complicated though.
634 */
635static struct stream *
636rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
637{
638	struct diffinfo dibuf, *di;
639	struct stream *orig, *dest, *rd;
640	struct buf *buf_orig;
641	struct keyword *k;
642	int error;
643
644	buf_orig = NULL;
645	error = 0;
646
647	/*
648	 * If diffbase is NULL or we are head (the old head), we have a normal
649	 * complete deltatext.
650	 */
651	if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
652		orig = stream_open_buf(d->text);
653		return (orig);
654	}
655
656	di = &dibuf;
657	/* If not, we need to apply our diff to that of our diffbase. */
658	orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
659	if (orig == NULL)
660		return (NULL);
661
662	/*
663	 * Now that we are sure we have a complete deltatext in ret, let's apply
664	 * our diff to it.
665	 */
666	*buf_dest = buf_new(BUF_SIZE_DEFAULT);
667	dest = stream_open_buf(*buf_dest);
668
669	di->di_rcsfile = rf->name;
670	di->di_cvsroot = rf->cvsroot;
671	di->di_revnum = d->revnum;
672	di->di_revdate = d->revdate;
673	di->di_author = d->author;
674	di->di_tag = rf->colltag;
675	di->di_state = d->state;
676	di->di_expand = EXPAND_OLD;
677	rd = stream_open_buf(d->text);
678	k = keyword_new();
679	error = diff_apply(rd, orig, dest, k, di, 0);
680	stream_flush(dest);
681	stream_close(rd);
682	stream_close(orig);
683	stream_close(dest);
684	keyword_free(k);
685	if (buf_orig != NULL)
686		buf_free(buf_orig);
687	if (error) {
688		lprintf(-1, "Error applying diff: %d\n", error);
689		return (NULL);
690	}
691
692	/* Now reopen the stream for the reading. */
693	dest = stream_open_buf(*buf_dest);
694	return (dest);
695}
696
697/* Print content of rcsfile. Useful for debugging. */
698void
699rcsfile_print(struct rcsfile *rf)
700{
701	struct delta *d;
702	struct tag *t;
703	struct string *s;
704	struct stream *in;
705	char *line;
706
707	lprintf(1, "\n");
708	if (rf->name != NULL)
709		lprintf(1, "name: '%s'\n", rf->name);
710	if (rf->head != NULL)
711		lprintf(1, "head: '%s'\n", rf->head);
712	if (rf->branch != NULL)
713		lprintf(1, "branch: '%s'\n", rf->branch);
714	lprintf(1, "Access: ");
715	STAILQ_FOREACH(s, &rf->accesslist, string_next)
716		lprintf(1, "'%s' ", s->str);
717	lprintf(1, "\n");
718
719	/* Print all tags. */
720	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
721		lprintf(1, "Tag: ");
722		if (t->tag != NULL)
723			lprintf(1, "name: %s ", t->tag);
724		if (t->revnum != NULL)
725			lprintf(1, "rev: %s", t->revnum);
726		lprintf(1, "\n");
727	}
728
729	if (rf->strictlock)
730		lprintf(1, "Strict!\n");
731	if (rf->comment != NULL)
732		lprintf(1, "comment: '%s'\n", rf->comment);
733	if (rf->expand != EXPAND_DEFAULT);
734		lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand));
735
736	/* Print all deltas. */
737	LIST_FOREACH(d, &rf->deltatable, table_next) {
738		lprintf(1, "Delta: ");
739		if (d->revdate != NULL)
740			lprintf(1, "date: %s ", d->revdate);
741		if (d->revnum != NULL)
742			lprintf(1, "rev: %s", d->revnum);
743		if (d->author != NULL)
744			lprintf(1, "author: %s", d->author);
745		if (d->state != NULL)
746			lprintf(1, "state: %s", d->state);
747
748		lprintf(1, "Text:\n");
749		in = stream_open_buf(d->text);
750		line = stream_getln(in, NULL);
751		while (line != NULL) {
752			lprintf(1, "TEXT: %s\n", line);
753			line = stream_getln(in, NULL);
754		}
755		stream_close(in);
756		lprintf(1, "\n");
757	}
758
759	if (rf->desc != NULL)
760		lprintf(1, "desc: '%s'\n", rf->desc);
761}
762
763/* Free all memory associated with a struct rcsfile. */
764void
765rcsfile_free(struct rcsfile *rf)
766{
767	struct delta *d;
768	struct tag *t;
769	struct string *s;
770
771	if (rf->name != NULL)
772		free(rf->name);
773	if (rf->head != NULL)
774		free(rf->head);
775	if (rf->branch != NULL)
776		free(rf->branch);
777	if (rf->cvsroot != NULL)
778		free(rf->cvsroot);
779	if (rf->colltag != NULL)
780		free(rf->colltag);
781
782	/* Free all access ids. */
783	while (!STAILQ_EMPTY(&rf->accesslist)) {
784		s = STAILQ_FIRST(&rf->accesslist);
785		STAILQ_REMOVE_HEAD(&rf->accesslist, string_next);
786		if (s->str != NULL)
787			free(s->str);
788		free(s);
789	}
790
791	/* Free all tags. */
792	while (!STAILQ_EMPTY(&rf->taglist)) {
793		t = STAILQ_FIRST(&rf->taglist);
794		STAILQ_REMOVE_HEAD(&rf->taglist, tag_next);
795		if (t->tag != NULL)
796			free(t->tag);
797		if (t->revnum != NULL)
798			free(t->revnum);
799		free(t);
800	}
801
802	if (rf->comment != NULL)
803		free(rf->comment);
804
805	/* Free all deltas in global list */
806	while (!LIST_EMPTY(&rf->deltatable)) {
807		d = LIST_FIRST(&rf->deltatable);
808		if (!rf->ro)
809			LIST_REMOVE(d, delta_next);
810		LIST_REMOVE(d, table_next);
811		rcsfile_freedelta(d);
812	}
813
814	/* Free global branch. */
815	if (rf->trunk->revnum != NULL)
816		free(rf->trunk->revnum);
817	free(rf->trunk);
818
819	if (rf->desc != NULL)
820		free(rf->desc);
821
822	free(rf);
823}
824
825/*
826 * Free a RCS delta.
827 */
828static void
829rcsfile_freedelta(struct delta *d)
830{
831	struct branch *b;
832
833	if (d->revdate != NULL)
834		free(d->revdate);
835	if (d->revnum != NULL)
836		free(d->revnum);
837	if (d->author != NULL)
838		free(d->author);
839	if (d->state != NULL)
840		free(d->state);
841	if (d->log != NULL)
842		buf_free(d->log);
843	if (d->text != NULL)
844		buf_free(d->text);
845
846	/* Free all subbranches of a delta. */
847	while (!LIST_EMPTY(&d->branchlist)) {
848		b = LIST_FIRST(&d->branchlist);
849		LIST_REMOVE(b, branch_next);
850		free(b->revnum);
851		free(b);
852	}
853	free(d);
854}
855
856/*
857 * Functions for editing RCS deltas.
858 */
859
860/* Add a new entry to the access list. */
861void
862rcsfile_addaccess(struct rcsfile *rf, char *id)
863{
864	struct string *s;
865
866	s = xmalloc(sizeof(struct string));
867	s->str = xstrdup(id);
868	STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next);
869}
870
871/* Add a tag to a RCS file. */
872void
873rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum)
874{
875	struct tag *t;
876
877	t = xmalloc(sizeof(struct tag));
878	t->tag = xstrdup(tag);
879	t->revnum = xstrdup(revnum);
880
881	STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next);
882}
883
884/* Import a tag to a RCS file. */
885void
886rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum)
887{
888	struct tag *t;
889
890	t = xmalloc(sizeof(struct tag));
891	t->tag = xstrdup(tag);
892	t->revnum = xstrdup(revnum);
893
894	STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next);
895}
896
897/*
898 * Delete a revision from the global delta list and the branch it is in. Csup
899 * will tell us to delete the tags involved.
900 */
901void
902rcsfile_deleterev(struct rcsfile *rf, char *revname)
903{
904	struct delta *d;
905
906	d = rcsfile_getdelta(rf, revname);
907	if (!rf->ro)
908		LIST_REMOVE(d, delta_next);
909	LIST_REMOVE(d, table_next);
910	rcsfile_freedelta(d);
911}
912
913/* Delete a tag from the tag list. */
914void
915rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum)
916{
917	struct tag *t;
918
919	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
920		if ((strcmp(tag, t->tag) == 0) &&
921		    (strcmp(revnum, t->revnum) == 0)) {
922			STAILQ_REMOVE(&rf->taglist, t, tag, tag_next);
923			free(t->tag);
924			free(t->revnum);
925			free(t);
926			return;
927		}
928	}
929}
930
931/*
932 * Searches the global deltalist for a delta.
933 */
934struct delta *
935rcsfile_getdelta(struct rcsfile *rf, char *revnum)
936{
937	struct delta *d;
938
939	LIST_FOREACH(d, &rf->deltatable, table_next) {
940		if (strcmp(revnum, d->revnum) == 0)
941			return (d);
942	}
943	return (NULL);
944}
945
946/* Set rcsfile head. */
947void
948rcsfile_setval(struct rcsfile *rf, int field, char *val)
949{
950	size_t len;
951
952	switch (field) {
953	case RCSFILE_HEAD:
954		if (rf->head != NULL)
955			free(rf->head);
956		rf->head = xstrdup(val);
957		break;
958	case RCSFILE_BRANCH:
959		if (rf->branch != NULL)
960			free(rf->branch);
961		rf->branch = (val == NULL) ? NULL : xstrdup(val);
962		break;
963	case RCSFILE_STRICT:
964		if (val != NULL)
965			rf->strictlock = 1;
966		break;
967	case RCSFILE_COMMENT:
968		if (rf->comment != NULL)
969			free(rf->comment);
970		rf->comment = xstrdup(val);
971		break;
972	case RCSFILE_EXPAND:
973		len = strlen(val) - 1;
974		val++;
975		val[len - 1] = '\0';
976		rf->expand = keyword_decode_expand(val);
977		break;
978	case RCSFILE_DESC:
979		if (rf->desc != NULL)
980			free(rf->desc);
981		rf->desc = xstrdup(val);
982		break;
983	default:
984		lprintf(-1, "Setting invalid RCSfile value.\n");
985		break;
986	}
987}
988
989/* Create and initialize a delta. */
990static struct delta *
991rcsfile_createdelta(char *revnum)
992{
993	struct delta *d;
994
995	d = xmalloc(sizeof(struct delta));
996	d->revnum = xstrdup(revnum);
997	d->revdate = NULL;
998	d->state = NULL;
999	d->author = NULL;
1000	d->log = buf_new(BUF_SIZE_DEFAULT);
1001	d->text = buf_new(BUF_SIZE_DEFAULT);
1002	d->diffbase = NULL;
1003
1004	LIST_INIT(&d->branchlist);
1005	return (d);
1006}
1007
1008/* Add a delta to a imported delta tree. Used by the updater. */
1009struct delta *
1010rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1011    char *diffbase)
1012{
1013	struct branch *b;
1014	struct delta *d, *d_bp, *d_next;
1015	char *brev, *bprev;
1016	int trunk;
1017
1018	d_next = NULL;
1019	d = rcsfile_getdelta(rf, revnum);
1020	if (d != NULL) {
1021		lprintf(-1, "Delta %s already exists!\n", revnum);
1022		return (NULL);
1023	}
1024	d = rcsfile_createdelta(revnum);
1025	d->placeholder = 0;
1026	d->revdate = xstrdup(revdate);
1027	d->author = xstrdup(author);
1028	d->diffbase = rcsfile_getdelta(rf, diffbase);
1029
1030	/* If it's trunk, insert it in the head branch list. */
1031	b = rcsrev_istrunk(d->revnum) ? rf->trunk :
1032	    rcsfile_getbranch(rf, d->revnum);
1033
1034	/*
1035	 * We didn't find a branch, check if we can find a branchpoint and
1036	 * create a branch there.
1037	 */
1038	if (b == NULL) {
1039		brev = rcsrev_prefix(d->revnum);
1040		bprev = rcsrev_prefix(brev);
1041
1042		d_bp = rcsfile_getdelta(rf, bprev);
1043		free(bprev);
1044		if (d_bp == NULL) {
1045			lprintf(-1, "No branch point for adding delta %s\n",
1046			    d->revnum);
1047			return (NULL);
1048		}
1049
1050		/* Create the branch and insert in delta. */
1051		b = xmalloc(sizeof(struct branch));
1052		b->revnum = brev;
1053		LIST_INIT(&b->deltalist);
1054		rcsdelta_insertbranch(d_bp, b);
1055	}
1056
1057	/* Insert both into the tree, and into the lookup list. */
1058	trunk = rcsrev_istrunk(d->revnum);
1059	rcsfile_insertdelta(b, d, trunk);
1060	rcsfile_insertsorteddelta(rf, d);
1061	return (d);
1062}
1063
1064/* Adds a delta to a rcsfile struct. Used by the parser. */
1065void
1066rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1067    char *state, char *next)
1068{
1069	struct branch *b;
1070	struct delta *d, *d_bp, *d_next;
1071	char *brev, *bprev;
1072	int trunk;
1073
1074	d_next = NULL;
1075	d = rcsfile_getdelta(rf, revnum);
1076
1077	if (d == NULL) {
1078		/* If not, we'll just create a new entry. */
1079		d = rcsfile_createdelta(revnum);
1080		d->placeholder = 0;
1081	} else {
1082		if (d->placeholder == 0) {
1083			lprintf(-1, "Trying to import already existing delta\n");
1084			return;
1085		}
1086	}
1087	/*
1088	 * If already exists, assume that only revnum is filled out, and set the
1089	 * rest of the fields. This should be an OK assumption given that we can
1090	 * be sure internally that the structure is sufficiently initialized so
1091	 * we won't have any unfreed memory.
1092	 */
1093	d->revdate = xstrdup(revdate);
1094	d->author = xstrdup(author);
1095	if (state != NULL)
1096		d->state = xstrdup(state);
1097
1098	/* If we have a next, create a placeholder for it. */
1099	if (next != NULL) {
1100		d_next = rcsfile_createdelta(next);
1101		d_next->placeholder = 1;
1102		/* Diffbase should be the previous. */
1103		d_next->diffbase = d;
1104	}
1105
1106	/* If we're opening read-only, do minimal work. */
1107	if (rf->ro) {
1108		if (!d->placeholder)
1109			rcsfile_insertsorteddelta(rf, d);
1110		else
1111			d->placeholder = 0;
1112		if (d_next != NULL)
1113			rcsfile_insertsorteddelta(rf, d_next);
1114		return;
1115	}
1116
1117	/* If it's trunk, insert it in the head branch list. */
1118	b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
1119	    d->revnum);
1120
1121	/*
1122	 * We didn't find a branch, check if we can find a branchpoint and
1123	 * create a branch there.
1124	 */
1125	if (b == NULL) {
1126		brev = rcsrev_prefix(d->revnum);
1127		bprev = rcsrev_prefix(brev);
1128
1129		d_bp = rcsfile_getdelta(rf, bprev);
1130		free(bprev);
1131		if (d_bp == NULL) {
1132			lprintf(-1, "No branch point for adding delta %s\n",
1133			    d->revnum);
1134			return;
1135		}
1136
1137		/* Create the branch and insert in delta. */
1138		b = xmalloc(sizeof(struct branch));
1139		b->revnum = brev;
1140		LIST_INIT(&b->deltalist);
1141		rcsdelta_insertbranch(d_bp, b);
1142	}
1143
1144	/* Insert if not a placeholder. */
1145	if (!d->placeholder) {
1146		/* Insert both into the tree, and into the lookup list. */
1147		if (rcsrev_istrunk(d->revnum))
1148			rcsfile_insertdelta(b, d, 1);
1149		else {
1150			rcsfile_insertdelta(b, d, 0);
1151			/*
1152			 * On import we need to set the diffbase to our
1153			 * branchpoint for writing out later.
1154			 */
1155			if (LIST_FIRST(&b->deltalist) == d) {
1156				brev = rcsrev_prefix(d->revnum);
1157				bprev = rcsrev_prefix(brev);
1158				d_bp = rcsfile_getdelta(rf, bprev);
1159				/* This should really not happen. */
1160				assert(d_bp != NULL);
1161				d->diffbase = d_bp;
1162				free(brev);
1163				free(bprev);
1164			}
1165		}
1166		rcsfile_insertsorteddelta(rf, d);
1167	} else /* Not a placeholder anymore. */ {
1168		d->placeholder = 0;
1169		/* Put it into the tree. */
1170		trunk = rcsrev_istrunk(d->revnum);
1171		rcsfile_insertdelta(b, d, trunk);
1172	}
1173
1174	/* If we have a next, insert the placeholder into the lookup list. */
1175	if (d_next != NULL)
1176		rcsfile_insertsorteddelta(rf, d_next);
1177}
1178
1179/*
1180 * Find the branch of a revision number.
1181 */
1182static struct branch *
1183rcsfile_getbranch(struct rcsfile *rf, char *revnum)
1184{
1185	struct branch *b;
1186	struct delta *d;
1187	char *branchrev, *bprev;
1188
1189	branchrev = rcsrev_prefix(revnum);
1190	bprev = rcsrev_prefix(branchrev);
1191	d = rcsfile_getdelta(rf, bprev);
1192	free(bprev);
1193	LIST_FOREACH(b, &d->branchlist, branch_next) {
1194		if(rcsnum_cmp(b->revnum, branchrev) == 0) {
1195			free(branchrev);
1196			return (b);
1197		}
1198	}
1199	free(branchrev);
1200	return (NULL);
1201}
1202
1203/* Insert a branch into a delta, sorted by branch revision date. */
1204static void
1205rcsdelta_insertbranch(struct delta *d, struct branch *b)
1206{
1207	struct branch *b_iter;
1208
1209	/* If it's empty, insert into head. */
1210	if (LIST_EMPTY(&d->branchlist)) {
1211		LIST_INSERT_HEAD(&d->branchlist, b, branch_next);
1212		return;
1213	}
1214
1215	/* Just put it in before the revdate that is lower. */
1216	LIST_FOREACH(b_iter, &d->branchlist, branch_next) {
1217		if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) {
1218			LIST_INSERT_BEFORE(b_iter, b, branch_next);
1219			return;
1220		}
1221		if (LIST_NEXT(b_iter, branch_next) == NULL)
1222			break;
1223	}
1224	/* Insert after last element. */
1225	LIST_INSERT_AFTER(b_iter, b, branch_next);
1226}
1227
1228/* Insert a delta into the correct place in the table of the rcsfile. */
1229static void
1230rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d)
1231{
1232	struct delta *d2;
1233
1234	/* If it's empty, insert into head. */
1235	if (LIST_EMPTY(&rf->deltatable)) {
1236		LIST_INSERT_HEAD(&rf->deltatable, d, table_next);
1237		return;
1238	}
1239
1240	/* Just put it in before the revdate that is lower. */
1241	LIST_FOREACH(d2, &rf->deltatable, table_next) {
1242		if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) {
1243			LIST_INSERT_BEFORE(d2, d, table_next);
1244			return;
1245		}
1246		if (LIST_NEXT(d2, table_next) == NULL)
1247			break;
1248	}
1249	/* Insert after last element. */
1250	LIST_INSERT_AFTER(d2, d, table_next);
1251}
1252
1253/*
1254 * Insert a delta into the correct place in branch. A trunk branch will have
1255 * different ordering scheme and be sorted by revision number, but a normal
1256 * branch will be sorted by date to maintain compability with branches that is
1257 * "hand-hacked".
1258 */
1259static void
1260rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk)
1261{
1262	struct delta *d2;
1263
1264	/* If it's empty, insert into head. */
1265	if (LIST_EMPTY(&b->deltalist)) {
1266		LIST_INSERT_HEAD(&b->deltalist, d, delta_next);
1267		return;
1268	}
1269
1270	/*
1271	 * Just put it in before the revnum that is lower. Sort trunk branch by
1272	 * branchnum but the subbranches after deltadate.
1273	 */
1274	LIST_FOREACH(d2, &b->deltalist, delta_next) {
1275		if (trunk) {
1276			if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) {
1277				LIST_INSERT_BEFORE(d2, d, delta_next);
1278				return;
1279			}
1280		} else {
1281			/* XXX: here we depend on the date being set, but it
1282			 * should be before this is called anyway. */
1283			if (rcsnum_cmp(d->revnum, d2->revnum) < 0) {
1284				LIST_INSERT_BEFORE(d2, d, delta_next);
1285				return;
1286			}
1287		}
1288		if (LIST_NEXT(d2, delta_next) == NULL)
1289			break;
1290	}
1291	/* Insert after last element. */
1292	LIST_INSERT_AFTER(d2, d, delta_next);
1293}
1294
1295
1296/* Add logtext to a delta. Assume the delta already exists. */
1297int
1298rcsdelta_addlog(struct delta *d, char *log, int len)
1299{
1300	struct stream *dest;
1301	int nbytes;
1302
1303	assert(d != NULL);
1304	/* Strip away '@' at beginning and end. */
1305	log++;
1306	len--;
1307	log[len - 1] = '\0';
1308	dest = stream_open_buf(d->log);
1309	nbytes = stream_write(dest, log, len - 1);
1310	stream_close(dest);
1311	return ((nbytes == -1) ? -1 : 0);
1312}
1313
1314/* Add deltatext to a delta. Assume the delta already exists. */
1315int
1316rcsdelta_addtext(struct delta *d, char *text, int len)
1317{
1318	struct stream *dest;
1319	int nbytes;
1320
1321	assert(d != NULL);
1322	/* Strip away '@' at beginning and end. */
1323	text++;
1324	len--;
1325	text[len - 1] = '\0';
1326
1327	dest = stream_open_buf(d->text);
1328	nbytes = stream_write(dest, text, len - 1);
1329	stream_close(dest);
1330	return ((nbytes == -1) ? -1 : 0);
1331}
1332
1333/* Add a deltatext logline to a delta. */
1334int
1335rcsdelta_appendlog(struct delta *d, char *logline, size_t size)
1336{
1337	struct stream *dest;
1338	int error;
1339
1340	assert(d != NULL);
1341	dest = stream_open_buf(d->log);
1342	error = rcsdelta_writestring(logline, size, dest);
1343	stream_close(dest);
1344	return (error);
1345}
1346
1347/* Add a deltatext textline to a delta. */
1348int
1349rcsdelta_appendtext(struct delta *d, char *textline, size_t size)
1350{
1351	struct stream *dest;
1352	int error;
1353
1354	assert(d != NULL);
1355	dest = stream_open_buf(d->text);
1356	error = rcsdelta_writestring(textline, size, dest);
1357	stream_close(dest);
1358	return (error);
1359}
1360
1361static int
1362rcsdelta_writestring(char *textline, size_t size, struct stream *dest)
1363{
1364	char buf[3];
1365	size_t i;
1366	int count;
1367
1368	for (i = 0; i < size; i++) {
1369		buf[0] = textline[i];
1370		buf[1] = '\0';
1371		count = 1;
1372		/* Expand @'s */
1373		if (buf[0] == '@') {
1374			buf[1] = '@';
1375			buf[2] = '\0';
1376			count = 2;
1377		}
1378		if (stream_write(dest, buf, count) == -1)
1379			return (-1);
1380	}
1381	return (0);
1382}
1383
1384/* Set delta state. */
1385void
1386rcsdelta_setstate(struct delta *d, char *state)
1387{
1388
1389	if (d->state != NULL)
1390		free(state);
1391	if (state != NULL) {
1392		d->state = xstrdup(state);
1393		return;
1394	}
1395	d->state = NULL;
1396}
1397
1398/* Truncate the deltalog with a certain offset. */
1399void
1400rcsdelta_truncatelog(struct delta *d, off_t offset)
1401{
1402
1403	stream_truncate_buf(d->log, offset);
1404}
1405
1406/* Truncate the deltatext with a certain offset. */
1407void
1408rcsdelta_truncatetext(struct delta *d, off_t offset)
1409{
1410
1411	stream_truncate_buf(d->text, offset);
1412}
1413