summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris Vink <joris@cvs.openbsd.org>2005-10-22 17:32:58 +0000
committerJoris Vink <joris@cvs.openbsd.org>2005-10-22 17:32:58 +0000
commit16e8927d403df86f9f577b0d491d1ef9ac11ce97 (patch)
tree074cdbe132f562b97d4cec177fd22af8aa77c729
parent507840a892753fe4ed5a99c4f0105d29e2bea00a (diff)
diff3 support, needed for merging files together;
"go for it" niallo@
-rw-r--r--usr.bin/cvs/cvs.h24
-rw-r--r--usr.bin/cvs/diff.c7
-rw-r--r--usr.bin/cvs/diff.h9
-rw-r--r--usr.bin/cvs/diff3.c808
-rw-r--r--usr.bin/cvs/rcs.c220
-rw-r--r--usr.bin/cvs/rcs.h4
-rw-r--r--usr.bin/cvs/resp.c4
-rw-r--r--usr.bin/cvs/util.c114
8 files changed, 994 insertions, 196 deletions
diff --git a/usr.bin/cvs/cvs.h b/usr.bin/cvs/cvs.h
index 74fe7e83baf..f08fb3f7316 100644
--- a/usr.bin/cvs/cvs.h
+++ b/usr.bin/cvs/cvs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cvs.h,v 1.86 2005/10/07 21:47:32 reyk Exp $ */
+/* $OpenBSD: cvs.h,v 1.87 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -410,6 +410,21 @@ int cvs_logmsg_send(struct cvsroot *, const char *);
time_t cvs_date_parse(const char *);
/* util.c */
+
+struct cvs_line {
+ char *l_line;
+ int l_lineno;
+ TAILQ_ENTRY(cvs_line) l_list;
+};
+
+TAILQ_HEAD(cvs_tqh, cvs_line);
+
+struct cvs_lines {
+ int l_nblines;
+ char *l_data;
+ struct cvs_tqh l_lines;
+};
+
int cvs_readrepo(const char *, char *, size_t);
int cvs_modetostr(mode_t, char *, size_t);
int cvs_strtomode(const char *, mode_t *);
@@ -431,5 +446,12 @@ void cvs_write_tagfile(char *, char *, int);
void cvs_parse_tagfile(char **, char **, int *);
size_t cvs_path_cat(const char *, const char *, char *, size_t);
+BUF *cvs_patchfile(const char *, const char *,
+ int (*p)(struct cvs_lines *, struct cvs_lines *));
+struct cvs_lines *cvs_splitlines(const char *);
+void cvs_freelines(struct cvs_lines *);
+
+/* XXX */
+int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *);
#endif /* CVS_H */
diff --git a/usr.bin/cvs/diff.c b/usr.bin/cvs/diff.c
index 5b449576485..636d7c2e2ac 100644
--- a/usr.bin/cvs/diff.c
+++ b/usr.bin/cvs/diff.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: diff.c,v 1.62 2005/10/11 18:01:57 joris Exp $ */
+/* $OpenBSD: diff.c,v 1.63 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (C) Caldera International Inc. 2001-2002.
* All rights reserved.
@@ -185,7 +185,6 @@ static int cvs_diff_pre_exec(struct cvsroot *);
static int cvs_diff_cleanup(void);
#endif
-static void diff_output(const char *, ...);
static void output(const char *, FILE *, const char *, FILE *);
static void check(FILE *, FILE *);
static void range(int, int, char *);
@@ -250,7 +249,7 @@ static struct context_vec *context_vec_ptr;
static char lastbuf[FUNCTION_CONTEXT_SIZE];
static int lastline;
static int lastmatchline;
-static BUF *diffbuf = NULL;
+BUF *diffbuf = NULL;
/*
* chrtran points to one of 2 translation tables: cup2low if folding upper to
@@ -1794,7 +1793,7 @@ dump_unified_vec(FILE *f1, FILE *f2)
context_vec_ptr = context_vec_start - 1;
}
-static void
+void
diff_output(const char *fmt, ...)
{
va_list vap;
diff --git a/usr.bin/cvs/diff.h b/usr.bin/cvs/diff.h
index 41fc542a2ca..cd261370da4 100644
--- a/usr.bin/cvs/diff.h
+++ b/usr.bin/cvs/diff.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: diff.h,v 1.3 2005/10/11 14:27:27 joris Exp $ */
+/* $OpenBSD: diff.h,v 1.4 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (C) Caldera International Inc. 2001-2002.
* All rights reserved.
@@ -93,8 +93,13 @@
#define D_SKIPPED2 9 /* path2 was a special file */
-
+BUF *cvs_diff3(RCSFILE *, char *, RCSNUM *, RCSNUM *);
+void diff_output(const char *, ...);
int cvs_diffreg(const char *, const char *, BUF *out);
+int ed_patch_lines(struct cvs_lines *, struct cvs_lines *);
+
extern int diff_format;
extern char *diff_file;
+extern BUF *diffbuf;
+
#endif
diff --git a/usr.bin/cvs/diff3.c b/usr.bin/cvs/diff3.c
new file mode 100644
index 00000000000..645be253d2e
--- /dev/null
+++ b/usr.bin/cvs/diff3.c
@@ -0,0 +1,808 @@
+/* $OpenBSD: diff3.c,v 1.1 2005/10/22 17:32:57 joris Exp $ */
+
+/*
+ * Copyright (C) Caldera International Inc. 2001-2002.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code and documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed or owned by Caldera
+ * International, Inc.
+ * 4. Neither the name of Caldera International, Inc. nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)diff3.c 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] = "$OpenBSD: diff3.c,v 1.1 2005/10/22 17:32:57 joris Exp $";
+#endif /* not lint */
+
+#include <sys/queue.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cvs.h"
+#include "log.h"
+#include "diff.h"
+
+/* diff3 - 3-way differential file comparison */
+
+/* diff3 [-ex3EX] d13 d23 f1 f2 f3 [m1 m3]
+ *
+ * d13 = diff report on f1 vs f3
+ * d23 = diff report on f2 vs f3
+ * f1, f2, f3 the 3 files
+ * if changes in f1 overlap with changes in f3, m1 and m3 are used
+ * to mark the overlaps; otherwise, the file names f1 and f3 are used
+ * (only for options E and X).
+ */
+
+/*
+ * "from" is first in range of changed lines; "to" is last+1
+ * from=to=line after point of insertion for added lines.
+ */
+struct range {
+ int from;
+ int to;
+};
+
+struct diff {
+ struct range old;
+ struct range new;
+};
+
+static size_t szchanges;
+
+static struct diff *d13;
+static struct diff *d23;
+
+/*
+ * "de" is used to gather editing scripts. These are later spewed out in
+ * reverse order. Its first element must be all zero, the "new" component
+ * of "de" contains line positions or byte positions depending on when you
+ * look (!?). Array overlap indicates which sections in "de" correspond to
+ * lines that are different in all three files.
+ */
+static struct diff *de;
+static char *overlap;
+static int overlapcnt;
+static FILE *fp[3];
+static int cline[3]; /* # of the last-read line in each file (0-2) */
+
+/*
+ * the latest known correspondence between line numbers of the 3 files
+ * is stored in last[1-3];
+ */
+static int last[4];
+static int eflag;
+static int oflag; /* indicates whether to mark overlaps (-E or -X)*/
+static int debug = 0;
+static char f1mark[40], f3mark[40]; /* markers for -E and -X */
+
+static int duplicate(struct range *, struct range *);
+static int edit(struct diff *, int, int);
+static char *getchange(FILE *);
+static char *getline(FILE *, size_t *);
+static int number(char **);
+static int readin(char *, struct diff **);
+static int skip(int, int, char *);
+static int edscript(int);
+static int merge(int, int);
+static void change(int, struct range *, int);
+static void keep(int, struct range *);
+static void prange(struct range *);
+static void repos(int);
+static void separate(const char *);
+static void increase(void);
+static int diff3_internal(int, char **, const char *, const char *);
+
+BUF *
+cvs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2)
+{
+ int ret, argc;
+ char *data, *patch;
+ char *argv[5], r1[16], r2[16];
+ char path1[MAXPATHLEN], path2[MAXPATHLEN], path3[MAXPATHLEN];
+ char dp13[MAXPATHLEN], dp23[MAXPATHLEN];
+ BUF *b1, *b2, *b3, *d1, *d2, *diffb;
+
+ ret = -1;
+ b1 = b2 = b3 = d1 = d2 = diffb = NULL;
+
+ rcsnum_tostr(rev1, r1, sizeof(r1));
+ rcsnum_tostr(rev2, r2, sizeof(r2));
+
+ cvs_printf("merging changes between '%s' and '%s'", r1, r2);
+ cvs_printf(" into '%s'\n", workfile);
+
+ if ((b1 = cvs_buf_load(workfile, BUF_AUTOEXT)) == NULL)
+ goto out;
+
+ if ((b2 = rcs_getrev(rf, rev1)) == NULL)
+ goto out;
+
+ if ((b3 = rcs_getrev(rf, rev2)) == NULL)
+ goto out;
+
+ if ((d1 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL)
+ goto out;
+
+ if ((d2 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL)
+ goto out;
+
+ if ((diffb = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL)
+ goto out;
+
+ strlcpy(path1, "/tmp/diff1.XXXXXXXXXX", sizeof(path1));
+ if (cvs_buf_write_stmp(b1, path1, 0600) == -1)
+ goto out;
+
+ strlcpy(path2, "/tmp/diff2.XXXXXXXXXX", sizeof(path2));
+ if (cvs_buf_write_stmp(b2, path2, 0600) == -1)
+ goto out;
+
+ strlcpy(path3, "/tmp/diff3.XXXXXXXXXX", sizeof(path3));
+ if (cvs_buf_write_stmp(b3, path3, 0600) == -1)
+ goto out;
+
+ cvs_buf_free(b2);
+ b2 = NULL;
+
+ cvs_diffreg(path1, path3, d1);
+ cvs_diffreg(path2, path3, d2);
+
+ strlcpy(dp13, "/tmp/d13.XXXXXXXXXX", sizeof(dp13));
+ if (cvs_buf_write_stmp(d1, dp13, 0600) < 0)
+ goto out;
+
+ cvs_buf_free(d1);
+ d1 = NULL;
+
+ strlcpy(dp23, "/tmp/d23.XXXXXXXXXX", sizeof(dp23));
+ if (cvs_buf_write_stmp(d2, dp23, 0600) < 0)
+ goto out;
+
+ cvs_buf_free(d2);
+ d2 = NULL;
+
+ argc = 0;
+ diffbuf = diffb;
+ argv[argc++] = dp13;
+ argv[argc++] = dp23;
+ argv[argc++] = path1;
+ argv[argc++] = path2;
+ argv[argc++] = path3;
+ if ((ret = diff3_internal(argc, argv, workfile, r2)) < 0)
+ goto out;
+
+ if (cvs_buf_putc(diffb, '\0') < 0) {
+ cvs_buf_free(diffb);
+ goto out;
+ }
+
+ if (cvs_buf_putc(b1, '\0') < 0) {
+ cvs_buf_free(diffb);
+ goto out;
+ }
+
+ patch = cvs_buf_release(diffb);
+ data = cvs_buf_release(b1);
+ diffb = b1 = NULL;
+
+ if ((diffb = cvs_patchfile(data, patch, ed_patch_lines)) == NULL)
+ goto out;
+
+ free(data);
+ free(patch);
+
+out:
+ if (b1 != NULL)
+ cvs_buf_free(b1);
+ if (b2 != NULL)
+ cvs_buf_free(b2);
+ if (b3 != NULL)
+ cvs_buf_free(b3);
+ if (d1 != NULL)
+ cvs_buf_free(d1);
+ if (d2 != NULL)
+ cvs_buf_free(d2);
+
+ (void)unlink(path1);
+ (void)unlink(path2);
+ (void)unlink(path3);
+ (void)unlink(dp13);
+ (void)unlink(dp23);
+
+ return (diffb);
+}
+
+static int
+diff3_internal(int argc, char **argv, const char *fmark, const char *rmark)
+{
+ int i, m, n;
+
+ /* XXX */
+ eflag = 3;
+ oflag = 1;
+
+ if (argc < 5)
+ return (-1);
+
+ snprintf(f1mark, sizeof(f1mark), "<<<<<<< %s", fmark);
+ snprintf(f3mark, sizeof(f3mark), ">>>>>>> %s", rmark);
+
+ increase();
+ m = readin(argv[0], &d13);
+ n = readin(argv[1], &d23);
+
+ for (i = 0; i <= 2; i++) {
+ if ((fp[i] = fopen(argv[i + 2], "r")) == NULL) {
+ cvs_log(LP_ERRNO, "%s", argv[i + 2]);
+ return (-1);
+ }
+ }
+
+ return (merge(m, n));
+}
+
+int
+ed_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines)
+{
+ char op, *ep;
+ struct cvs_line *sort, *lp, *dlp, *ndlp;
+ int start, end, busy, i, lineno;
+
+ dlp = TAILQ_FIRST(&(dlines->l_lines));
+ lp = TAILQ_FIRST(&(plines->l_lines));
+
+ busy = 0;
+ for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
+ lp = TAILQ_NEXT(lp, l_list)) {
+ op = lp->l_line[strlen(lp->l_line) - 1];
+ start = (int)strtol(lp->l_line, &ep, 10);
+ if (op == 'a') {
+ if ((start > dlines->l_nblines) ||
+ (start < 0) || (*ep != 'a'))
+ return (-1);
+ } else if (op == 'c') {
+ if ((start > dlines->l_nblines) ||
+ (start < 0) || (*ep != ','))
+ return (-1);
+
+ ep++;
+ end = (int)strtol(ep, &ep, 10);
+ if ((end < 0) || (*ep != 'c'))
+ return (-1);
+ }
+
+ for (;;) {
+ if (dlp == NULL)
+ break;
+ if (dlp->l_lineno == start)
+ break;
+ if (dlp->l_lineno > start) {
+ dlp = TAILQ_PREV(dlp, cvs_tqh, l_list);
+ } else if (dlp->l_lineno < start) {
+ ndlp = TAILQ_NEXT(dlp, l_list);
+ if (ndlp->l_lineno > start)
+ break;
+ dlp = ndlp;
+ }
+ }
+
+ if (dlp == NULL)
+ return (-1);
+
+ if (op == 'c') {
+ for (i = start; i <= end; i++) {
+ ndlp = TAILQ_NEXT(dlp, l_list);
+ TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
+ dlp = ndlp;
+ }
+ }
+
+ if (op == 'a' || op == 'c') {
+ for (;;) {
+ ndlp = lp;
+ lp = TAILQ_NEXT(lp, l_list);
+ if (lp == NULL)
+ return (-1);
+
+ if (!strcmp(lp->l_line, "."))
+ break;
+
+ TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
+ TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
+ lp, l_list);
+ dlp = lp;
+
+ lp->l_lineno = start;
+ lp = ndlp;
+ }
+ }
+
+ /*
+ * always resort lines as the markers might be put at the
+ * same line as we first started editing.
+ */
+ lineno = 0;
+ TAILQ_FOREACH(sort, &(dlines->l_lines), l_list)
+ sort->l_lineno = lineno++;
+ dlines->l_nblines = lineno - 1;
+ }
+
+ return (0);
+}
+
+/*
+ * Pick up the line numbers of all changes from one change file.
+ * (This puts the numbers in a vector, which is not strictly necessary,
+ * since the vector is processed in one sequential pass.
+ * The vector could be optimized out of existence)
+ */
+static int
+readin(char *name, struct diff **dd)
+{
+ int a, b, c, d;
+ char kind, *p;
+ size_t i;
+
+ fp[0] = fopen(name, "r");
+ for (i = 0; (p = getchange(fp[0])); i++) {
+ if (i >= szchanges - 1)
+ increase();
+ a = b = number(&p);
+ if (*p == ',') {
+ p++;
+ b = number(&p);
+ }
+ kind = *p++;
+ c = d = number(&p);
+ if (*p==',') {
+ p++;
+ d = number(&p);
+ }
+ if (kind == 'a')
+ a++;
+ if (kind == 'd')
+ c++;
+ b++;
+ d++;
+ (*dd)[i].old.from = a;
+ (*dd)[i].old.to = b;
+ (*dd)[i].new.from = c;
+ (*dd)[i].new.to = d;
+ }
+
+ (*dd)[i].old.from = (*dd)[i-1].old.to;
+ (*dd)[i].new.from = (*dd)[i-1].new.to;
+ (void)fclose(fp[0]);
+
+ return (i);
+}
+
+static int
+number(char **lc)
+{
+ int nn;
+
+ nn = 0;
+ while (isdigit((unsigned char)(**lc)))
+ nn = nn*10 + *(*lc)++ - '0';
+
+ return (nn);
+}
+
+static char *
+getchange(FILE *b)
+{
+ char *line;
+
+ while ((line = getline(b, NULL))) {
+ if (isdigit((unsigned char)line[0]))
+ return (line);
+ }
+
+ return (NULL);
+}
+
+static char *
+getline(FILE *b, size_t *n)
+{
+ char *cp;
+ size_t len;
+ static char *buf;
+ static size_t bufsize;
+
+ if ((cp = fgetln(b, &len)) == NULL)
+ return (NULL);
+
+ if (cp[len - 1] != '\n')
+ len++;
+ if (len + 1 > bufsize) {
+ do {
+ bufsize += 1024;
+ } while (len + 1 > bufsize);
+ if ((buf = realloc(buf, bufsize)) == NULL)
+ err(EXIT_FAILURE, NULL);
+ }
+ memcpy(buf, cp, len - 1);
+ buf[len - 1] = '\n';
+ buf[len] = '\0';
+ if (n != NULL)
+ *n = len;
+
+ return (buf);
+}
+
+static int
+merge(int m1, int m2)
+{
+ struct diff *d1, *d2, *d3;
+ int dpl, j, t1, t2;
+
+ d1 = d13;
+ d2 = d23;
+ j = 0;
+ while ((t1 = d1 < d13 + m1) | (t2 = d2 < d23 + m2)) {
+ if (debug) {
+ printf("%d,%d=%d,%d %d,%d=%d,%d\n",
+ d1->old.from,d1->old.to,
+ d1->new.from,d1->new.to,
+ d2->old.from,d2->old.to,
+ d2->new.from,d2->new.to);
+ }
+
+ /* first file is different from others */
+ if (!t2 || (t1 && d1->new.to < d2->new.from)) {
+ /* stuff peculiar to 1st file */
+ if (eflag==0) {
+ separate("1");
+ change(1, &d1->old, 0);
+ keep(2, &d1->new);
+ change(3, &d1->new, 0);
+ }
+ d1++;
+ continue;
+ }
+
+ /* second file is different from others */
+ if (!t1 || (t2 && d2->new.to < d1->new.from)) {
+ if (eflag==0) {
+ separate("2");
+ keep(1, &d2->new);
+ change(2, &d2->old, 0);
+ change(3, &d2->new, 0);
+ }
+ d2++;
+ continue;
+ }
+
+ /*
+ * Merge overlapping changes in first file
+ * this happens after extension (see below).
+ */
+ if (d1 + 1 < d13 + m1 && d1->new.to >= d1[1].new.from) {
+ d1[1].old.from = d1->old.from;
+ d1[1].new.from = d1->new.from;
+ d1++;
+ continue;
+ }
+
+ /* merge overlapping changes in second */
+ if (d2 + 1 < d23 + m2 && d2->new.to >= d2[1].new.from) {
+ d2[1].old.from = d2->old.from;
+ d2[1].new.from = d2->new.from;
+ d2++;
+ continue;
+ }
+ /* stuff peculiar to third file or different in all */
+ if (d1->new.from == d2->new.from && d1->new.to == d2->new.to) {
+ dpl = duplicate(&d1->old,&d2->old);
+ if (dpl == -1)
+ return (-1);
+
+ /*
+ * dpl = 0 means all files differ
+ * dpl = 1 means files 1 and 2 identical
+ */
+ if (eflag==0) {
+ separate(dpl ? "3" : "");
+ change(1, &d1->old, dpl);
+ change(2, &d2->old, 0);
+ d3 = d1->old.to > d1->old.from ? d1 : d2;
+ change(3, &d3->new, 0);
+ } else
+ j = edit(d1, dpl, j);
+ d1++;
+ d2++;
+ continue;
+ }
+
+ /*
+ * Overlapping changes from file 1 and 2; extend changes
+ * appropriately to make them coincide.
+ */
+ if (d1->new.from < d2->new.from) {
+ d2->old.from -= d2->new.from-d1->new.from;
+ d2->new.from = d1->new.from;
+ } else if (d2->new.from < d1->new.from) {
+ d1->old.from -= d1->new.from-d2->new.from;
+ d1->new.from = d2->new.from;
+ }
+ if (d1->new.to > d2->new.to) {
+ d2->old.to += d1->new.to - d2->new.to;
+ d2->new.to = d1->new.to;
+ } else if (d2->new.to > d1->new.to) {
+ d1->old.to += d2->new.to - d1->new.to;
+ d1->new.to = d2->new.to;
+ }
+ }
+
+ return (edscript(j));
+}
+
+static void
+separate(const char *s)
+{
+ diff_output("====%s\n", s);
+}
+
+/*
+ * The range of lines rold.from thru rold.to in file i is to be changed.
+ * It is to be printed only if it does not duplicate something to be
+ * printed later.
+ */
+static void
+change(int i, struct range *rold, int fdup)
+{
+ diff_output("%d:", i);
+ last[i] = rold->to;
+ prange(rold);
+ if (fdup || debug)
+ return;
+ i--;
+ (void)skip(i, rold->from, NULL);
+ (void)skip(i, rold->to, " ");
+}
+
+/*
+ * print the range of line numbers, rold.from thru rold.to, as n1,n2 or n1
+ */
+static void
+prange(struct range *rold)
+{
+ if (rold->to <= rold->from)
+ diff_output("%da\n", rold->from - 1);
+ else {
+ diff_output("%d", rold->from);
+ if (rold->to > rold->from+1)
+ diff_output(",%d", rold->to - 1);
+ diff_output("c\n");
+ }
+}
+
+/*
+ * No difference was reported by diff between file 1 (or 2) and file 3,
+ * and an artificial dummy difference (trange) must be ginned up to
+ * correspond to the change reported in the other file.
+ */
+static void
+keep(int i, struct range *rnew)
+{
+ int delta;
+ struct range trange;
+
+ delta = last[3] - last[i];
+ trange.from = rnew->from - delta;
+ trange.to = rnew->to - delta;
+ change(i, &trange, 1);
+}
+
+/*
+ * skip to just before line number from in file "i". If "pr" is non-NULL,
+ * print all skipped stuff with string pr as a prefix.
+ */
+static int
+skip(int i, int from, char *pr)
+{
+ size_t j, n;
+ char *line;
+
+ for (n = 0; cline[i] < from - 1; n += j) {
+ if ((line = getline(fp[i], &j)) == NULL)
+ return (-1);
+ if (pr != NULL)
+ diff_output("%s%s", pr, line);
+ cline[i]++;
+ }
+ return ((int) n);
+}
+
+/*
+ * Return 1 or 0 according as the old range (in file 1) contains exactly
+ * the same data as the new range (in file 2).
+ */
+static int
+duplicate(struct range *r1, struct range *r2)
+{
+ int c,d;
+ int nchar;
+ int nline;
+
+ if (r1->to-r1->from != r2->to-r2->from)
+ return (0);
+ (void)skip(0, r1->from, NULL);
+ (void)skip(1, r2->from, NULL);
+ nchar = 0;
+ for (nline=0; nline < r1->to - r1->from; nline++) {
+ do {
+ c = getc(fp[0]);
+ d = getc(fp[1]);
+ if (c == -1 || d== -1)
+ return (-1);
+ nchar++;
+ if (c != d) {
+ repos(nchar);
+ return (0);
+ }
+ } while (c != '\n');
+ }
+ repos(nchar);
+ return (1);
+}
+
+static void
+repos(int nchar)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ (void)fseek(fp[i], (long)-nchar, 1);
+}
+
+/*
+ * collect an editing script for later regurgitation
+ */
+static int
+edit(struct diff *diff, int fdup, int j)
+{
+ if (((fdup + 1) & eflag) == 0)
+ return (j);
+ j++;
+ overlap[j] = !fdup;
+ if (!fdup)
+ overlapcnt++;
+ de[j].old.from = diff->old.from;
+ de[j].old.to = diff->old.to;
+ de[j].new.from = de[j-1].new.to + skip(2, diff->new.from, NULL);
+ de[j].new.to = de[j].new.from + skip(2, diff->new.to, NULL);
+ return (j);
+}
+
+/* regurgitate */
+static int
+edscript(int n)
+{
+ int j, k;
+ char block[BUFSIZ];
+
+ for (n = n; n > 0; n--) {
+ if (!oflag || !overlap[n])
+ prange(&de[n].old);
+ else
+ diff_output("%da\n=======\n", de[n].old.to -1);
+ (void)fseek(fp[2], (long)de[n].new.from, 0);
+ for (k = de[n].new.to-de[n].new.from; k > 0; k-= j) {
+ j = k > BUFSIZ ? BUFSIZ : k;
+ if (fread(block, 1, j, fp[2]) != (size_t)j)
+ return (-1);
+ block[j] = '\0';
+ diff_output("%s", block);
+ }
+ if (!oflag || !overlap[n])
+ diff_output(".\n");
+ else {
+ diff_output("%s\n.\n", f3mark);
+ diff_output("%da\n%s\n.\n", de[n].old.from - 1, f1mark);
+ }
+ }
+
+ return (overlapcnt);
+}
+
+static void
+increase(void)
+{
+ struct diff *p;
+ char *q;
+ size_t newsz, incr;
+
+ /* are the memset(3) calls needed? */
+ newsz = szchanges == 0 ? 64 : 2 * szchanges;
+ incr = newsz - szchanges;
+
+ p = realloc(d13, newsz * sizeof(struct diff));
+ if (p == NULL)
+ err(1, NULL);
+ memset(p + szchanges, 0, incr * sizeof(struct diff));
+ d13 = p;
+ p = realloc(d23, newsz * sizeof(struct diff));
+ if (p == NULL)
+ err(1, NULL);
+ memset(p + szchanges, 0, incr * sizeof(struct diff));
+ d23 = p;
+ p = realloc(de, newsz * sizeof(struct diff));
+ if (p == NULL)
+ err(1, NULL);
+ memset(p + szchanges, 0, incr * sizeof(struct diff));
+ de = p;
+ q = realloc(overlap, newsz * sizeof(char));
+ if (q == NULL)
+ err(1, NULL);
+ memset(q + szchanges, 0, incr * sizeof(char));
+ overlap = q;
+ szchanges = newsz;
+}
diff --git a/usr.bin/cvs/rcs.c b/usr.bin/cvs/rcs.c
index b9c6d59c078..b801a316539 100644
--- a/usr.bin/cvs/rcs.c
+++ b/usr.bin/cvs/rcs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rcs.c,v 1.93 2005/10/22 17:23:21 joris Exp $ */
+/* $OpenBSD: rcs.c,v 1.94 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -38,6 +38,7 @@
#include <string.h>
#include <unistd.h>
+#include "cvs.h"
#include "log.h"
#include "rcs.h"
#include "diff.h"
@@ -98,24 +99,10 @@ struct rcs_pdata {
};
-struct rcs_line {
- char *rl_line;
- int rl_lineno;
- TAILQ_ENTRY(rcs_line) rl_list;
-};
-TAILQ_HEAD(rcs_tqh, rcs_line);
-
-struct rcs_foo {
- int rl_nblines;
- char *rl_data;
- struct rcs_tqh rl_lines;
-};
-
#define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
#define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen
-
/* invalid characters in RCS symbol names */
static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
@@ -297,14 +284,11 @@ static void rcs_freepdata(struct rcs_pdata *);
static int rcs_gettok(RCSFILE *);
static int rcs_pushtok(RCSFILE *, const char *, int);
static int rcs_growbuf(RCSFILE *);
-static int rcs_patch_lines(struct rcs_foo *, struct rcs_foo *);
static int rcs_strprint(const u_char *, size_t, FILE *);
static int rcs_expand_keywords(char *, struct rcs_delta *, char *, char *,
size_t, int);
static struct rcs_delta *rcs_findrev(RCSFILE *, const RCSNUM *);
-static struct rcs_foo *rcs_splitlines(const char *);
-static void rcs_freefoo(struct rcs_foo *);
/*
* rcs_open()
@@ -1168,76 +1152,22 @@ rcs_tag_resolve(RCSFILE *file, const char *tag)
return (num);
}
-
-/*
- * rcs_patch()
- *
- * Apply an RCS-format patch pointed to by <patch> to the file contents
- * found in <data>.
- * Returns 0 on success, or -1 on failure.
- */
-BUF*
-rcs_patch(const char *data, const char *patch)
-{
- struct rcs_foo *dlines, *plines;
- struct rcs_line *lp;
- size_t len;
- int lineno;
- BUF *res;
-
- len = strlen(data);
- res = cvs_buf_alloc(len, BUF_AUTOEXT);
- if (res == NULL)
- return (NULL);
-
- dlines = rcs_splitlines(data);
- if (dlines == NULL) {
- cvs_buf_free(res);
- return (NULL);
- }
-
- plines = rcs_splitlines(patch);
- if (plines == NULL) {
- cvs_buf_free(res);
- rcs_freefoo(dlines);
- return (NULL);
- }
-
- if (rcs_patch_lines(dlines, plines) < 0) {
- cvs_buf_free(res);
- rcs_freefoo(plines);
- rcs_freefoo(dlines);
- return (NULL);
- }
-
- lineno = 0;
- TAILQ_FOREACH(lp, &dlines->rl_lines, rl_list) {
- if (lineno != 0)
- cvs_buf_fappend(res, "%s\n", lp->rl_line);
- lineno++;
- }
-
- rcs_freefoo(dlines);
- rcs_freefoo(plines);
- return (res);
-}
-
-static int
-rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
+int
+rcs_patch_lines(struct cvs_lines *dlines, struct cvs_lines *plines)
{
char op, *ep;
- struct rcs_line *lp, *dlp, *ndlp;
+ struct cvs_line *lp, *dlp, *ndlp;
int i, lineno, nbln;
- dlp = TAILQ_FIRST(&(dlines->rl_lines));
- lp = TAILQ_FIRST(&(plines->rl_lines));
+ dlp = TAILQ_FIRST(&(dlines->l_lines));
+ lp = TAILQ_FIRST(&(plines->l_lines));
/* skip first bogus line */
- for (lp = TAILQ_NEXT(lp, rl_list); lp != NULL;
- lp = TAILQ_NEXT(lp, rl_list)) {
- op = *(lp->rl_line);
- lineno = (int)strtol((lp->rl_line + 1), &ep, 10);
- if ((lineno > dlines->rl_nblines) || (lineno < 0) ||
+ for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
+ lp = TAILQ_NEXT(lp, l_list)) {
+ op = *(lp->l_line);
+ lineno = (int)strtol((lp->l_line + 1), &ep, 10);
+ if ((lineno > dlines->l_nblines) || (lineno < 0) ||
(*ep != ' ')) {
cvs_log(LP_ERR,
"invalid line specification in RCS patch");
@@ -1255,13 +1185,13 @@ rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
for (;;) {
if (dlp == NULL)
break;
- if (dlp->rl_lineno == lineno)
+ if (dlp->l_lineno == lineno)
break;
- if (dlp->rl_lineno > lineno) {
- dlp = TAILQ_PREV(dlp, rcs_tqh, rl_list);
- } else if (dlp->rl_lineno < lineno) {
- ndlp = TAILQ_NEXT(dlp, rl_list);
- if (ndlp->rl_lineno > lineno)
+ if (dlp->l_lineno > lineno) {
+ dlp = TAILQ_PREV(dlp, cvs_tqh, l_list);
+ } else if (dlp->l_lineno < lineno) {
+ ndlp = TAILQ_NEXT(dlp, l_list);
+ if (ndlp->l_lineno > lineno)
break;
dlp = ndlp;
}
@@ -1274,25 +1204,25 @@ rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
if (op == 'd') {
for (i = 0; (i < nbln) && (dlp != NULL); i++) {
- ndlp = TAILQ_NEXT(dlp, rl_list);
- TAILQ_REMOVE(&(dlines->rl_lines), dlp, rl_list);
+ ndlp = TAILQ_NEXT(dlp, l_list);
+ TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
dlp = ndlp;
}
} else if (op == 'a') {
for (i = 0; i < nbln; i++) {
ndlp = lp;
- lp = TAILQ_NEXT(lp, rl_list);
+ lp = TAILQ_NEXT(lp, l_list);
if (lp == NULL) {
cvs_log(LP_ERR, "truncated RCS patch");
return (-1);
}
- TAILQ_REMOVE(&(plines->rl_lines), lp, rl_list);
- TAILQ_INSERT_AFTER(&(dlines->rl_lines), dlp,
- lp, rl_list);
+ TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
+ TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
+ lp, l_list);
dlp = lp;
/* we don't want lookup to block on those */
- lp->rl_lineno = lineno;
+ lp->l_lineno = lineno;
lp = ndlp;
}
@@ -1302,15 +1232,15 @@ rcs_patch_lines(struct rcs_foo *dlines, struct rcs_foo *plines)
}
/* last line of the patch, done */
- if (lp->rl_lineno == plines->rl_nblines)
+ if (lp->l_lineno == plines->l_nblines)
break;
}
/* once we're done patching, rebuild the line numbers */
lineno = 0;
- TAILQ_FOREACH(lp, &(dlines->rl_lines), rl_list)
- lp->rl_lineno = lineno++;
- dlines->rl_nblines = lineno - 1;
+ TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
+ lp->l_lineno = lineno++;
+ dlines->l_nblines = lineno - 1;
return (0);
}
@@ -1331,8 +1261,8 @@ rcs_getrev(RCSFILE *rfp, RCSNUM *frev)
RCSNUM *crev, *rev;
BUF *rbuf, *dbuf = NULL;
struct rcs_delta *rdp = NULL;
- struct rcs_foo *lines;
- struct rcs_line *lp;
+ struct cvs_lines *lines;
+ struct cvs_line *lp;
char out[1024]; /* XXX */
if (rfp->rf_head == NULL)
@@ -1378,7 +1308,8 @@ rcs_getrev(RCSFILE *rfp, RCSNUM *frev)
return (NULL);
}
bp = cvs_buf_release(rbuf);
- rbuf = rcs_patch((char *)bp, (char *)rdp->rd_text);
+ rbuf = cvs_patchfile((char *)bp, (char *)rdp->rd_text,
+ rcs_patch_lines);
free(bp);
if (rbuf == NULL)
break;
@@ -1405,16 +1336,16 @@ rcs_getrev(RCSFILE *rfp, RCSNUM *frev)
}
bp = cvs_buf_release(rbuf);
- if ((lines = rcs_splitlines((char *)bp)) != NULL) {
+ if ((lines = cvs_splitlines((char *)bp)) != NULL) {
res = 0;
- TAILQ_FOREACH(lp, &lines->rl_lines, rl_list) {
+ TAILQ_FOREACH(lp, &lines->l_lines, l_list) {
if (res++ == 0)
continue;
rcs_expand_keywords(rfp->rf_path, rdp,
- lp->rl_line, out, sizeof(out), expmode);
+ lp->l_line, out, sizeof(out), expmode);
cvs_buf_fappend(dbuf, "%s\n", out);
}
- rcs_freefoo(lines);
+ cvs_freelines(lines);
}
free(bp);
}
@@ -2689,83 +2620,6 @@ rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
/*
- * rcs_splitlines()
- *
- * Split the contents of a file into a list of lines.
- */
-static struct rcs_foo*
-rcs_splitlines(const char *fcont)
-{
- char *dcp;
- struct rcs_foo *foo;
- struct rcs_line *lp;
-
- foo = (struct rcs_foo *)malloc(sizeof(*foo));
- if (foo == NULL) {
- cvs_log(LP_ERR, "failed to allocate line structure");
- return (NULL);
- }
- TAILQ_INIT(&(foo->rl_lines));
- foo->rl_nblines = 0;
- foo->rl_data = strdup(fcont);
- if (foo->rl_data == NULL) {
- cvs_log(LP_ERRNO, "failed to copy file contents");
- free(foo);
- return (NULL);
- }
-
- /*
- * Add a first bogus line with line number 0. This is used so we
- * can position the line pointer before 1 when changing the first line
- * in rcs_patch().
- */
- lp = (struct rcs_line *)malloc(sizeof(*lp));
- if (lp == NULL) {
- rcs_freefoo(foo);
- return (NULL);
- }
-
- lp->rl_line = NULL;
- lp->rl_lineno = 0;
- TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
-
-
- for (dcp = foo->rl_data; *dcp != '\0';) {
- lp = (struct rcs_line *)malloc(sizeof(*lp));
- if (lp == NULL) {
- rcs_freefoo(foo);
- cvs_log(LP_ERR, "failed to allocate line entry");
- return (NULL);
- }
-
- lp->rl_line = dcp;
- lp->rl_lineno = ++(foo->rl_nblines);
- TAILQ_INSERT_TAIL(&(foo->rl_lines), lp, rl_list);
-
- dcp = strchr(dcp, '\n');
- if (dcp == NULL) {
- break;
- }
- *(dcp++) = '\0';
- }
-
- return (foo);
-}
-
-static void
-rcs_freefoo(struct rcs_foo *fp)
-{
- struct rcs_line *lp;
-
- while ((lp = TAILQ_FIRST(&fp->rl_lines)) != NULL) {
- TAILQ_REMOVE(&fp->rl_lines, lp, rl_list);
- free(lp);
- }
- free(fp->rl_data);
- free(fp);
-}
-
-/*
* rcs_growbuf()
*
* Attempt to grow the internal parse buffer for the RCS file <rf> by
diff --git a/usr.bin/cvs/rcs.h b/usr.bin/cvs/rcs.h
index 8ebf55ba010..326e5bd83b1 100644
--- a/usr.bin/cvs/rcs.h
+++ b/usr.bin/cvs/rcs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rcs.h,v 1.35 2005/10/15 22:56:03 niallo Exp $ */
+/* $OpenBSD: rcs.h,v 1.36 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -221,8 +221,6 @@ int rcs_kflag_get(const char *);
void rcs_kflag_usage(void);
int rcs_kw_expand(RCSFILE *, u_char *, size_t, size_t *);
-BUF *rcs_patch(const char *, const char *);
-
RCSNUM *rcsnum_alloc(void);
RCSNUM *rcsnum_parse(const char *);
RCSNUM *rcsnum_brtorev(const RCSNUM *);
diff --git a/usr.bin/cvs/resp.c b/usr.bin/cvs/resp.c
index 5c3df305d6a..06fabf277ce 100644
--- a/usr.bin/cvs/resp.c
+++ b/usr.bin/cvs/resp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: resp.c,v 1.60 2005/10/07 21:47:32 reyk Exp $ */
+/* $OpenBSD: resp.c,v 1.61 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -838,7 +838,7 @@ cvs_resp_rcsdiff(struct cvsroot *root, int type, char *line)
orig = cvs_buf_release(fcont);
patch = cvs_buf_release(patchbuf);
- res = rcs_patch(orig, patch);
+ res = cvs_patchfile(orig, patch, rcs_patch_lines);
if (res == NULL)
return (-1);
diff --git a/usr.bin/cvs/util.c b/usr.bin/cvs/util.c
index 4ba6ef57263..edf65d8059d 100644
--- a/usr.bin/cvs/util.c
+++ b/usr.bin/cvs/util.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.c,v 1.53 2005/10/07 21:47:32 reyk Exp $ */
+/* $OpenBSD: util.c,v 1.54 2005/10/22 17:32:57 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -39,6 +39,8 @@
#include "cvs.h"
#include "log.h"
+#if !defined(RCSPROG)
+
/* letter -> mode type map */
static const int cvs_modetypes[26] = {
-1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1,
@@ -914,3 +916,113 @@ cvs_parse_tagfile(char **tagp, char **datep, int *nbp)
(void)fclose(fp);
}
+
+#endif /* !RCSPROG */
+
+/*
+ * Split the contents of a file into a list of lines.
+ */
+struct cvs_lines *
+cvs_splitlines(const char *fcont)
+{
+ char *dcp;
+ struct cvs_lines *lines;
+ struct cvs_line *lp;
+
+ lines = (struct cvs_lines *)malloc(sizeof(*lines));
+ if (lines == NULL)
+ return (NULL);
+
+ TAILQ_INIT(&(lines->l_lines));
+ lines->l_nblines = 0;
+ lines->l_data = strdup(fcont);
+ if (lines->l_data == NULL) {
+ free(lines);
+ return (NULL);
+ }
+
+ lp = (struct cvs_line *)malloc(sizeof(*lp));
+ if (lp == NULL) {
+ cvs_freelines(lines);
+ return (NULL);
+ }
+
+ lp->l_line = NULL;
+ lp->l_lineno = 0;
+ TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
+
+ for (dcp = lines->l_data; *dcp != '\0';) {
+ lp = (struct cvs_line *)malloc(sizeof(*lp));
+ if (lp == NULL) {
+ cvs_freelines(lines);
+ return (NULL);
+ }
+
+ lp->l_line = dcp;
+ lp->l_lineno = ++(lines->l_nblines);
+ TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list);
+
+ dcp = strchr(dcp, '\n');
+ if (dcp == NULL)
+ break;
+ *(dcp++) = '\0';
+ }
+
+ return (lines);
+}
+
+void
+cvs_freelines(struct cvs_lines *lines)
+{
+ struct cvs_line *lp;
+
+ while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) {
+ TAILQ_REMOVE(&(lines->l_lines), lp, l_list);
+ free(lp);
+ }
+
+ free(lines->l_data);
+ free(lines);
+}
+
+BUF *
+cvs_patchfile(const char *data, const char *patch,
+ int (*p)(struct cvs_lines *, struct cvs_lines *))
+{
+ struct cvs_lines *dlines, *plines;
+ struct cvs_line *lp;
+ size_t len;
+ int lineno;
+ BUF *res;
+
+ len = strlen(data);
+
+ if ((dlines = cvs_splitlines(data)) == NULL)
+ return (NULL);
+
+ if ((plines = cvs_splitlines(patch)) == NULL)
+ return (NULL);
+
+ if (p(dlines, plines) < 0) {
+ cvs_freelines(dlines);
+ cvs_freelines(plines);
+ return (NULL);
+ }
+
+ if ((res = cvs_buf_alloc(len, BUF_AUTOEXT)) == NULL) {
+ cvs_freelines(dlines);
+ cvs_freelines(plines);
+ return (NULL);
+ }
+
+ lineno = 0;
+ TAILQ_FOREACH(lp, &dlines->l_lines, l_list) {
+ if (lineno != 0)
+ cvs_buf_fappend(res, "%s\n", lp->l_line);
+ lineno++;
+ }
+
+ cvs_freelines(dlines);
+ cvs_freelines(plines);
+ return (res);
+}