summaryrefslogtreecommitdiff
path: root/sys/src/cmd/history.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/history.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/history.c')
-rwxr-xr-xsys/src/cmd/history.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/sys/src/cmd/history.c b/sys/src/cmd/history.c
new file mode 100755
index 000000000..283da04df
--- /dev/null
+++ b/sys/src/cmd/history.c
@@ -0,0 +1,353 @@
+#include <u.h>
+#include <libc.h>
+
+#define MINUTE(x) ((long)(x)*60L)
+#define HOUR(x) (MINUTE(x)*60L)
+#define YEAR(x) (HOUR(x)*24L*360L)
+
+int verb;
+int uflag;
+int force;
+int diff;
+char* sflag;
+char* dargv[20] = {
+ "diff",
+};
+int ndargv = 1;
+
+void usage(void);
+void ysearch(char*, char*);
+long starttime(char*);
+void lastbefore(ulong, char*, char*, char*);
+char* prtime(ulong);
+void darg(char*);
+
+void
+main(int argc, char *argv[])
+{
+ int i;
+ char *ndump;
+
+ ndump = nil;
+ ARGBEGIN {
+ default:
+ usage();
+ case 'a':
+ darg("-a");
+ break;
+ case 'b':
+ darg("-b");
+ break;
+ case 'c':
+ darg("-c");
+ break;
+ case 'e':
+ darg("-e");
+ break;
+ case 'm':
+ darg("-m");
+ break;
+ case 'n':
+ darg("-n");
+ break;
+ case 'w':
+ darg("-w");
+ break;
+ case 'D':
+ diff = 1;
+ break;
+ case 'd':
+ ndump = ARGF();
+ break;
+ case 'f':
+ force = 1;
+ break;
+ case 's':
+ sflag = ARGF();
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'v':
+ verb = 1;
+ break;
+ } ARGEND
+
+ if(argc == 0)
+ usage();
+
+ for(i=0; i<argc; i++)
+ ysearch(argv[i], ndump);
+ exits(0);
+}
+
+void
+darg(char* a)
+{
+ if(ndargv >= nelem(dargv)-3)
+ return;
+ dargv[ndargv++] = a;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: history [-bDfuv] [-d dumpfilesystem] [-s yyyymmdd] files\n");
+ exits("usage");
+}
+
+void
+ysearch(char *file, char *ndump)
+{
+ char fil[400], buf[500], nbuf[100], pair[2][500], *p;
+ Tm *tm;
+ Waitmsg *w;
+ Dir *dir, *d;
+ ulong otime, dt;
+ int toggle, started, missing;
+
+ fil[0] = 0;
+ if(file[0] != '/') {
+ getwd(strchr(fil, 0), 100);
+ strcat(fil, "/");
+ }
+ strcat(fil, file);
+ if(memcmp(fil, "/n/", 3) == 0){
+ p = strchr(fil+3, '/');
+ if(p == nil)
+ p = fil+strlen(fil);
+ if(ndump == nil){
+ if(p-fil >= sizeof nbuf-10){
+ fprint(2, "%s: dump name too long", fil);
+ return;
+ }
+ memmove(nbuf, fil+3, p-(fil+3));
+ nbuf[p-(fil+3)] = 0;
+ strcat(nbuf, "dump");
+ ndump = nbuf;
+ }
+ memmove(fil, p, strlen(p)+1);
+ }
+ if(ndump == nil)
+ ndump = "dump";
+
+ tm = localtime(time(0));
+ snprint(buf, sizeof buf, "/n/%s/%.4d/", ndump, tm->year+1900);
+ if(access(buf, AREAD) < 0) {
+ if(verb)
+ print("mounting dump %s\n", ndump);
+ if(rfork(RFFDG|RFPROC) == 0) {
+ execl("/bin/rc", "rc", "9fs", ndump, nil);
+ exits(0);
+ }
+ w = wait();
+ if(w == nil){
+ fprint(2, "history: wait error: %r\n");
+ exits("wait");
+ }
+ if(w->msg[0] != '\0'){
+ fprint(2, "9fs failed: %s\n", w->msg);
+ exits(w->msg);
+ }
+ free(w);
+ }
+
+ started = 0;
+ dir = dirstat(file);
+ if(dir == nil)
+ fprint(2, "history: warning: %s does not exist\n", file);
+ else{
+ print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
+ started = 1;
+ strecpy(pair[1], pair[1]+sizeof pair[1], file);
+ }
+ free(dir);
+ otime = starttime(sflag);
+ toggle = 0;
+ for(;;) {
+ lastbefore(otime, fil, buf, ndump);
+ dir = dirstat(buf);
+ if(dir == nil) {
+ if(!force)
+ return;
+ dir = malloc(sizeof(Dir));
+ nulldir(dir);
+ dir->mtime = otime + 1;
+ }
+ dt = HOUR(12);
+ missing = 0;
+ while(otime <= dir->mtime){
+ if(verb)
+ print("backup %ld, %ld\n", dir->mtime, otime-dt);
+ lastbefore(otime-dt, fil, buf, ndump);
+ d = dirstat(buf);
+ if(d == nil){
+ if(!force)
+ return;
+ if(!missing)
+ print("removed %s\n", buf);
+ missing = 1;
+ }else{
+ free(dir);
+ dir = d;
+ }
+ dt += HOUR(12);
+ }
+ strcpy(pair[toggle], buf);
+ if(diff && started){
+ switch(rfork(RFFDG|RFPROC)){
+ case 0:
+ dargv[ndargv] = pair[toggle];
+ dargv[ndargv+1] = pair[toggle ^ 1];
+ exec("/bin/diff", dargv);
+ fprint(2, "can't exec diff: %r\n");
+ exits(0);
+ case -1:
+ fprint(2, "can't fork diff: %r\n");
+ break;
+ default:
+ while(waitpid() != -1)
+ ;
+ break;
+ }
+ }
+ print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
+ toggle ^= 1;
+ started = 1;
+ otime = dir->mtime;
+ free(dir);
+ }
+}
+
+void
+lastbefore(ulong t, char *f, char *b, char *ndump)
+{
+ Tm *tm;
+ Dir *dir;
+ int vers, try;
+ ulong t0, mtime;
+ int i, n, fd;
+
+ t0 = t;
+ if(verb)
+ print("%ld lastbefore %s\n", t0, f);
+ mtime = 0;
+ for(try=0; try<30; try++) {
+ tm = localtime(t);
+ sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
+ tm->year+1900, tm->mon+1, tm->mday);
+ dir = dirstat(b);
+ if(dir){
+ mtime = dir->mtime;
+ free(dir);
+ }
+ if(dir==nil || mtime > t0) {
+ if(verb)
+ print("%ld earlier %s\n", mtime, b);
+ t -= HOUR(24);
+ continue;
+ }
+ if(strstr(ndump, "snap")){
+ fd = open(b, OREAD);
+ if(fd < 0)
+ continue;
+ n = dirreadall(fd, &dir);
+ close(fd);
+ if(n == 0)
+ continue;
+ for(i = n-1; i > 0; i--){
+ if(dir[i].mtime > t0)
+ break;
+ }
+ sprint(b, "/n/%s/%.4d/%.2d%.2d/%s%s", ndump,
+ tm->year+1900, tm->mon+1, tm->mday, dir[i].name, f);
+ free(dir);
+ } else {
+ for(vers=0;; vers++) {
+ sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
+ tm->year+1900, tm->mon+1, tm->mday, vers+1);
+ dir = dirstat(b);
+ if(dir){
+ mtime = dir->mtime;
+ free(dir);
+ }
+ if(dir==nil || mtime > t0)
+ break;
+ if(verb)
+ print("%ld later %s\n", mtime, b);
+ }
+ sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
+ tm->year+1900, tm->mon+1, tm->mday, f);
+ if(vers)
+ sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
+ tm->year+1900, tm->mon+1, tm->mday, vers, f);
+ }
+ return;
+ }
+ strcpy(b, "XXX"); /* error */
+}
+
+char*
+prtime(ulong t)
+{
+ static char buf[100];
+ char *b;
+ Tm *tm;
+
+ if(uflag)
+ tm = gmtime(t);
+ else
+ tm = localtime(t);
+ b = asctime(tm);
+ memcpy(buf, b+4, 24);
+ buf[24] = 0;
+ return buf;
+}
+
+long
+starttime(char *s)
+{
+ Tm *tm;
+ long t, dt;
+ int i, yr, mo, da;
+
+ t = time(0);
+ if(s == 0)
+ return t;
+ for(i=0; s[i]; i++)
+ if(s[i] < '0' || s[i] > '9') {
+ fprint(2, "bad start time: %s\n", s);
+ return t;
+ }
+ if(strlen(s)==6){
+ yr = (s[0]-'0')*10 + s[1]-'0';
+ mo = (s[2]-'0')*10 + s[3]-'0' - 1;
+ da = (s[4]-'0')*10 + s[5]-'0';
+ if(yr < 70)
+ yr += 100;
+ }else if(strlen(s)==8){
+ yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
+ yr -= 1900;
+ mo = (s[4]-'0')*10 + s[5]-'0' - 1;
+ da = (s[6]-'0')*10 + s[7]-'0';
+ }else{
+ fprint(2, "bad start time: %s\n", s);
+ return t;
+ }
+ t = 0;
+ dt = YEAR(10);
+ for(i=0; i<50; i++) {
+ tm = localtime(t+dt);
+ if(yr > tm->year ||
+ (yr == tm->year && mo > tm->mon) ||
+ (yr == tm->year && mo == tm->mon) && da > tm->mday) {
+ t += dt;
+ continue;
+ }
+ dt /= 2;
+ if(dt == 0)
+ break;
+ }
+ t += HOUR(12); /* .5 day to get to noon of argument */
+ return t;
+}