diff options
author | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-11-02 20:28:12 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-11-02 20:28:12 +0100 |
commit | c0c2660f749b87a52c7a3b80591cab5b6a503f95 (patch) | |
tree | 0b13848d87ca61246a44558b03b721770bdb13f6 /sys/src/cmd/derp.c | |
parent | 5cb6be9ce3bb53909879d6911cc2411f8458ed10 (diff) |
add directory-examining recursive compare derp(1)
Diffstat (limited to 'sys/src/cmd/derp.c')
-rw-r--r-- | sys/src/cmd/derp.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/sys/src/cmd/derp.c b/sys/src/cmd/derp.c new file mode 100644 index 000000000..a16ef3b1e --- /dev/null +++ b/sys/src/cmd/derp.c @@ -0,0 +1,474 @@ +#include <u.h> +#include <libc.h> + +int permcheck = 0; +int usercheck = 0; +int dumpcheck = 1; +int sizecheck = 1; +int timecheck = 0; + +int quiet = 0; +int errors = 0; +int noerror = 0; + +void +error(char *fmt, ...) +{ + char buf[ERRMAX]; + va_list a; + + errors++; + if(!quiet){ + va_start(a, fmt); + vsnprint(buf, sizeof(buf), fmt, a); + va_end(a); + + fprint(2, "%s: %s\n", argv0, buf); + } + if(!noerror) + exits("errors"); +} + +#pragma varargck argpos error 1 + +enum { + BUFSIZE = 8*1024, +}; + +int +cmpfile(char *a, char *b) +{ + static uchar buf1[BUFSIZE], buf2[BUFSIZE]; + int r, n, m, fd1, fd2; + + if((fd1 = open(a, OREAD)) < 0) + error("can't open %s: %r", a); + if((fd2 = open(b, OREAD)) < 0) + error("can't open %s: %r", b); + + r = fd1 != fd2; + if(fd1 >= 0 && fd2 >= 0) + do{ + if((n = readn(fd1, buf1, sizeof(buf1))) < 0){ + error("can't read %s: %r", a); + break; + } + m = n; + if(m == 0) + m++; /* read past eof to verify size */ + if((m = readn(fd2, buf2, m)) < 0){ + error("can't read %s: %r", b); + break; + } + if(m != n) + break; + if(n == 0){ + r = 0; + break; + } + } while(memcmp(buf1, buf2, n) == 0); + + if(fd1 >= 0) + close(fd1); + if(fd2 >= 0) + close(fd2); + + return r; +} + +int +samefile(Dir *a, Dir *b) +{ + if(a == b) + return 1; + + if(a->type == b->type && a->dev == b->dev && + a->qid.type == b->qid.type && + a->qid.path == b->qid.path && + a->qid.vers == b->qid.vers){ + + if((a->qid.type & QTDIR) == 0) + return 1; + + /* + * directories in /n/dump have the same qid, but + * atime can be used to skip potentially + * untouched subtrees. + */ + if(dumpcheck && a->atime == b->atime) + return 1; + } + + return 0; +} + +int +dcmp(Dir *a, Dir *b) +{ + if(a == nil || b == nil) + return a != b; + + if(samefile(a, b)) + return 0; + + if((a->qid.type | b->qid.type) & QTDIR) + return 1; + + if((a->mode ^ b->mode) & permcheck) + return 1; + + if(usercheck){ + if(strcmp(a->uid, b->uid) != 0) + return 1; + if(strcmp(a->gid, b->gid) != 0) + return 1; + } + + if(sizecheck) + if(a->length != b->length) + return 1; + + if(timecheck) + if(a->mtime != b->mtime) + return 1; + + if(sizecheck && timecheck) + return 0; + + return cmpfile(a->name, b->name); +} + +Dir* +statdir(char *path) +{ + Dir *d; + + d = dirstat(path); + if(d == nil) + error("can't stat %s: %r", path); + else + d->name = strdup(path); + return d; +} + +Dir* +absdir(Dir *d, char *path) +{ + if(d != nil) + d->name = smprint("%s/%s", path, d->name); + return d; +} + +void +cleardir(Dir *d) +{ + if(d != nil){ + free(d->name); + d->name = ""; + } +} + +void +freedir(Dir *d) +{ + cleardir(d); + free(d); +} + +int +dnamecmp(void *a, void *b) +{ + return strcmp(((Dir*)a)->name, ((Dir*)b)->name); +} + +int +readifdir(Dir **dp) +{ + int n, fd; + Dir *d; + + d = *dp; + *dp = nil; + if(d == nil || (d->qid.type & QTDIR) == 0) + return 0; + fd = open(d->name, OREAD); + if(fd < 0){ + error("can't open %s: %r", d->name); + return -1; + } + if((n = dirreadall(fd, dp)) < 0) + error("can't read %s: %r", d->name); + close(fd); + if(n > 1) + qsort(*dp, n, sizeof(Dir), dnamecmp); + return n; +} + +void +diffgen(Dir *ld, Dir *rd, Dir *ad, char *path); + +void +diffdir(Dir *ld, Dir *rd, Dir *ad, char *path) +{ + int n, m, o, i, j, k, t, v; + char *sp, *lp, *rp, *ap; + Dir *od; + + lp = rp = ap = nil; + if(ld != nil) + lp = ld->name; + if(rd != nil) + rp = rd->name; + if(ad != nil){ + /* check if ad is the same as ld or rd */ + if(ld != nil && samefile(ad, ld)){ + ap = ld->name; + ad = nil; /* don't read directory twice */ + } + else if(rd != nil && samefile(ad, rd)){ + ap = rd->name; + ad = nil; /* don't read directory twice */ + } + else + ap = ad->name; + } + + n = readifdir(&ld); + m = readifdir(&rd); + if(n <= 0 && m <= 0) + return; + + /* at least one side is directory */ + o = readifdir(&ad); + + i = j = k = 0; + for(;;){ + if(i < n) + t = (j < m) ? strcmp(ld[i].name, rd[j].name) : -1; + else if(j < m) + t = 1; + else + break; + + od = nil; + if(t < 0){ + sp = smprint("%s/%s", path, ld[i].name); + if(ap == lp) + od = &ld[i]; + else while(k < o){ + v = strcmp(ad[k].name, ld[i].name); + if(v == 0){ + od = absdir(&ad[k++], ap); + break; + } else if(v > 0) + break; + k++; + } + diffgen(absdir(&ld[i], lp), nil, od, sp); + cleardir(&ld[i]); + if(&ld[i] == od) + od = nil; + i++; + } else { + sp = smprint("%s/%s", path, rd[j].name); + if(ap == rp) + od = &rd[j]; + else while(k < o){ + v = strcmp(ad[k].name, rd[j].name); + if(v == 0){ + od = absdir(&ad[k++], ap); + break; + } else if(v > 0) + break; + k++; + } + if(t > 0) + diffgen(nil, absdir(&rd[j], rp), od, sp); + else { + if(ap == lp) + od = &ld[i]; + diffgen(absdir(&ld[i], lp), absdir(&rd[j], rp), od, sp); + cleardir(&ld[i]); + if(&ld[i] == od) + od = nil; + i++; + } + cleardir(&rd[j]); + if(&rd[j] == od) + od = nil; + j++; + } + cleardir(od); + free(sp); + } + + free(ld); + free(rd); + free(ad); +} + +void +diffgen(Dir *ld, Dir *rd, Dir *ad, char *path) +{ + char *p; + + if(dcmp(ld, rd) == 0) + return; + + p = nil; + if(ld == nil || rd == nil){ + /* one side doesnt exit anymore */ + if(ad != nil){ + /* existed before, is deletion */ + if(ld != nil && (ad->qid.type & QTDIR) && (ld->qid.type & QTDIR)){ + /* remote deleted direcotry, remote newer */ + p = smprint("nd\t%s\n", path); + } else if(rd != nil && (ad->qid.type & QTDIR) && (rd->qid.type & QTDIR)){ + /* local deleted direcotry, local newer */ + p = smprint("dn\t%s\n", path); + } else if(dcmp(rd, ad) == 0){ + /* local deleted file, local newer */ + print("dn\t%s\n", path); + } else if(dcmp(ld, ad) == 0){ + /* remote deleted file, remote newer */ + print("nd\t%s\n", path); + } else if(ld != nil){ + /* local modified, remote deleted, conflict */ + print("md!\t%s\n", path); + } else { + /* remote modified, local deleted, conflict */ + print("dm!\t%s\n", path); + } + } else { + /* didnt exist before, is addition */ + if(ld != nil){ + /* local added file, local newer */ + print("an\t%s\n", path); + } else { + /* remote added file, remote newer */ + print("na\t%s\n", path); + } + } + } else { + if(ad != nil){ + /* existed before, is modification */ + if((ad->qid.type & QTDIR) && (ld->qid.type & QTDIR) && (rd->qid.type & QTDIR)){ + /* all still directories, no problem */ + } else if(dcmp(rd, ad) == 0){ + /* local modified file, local newer */ + print("mn\t%s\n", path); + } else if(dcmp(ld, ad) == 0){ + /* remote modified file, remote newer */ + print("nm\t%s\n", path); + } else { + /* local and remote modified, conflict */ + print("mm!\t%s\n", path); + } + } else { + /* didnt exist before, is addition from both */ + if((ld->qid.type & QTDIR) && (rd->qid.type & QTDIR)){ + /* local and remote added directories, no problem */ + } else { + /* local and remote added files, conflict */ + print("aa!\t%s\n", path); + } + } + } + + diffdir(ld, rd, ad, path); + + if(p != nil){ + print("%s", p); + free(p); + } +} + +void +diff3(char *lp, char *ap, char *rp) +{ + Dir *ld, *rd, *ad; + char *name; + + rd = ad = nil; + if((ld = statdir(lp)) == nil) + goto Out; + if((rd = statdir(rp)) == nil) + goto Out; + + if(strcmp(ap, lp) == 0) + ad = ld; + else if(strcmp(ap, rp) == 0) + ad = rd; + else if((ad = statdir(ap)) == nil) + goto Out; + else if(samefile(ad, ld)){ + freedir(ad); + ad = ld; + } + else if(samefile(ad, rd)){ + freedir(ad); + ad = rd; + } + + if(ld->qid.type & QTDIR) + name = "."; + else { + if(name = strrchr(lp, '/')) + name++; + else + name = lp; + } + diffgen(ld, rd, ad, name); +Out: + freedir(ld); + freedir(rd); + if(ad != nil && ad != ld && ad != rd) + freedir(ad); +} + +void +usage(void) +{ + fprint(2, "usage: %s [ -qcutDL ] [ -p perms ] myfile oldfile yourfile\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + ARGBEGIN { + case 'q': + quiet = 1; + break; + case 'c': + noerror = 1; + break; + case 'u': + usercheck = 1; + break; + case 't': + timecheck = 1; + break; + case 'D': + dumpcheck = 0; + break; + case 'L': + sizecheck = 0; + break; + case 'p': + permcheck = strtol(EARGF(usage()), nil, 8) & 0777; + break; + default: + usage(); + } ARGEND; + + if(argc != 3) + usage(); + + diff3(argv[0], argv[1], argv[2]); + + if(errors) + exits("errors"); + + exits(0); +} |