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