13979Smark /* Copyright (c) 1981 Regents of the University of California */
2*4051Smark static char *sccsid = "@(#)ex3.7preserve.c	7.5	08/02/81";
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"
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 */
46*4051Smark 	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 {
73460Smark 	register FILE *tf;
74460Smark 	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 
103460Smark 	tf = fopen(".", "r");
104460Smark 	if (tf == NULL) {
1053995Smark 		perror(TMP);
106460Smark 		exit(1);
1073995Smark 	}
108460Smark 	while (fread((char *) &dirent, sizeof dirent, 1, tf) == 1) {
109460Smark 		if (dirent.d_ino == 0)
110460Smark 			continue;
111460Smark 		/*
112460Smark 		 * Ex temporaries must begin with Ex;
113460Smark 		 * we check that the 10th character of the name is null
114460Smark 		 * so we won't have to worry about non-null terminated names
115460Smark 		 * later on.
116460Smark 		 */
117460Smark 		if (dirent.d_name[0] != 'E' || dirent.d_name[1] != 'x' || dirent.d_name[10])
118460Smark 			continue;
119460Smark 		if (stat(dirent.d_name, &stbuf))
120460Smark 			continue;
121460Smark 		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
122460Smark 			continue;
123460Smark 		/*
124460Smark 		 * Save the bastard.
125460Smark 		 */
126460Smark 		ignore(copyout(dirent.d_name));
127460Smark 	}
128460Smark 	exit(0);
129460Smark }
130460Smark 
131460Smark char	pattern[] =	usrpath(preserve/Exaa`XXXXX);
132460Smark 
133460Smark /*
134460Smark  * Copy file name into usrpath(preserve)/...
135460Smark  * If name is (char *) 0, then do the standard input.
136460Smark  * We make some checks on the input to make sure it is
137460Smark  * really an editor temporary, generate a name for the
138460Smark  * file (this is the slowest thing since we must stat
139460Smark  * to find a unique name), and finally copy the file.
140460Smark  */
141460Smark copyout(name)
142460Smark 	char *name;
143460Smark {
144460Smark 	int i;
145460Smark 	static int reenter;
146460Smark 	char buf[BUFSIZ];
147460Smark 
148460Smark 	/*
149460Smark 	 * The first time we put in the digits of our
150460Smark 	 * process number at the end of the pattern.
151460Smark 	 */
152460Smark 	if (reenter == 0) {
153460Smark 		mkdigits(pattern);
154460Smark 		reenter++;
155460Smark 	}
156460Smark 
157460Smark 	/*
158460Smark 	 * If a file name was given, make it the standard
159460Smark 	 * input if possible.
160460Smark 	 */
161460Smark 	if (name != 0) {
162460Smark 		ignore(close(0));
163460Smark 		/*
164460Smark 		 * Need read/write access for arcane reasons
165460Smark 		 * (see below).
166460Smark 		 */
167460Smark 		if (open(name, 2) < 0)
168460Smark 			return (-1);
169460Smark 	}
170460Smark 
171460Smark 	/*
172460Smark 	 * Get the header block.
173460Smark 	 */
174460Smark 	ignorl(lseek(0, 0l, 0));
175460Smark 	if (read(0, (char *) &H, sizeof H) != sizeof H) {
176460Smark format:
177460Smark 		if (name == 0)
1781540Smark 			fprintf(stderr, "Buffer format error\t");
179460Smark 		return (-1);
180460Smark 	}
181460Smark 
182460Smark 	/*
183460Smark 	 * Consistency checsks so we don't copy out garbage.
184460Smark 	 */
185460Smark 	if (H.Flines < 0) {
186460Smark #ifdef DEBUG
187460Smark 		fprintf(stderr, "Negative number of lines\n");
188460Smark #endif
189460Smark 		goto format;
190460Smark 	}
191483Smark 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
192460Smark #ifdef DEBUG
193460Smark 		fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
194460Smark #endif
195460Smark 		goto format;
196460Smark 	}
197460Smark 	if (name == 0 && H.Uid != getuid()) {
198460Smark #ifdef DEBUG
199460Smark 		fprintf(stderr, "Wrong user-id\n");
200460Smark #endif
201460Smark 		goto format;
202460Smark 	}
203460Smark 	if (lseek(0, 0l, 0)) {
204460Smark #ifdef DEBUG
205460Smark 		fprintf(stderr, "Negative number of lines\n");
206460Smark #endif
207460Smark 		goto format;
208460Smark 	}
209460Smark 
210460Smark 	/*
211460Smark 	 * If no name was assigned to the file, then give it the name
212460Smark 	 * LOST, by putting this in the header.
213460Smark 	 */
214460Smark 	if (H.Savedfile[0] == 0) {
215460Smark 		strcpy(H.Savedfile, "LOST");
216460Smark 		ignore(write(0, (char *) &H, sizeof H));
217460Smark 		H.Savedfile[0] = 0;
218460Smark 		lseek(0, 0l, 0);
219460Smark 	}
220460Smark 
221460Smark 	/*
222460Smark 	 * File is good.  Get a name and create a file for the copy.
223460Smark 	 */
224460Smark 	mknext(pattern);
225460Smark 	ignore(close(1));
226460Smark 	if (creat(pattern, 0600) < 0) {
227460Smark 		if (name == 0)
228460Smark 			perror(pattern);
229460Smark 		return (1);
230460Smark 	}
231460Smark 
232460Smark 	/*
233460Smark 	 * Make the target be owned by the owner of the file.
234460Smark 	 */
235460Smark 	ignore(chown(pattern, H.Uid, 0));
236460Smark 
237460Smark 	/*
238460Smark 	 * Copy the file.
239460Smark 	 */
240460Smark 	for (;;) {
241460Smark 		i = read(0, buf, BUFSIZ);
242460Smark 		if (i < 0) {
243460Smark 			if (name)
244460Smark 				perror("Buffer read error");
245460Smark 			ignore(unlink(pattern));
246460Smark 			return (-1);
247460Smark 		}
248460Smark 		if (i == 0) {
249460Smark 			if (name)
250460Smark 				ignore(unlink(name));
251460Smark 			notify(H.Uid, H.Savedfile, (int) name);
252460Smark 			return (0);
253460Smark 		}
254460Smark 		if (write(1, buf, i) != i) {
255460Smark 			if (name == 0)
256460Smark 				perror(pattern);
257460Smark 			unlink(pattern);
258460Smark 			return (-1);
259460Smark 		}
260460Smark 	}
261460Smark }
262460Smark 
263460Smark /*
264460Smark  * Blast the last 5 characters of cp to be the process number.
265460Smark  */
266460Smark mkdigits(cp)
267460Smark 	char *cp;
268460Smark {
269460Smark 	register int i, j;
270460Smark 
271460Smark 	for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
272460Smark 		*--cp = i % 10 | '0';
273460Smark }
274460Smark 
275460Smark /*
276460Smark  * Make the name in cp be unique by clobbering up to
277460Smark  * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
278460Smark  * Mktemp gets weird names too quickly to be useful here.
279460Smark  */
280460Smark mknext(cp)
281460Smark 	char *cp;
282460Smark {
283460Smark 	char *dcp;
284460Smark 	struct stat stb;
285460Smark 
286460Smark 	dcp = cp + strlen(cp) - 1;
287460Smark 	while (isdigit(*dcp))
288460Smark 		dcp--;
289460Smark whoops:
290460Smark 	if (dcp[0] == 'z') {
291460Smark 		dcp[0] = 'a';
292460Smark 		if (dcp[-1] == 'z') {
293460Smark 			dcp[-1] = 'a';
294460Smark 			if (dcp[-2] == 'z')
2951540Smark 				fprintf(stderr, "Can't find a name\t");
296460Smark 			dcp[-2]++;
297460Smark 		} else
298460Smark 			dcp[-1]++;
299460Smark 	} else
300460Smark 		dcp[0]++;
301460Smark 	if (stat(cp, &stb) == 0)
302460Smark 		goto whoops;
303460Smark }
304460Smark 
305460Smark /*
306460Smark  * Notify user uid that his file fname has been saved.
307460Smark  */
308460Smark notify(uid, fname, flag)
309460Smark 	int uid;
310460Smark 	char *fname;
311460Smark {
312460Smark 	struct passwd *pp = getpwuid(uid);
313460Smark 	register FILE *mf;
314460Smark 	char cmd[BUFSIZ];
315460Smark 
316460Smark 	if (pp == NULL)
317460Smark 		return;
318460Smark 	sprintf(cmd, "mail %s", pp->pw_name);
319460Smark 	mf = popen(cmd, "w");
320460Smark 	if (mf == NULL)
321460Smark 		return;
322460Smark 	setbuf(mf, cmd);
323460Smark 	if (fname[0] == 0) {
324460Smark 		fprintf(mf,
325460Smark "A copy of an editor buffer of yours was saved when %s.\n",
3261540Smark 		flag ? "the system went down" : "the editor was killed");
327460Smark 		fprintf(mf,
328460Smark "No name was associated with this buffer so it has been named \"LOST\".\n");
329460Smark 	} else
330460Smark 		fprintf(mf,
331460Smark "A copy of an editor buffer of your file \"%s\"\nwas saved when %s.\n", fname,
3321540Smark 		/*
3331540Smark 		 * "the editor was killed" is perhaps still not an ideal
3341540Smark 		 * error message.  Usually, either it was forcably terminated
3351540Smark 		 * or the phone was hung up, but we don't know which.
3361540Smark 		 */
3371540Smark 		flag ? "the system went down" : "the editor was killed");
338460Smark 	fprintf(mf,
339460Smark "This buffer can be retrieved using the \"recover\" command of the editor.\n");
340460Smark 	fprintf(mf,
341460Smark "An easy way to do this is to give the command \"ex -r %s\".\n",fname);
342460Smark 	fprintf(mf,
343460Smark "This works for \"edit\" and \"vi\" also.\n");
344460Smark 	pclose(mf);
345460Smark }
346460Smark 
347460Smark /*
348460Smark  *	people making love
349460Smark  *	never exactly the same
350460Smark  *	just like a snowflake
351460Smark  */
352460Smark 
353460Smark #ifdef lint
354460Smark Ignore(a)
355460Smark 	int a;
356460Smark {
357460Smark 
358460Smark 	a = a;
359460Smark }
360460Smark 
361460Smark Ignorl(a)
362460Smark 	long a;
363460Smark {
364460Smark 
365460Smark 	a = a;
366460Smark }
367460Smark #endif
368