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
usage(void)19 usage(void)
20 {
21 fprint(2, "usage: idiff [-bw] file1 file2\n");
22 exits("usage");
23 }
24
25 void
main(int argc,char ** argv)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
opentemp(char * template,int mode,long perm)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
rundiff(char * arg1,char * arg2,int outfd)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
runcmd(char * cmd)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
parse(char * s,int * pfrom1,int * pto1,int * pcmd,int * pfrom2,int * pto2)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
skiplines(Biobuf * b,char * name,int n)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
copylines(Biobuf * bin,char * nin,Biobuf * bout,char * nout,int n)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
copy(Biobuf * bin,char * nin,Biobuf * bout,char * nout)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
idiff(Biobuf * b1,char * name1,Biobuf * b2,char * name2,Biobuf * bdiff,char * namediff,Biobuf * bout,char * nameout)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