xref: /plan9/sys/src/cmd/history.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
17dd7cddfSDavid du Colombier #include	<u.h>
27dd7cddfSDavid du Colombier #include	<libc.h>
37dd7cddfSDavid du Colombier 
47dd7cddfSDavid du Colombier #define	MINUTE(x)	((long)(x)*60L)
57dd7cddfSDavid du Colombier #define	HOUR(x)		(MINUTE(x)*60L)
67dd7cddfSDavid du Colombier #define	YEAR(x)		(HOUR(x)*24L*360L)
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier int	verb;
97dd7cddfSDavid du Colombier int	uflag;
107fd46167SDavid du Colombier int	force;
117dd7cddfSDavid du Colombier int	diff;
127dd7cddfSDavid du Colombier int	diffb;
137dd7cddfSDavid du Colombier char*	sflag;
147dd7cddfSDavid du Colombier 
15f8e525acSDavid du Colombier void	ysearch(char*, char*);
167dd7cddfSDavid du Colombier long	starttime(char*);
17f8e525acSDavid du Colombier void	lastbefore(ulong, char*, char*, char*);
187dd7cddfSDavid du Colombier char*	prtime(ulong);
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier void
217dd7cddfSDavid du Colombier main(int argc, char *argv[])
227dd7cddfSDavid du Colombier {
237dd7cddfSDavid du Colombier 	int i;
24f8e525acSDavid du Colombier 	char *ndump;
257dd7cddfSDavid du Colombier 
26f8e525acSDavid du Colombier 	ndump = nil;
277dd7cddfSDavid du Colombier 	ARGBEGIN {
287dd7cddfSDavid du Colombier 	default:
297dd7cddfSDavid du Colombier 		goto usage;
307dd7cddfSDavid du Colombier 	case 'v':
317dd7cddfSDavid du Colombier 		verb = 1;
327dd7cddfSDavid du Colombier 		break;
337fd46167SDavid du Colombier 	case 'f':
347fd46167SDavid du Colombier 		force = 1;
357fd46167SDavid du Colombier 		break;
367dd7cddfSDavid du Colombier 	case 'd':
377dd7cddfSDavid du Colombier 		ndump = ARGF();
387dd7cddfSDavid du Colombier 		break;
397dd7cddfSDavid du Colombier 	case 'D':
407dd7cddfSDavid du Colombier 		diff = 1;
417dd7cddfSDavid du Colombier 		break;
427dd7cddfSDavid du Colombier 	case 'b':
437dd7cddfSDavid du Colombier 		diffb = 1;
447dd7cddfSDavid du Colombier 		break;
457dd7cddfSDavid du Colombier 	case 's':
467dd7cddfSDavid du Colombier 		sflag = ARGF();
477dd7cddfSDavid du Colombier 		break;
487dd7cddfSDavid du Colombier 	case 'u':
497dd7cddfSDavid du Colombier 		uflag = 1;
507dd7cddfSDavid du Colombier 		break;
517dd7cddfSDavid du Colombier 	} ARGEND
527dd7cddfSDavid du Colombier 
537dd7cddfSDavid du Colombier 	if(argc == 0) {
547dd7cddfSDavid du Colombier 	usage:
5524e2e655SDavid du Colombier 		fprint(2, "usage: history [-bDfuv] [-d 9fsname] [-s yyyymmdd] files\n");
567dd7cddfSDavid du Colombier 		exits(0);
577dd7cddfSDavid du Colombier 	}
587dd7cddfSDavid du Colombier 
59f8e525acSDavid du Colombier 	for(i=0; i<argc; i++)
60f8e525acSDavid du Colombier 		ysearch(argv[i], ndump);
61f8e525acSDavid du Colombier 	exits(0);
62f8e525acSDavid du Colombier }
63f8e525acSDavid du Colombier 
64f8e525acSDavid du Colombier void
65f8e525acSDavid du Colombier ysearch(char *file, char *ndump)
66f8e525acSDavid du Colombier {
67f8e525acSDavid du Colombier 	char fil[400], buf[500], nbuf[100], pair[2][500], *p;
68f8e525acSDavid du Colombier 	Tm *tm;
69f8e525acSDavid du Colombier 	Waitmsg *w;
70f8e525acSDavid du Colombier 	Dir *dir, *d;
71f8e525acSDavid du Colombier 	ulong otime, dt;
72f8e525acSDavid du Colombier 	int toggle, started, missing;
73f8e525acSDavid du Colombier 
7459c21d95SDavid du Colombier 	fil[0] = 0;
7559c21d95SDavid du Colombier 	if(file[0] != '/') {
7659c21d95SDavid du Colombier 		getwd(strchr(fil, 0), 100);
7759c21d95SDavid du Colombier 		strcat(fil, "/");
7859c21d95SDavid du Colombier 	}
7959c21d95SDavid du Colombier 	strcat(fil, file);
8024e2e655SDavid du Colombier 	if(memcmp(fil, "/n/", 3) == 0){
8159c21d95SDavid du Colombier 		p = strchr(fil+3, '/');
82f8e525acSDavid du Colombier 		if(p == nil)
8359c21d95SDavid du Colombier 			p = fil+strlen(fil);
8459c21d95SDavid du Colombier 		if(ndump == nil){
8559c21d95SDavid du Colombier 			if(p-fil >= sizeof nbuf-10){
8659c21d95SDavid du Colombier 				fprint(2, "%s: dump name too long", fil);
87f8e525acSDavid du Colombier 				return;
88f8e525acSDavid du Colombier 			}
8959c21d95SDavid du Colombier 			memmove(nbuf, fil+3, p-(fil+3));
9059c21d95SDavid du Colombier 			nbuf[p-(fil+3)] = 0;
91f8e525acSDavid du Colombier 			strcat(nbuf, "dump");
92f8e525acSDavid du Colombier 			ndump = nbuf;
93f8e525acSDavid du Colombier 		}
9459c21d95SDavid du Colombier 		memmove(fil, p, strlen(p)+1);
9559c21d95SDavid du Colombier 	}
9659c21d95SDavid du Colombier 	if(ndump == nil)
9759c21d95SDavid du Colombier 		ndump = "dump";
98f8e525acSDavid du Colombier 
997dd7cddfSDavid du Colombier 	tm = localtime(time(0));
100*4d44ba9bSDavid du Colombier 	snprint(buf, sizeof buf, "/n/%s/%.4d/", ndump, tm->year+1900);
1019a747e4fSDavid du Colombier 	if(access(buf, AREAD) < 0) {
1027dd7cddfSDavid du Colombier 		if(verb)
103f8e525acSDavid du Colombier 			print("mounting dump %s\n", ndump);
1047dd7cddfSDavid du Colombier 		if(rfork(RFFDG|RFPROC) == 0) {
1057dd7cddfSDavid du Colombier 			execl("/bin/rc", "rc", "9fs", ndump, 0);
1067dd7cddfSDavid du Colombier 			exits(0);
1077dd7cddfSDavid du Colombier 		}
1089a747e4fSDavid du Colombier 		w = wait();
1099a747e4fSDavid du Colombier 		if(w == nil){
1109a747e4fSDavid du Colombier 			fprint(2, "history: wait error: %r\n");
1119a747e4fSDavid du Colombier 			exits("wait");
1129a747e4fSDavid du Colombier 		}
1139a747e4fSDavid du Colombier 		if(w->msg[0] != '\0'){
1149a747e4fSDavid du Colombier 			fprint(2, "9fs failed: %s\n", w->msg);
1159a747e4fSDavid du Colombier 			exits(w->msg);
1169a747e4fSDavid du Colombier 		}
1179a747e4fSDavid du Colombier 		free(w);
1187dd7cddfSDavid du Colombier 	}
1197dd7cddfSDavid du Colombier 
1209a747e4fSDavid du Colombier 	started = 0;
1219a747e4fSDavid du Colombier 	dir = dirstat(file);
1229a747e4fSDavid du Colombier 	if(dir == nil)
1239a747e4fSDavid du Colombier 		fprint(2, "history: warning: %s does not exist\n", file);
1249a747e4fSDavid du Colombier 	else{
1259a747e4fSDavid du Colombier 		print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
1269a747e4fSDavid du Colombier 		started = 1;
127*4d44ba9bSDavid du Colombier 		strecpy(pair[1], pair[1]+sizeof pair[1], file);
1289a747e4fSDavid du Colombier 	}
1299a747e4fSDavid du Colombier 	free(dir);
1309a747e4fSDavid du Colombier 	otime = starttime(sflag);
1317dd7cddfSDavid du Colombier 	toggle = 0;
1327dd7cddfSDavid du Colombier 	for(;;) {
133f8e525acSDavid du Colombier 		lastbefore(otime, fil, buf, ndump);
1349a747e4fSDavid du Colombier 		dir = dirstat(buf);
1357fd46167SDavid du Colombier 		if(dir == nil) {
1367fd46167SDavid du Colombier 			if(!force)
1377dd7cddfSDavid du Colombier 				return;
1387fd46167SDavid du Colombier 			dir = malloc(sizeof(Dir));
1397fd46167SDavid du Colombier 			nulldir(dir);
1407fd46167SDavid du Colombier 			dir->mtime = otime + 1;
1417fd46167SDavid du Colombier 		}
1427dd7cddfSDavid du Colombier 		dt = HOUR(12);
1437fd46167SDavid du Colombier 		missing = 0;
1449a747e4fSDavid du Colombier 		while(otime <= dir->mtime){
1457dd7cddfSDavid du Colombier 			if(verb)
1469a747e4fSDavid du Colombier 				print("backup %ld, %ld\n", dir->mtime, otime-dt);
147f8e525acSDavid du Colombier 			lastbefore(otime-dt, fil, buf, ndump);
1487fd46167SDavid du Colombier 			d = dirstat(buf);
1497fd46167SDavid du Colombier 			if(d == nil){
1507fd46167SDavid du Colombier 				if(!force)
1517dd7cddfSDavid du Colombier 					return;
1527fd46167SDavid du Colombier 				if(!missing)
1537fd46167SDavid du Colombier 					print("removed %s\n", buf);
1547fd46167SDavid du Colombier 				missing = 1;
1557fd46167SDavid du Colombier 			}else{
1567fd46167SDavid du Colombier 				free(dir);
1577fd46167SDavid du Colombier 				dir = d;
1587fd46167SDavid du Colombier 			}
1597dd7cddfSDavid du Colombier 			dt += HOUR(12);
1607dd7cddfSDavid du Colombier 		}
1617dd7cddfSDavid du Colombier 		strcpy(pair[toggle], buf);
1627dd7cddfSDavid du Colombier 		if(diff && started){
1637dd7cddfSDavid du Colombier 			switch(rfork(RFFDG|RFPROC)){
1647dd7cddfSDavid du Colombier 			case 0:
1657dd7cddfSDavid du Colombier 				if(diffb)
16659cc4ca5SDavid du Colombier 					execl("/bin/diff", "diff", "-nb", pair[toggle ^ 1], pair[toggle], 0);
1677dd7cddfSDavid du Colombier 				else
16859cc4ca5SDavid du Colombier 					execl("/bin/diff", "diff", "-n", pair[toggle ^ 1], pair[toggle], 0);
1697dd7cddfSDavid du Colombier 				fprint(2, "can't exec diff: %r\n");
1707dd7cddfSDavid du Colombier 				exits(0);
1717dd7cddfSDavid du Colombier 			case -1:
1727dd7cddfSDavid du Colombier 				fprint(2, "can't fork diff: %r\n");
1737dd7cddfSDavid du Colombier 				break;
1747dd7cddfSDavid du Colombier 			default:
1759a747e4fSDavid du Colombier 				while(waitpid() != -1)
1767dd7cddfSDavid du Colombier 					;
1777dd7cddfSDavid du Colombier 				break;
1787dd7cddfSDavid du Colombier 			}
1797dd7cddfSDavid du Colombier 		}
1809a747e4fSDavid du Colombier 		print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
1817dd7cddfSDavid du Colombier 		toggle ^= 1;
1827dd7cddfSDavid du Colombier 		started = 1;
1839a747e4fSDavid du Colombier 		otime = dir->mtime;
1847fd46167SDavid du Colombier 		free(dir);
1857dd7cddfSDavid du Colombier 	}
1867dd7cddfSDavid du Colombier }
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier void
189f8e525acSDavid du Colombier lastbefore(ulong t, char *f, char *b, char *ndump)
1907dd7cddfSDavid du Colombier {
1917dd7cddfSDavid du Colombier 	Tm *tm;
1929a747e4fSDavid du Colombier 	Dir *dir;
1937dd7cddfSDavid du Colombier 	int vers, try;
1949a747e4fSDavid du Colombier 	ulong t0, mtime;
19525747282SDavid du Colombier 	int i, n, fd;
1967dd7cddfSDavid du Colombier 
1977dd7cddfSDavid du Colombier 	t0 = t;
1987dd7cddfSDavid du Colombier 	if(verb)
1997dd7cddfSDavid du Colombier 		print("%ld lastbefore %s\n", t0, f);
2009a747e4fSDavid du Colombier 	mtime = 0;
201da51d93aSDavid du Colombier 	for(try=0; try<30; try++) {
2027dd7cddfSDavid du Colombier 		tm = localtime(t);
2037dd7cddfSDavid du Colombier 		sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
2047dd7cddfSDavid du Colombier 			tm->year+1900, tm->mon+1, tm->mday);
2059a747e4fSDavid du Colombier 		dir = dirstat(b);
2069a747e4fSDavid du Colombier 		if(dir){
2079a747e4fSDavid du Colombier 			mtime = dir->mtime;
2089a747e4fSDavid du Colombier 			free(dir);
2099a747e4fSDavid du Colombier 		}
2109a747e4fSDavid du Colombier 		if(dir==nil || mtime > t0) {
2117dd7cddfSDavid du Colombier 			if(verb)
2129a747e4fSDavid du Colombier 				print("%ld earlier %s\n", mtime, b);
2137dd7cddfSDavid du Colombier 			t -= HOUR(24);
2147dd7cddfSDavid du Colombier 			continue;
2157dd7cddfSDavid du Colombier 		}
21625747282SDavid du Colombier 		if(strstr(ndump, "snap")){
21725747282SDavid du Colombier 			fd = open(b, OREAD);
21825747282SDavid du Colombier 			if(fd < 0)
21925747282SDavid du Colombier 				continue;
22025747282SDavid du Colombier 			n = dirreadall(fd, &dir);
22125747282SDavid du Colombier 			close(fd);
22225747282SDavid du Colombier 			if(n == 0)
22325747282SDavid du Colombier 				continue;
22425747282SDavid du Colombier 			for(i = n-1; i > 0; i--){
22525747282SDavid du Colombier 				if(dir[i].mtime > t0)
22625747282SDavid du Colombier 					break;
22725747282SDavid du Colombier 			}
22825747282SDavid du Colombier 			sprint(b, "/n/%s/%.4d/%.2d%.2d/%s%s", ndump,
22925747282SDavid du Colombier 				tm->year+1900, tm->mon+1, tm->mday, dir[i].name, f);
23025747282SDavid du Colombier 			free(dir);
23125747282SDavid du Colombier 		} else {
2327dd7cddfSDavid du Colombier 			for(vers=0;; vers++) {
2337dd7cddfSDavid du Colombier 				sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
2347dd7cddfSDavid du Colombier 					tm->year+1900, tm->mon+1, tm->mday, vers+1);
2359a747e4fSDavid du Colombier 				dir = dirstat(b);
2369a747e4fSDavid du Colombier 				if(dir){
2379a747e4fSDavid du Colombier 					mtime = dir->mtime;
2389a747e4fSDavid du Colombier 					free(dir);
2399a747e4fSDavid du Colombier 				}
2409a747e4fSDavid du Colombier 				if(dir==nil || mtime > t0)
2417dd7cddfSDavid du Colombier 					break;
2427dd7cddfSDavid du Colombier 				if(verb)
2439a747e4fSDavid du Colombier 					print("%ld later %s\n", mtime, b);
2447dd7cddfSDavid du Colombier 			}
2457dd7cddfSDavid du Colombier 			sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
2467dd7cddfSDavid du Colombier 				tm->year+1900, tm->mon+1, tm->mday, f);
2477dd7cddfSDavid du Colombier 			if(vers)
2487dd7cddfSDavid du Colombier 				sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
2497dd7cddfSDavid du Colombier 					tm->year+1900, tm->mon+1, tm->mday, vers, f);
25025747282SDavid du Colombier 		}
2517dd7cddfSDavid du Colombier 		return;
2527dd7cddfSDavid du Colombier 	}
2537dd7cddfSDavid du Colombier 	strcpy(b, "XXX");	/* error */
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier char*
2577dd7cddfSDavid du Colombier prtime(ulong t)
2587dd7cddfSDavid du Colombier {
2597dd7cddfSDavid du Colombier 	static char buf[100];
2607dd7cddfSDavid du Colombier 	char *b;
2617dd7cddfSDavid du Colombier 	Tm *tm;
2627dd7cddfSDavid du Colombier 
2637dd7cddfSDavid du Colombier 	if(uflag)
2647dd7cddfSDavid du Colombier 		tm = gmtime(t);
2657dd7cddfSDavid du Colombier 	else
2667dd7cddfSDavid du Colombier 		tm = localtime(t);
2677dd7cddfSDavid du Colombier 	b = asctime(tm);
2687dd7cddfSDavid du Colombier 	memcpy(buf, b+4, 24);
2697dd7cddfSDavid du Colombier 	buf[24] = 0;
2707dd7cddfSDavid du Colombier 	return buf;
2717dd7cddfSDavid du Colombier }
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier long
2747dd7cddfSDavid du Colombier starttime(char *s)
2757dd7cddfSDavid du Colombier {
2767dd7cddfSDavid du Colombier 	Tm *tm;
2777dd7cddfSDavid du Colombier 	long t, dt;
2787dd7cddfSDavid du Colombier 	int i, yr, mo, da;
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier 	t = time(0);
2817dd7cddfSDavid du Colombier 	if(s == 0)
2827dd7cddfSDavid du Colombier 		return t;
2839a747e4fSDavid du Colombier 	for(i=0; s[i]; i++)
2847dd7cddfSDavid du Colombier 		if(s[i] < '0' || s[i] > '9') {
2857dd7cddfSDavid du Colombier 			fprint(2, "bad start time: %s\n", s);
2867dd7cddfSDavid du Colombier 			return t;
2877dd7cddfSDavid du Colombier 		}
2889a747e4fSDavid du Colombier 	if(strlen(s)==6){
2897dd7cddfSDavid du Colombier 		yr = (s[0]-'0')*10 + s[1]-'0';
2907dd7cddfSDavid du Colombier 		mo = (s[2]-'0')*10 + s[3]-'0' - 1;
2917dd7cddfSDavid du Colombier 		da = (s[4]-'0')*10 + s[5]-'0';
2929a747e4fSDavid du Colombier 		if(yr < 70)
2939a747e4fSDavid du Colombier 			yr += 100;
2949a747e4fSDavid du Colombier 	}else if(strlen(s)==8){
2959a747e4fSDavid du Colombier 		yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
2969a747e4fSDavid du Colombier 		yr -= 1900;
2979a747e4fSDavid du Colombier 		mo = (s[4]-'0')*10 + s[5]-'0' - 1;
2989a747e4fSDavid du Colombier 		da = (s[6]-'0')*10 + s[7]-'0';
2999a747e4fSDavid du Colombier 	}else{
3009a747e4fSDavid du Colombier 		fprint(2, "bad start time: %s\n", s);
3019a747e4fSDavid du Colombier 		return t;
3029a747e4fSDavid du Colombier 	}
3037dd7cddfSDavid du Colombier 	t = 0;
3047dd7cddfSDavid du Colombier 	dt = YEAR(10);
3057dd7cddfSDavid du Colombier 	for(i=0; i<50; i++) {
3067dd7cddfSDavid du Colombier 		tm = localtime(t+dt);
3077dd7cddfSDavid du Colombier 		if(yr > tm->year ||
3087dd7cddfSDavid du Colombier 		  (yr == tm->year && mo > tm->mon) ||
3097dd7cddfSDavid du Colombier 		  (yr == tm->year && mo == tm->mon) && da > tm->mday) {
3107dd7cddfSDavid du Colombier 			t += dt;
3117dd7cddfSDavid du Colombier 			continue;
3127dd7cddfSDavid du Colombier 		}
3137dd7cddfSDavid du Colombier 		dt /= 2;
3147dd7cddfSDavid du Colombier 		if(dt == 0)
3157dd7cddfSDavid du Colombier 			break;
3167dd7cddfSDavid du Colombier 	}
3177dd7cddfSDavid du Colombier 	t += HOUR(12);	/* .5 day to get to noon of argument */
3187dd7cddfSDavid du Colombier 	return t;
3197dd7cddfSDavid du Colombier }
320