]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/idiff.c
mothra: fix alt display resizing, filter control characters in panel entries, use...
[plan9front.git] / sys / src / cmd / idiff.c
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
19 usage(void)
20 {
21         fprint(2, "usage: idiff [-bw] file1 file2\n");
22         exits("usage");
23 }
24
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;
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
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
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
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
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
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
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
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
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 }