xref: /plan9/sys/src/cmd/history.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 	if(ndump == nil){
81 		if(slashnhack && memcmp(file, "/n/", 3) == 0){
82 			p = strchr(file+3, '/');
83 			if(p == nil)
84 				p = file+strlen(file);
85 			if(p-file >= sizeof nbuf-10){
86 				fprint(2, "%s: dump name too long", file);
87 				return;
88 			}
89 			memmove(nbuf, file+3, p-(file+3));
90 			nbuf[p-(file+3)] = 0;
91 			strcat(nbuf, "dump");
92 			ndump = nbuf;
93 		}else
94 			ndump = "dump";
95 	}
96 
97 	tm = localtime(time(0));
98 	sprint(buf, "/n/%s/%.4d/", ndump, tm->year+1900);
99 	if(access(buf, AREAD) < 0) {
100 		if(verb)
101 			print("mounting dump %s\n", ndump);
102 		if(rfork(RFFDG|RFPROC) == 0) {
103 			execl("/bin/rc", "rc", "9fs", ndump, 0);
104 			exits(0);
105 		}
106 		w = wait();
107 		if(w == nil){
108 			fprint(2, "history: wait error: %r\n");
109 			exits("wait");
110 		}
111 		if(w->msg[0] != '\0'){
112 			fprint(2, "9fs failed: %s\n", w->msg);
113 			exits(w->msg);
114 		}
115 		free(w);
116 	}
117 
118 	started = 0;
119 	dir = dirstat(file);
120 	if(dir == nil)
121 		fprint(2, "history: warning: %s does not exist\n", file);
122 	else{
123 		print("%s %s %lld [%s]\n", prtime(dir->mtime), file, dir->length, dir->muid);
124 		started = 1;
125 		strcpy(pair[1], file);
126 	}
127 	free(dir);
128 	fil[0] = 0;
129 	if(file[0] != '/') {
130 		getwd(strchr(fil, 0), 100);
131 		strcat(fil, "/");
132 	}
133 	strcat(fil, file);
134 	if(slashnhack && memcmp(fil, "/n/", 3) == 0){
135 		p = strchr(fil+3, '/');
136 		if(p)
137 			memmove(fil, p, strlen(p)+1);
138 	}
139 	otime = starttime(sflag);
140 	toggle = 0;
141 	for(;;) {
142 		lastbefore(otime, fil, buf, ndump);
143 		dir = dirstat(buf);
144 		if(dir == nil) {
145 			if(!force)
146 				return;
147 			dir = malloc(sizeof(Dir));
148 			nulldir(dir);
149 			dir->mtime = otime + 1;
150 		}
151 		dt = HOUR(12);
152 		missing = 0;
153 		while(otime <= dir->mtime){
154 			if(verb)
155 				print("backup %ld, %ld\n", dir->mtime, otime-dt);
156 			lastbefore(otime-dt, fil, buf, ndump);
157 			d = dirstat(buf);
158 			if(d == nil){
159 				if(!force)
160 					return;
161 				if(!missing)
162 					print("removed %s\n", buf);
163 				missing = 1;
164 			}else{
165 				free(dir);
166 				dir = d;
167 			}
168 			dt += HOUR(12);
169 		}
170 		strcpy(pair[toggle], buf);
171 		if(diff && started){
172 			switch(rfork(RFFDG|RFPROC)){
173 			case 0:
174 				if(diffb)
175 					execl("/bin/diff", "diff", "-nb", pair[toggle ^ 1], pair[toggle], 0);
176 				else
177 					execl("/bin/diff", "diff", "-n", pair[toggle ^ 1], pair[toggle], 0);
178 				fprint(2, "can't exec diff: %r\n");
179 				exits(0);
180 			case -1:
181 				fprint(2, "can't fork diff: %r\n");
182 				break;
183 			default:
184 				while(waitpid() != -1)
185 					;
186 				break;
187 			}
188 		}
189 		print("%s %s %lld [%s]\n", prtime(dir->mtime), buf, dir->length, dir->muid);
190 		toggle ^= 1;
191 		started = 1;
192 		otime = dir->mtime;
193 		free(dir);
194 	}
195 }
196 
197 void
198 lastbefore(ulong t, char *f, char *b, char *ndump)
199 {
200 	Tm *tm;
201 	Dir *dir;
202 	int vers, try;
203 	ulong t0, mtime;
204 
205 	t0 = t;
206 	if(verb)
207 		print("%ld lastbefore %s\n", t0, f);
208 	mtime = 0;
209 	for(try=0; try<10; try++) {
210 		tm = localtime(t);
211 		sprint(b, "/n/%s/%.4d/%.2d%.2d", ndump,
212 			tm->year+1900, tm->mon+1, tm->mday);
213 		dir = dirstat(b);
214 		if(dir){
215 			mtime = dir->mtime;
216 			free(dir);
217 		}
218 		if(dir==nil || mtime > t0) {
219 			if(verb)
220 				print("%ld earlier %s\n", mtime, b);
221 			t -= HOUR(24);
222 			continue;
223 		}
224 		for(vers=0;; vers++) {
225 			sprint(b, "/n/%s/%.4d/%.2d%.2d%d", ndump,
226 				tm->year+1900, tm->mon+1, tm->mday, vers+1);
227 			dir = dirstat(b);
228 			if(dir){
229 				mtime = dir->mtime;
230 				free(dir);
231 			}
232 			if(dir==nil || mtime > t0)
233 				break;
234 			if(verb)
235 				print("%ld later %s\n", mtime, b);
236 		}
237 		sprint(b, "/n/%s/%.4d/%.2d%.2d%s", ndump,
238 			tm->year+1900, tm->mon+1, tm->mday, f);
239 		if(vers)
240 			sprint(b, "/n/%s/%.4d/%.2d%.2d%d%s", ndump,
241 				tm->year+1900, tm->mon+1, tm->mday, vers, f);
242 		return;
243 	}
244 	strcpy(b, "XXX");	/* error */
245 }
246 
247 char*
248 prtime(ulong t)
249 {
250 	static char buf[100];
251 	char *b;
252 	Tm *tm;
253 
254 	if(uflag)
255 		tm = gmtime(t);
256 	else
257 		tm = localtime(t);
258 	b = asctime(tm);
259 	memcpy(buf, b+4, 24);
260 	buf[24] = 0;
261 	return buf;
262 }
263 
264 long
265 starttime(char *s)
266 {
267 	Tm *tm;
268 	long t, dt;
269 	int i, yr, mo, da;
270 
271 	t = time(0);
272 	if(s == 0)
273 		return t;
274 	for(i=0; s[i]; i++)
275 		if(s[i] < '0' || s[i] > '9') {
276 			fprint(2, "bad start time: %s\n", s);
277 			return t;
278 		}
279 	if(strlen(s)==6){
280 		yr = (s[0]-'0')*10 + s[1]-'0';
281 		mo = (s[2]-'0')*10 + s[3]-'0' - 1;
282 		da = (s[4]-'0')*10 + s[5]-'0';
283 		if(yr < 70)
284 			yr += 100;
285 	}else if(strlen(s)==8){
286 		yr = (((s[0]-'0')*10 + s[1]-'0')*10 + s[2]-'0')*10 + s[3]-'0';
287 		yr -= 1900;
288 		mo = (s[4]-'0')*10 + s[5]-'0' - 1;
289 		da = (s[6]-'0')*10 + s[7]-'0';
290 	}else{
291 		fprint(2, "bad start time: %s\n", s);
292 		return t;
293 	}
294 	t = 0;
295 	dt = YEAR(10);
296 	for(i=0; i<50; i++) {
297 		tm = localtime(t+dt);
298 		if(yr > tm->year ||
299 		  (yr == tm->year && mo > tm->mon) ||
300 		  (yr == tm->year && mo == tm->mon) && da > tm->mday) {
301 			t += dt;
302 			continue;
303 		}
304 		dt /= 2;
305 		if(dt == 0)
306 			break;
307 	}
308 	t += HOUR(12);	/* .5 day to get to noon of argument */
309 	return t;
310 }
311