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