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 void rundiff(char*, char*, int);
17 void
18 usage(void)
19 {
20 fprint(2, "usage: idiff [-bw] file1 file2\n");
21 exits("usage");
22 }
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;
32 ARGBEGIN{
33 default:
34 usage();
35 case 'b':
36 diffbflag++;
37 break;
38 case 'w':
39 diffwflag++;
40 break;
41 }ARGEND
43 if(argc != 2)
44 usage();
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);
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]);
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 }
80 void
81 rundiff(char *arg1, char *arg2, int outfd)
82 {
83 char *arg[10], *p;
84 int narg, pid;
85 Waitmsg *w;
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;
99 switch(pid = fork()){
100 case -1:
101 sysfatal("fork: %r");
103 case 0:
104 dup(outfd, 1);
105 close(0);
106 exec("9", arg);
107 sysfatal("exec: %r");
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);
121 void
122 runcmd(char *cmd)
124 char *arg[10];
125 int narg, pid, wpid;
127 narg = 0;
128 arg[narg++] = "rc";
129 arg[narg++] = "-c";
130 arg[narg++] = cmd;
131 arg[narg] = nil;
133 switch(pid = fork()){
134 case -1:
135 sysfatal("fork: %r");
137 case 0:
138 exec("rc", arg);
139 sysfatal("exec: %r");
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);
150 void
151 parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
153 *pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
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;
182 void
183 skiplines(Biobuf *b, char *name, int n)
185 int i;
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);
196 void
197 copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n)
199 char buf[4096], *p;
200 int i, m;
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);
213 if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin))
214 sysfatal("error writing %s: %r", nout);
218 void
219 copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout)
221 char buf[4096];
222 int m;
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);
230 void
231 idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout)
233 char buf[256], *p;
234 int interactive, defaultanswer, cmd, diffoffset;
235 int n, from1, to1, from2, to2, nf1, nf2;
236 Biobuf berr;
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;
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;
306 }while(buf[0] != '<' && buf[0] != '>' && buf[0] != '=');
307 nf1 = to1;
308 nf2 = to2;
310 copy(b1, name1, bout, nameout);