23 vsnprint(buf, sizeof(buf), fmt, a);
26 fprint(2, "%s: %s\n", argv0, buf);
32 #pragma varargck argpos error 1
39 cmpfile(char *a, char *b)
41 static uchar buf1[BUFSIZE], buf2[BUFSIZE];
42 int r, n, m, fd1, fd2;
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);
50 if(fd1 >= 0 && fd2 >= 0)
52 if((n = readn(fd1, buf1, sizeof(buf1))) < 0){
53 error("can't read %s: %r", a);
58 m++; /* read past eof to verify size */
59 if((m = readn(fd2, buf2, m)) < 0){
60 error("can't read %s: %r", b);
69 } while(memcmp(buf1, buf2, n) == 0);
80 samefile(Dir *a, Dir *b)
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){
90 if((a->qid.type & QTDIR) == 0)
94 * directories in /n/dump have the same qid, but
95 * atime can be used to skip potentially
98 if(dumpcheck && a->atime == b->atime)
108 if(a == nil || b == nil)
114 if((a->qid.type | b->qid.type) & QTDIR)
117 if((a->mode ^ b->mode) & permcheck)
121 if(strcmp(a->uid, b->uid) != 0)
123 if(strcmp(a->gid, b->gid) != 0)
128 if(a->length != b->length)
132 if(a->mtime != b->mtime)
135 if(sizecheck && timecheck)
138 return cmpfile(a->name, b->name);
148 error("can't stat %s: %r", path);
150 d->name = strdup(path);
155 absdir(Dir *d, char *path)
158 d->name = smprint("%s/%s", path, d->name);
179 dnamecmp(void *a, void *b)
181 return strcmp(((Dir*)a)->name, ((Dir*)b)->name);
192 if(d == nil || (d->qid.type & QTDIR) == 0)
194 fd = open(d->name, OREAD);
196 error("can't open %s: %r", d->name);
199 if((n = dirreadall(fd, dp)) < 0)
200 error("can't read %s: %r", d->name);
203 qsort(*dp, n, sizeof(Dir), dnamecmp);
208 diffgen(Dir *ld, Dir *rd, Dir *ad, char *path);
211 diffdir(Dir *ld, Dir *rd, Dir *ad, char *path)
213 int n, m, o, i, j, k, t, v;
214 char *sp, *lp, *rp, *ap;
223 /* check if ad is the same as ld or rd */
224 if(ld != nil && samefile(ad, ld)){
226 ad = nil; /* don't read directory twice */
228 else if(rd != nil && samefile(ad, rd)){
230 ad = nil; /* don't read directory twice */
241 /* at least one side is directory */
247 t = (j < m) ? strcmp(ld[i].name, rd[j].name) : -1;
255 sp = smprint("%s/%s", path, ld[i].name);
259 v = strcmp(ad[k].name, ld[i].name);
261 od = absdir(&ad[k++], ap);
267 diffgen(absdir(&ld[i], lp), nil, od, sp);
273 sp = smprint("%s/%s", path, rd[j].name);
277 v = strcmp(ad[k].name, rd[j].name);
279 od = absdir(&ad[k++], ap);
286 diffgen(nil, absdir(&rd[j], rp), od, sp);
290 diffgen(absdir(&ld[i], lp), absdir(&rd[j], rp), od, sp);
311 diffgen(Dir *ld, Dir *rd, Dir *ad, char *path)
315 if(dcmp(ld, rd) == 0)
319 if(ld == nil || rd == nil){
320 /* one side doesnt exit anymore */
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);
339 /* remote modified, local deleted, conflict */
340 print("dm!\t%s\n", path);
343 /* didnt exist before, is addition */
345 /* local added file, local newer */
346 print("an\t%s\n", path);
348 /* remote added file, remote newer */
349 print("na\t%s\n", path);
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);
364 /* local and remote modified, conflict */
365 print("mm!\t%s\n", path);
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 */
372 /* local and remote added files, conflict */
373 print("aa!\t%s\n", path);
378 diffdir(ld, rd, ad, path);
387 diff3(char *lp, char *ap, char *rp)
393 if((ld = statdir(lp)) == nil)
395 if((rd = statdir(rp)) == nil)
398 if(strcmp(ap, lp) == 0)
400 else if(strcmp(ap, rp) == 0)
402 else if((ad = statdir(ap)) == nil)
404 else if(samefile(ad, ld)){
408 else if(samefile(ad, rd)){
413 if(ld->qid.type & QTDIR)
416 if(name = strrchr(lp, '/'))
421 diffgen(ld, rd, ad, name);
425 if(ad != nil && ad != ld && ad != rd)
432 fprint(2, "usage: %s [ -qcutDL ] [ -p perms ] myfile oldfile yourfile\n", argv0);
437 main(int argc, char *argv[])
459 permcheck = strtol(EARGF(usage()), nil, 8) & 0777;
468 diff3(argv[0], argv[1], argv[2]);