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