121704Sdist /*
221704Sdist  * Copyright (c) 1980 Regents of the University of California.
321704Sdist  * All rights reserved.  The Berkeley software License Agreement
421704Sdist  * specifies the terms and conditions for redistribution.
521704Sdist  */
621704Sdist 
721704Sdist #ifndef lint
822752Sbloom char *copyright =
921704Sdist "@(#) Copyright (c) 1980 Regents of the University of California.\n\
1021704Sdist  All rights reserved.\n";
1121704Sdist #endif not lint
1221704Sdist 
1321704Sdist #ifndef lint
14*25941Sbloom static char *sccsid = "@(#)ex3.7preserve.c	7.13 (Berkeley) 01/22/86";
1521704Sdist #endif not lint
1621704Sdist 
17460Smark #include <stdio.h>
18460Smark #include <ctype.h>
1912956Sralph #include <sys/param.h>
20460Smark #include <sys/stat.h>
21460Smark #include <sys/dir.h>
22460Smark #include <pwd.h>
2313759Ssam #include "uparm.h"
2413759Ssam 
253995Smark #define TMP	"/tmp"
26460Smark 
27483Smark #ifdef VMUNIX
28483Smark #define	HBLKS	2
294001Smark #else
304001Smark #define HBLKS	1
31483Smark #endif
32483Smark 
333995Smark char xstr[1];			/* make loader happy */
343995Smark 
35460Smark /*
36460Smark  * Expreserve - preserve a file in usrpath(preserve)
37460Smark  * Bill Joy UCB November 13, 1977
38460Smark  *
39460Smark  * This routine is very naive - it doesn't remove anything from
4013759Ssam  * usrpath(preserve)... this may mean that we leave
4113759Ssam  * stuff there... the danger in doing anything with usrpath(preserve)
42460Smark  * is that the clock may be screwed up and we may get confused.
43460Smark  *
44460Smark  * We are called in two ways - first from the editor with no argumentss
45460Smark  * and the standard input open on the temp file. Second with an argument
46460Smark  * to preserve the entire contents of /tmp (root only).
47460Smark  *
48460Smark  * BUG: should do something about preserving Rx... (register contents)
49460Smark  *      temporaries.
50460Smark  */
51460Smark 
52483Smark #ifndef VMUNIX
53460Smark #define	LBLKS	125
54483Smark #else
55483Smark #define	LBLKS	900
56483Smark #endif
57460Smark #define	FNSIZE	128
58460Smark 
59460Smark struct 	header {
60460Smark 	time_t	Time;			/* Time temp file last updated */
614051Smark 	int	Uid;			/* This users identity */
62483Smark #ifndef VMUNIX
63460Smark 	short	Flines;			/* Number of lines in file */
64483Smark #else
65483Smark 	int	Flines;
66483Smark #endif
67460Smark 	char	Savedfile[FNSIZE];	/* The current file name */
68460Smark 	short	Blocks[LBLKS];		/* Blocks where line pointers stashed */
69460Smark } H;
70460Smark 
71460Smark #ifdef	lint
72460Smark #define	ignore(a)	Ignore(a)
73460Smark #define	ignorl(a)	Ignorl(a)
74460Smark #else
75460Smark #define	ignore(a)	a
76460Smark #define	ignorl(a)	a
77460Smark #endif
78460Smark 
79460Smark struct	passwd *getpwuid();
80460Smark off_t	lseek();
81460Smark FILE	*popen();
82460Smark 
83460Smark #define eq(a, b) strcmp(a, b) == 0
84460Smark 
85460Smark main(argc)
86460Smark 	int argc;
87460Smark {
8812956Sralph 	register DIR *tf;
8912956Sralph 	struct direct *dirent;
90460Smark 	struct stat stbuf;
91460Smark 
92460Smark 	/*
93460Smark 	 * If only one argument, then preserve the standard input.
94460Smark 	 */
95460Smark 	if (argc == 1) {
96460Smark 		if (copyout((char *) 0))
97460Smark 			exit(1);
98460Smark 		exit(0);
99460Smark 	}
100460Smark 
101460Smark 	/*
102460Smark 	 * If not super user, then can only preserve standard input.
103460Smark 	 */
104460Smark 	if (getuid()) {
105460Smark 		fprintf(stderr, "NOT super user\n");
106460Smark 		exit(1);
107460Smark 	}
108460Smark 
109460Smark 	/*
110460Smark 	 * ... else preserve all the stuff in /tmp, removing
111460Smark 	 * it as we go.
112460Smark 	 */
1133979Smark 	if (chdir(TMP) < 0) {
1143979Smark 		perror(TMP);
1153995Smark 		exit(1);
1163995Smark 	}
117460Smark 
11812956Sralph 	tf = opendir(".");
119460Smark 	if (tf == NULL) {
1203995Smark 		perror(TMP);
121460Smark 		exit(1);
1223995Smark 	}
12312956Sralph 	while ((dirent = readdir(tf)) != NULL) {
12413759Ssam 		/* Ex temporaries must begin with Ex. */
12513759Ssam 		if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
126460Smark 			continue;
12712956Sralph 		if (stat(dirent->d_name, &stbuf))
128460Smark 			continue;
129460Smark 		if ((stbuf.st_mode & S_IFMT) != S_IFREG)
130460Smark 			continue;
131460Smark 		/*
132460Smark 		 * Save the bastard.
133460Smark 		 */
13412956Sralph 		ignore(copyout(dirent->d_name));
135460Smark 	}
13612956Sralph 	closedir(tf);
137460Smark 	exit(0);
138460Smark }
139460Smark 
140460Smark char	pattern[] =	usrpath(preserve/Exaa`XXXXX);
141460Smark 
142460Smark /*
143460Smark  * Copy file name into usrpath(preserve)/...
144460Smark  * If name is (char *) 0, then do the standard input.
145460Smark  * We make some checks on the input to make sure it is
146460Smark  * really an editor temporary, generate a name for the
147460Smark  * file (this is the slowest thing since we must stat
148460Smark  * to find a unique name), and finally copy the file.
149460Smark  */
150460Smark copyout(name)
151460Smark 	char *name;
152460Smark {
153460Smark 	int i;
154460Smark 	static int reenter;
155460Smark 	char buf[BUFSIZ];
156460Smark 
157460Smark 	/*
158460Smark 	 * The first time we put in the digits of our
159460Smark 	 * process number at the end of the pattern.
160460Smark 	 */
161460Smark 	if (reenter == 0) {
162460Smark 		mkdigits(pattern);
163460Smark 		reenter++;
164460Smark 	}
165460Smark 
166460Smark 	/*
167460Smark 	 * If a file name was given, make it the standard
168460Smark 	 * input if possible.
169460Smark 	 */
170460Smark 	if (name != 0) {
171460Smark 		ignore(close(0));
172460Smark 		/*
173460Smark 		 * Need read/write access for arcane reasons
174460Smark 		 * (see below).
175460Smark 		 */
176460Smark 		if (open(name, 2) < 0)
177460Smark 			return (-1);
178460Smark 	}
179460Smark 
180460Smark 	/*
181460Smark 	 * Get the header block.
182460Smark 	 */
183460Smark 	ignorl(lseek(0, 0l, 0));
184460Smark 	if (read(0, (char *) &H, sizeof H) != sizeof H) {
185460Smark format:
186460Smark 		if (name == 0)
1871540Smark 			fprintf(stderr, "Buffer format error\t");
188460Smark 		return (-1);
189460Smark 	}
190460Smark 
191460Smark 	/*
192460Smark 	 * Consistency checsks so we don't copy out garbage.
193460Smark 	 */
194460Smark 	if (H.Flines < 0) {
195460Smark #ifdef DEBUG
196460Smark 		fprintf(stderr, "Negative number of lines\n");
197460Smark #endif
198460Smark 		goto format;
199460Smark 	}
200483Smark 	if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
201460Smark #ifdef DEBUG
202460Smark 		fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
203460Smark #endif
204460Smark 		goto format;
205460Smark 	}
206460Smark 	if (name == 0 && H.Uid != getuid()) {
207460Smark #ifdef DEBUG
208460Smark 		fprintf(stderr, "Wrong user-id\n");
209460Smark #endif
210460Smark 		goto format;
211460Smark 	}
212460Smark 	if (lseek(0, 0l, 0)) {
213460Smark #ifdef DEBUG
214460Smark 		fprintf(stderr, "Negative number of lines\n");
215460Smark #endif
216460Smark 		goto format;
217460Smark 	}
218460Smark 
219460Smark 	/*
220460Smark 	 * If no name was assigned to the file, then give it the name
221460Smark 	 * LOST, by putting this in the header.
222460Smark 	 */
223460Smark 	if (H.Savedfile[0] == 0) {
224460Smark 		strcpy(H.Savedfile, "LOST");
225460Smark 		ignore(write(0, (char *) &H, sizeof H));
226460Smark 		H.Savedfile[0] = 0;
227460Smark 		lseek(0, 0l, 0);
228460Smark 	}
229460Smark 
230460Smark 	/*
231460Smark 	 * File is good.  Get a name and create a file for the copy.
232460Smark 	 */
233460Smark 	mknext(pattern);
234460Smark 	ignore(close(1));
235460Smark 	if (creat(pattern, 0600) < 0) {
236460Smark 		if (name == 0)
237460Smark 			perror(pattern);
238460Smark 		return (1);
239460Smark 	}
240460Smark 
241460Smark 	/*
242460Smark 	 * Make the target be owned by the owner of the file.
243460Smark 	 */
244460Smark 	ignore(chown(pattern, H.Uid, 0));
245460Smark 
246460Smark 	/*
247460Smark 	 * Copy the file.
248460Smark 	 */
249460Smark 	for (;;) {
250460Smark 		i = read(0, buf, BUFSIZ);
251460Smark 		if (i < 0) {
252460Smark 			if (name)
253460Smark 				perror("Buffer read error");
254460Smark 			ignore(unlink(pattern));
255460Smark 			return (-1);
256460Smark 		}
257460Smark 		if (i == 0) {
258460Smark 			if (name)
259460Smark 				ignore(unlink(name));
26016045Srrh 			notify(H.Uid, H.Savedfile, (int) name, H.Time);
261460Smark 			return (0);
262460Smark 		}
263460Smark 		if (write(1, buf, i) != i) {
264460Smark 			if (name == 0)
265460Smark 				perror(pattern);
266460Smark 			unlink(pattern);
267460Smark 			return (-1);
268460Smark 		}
269460Smark 	}
270460Smark }
271460Smark 
272460Smark /*
273460Smark  * Blast the last 5 characters of cp to be the process number.
274460Smark  */
275460Smark mkdigits(cp)
276460Smark 	char *cp;
277460Smark {
278460Smark 	register int i, j;
279460Smark 
280460Smark 	for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
281460Smark 		*--cp = i % 10 | '0';
282460Smark }
283460Smark 
284460Smark /*
285460Smark  * Make the name in cp be unique by clobbering up to
286460Smark  * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
287460Smark  * Mktemp gets weird names too quickly to be useful here.
288460Smark  */
289460Smark mknext(cp)
290460Smark 	char *cp;
291460Smark {
292460Smark 	char *dcp;
293460Smark 	struct stat stb;
294460Smark 
295460Smark 	dcp = cp + strlen(cp) - 1;
296460Smark 	while (isdigit(*dcp))
297460Smark 		dcp--;
298460Smark whoops:
299460Smark 	if (dcp[0] == 'z') {
300460Smark 		dcp[0] = 'a';
301460Smark 		if (dcp[-1] == 'z') {
302460Smark 			dcp[-1] = 'a';
303460Smark 			if (dcp[-2] == 'z')
3041540Smark 				fprintf(stderr, "Can't find a name\t");
305460Smark 			dcp[-2]++;
306460Smark 		} else
307460Smark 			dcp[-1]++;
308460Smark 	} else
309460Smark 		dcp[0]++;
310460Smark 	if (stat(cp, &stb) == 0)
311460Smark 		goto whoops;
312460Smark }
313460Smark 
314460Smark /*
315460Smark  * Notify user uid that his file fname has been saved.
316460Smark  */
31716045Srrh notify(uid, fname, flag, time)
318460Smark 	int uid;
319460Smark 	char *fname;
32016045Srrh 	time_t	time;
321460Smark {
322460Smark 	struct passwd *pp = getpwuid(uid);
323460Smark 	register FILE *mf;
32416045Srrh 	char	cmd[BUFSIZ];
32516045Srrh 	char	hostname[128];
32616045Srrh 	char	croak[128];
32716045Srrh 	char	*timestamp, *ctime();
328460Smark 
329460Smark 	if (pp == NULL)
330460Smark 		return;
33116045Srrh 	gethostname(hostname, sizeof(hostname));
33216045Srrh 	timestamp = ctime(&time);
33316045Srrh 	timestamp[16] = 0;	/* blast from seconds on */
33414625Smckusick 	sprintf(cmd, "/bin/mail %s", pp->pw_name);
33517462Sralph 	setuid(getuid());
336460Smark 	mf = popen(cmd, "w");
337460Smark 	if (mf == NULL)
338460Smark 		return;
339460Smark 	setbuf(mf, cmd);
34016045Srrh 	/*
34116045Srrh 	 *	flag says how the editor croaked:
34216045Srrh 	 * "the editor was killed" is perhaps still not an ideal
34316045Srrh 	 * error message.  Usually, either it was forcably terminated
34416045Srrh 	 * or the phone was hung up, but we don't know which.
34516045Srrh 	 */
34616045Srrh 	sprintf(croak, flag
34716045Srrh 		? "the system went down"
34816045Srrh 		: "the editor was killed");
349460Smark 	if (fname[0] == 0) {
350*25941Sbloom 		fname = "LOST";
351460Smark 		fprintf(mf,
35216045Srrh "Subject: editor saved ``LOST''\n");
353460Smark 		fprintf(mf,
35416045Srrh "You were editing a file without a name\n");
355460Smark 		fprintf(mf,
35616045Srrh "at <%s> on the machine ``%s'' when %s.\n", timestamp, hostname, croak);
35716045Srrh 		fprintf(mf,
35816045Srrh "Since the file had no name, it has been named \"LOST\".\n");
35916045Srrh 	} else {
36016045Srrh 		fprintf(mf,
36116045Srrh "Subject: editor saved ``%s''\n", fname);
36216045Srrh 		fprintf(mf,
36316045Srrh "You were editing the file \"%s\"\n", fname);
36416045Srrh 		fprintf(mf,
36516045Srrh "at <%s> on the machine ``%s''\n", timestamp, hostname);
36616045Srrh 		fprintf(mf,
36716045Srrh "when %s.\n", croak);
36816045Srrh 	}
369460Smark 	fprintf(mf,
37016045Srrh "\nYou can retrieve most of your changes to this file\n");
371460Smark 	fprintf(mf,
37216045Srrh "using the \"recover\" command of the editor.\n");
373460Smark 	fprintf(mf,
37416045Srrh "An easy way to do this is to give the command \"vi -r %s\".\n", fname);
37516045Srrh 	fprintf(mf,
37616045Srrh "This method also works using \"ex\" and \"edit\".\n");
377460Smark 	pclose(mf);
378460Smark }
379460Smark 
380460Smark /*
381460Smark  *	people making love
382460Smark  *	never exactly the same
383460Smark  *	just like a snowflake
384460Smark  */
385460Smark 
386460Smark #ifdef lint
387460Smark Ignore(a)
388460Smark 	int a;
389460Smark {
390460Smark 
391460Smark 	a = a;
392460Smark }
393460Smark 
394460Smark Ignorl(a)
395460Smark 	long a;
396460Smark {
397460Smark 
398460Smark 	a = a;
399460Smark }
400460Smark #endif
401