nroff -man wiggle.1 > wiggle.man
clean:
- rm -f *.o *.man wiggle .version* demo.patch
+ rm -f *.o *.man wiggle .version* demo.patch version
find . -name core -o -name '*.tmp*' -o -name .tmp | xargs rm -f
install : wiggle wiggle.1
char short_options1[] = "xdmwlrhi123pVRvqB"; /* not mode B */
char short_options2[] = "xdmwlrhi123p::VRvqB"; /* mode B */
-
struct option long_options[] = {
{"browse", 0, 0, 'B'},
{"extract", 0, 0, 'x'},
best guess only included if there is one.
-- search show go to right column and highlight
+- search should go to right column and highlight
- do we always recenter top when we split - what if near but not at bottom
- side-by-side sometimes shows last line of a chunk with
the next chunk
* We walk the whole matrix in a breadth first fashion following a
* 'front' on which x+y is constant. Along this front we examine each
* diagonal. For each point we calculate a 'value' for the match so
- * far. This will be in some particlar chunk. For each chunk we
+ * far. This will be in some particular chunk. For each chunk we
* separately record the best value found so far, and where it was.
* To choose a new value for each point we calculate based on the
* previous value on each neighbouring diagonal and on this diagonal.
return rv;
}
-#if 0
-static void printword(struct elmnt e)
-{
- if (e.start[0])
- printf("%.*s", e.len, e.start);
- else {
- int a, b, c;
- sscanf(e.start+1, "%d %d %d", &a, &b, &c);
- printf("*** %d,%d **** %d\n", b, c, a);
- }
-}
-#endif
-
/*
* Reduce a file by discarding less interesting words
* Words that end with a newline are interesting (so all words
return csl;
}
-
#ifdef MAIN
main(int argc, char *argv[])
/*
* split patch or merge files.
- *
*/
#include "wiggle.h"
#include <stdlib.h>
+/* skip 'cp' past the new '\n', or all the way to 'end' */
static void skip_eol(char **cp, char *end)
{
char *c = *cp;
*cp = c;
}
+/* copy one line, or to end, from 'cp' into the stream, extending
+ * the stream.
+ */
static void copyline(struct stream *s, char **cp, char *end)
{
char *from = *cp;
memcpy(c, list[i].body, list[i].len);
c += list[i].len;
list[i].len = 0;
+ free(list[i].body);
}
c[0] = 0;
}
if (read(fd, s.body, s.len) != s.len) {
free(s.body);
s.body = NULL;
+ die();
}
} else
die();
return cnt;
}
-
struct ci make_merger(struct file af, struct file bf, struct file cf,
struct csl *csl1, struct csl *csl2, int words,
int ignore_already)
*
*/
-
-
struct chunk {
struct patch *patch; /* the patch this chunk is from */
struct file *file; /* the file this chunk patches */
struct patch *next; /* previous patch that was loaded */
} *patches = NULL;
-
-
void report(void)
{
struct patch *p;
new->depends[new->cnt-1] = old;
}
-
void add_chunk(struct patch *p, struct file *f, int os, int oe, int ns, int ne)
{
struct chunk *c = malloc(sizeof(struct chunk));
*/
/*
- * Split a stream into words or line
+ * Split a stream into words or lines
*
* A word is one of:
* string of [A-Za-z0-9_]
cnt = split_internal(c, end, type, NULL);
f.list = malloc(cnt*sizeof(struct elmnt));
+ if (!f.list)
+ die();
f.elcnt = split_internal(c, end, type, f.list);
return f;
rows = 15;
}
- /* Draw a bow around the 'help' area */
+ /* Draw a border around the 'help' area */
(void)attrset(A_STANDOUT);
for (c = left; c < left+cols; c++) {
mvaddch(top-1, c, '-');
}
}
-
/* Type names are needed for tracing only. */
static char *typenames[] = {
[End] = "End",
#define WIGGLED 64 /* a conflict that was successfully resolved */
#define CONFLICTED 128 /* a conflict that was not successfully resolved */
-
/* Displaying a Merge.
* The first step is to linearise the merge. The merge in inherently
* parallel with before/after streams. However much of the whole document
struct elmnt e;
int unmatched = 0;
-
do {
if (m[pos.p.m].type == Changed)
rv |= CHANGED | CHANGES;
NULL
};
static char *result_help[] = {
- "This is the 'result' view which show just the",
+ "This is the 'result' view which shows just the",
"result of applying the patch. When a conflict",
"occurred this view does not show the full conflict",
"but only the 'after' part of the patch. To see",
char **modehelp = merge_help;
int row, start = 0;
- int trow; /* screen-row while searching. If we cannot find,
+ int trow; /* screen-row while searching. If we cannot find,
* we forget this number */
int col = 0, target = 0;
struct mpos pos; /* current point */
sm = load_file(p->file);
}
}
+ /* FIXME check for errors in the stream */
fm = split_stream(sm, ByWord);
fb = split_stream(sb, ByWord);
fa = split_stream(sa, ByWord);
*
* Wiggle can also read patch and merge files. Unlike 'merge' it does not
* need to be given three separate files, but can be given a file and a patch
- * and it will extract the pieces of the two two other files that it needs from
+ * and it will extract the pieces of the two other files that it needs from
* the patch.
*
* Wiggle performs one of three core function:
* --diff -d report differences between two files
* --merge -m merge the changes between two files into a third file
*
+ * This is also a --browse (-B) mode which provides interactive access
+ * to the merger.
+ *
* To perform these, wiggle requires 1, 2, or 3 input streams respectively.
- * I can get there from individual files, from a diff (unified or context) or
+ * I can get these from individual files, from a diff (unified or context) or
* from a merge file.
*
* For merge:
* One of the flags -1 -2 or -3 must also be given and they indicate which
* part of the patch or merge to extract.
*
- * Difference calculate and merging is performed on lines (-l) or words (-w).
- * In the case of -w, an initial diff is computed based on non-trivial words.
- * i.e. spaces are ignored
+ * Difference calculation and merging is performed on lines (-l) or words (-w).
+ * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
+ * or 3/ any other single character.
+ *
+ * In the case of -w, an initial diff is computed based on non-trivial words
+ * which includes alhpanumeric words and newlines.
*
* This diff is computed from the ends of the file and is used to find
* a suitable starting point and range. Then a more precise diff is
printf("@@ -%d,%d +%d,%d @@\n", b, c, e, f);
}
-
-#if 0
-/* Remove any entries from the common-sublist that are
- * just spaces, tabs, or newlines
- */
-static void cleanlist(struct file a, struct file b, struct csl *list)
-{
- struct csl *new = list;
-
- while (list->len) {
- int i;
- int ap;
- for (ap = list->a; ap < list->a+list->len; ap++) {
- for (i = 0; i < a.list[ap].len; i++) {
- char c = a.list[ap].start[i];
- if (isalnum(c))
- break;
- }
- if (i != a.list[ap].len)
- break;
- }
- if (ap == list->a+list->len)
- list++;
- else
- *new++ = *list++;
- }
- *new = *list;
-}
-#endif
-
int main(int argc, char *argv[])
{
int opt;
FILE *outfile = stdout;
char *helpmsg;
char *base0;
+ struct ci ci;
struct stream f, flist[3];
struct file fl[3];
base0++;
else
base0 = argv[0];
-#if 0
- /* The name 'vpatch' seems to be used elsewhere */
- if (strcmp(base0, "vpatch") == 0) {
- Cmd = base0;
- mode = 'B';
- }
-#endif
+
while ((opt = getopt_long(argc, argv,
short_options(mode), long_options,
&option_index)) != -1)
csl1 = diff(fl[0], fl[1]);
csl2 = diff(fl[1], fl[2]);
-#if 0
- cleanlist(fl[0], fl[1], csl1);
- cleanlist(fl[1], fl[2], csl2);
-#endif
-
- {
- struct ci ci;
+ ci = print_merge2(outfile, &fl[0], &fl[1], &fl[2],
+ csl1, csl2, obj == 'w',
+ ignore);
+ if (!quiet && ci.conflicts)
+ fprintf(stderr,
+ "%d unresolved conflict%s found\n",
+ ci.conflicts,
+ ci.conflicts == 1 ? "" : "s");
+ if (!quiet && ci.ignored)
+ fprintf(stderr,
+ "%d already-applied change%s ignored\n",
+ ci.ignored,
+ ci.ignored == 1 ? "" : "s");
+ exit_status = (ci.conflicts > 0);
- ci = print_merge2(outfile, &fl[0], &fl[1], &fl[2],
- csl1, csl2, obj == 'w',
- ignore);
- if (!quiet && ci.conflicts)
- fprintf(stderr,
- "%d unresolved conflict%s found\n",
- ci.conflicts,
- ci.conflicts == 1 ? "" : "s");
- if (!quiet && ci.ignored)
- fprintf(stderr,
- "%d already-applied change%s ignored\n",
- ci.ignored,
- ci.ignored == 1 ? "" : "s");
- exit_status = (ci.conflicts > 0);
- }
if (replace) {
fclose(outfile);
if (rename(argv[optind], orignew) == 0 &&
int len;
};
-
+/* an 'elmnt' is a word or a line from the file.
+ * 'start' points into a 'body' in a stream.
+ * When a stream is made of 'diff' hunks, there is a special
+ * elmnt at the start of each hunk which starts with '\0' and
+ * records the line offsets of the hunk. These are 20 bytes long.
+ * "\0\d{5} \d{5} \d{5}\n\0"
+ * The 3 numbers are: chunk number, starting line, number if lines.
+ * An element with len==0 marks EOF.
+ */
struct elmnt {
char *start;
int hash;
strncmp(a->start, b->start, a->len) == 0;
}
+/* end-of-line is important for narrowing conflicts.
+ * In line mode, every element is a line and 'ends a line'
+ * In word mode, the newline element and the diff-hunk element
+ * end a line.
+ */
static inline int ends_line(struct elmnt e)
{
if (e.len == 20 && e.start[0] == 0)
* Actually... it is possibly for a 'Changed' section to bound
* a conflict as it indicates a successful match of A and B.
* For line-wise merges, any Changed or Unchanged section bounds a conflict
- * For word-wise merges, and Changed or Unchanged section that matches
+ * For word-wise merges, any Changed or Unchanged section that matches
* a newline, or immediately follows a newline (in all files) can bound
* a conflict.
*/
int conflicts, wiggles, ignored;
struct merge *merger;
};
-extern struct ci print_merge(FILE *out,
- struct file *a, struct file *b, struct file *c,
- struct csl *c1, struct csl *c2,
- int words);
extern struct ci print_merge2(FILE *out,
struct file *a, struct file *b, struct file *c,
struct csl *c1, struct csl *c2,
extern void cleanlist(struct file a, struct file b, struct csl *list);
-#define ByLine 0
-#define ByWord 1
+enum {
+ ByLine,
+ ByWord,
+};