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