idiff.c (6460B)
1 /* 2 * interactive diff, inspired/stolen from 3 * kernighan and pike, _unix programming environment_. 4 */ 5 6 #include <u.h> 7 #include <libc.h> 8 #include <bio.h> 9 10 int diffbflag; 11 int diffwflag; 12 13 void copy(Biobuf*, char*, Biobuf*, char*); 14 void idiff(Biobuf*, char*, Biobuf*, char*, Biobuf*, char*, Biobuf*, char*); 15 void rundiff(char*, char*, int); 16 17 void 18 usage(void) 19 { 20 fprint(2, "usage: idiff [-bw] file1 file2\n"); 21 exits("usage"); 22 } 23 24 void 25 main(int argc, char **argv) 26 { 27 int fd, ofd; 28 char diffout[40], idiffout[40]; 29 Biobuf *b1, *b2, bdiff, bout, bstdout; 30 Dir *d; 31 32 ARGBEGIN{ 33 default: 34 usage(); 35 case 'b': 36 diffbflag++; 37 break; 38 case 'w': 39 diffwflag++; 40 break; 41 }ARGEND 42 43 if(argc != 2) 44 usage(); 45 46 if((d = dirstat(argv[0])) == nil) 47 sysfatal("stat %s: %r", argv[0]); 48 if(d->mode&DMDIR) 49 sysfatal("%s is a directory", argv[0]); 50 free(d); 51 if((d = dirstat(argv[1])) == nil) 52 sysfatal("stat %s: %r", argv[1]); 53 if(d->mode&DMDIR) 54 sysfatal("%s is a directory", argv[1]); 55 free(d); 56 57 if((b1 = Bopen(argv[0], OREAD)) == nil) 58 sysfatal("open %s: %r", argv[0]); 59 if((b2 = Bopen(argv[1], OREAD)) == nil) 60 sysfatal("open %s: %r", argv[1]); 61 62 strcpy(diffout, "/tmp/idiff.XXXXXX"); 63 fd = opentemp(diffout, ORDWR|ORCLOSE); 64 strcpy(idiffout, "/tmp/idiff.XXXXXX"); 65 ofd = opentemp(idiffout, ORDWR|ORCLOSE); 66 rundiff(argv[0], argv[1], fd); 67 seek(fd, 0, 0); 68 Binit(&bdiff, fd, OREAD); 69 Binit(&bout, ofd, OWRITE); 70 idiff(b1, argv[0], b2, argv[1], &bdiff, diffout, &bout, idiffout); 71 Bterm(&bdiff); 72 Bflush(&bout); 73 seek(ofd, 0, 0); 74 Binit(&bout, ofd, OREAD); 75 Binit(&bstdout, 1, OWRITE); 76 copy(&bout, idiffout, &bstdout, "<stdout>"); 77 exits(nil); 78 } 79 80 void 81 rundiff(char *arg1, char *arg2, int outfd) 82 { 83 char *arg[10], *p; 84 int narg, pid; 85 Waitmsg *w; 86 87 narg = 0; 88 arg[narg++] = "9"; 89 arg[narg++] = "diff"; 90 arg[narg++] = "-n"; 91 if(diffbflag) 92 arg[narg++] = "-b"; 93 if(diffwflag) 94 arg[narg++] = "-w"; 95 arg[narg++] = arg1; 96 arg[narg++] = arg2; 97 arg[narg] = nil; 98 99 switch(pid = fork()){ 100 case -1: 101 sysfatal("fork: %r"); 102 103 case 0: 104 dup(outfd, 1); 105 close(0); 106 exec("9", arg); 107 sysfatal("exec: %r"); 108 109 default: 110 w = wait(); 111 if(w==nil) 112 sysfatal("wait: %r"); 113 if(w->pid != pid) 114 sysfatal("wait got unexpected pid %d", w->pid); 115 if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0) 116 sysfatal("%s", w->msg); 117 free(w); 118 } 119 } 120 121 void 122 runcmd(char *cmd) 123 { 124 char *arg[10]; 125 int narg, pid, wpid; 126 127 narg = 0; 128 arg[narg++] = "rc"; 129 arg[narg++] = "-c"; 130 arg[narg++] = cmd; 131 arg[narg] = nil; 132 133 switch(pid = fork()){ 134 case -1: 135 sysfatal("fork: %r"); 136 137 case 0: 138 exec("rc", arg); 139 sysfatal("exec: %r"); 140 141 default: 142 wpid = waitpid(); 143 if(wpid < 0) 144 sysfatal("wait: %r"); 145 if(wpid != pid) 146 sysfatal("wait got unexpected pid %d", wpid); 147 } 148 } 149 150 void 151 parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2) 152 { 153 *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0; 154 155 s = strchr(s, ':'); 156 if(s == nil) 157 sysfatal("bad diff output0"); 158 s++; 159 *pfrom1 = strtol(s, &s, 10); 160 if(*s == ','){ 161 s++; 162 *pto1 = strtol(s, &s, 10); 163 }else 164 *pto1 = *pfrom1; 165 if(*s++ != ' ') 166 sysfatal("bad diff output1"); 167 *pcmd = *s++; 168 if(*s++ != ' ') 169 sysfatal("bad diff output2"); 170 s = strchr(s, ':'); 171 if(s == nil) 172 sysfatal("bad diff output3"); 173 s++; 174 *pfrom2 = strtol(s, &s, 10); 175 if(*s == ','){ 176 s++; 177 *pto2 = strtol(s, &s, 10); 178 }else 179 *pto2 = *pfrom2; 180 } 181 182 void 183 skiplines(Biobuf *b, char *name, int n) 184 { 185 int i; 186 187 for(i=0; i<n; i++){ 188 while(Brdline(b, '\n')==nil){ 189 if(Blinelen(b) <= 0) 190 sysfatal("early end of file on %s", name); 191 Bseek(b, Blinelen(b), 1); 192 } 193 } 194 } 195 196 void 197 copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n) 198 { 199 char buf[4096], *p; 200 int i, m; 201 202 for(i=0; i<n; i++){ 203 while((p=Brdline(bin, '\n'))==nil){ 204 if(Blinelen(bin) <= 0) 205 sysfatal("early end of file on %s", nin); 206 m = Blinelen(bin); 207 if(m > sizeof buf) 208 m = sizeof buf; 209 m = Bread(bin, buf, m); 210 if(Bwrite(bout, buf, m) != m) 211 sysfatal("error writing %s: %r", nout); 212 } 213 if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin)) 214 sysfatal("error writing %s: %r", nout); 215 } 216 } 217 218 void 219 copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout) 220 { 221 char buf[4096]; 222 int m; 223 224 USED(nin); 225 while((m = Bread(bin, buf, sizeof buf)) > 0) 226 if(Bwrite(bout, buf, m) != m) 227 sysfatal("error writing %s: %r", nout); 228 } 229 230 void 231 idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout) 232 { 233 char buf[256], *p; 234 int interactive, defaultanswer, cmd, diffoffset; 235 int n, from1, to1, from2, to2, nf1, nf2; 236 Biobuf berr; 237 238 nf1 = 1; 239 nf2 = 1; 240 interactive = 1; 241 defaultanswer = 0; 242 Binit(&berr, 2, OWRITE); 243 while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){ 244 p[Blinelen(bdiff)-1] = '\0'; 245 parse(p, &from1, &to1, &cmd, &from2, &to2); 246 p[Blinelen(bdiff)-1] = '\n'; 247 n = to1-from1 + to2-from2 + 1; /* #lines from diff */ 248 if(cmd == 'c') 249 n += 2; 250 else if(cmd == 'a') 251 from1++; 252 else if(cmd == 'd') 253 from2++; 254 to1++; /* make half-open intervals */ 255 to2++; 256 if(interactive){ 257 p[Blinelen(bdiff)-1] = '\0'; 258 fprint(2, "%s\n", p); 259 p[Blinelen(bdiff)-1] = '\n'; 260 copylines(bdiff, namediff, &berr, "<stderr>", n); 261 Bflush(&berr); 262 }else 263 skiplines(bdiff, namediff, n); 264 do{ 265 if(interactive){ 266 fprint(2, "? "); 267 memset(buf, 0, sizeof buf); 268 if(read(0, buf, sizeof buf - 1) < 0) 269 sysfatal("read console: %r"); 270 }else 271 buf[0] = defaultanswer; 272 273 switch(buf[0]){ 274 case '>': 275 copylines(b1, name1, bout, nameout, from1-nf1); 276 skiplines(b1, name1, to1-from1); 277 skiplines(b2, name2, from2-nf2); 278 copylines(b2, name2, bout, nameout, to2-from2); 279 break; 280 case '<': 281 copylines(b1, name1, bout, nameout, to1-nf1); 282 skiplines(b2, name2, to2-nf2); 283 break; 284 case '=': 285 copylines(b1, name1, bout, nameout, from1-nf1); 286 skiplines(b1, name1, to1-from1); 287 skiplines(b2, name2, to2-nf2); 288 if(Bseek(bdiff, diffoffset, 0) != diffoffset) 289 sysfatal("seek in diff output: %r"); 290 copylines(bdiff, namediff, bout, nameout, n+1); 291 break; 292 case '!': 293 runcmd(buf+1); 294 break; 295 case 'q': 296 if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){ 297 interactive = 0; 298 defaultanswer = buf[1]; 299 }else 300 fprint(2, "must be q<, q>, or q=\n"); 301 break; 302 default: 303 fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n"); 304 break; 305 } 306 }while(buf[0] != '<' && buf[0] != '>' && buf[0] != '='); 307 nf1 = to1; 308 nf2 = to2; 309 } 310 copy(b1, name1, bout, nameout); 311 }