mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
336 lines
6.7 KiB
C
336 lines
6.7 KiB
C
|
/*
|
||
|
* interactive diff, inspired/stolen from
|
||
|
* kernighan and pike, _unix programming environment_.
|
||
|
*/
|
||
|
|
||
|
#include <u.h>
|
||
|
#include <libc.h>
|
||
|
#include <bio.h>
|
||
|
|
||
|
int diffbflag;
|
||
|
int diffwflag;
|
||
|
|
||
|
void copy(Biobuf*, char*, Biobuf*, char*);
|
||
|
void idiff(Biobuf*, char*, Biobuf*, char*, Biobuf*, char*, Biobuf*, char*);
|
||
|
int opentemp(char*, int, long);
|
||
|
void rundiff(char*, char*, int);
|
||
|
|
||
|
void
|
||
|
usage(void)
|
||
|
{
|
||
|
fprint(2, "usage: idiff [-bw] file1 file2\n");
|
||
|
exits("usage");
|
||
|
}
|
||
|
|
||
|
void
|
||
|
main(int argc, char **argv)
|
||
|
{
|
||
|
int fd, ofd;
|
||
|
char diffout[40], idiffout[40];
|
||
|
Biobuf *b1, *b2, bdiff, bout, bstdout;
|
||
|
Dir *d;
|
||
|
|
||
|
ARGBEGIN{
|
||
|
default:
|
||
|
usage();
|
||
|
case 'b':
|
||
|
diffbflag++;
|
||
|
break;
|
||
|
case 'w':
|
||
|
diffwflag++;
|
||
|
break;
|
||
|
}ARGEND
|
||
|
|
||
|
if(argc != 2)
|
||
|
usage();
|
||
|
|
||
|
if((d = dirstat(argv[0])) == nil)
|
||
|
sysfatal("stat %s: %r", argv[0]);
|
||
|
if(d->mode&DMDIR)
|
||
|
sysfatal("%s is a directory", argv[0]);
|
||
|
free(d);
|
||
|
if((d = dirstat(argv[1])) == nil)
|
||
|
sysfatal("stat %s: %r", argv[1]);
|
||
|
if(d->mode&DMDIR)
|
||
|
sysfatal("%s is a directory", argv[1]);
|
||
|
free(d);
|
||
|
|
||
|
if((b1 = Bopen(argv[0], OREAD)) == nil)
|
||
|
sysfatal("open %s: %r", argv[0]);
|
||
|
if((b2 = Bopen(argv[1], OREAD)) == nil)
|
||
|
sysfatal("open %s: %r", argv[1]);
|
||
|
|
||
|
strcpy(diffout, "/tmp/idiff.XXXXXX");
|
||
|
fd = opentemp(diffout, ORDWR|ORCLOSE, 0);
|
||
|
strcpy(idiffout, "/tmp/idiff.XXXXXX");
|
||
|
ofd = opentemp(idiffout, ORDWR|ORCLOSE, 0);
|
||
|
rundiff(argv[0], argv[1], fd);
|
||
|
seek(fd, 0, 0);
|
||
|
Binit(&bdiff, fd, OREAD);
|
||
|
Binit(&bout, ofd, OWRITE);
|
||
|
idiff(b1, argv[0], b2, argv[1], &bdiff, diffout, &bout, idiffout);
|
||
|
Bterm(&bdiff);
|
||
|
Bflush(&bout);
|
||
|
seek(ofd, 0, 0);
|
||
|
Binit(&bout, ofd, OREAD);
|
||
|
Binit(&bstdout, 1, OWRITE);
|
||
|
copy(&bout, idiffout, &bstdout, "<stdout>");
|
||
|
exits(nil);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
opentemp(char *template, int mode, long perm)
|
||
|
{
|
||
|
int fd, i;
|
||
|
char *p;
|
||
|
|
||
|
p = strdup(template);
|
||
|
if(p == nil)
|
||
|
sysfatal("strdup out of memory");
|
||
|
fd = -1;
|
||
|
for(i=0; i<10; i++){
|
||
|
mktemp(p);
|
||
|
if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
|
||
|
break;
|
||
|
strcpy(p, template);
|
||
|
}
|
||
|
if(fd < 0)
|
||
|
sysfatal("could not create temporary file");
|
||
|
strcpy(template, p);
|
||
|
free(p);
|
||
|
|
||
|
return fd;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
rundiff(char *arg1, char *arg2, int outfd)
|
||
|
{
|
||
|
char *arg[10], *p;
|
||
|
int narg, pid;
|
||
|
Waitmsg *w;
|
||
|
|
||
|
narg = 0;
|
||
|
arg[narg++] = "/bin/diff";
|
||
|
arg[narg++] = "-n";
|
||
|
if(diffbflag)
|
||
|
arg[narg++] = "-b";
|
||
|
if(diffwflag)
|
||
|
arg[narg++] = "-w";
|
||
|
arg[narg++] = arg1;
|
||
|
arg[narg++] = arg2;
|
||
|
arg[narg] = nil;
|
||
|
|
||
|
switch(pid = fork()){
|
||
|
case -1:
|
||
|
sysfatal("fork: %r");
|
||
|
|
||
|
case 0:
|
||
|
dup(outfd, 1);
|
||
|
close(0);
|
||
|
exec("/bin/diff", arg);
|
||
|
sysfatal("exec: %r");
|
||
|
|
||
|
default:
|
||
|
w = wait();
|
||
|
if(w==nil)
|
||
|
sysfatal("wait: %r");
|
||
|
if(w->pid != pid)
|
||
|
sysfatal("wait got unexpected pid %d", w->pid);
|
||
|
if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0)
|
||
|
sysfatal("%s", w->msg);
|
||
|
free(w);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
runcmd(char *cmd)
|
||
|
{
|
||
|
char *arg[10];
|
||
|
int narg, pid, wpid;
|
||
|
|
||
|
narg = 0;
|
||
|
arg[narg++] = "/bin/rc";
|
||
|
arg[narg++] = "-c";
|
||
|
arg[narg++] = cmd;
|
||
|
arg[narg] = nil;
|
||
|
|
||
|
switch(pid = fork()){
|
||
|
case -1:
|
||
|
sysfatal("fork: %r");
|
||
|
|
||
|
case 0:
|
||
|
exec("/bin/rc", arg);
|
||
|
sysfatal("exec: %r");
|
||
|
|
||
|
default:
|
||
|
wpid = waitpid();
|
||
|
if(wpid < 0)
|
||
|
sysfatal("wait: %r");
|
||
|
if(wpid != pid)
|
||
|
sysfatal("wait got unexpected pid %d", wpid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
|
||
|
{
|
||
|
*pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
|
||
|
|
||
|
s = strchr(s, ':');
|
||
|
if(s == nil)
|
||
|
sysfatal("bad diff output0");
|
||
|
s++;
|
||
|
*pfrom1 = strtol(s, &s, 10);
|
||
|
if(*s == ','){
|
||
|
s++;
|
||
|
*pto1 = strtol(s, &s, 10);
|
||
|
}else
|
||
|
*pto1 = *pfrom1;
|
||
|
if(*s++ != ' ')
|
||
|
sysfatal("bad diff output1");
|
||
|
*pcmd = *s++;
|
||
|
if(*s++ != ' ')
|
||
|
sysfatal("bad diff output2");
|
||
|
s = strchr(s, ':');
|
||
|
if(s == nil)
|
||
|
sysfatal("bad diff output3");
|
||
|
s++;
|
||
|
*pfrom2 = strtol(s, &s, 10);
|
||
|
if(*s == ','){
|
||
|
s++;
|
||
|
*pto2 = strtol(s, &s, 10);
|
||
|
}else
|
||
|
*pto2 = *pfrom2;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
skiplines(Biobuf *b, char *name, int n)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for(i=0; i<n; i++){
|
||
|
while(Brdline(b, '\n')==nil){
|
||
|
if(Blinelen(b) <= 0)
|
||
|
sysfatal("early end of file on %s", name);
|
||
|
Bseek(b, Blinelen(b), 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n)
|
||
|
{
|
||
|
char buf[4096], *p;
|
||
|
int i, m;
|
||
|
|
||
|
for(i=0; i<n; i++){
|
||
|
while((p=Brdline(bin, '\n'))==nil){
|
||
|
if(Blinelen(bin) <= 0)
|
||
|
sysfatal("early end of file on %s", nin);
|
||
|
m = Blinelen(bin);
|
||
|
if(m > sizeof buf)
|
||
|
m = sizeof buf;
|
||
|
m = Bread(bin, buf, m);
|
||
|
if(Bwrite(bout, buf, m) != m)
|
||
|
sysfatal("error writing %s: %r", nout);
|
||
|
}
|
||
|
if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin))
|
||
|
sysfatal("error writing %s: %r", nout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout)
|
||
|
{
|
||
|
char buf[4096];
|
||
|
int m;
|
||
|
|
||
|
USED(nin);
|
||
|
while((m = Bread(bin, buf, sizeof buf)) > 0)
|
||
|
if(Bwrite(bout, buf, m) != m)
|
||
|
sysfatal("error writing %s: %r", nout);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout)
|
||
|
{
|
||
|
char buf[256], *p;
|
||
|
int interactive, defaultanswer, cmd, diffoffset;
|
||
|
int n, from1, to1, from2, to2, nf1, nf2;
|
||
|
Biobuf berr;
|
||
|
|
||
|
nf1 = 1;
|
||
|
nf2 = 1;
|
||
|
interactive = 1;
|
||
|
defaultanswer = 0;
|
||
|
Binit(&berr, 2, OWRITE);
|
||
|
while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){
|
||
|
p[Blinelen(bdiff)-1] = '\0';
|
||
|
parse(p, &from1, &to1, &cmd, &from2, &to2);
|
||
|
p[Blinelen(bdiff)-1] = '\n';
|
||
|
n = to1-from1 + to2-from2 + 1; /* #lines from diff */
|
||
|
if(cmd == 'c')
|
||
|
n += 2;
|
||
|
else if(cmd == 'a')
|
||
|
from1++;
|
||
|
else if(cmd == 'd')
|
||
|
from2++;
|
||
|
to1++; /* make half-open intervals */
|
||
|
to2++;
|
||
|
if(interactive){
|
||
|
p[Blinelen(bdiff)-1] = '\0';
|
||
|
fprint(2, "%s\n", p);
|
||
|
p[Blinelen(bdiff)-1] = '\n';
|
||
|
copylines(bdiff, namediff, &berr, "<stderr>", n);
|
||
|
Bflush(&berr);
|
||
|
}else
|
||
|
skiplines(bdiff, namediff, n);
|
||
|
do{
|
||
|
if(interactive){
|
||
|
fprint(2, "? ");
|
||
|
memset(buf, 0, sizeof buf);
|
||
|
if(read(0, buf, sizeof buf - 1) < 0)
|
||
|
sysfatal("read console: %r");
|
||
|
}else
|
||
|
buf[0] = defaultanswer;
|
||
|
|
||
|
switch(buf[0]){
|
||
|
case '>':
|
||
|
copylines(b1, name1, bout, nameout, from1-nf1);
|
||
|
skiplines(b1, name1, to1-from1);
|
||
|
skiplines(b2, name2, from2-nf2);
|
||
|
copylines(b2, name2, bout, nameout, to2-from2);
|
||
|
break;
|
||
|
case '<':
|
||
|
copylines(b1, name1, bout, nameout, to1-nf1);
|
||
|
skiplines(b2, name2, to2-nf2);
|
||
|
break;
|
||
|
case '=':
|
||
|
copylines(b1, name1, bout, nameout, from1-nf1);
|
||
|
skiplines(b1, name1, to1-from1);
|
||
|
skiplines(b2, name2, to2-nf2);
|
||
|
if(Bseek(bdiff, diffoffset, 0) != diffoffset)
|
||
|
sysfatal("seek in diff output: %r");
|
||
|
copylines(bdiff, namediff, bout, nameout, n+1);
|
||
|
break;
|
||
|
case '!':
|
||
|
runcmd(buf+1);
|
||
|
break;
|
||
|
case 'q':
|
||
|
if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){
|
||
|
interactive = 0;
|
||
|
defaultanswer = buf[1];
|
||
|
}else
|
||
|
fprint(2, "must be q<, q>, or q=\n");
|
||
|
break;
|
||
|
default:
|
||
|
fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n");
|
||
|
break;
|
||
|
}
|
||
|
}while(buf[0] != '<' && buf[0] != '>' && buf[0] != '=');
|
||
|
nf1 = to1;
|
||
|
nf2 = to2;
|
||
|
}
|
||
|
copy(b1, name1, bout, nameout);
|
||
|
}
|