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 int diffbflag;
11 int diffwflag;
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);
18 void
19 usage(void)
20 {
21 fprint(2, "usage: idiff [-bw] file1 file2\n");
22 exits("usage");
23 }
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;
33 ARGBEGIN{
34 default:
35 usage();
36 case 'b':
37 diffbflag++;
38 break;
39 case 'w':
40 diffwflag++;
41 break;
42 }ARGEND
44 if(argc != 2)
45 usage();
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);
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]);
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 }
81 int
82 opentemp(char *template, int mode, long perm)
83 {
84 int fd;
85 Dir d;
87 fd = mkstemp(template);
88 if(fd < 0)
89 sysfatal("could not create temporary file");
90 nulldir(&d);
91 d.mode = perm;
92 dirfwstat(fd, &d);
94 return fd;
95 }
97 void
98 rundiff(char *arg1, char *arg2, int outfd)
99 {
100 char *arg[10], *p;
101 int narg, pid;
102 Waitmsg *w;
104 narg = 0;
105 arg[narg++] = "/bin/diff";
106 arg[narg++] = "-n";
107 if(diffbflag)
108 arg[narg++] = "-b";
109 if(diffwflag)
110 arg[narg++] = "-w";
111 arg[narg++] = arg1;
112 arg[narg++] = arg2;
113 arg[narg] = nil;
115 switch(pid = fork()){
116 case -1:
117 sysfatal("fork: %r");
119 case 0:
120 dup(outfd, 1);
121 close(0);
122 exec("/bin/diff", arg);
123 sysfatal("exec: %r");
125 default:
126 w = wait();
127 if(w==nil)
128 sysfatal("wait: %r");
129 if(w->pid != pid)
130 sysfatal("wait got unexpected pid %d", w->pid);
131 if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0)
132 sysfatal("%s", w->msg);
133 free(w);
137 void
138 runcmd(char *cmd)
140 char *arg[10];
141 int narg, pid, wpid;
143 narg = 0;
144 arg[narg++] = "/bin/rc";
145 arg[narg++] = "-c";
146 arg[narg++] = cmd;
147 arg[narg] = nil;
149 switch(pid = fork()){
150 case -1:
151 sysfatal("fork: %r");
153 case 0:
154 exec("/bin/rc", arg);
155 sysfatal("exec: %r");
157 default:
158 wpid = waitpid();
159 if(wpid < 0)
160 sysfatal("wait: %r");
161 if(wpid != pid)
162 sysfatal("wait got unexpected pid %d", wpid);
166 void
167 parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
169 *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
171 s = strchr(s, ':');
172 if(s == nil)
173 sysfatal("bad diff output0");
174 s++;
175 *pfrom1 = strtol(s, &s, 10);
176 if(*s == ','){
177 s++;
178 *pto1 = strtol(s, &s, 10);
179 }else
180 *pto1 = *pfrom1;
181 if(*s++ != ' ')
182 sysfatal("bad diff output1");
183 *pcmd = *s++;
184 if(*s++ != ' ')
185 sysfatal("bad diff output2");
186 s = strchr(s, ':');
187 if(s == nil)
188 sysfatal("bad diff output3");
189 s++;
190 *pfrom2 = strtol(s, &s, 10);
191 if(*s == ','){
192 s++;
193 *pto2 = strtol(s, &s, 10);
194 }else
195 *pto2 = *pfrom2;
198 void
199 skiplines(Biobuf *b, char *name, int n)
201 int i;
203 for(i=0; i<n; i++){
204 while(Brdline(b, '\n')==nil){
205 if(Blinelen(b) <= 0)
206 sysfatal("early end of file on %s", name);
207 Bseek(b, Blinelen(b), 1);
212 void
213 copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n)
215 char buf[4096], *p;
216 int i, m;
218 for(i=0; i<n; i++){
219 while((p=Brdline(bin, '\n'))==nil){
220 if(Blinelen(bin) <= 0)
221 sysfatal("early end of file on %s", nin);
222 m = Blinelen(bin);
223 if(m > sizeof buf)
224 m = sizeof buf;
225 m = Bread(bin, buf, m);
226 if(Bwrite(bout, buf, m) != m)
227 sysfatal("error writing %s: %r", nout);
229 if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin))
230 sysfatal("error writing %s: %r", nout);
234 void
235 copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout)
237 char buf[4096];
238 int m;
240 USED(nin);
241 while((m = Bread(bin, buf, sizeof buf)) > 0)
242 if(Bwrite(bout, buf, m) != m)
243 sysfatal("error writing %s: %r", nout);
246 void
247 idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout)
249 char buf[256], *p;
250 int interactive, defaultanswer, cmd, diffoffset;
251 int n, from1, to1, from2, to2, nf1, nf2;
252 Biobuf berr;
254 nf1 = 1;
255 nf2 = 1;
256 interactive = 1;
257 defaultanswer = 0;
258 Binit(&berr, 2, OWRITE);
259 while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){
260 p[Blinelen(bdiff)-1] = '\0';
261 parse(p, &from1, &to1, &cmd, &from2, &to2);
262 p[Blinelen(bdiff)-1] = '\n';
263 n = to1-from1 + to2-from2 + 1; /* #lines from diff */
264 if(cmd == 'c')
265 n += 2;
266 else if(cmd == 'a')
267 from1++;
268 else if(cmd == 'd')
269 from2++;
270 to1++; /* make half-open intervals */
271 to2++;
272 if(interactive){
273 p[Blinelen(bdiff)-1] = '\0';
274 fprint(2, "%s\n", p);
275 p[Blinelen(bdiff)-1] = '\n';
276 copylines(bdiff, namediff, &berr, "<stderr>", n);
277 Bflush(&berr);
278 }else
279 skiplines(bdiff, namediff, n);
280 do{
281 if(interactive){
282 fprint(2, "? ");
283 memset(buf, 0, sizeof buf);
284 if(read(0, buf, sizeof buf - 1) < 0)
285 sysfatal("read console: %r");
286 }else
287 buf[0] = defaultanswer;
289 switch(buf[0]){
290 case '>':
291 copylines(b1, name1, bout, nameout, from1-nf1);
292 skiplines(b1, name1, to1-from1);
293 skiplines(b2, name2, from2-nf2);
294 copylines(b2, name2, bout, nameout, to2-from2);
295 break;
296 case '<':
297 copylines(b1, name1, bout, nameout, to1-nf1);
298 skiplines(b2, name2, to2-nf2);
299 break;
300 case '=':
301 copylines(b1, name1, bout, nameout, from1-nf1);
302 skiplines(b1, name1, to1-from1);
303 skiplines(b2, name2, to2-nf2);
304 if(Bseek(bdiff, diffoffset, 0) != diffoffset)
305 sysfatal("seek in diff output: %r");
306 copylines(bdiff, namediff, bout, nameout, n+1);
307 break;
308 case '!':
309 runcmd(buf+1);
310 break;
311 case 'q':
312 if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){
313 interactive = 0;
314 defaultanswer = buf[1];
315 }else
316 fprint(2, "must be q<, q>, or q=\n");
317 break;
318 default:
319 fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n");
320 break;
322 }while(buf[0] != '<' && buf[0] != '>' && buf[0] != '=');
323 nf1 = to1;
324 nf2 = to2;
326 copy(b1, name1, bout, nameout);