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