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