version 1.7, 2005/12/27 04:35:22 |
version 1.8, 2005/12/27 04:43:01 |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/wait.h> |
#include <sys/wait.h> |
|
|
#include <assert.h> |
|
#include <ctype.h> |
#include <ctype.h> |
#include <err.h> |
#include <err.h> |
#include <getopt.h> |
#include <getopt.h> |
|
|
size_t line_width; /* width of a line (two columns and divider) */ |
size_t line_width; /* width of a line (two columns and divider) */ |
size_t width; /* width of each column */ |
size_t width; /* width of each column */ |
size_t file1ln, file2ln; /* line number of file1 and file2 */ |
size_t file1ln, file2ln; /* line number of file1 and file2 */ |
int Dflag; /* debug - verify lots of things */ |
|
int lflag; /* print only left column for identical lines */ |
int lflag; /* print only left column for identical lines */ |
int sflag; /* skip identical lines */ |
int sflag; /* skip identical lines */ |
FILE *outfile; /* file to save changes to */ |
FILE *outfile; /* file to save changes to */ |
|
|
/* Add first argument, the program name. */ |
/* Add first argument, the program name. */ |
diffargv[diffargc++] = diffprog; |
diffargv[diffargc++] = diffprog; |
|
|
while ((ch = getopt_long(argc, argv, "aBbDdEHI:ilo:stWw:", |
while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:", |
longopts, NULL)) != -1) { |
longopts, NULL)) != -1) { |
const char *errstr; |
const char *errstr; |
|
|
|
|
case 'b': |
case 'b': |
diffargv[diffargc++] = "-b"; |
diffargv[diffargc++] = "-b"; |
break; |
break; |
case 'D': |
|
Dflag = 1; |
|
break; |
|
case 'd': |
case 'd': |
diffargv[diffargc++] = "-d"; |
diffargv[diffargc++] = "-d"; |
break; |
break; |
|
|
|
|
/* Subtract column divider and divide by two. */ |
/* Subtract column divider and divide by two. */ |
width = (wflag - 3) / 2; |
width = (wflag - 3) / 2; |
if (Dflag) |
|
assert(width > 0); |
|
/* Make sure line_width can fit in size_t. */ |
/* Make sure line_width can fit in size_t. */ |
if (width > (SIZE_T_MAX - 3) / 2) |
if (width > (SIZE_T_MAX - 3) / 2) |
errx(2, "width is too large: %zu", width); |
errx(2, "width is too large: %zu", width); |
|
|
static void |
static void |
printcol(const char *s, size_t *col, const size_t col_max) |
printcol(const char *s, size_t *col, const size_t col_max) |
{ |
{ |
if (Dflag) { |
|
assert(s); |
|
assert(*col <= col_max); |
|
} |
|
|
|
for (; *s && *col < col_max; ++s) { |
for (; *s && *col < col_max; ++s) { |
size_t new_col; |
size_t new_col; |
|
|
if (Dflag) |
|
assert(*s != '\n'); |
|
|
|
switch (*s) { |
switch (*s) { |
case '\t': |
case '\t': |
/* |
/* |
|
|
{ |
{ |
size_t col; |
size_t col; |
|
|
if (Dflag) { |
|
/* These are the only legal column dividers. */ |
|
assert(div == '<' || div == '|' || div == '>' || div == ' '); |
|
/* These are the only valid combinations. */ |
|
assert((s1 != NULL && div == '<' && s2 == NULL) || div != '<'); |
|
assert((s1 == NULL && div == '>' && s2 != NULL) || div != '>'); |
|
assert((s1 != NULL && div == '|' && s2 != NULL && s2 != s1) || |
|
div != '|'); |
|
assert((s1 != NULL && div == ' ' && (s2 == s1 || s2 == NULL)) || |
|
div != ' '); |
|
} |
|
|
|
/* Print first column. Skips if s1 == NULL. */ |
/* Print first column. Skips if s1 == NULL. */ |
col = 0; |
col = 0; |
if (s1) { |
if (s1) { |
/* Skip angle bracket and space. */ |
/* Skip angle bracket and space. */ |
printcol(s1, &col, width); |
printcol(s1, &col, width); |
|
|
/* We should never exceed the width. */ |
|
if (Dflag) |
|
assert(col <= width); |
|
} |
} |
|
|
/* Only print left column. */ |
/* Only print left column. */ |
|
|
/* Skip angle bracket and space. */ |
/* Skip angle bracket and space. */ |
printcol(s2, &col, line_width); |
printcol(s2, &col, line_width); |
|
|
/* We should never exceed the line width. */ |
|
if (Dflag) |
|
assert(col <= line_width); |
|
|
|
putchar('\n'); |
putchar('\n'); |
} |
} |
|
|
|
|
const char delim[3] = {'\0', '\0', '\0'}; |
const char delim[3] = {'\0', '\0', '\0'}; |
char *s; |
char *s; |
|
|
if (Dflag) |
|
assert(file); |
|
|
|
/* XXX - Is this necessary? */ |
/* XXX - Is this necessary? */ |
clearerr(file); |
clearerr(file); |
|
|
|
|
err(2, "error reading file"); |
err(2, "error reading file"); |
|
|
if (!s) { |
if (!s) { |
/* NULL from fparseln() should mean EOF. */ |
|
if (Dflag) |
|
assert(feof(file)); |
|
|
|
return (NULL); |
return (NULL); |
} |
} |
|
|
|
|
/* Process unmodified lines. */ |
/* Process unmodified lines. */ |
processq(); |
processq(); |
|
|
if (Dflag) { |
|
/* |
|
* We are now at the line where adds, changes, |
|
* or deletions occur. |
|
*/ |
|
assert(file1start == file1ln); |
|
assert(file2start == file2ln); |
|
assert(file1start <= file1end); |
|
assert(file2start <= file2end); |
|
} |
|
switch (cmd) { |
switch (cmd) { |
case 'a': |
case 'a': |
/* A range cannot be specified for file1. */ |
|
if (Dflag) |
|
assert(file1start == file1end); |
|
|
|
printa(difffile, file2end); |
printa(difffile, file2end); |
break; |
break; |
|
|
|
|
break; |
break; |
|
|
case 'd': |
case 'd': |
/* A range cannot be specified for file2. */ |
|
if (Dflag) |
|
assert(file2start == file2end); |
|
|
|
printd(origfile, difffile, file1end); |
printd(origfile, difffile, file1end); |
break; |
break; |
|
|
|
|
static void |
static void |
freediff(const struct diffline *diffp) |
freediff(const struct diffline *diffp) |
{ |
{ |
if (Dflag) |
|
assert(diffp); |
|
|
|
if (diffp->left) |
if (diffp->left) |
free((void *)diffp->left); |
free((void *)diffp->left); |
|
|
const static char *oldstr = NULL; |
const static char *oldstr = NULL; |
char *newstr; |
char *newstr; |
|
|
if (Dflag) |
|
assert(append); |
|
|
|
/* |
/* |
* First string is NULL, so just copy append. |
* First string is NULL, so just copy append. |
|
|
offset = strlen(*s); |
offset = strlen(*s); |
oldstr = *s; |
oldstr = *s; |
} |
} |
/* This should always be the end of the string. */ |
|
if (Dflag) { |
|
assert(*(*s + offset) == '\0'); |
|
assert(strlen(*s) == offset); |
|
} |
|
|
|
/* Length = strlen(*s) + \n + strlen(append) + '\0'. */ |
/* Length = strlen(*s) + \n + strlen(append) + '\0'. */ |
newlen = offset + 1 + strlen(append) + 1; |
newlen = offset + 1 + strlen(append) + 1; |
|
|
strlcpy(*s + offset, "\n", newlen - offset); |
strlcpy(*s + offset, "\n", newlen - offset); |
copied = strlcat(*s + offset, append, newlen - offset); |
copied = strlcat(*s + offset, append, newlen - offset); |
|
|
/* |
|
* We should have copied exactly newlen characters, including |
|
* the terminating NUL. `copied' includes the \n character. |
|
*/ |
|
if (Dflag) |
|
assert(offset + copied + 1 == newlen); |
|
|
|
/* Store generated string's values. */ |
/* Store generated string's values. */ |
offset = newlen - 1; |
offset = newlen - 1; |
oldstr = *s; |
oldstr = *s; |
|
|
* should be the same. If div is not set, then store |
* should be the same. If div is not set, then store |
* this as this set's div. |
* this as this set's div. |
*/ |
*/ |
if (Dflag) |
|
assert(div == diffp->div || !div); |
|
if (!div) |
if (!div) |
div = diffp->div; |
div = diffp->div; |
|
|
|
|
astrcat(&right, diffp->right); |
astrcat(&right, diffp->right); |
} |
} |
|
|
/* div should no longer be NUL. */ |
|
if (Dflag) |
|
assert(div); |
|
|
|
/* Empty queue and free each diff line and its elements. */ |
/* Empty queue and free each diff line and its elements. */ |
while (!SIMPLEQ_EMPTY(&diffhead)) { |
while (!SIMPLEQ_EMPTY(&diffhead)) { |
diffp = SIMPLEQ_FIRST(&diffhead); |
diffp = SIMPLEQ_FIRST(&diffhead); |
|
|
{ |
{ |
size_t len; |
size_t len; |
|
|
if (Dflag) { |
|
assert(s); |
|
assert(*s == '<' || *s == '>'); |
|
assert(*(s + 1) == ' '); |
|
} |
|
|
|
/* Remove angle bracket and space but keep the NUL. */ |
/* Remove angle bracket and space but keep the NUL. */ |
len = strlen(s) - 2 + 1; |
len = strlen(s) - 2 + 1; |
/* Copy at least the NUL. */ |
|
if (Dflag) |
|
assert(len > 0); |
|
/* Move everything two characters over. */ |
/* Move everything two characters over. */ |
memmove(s, s + 2, len); |
memmove(s, s + 2, len); |
} |
} |
|
|
{ |
{ |
char *line; |
char *line; |
|
|
if (Dflag) { |
|
assert(file); |
|
assert(file2ln <= line2); |
|
} |
|
|
|
for (; file2ln <= line2; ++file2ln) { |
for (; file2ln <= line2; ++file2ln) { |
if (!(line = xfgets(file))) |
if (!(line = xfgets(file))) |
errx(2, "append ended early"); |
errx(2, "append ended early"); |
|
|
SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead); |
SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead); |
char *line; |
char *line; |
|
|
if (Dflag) { |
|
assert(file1); |
|
assert(file2); |
|
assert(file1ln <= file1end); |
|
assert(file2ln <= file2end); |
|
/* Change diff sets always start out with an empty queue. */ |
|
assert(SIMPLEQ_EMPTY(&diffhead)); |
|
} |
|
|
|
/* Read lines to be deleted. */ |
/* Read lines to be deleted. */ |
for (; file1ln <= file1end; ++file1ln) { |
for (; file1ln <= file1end; ++file1ln) { |
struct fileline *linep; |
struct fileline *linep; |
|
|
if (!(line2 = xfgets(file2))) |
if (!(line2 = xfgets(file2))) |
errx(2, "error reading diff in delete in change"); |
errx(2, "error reading diff in delete in change"); |
|
|
/* Verify lines. */ |
|
if (Dflag && strncmp("< ", line2, 2) != 0) |
|
errx(2, "invalid del/change diff: %s", line2); |
|
if (Dflag && strcmp(line1, line2 + 2)) |
|
warnx("diff differs from file1:\ndiff:\n%s\nfile:\n%s", |
|
line2, line1); |
|
|
|
/* Unused now. */ |
/* Unused now. */ |
free((void *)line2); |
free((void *)line2); |
|
|
|
|
/* There should be a divider here. */ |
/* There should be a divider here. */ |
if (!(line = xfgets(file2))) |
if (!(line = xfgets(file2))) |
errx(2, "error reading diff in change: expected divider"); |
errx(2, "error reading diff in change: expected divider"); |
if (Dflag && strcmp("---", line)) |
|
errx(2, "divider expected: %s", line); |
|
free(line); |
free(line); |
|
|
#define getaddln(add) do { \ |
#define getaddln(add) do { \ |
/* Read diff for line. */ \ |
/* Read diff for line. */ \ |
if (!((add) = xfgets(file2))) \ |
if (!((add) = xfgets(file2))) \ |
errx(2, "error reading add in change"); \ |
errx(2, "error reading add in change"); \ |
/* Verify line. */ \ |
|
if (Dflag && strncmp("> ", (add), 2)) \ |
|
errx(2, "invalid add/change diff: %s", (add)); \ |
|
/* Remove ``> ''. */ \ |
/* Remove ``> ''. */ \ |
undiff(add); \ |
undiff(add); \ |
} while (0) |
} while (0) |
|
|
{ |
{ |
const char *line1, *line2; |
const char *line1, *line2; |
|
|
if (Dflag) { |
|
assert(file1); |
|
assert(file2); |
|
assert(file1ln <= file1end); |
|
/* Delete diff sets always start with an empty queue. */ |
|
assert(SIMPLEQ_EMPTY(&diffhead)); |
|
} |
|
|
|
/* Print out lines file1ln to line2. */ |
/* Print out lines file1ln to line2. */ |
for (; file1ln <= file1end; ++file1ln) { |
for (; file1ln <= file1end; ++file1ln) { |
/* XXX - Why can't this handle stdin? */ |
/* XXX - Why can't this handle stdin? */ |
|
|
errx(2, "file1 ended early in delete"); |
errx(2, "file1 ended early in delete"); |
if (!(line2 = xfgets(file2))) |
if (!(line2 = xfgets(file2))) |
errx(2, "diff ended early in delete"); |
errx(2, "diff ended early in delete"); |
/* Compare delete line from diff to file1. */ |
|
if (Dflag && strcmp(line1, line2 + 2) != 0) |
|
warnx("diff differs from file1:\ndiff:\n%s\nfile:\n%s", |
|
line2, line1); |
|
free((void *)line2); |
free((void *)line2); |
enqueue(line1, '<', NULL); |
enqueue(line1, '<', NULL); |
} |
} |
|
|
extern char *__progname; |
extern char *__progname; |
|
|
fprintf(stderr, |
fprintf(stderr, |
"usage: %s [-abDdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n", |
"usage: %s [-abdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n", |
__progname); |
__progname); |
exit(2); |
exit(2); |
} |
} |