|  | /* | 
|  | * 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*); | 
|  | 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); | 
|  | strcpy(idiffout, "/tmp/idiff.XXXXXX"); | 
|  | ofd = opentemp(idiffout, ORDWR|ORCLOSE); | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void | 
|  | rundiff(char *arg1, char *arg2, int outfd) | 
|  | { | 
|  | char *arg[10], *p; | 
|  | int narg, pid; | 
|  | Waitmsg *w; | 
|  |  | 
|  | narg = 0; | 
|  | arg[narg++] = "9"; | 
|  | arg[narg++] = "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("9", 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++] = "rc"; | 
|  | arg[narg++] = "-c"; | 
|  | arg[narg++] = cmd; | 
|  | arg[narg] = nil; | 
|  |  | 
|  | switch(pid = fork()){ | 
|  | case -1: | 
|  | sysfatal("fork: %r"); | 
|  |  | 
|  | case 0: | 
|  | exec("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); | 
|  | } |