rcsfile.c revision 185569
1/*-
2 * Copyright (c) 2007-2008, 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: projects/csup_cvsmode/contrib/csup/rcsfile.c 185569 2008-12-02 20:48:45Z 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	STAILQ_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	STAILQ_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	struct branch *trunk; /* The tip delta. */
123
124	LIST_HEAD(, delta) deltatable;
125	LIST_HEAD(, delta) deltatable_dates;
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 void		 rcsdelta_writestring(char *, size_t, struct stream *);
144
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";
156
157void print_stream(struct stream *);
158
159/* Print the contents of a stream, for debugging. */
160void
161print_stream(struct stream *s)
162{
163	char *line;
164
165	line = stream_getln(s, NULL);
166	while (line != NULL) {
167		lprintf(-1, "%s\n", line);
168		line = stream_getln(s, NULL);
169	}
170	lprintf(-1, "\n");
171}
172
173/*
174 * Parse rcsfile from path and return a pointer to it.
175 */
176struct rcsfile *
177rcsfile_frompath(char *path, char *name, char *cvsroot, char *colltag)
178{
179	struct rcsfile *rf;
180	FILE *infp;
181	int error;
182
183	if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
184		return (NULL);
185
186	rf = xmalloc(sizeof(struct rcsfile));
187	rf->name = xstrdup(name);
188	rf->cvsroot = xstrdup(cvsroot);
189	rf->colltag = xstrdup(colltag);
190
191	/* Initialize head branch. */
192	rf->trunk = xmalloc(sizeof(struct branch));
193	rf->trunk->revnum = xstrdup("1");
194	LIST_INIT(&rf->trunk->deltalist);
195	/* Initialize delta list. */
196	LIST_INIT(&rf->deltatable);
197	/* Initialize tag list. */
198	STAILQ_INIT(&rf->taglist);
199	/* Initialize accesslist. */
200	STAILQ_INIT(&rf->accesslist);
201
202	/* Initialize all fields. */
203	rf->head = NULL;
204	rf->branch = NULL;
205	rf->strictlock = 0;
206	rf->comment = NULL;
207	rf->expand = -1;
208	rf->desc = NULL;
209
210	infp = fopen(path, "r");
211	if (infp == NULL) {
212		lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
213		rcsfile_free(rf);
214		return (NULL);
215	}
216	error = rcsparse_run(rf, infp);
217	fclose(infp);
218	if (error) {
219		lprintf(-1, "Error parsing \"%s\"\n", name);
220		rcsfile_free(rf);
221		return (NULL);
222	}
223	return (rf);
224}
225
226/*
227 * Write content of rcsfile to server. Assumes we have a complete RCS file
228 * loaded.
229 */
230int
231rcsfile_send_details(struct rcsfile *rf, struct stream *wr)
232{
233	struct delta *d;
234	struct tag *t;
235	int error;
236
237	assert(rf != NULL);
238
239	error = proto_printf(wr, "V %s\n", rf->name);
240	if (error)
241		return(error);
242
243	/* Write default branch. */
244	if (rf->branch == NULL)
245		error = proto_printf(wr, "b\n");
246	else
247		error = proto_printf(wr, "B %s\n", rf->branch);
248	if (error)
249		return(error);
250
251	/* Write deltas to server. */
252	error = proto_printf(wr, "D\n");
253	if (error)
254		return(error);
255
256	LIST_FOREACH(d, &rf->deltatable, table_next) {
257		error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
258		if (error)
259			return(error);
260	}
261	error = proto_printf(wr, ".\n");
262
263	if (error)
264		return(error);
265	/* Write expand. */
266	if (rf->expand >= 0) {
267		error = proto_printf(wr, "E %s\n",
268		    keyword_encode_expand(rf->expand));
269		if (error)
270			return(error);
271	}
272
273	/* Write tags to server. */
274	error = proto_printf(wr, "T\n");
275	if (error)
276		return(error);
277	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
278		error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
279		if (error)
280			return(error);
281	}
282	error = proto_printf(wr, ".\n");
283	if (error)
284		return(error);
285	error = proto_printf(wr, ".\n");
286	return (error);
287}
288
289/*
290 * Write a RCS file to disk represented by the destination stream. Keep track of
291 * deltas with a stack and an inverted stack.
292 */
293int
294rcsfile_write(struct rcsfile *rf, struct stream *dest)
295{
296	STAILQ_HEAD(, delta) deltastack;
297	STAILQ_HEAD(, delta) deltalist_inverted;
298	struct tag *t;
299	struct branch *b;
300	struct delta *d, *d_tmp, *d_next;
301	int error;
302
303	/* First write head. */
304	d = LIST_FIRST(&rf->trunk->deltalist);
305	stream_printf(dest, "head%s%s;\n", head_space, d->revnum);
306
307	/* Write branch, if we have. */
308	if (rf->branch != NULL)
309		stream_printf(dest, "branch%s%s;\n", branch_space, rf->branch);
310
311	/* Write access. */
312	stream_printf(dest, "access");
313#if 0
314	if (!STAILQ_EMPTY(&rf->accesslist)) {
315		/*
316		 * XXX: Write out access. This doesn't seem to be necessary for
317		 * the time being.
318		 */
319	}
320#endif
321	stream_printf(dest, ";\n");
322
323	/* Write out taglist. */
324	stream_printf(dest, "symbols");
325	if (!STAILQ_EMPTY(&rf->taglist)) {
326		STAILQ_FOREACH(t, &rf->taglist, tag_next) {
327			stream_printf(dest, "\n%s%s:%s", tag_space, t->tag,
328			    t->revnum);
329		}
330	}
331	stream_printf(dest, ";\n");
332
333	/* Write out locks and strict. */
334	stream_printf(dest, "locks;");
335	if (rf->strictlock)
336		stream_printf(dest, " strict;");
337	stream_printf(dest, "\n");
338
339	/* Write out the comment. */
340	if (rf->comment != NULL)
341		stream_printf(dest, "comment%s%s;\n", comment_space, rf->comment);
342
343	stream_printf(dest, "\n\n");
344
345	/*
346	 * Write out deltas. We use a stack where we push the appropriate deltas
347	 * that is to be written out during the loop.
348	 */
349	STAILQ_INIT(&deltastack);
350	d = LIST_FIRST(&rf->trunk->deltalist);
351	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
352	while (!STAILQ_EMPTY(&deltastack)) {
353		d = STAILQ_FIRST(&deltastack);
354		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
355		/* Do not write out placeholders just to be safe. */
356		if (d->placeholder)
357			continue;
358		stream_printf(dest, "%s\n", d->revnum);
359		stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
360		    date_space, d->revdate, auth_space, d->author,
361		    state_space);
362		if (d->state != NULL)
363			stream_printf(dest, " %s", d->state);
364		stream_printf(dest, ";\n");
365		stream_printf(dest, "branches");
366		/*
367		 * Write out our branches. Add them to a reversed list for use
368		 * later when we write out the text.
369		 */
370		STAILQ_INIT(&deltalist_inverted);
371		STAILQ_FOREACH(b, &d->branchlist, branch_next) {
372			d_tmp = LIST_FIRST(&b->deltalist);
373			STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);
374			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
375		}
376
377		/* Push branch heads on stack. */
378		STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
379			if (d_tmp == NULL)
380				err(1, "empty branch!");
381			stream_printf(dest, "\n%s%s", branches_space,
382			    d_tmp->revnum);
383		}
384		stream_printf(dest, ";\n");
385
386		stream_printf(dest, "next%s", next_space);
387		/* Push next delta on stack. */
388		d_next = LIST_NEXT(d, delta_next);
389		if (d_next != NULL) {
390			stream_printf(dest, "%s", d_next->revnum);
391			STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
392		}
393		stream_printf(dest, ";\n\n");
394	}
395	stream_printf(dest, "\n");
396	/* Write out desc. */
397	stream_printf(dest, "desc\n@@");
398	d = LIST_FIRST(&rf->trunk->deltalist);
399
400	/*
401	 * XXX: We do not take as much care as cvsup to cope with hand-hacked
402	 * RCS-files, and therefore we'll just let them be updated. If having
403	 * them correct is important, it will be catched by the checksum anyway.
404	 */
405
406	/* Write out deltatexts. */
407	error = rcsfile_write_deltatext(rf, dest);
408	stream_printf(dest, "\n");
409	return (error);
410}
411
412/*
413 * Write out deltatexts of a delta and it's subbranches recursively.
414 */
415int
416rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
417{
418	STAILQ_HEAD(, delta) deltastack;
419	LIST_HEAD(, delta) branchlist_datesorted;
420	struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
421	struct stream *in;
422	struct branch *b;
423	size_t size;
424	char *line;
425	int error;
426
427	error = 0;
428	STAILQ_INIT(&deltastack);
429	d = LIST_FIRST(&rf->trunk->deltalist);
430	d->prev = NULL;
431	STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
432	while (!STAILQ_EMPTY(&deltastack)) {
433		d = STAILQ_FIRST(&deltastack);
434		STAILQ_REMOVE_HEAD(&deltastack, stack_next);
435		/* Do not write out placeholders just to be safe. */
436		if (d->placeholder)
437			return (0);
438		stream_printf(dest, "\n\n\n%s\n", d->revnum);
439		stream_printf(dest, "log\n@");
440		in = stream_open_buf(d->log);
441		line = stream_getln(in, &size);
442		while (line != NULL) {
443			stream_write(dest, line, size);
444			line = stream_getln(in, &size);
445		}
446		stream_close(in);
447		stream_printf(dest, "@\n");
448		stream_printf(dest, "text\n@");
449		error = rcsfile_puttext(rf, dest, d, d->prev);
450		if (error)
451			return (error);
452		stream_printf(dest, "@");
453
454		LIST_INIT(&branchlist_datesorted);
455		d_next = LIST_NEXT(d, delta_next);
456		if (d_next != NULL) {
457			d_next->prev = d;
458			/*
459			 * If it's trunk, treat it like the oldest, if not treat
460			 * it like a child.
461			 */
462			if (rcsrev_istrunk(d_next->revnum))
463				STAILQ_INSERT_HEAD(&deltastack, d_next,
464				    stack_next);
465			else
466				LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
467				    branch_next_date);
468		}
469
470		/*
471		 * First, we need to sort our branches based on their date to
472		 * take into account some self-hacked RCS files.
473		 */
474		STAILQ_FOREACH(b, &d->branchlist, branch_next) {
475			d_tmp = LIST_FIRST(&b->deltalist);
476			if (LIST_EMPTY(&branchlist_datesorted)) {
477				LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp,
478				    branch_next_date);
479				continue;
480			}
481
482			d_tmp2 = LIST_FIRST(&branchlist_datesorted);
483			if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) < 0) {
484				LIST_INSERT_BEFORE(d_tmp2, d_tmp,
485				    branch_next_date);
486				continue;
487			}
488			while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
489			    != NULL) {
490				if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
491				    < 0)
492					break;
493				d_tmp2 = d_tmp3;
494			}
495			LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
496		}
497		/*
498		 * Invert the deltalist of a branch, since we're writing them
499		 * the opposite way.
500		 */
501		LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
502                        d_tmp->prev = d;
503			STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
504		}
505	}
506	return (0);
507}
508
509/*
510 * Generates text given a delta and a diffbase.
511 */
512static int
513rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
514    struct delta *diffbase)
515{
516	struct stream *in, *rd, *orig;
517	struct keyword *k;
518	struct diffinfo dibuf, *di;
519	struct buf *b;
520	size_t size;
521	char *line;
522	int error;
523
524	di = &dibuf;
525	b = NULL;
526	error = 0;
527
528	/* Write if the diffbase is the previous */
529	if (d->diffbase == diffbase) {
530
531		/* Write out the text. */
532		in = stream_open_buf(d->text);
533		line = stream_getln(in, &size);
534		while (line != NULL) {
535			stream_write(dest, line, size);
536			line = stream_getln(in, &size);
537		}
538		stream_close(in);
539	/* We need to apply diff to produce text, this is probably HEAD. */
540	} else if (diffbase == NULL) {
541		/* Apply diff. */
542		orig = rcsfile_getdeltatext(rf, d, &b);
543		if (orig == NULL) {
544			error = -1;
545			goto cleanup;
546		}
547		line = stream_getln(orig, &size);
548		while (line != NULL) {
549			stream_write(dest, line, size);
550			line = stream_getln(orig, &size);
551		}
552		stream_close(orig);
553	/*
554	 * A new head was probably added, and now the previous HEAD must be
555	 * changed to include the diff instead.
556	 */
557	} else if (diffbase->diffbase == d) {
558		/* Get reverse diff. */
559		orig = rcsfile_getdeltatext(rf, d, &b);
560		if (orig == NULL) {
561			error = -1;
562			goto cleanup;
563		}
564		rd = stream_open_buf(diffbase->text);
565		di->di_rcsfile = rf->name;
566		di->di_cvsroot = rf->cvsroot;
567		di->di_revnum = d->revnum;
568		di->di_revdate = d->revdate;
569		di->di_author = d->author;
570		di->di_tag = rf->colltag;
571		di->di_state = d->state;
572		di->di_expand = rf->expand;
573		k = keyword_new();
574
575		rd = stream_open_buf(diffbase->text);
576		error = diff_reverse(rd, orig, dest, k, di);
577		if (error) {
578			lprintf(-1, "Error applying reverse diff: %d\n", error);
579			goto cleanup;
580		}
581		keyword_free(k);
582		stream_close(rd);
583		stream_close(orig);
584	}
585cleanup:
586	if (b != NULL)
587		buf_free(b);
588	return (error);
589}
590
591/*
592 * Return a stream with an applied diff of a delta.
593 * XXX: extra overhead on the last apply. Could write directly to file, but
594 * makes things complicated though.
595 */
596static struct stream *
597rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
598{
599	struct diffinfo dibuf, *di;
600	struct stream *orig, *dest, *rd;
601	struct buf *buf_orig;
602	struct keyword *k;
603	int error;
604
605	buf_orig = NULL;
606	error = 0;
607
608	/*
609	 * If diffbase is NULL or we are head (the old head), we have a normal
610	 * complete deltatext.
611	 */
612	if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
613		orig = stream_open_buf(d->text);
614		return (orig);
615	}
616
617	di = &dibuf;
618	/* If not, we need to apply our diff to that of our diffbase. */
619	orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
620	if (orig == NULL)
621		return (NULL);
622
623	/*
624	 * Now that we are sure we have a complete deltatext in ret, let's apply
625	 * our diff to it.
626	 */
627	*buf_dest = buf_new(BUF_SIZE_DEFAULT);
628	dest = stream_open_buf(*buf_dest);
629
630	di->di_rcsfile = rf->name;
631	di->di_cvsroot = rf->cvsroot;
632	di->di_revnum = d->revnum;
633	di->di_revdate = d->revdate;
634	di->di_author = d->author;
635	di->di_tag = rf->colltag;
636	di->di_state = d->state;
637	di->di_expand = rf->expand;
638	rd = stream_open_buf(d->text);
639	k = keyword_new();
640	error = diff_apply(rd, orig, dest, k, di, 0);
641	stream_flush(dest);
642	stream_close(rd);
643	stream_close(orig);
644	stream_close(dest);
645	keyword_free(k);
646	if (buf_orig != NULL)
647		buf_free(buf_orig);
648	if (error) {
649		lprintf(-1, "Error applying diff: %d\n", error);
650		return (NULL);
651	}
652
653	/* Now reopen the stream for the reading. */
654	dest = stream_open_buf(*buf_dest);
655	return (dest);
656}
657
658/* Print content of rcsfile. Useful for debugging. */
659void
660rcsfile_print(struct rcsfile *rf)
661{
662	struct delta *d;
663	struct tag *t;
664	struct string *s;
665	struct stream *in;
666	char *line;
667
668	lprintf(1, "\n");
669	if (rf->name != NULL)
670		lprintf(1, "name: '%s'\n", rf->name);
671	if (rf->head != NULL)
672		lprintf(1, "head: '%s'\n", rf->head);
673	if (rf->branch != NULL)
674		lprintf(1, "branch: '%s'\n", rf->branch);
675	lprintf(1, "Access: ");
676	STAILQ_FOREACH(s, &rf->accesslist, string_next)
677		lprintf(1, "'%s' ", s->str);
678	lprintf(1, "\n");
679
680	/* Print all tags. */
681	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
682		lprintf(1, "Tag: ");
683		if (t->tag != NULL)
684			lprintf(1, "name: %s ", t->tag);
685		if (t->revnum != NULL)
686			lprintf(1, "rev: %s", t->revnum);
687		lprintf(1, "\n");
688	}
689
690	if (rf->strictlock)
691		lprintf(1, "Strict!\n");
692	if (rf->comment != NULL)
693		lprintf(1, "comment: '%s'\n", rf->comment);
694	if (rf->expand >= 0)
695		lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand));
696
697	/* Print all deltas. */
698	LIST_FOREACH(d, &rf->deltatable, table_next) {
699		lprintf(1, "Delta: ");
700		if (d->revdate != NULL)
701			lprintf(1, "date: %s ", d->revdate);
702		if (d->revnum != NULL)
703			lprintf(1, "rev: %s", d->revnum);
704		if (d->author != NULL)
705			lprintf(1, "author: %s", d->author);
706		if (d->state != NULL)
707			lprintf(1, "state: %s", d->state);
708
709		lprintf(1, "Text:\n");
710		in = stream_open_buf(d->text);
711		line = stream_getln(in, NULL);
712		while (line != NULL) {
713			lprintf(1, "TEXT: %s\n", line);
714			line = stream_getln(in, NULL);
715		}
716		stream_close(in);
717		lprintf(1, "\n");
718	}
719
720	if (rf->desc != NULL)
721		lprintf(1, "desc: '%s'\n", rf->desc);
722}
723
724/* Free all memory associated with a struct rcsfile. */
725void
726rcsfile_free(struct rcsfile *rf)
727{
728	struct delta *d;
729	struct tag *t;
730	struct string *s;
731
732	if (rf->name != NULL)
733		free(rf->name);
734	if (rf->head != NULL)
735		free(rf->head);
736	if (rf->branch != NULL)
737		free(rf->branch);
738	if (rf->cvsroot != NULL)
739		free(rf->cvsroot);
740	if (rf->colltag != NULL)
741		free(rf->colltag);
742
743	/* Free all access ids. */
744	while (!STAILQ_EMPTY(&rf->accesslist)) {
745		s = STAILQ_FIRST(&rf->accesslist);
746		STAILQ_REMOVE_HEAD(&rf->accesslist, string_next);
747		if (s->str != NULL)
748			free(s->str);
749		free(s);
750	}
751
752	/* Free all tags. */
753	while (!STAILQ_EMPTY(&rf->taglist)) {
754		t = STAILQ_FIRST(&rf->taglist);
755		STAILQ_REMOVE_HEAD(&rf->taglist, tag_next);
756		if (t->tag != NULL)
757			free(t->tag);
758		if (t->revnum != NULL)
759			free(t->revnum);
760		free(t);
761	}
762
763	if (rf->comment != NULL)
764		free(rf->comment);
765
766	/* Free all deltas in global list */
767	while (!LIST_EMPTY(&rf->deltatable)) {
768		d = LIST_FIRST(&rf->deltatable);
769		LIST_REMOVE(d, delta_next);
770		LIST_REMOVE(d, table_next);
771		rcsfile_freedelta(d);
772	}
773
774	/* Free global branch. */
775	if (rf->trunk->revnum != NULL)
776		free(rf->trunk->revnum);
777	free(rf->trunk);
778
779	if (rf->desc != NULL)
780		free(rf->desc);
781
782	free(rf);
783}
784
785/*
786 * Free a RCS delta.
787 */
788static void
789rcsfile_freedelta(struct delta *d)
790{
791	struct branch *b;
792
793	if (d->revdate != NULL)
794		free(d->revdate);
795	if (d->revnum != NULL)
796		free(d->revnum);
797	if (d->author != NULL)
798		free(d->author);
799	if (d->state != NULL)
800		free(d->state);
801	if (d->log != NULL)
802		buf_free(d->log);
803	if (d->text != NULL)
804		buf_free(d->text);
805
806	/* Free all subbranches of a delta. */
807	/* XXX: Is this ok? Since the branchpoint is removed, there is no good
808	 * reason for the branch to exists, but we might still have deltas in
809	 * these branches.
810	 */
811	while (!STAILQ_EMPTY(&d->branchlist)) {
812		b = STAILQ_FIRST(&d->branchlist);
813		STAILQ_REMOVE_HEAD(&d->branchlist, branch_next);
814		free(b->revnum);
815		free(b);
816	}
817	free(d);
818}
819
820/*
821 * Functions for editing RCS deltas.
822 */
823
824/* Add a new entry to the access list. */
825void
826rcsfile_addaccess(struct rcsfile *rf, char *id)
827{
828	struct string *s;
829
830	s = xmalloc(sizeof(struct string));
831	s->str = xstrdup(id);
832	STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next);
833}
834
835/* Add a tag to a RCS file. */
836void
837rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum)
838{
839	struct tag *t;
840
841	t = xmalloc(sizeof(struct tag));
842	t->tag = xstrdup(tag);
843	t->revnum = xstrdup(revnum);
844
845	STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next);
846}
847
848/* Import a tag to a RCS file. */
849void
850rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum)
851{
852	struct tag *t;
853
854	t = xmalloc(sizeof(struct tag));
855	t->tag = xstrdup(tag);
856	t->revnum = xstrdup(revnum);
857
858	STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next);
859}
860
861/*
862 * Delete a revision from the global delta list and the branch it is in. Csup
863 * will tell us to delete the tags involved.
864 */
865void
866rcsfile_deleterev(struct rcsfile *rf, char *revname)
867{
868	struct delta *d;
869
870	d = rcsfile_getdelta(rf, revname);
871	LIST_REMOVE(d, delta_next);
872	LIST_REMOVE(d, table_next);
873	rcsfile_freedelta(d);
874}
875
876/* Delete a tag from the tag list. */
877void
878rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum)
879{
880	struct tag *t;
881
882	STAILQ_FOREACH(t, &rf->taglist, tag_next) {
883		if ((strcmp(tag, t->tag) == 0) &&
884		    (strcmp(revnum, t->revnum) == 0)) {
885			STAILQ_REMOVE(&rf->taglist, t, tag, tag_next);
886			free(t->tag);
887			free(t->revnum);
888			free(t);
889			return;
890		}
891	}
892}
893
894/*
895 * Searches the global deltalist for a delta.
896 */
897struct delta *
898rcsfile_getdelta(struct rcsfile *rf, char *revnum)
899{
900	struct delta *d;
901
902	LIST_FOREACH(d, &rf->deltatable, table_next) {
903		if (strcmp(revnum, d->revnum) == 0)
904			return (d);
905	}
906	return (NULL);
907}
908
909/* Set rcsfile head. */
910void
911rcsfile_setval(struct rcsfile *rf, int field, char *val)
912{
913
914	switch (field) {
915		case RCSFILE_HEAD:
916			if (rf->head != NULL)
917				free(rf->head);
918			rf->head = xstrdup(val);
919		break;
920		case RCSFILE_BRANCH:
921			if (rf->branch != NULL)
922				free(rf->branch);
923			rf->branch = (val == NULL) ? NULL : xstrdup(val);
924		break;
925		case RCSFILE_STRICT:
926			if (val != NULL)
927				rf->strictlock = 1;
928		break;
929		case RCSFILE_COMMENT:
930			if (rf->comment != NULL)
931				free(rf->comment);
932			rf->comment = xstrdup(val);
933		break;
934		case RCSFILE_EXPAND:
935			rf->expand = keyword_decode_expand(val);
936		break;
937		case RCSFILE_DESC:
938			if (rf->desc != NULL)
939				free(rf->desc);
940			rf->desc = xstrdup(val);
941		break;
942		default:
943			lprintf(-1, "Setting invalid RCSfile value.\n");
944		break;
945	}
946}
947
948/* Create and initialize a delta. */
949static struct delta *
950rcsfile_createdelta(char *revnum)
951{
952	struct delta *d;
953
954	d = xmalloc(sizeof(struct delta));
955	d->revnum = xstrdup(revnum);
956	d->revdate = NULL;
957	d->state = NULL;
958	d->author = NULL;
959	d->log = buf_new(BUF_SIZE_DEFAULT);
960	d->text = buf_new(BUF_SIZE_DEFAULT);
961	d->diffbase = NULL;
962
963	STAILQ_INIT(&d->branchlist);
964	return (d);
965}
966
967/* Add a delta to a imported delta tree. Used by the updater. */
968struct delta *
969rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
970    char *diffbase)
971{
972	struct branch *b;
973	struct delta *d, *d_bp, *d_next;
974	char *brev, *bprev;
975	int trunk;
976
977	d_next = NULL;
978	d = rcsfile_getdelta(rf, revnum);
979	if (d != NULL) {
980		lprintf(-1, "Delta %s already exists!\n", revnum);
981		return (NULL);
982	}
983	d = rcsfile_createdelta(revnum);
984	d->placeholder = 0;
985	d->revdate = xstrdup(revdate);
986	d->author = xstrdup(author);
987	d->diffbase = rcsfile_getdelta(rf, diffbase);
988
989	/* If it's trunk, insert it in the head branch list. */
990	b = rcsrev_istrunk(d->revnum) ? rf->trunk :
991	    rcsfile_getbranch(rf, d->revnum);
992
993	/*
994	 * We didn't find a branch, check if we can find a branchpoint and
995	 * create a branch there.
996	 */
997	if (b == NULL) {
998		brev = rcsrev_prefix(d->revnum);
999		bprev = rcsrev_prefix(brev);
1000
1001		d_bp = rcsfile_getdelta(rf, bprev);
1002		free(bprev);
1003		if (d_bp == NULL) {
1004			lprintf(-1, "No branch point for adding delta %s\n",
1005			    d->revnum);
1006			return (NULL);
1007		}
1008
1009		/* Create the branch and insert in delta. */
1010		b = xmalloc(sizeof(struct branch));
1011		b->revnum = brev;
1012		LIST_INIT(&b->deltalist);
1013		STAILQ_INSERT_HEAD(&d_bp->branchlist, b, branch_next);
1014	}
1015
1016	/* Insert both into the tree, and into the lookup list. */
1017	trunk = rcsrev_istrunk(d->revnum);
1018	rcsfile_insertdelta(b, d, trunk);
1019	rcsfile_insertsorteddelta(rf, d);
1020	return (d);
1021}
1022
1023/* Adds a delta to a rcsfile struct. Used by the parser. */
1024void
1025rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
1026    char *state, char *next)
1027{
1028	struct branch *b;
1029	struct delta *d, *d_bp, *d_next;
1030	char *brev, *bprev;
1031	int trunk;
1032
1033	d_next = NULL;
1034	d = rcsfile_getdelta(rf, revnum);
1035
1036	if (d == NULL) {
1037		/* If not, we'll just create a new entry. */
1038		d = rcsfile_createdelta(revnum);
1039		d->placeholder = 0;
1040	} else {
1041		if (d->placeholder == 0) {
1042			lprintf(-1, "Trying to import already existing delta\n");
1043			return;
1044		}
1045	}
1046	/*
1047	 * If already exists, assume that only revnum is filled out, and set the
1048	 * rest of the fields. This should be an OK assumption given that we can
1049	 * be sure internally that the structure is sufficiently initialized so
1050	 * we won't have any unfreed memory.
1051	 */
1052	d->revdate = xstrdup(revdate);
1053	d->author = xstrdup(author);
1054	if (state != NULL)
1055		d->state = xstrdup(state);
1056
1057	/* If we have a next, create a placeholder for it. */
1058	if (next != NULL) {
1059		d_next = rcsfile_createdelta(next);
1060		d_next->placeholder = 1;
1061		/* Diffbase should be the previous. */
1062		d_next->diffbase = d;
1063	}
1064
1065	/* If it's trunk, insert it in the head branch list. */
1066	b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
1067	    d->revnum);
1068
1069	/*
1070	 * We didn't find a branch, check if we can find a branchpoint and
1071	 * create a branch there.
1072	 */
1073	if (b == NULL) {
1074		brev = rcsrev_prefix(d->revnum);
1075		bprev = rcsrev_prefix(brev);
1076
1077		d_bp = rcsfile_getdelta(rf, bprev);
1078		free(bprev);
1079		if (d_bp == NULL) {
1080			lprintf(-1, "No branch point for adding delta %s\n",
1081			    d->revnum);
1082			return;
1083		}
1084
1085		/* Create the branch and insert in delta. */
1086		b = xmalloc(sizeof(struct branch));
1087		b->revnum = brev;
1088		LIST_INIT(&b->deltalist);
1089		STAILQ_INSERT_HEAD(&d_bp->branchlist, b, branch_next);
1090	}
1091
1092	/* Insert if not a placeholder. */
1093	if (!d->placeholder) {
1094		/* Insert both into the tree, and into the lookup list. */
1095		if (rcsrev_istrunk(d->revnum))
1096			rcsfile_insertdelta(b, d, 1);
1097		else {
1098			rcsfile_insertdelta(b, d, 0);
1099			/*
1100			 * On import we need to set the diffbase to our
1101			 * branchpoint for writing out later.
1102			 * XXX: this could perhaps be done at a better time.
1103			 */
1104			if (LIST_FIRST(&b->deltalist) == d) {
1105				brev = rcsrev_prefix(d->revnum);
1106				bprev = rcsrev_prefix(brev);
1107				d_bp = rcsfile_getdelta(rf, bprev);
1108				/* This should really not happen. */
1109				assert(d_bp != NULL);
1110				d->diffbase = d_bp;
1111				free(brev);
1112				free(bprev);
1113			}
1114		}
1115		rcsfile_insertsorteddelta(rf, d);
1116	} else /* Not a placeholder anymore. */ {
1117		d->placeholder = 0;
1118		/* Put it into the tree. */
1119		trunk = rcsrev_istrunk(d->revnum);
1120		rcsfile_insertdelta(b, d, trunk);
1121	}
1122
1123	/* If we have a next, insert the placeholder into the lookup list. */
1124	if (d_next != NULL)
1125		rcsfile_insertsorteddelta(rf, d_next);
1126}
1127
1128/*
1129 * Find the branch of a revision number.
1130 */
1131static struct branch *
1132rcsfile_getbranch(struct rcsfile *rf, char *revnum)
1133{
1134	struct branch *b;
1135	struct delta *d;
1136	char *branchrev, *bprev;
1137
1138	branchrev = rcsrev_prefix(revnum);
1139	bprev = rcsrev_prefix(branchrev);
1140	d = rcsfile_getdelta(rf, bprev);
1141	free(bprev);
1142	STAILQ_FOREACH(b, &d->branchlist, branch_next) {
1143		if(rcsnum_cmp(b->revnum, branchrev) == 0) {
1144			free(branchrev);
1145			return (b);
1146		}
1147	}
1148	free(branchrev);
1149	return (NULL);
1150}
1151
1152/*
1153 * Insert a delta into the correct place in the table of the rcsfile. Sorted by
1154 * date.
1155 */
1156static void
1157rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d)
1158{
1159	struct delta *d2;
1160
1161	/* If it's empty, insert into head. */
1162	if (LIST_EMPTY(&rf->deltatable)) {
1163		LIST_INSERT_HEAD(&rf->deltatable, d, table_next);
1164		return;
1165	}
1166
1167	/* Just put it in before the revdate that is lower. */
1168	LIST_FOREACH(d2, &rf->deltatable, table_next) {
1169		if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) {
1170			LIST_INSERT_BEFORE(d2, d, table_next);
1171			return;
1172		}
1173		if (LIST_NEXT(d2, table_next) == NULL)
1174			break;
1175	}
1176	/* Insert after last element. */
1177	LIST_INSERT_AFTER(d2, d, table_next);
1178}
1179
1180/*
1181 * Insert a delta into the correct place in branch. A trunk branch will have
1182 * different ordering scheme and be sorted by revision number, but a normal
1183 * branch will be sorted by date to maintain compability with branches that is
1184 * "hand-hacked".
1185 */
1186static void
1187rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk)
1188{
1189	struct delta *d2;
1190
1191	/* If it's empty, insert into head. */
1192	if (LIST_EMPTY(&b->deltalist)) {
1193		LIST_INSERT_HEAD(&b->deltalist, d, delta_next);
1194		return;
1195	}
1196
1197	/*
1198	 * Just put it in before the revnum that is lower. Sort trunk branch by
1199	 * branchnum but the subbranches after deltadate.
1200	 */
1201	LIST_FOREACH(d2, &b->deltalist, delta_next) {
1202		if (trunk) {
1203			if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) {
1204				LIST_INSERT_BEFORE(d2, d, delta_next);
1205				return;
1206			}
1207		} else {
1208			/* XXX: here we depend on the date being set, but it
1209			 * should be before this is called anyway. */
1210			if (rcsnum_cmp(d->revdate, d2->revdate) < 0) {
1211				LIST_INSERT_BEFORE(d2, d, delta_next);
1212				return;
1213			}
1214		}
1215		if (LIST_NEXT(d2, delta_next) == NULL)
1216			break;
1217	}
1218	/* Insert after last element. */
1219	LIST_INSERT_AFTER(d2, d, delta_next);
1220}
1221
1222
1223/* Add logtext to a delta. Assume the delta already exists. */
1224int
1225rcsdelta_addlog(struct delta *d, char *log)
1226{
1227	struct stream *dest;
1228
1229	assert(d != NULL);
1230	log++;
1231	log[strlen(log) - 1] = '\0';
1232
1233	dest = stream_open_buf(d->log);
1234	stream_write(dest, log, strlen(log));
1235	stream_close(dest);
1236	return (0);
1237}
1238
1239/* Add deltatext to a delta. Assume the delta already exists. */
1240int
1241rcsdelta_addtext(struct delta *d, char *text)
1242{
1243	struct stream *dest;
1244
1245	assert(d != NULL);
1246	text++;
1247	text[strlen(text) - 1] = '\0';
1248
1249	dest = stream_open_buf(d->text);
1250	stream_write(dest, text, strlen(text));
1251	stream_close(dest);
1252	return (0);
1253}
1254
1255/* Add a deltatext logline to a delta. */
1256void
1257rcsdelta_appendlog(struct delta *d, char *logline, size_t size)
1258{
1259	struct stream *dest;
1260
1261	assert(d != NULL);
1262	dest = stream_open_buf(d->log);
1263	rcsdelta_writestring(logline, size, dest);
1264	stream_close(dest);
1265}
1266
1267/* Add a deltatext textline to a delta. */
1268void
1269rcsdelta_appendtext(struct delta *d, char *textline, size_t size)
1270{
1271	struct stream *dest;
1272
1273	assert(d != NULL);
1274	dest = stream_open_buf(d->text);
1275	rcsdelta_writestring(textline, size, dest);
1276	stream_close(dest);
1277}
1278
1279static void
1280rcsdelta_writestring(char *textline, size_t size, struct stream *dest)
1281{
1282	char buf[3];
1283	size_t i;
1284	int count;
1285
1286	for (i = 0; i < size; i++) {
1287		buf[0] = textline[i];
1288		buf[1] = '\0';
1289		count = 1;
1290		/* Expand @'s */
1291		if (buf[0] == '@') {
1292			buf[1] = '@';
1293			buf[2] = '\0';
1294			count = 2;
1295		}
1296		stream_write(dest, buf, count);
1297	}
1298}
1299
1300/* Set delta state. */
1301void
1302rcsdelta_setstate(struct delta *d, char *state)
1303{
1304
1305	if (d->state != NULL)
1306		free(state);
1307	if (state != NULL) {
1308		d->state = xstrdup(state);
1309		return;
1310	}
1311	d->state = NULL;
1312}
1313
1314/* Truncate the deltalog with a certain offset. */
1315void
1316rcsdelta_truncatelog(struct delta *d, off_t offset)
1317{
1318
1319	stream_truncate_buf(d->log, offset);
1320}
1321
1322/* Truncate the deltatext with a certain offset. */
1323void
1324rcsdelta_truncatetext(struct delta *d, off_t offset)
1325{
1326
1327	stream_truncate_buf(d->text, offset);
1328}
1329