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