13979Smark /* Copyright (c) 1981 Regents of the University of California */
2*12956Sralph static char *sccsid = "@(#)ex3.7preserve.c	7.6	06/10/83";
3460Smark #include <stdio.h>
4460Smark #include <ctype.h>
5*12956Sralph #include <sys/param.h>
6460Smark #include <sys/stat.h>
7460Smark #include <sys/dir.h>
8460Smark #include <pwd.h>
9460Smark #include "local/uparm.h"
103979Smark 				/* mjm: "/tmp" --> TMP */
113995Smark #define TMP	"/tmp"
12460Smark 
13483Smark #ifdef VMUNIX
14483Smark #define	HBLKS	2
154001Smark #else
164001Smark #define HBLKS	1
17483Smark #endif
18483Smark 
193995Smark char xstr[1];			/* make loader happy */
203995Smark 
21460Smark /*
22460Smark  * Expreserve - preserve a file in usrpath(preserve)
23460Smark  * Bill Joy UCB November 13, 1977
24460Smark  *
25460Smark  * This routine is very naive - it doesn't remove anything from
263995Smark  * usrpath(preserve)... this may mean that we  * stuff there... the danger in doing anything with usrpath(preserve)
27460Smark  * is that the clock may be screwed up and we may get confused.
28460Smark  *
29460Smark  * We are called in two ways - first from the editor with no argumentss
30460Smark  * and the standard input open on the temp file. Second with an argument
31460Smark  * to preserve the entire contents of /tmp (root only).
32460Smark  *
33460Smark  * BUG: should do something about preserving Rx... (register contents)
34460Smark  *      temporaries.
35460Smark  */
36460Smark 
37483Smark #ifndef VMUNIX
38460Smark #define	LBLKS	125
39483Smark #else
40483Smark #define	LBLKS	900
41483Smark #endif
42460Smark #define	FNSIZE	128
43460Smark 
44460Smark struct 	header {
45460Smark 	time_t	Time;			/* Time temp file last updated */
464051Smark 	int	Uid;			/* This users identity */
47483Smark #ifndef VMUNIX
48460Smark 	short	Flines;			/* Number of lines in file */
49483Smark #else
50483Smark 	int	Flines;
51483Smark #endif
52460Smark 	char	Savedfile[FNSIZE];	/* The current file name */
53460Smark 	short	Blocks[LBLKS];		/* Blocks where line pointers stashed */
54460Smark } H;
55460Smark 
56460Smark #ifdef	lint
57460Smark #define	ignore(a)	Ignore(a)
58460Smark #define	ignorl(a)	Ignorl(a)
59460Smark #else
60460Smark #define	ignore(a)	a
61460Smark #define	ignorl(a)	a
62460Smark #endif
63460Smark 
64460Smark struct	passwd *getpwuid();
65460Smark off_t	lseek();
66460Smark FILE	*popen();
67460Smark 
68460Smark #define eq(a, b) strcmp(a, b) == 0
69460Smark 
70460Smark main(argc)
71460Smark 	int argc;
72460Smark {
73*12956Sralph 	register DIR *tf;
74*12956Sralph 	struct direct *dirent;
75460Smark 	struct stat stbuf;
76460Smark 
77460Smark 	/*
78460Smark 	 * If only one argument, then preserve the standard input.
79460Smark 	 */
80460Smark 	if (argc == 1) {
81460Smark 		if (copyout((char *) 0))
82460Smark 			exit(1);
83460Smark 		exit(0);
84460Smark 	}
85460Smark 
86460Smark 	/*
87460Smark 	 * If not super user, then can only preserve standard input.
88460Smark 	 */
89460Smark 	if (getuid()) {
90460Smark 		fprintf(stderr, "NOT super user\n");
91460Smark 		exit(1);
92460Smark 	}
93460Smark 
94460Smark 	/*
95460Smark 	 * ... else preserve all the stuff in /tmp, removing
96460Smark 	 * it as we go.
97460Smark 	 */
983979Smark 	if (chdir(TMP) < 0) {
993979Smark 		perror(TMP);
1003995Smark 		exit(1);
1013995Smark 	}
102460Smark 
103*12956Sralph 	tf = opendir(".");
104460Smark 	if (tf == NULL) {
1053995Smark 		perror(TMP);
106460Smark 		exit(1);
1073995Smark 	}
108*12956Sralph 	while ((dirent = readdir(tf)) != NULL) {
109460Smark 		/*
110460Smark 		 * Ex temporaries must begin with Ex;
111460Smark 		 * we check that the 10th character of the name is null
112460Smark 		 * so we won't have to worry about non-null terminated names
113460Smark 		 * later on.
114460Smark 		 */
115*12956Sralph 		if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x' || dirent->d_name[10])
116460Smark 			continue;
117*12956Sralph 		if (stat(dirent->d_name, &stbuf))
118460Smark 			continue;
119460Smark 		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
120460Smark 			continue;
121460Smark 		/*
122460Smark 		 * Save the bastard.
123460Smark 		 */
124*12956Sralph 		ignore(copyout(dirent->d_name));
125460Smark 	}
126*12956Sralph 	closedir(tf);
127460Smark 	exit(0);
128460Smark }
129460Smark 
130460Smark char	pattern[] =	usrpath(preserve/Exaa`XXXXX);
131460Smark 
132460Smark /*
133460Smark  * Copy file name into usrpath(preserve)/...
134460Smark  * If name is (char *) 0, then do the standard input.
135460Smark  * We make some checks on the input to make sure it is
136460Smark  * really an editor temporary, generate a name for the
137460Smark  * file (this is the slowest thing since we must stat
138460Smark  * to find a unique name), and finally copy the file.
139460Smark  */
140460Smark copyout(name)
141460Smark 	char *name;
142460Smark {
143460Smark 	int i;
144460Smark 	static int reenter;
145460Smark 	char buf[BUFSIZ];
146460Smark 
147460Smark 	/*
148460Smark 	 * The first time we put in the digits of our
149460Smark 	 * process number at the end of the pattern.
150460Smark 	 */
151460Smark 	if (reenter == 0) {
152460Smark 		mkdigits(pattern);
153460Smark 		reenter++;
154460Smark 	}
155460Smark 
156460Smark 	/*
157460Smark 	 * If a file name was given, make it the standard
158460Smark 	 * input if possible.
159460Smark 	 */
160460Smark 	if (name != 0) {
161460Smark 		ignore(close(0));
162460Smark 		/*
163460Smark 		 * Need read/write access for arcane reasons
164460Smark 		 * (see below).
165460Smark 		 */
166460Smark 		if (open(name, 2) < 0)
167460Smark 			return (-1);
168460Smark 	}
169460Smark 
170460Smark 	/*
171460Smark 	 * Get the header block.
172460Smark 	 */
173460Smark 	ignorl(lseek(0, 0l, 0));
174460Smark 	if (read(0, (char *) &H, sizeof H) != sizeof H) {
175460Smark format:
176460Smark 		if (name == 0)
1771540Smark 			fprintf(stderr, "Buffer format error\t");
178460Smark 		return (-1);
179460Smark 	}
180460Smark 
181460Smark 	/*
182460Smark 	 * Consistency checsks so we don't copy out garbage.
183460Smark 	 */
184460Smark 	if (H.Flines < 0) {
185460Smark #ifdef DEBUG
186460Smark 		fprintf(stderr, "Negative number of lines\n");
187460Smark #endif
188460Smark 		goto format;
189460Smark 	}
190483Smark 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
191460Smark #ifdef DEBUG
192460Smark 		fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
193460Smark #endif
194460Smark 		goto format;
195460Smark 	}
196460Smark 	if (name == 0 && H.Uid != getuid()) {
197460Smark #ifdef DEBUG
198460Smark 		fprintf(stderr, "Wrong user-id\n");
199460Smark #endif
200460Smark 		goto format;
201460Smark 	}
202460Smark 	if (lseek(0, 0l, 0)) {
203460Smark #ifdef DEBUG
204460Smark 		fprintf(stderr, "Negative number of lines\n");
205460Smark #endif
206460Smark 		goto format;
207460Smark 	}
208460Smark 
209460Smark 	/*
210460Smark 	 * If no name was assigned to the file, then give it the name
211460Smark 	 * LOST, by putting this in the header.
212460Smark 	 */
213460Smark 	if (H.Savedfile[0] == 0) {
214460Smark 		strcpy(H.Savedfile, "LOST");
215460Smark 		ignore(write(0, (char *) &H, sizeof H));
216460Smark 		H.Savedfile[0] = 0;
217460Smark 		lseek(0, 0l, 0);
218460Smark 	}
219460Smark 
220460Smark 	/*
221460Smark 	 * File is good.  Get a name and create a file for the copy.
222460Smark 	 */
223460Smark 	mknext(pattern);
224460Smark 	ignore(close(1));
225460Smark 	if (creat(pattern, 0600) < 0) {
226460Smark 		if (name == 0)
227460Smark 			perror(pattern);
228460Smark 		return (1);
229460Smark 	}
230460Smark 
231460Smark 	/*
232460Smark 	 * Make the target be owned by the owner of the file.
233460Smark 	 */
234460Smark 	ignore(chown(pattern, H.Uid, 0));
235460Smark 
236460Smark 	/*
237460Smark 	 * Copy the file.
238460Smark 	 */
239460Smark 	for (;;) {
240460Smark 		i = read(0, buf, BUFSIZ);
241460Smark 		if (i < 0) {
242460Smark 			if (name)
243460Smark 				perror("Buffer read error");
244460Smark 			ignore(unlink(pattern));
245460Smark 			return (-1);
246460Smark 		}
247460Smark 		if (i == 0) {
248460Smark 			if (name)
249460Smark 				ignore(unlink(name));
250460Smark 			notify(H.Uid, H.Savedfile, (int) name);
251460Smark 			return (0);
252460Smark 		}
253460Smark 		if (write(1, buf, i) != i) {
254460Smark 			if (name == 0)
255460Smark 				perror(pattern);
256460Smark 			unlink(pattern);
257460Smark 			return (-1);
258460Smark 		}
259460Smark 	}
260460Smark }
261460Smark 
262460Smark /*
263460Smark  * Blast the last 5 characters of cp to be the process number.
264460Smark  */
265460Smark mkdigits(cp)
266460Smark 	char *cp;
267460Smark {
268460Smark 	register int i, j;
269460Smark 
270460Smark 	for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
271460Smark 		*--cp = i % 10 | '0';
272460Smark }
273460Smark 
274460Smark /*
275460Smark  * Make the name in cp be unique by clobbering up to
276460Smark  * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
277460Smark  * Mktemp gets weird names too quickly to be useful here.
278460Smark  */
279460Smark mknext(cp)
280460Smark 	char *cp;
281460Smark {
282460Smark 	char *dcp;
283460Smark 	struct stat stb;
284460Smark 
285460Smark 	dcp = cp + strlen(cp) - 1;
286460Smark 	while (isdigit(*dcp))
287460Smark 		dcp--;
288460Smark whoops:
289460Smark 	if (dcp[0] == 'z') {
290460Smark 		dcp[0] = 'a';
291460Smark 		if (dcp[-1] == 'z') {
292460Smark 			dcp[-1] = 'a';
293460Smark 			if (dcp[-2] == 'z')
2941540Smark 				fprintf(stderr, "Can't find a name\t");
295460Smark 			dcp[-2]++;
296460Smark 		} else
297460Smark 			dcp[-1]++;
298460Smark 	} else
299460Smark 		dcp[0]++;
300460Smark 	if (stat(cp, &stb) == 0)
301460Smark 		goto whoops;
302460Smark }
303460Smark 
304460Smark /*
305460Smark  * Notify user uid that his file fname has been saved.
306460Smark  */
307460Smark notify(uid, fname, flag)
308460Smark 	int uid;
309460Smark 	char *fname;
310460Smark {
311460Smark 	struct passwd *pp = getpwuid(uid);
312460Smark 	register FILE *mf;
313460Smark 	char cmd[BUFSIZ];
314460Smark 
315460Smark 	if (pp == NULL)
316460Smark 		return;
317460Smark 	sprintf(cmd, "mail %s", pp->pw_name);
318460Smark 	mf = popen(cmd, "w");
319460Smark 	if (mf == NULL)
320460Smark 		return;
321460Smark 	setbuf(mf, cmd);
322460Smark 	if (fname[0] == 0) {
323460Smark 		fprintf(mf,
324460Smark "A copy of an editor buffer of yours was saved when %s.\n",
3251540Smark 		flag ? "the system went down" : "the editor was killed");
326460Smark 		fprintf(mf,
327460Smark "No name was associated with this buffer so it has been named \"LOST\".\n");
328460Smark 	} else
329460Smark 		fprintf(mf,
330460Smark "A copy of an editor buffer of your file \"%s\"\nwas saved when %s.\n", fname,
3311540Smark 		/*
3321540Smark 		 * "the editor was killed" is perhaps still not an ideal
3331540Smark 		 * error message.  Usually, either it was forcably terminated
3341540Smark 		 * or the phone was hung up, but we don't know which.
3351540Smark 		 */
3361540Smark 		flag ? "the system went down" : "the editor was killed");
337460Smark 	fprintf(mf,
338460Smark "This buffer can be retrieved using the \"recover\" command of the editor.\n");
339460Smark 	fprintf(mf,
340460Smark "An easy way to do this is to give the command \"ex -r %s\".\n",fname);
341460Smark 	fprintf(mf,
342460Smark "This works for \"edit\" and \"vi\" also.\n");
343460Smark 	pclose(mf);
344460Smark }
345460Smark 
346460Smark /*
347460Smark  *	people making love
348460Smark  *	never exactly the same
349460Smark  *	just like a snowflake
350460Smark  */
351460Smark 
352460Smark #ifdef lint
353460Smark Ignore(a)
354460Smark 	int a;
355460Smark {
356460Smark 
357460Smark 	a = a;
358460Smark }
359460Smark 
360460Smark Ignorl(a)
361460Smark 	long a;
362460Smark {
363460Smark 
364460Smark 	a = a;
365460Smark }
366460Smark #endif
367