Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}