V10/cmd/idiff.c
/* idiff: interactive diff */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#define HUGE 10000 /* large number of lines */
char *progname;
char diffout[100];
char tempfile[100];
char *outfile = "idiff.out";
FILE *efopen(char *, char *);
char *basename(char *);
void idiff(FILE *f1, FILE *f2, FILE *fin, FILE *fout);
void parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2);
void nskip(FILE *fin, int n);
void ncopy(FILE *fin, int n, FILE *fout);
void cleanup(int);
/* these have to be undone in a non-posix world: */
/* int fileno(FILE *); */
#define S_ISDIR(n) ((n) & S_IFDIR) /* vaxes */
#define remove unlink
main(int argc, char *argv[])
{
FILE *fin, *fout, *f1, *f2;
char buf[BUFSIZ], diffopts[100], *p;
struct stat stbuf;
progname = argv[0];
buf[0] = 0;
while (argc > 3 && argv[1][0] == '-') { /* assume all flags go to diff for now */
strcat(diffopts, argv[1]);
strcat(diffopts, " ");
argv++;
argc--;
}
if (argc != 3) {
fprintf(stderr, "Usage: idiff [-opts] file1 file2\n");
exit(1);
}
f1 = efopen(argv[1], "r");
f2 = efopen(argv[2], "r");
fstat(fileno(f2), &stbuf);
if (S_ISDIR(stbuf.st_mode)) {
fclose(f2);
sprintf(buf, "%s/%s", argv[2], basename(argv[1]));
f2 = efopen(buf, "r");
}
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, cleanup);
fout = efopen(outfile, "w");
tmpnam(diffout);
sprintf(buf,"diff %s %s %s >%s", diffopts, argv[1], argv[2], diffout);
system(buf);
fin = efopen(diffout, "r");
tmpnam(tempfile);
idiff(f1, f2, fin, fout);
remove(diffout);
printf("%s output in file %s\n", progname, outfile);
exit(0);
}
void cleanup(int i)
{
remove(diffout);
remove(tempfile);
remove(outfile);
exit(1);
}
char *basename(char *s) /* find last component of filename */
{
static char *p;
for (p = s+strlen(s)-1; p >= s; p--)
if (*p == '/')
return p+1;
return s;
}
void idiff(FILE *f1, FILE *f2, FILE *fin, FILE *fout) /* process diffs */
{
char buf[BUFSIZ], buf2[BUFSIZ];
FILE *ft;
int cmd, n, from1, to1, from2, to2, nf1, nf2, done;
nf1 = nf2 = 0;
done = 0;
while (!done && fgets(buf, sizeof buf, fin) != NULL) {
parse(buf, &from1, &to1, &cmd, &from2, &to2);
n = to1-from1 + to2-from2 + 1; /* #lines from diff */
if (cmd == 'c')
n += 2;
else if (cmd == 'a')
from1++;
else if (cmd == 'd')
from2++;
printf("%s", buf);
while (n-- > 0) {
fgets(buf, sizeof buf, fin);
printf("%s", buf);
}
for(;;) {
printf("? ");
fflush(stdout);
fgets(buf, sizeof buf, stdin);
switch (buf[0]) {
case '2':
to1 = to2 = HUGE;
done = 1;
case '>':
nskip(f1, to1-nf1);
ncopy(f2, to2-nf2, fout);
break;
case '1':
to1 = to2 = HUGE;
done = 1;
case '<':
nskip(f2, to2-nf2);
ncopy(f1, to1-nf1, fout);
break;
case 'e':
ncopy(f1, from1-1-nf1, fout);
nskip(f2, from2-1-nf2);
ft = efopen(tempfile, "w");
ncopy(f1, to1+1-from1, ft);
fprintf(ft, "---\n");
ncopy(f2, to2+1-from2, ft);
fclose(ft);
sprintf(buf2, "ed %s", tempfile);
system(buf2);
ft = efopen(tempfile, "r");
ncopy(ft, HUGE, fout);
fclose(ft);
break;
case '!':
system(buf+1);
printf("!\n");
break;
case 'd':
nskip(f1, to1-nf1);
nskip(f2, to2-nf2);
break;
default:
printf("< > d e 1 2 !\n");
continue;
}
break;
}
nf1 = to1;
nf2 = to2;
}
ncopy(f1, HUGE, fout); /* can fail on very long files */
remove(tempfile);
}
void parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
{
#define a2i(p) while (isdigit(*s)) p = 10*(p) + *s++ - '0'
*pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
a2i(*pfrom1);
if (*s == ',') {
s++;
a2i(*pto1);
} else
*pto1 = *pfrom1;
*pcmd = *s++;
a2i(*pfrom2);
if (*s == ',') {
s++;
a2i(*pto2);
} else
*pto2 = *pfrom2;
}
void nskip(FILE *fin, int n) /* skip n lines of file fin */
{
char buf[BUFSIZ];
while (n-- > 0)
fgets(buf, sizeof buf, fin);
}
void ncopy(FILE *fin, int n, FILE *fout) /* copy n lines from fin to fout */
{
char buf[BUFSIZ];
while (n-- > 0) {
if (fgets(buf, sizeof buf, fin) == NULL)
return;
fputs(buf, fout);
}
}
FILE *efopen(char *file, char *mode) /* fopen file, die if can't */
{
FILE *fp;
extern char *progname;
if ((fp = fopen(file, mode)) != NULL)
return fp;
fprintf(stderr, "%s: can't open file %s mode %s\n",
progname, file, mode);
exit(1);
}