version 1.12, 2005/12/28 05:57:46 |
version 1.13, 2006/02/14 08:26:20 |
|
|
static void enqueue(const char *, const char, const char *); |
static void enqueue(const char *, const char, const char *); |
static void freediff(const struct diffline *); |
static void freediff(const struct diffline *); |
static void int_usage(void); |
static void int_usage(void); |
static int parsecmd(FILE *, FILE *); |
static int parsecmd(FILE *, FILE *, FILE *); |
static void printa(FILE *, size_t); |
static void printa(FILE *, size_t); |
static void printc(FILE *, size_t, FILE *, size_t); |
static void printc(FILE *, size_t, FILE *, size_t); |
static void printcol(const char *, size_t *, const size_t); |
static void printcol(const char *, size_t *, const size_t); |
static void printd(FILE *, FILE *, size_t); |
static void printd(FILE *, size_t); |
static void println(const char *, const char, const char *); |
static void println(const char *, const char, const char *); |
static void processq(void); |
static void processq(void); |
static void prompt(const char *, const char *); |
static void prompt(const char *, const char *); |
static void undiff(char *); |
|
__dead static void usage(void); |
__dead static void usage(void); |
static char *xfgets(FILE *); |
static char *xfgets(FILE *); |
|
|
|
|
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 Iflag = 0; /* ignore sets matching regexp */ |
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 */ |
|
|
int |
int |
main(int argc, char **argv) |
main(int argc, char **argv) |
{ |
{ |
FILE *difffile, *origfile; |
FILE *diffpipe, *file1, *file2; |
size_t diffargc = 0, wflag = WIDTH; |
size_t diffargc = 0, wflag = WIDTH; |
int ch, fd[2], status; |
int ch, fd[2], status; |
pid_t pid; |
pid_t pid; |
const char *cmd, **diffargv, *diffprog = "diff"; |
const char **diffargv, *diffprog = "diff", *s1, *s2; |
|
|
/* |
/* |
* Process diff flags. |
* Process diff flags. |
|
|
diffargv[diffargc++] = "-H"; |
diffargv[diffargc++] = "-H"; |
break; |
break; |
case 'I': |
case 'I': |
|
Iflag = 1; |
diffargv[diffargc++] = "-I"; |
diffargv[diffargc++] = "-I"; |
diffargv[diffargc++] = optarg; |
diffargv[diffargc++] = optarg; |
break; |
break; |
|
|
close(fd[1]); |
close(fd[1]); |
|
|
/* Open pipe to diff command. */ |
/* Open pipe to diff command. */ |
if ((difffile = fdopen(fd[0], "r")) == NULL) |
if ((diffpipe = fdopen(fd[0], "r")) == NULL) |
err(2, "could not open diff pipe"); |
err(2, "could not open diff pipe"); |
/* If file1 was given as `-', open stdin. */ |
/* If file1 or file2 were given as `-', open stdin. */ |
/* XXX - Does not work. */ |
/* XXX - Does not work. */ |
if (strcmp(argv[0], "-") == 0) |
if (strcmp(argv[0], "-") == 0) |
origfile = stdin; |
file1 = stdin; |
/* Otherwise, open as normal file. */ |
/* Otherwise, open as normal file. */ |
else if ((origfile = fopen(argv[0], "r")) == NULL) |
else if ((file1 = fopen(argv[0], "r")) == NULL) |
err(2, "could not open file1: %s", argv[0]); |
err(2, "could not open file1: %s", argv[0]); |
|
/* XXX - Handle (file1 == file2 == stdin) case. */ |
|
if (strcmp(argv[1], "-") == 0) |
|
file2 = stdin; |
|
/* Otherwise, open as normal file. */ |
|
else if ((file2 = fopen(argv[1], "r")) == NULL) |
|
err(2, "could not open file2: %s", argv[1]); |
/* Line numbers start at one. */ |
/* Line numbers start at one. */ |
file1ln = file2ln = 1; |
file1ln = file2ln = 1; |
|
|
/* Read and parse diff output. */ |
/* Read and parse diff output. */ |
while (parsecmd(difffile, origfile) != EOF) |
while (parsecmd(diffpipe, file1, file2) != EOF) |
; |
; |
fclose(difffile); |
fclose(diffpipe); |
|
|
/* Wait for diff to exit. */ |
/* Wait for diff to exit. */ |
if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || |
if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || |
|
|
err(2, "diff exited abnormally"); |
err(2, "diff exited abnormally"); |
|
|
/* No more diffs, so print common lines. */ |
/* No more diffs, so print common lines. */ |
while ((cmd = xfgets(origfile))) |
if (lflag) |
enqueue(cmd, ' ', lflag ? NULL : cmd); |
while ((s1 = xfgets(file1))) |
fclose(origfile); |
enqueue(s1, ' ', NULL); |
|
else |
|
for (;;) { |
|
s1 = xfgets(file1); |
|
s2 = xfgets(file2); |
|
if (s1 || s2) |
|
enqueue(s1, ' ', s2); |
|
else |
|
break; |
|
} |
|
fclose(file1); |
|
fclose(file2); |
/* Process unmodified lines. */ |
/* Process unmodified lines. */ |
processq(); |
processq(); |
|
|
|
|
} |
} |
|
|
/* |
/* |
* Parse ed commands from diff and print lines from difffile |
* Parse ed commands from diffpipe and print lines from file1 (lines |
* (lines to add or change) or origfile (lines to change or delete). |
* to change or delete) or file2 (lines to add or change). |
* Returns EOF or not. |
* Returns EOF or 0. |
*/ |
*/ |
static int |
static int |
parsecmd(FILE *difffile, FILE *origfile) |
parsecmd(FILE *diffpipe, FILE *file1, FILE *file2) |
{ |
{ |
size_t file1start, file1end, file2start, file2end; |
size_t file1start, file1end, file2start, file2end, n; |
/* ed command line and pointer to characters in line */ |
/* ed command line and pointer to characters in line */ |
char *line, *p, *q; |
char *line, *p, *q; |
const char *errstr; |
const char *errstr; |
char c, cmd; |
char c, cmd; |
|
|
/* Read ed command. */ |
/* Read ed command. */ |
if (!(line = xfgets(difffile))) |
if (!(line = xfgets(diffpipe))) |
return (EOF); |
return (EOF); |
|
|
p = line; |
p = line; |
|
|
file2start = ++file2end; |
file2start = ++file2end; |
} |
} |
|
|
/* Skip unmodified lines. */ |
/* |
for (; file1ln < file1start; ++file1ln, ++file2ln) { |
* Continue reading file1 and file2 until we reach line numbers |
const char *line; |
* specified by diff. Should only happen with -I flag. |
|
*/ |
|
for (; file1ln < file1start && file2ln < file2start; |
|
++file1ln, ++file2ln) { |
|
const char *s1, *s2; |
|
|
if (!(line = xfgets(origfile))) |
if (!(s1 = xfgets(file1))) |
errx(2, "file1 shorter than expected"); |
errx(2, "file1 shorter than expected"); |
|
if (!(s2 = xfgets(file2))) |
|
errx(2, "file2 shorter than expected"); |
|
|
/* If the -l flag was specified, print only left column. */ |
/* If the -l flag was specified, print only left column. */ |
enqueue(line, ' ', lflag ? NULL : line); |
if (lflag) { |
|
free((void *)s2); |
|
/* |
|
* XXX - If -l and -I are both specified, all |
|
* unchanged or ignored lines are shown with a |
|
* `(' divider. This matches GNU sdiff, but I |
|
* believe it is a bug. Just check out: |
|
* gsdiff -l -I '^$' samefile samefile. |
|
*/ |
|
if (Iflag) |
|
enqueue(s1, '(', NULL); |
|
else |
|
enqueue(s1, ' ', NULL); |
|
} else |
|
enqueue(s1, ' ', s2); |
} |
} |
/* Process unmodified lines. */ |
/* Ignore deleted lines. */ |
|
for (; file1ln < file1start; ++file1ln) { |
|
const char *s; |
|
|
|
if (!(s = xfgets(file1))) |
|
errx(2, "file1 shorter than expected"); |
|
|
|
enqueue(s, '(', NULL); |
|
} |
|
/* Ignore added lines. */ |
|
for (; file2ln < file2start; ++file2ln) { |
|
const char *s; |
|
|
|
if (!(s = xfgets(file2))) |
|
errx(2, "file2 shorter than expected"); |
|
|
|
/* If -l flag was given, don't print right column. */ |
|
if (lflag) |
|
free((void *)s); |
|
else |
|
enqueue(NULL, ')', s); |
|
} |
|
|
|
/* Process unmodified or skipped lines. */ |
processq(); |
processq(); |
|
|
switch (cmd) { |
switch (cmd) { |
case 'a': |
case 'a': |
printa(difffile, file2end); |
printa(file2, file2end); |
|
n = file2end - file2start + 1; |
break; |
break; |
|
|
case 'c': |
case 'c': |
printc(origfile, file1end, difffile, file2end); |
printc(file1, file1end, file2, file2end); |
|
n = file1end - file1start + 1 + 1 + file2end - file2start + 1; |
break; |
break; |
|
|
case 'd': |
case 'd': |
printd(origfile, difffile, file1end); |
printd(file1, file1end); |
|
n = file1end - file1start + 1; |
break; |
break; |
|
|
default: |
default: |
errx(2, "invalid diff command: %c: %s", cmd, line); |
errx(2, "invalid diff command: %c: %s", cmd, line); |
} |
} |
|
|
|
/* Skip to next ed line. */ |
|
while (n--) |
|
if (!xfgets(diffpipe)) |
|
errx(2, "diff ended early"); |
|
|
return (0); |
return (0); |
} |
} |
|
|
|
|
static void |
static void |
freediff(const struct diffline *diffp) |
freediff(const struct diffline *diffp) |
{ |
{ |
|
|
if (diffp->left) |
if (diffp->left) |
free((void *)diffp->left); |
free((void *)diffp->left); |
/* |
if (diffp->right) |
* Free right string only if it is different than left. |
|
* The strings are the same when the lines are identical. |
|
*/ |
|
if (diffp->right && diffp->right != diffp->left) |
|
free((void *)diffp->right); |
free((void *)diffp->right); |
|
free((void *)diffp); |
} |
} |
|
|
/* |
/* |
|
|
div = diffp->div; |
div = diffp->div; |
|
|
/* |
/* |
* If the -s flag was not given or the lines are not |
* Print changed lines if -s was given, |
* identical then print columns. |
* print all lines if -s was not given. |
*/ |
*/ |
if (!sflag || diffp->div != ' ') |
if (!sflag || div == '|' || div == '<' || div == '>') |
println(diffp->left, diffp->div, diffp->right); |
println(diffp->left, diffp->div, diffp->right); |
|
|
/* Append new lines to diff set. */ |
/* Append new lines to diff set. */ |
|
|
/* 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); |
freediff(diffp); |
|
SIMPLEQ_REMOVE_HEAD(&diffhead, diffentries); |
SIMPLEQ_REMOVE_HEAD(&diffhead, diffentries); |
free(diffp); |
freediff(diffp); |
} |
} |
|
|
/* Write to outfile, prompting user if lines are different. */ |
/* Write to outfile, prompting user if lines are different. */ |
if (outfile) { |
if (outfile) |
if (div == ' ') |
switch (div) { |
|
case ' ': case '(': case ')': |
fprintf(outfile, "%s\n", left); |
fprintf(outfile, "%s\n", left); |
else |
break; |
|
case '|': case '<': case '>': |
prompt(left, right); |
prompt(left, right); |
} |
break; |
|
default: |
|
errx(2, "invalid divider: %c", div); |
|
} |
|
|
/* Free left and right. */ |
/* Free left and right. */ |
if (left) |
if (left) |
|
|
} |
} |
|
|
/* |
/* |
* Remove angle bracket in front of diff line. |
|
*/ |
|
static void |
|
undiff(char *s) |
|
{ |
|
size_t len; |
|
|
|
/* Remove angle bracket and space but keep the NUL. */ |
|
len = strlen(s) - 2 + 1; |
|
/* Move everything two characters over. */ |
|
memmove(s, s + 2, len); |
|
} |
|
|
|
/* |
|
* Print lines following an (a)ppend command. |
* Print lines following an (a)ppend command. |
*/ |
*/ |
static void |
static void |
|
|
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"); |
undiff(line); |
|
enqueue(NULL, '>', line); |
enqueue(NULL, '>', line); |
} |
} |
|
|
|
|
const char *line; |
const char *line; |
}; |
}; |
SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead); |
SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead); |
char *line; |
|
|
|
/* Read lines to be deleted. */ |
/* Read lines to be deleted. */ |
for (; file1ln <= file1end; ++file1ln) { |
for (; file1ln <= file1end; ++file1ln) { |
struct fileline *linep; |
struct fileline *linep; |
const char *line1, *line2; |
const char *line1; |
|
|
/* Read lines from both. */ |
/* Read lines from both. */ |
if (!(line1 = xfgets(file1))) |
if (!(line1 = xfgets(file1))) |
errx(2, "error reading file1 in delete in change"); |
errx(2, "error reading file1 in delete in change"); |
if (!(line2 = xfgets(file2))) |
|
errx(2, "error reading diff in delete in change"); |
|
|
|
/* Unused now. */ |
|
free((void *)line2); |
|
|
|
/* Add to delete queue. */ |
/* Add to delete queue. */ |
if (!(linep = malloc(sizeof(struct fileline)))) |
if (!(linep = malloc(sizeof(struct fileline)))) |
err(2, "printc"); |
err(2, "printc"); |
|
|
SIMPLEQ_INSERT_TAIL(&delqhead, linep, fileentries); |
SIMPLEQ_INSERT_TAIL(&delqhead, linep, fileentries); |
} |
} |
|
|
/* There should be a divider here. */ |
|
if (!(line = xfgets(file2))) |
|
errx(2, "error reading diff in change: expected divider"); |
|
free(line); |
|
|
|
#define getaddln(add) do { \ |
|
/* Read diff for line. */ \ |
|
if (!((add) = xfgets(file2))) \ |
|
errx(2, "error reading add in change"); \ |
|
/* Remove ``> ''. */ \ |
|
undiff(add); \ |
|
} while (0) |
|
/* Process changed lines.. */ |
/* Process changed lines.. */ |
for (; !SIMPLEQ_EMPTY(&delqhead) && file2ln <= file2end; |
for (; !SIMPLEQ_EMPTY(&delqhead) && file2ln <= file2end; |
++file2ln) { |
++file2ln) { |
|
|
char *add; |
char *add; |
|
|
/* Get add line. */ |
/* Get add line. */ |
getaddln(add); |
if (!(add = xfgets(file2))) |
|
errx(2, "error reading add in change"); |
|
|
del = SIMPLEQ_FIRST(&delqhead); |
del = SIMPLEQ_FIRST(&delqhead); |
enqueue(del->line, '|', add); |
enqueue(del->line, '|', add); |
|
|
char *add; |
char *add; |
|
|
/* Get add line. */ |
/* Get add line. */ |
getaddln(add); |
if (!(add = xfgets(file2))) |
|
errx(2, "error reading add in change"); |
|
|
enqueue(NULL, '>', add); |
enqueue(NULL, '>', add); |
} |
} |
|
|
* Print deleted lines from file, from file1ln to file1end. |
* Print deleted lines from file, from file1ln to file1end. |
*/ |
*/ |
static void |
static void |
printd(FILE *file1, FILE *file2, size_t file1end) |
printd(FILE *file1, size_t file1end) |
{ |
{ |
const char *line1, *line2; |
const char *line1; |
|
|
/* 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? */ |
if (!(line1 = xfgets(file1))) |
if (!(line1 = xfgets(file1))) |
errx(2, "file1 ended early in delete"); |
errx(2, "file1 ended early in delete"); |
if (!(line2 = xfgets(file2))) |
|
errx(2, "diff ended early in delete"); |
|
free((void *)line2); |
|
enqueue(line1, '<', NULL); |
enqueue(line1, '<', NULL); |
} |
} |
processq(); |
processq(); |