23 vsnprint(buf, sizeof(buf), fmt, a);
26 fprint(2, "%s: %s\n", argv0, buf);
32 #pragma varargck argpos error 1
39 if((v = malloc(n)) == nil){
41 error("out of memory");
51 cmpfile(char *a, char *b)
53 static uchar buf1[BUFSIZE], buf2[BUFSIZE];
54 int r, n, m, fd1, fd2;
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);
62 if(fd1 >= 0 && fd2 >= 0)
64 if((n = readn(fd1, buf1, sizeof(buf1))) < 0){
65 error("can't read %s: %r", a);
70 m++; /* read past eof to verify size */
71 if((m = readn(fd2, buf2, m)) < 0){
72 error("can't read %s: %r", b);
81 } while(memcmp(buf1, buf2, n) == 0);
92 samefile(Dir *a, Dir *b)
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){
102 if((a->qid.type & QTDIR) == 0)
106 * directories in /n/dump have the same qid, but
107 * atime can be used to skip potentially
108 * untouched subtrees.
110 if(dumpcheck && a->atime == b->atime)
120 if(a == nil || b == nil)
126 if((a->qid.type | b->qid.type) & QTDIR)
129 if((a->mode ^ b->mode) & permcheck)
133 if(strcmp(a->uid, b->uid) != 0)
135 if(strcmp(a->gid, b->gid) != 0)
140 if(a->length != b->length)
144 if(a->mtime != b->mtime)
147 if(sizecheck && timecheck)
150 return cmpfile(a->name, b->name);
160 error("can't stat %s: %r", path);
162 d->name = emalloc(strlen(path)+1);
163 strcpy(d->name, path);
169 pjoin(char *path, char *name)
175 s = emalloc(n+strlen(name)+2);
177 if(path[0] != '\0' && path[n-1] != '/')
184 absdir(Dir *d, char *path)
187 d->name = pjoin(path, d->name);
208 dnamecmp(void *a, void *b)
210 return strcmp(((Dir*)a)->name, ((Dir*)b)->name);
221 if(d == nil || (d->qid.type & QTDIR) == 0)
223 fd = open(d->name, OREAD);
225 error("can't open %s: %r", d->name);
228 if((n = dirreadall(fd, dp)) < 0)
229 error("can't read %s: %r", d->name);
232 qsort(*dp, n, sizeof(Dir), dnamecmp);
237 diffgen(Dir *ld, Dir *rd, Dir *ad, char *path);
240 diffdir(Dir *ld, Dir *rd, Dir *ad, char *path)
242 int n, m, o, i, j, k, t, v;
243 char *sp, *lp, *rp, *ap;
252 /* check if ad is the same as ld or rd */
253 if(ld != nil && samefile(ad, ld)){
255 ad = nil; /* don't read directory twice */
257 else if(rd != nil && samefile(ad, rd)){
259 ad = nil; /* don't read directory twice */
270 /* at least one side is directory */
276 t = (j < m) ? strcmp(ld[i].name, rd[j].name) : -1;
284 sp = pjoin(path, ld[i].name);
288 v = strcmp(ad[k].name, ld[i].name);
290 od = absdir(&ad[k++], ap);
296 diffgen(absdir(&ld[i], lp), nil, od, sp);
302 sp = pjoin(path, rd[j].name);
306 v = strcmp(ad[k].name, rd[j].name);
308 od = absdir(&ad[k++], ap);
315 diffgen(nil, absdir(&rd[j], rp), od, sp);
319 diffgen(absdir(&ld[i], lp), absdir(&rd[j], rp), od, sp);
340 diffgen(Dir *ld, Dir *rd, Dir *ad, char *path)
342 if(dcmp(ld, rd) == 0)
345 if(ld == nil || rd == nil){
346 /* one side doesnt exit anymore */
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);
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);
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);
371 /* local modified, remote deleted, conflict */
372 print("md!\t%s\n", path);
375 if((rd->qid.type ^ ad->qid.type) & QTDIR){
376 /* remote file type change, local deleted, no conflict */
377 diffgen(nil, rd, nil, path);
380 /* remote modified, local deleted, conflict */
381 print("dm!\t%s\n", path);
385 /* didnt exist before, is addition */
387 /* local added file, local newer */
388 print("an\t%s\n", path);
390 /* remote added file, remote newer */
391 print("na\t%s\n", path);
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);
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);
415 /* remote modified file, remote newer */
416 print("nm\t%s\n", path);
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);
425 /* local and remote became diverging files, conflict */
426 print("aa!\t%s\n", path);
429 /* local and remote modified, conflict */
430 print("mm!\t%s\n", path);
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 */
439 /* local and remote added diverging files, conflict */
440 print("aa!\t%s\n", path);
446 diffdir(ld, rd, ad, path);
450 diff3(char *lp, char *ap, char *rp)
457 if(ld == nil && rd == nil)
459 else if(strcmp(ap, lp) == 0)
461 else if(strcmp(ap, rp) == 0)
463 else if((ad = statdir(ap)) != nil){
464 if(ld != nil && samefile(ad, ld)){
468 else if(rd != nil && samefile(ad, rd)){
473 diffgen(ld, rd, ad, "");
477 if(ad != nil && ad != ld && ad != rd)
484 fprint(2, "usage: %s [ -qcutDL ] [ -p perms ] myfile oldfile yourfile\n", argv0);
489 main(int argc, char *argv[])
511 permcheck = strtol(EARGF(usage()), nil, 8) & 0777;
520 diff3(argv[0], argv[1], argv[2]);