From: Neil Brown Date: Sun, 13 May 2012 23:17:32 +0000 (+1000) Subject: Move parse_patch and related to new parse.c X-Git-Tag: v0.9~5 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=51d4fd0100e3bd2fa85e6b87a2a9dfc64062c4cf;p=wiggle.git Move parse_patch and related to new parse.c ... so we can use it from wiggle.c Signed-off-by: NeilBrown --- diff --git a/Makefile b/Makefile index 02f65c7..46ab769 100644 --- a/Makefile +++ b/Makefile @@ -16,9 +16,9 @@ LDLIBS = -lncurses all: wiggle wiggle.man test -wiggle : wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o \ +wiggle : wiggle.o load.o parse.o split.o extract.o diff.o bestmatch.o ReadMe.o \ merge2.o vpatch.o ccan/hash/hash.o -wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o \ +wiggle.o load.o parse.o split.o extract.o diff.o bestmatch.o ReadMe.o \ merge2.o vpatch.o :: wiggle.h split.o :: ccan/hash/hash.h config.h diff --git a/merge2.c b/merge2.c index 3408076..96768fb 100644 --- a/merge2.c +++ b/merge2.c @@ -24,7 +24,6 @@ */ #include "wiggle.h" -#include /* * Second attempt at merging.... @@ -49,11 +48,6 @@ static inline int min(int a, int b) { return a < b ? a : b; } -static inline void assert(int a) -{ - if (!a) - abort(); -} static int check_alreadyapplied(struct file af, struct file cf, struct merge *m) diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..15b06ae --- /dev/null +++ b/parse.c @@ -0,0 +1,324 @@ +/* + * wiggle - apply rejected patches + * + * Copyright (C) 2003-2012 Neil Brown + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Author: Neil Brown + * Email: + */ + +/* + * Parse a patch file to find the names of the different + * files to patch and record which parts of the patch + * file applies to which target file. + */ + +#include "wiggle.h" +#include +#include + +/* determine how much we need to stripe of the front of + * paths to find them from current directory. This is + * used to guess correct '-p' value. + */ +static int get_strip(char *file) +{ + int fd; + int strip = 0; + + while (file && *file) { + fd = open(file, O_RDONLY); + if (fd >= 0) { + close(fd); + return strip; + } + strip++; + file = strchr(file, '/'); + if (file) + while (*file == '/') + file++; + } + return -1; + +} + +int set_prefix(struct plist *pl, int n, int strip) +{ + int i; + for (i = 0; i < 4 && i < n && strip < 0; i++) + strip = get_strip(pl[i].file); + + if (strip < 0) { + fprintf(stderr, "%s: Cannot find files to patch: please specify --strip\n", + Cmd); + return 0; + } + for (i = 0; i < n; i++) { + char *p = pl[i].file; + int j; + for (j = 0; j < strip; j++) { + if (p) + p = strchr(p, '/'); + while (p && *p == '/') + p++; + } + if (p == NULL) { + fprintf(stderr, "%s: cannot strip %d segments from %s\n", + Cmd, strip, pl[i].file); + return 0; + } + pl[i].file = p; + } + return 1; +} + +static int pl_cmp(const void *av, const void *bv) +{ + const struct plist *a = av; + const struct plist *b = bv; + return strcmp(a->file, b->file); +} + +static int common_depth(char *a, char *b) +{ + /* find number of path segments that these two have + * in common + */ + int depth = 0; + while (1) { + char *c; + int al, bl; + c = strchr(a, '/'); + if (c) + al = c-a; + else + al = strlen(a); + c = strchr(b, '/'); + if (c) + bl = c-b; + else + bl = strlen(b); + if (al == 0 || al != bl || strncmp(a, b, al) != 0) + return depth; + a += al; + while (*a == '/') + a++; + b += bl; + while (*b == '/') + b++; + + depth++; + } +} + +static struct plist *patch_add_file(struct plist *pl, int *np, char *file, + unsigned int start, unsigned int end) +{ + /* size of pl is 0, 16, n^2 */ + int n = *np; + int asize; + + while (*file == '/') + /* leading '/' are bad... */ + file++; + + if (n == 0) + asize = 0; + else if (n <= 16) + asize = 16; + else if ((n&(n-1)) == 0) + asize = n; + else + asize = n+1; /* not accurate, but not too large */ + if (asize <= n) { + /* need to extend array */ + struct plist *npl; + if (asize < 16) + asize = 16; + else + asize += asize; + npl = realloc(pl, asize * sizeof(struct plist)); + if (!npl) { + fprintf(stderr, "realloc failed - skipping %s\n", file); + return pl; + } + pl = npl; + } + pl[n].file = file; + pl[n].start = start; + pl[n].end = end; + pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1; + pl[n].chunks = pl[n].wiggles = 0; pl[n].conflicts = 100; + pl[n].open = 1; + pl[n].calced = 0; + pl[n].is_merge = 0; + *np = n+1; + return pl; +} + +static struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr) +{ + /* any parent of file that is not a parent of curr + * needs to be added to pl + */ + int d = common_depth(file, curr); + char *buf = curr; + while (d) { + char *c = strchr(file, '/'); + int l; + if (c) + l = c-file; + else + l = strlen(file); + file += l; + curr += l; + while (*file == '/') + file++; + while (*curr == '/') + curr++; + d--; + } + while (*file) { + if (curr > buf && curr[-1] != '/') + *curr++ = '/'; + while (*file && *file != '/') + *curr++ = *file++; + while (*file == '/') + file++; + *curr = '\0'; + if (*file) + pl = patch_add_file(pl, np, strdup(buf), + 0, 0); + } + return pl; +} + +struct plist *sort_patches(struct plist *pl, int *np) +{ + /* sort the patches, add directory names, and re-sort */ + char curr[1024]; + char *prev; + int parents[100]; + int prevnode[100]; + int i, n; + qsort(pl, *np, sizeof(struct plist), pl_cmp); + curr[0] = 0; + n = *np; + for (i = 0; i < n; i++) + pl = add_dir(pl, np, pl[i].file, curr); + + qsort(pl, *np, sizeof(struct plist), pl_cmp); + + /* array is now stable, so set up parent pointers */ + n = *np; + curr[0] = 0; + prevnode[0] = -1; + prev = ""; + for (i = 0; i < n; i++) { + int d = common_depth(prev, pl[i].file); + if (d == 0) + pl[i].parent = -1; + else { + pl[i].parent = parents[d-1]; + pl[pl[i].parent].last = i; + } + pl[i].prev = prevnode[d]; + if (pl[i].prev > -1) + pl[pl[i].prev].next = i; + prev = pl[i].file; + parents[d] = i; + prevnode[d] = i; + prevnode[d+1] = -1; + } + return pl; +} + +struct plist *parse_patch(FILE *f, FILE *of, int *np) +{ + /* read a multi-file patch from 'f' and record relevant + * details in a plist. + * if 'of' >= 0, fd might not be seekable so we write + * to 'of' and use lseek on 'of' to determine position + */ + struct plist *plist = NULL; + + *np = 0; + while (!feof(f)) { + /* first, find the start of a patch: "\n+++ " + * grab the file name and scan to the end of a line + */ + char *target = "\n+++ "; + char *target2 = "\n--- "; + char *pos = target; + int c; + char name[1024]; + unsigned start, end; + + while (*pos && (c = fgetc(f)) != EOF) { + if (of) + fputc(c, of); + if (c == *pos) + pos++; + else + pos = target; + } + if (c == EOF) + break; + assert(c == ' '); + /* now read a file name */ + pos = name; + while ((c = fgetc(f)) != EOF + && c != '\t' && c != '\n' && c != ' ' && + pos - name < 1023) { + *pos++ = c; + if (of) + fputc(c, of); + } + *pos = 0; + if (c == EOF) + break; + if (of) + fputc(c, of); + while (c != '\n' && (c = fgetc(f)) != EOF) + if (of) + fputc(c, of); + + start = ftell(of ?: f); + + if (c == EOF) + break; + + /* now skip to end - "\n--- " */ + pos = target2+1; + + while (*pos && (c = fgetc(f)) != EOF) { + if (of) + fputc(c, of); + if (c == *pos) + pos++; + else + pos = target2; + } + end = ftell(of ?: f); + if (pos > target2) + end -= (pos - target2) - 1; + plist = patch_add_file(plist, np, + strdup(name), start, end); + } + return plist; +} diff --git a/vpatch.c b/vpatch.c index e881cea..5432b59 100644 --- a/vpatch.c +++ b/vpatch.c @@ -47,8 +47,6 @@ static void term_init(void); -#define assert(x) do { if (!(x)) abort(); } while (0) - /* global attributes */ unsigned int a_delete, a_added, a_common, a_sep, a_void, a_unmatched, a_extra, a_already; @@ -1148,25 +1146,6 @@ static char *merge_window_help[] = { NULL }; -/* plist stores a list of patched files in an array - * Each entry identifies a file, the range of the - * original patch which applies to this file, some - * statistics concerning how many conflicts etc, and - * some linkage information so the list can be viewed - * as a directory-tree. - */ -struct plist { - char *file; - unsigned int start, end; - int parent; - int next, prev, last; - int open; - int chunks, wiggles, conflicts; - int calced; - int is_merge; - char *before, *after; -}; - static void merge_window(struct plist *p, FILE *f, int reverse) { /* Display the merge window in one of the selectable modes, @@ -1915,297 +1894,6 @@ static void show_merge(char *origname, FILE *patch, int reverse, merge_window(&p, patch, reverse); } -static struct plist *patch_add_file(struct plist *pl, int *np, char *file, - unsigned int start, unsigned int end) -{ - /* size of pl is 0, 16, n^2 */ - int n = *np; - int asize; - - while (*file == '/') - /* leading '/' are bad... */ - file++; - - if (n == 0) - asize = 0; - else if (n <= 16) - asize = 16; - else if ((n&(n-1)) == 0) - asize = n; - else - asize = n+1; /* not accurate, but not too large */ - if (asize <= n) { - /* need to extend array */ - struct plist *npl; - if (asize < 16) - asize = 16; - else - asize += asize; - npl = realloc(pl, asize * sizeof(struct plist)); - if (!npl) { - fprintf(stderr, "realloc failed - skipping %s\n", file); - return pl; - } - pl = npl; - } - pl[n].file = file; - pl[n].start = start; - pl[n].end = end; - pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1; - pl[n].chunks = pl[n].wiggles = 0; pl[n].conflicts = 100; - pl[n].open = 1; - pl[n].calced = 0; - pl[n].is_merge = 0; - *np = n+1; - return pl; -} - -static struct plist *parse_patch(FILE *f, FILE *of, int *np) -{ - /* read a multi-file patch from 'f' and record relevant - * details in a plist. - * if 'of' >= 0, fd might not be seekable so we write - * to 'of' and use lseek on 'of' to determine position - */ - struct plist *plist = NULL; - - *np = 0; - while (!feof(f)) { - /* first, find the start of a patch: "\n+++ " - * grab the file name and scan to the end of a line - */ - char *target = "\n+++ "; - char *target2 = "\n--- "; - char *pos = target; - int c; - char name[1024]; - unsigned start, end; - - while (*pos && (c = fgetc(f)) != EOF) { - if (of) - fputc(c, of); - if (c == *pos) - pos++; - else - pos = target; - } - if (c == EOF) - break; - assert(c == ' '); - /* now read a file name */ - pos = name; - while ((c = fgetc(f)) != EOF - && c != '\t' && c != '\n' && c != ' ' && - pos - name < 1023) { - *pos++ = c; - if (of) - fputc(c, of); - } - *pos = 0; - if (c == EOF) - break; - if (of) - fputc(c, of); - while (c != '\n' && (c = fgetc(f)) != EOF) - if (of) - fputc(c, of); - - start = ftell(of ?: f); - - if (c == EOF) - break; - - /* now skip to end - "\n--- " */ - pos = target2+1; - - while (*pos && (c = fgetc(f)) != EOF) { - if (of) - fputc(c, of); - if (c == *pos) - pos++; - else - pos = target2; - } - end = ftell(of ?: f); - if (pos > target2) - end -= (pos - target2) - 1; - plist = patch_add_file(plist, np, - strdup(name), start, end); - } - return plist; -} - -static int pl_cmp(const void *av, const void *bv) -{ - const struct plist *a = av; - const struct plist *b = bv; - return strcmp(a->file, b->file); -} - -static int common_depth(char *a, char *b) -{ - /* find number of path segments that these two have - * in common - */ - int depth = 0; - while (1) { - char *c; - int al, bl; - c = strchr(a, '/'); - if (c) - al = c-a; - else - al = strlen(a); - c = strchr(b, '/'); - if (c) - bl = c-b; - else - bl = strlen(b); - if (al == 0 || al != bl || strncmp(a, b, al) != 0) - return depth; - a += al; - while (*a == '/') - a++; - b += bl; - while (*b == '/') - b++; - - depth++; - } -} - -static struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr) -{ - /* any parent of file that is not a parent of curr - * needs to be added to pl - */ - int d = common_depth(file, curr); - char *buf = curr; - while (d) { - char *c = strchr(file, '/'); - int l; - if (c) - l = c-file; - else - l = strlen(file); - file += l; - curr += l; - while (*file == '/') - file++; - while (*curr == '/') - curr++; - d--; - } - while (*file) { - if (curr > buf && curr[-1] != '/') - *curr++ = '/'; - while (*file && *file != '/') - *curr++ = *file++; - while (*file == '/') - file++; - *curr = '\0'; - if (*file) - pl = patch_add_file(pl, np, strdup(buf), - 0, 0); - } - return pl; -} - -static struct plist *sort_patches(struct plist *pl, int *np) -{ - /* sort the patches, add directory names, and re-sort */ - char curr[1024]; - char *prev; - int parents[100]; - int prevnode[100]; - int i, n; - qsort(pl, *np, sizeof(struct plist), pl_cmp); - curr[0] = 0; - n = *np; - for (i = 0; i < n; i++) - pl = add_dir(pl, np, pl[i].file, curr); - - qsort(pl, *np, sizeof(struct plist), pl_cmp); - - /* array is now stable, so set up parent pointers */ - n = *np; - curr[0] = 0; - prevnode[0] = -1; - prev = ""; - for (i = 0; i < n; i++) { - int d = common_depth(prev, pl[i].file); - if (d == 0) - pl[i].parent = -1; - else { - pl[i].parent = parents[d-1]; - pl[pl[i].parent].last = i; - } - pl[i].prev = prevnode[d]; - if (pl[i].prev > -1) - pl[pl[i].prev].next = i; - prev = pl[i].file; - parents[d] = i; - prevnode[d] = i; - prevnode[d+1] = -1; - } - return pl; -} - -/* determine how much we need to stripe of the front of - * paths to find them from current directory. This is - * used to guess correct '-p' value. - */ -static int get_strip(char *file) -{ - int fd; - int strip = 0; - - while (file && *file) { - fd = open(file, O_RDONLY); - if (fd >= 0) { - close(fd); - return strip; - } - strip++; - file = strchr(file, '/'); - if (file) - while (*file == '/') - file++; - } - return -1; - -} - -static int set_prefix(struct plist *pl, int n, int strip) -{ - int i; - for (i = 0; i < 4 && i < n && strip < 0; i++) - strip = get_strip(pl[i].file); - - if (strip < 0) { - fprintf(stderr, "%s: Cannot find files to patch: please specify --strip\n", - Cmd); - return 0; - } - for (i = 0; i < n; i++) { - char *p = pl[i].file; - int j; - for (j = 0; j < strip; j++) { - if (p) - p = strchr(p, '/'); - while (p && *p == '/') - p++; - } - if (p == NULL) { - fprintf(stderr, "%s: cannot strip %d segments from %s\n", - Cmd, strip, pl[i].file); - return 0; - } - pl[i].file = p; - } - return 1; -} - static void calc_one(struct plist *pl, FILE *f, int reverse) { struct stream s1, s2; diff --git a/wiggle.h b/wiggle.h index 82e8e10..8e55489 100644 --- a/wiggle.h +++ b/wiggle.h @@ -26,6 +26,13 @@ #include #include #include +#include + +static inline void assert(int a) +{ + if (!a) + abort(); +} struct stream { char *body; @@ -117,8 +124,31 @@ struct merge { */ }; + +/* plist stores a list of patched files in an array + * Each entry identifies a file, the range of the + * original patch which applies to this file, some + * statistics concerning how many conflicts etc, and + * some linkage information so the list can be viewed + * as a directory-tree. + */ +struct plist { + char *file; + unsigned int start, end; + int parent; + int next, prev, last; + int open; + int chunks, wiggles, conflicts; + int calced; + int is_merge; + char *before, *after; +}; + +extern struct plist *sort_patches(struct plist *pl, int *np); +extern struct plist *parse_patch(FILE *f, FILE *of, int *np); extern struct stream load_segment(FILE *f, unsigned int start, unsigned int end); +extern int set_prefix(struct plist *pl, int n, int strip); extern struct stream load_file(char *name); extern int split_patch(struct stream, struct stream*, struct stream*); extern int split_merge(struct stream, struct stream*, struct stream*,