xref: /plan9/sys/src/cmd/diff/main.c (revision 56e2d73c64e38b03a72b5fdcfb50b0a26425b0f0)
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 [-abcefmnrw] 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 	p = mktemp(tmp[whichtmp++]);
85 	fd = create(p, OWRITE, 0600);
86 	if (fd < 0) {
87 		panic(mflag ? 0: 2, "cannot create %s: %r\n", p);
88 		return 0;
89 	}
90 	while ((i = read(input, buf, sizeof(buf))) > 0) {
91 		if ((i = write(fd, buf, i)) < 0)
92 			break;
93 	}
94 	*sb = dirfstat(fd);
95 	close(fd);
96 	if (i < 0) {
97 		panic(mflag ? 0: 2, "cannot read/write %s: %r\n", p);
98 		return 0;
99 	}
100 	return p;
101 }
102 
103 static char *
statfile(char * file,Dir ** sb)104 statfile(char *file, Dir **sb)
105 {
106 	Dir *dir;
107 	int input;
108 
109 	dir = dirstat(file);
110 	if(dir == nil) {
111 		if (strcmp(file, "-") || (dir = dirfstat(0)) == nil) {
112 			panic(mflag ? 0: 2, "cannot stat %s: %r\n", file);
113 			return 0;
114 		}
115 		free(dir);
116 		return mktmpfile(0, sb);
117 	}
118 	else if (!REGULAR_FILE(dir) && !DIRECTORY(dir)) {
119 		free(dir);
120 		if ((input = open(file, OREAD)) == -1) {
121 			panic(mflag ? 0: 2, "cannot open %s: %r\n", file);
122 			return 0;
123 		}
124 		file = mktmpfile(input, sb);
125 		close(input);
126 	}
127 	else
128 		*sb = dir;
129 	return file;
130 }
131 
132 void
diff(char * f,char * t,int level)133 diff(char *f, char *t, int level)
134 {
135 	char *fp, *tp, *p, fb[MAXPATHLEN+1], tb[MAXPATHLEN+1];
136 	Dir *fsb, *tsb;
137 
138 	if ((fp = statfile(f, &fsb)) == 0)
139 		goto Return;
140 	if ((tp = statfile(t, &tsb)) == 0){
141 		free(fsb);
142 		goto Return;
143 	}
144 	if (DIRECTORY(fsb) && DIRECTORY(tsb)) {
145 		if (rflag || level == 0)
146 			diffdir(fp, tp, level);
147 		else
148 			Bprint(&stdout, "Common subdirectories: %s and %s\n",
149 				fp, tp);
150 	}
151 	else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb))
152 		diffreg(fp, tp);
153 	else {
154 		if (REGULAR_FILE(fsb)) {
155 			if ((p = utfrrune(f, '/')) == 0)
156 				p = f;
157 			else
158 				p++;
159 			if (mkpathname(tb, tp, p) == 0)
160 				diffreg(fp, tb);
161 		}
162 		else {
163 			if ((p = utfrrune(t, '/')) == 0)
164 				p = t;
165 			else
166 				p++;
167 			if (mkpathname(fb, fp, p) == 0)
168 				diffreg(fb, tp);
169 		}
170 	}
171 	free(fsb);
172 	free(tsb);
173 Return:
174 	rmtmpfiles();
175 }
176 
177 void
main(int argc,char * argv[])178 main(int argc, char *argv[])
179 {
180 	char *p;
181 	int i;
182 	Dir *fsb, *tsb;
183 
184 	Binit(&stdout, 1, OWRITE);
185 	progname = argv0 = *argv;
186 	while (--argc && (*++argv)[0] == '-' && (*argv)[1]) {
187 		for (p = *argv+1; *p; p++) {
188 			switch (*p) {
189 
190 			case 'e':
191 			case 'f':
192 			case 'n':
193 			case 'c':
194 			case 'a':
195 				mode = *p;
196 				break;
197 
198 			case 'w':
199 				bflag = 2;
200 				break;
201 
202 			case 'b':
203 				bflag = 1;
204 				break;
205 
206 			case 'r':
207 				rflag = 1;
208 				break;
209 
210 			case 'm':
211 				mflag = 1;
212 				break;
213 
214 			case 'h':
215 			default:
216 				progname = "Usage";
217 				panic(2, usage);
218 			}
219 		}
220 	}
221 	if (argc < 2)
222 		panic(2, usage, progname);
223 	if ((tsb = dirstat(argv[argc-1])) == nil)
224 		panic(2, "can't stat %s\n", argv[argc-1]);
225 	if (argc > 2) {
226 		if (!DIRECTORY(tsb))
227 			panic(2, usage, progname);
228 		mflag = 1;
229 	}
230 	else {
231 		if ((fsb = dirstat(argv[0])) == nil)
232 			panic(2, "can't stat %s\n", argv[0]);
233 		if (DIRECTORY(fsb) && DIRECTORY(tsb))
234 			mflag = 1;
235 		free(fsb);
236 	}
237 	free(tsb);
238 	for (i = 0; i < argc-1; i++)
239 		diff(argv[i], argv[argc-1], 0);
240 	done(anychange);
241 	/*NOTREACHED*/
242 }
243 
244 static char noroom[] = "out of memory - try diff -h\n";
245 
246 void *
emalloc(unsigned n)247 emalloc(unsigned n)
248 {
249 	register void *p;
250 
251 	if ((p = malloc(n)) == 0)
252 		panic(2, noroom);
253 	return p;
254 }
255 
256 void *
erealloc(void * p,unsigned n)257 erealloc(void *p, unsigned n)
258 {
259 	register void *rp;
260 
261 	if ((rp = realloc(p, n)) == 0)
262 		panic(2, noroom);
263 	return rp;
264 }
265