| /* |
| * 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); |
| } |