Blob


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