xref: /plan9/sys/src/cmd/history.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include	<u.h>
2 #include	<libc.h>
3 
4 #define	MINUTE(x)	((long)(x)*60L)
5 #define	HOUR(x)		(MINUTE(x)*60L)
6 #define	YEAR(x)		(HOUR(x)*24L*360L)
7 
8 int	verb;
9 int	uflag;
10 int	diff;
11 int	diffb;
12 char*	ndump;
13 char*	sflag;
14 
15 void	ysearch(char*);
16 long	starttime(char*);
17 void	lastbefore(ulong, char*, char*);
18 char*	prtime(ulong);
19 
20 void
21 main(int argc, char *argv[])
22 {
23 	char buf[100];
24 	Tm *tm;
25 	Waitmsg *w;
26 	int i;
27 
28 	ndump = "dump";
29 	ARGBEGIN {
30 	default:
31 		goto usage;
32 	case 'v':
33 		verb = 1;
34 		break;
35 	case 'd':
36 		ndump = ARGF();
37 		break;
38 	case 'D':
39 		diff = 1;
40 		break;
41 	case 'b':
42 		diffb = 1;
43 		break;
44 	case 's':
45 		sflag = ARGF();
46 		break;
47 	case 'u':
48 		uflag = 1;
49 		break;
50 	} ARGEND
51 
52 	if(argc == 0) {
53 	usage:
54 		fprint(2, "usage: history [-vuD] [-d 9fsname] [-s yyyymmdd] files\n");
55 		exits(0);
56 	}
57 
58 	tm = localtime(time(0));
59 	sprint(buf, "/n/%s/%.4d/", ndump, tm->year+1900);
60 	if(access(buf, AREAD) < 0) {
61 		if(verb)
62 			print("mounting dump\n");
63 		if(rfork(RFFDG|RFPROC) == 0) {
64 			execl("/bin/rc", "rc", "9fs", ndump, 0);
65 			exits(0);
66 		}
67 		w = wait();
68 		if(w == nil){
69 			fprint(2, "history: wait error: %r\n");
70 			exits("wait");
71 		}
72 		if(w->msg[0] != '\0'){
73 			fprint(2, "9fs failed: %s\n", w->msg);
74 			exits(w->msg);
75 		}
76 		free(w);
77 	}
78 
79 	for(i=0; i<argc; i++)
80 		ysearch(argv[i]);
81 	exits(0);
82 }
83 
84 void
85 ysearch(char *file)
86 {
87 	char fil[400], buf[500], pair[2][500];
88 	Dir *dir;
89 	ulong otime, dt;
90 	int toggle, started;
91 
92 	started = 0;
93 	dir = dirstat(file);
94 	if(dir == nil)
95 		fprint(2, "history: warning: %s does not exist\n", file);
96 	else{
97 		print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
98 		started = 1;
99 		strcpy(pair[1], file);
100 	}
101 	free(dir);
102 	fil[0] = 0;
103 	if(file[0] != '/') {
104 		getwd(strchr(fil, 0), 100);
105 		strcat(fil, "/");
106 	}
107 	strcat(fil, file);
108 	otime = starttime(sflag);
109 	toggle = 0;
110 	for(;;) {
111 		lastbefore(otime, fil, buf);
112 		dir = dirstat(buf);
113 		if(dir == nil)
114 			return;
115 		dt = HOUR(12);
116 		while(otime <= dir->mtime) {
117 			if(verb)
118 				print("backup %ld, %ld\n", dir->mtime, otime-dt);
119 			lastbefore(otime-dt, fil, buf);
120 			free(dir);
121 			dir = dirstat(buf);
122 			if(dir == nil)
123 				return;
124 			dt += HOUR(12);
125 		}
126 		strcpy(pair[toggle], buf);
127 		if(diff && started){
128 			switch(rfork(RFFDG|RFPROC)){
129 			case 0:
130 				if(diffb)
131 					execl("/bin/diff", "diff", "-nb", pair[toggle ^ 1], pair[toggle], 0);
132 				else
133 					execl("/bin/diff", "diff", "-n", pair[toggle ^ 1], pair[toggle], 0);
134 				fprint(2, "can't exec diff: %r\n");
135 				exits(0);
136 			case -1:
137 				fprint(2, "can't fork diff: %r\n");
138 				break;
139 			default:
140 				while(waitpid() != -1)
141 					;
142 				break;
143 			}
144 		}
145 		print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
146 		toggle ^= 1;
147 		started = 1;
148 		otime = dir->mtime;
149 	}
150 }
151 
152 void
153 lastbefore(ulong t, char *f, char *b)
154 {
155 	Tm *tm;
156 	Dir *dir;
157 	int vers, try;
158 	ulong t0, mtime;
159 
160 	t0 = t;
161 	if(verb)
162 		print("%ld lastbefore %s\n", t0, f);
163 	mtime = 0;
164 	for(try=0; try<10; try++) {
165 		tm = localtime(t);
166 		sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
167 			tm->year+1900, tm->mon+1, tm->mday);
168 		dir = dirstat(b);
169 		if(dir){
170 			mtime = dir->mtime;
171 			free(dir);
172 		}
173 		if(dir==nil || mtime > t0) {
174 			if(verb)
175 				print("%ld earlier %s\n", mtime, b);
176 			t -= HOUR(24);
177 			continue;
178 		}
179 		for(vers=0;; vers++) {
180 			sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
181 				tm->year+1900, tm->mon+1, tm->mday, vers+1);
182 			dir = dirstat(b);
183 			if(dir){
184 				mtime = dir->mtime;
185 				free(dir);
186 			}
187 			if(dir==nil || mtime > t0)
188 				break;
189 			if(verb)
190 				print("%ld later %s\n", mtime, b);
191 		}
192 		sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
193 			tm->year+1900, tm->mon+1, tm->mday, f);
194 		if(vers)
195 			sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
196 				tm->year+1900, tm->mon+1, tm->mday, vers, f);
197 		return;
198 	}
199 	strcpy(b, "XXX");	/* error */
200 }
201 
202 char*
203 prtime(ulong t)
204 {
205 	static char buf[100];
206 	char *b;
207 	Tm *tm;
208 
209 	if(uflag)
210 		tm = gmtime(t);
211 	else
212 		tm = localtime(t);
213 	b = asctime(tm);
214 	memcpy(buf, b+4, 24);
215 	buf[24] = 0;
216 	return buf;
217 }
218 
219 long
220 starttime(char *s)
221 {
222 	Tm *tm;
223 	long t, dt;
224 	int i, yr, mo, da;
225 
226 	t = time(0);
227 	if(s == 0)
228 		return t;
229 	for(i=0; s[i]; i++)
230 		if(s[i] < '0' || s[i] > '9') {
231 			fprint(2, "bad start time: %s\n", s);
232 			return t;
233 		}
234 	if(strlen(s)==6){
235 		yr = (s[0]-'0')*10 + s[1]-'0';
236 		mo = (s[2]-'0')*10 + s[3]-'0' - 1;
237 		da = (s[4]-'0')*10 + s[5]-'0';
238 		if(yr < 70)
239 			yr += 100;
240 	}else if(strlen(s)==8){
241 		yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
242 		yr -= 1900;
243 		mo = (s[4]-'0')*10 + s[5]-'0' - 1;
244 		da = (s[6]-'0')*10 + s[7]-'0';
245 	}else{
246 		fprint(2, "bad start time: %s\n", s);
247 		return t;
248 	}
249 	t = 0;
250 	dt = YEAR(10);
251 	for(i=0; i<50; i++) {
252 		tm = localtime(t+dt);
253 		if(yr > tm->year ||
254 		  (yr == tm->year && mo > tm->mon) ||
255 		  (yr == tm->year && mo == tm->mon) && da > tm->mday) {
256 			t += dt;
257 			continue;
258 		}
259 		dt /= 2;
260 		if(dt == 0)
261 			break;
262 	}
263 	t += HOUR(12);	/* .5 day to get to noon of argument */
264 	return t;
265 }
266