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