diff options
author | Joris Vink <joris@cvs.openbsd.org> | 2005-10-22 17:32:58 +0000 |
---|---|---|
committer | Joris Vink <joris@cvs.openbsd.org> | 2005-10-22 17:32:58 +0000 |
commit | 16e8927d403df86f9f577b0d491d1ef9ac11ce97 (patch) | |
tree | 074cdbe132f562b97d4cec177fd22af8aa77c729 | |
parent | 507840a892753fe4ed5a99c4f0105d29e2bea00a (diff) |
diff3 support, needed for merging files together;
"go for it" niallo@
-rw-r--r-- | usr.bin/cvs/cvs.h | 24 | ||||
-rw-r--r-- | usr.bin/cvs/diff.c | 7 | ||||
-rw-r--r-- | usr.bin/cvs/diff.h | 9 | ||||
-rw-r--r-- | usr.bin/cvs/diff3.c | 808 | ||||
-rw-r--r-- | usr.bin/cvs/rcs.c | 220 | ||||
-rw-r--r-- | usr.bin/cvs/rcs.h | 4 | ||||
-rw-r--r-- | usr.bin/cvs/resp.c | 4 | ||||
-rw-r--r-- | usr.bin/cvs/util.c | 114 |
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); +} |