]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/derp.c
Add exponential function.
[plan9front.git] / sys / src / cmd / derp.c
1 #include <u.h>
2 #include <libc.h>
3
4 int     permcheck = 0;
5 int     usercheck = 0;
6 int     dumpcheck = 1;
7 int     sizecheck = 1;
8 int     timecheck = 0;
9
10 int     quiet = 0;
11 int     errors = 0;
12 int     noerror = 0;
13
14 void
15 error(char *fmt, ...)
16 {
17         char buf[ERRMAX];
18         va_list a;
19
20         errors++;
21         if(!quiet){
22                 va_start(a, fmt);
23                 vsnprint(buf, sizeof(buf), fmt, a);
24                 va_end(a);
25
26                 fprint(2, "%s: %s\n", argv0, buf);
27         }
28         if(!noerror)
29                 exits("errors");
30 }
31
32 #pragma varargck        argpos  error   1
33
34 enum {
35         BUFSIZE = 8*1024,
36 };
37
38 int
39 cmpfile(char *a, char *b)
40 {
41         static uchar buf1[BUFSIZE], buf2[BUFSIZE];
42         int r, n, m, fd1, fd2;
43         
44         if((fd1 = open(a, OREAD)) < 0)
45                 error("can't open %s: %r", a);
46         if((fd2 = open(b, OREAD)) < 0)
47                 error("can't open %s: %r", b);
48
49         r = fd1 != fd2;
50         if(fd1 >= 0 && fd2 >= 0)
51                 do{
52                         if((n = readn(fd1, buf1, sizeof(buf1))) < 0){
53                                 error("can't read %s: %r", a);
54                                 break;
55                         }
56                         m = n;
57                         if(m == 0)
58                                 m++;    /* read past eof to verify size */
59                         if((m = readn(fd2, buf2, m)) < 0){
60                                 error("can't read %s: %r", b);
61                                 break;
62                         }
63                         if(m != n)
64                                 break;
65                         if(n == 0){
66                                 r = 0;
67                                 break;
68                         }
69                 } while(memcmp(buf1, buf2, n) == 0);
70
71         if(fd1 >= 0)
72                 close(fd1);
73         if(fd2 >= 0)
74                 close(fd2);
75
76         return r;
77 }
78
79 int
80 samefile(Dir *a, Dir *b)
81 {
82         if(a == b)
83                 return 1;
84
85         if(a->type == b->type && a->dev == b->dev &&
86                 a->qid.type == b->qid.type &&
87                 a->qid.path == b->qid.path &&
88                 a->qid.vers == b->qid.vers){
89
90                 if((a->qid.type & QTDIR) == 0)
91                         return 1;
92
93                 /*
94                  * directories in /n/dump have the same qid, but
95                  * atime can be used to skip potentially
96                  * untouched subtrees.
97                  */
98                 if(dumpcheck && a->atime == b->atime)
99                         return 1;
100         }
101
102         return 0;
103 }
104
105 int
106 dcmp(Dir *a, Dir *b)
107 {
108         if(a == nil || b == nil)
109                 return a != b;
110
111         if(samefile(a, b))
112                 return 0;
113
114         if((a->qid.type | b->qid.type) & QTDIR)
115                 return 1;
116
117         if((a->mode ^ b->mode) & permcheck)
118                 return 1;
119
120         if(usercheck){
121                 if(strcmp(a->uid, b->uid) != 0)
122                         return 1;
123                 if(strcmp(a->gid, b->gid) != 0)
124                         return 1;
125         }
126
127         if(sizecheck)
128                 if(a->length != b->length)
129                         return 1;
130
131         if(timecheck)
132                 if(a->mtime != b->mtime)
133                         return 1;
134
135         if(sizecheck && timecheck)
136                 return 0;
137
138         return cmpfile(a->name, b->name);
139 }
140
141 Dir*
142 statdir(char *path)
143 {
144         Dir *d;
145
146         d = dirstat(path);
147         if(d == nil)
148                 error("can't stat %s: %r", path);
149         else
150                 d->name = strdup(path);
151         return d;
152 }
153
154 Dir*
155 absdir(Dir *d, char *path)
156 {
157         if(d != nil)
158                 d->name = smprint("%s/%s", path, d->name);
159         return d;
160 }
161
162 void
163 cleardir(Dir *d)
164 {
165         if(d != nil){
166                 free(d->name);
167                 d->name = "";
168         }
169 }
170
171 void
172 freedir(Dir *d)
173 {
174         cleardir(d);
175         free(d);
176 }
177
178 int
179 dnamecmp(void *a, void *b)
180 {
181         return strcmp(((Dir*)a)->name, ((Dir*)b)->name);
182 }
183
184 int
185 readifdir(Dir **dp)
186 {
187         int n, fd;
188         Dir *d;
189
190         d = *dp;
191         *dp = nil;
192         if(d == nil || (d->qid.type & QTDIR) == 0)
193                 return 0;
194         fd = open(d->name, OREAD);
195         if(fd < 0){
196                 error("can't open %s: %r", d->name);
197                 return -1;
198         }
199         if((n = dirreadall(fd, dp)) < 0)
200                 error("can't read %s: %r", d->name);
201         close(fd);
202         if(n > 1)
203                 qsort(*dp, n, sizeof(Dir), dnamecmp);
204         return n;
205 }
206
207 void
208 diffgen(Dir *ld, Dir *rd, Dir *ad, char *path);
209
210 void
211 diffdir(Dir *ld, Dir *rd, Dir *ad, char *path)
212 {
213         int n, m, o, i, j, k, t, v;
214         char *sp, *lp, *rp, *ap;
215         Dir *od;
216
217         lp = rp = ap = nil;
218         if(ld != nil)
219                 lp = ld->name;
220         if(rd != nil)
221                 rp = rd->name;
222         if(ad != nil){
223                 /* check if ad is the same as ld or rd */
224                 if(ld != nil && samefile(ad, ld)){
225                         ap = ld->name;
226                         ad = nil;       /* don't read directory twice */
227                 }
228                 else if(rd != nil && samefile(ad, rd)){
229                         ap = rd->name;
230                         ad = nil;       /* don't read directory twice */
231                 }
232                 else
233                         ap = ad->name;
234         }
235
236         n = readifdir(&ld);
237         m = readifdir(&rd);
238         if(n <= 0 && m <= 0)
239                 return;
240
241         /* at least one side is directory */
242         o = readifdir(&ad);
243
244         i = j = k = 0;
245         for(;;){
246                 if(i < n)
247                         t = (j < m) ? strcmp(ld[i].name, rd[j].name) : -1;
248                 else if(j < m)
249                         t = 1;
250                 else
251                         break;
252
253                 od = nil;
254                 if(t < 0){
255                         sp = smprint("%s/%s", path, ld[i].name);
256                         if(ap == lp)
257                                 od = &ld[i];
258                         else while(k < o){
259                                 v = strcmp(ad[k].name, ld[i].name);
260                                 if(v == 0){
261                                         od = absdir(&ad[k++], ap);
262                                         break;
263                                 } else if(v > 0)
264                                         break;
265                                 k++;
266                         }
267                         diffgen(absdir(&ld[i], lp), nil, od, sp);
268                         cleardir(&ld[i]);
269                         if(&ld[i] == od)
270                                 od = nil;
271                         i++;
272                 } else {
273                         sp = smprint("%s/%s", path, rd[j].name);
274                         if(ap == rp)
275                                 od = &rd[j];
276                         else while(k < o){
277                                 v = strcmp(ad[k].name, rd[j].name);
278                                 if(v == 0){
279                                         od = absdir(&ad[k++], ap);
280                                         break;
281                                 } else if(v > 0)
282                                         break;
283                                 k++;
284                         }
285                         if(t > 0)
286                                 diffgen(nil, absdir(&rd[j], rp), od, sp);
287                         else {
288                                 if(ap == lp)
289                                         od = &ld[i];
290                                 diffgen(absdir(&ld[i], lp), absdir(&rd[j], rp), od, sp);
291                                 cleardir(&ld[i]);
292                                 if(&ld[i] == od)
293                                         od = nil;
294                                 i++;
295                         }
296                         cleardir(&rd[j]);
297                         if(&rd[j] == od)
298                                 od = nil;
299                         j++;
300                 }
301                 cleardir(od);
302                 free(sp);
303         }
304
305         free(ld);
306         free(rd);
307         free(ad);
308 }
309
310 void
311 diffgen(Dir *ld, Dir *rd, Dir *ad, char *path)
312 {
313         char *p;
314
315         if(dcmp(ld, rd) == 0)
316                 return;
317
318         p = nil;
319         if(ld == nil || rd == nil){
320                 /* one side doesnt exit anymore */
321                 if(ad != nil){
322                         /* existed before, is deletion */
323                         if(ld != nil && (ad->qid.type & QTDIR) && (ld->qid.type & QTDIR)){
324                                 /* remote deleted direcotry, remote newer */
325                                 p = smprint("nd\t%s\n", path);
326                         } else if(rd != nil && (ad->qid.type & QTDIR) && (rd->qid.type & QTDIR)){
327                                 /* local deleted direcotry, local newer */
328                                 p = smprint("dn\t%s\n", path);
329                         } else if(dcmp(rd, ad) == 0){
330                                 /* local deleted file, local newer */
331                                 print("dn\t%s\n", path);
332                         } else if(dcmp(ld, ad) == 0){
333                                 /* remote deleted file, remote newer */
334                                 print("nd\t%s\n", path);
335                         } else if(ld != nil){
336                                 /* local modified, remote deleted, conflict */
337                                 print("md!\t%s\n", path);
338                         } else {
339                                 /* remote modified, local deleted, conflict */
340                                 print("dm!\t%s\n", path);
341                         }
342                 } else {
343                         /* didnt exist before, is addition */
344                         if(ld != nil){
345                                 /* local added file, local newer */
346                                 print("an\t%s\n", path);
347                         } else {
348                                 /* remote added file, remote newer */
349                                 print("na\t%s\n", path);
350                         }
351                 }
352         } else {
353                 if(ad != nil){
354                         /* existed before, is modification */
355                         if((ad->qid.type & QTDIR) && (ld->qid.type & QTDIR) && (rd->qid.type & QTDIR)){
356                                 /* all still directories, no problem */
357                         } else if(dcmp(rd, ad) == 0){
358                                 /* local modified file, local newer */
359                                 print("mn\t%s\n", path);
360                         } else if(dcmp(ld, ad) == 0){
361                                 /* remote modified file, remote newer */
362                                 print("nm\t%s\n", path);
363                         } else {
364                                 /* local and remote modified, conflict */
365                                 print("mm!\t%s\n", path);
366                         }
367                 } else {
368                         /* didnt exist before, is addition from both */
369                         if((ld->qid.type & QTDIR) && (rd->qid.type & QTDIR)){
370                                 /* local and remote added directories, no problem */
371                         } else {
372                                 /* local and remote added files, conflict */
373                                 print("aa!\t%s\n", path);
374                         }
375                 }
376         }
377
378         diffdir(ld, rd, ad, path);
379
380         if(p != nil){
381                 print("%s", p);
382                 free(p);
383         }
384 }
385
386 void
387 diff3(char *lp, char *ap, char *rp)
388 {
389         Dir *ld, *rd, *ad;
390         char *name;
391
392         rd = ad = nil;
393         if((ld = statdir(lp)) == nil)
394                 goto Out;
395         if((rd = statdir(rp)) == nil)
396                 goto Out;
397
398         if(strcmp(ap, lp) == 0)
399                 ad = ld;
400         else if(strcmp(ap, rp) == 0)
401                 ad = rd;
402         else if((ad = statdir(ap)) == nil)
403                 goto Out;
404         else if(samefile(ad, ld)){
405                 freedir(ad);
406                 ad = ld;
407         }
408         else if(samefile(ad, rd)){
409                 freedir(ad);
410                 ad = rd;
411         }
412
413         if(ld->qid.type & QTDIR)
414                 name = ".";
415         else {
416                 if(name = strrchr(lp, '/'))
417                         name++;
418                 else
419                         name = lp;
420         }
421         diffgen(ld, rd, ad, name);
422 Out:
423         freedir(ld);
424         freedir(rd);
425         if(ad != nil && ad != ld && ad != rd)
426                 freedir(ad);
427 }
428
429 void
430 usage(void)
431 {
432         fprint(2, "usage: %s [ -qcutDL ] [ -p perms ] myfile oldfile yourfile\n", argv0);
433         exits("usage");
434 }
435
436 void
437 main(int argc, char *argv[])
438 {
439         ARGBEGIN {
440         case 'q':
441                 quiet = 1;
442                 break;
443         case 'c':
444                 noerror = 1;
445                 break;
446         case 'u':
447                 usercheck = 1;
448                 break; 
449         case 't':
450                 timecheck = 1;
451                 break;
452         case 'D':
453                 dumpcheck = 0;
454                 break;
455         case 'L':
456                 sizecheck = 0;
457                 break;
458         case 'p':
459                 permcheck = strtol(EARGF(usage()), nil, 8) & 0777;
460                 break;
461         default:
462                 usage();
463         } ARGEND;
464
465         if(argc != 3)
466                 usage();
467
468         diff3(argv[0], argv[1], argv[2]);
469
470         if(errors)
471                 exits("errors");
472
473         exits(0);
474 }