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