tool to update file trees. reports differences and commands needed to make dst like src. fmode may probably go now. Useful to synchronize, without detecting conflicts. Reference: /n/patches.lsub.org/patch/up Date: Sat Apr 14 23:07:58 CES 2012 Signed-off-by: nemo@lsub.org --- /sys/man/1/up Thu Jan 1 00:00:00 1970 +++ /sys/man/1/up Sat Apr 14 22:46:33 2012 @@ -0,0 +1,58 @@ +.TH UP 1 +.SH NAME +up \- update a tree +.SH SYNOPSIS +.B up +[ +.B -cugs +] +.I src +.I dst +.SH DESCRIPTION +.I Up +prints shell commands needed to make +.I dst +like +.IR src . +Both arguments may name files or directories and exist or not +exist. +.PP +By default, user and group ids are ignored. Flag +.B -u +makes the user and group ids to be considered. +Flag +.B -g +makes group ids to be considered. +.PP +Flag +.B -c +makes +.I up +report changes instead of commands to update the +destination file. Each change is indicated by the initial of +.IR added , +.IR deleted , +.IR mode-changed , +and +.IR changed +followed by the file name, relative to the destination directory. +.PP +Flag +.B -s +makes the tool silent, reporting through +the exit status non-nil only if +.I src +and +.I dst +differ. +.SH SOURCE +.B /sys/src/cmd/up.c +.SH EXAMPLE +.EX +; up /rc/bin/patch /n/src/nix/rc/bin/patch +chmod 0775 /n/src/nix/rc/bin/patch/applied +fcp -x /rc/bin/patch/apply /n/src/nix/rc/bin/patch/apply +chmod 0775 /n/src/nix/rc/bin/patch/create +chmod 0775 /n/src/nix/rc/bin/patch/okay +chmod 0775 /n/src/nix/rc/bin/patch/undo +.EE --- /sys/src/cmd/up.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/up.c Sat Apr 14 23:00:56 2012 @@ -0,0 +1,324 @@ +/* + * File update tool. + * Report actions needed to make dst be like src + */ + +#include +#include +#include + +enum +{ + Rc = 0, + Chg, + Sts, +}; + +static int dogids, douids; +static char *fcparg = "-x"; +static int omode = Rc; +static char *droot; +static Biobuf bout; + +static int +realerror(void) +{ + char buf[128]; + + rerrstr(buf, sizeof buf); + return strstr(buf, "does not exist") == 0; +} + +static int +samecontent(char *f1, char *f2) +{ + Waitmsg *m; + + if(fork() == 0){ + execl("/bin/cmp", "cmp", "-s", f1, f2, nil); + sysfatal("exec /bin/cmp: %r"); + } + m = wait(); + if(m->msg[0] == 0){ + free(m); + return 1; + }else{ + free(m); + return 0; + } +} + +static Dir* +findname(char *name, Dir *d, int nd) +{ + int i; + + for(i = 0; i < nd; i++) + if(d[i].name != nil && strcmp(d[i].name, name) == 0) + return &d[i]; + return nil; +} + +static int update(Dir*, char*, Dir*, char*); + +static int +updatedir(char *src, char *dst) +{ + int sfd, dfd, nsd, ndd, r, i; + Dir *sd, *dd, *csd, *cdd; + char *csrc, *cdst; + + sfd = open(src, OREAD); + if(sfd < 0){ + fprint(2, "%s: %s: %r\n", argv0, src); + return -1; + } + dfd = open(dst, OREAD); + if(dfd < 0){ + close(sfd); + fprint(2, "%s: %s: %r\n", argv0, dst); + return -1; + } + sd = nil; + dd = nil; + nsd = dirreadall(sfd, &sd); + ndd = dirreadall(dfd, &dd); + close(sfd); + close(dfd); + if(nsd < 0){ + fprint(2, "%s: %s: %r\n", argv0, src); + goto fail; + } + if(ndd < 0){ + fprint(2, "%s: %s: %r\n", argv0, dst); + goto fail; + } + + for(i = 0; i < nsd; i++){ + csd = &sd[i]; + cdd = findname(csd->name, dd, ndd); + csrc = smprint("%s/%s", src, csd->name); + cdst = smprint("%s/%s", dst, csd->name); + r = update(csd, csrc, cdd, cdst); + free(csrc); + free(cdst); + if(r < 0) + goto fail; + if(cdd != nil) + cdd->name = nil; + } + + for(i = 0; i < ndd; i++) + if(dd[i].name != nil){ + csrc = smprint("%s/%s", src, dd[i].name); + cdst = smprint("%s/%s", dst, dd[i].name); + r = update(nil, csrc, &dd[i], cdst); + free(csrc); + free(cdst); + if(r < 0) + goto fail; + } + + free(sd); + free(dd); + return 0; +fail: + free(sd); + free(dd); + return -1; +} + +static int +samemode(Dir *d1, Dir *d2) +{ + long m1, m2; + + m1 = d1->mode & (0777|DMAPPEND|DMEXCL|DMTMP); + m2 = d2->mode & (0777|DMAPPEND|DMEXCL|DMTMP); + return m1 == m2; +} + +static void +updatemode(char *f, long mode) +{ + if(omode == Sts) + exits(f); + Bprint(&bout, "chmod 0%lo %s\n", mode&(0777|DMAPPEND|DMEXCL|DMTMP), f); +} + +static void +updategid(char *f, char *gid) +{ + if(omode == Sts) + exits(f); + Bprint(&bout, "chgrp %s %s\n", gid, f); +} + +static void +updateuid(char *f, char *uid) +{ + if(omode == Sts) + exits(f); + Bprint(&bout, "chgrp -o %s %s\n", uid, f); +} + +static char* +drel(char *n) +{ + static int l; + + if(l == 0) + l = strlen(droot); + if(n[l] == 0) + return "/"; + return n + l; +} + +static void +updatecp(char *src, char *dst, int isdir) +{ + if(omode == Sts) + exits(dst); + if(omode == Chg){ + Bprint(&bout, "c %s\n", drel(dst)); + return; + } + if(isdir) + Bprint(&bout, "mkdir -p %s && dircp %s %s\n", dst, src, dst); + else + Bprint(&bout, "fcp -x %s %s\n", src, dst); +} + +static void +updatenew(char *src, char *dst, int isdir) +{ + if(omode == Sts) + exits(dst); + if(omode == Chg){ + Bprint(&bout, "a %s\n", drel(dst)); + return; + } + if(isdir) + Bprint(&bout, "mkdir -p %s && dircp %s %s\n", dst, src, dst); + else + Bprint(&bout, "fcp %s %s %s\n", fcparg, src, dst); +} + +static void +updatedel(char *f) +{ + if(omode == Sts) + exits(f); + if(omode == Chg){ + Bprint(&bout, "d %s\n", drel(f)); + return; + } + Bprint(&bout, "rm -rf %s\n", f); +} + +static void +updatestat(char *f, long, char *, char *) +{ + if(omode == Sts) + exits(f); + Bprint(&bout, "m %s\n", drel(f)); +} + +static int +update(Dir *sd, char *src, Dir *dd, char *dst) +{ + if(sd == nil && dd == nil) + return 0; + + if(droot == nil) + droot = dst; + + if(sd == nil && dd != nil){ + updatedel(dst); + return 0; + } + + if(sd != nil && dd == nil){ + updatenew(src, dst, sd->mode&DMDIR); + return 0; + } + + if((sd->mode&DMDIR) != (dd->mode&DMDIR)){ + updatedel(dst); + updatenew(src, dst, sd->mode&DMDIR); + return 0; + } + + if((sd->mode&DMDIR) == 0){ + if(samecontent(src, dst) == 0){ + updatecp(src, dst, 0); + return 0; + } + if(omode == Chg && + (samemode(sd, dd) == 0 || + (dogids && strcmp(sd->gid, dd->gid) != 0) || + (douids && strcmp(sd->uid, dd->uid) != 0)) ){ + updatestat(dst, sd->mode, sd->uid, sd->gid); + return 0; + } + if(samemode(sd, dd) == 0) + updatemode(dst, sd->mode); + if(dogids && strcmp(sd->gid, dd->gid) != 0) + updategid(dst, sd->gid); + if(douids && strcmp(sd->uid, dd->uid) != 0) + updateuid(dst, sd->uid); + return 0; + } + + return updatedir(src, dst); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-cugs] src dst\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Dir *sd, *dd; + + ARGBEGIN{ + case 'c': + omode = Chg; + break; + case 'u': + dogids = 1; + douids = 1; + fcparg = "-gux"; + break; + case 'g': + dogids = 1; + fcparg = "-gx"; + break; + case 's': + omode = Sts; + break; + default: + usage(); + }ARGEND + if(argc != 2) + usage(); + outofmemoryexits(1); + if(Binit(&bout, 1, OWRITE) < 0) + sysfatal("Binit: %r"); + sd = dirstat(argv[0]); + if(sd == nil && realerror()) + sysfatal("%s: %r", argv[0]); + dd = dirstat(argv[1]); + if(dd == nil && realerror()) + sysfatal("%s: %r", argv[1]); + if(update(sd, argv[0], dd, argv[1]) < 0){ + Bterm(&bout); + exits("errors"); + } + Bterm(&bout); + exits(nil); +} Binary files /386/bin/up and up.1 differ