xref: /plan9-contrib/sys/src/cmd/diff/main.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "diff.h"
5 
6 #define	DIRECTORY(s)		((s).qid.path&CHDIR)
7 #define	REGULAR_FILE(s)		((s).type == 'M' && !DIRECTORY(s))
8 
9 Biobuf	stdout;
10 
11 static char *tmp[] = {"/tmp/diff1", "/tmp/diff2"};
12 static int whichtmp;
13 static char *progname;
14 static char usage[] = "diff [ -efmnbwr ] file1 ... file2\n";
15 
16 static void
17 rmtmpfiles(void)
18 {
19 	while (whichtmp > 0) {
20 		whichtmp--;
21 		remove(tmp[whichtmp]);
22 	}
23 }
24 
25 void
26 done(int status)
27 {
28 	rmtmpfiles();
29 	switch(status)
30 	{
31 	case 0:
32 		exits("");
33 	case 1:
34 		exits("some");
35 	default:
36 		exits("error");
37 	}
38 	/*NOTREACHED*/
39 }
40 
41 void
42 panic(int status, char *fmt, ...)
43 {
44 	va_list arg;
45 	char buf[1024], *out;
46 
47 	Bflush(&stdout);
48 
49 	out = buf+snprint(buf, sizeof(buf), "%s: ", progname);
50 	va_start(arg, fmt);
51 	out = doprint(out, buf+sizeof(buf), fmt, arg);
52 	va_end(arg);
53 	write(2, buf, out-buf);
54 	if (status)
55 		done(status);
56 		/*NOTREACHED*/
57 }
58 
59 static int
60 catch(void *a, char *msg)
61 {
62 	USED(a);
63 	panic(2, msg);
64 	return 1;
65 }
66 
67 int
68 mkpathname(char *pathname, char *path, char *name)
69 {
70 	if (strlen(path) + strlen(name) > MAXPATHLEN) {
71 		panic(0, "pathname %s/%s too long\n", path, name);
72 		return 1;
73 	}
74 	sprint(pathname, "%s/%s", path, name);
75 	return 0;
76 }
77 
78 static char *
79 mktmpfile(int input, Dir *sb)
80 {
81 	int fd, i;
82 	char *p;
83 	char buf[8192];
84 
85 	atnotify(catch, 1);
86 	p = tmp[whichtmp++];
87 	fd = create(p, OWRITE, 0600);
88 	if (fd < 0) {
89 		panic(mflag ? 0: 2, "cannot create %s: %r\n", p);
90 		return 0;
91 	}
92 	while ((i = read(input, buf, sizeof(buf))) > 0) {
93 		if ((i = write(fd, buf, i)) < 0)
94 			break;
95 	}
96 	dirfstat(fd, sb);
97 	close(fd);
98 	if (i < 0) {
99 		panic(mflag ? 0: 2, "cannot read/write %s: %r\n", p);
100 		return 0;
101 	}
102 	return p;
103 }
104 
105 static char *
106 statfile(char *file, Dir *sb)
107 {
108 	int input;
109 
110 	if (dirstat(file, sb) == -1) {
111 		if (strcmp(file, "-") || dirfstat(0, sb) == -1) {
112 			panic(mflag ? 0: 2, "cannot stat %s: %r\n", file);
113 			return 0;
114 		}
115 		file = mktmpfile(0, sb);
116 	}
117 	else if (!REGULAR_FILE(*sb) && !DIRECTORY(*sb)) {
118 		if ((input = open(file, OREAD)) == -1) {
119 			panic(mflag ? 0: 2, "cannot open %s: %r\n", file);
120 			return 0;
121 		}
122 		file = mktmpfile(input, sb);
123 		close(input);
124 	}
125 	return file;
126 }
127 
128 void
129 diff(char *f, char *t, int level)
130 {
131 	char *fp, *tp, *p, fb[MAXPATHLEN+1], tb[MAXPATHLEN+1];
132 	Dir fsb, tsb;
133 
134 	if ((fp = statfile(f, &fsb)) == 0)
135 		return;
136 	if ((tp = statfile(t, &tsb)) == 0)
137 		return;
138 	if (DIRECTORY(fsb) && DIRECTORY(tsb)) {
139 		if (rflag || level == 0)
140 			diffdir(fp, tp, level);
141 		else
142 			Bprint(&stdout, "Common subdirectories: %s and %s\n",
143 				fp, tp);
144 	}
145 	else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb))
146 		diffreg(fp, tp);
147 	else {
148 		if (REGULAR_FILE(fsb)) {
149 			if ((p = utfrrune(f, '/')) == 0)
150 				p = f;
151 			else
152 				p++;
153 			if (mkpathname(tb, tp, p))
154 				return;
155 			diffreg(fp, tb);
156 		}
157 		else {
158 			if ((p = utfrrune(t, '/')) == 0)
159 				p = t;
160 			else
161 				p++;
162 			if (mkpathname(fb, fp, p))
163 				return;
164 			diffreg(fb, tp);
165 		}
166 	}
167 }
168 
169 void
170 main(int argc, char *argv[])
171 {
172 	char *p;
173 	int i;
174 	Dir fsb, tsb;
175 
176 	Binit(&stdout, 1, OWRITE);
177 	progname = *argv;
178 	while (--argc && (*++argv)[0] == '-' && (*argv)[1]) {
179 		for (p = *argv+1; *p; p++) {
180 			switch (*p) {
181 
182 			case 'e':
183 			case 'f':
184 			case 'n':
185 				mode = *p;
186 				break;
187 
188 			case 'w':
189 				bflag = 2;
190 				break;
191 
192 			case 'b':
193 				bflag = 1;
194 				break;
195 
196 			case 'r':
197 				rflag = 1;
198 				break;
199 
200 			case 'm':
201 				mflag = 1;
202 				break;
203 
204 			case 'h':
205 			default:
206 				progname = "Usage";
207 				panic(2, usage);
208 			}
209 		}
210 	}
211 	if (argc < 2)
212 		panic(2, usage, progname);
213 	if (dirstat(argv[argc-1], &tsb) == -1)
214 		panic(2, "can't stat %s\n", argv[argc-1]);
215 	if (argc > 2) {
216 		if (!DIRECTORY(tsb))
217 			panic(2, usage, progname);
218 		mflag = 1;
219 	}
220 	else {
221 		if (dirstat(argv[0], &fsb) == -1)
222 			panic(2, "can't stat %s\n", argv[0]);
223 		if (DIRECTORY(fsb) && DIRECTORY(tsb))
224 			mflag = 1;
225 	}
226 	for (i = 0; i < argc-1; i++) {
227 		diff(argv[i], argv[argc-1], 0);
228 		rmtmpfiles();
229 	}
230 	done(anychange);
231 	/*NOTREACHED*/
232 }
233 
234 static char noroom[] = "out of memory - try diff -h\n";
235 
236 void *
237 emalloc(unsigned n)
238 {
239 	register void *p;
240 
241 	if ((p = malloc(n)) == 0)
242 		panic(2, noroom);
243 	return p;
244 }
245 
246 void *
247 erealloc(void *p, unsigned n)
248 {
249 	register void *rp;
250 
251 	if ((rp = realloc(p, n)) == 0)
252 		panic(2, noroom);
253 	return rp;
254 }
255