1: /* Copyright (c) 1979 Regents of the University of California */
2: #ifdef V6
3: #include <retrofit.h>
4: #endif
5: #include <stdio.h>
6: #include <ctype.h>
7: #include <sys/types.h>
8: #include <sys/stat.h>
9: #include <sys/dir.h>
10: #include <pwd.h>
11: #include "local/uparm.h"
12:
13: /*
14: * Expreserve - preserve a file in usrpath(preserve)
15: * Bill Joy UCB November 13, 1977
16: *
17: * This routine is very naive - it doesn't remove anything from
18: * usrpath(preserve)... this may mean that we will be unable to preserve
19: * stuff there... the danger in doing anything with usrpath(preserve)
20: * is that the clock may be screwed up and we may get confused.
21: *
22: * We are called in two ways - first from the editor with no argumentss
23: * and the standard input open on the temp file. Second with an argument
24: * to preserve the entire contents of /tmp (root only).
25: *
26: * BUG: should do something about preserving Rx... (register contents)
27: * temporaries.
28: */
29:
30: #define LBLKS 125
31: #define FNSIZE 128
32:
33: struct {
34: time_t Time; /* Time temp file last updated */
35: short Uid; /* This users identity */
36: short Flines; /* Number of lines in file */
37: char Savedfile[FNSIZE]; /* The current file name */
38: short Blocks[LBLKS]; /* Blocks where line pointers stashed */
39: } H;
40:
41: #ifdef lint
42: #define ignore(a) Ignore(a)
43: #define ignorl(a) Ignorl(a)
44: #else
45: #define ignore(a) a
46: #define ignorl(a) a
47: #endif
48:
49: struct passwd *getpwuid();
50: #ifndef V6
51: off_t lseek();
52: #endif
53: FILE *popen();
54:
55: #define eq(a, b) strcmp(a, b) == 0
56:
57: main(argc)
58: int argc;
59: {
60: register FILE *tf;
61: struct direct dirent;
62: struct stat stbuf;
63:
64: /*
65: * If only one argument, then preserve the standard input.
66: */
67: if (argc == 1) {
68: if (copyout((char *) 0))
69: exit(1);
70: exit(0);
71: }
72:
73: /*
74: * If not super user, then can only preserve standard input.
75: */
76: if (getuid()) {
77: fprintf(stderr, "NOT super user\n");
78: exit(1);
79: }
80:
81: /*
82: * ... else preserve all the stuff in /tmp, removing
83: * it as we go.
84: */
85: if (chdir("/tmp") < 0) {
86: perror("/tmp");
87: exit(1);
88: }
89:
90: tf = fopen(".", "r");
91: if (tf == NULL) {
92: perror("/tmp");
93: exit(1);
94: }
95: while (fread((char *) &dirent, sizeof dirent, 1, tf) == 1) {
96: if (dirent.d_ino == 0)
97: continue;
98: /*
99: * Ex temporaries must begin with Ex;
100: * we check that the 10th character of the name is null
101: * so we won't have to worry about non-null terminated names
102: * later on.
103: */
104: if (dirent.d_name[0] != 'E' || dirent.d_name[1] != 'x' || dirent.d_name[10])
105: continue;
106: if (stat(dirent.d_name, &stbuf))
107: {
108: #ifdef DEBUG
109: fprintf(stderr, "stat(%s) fails\n", dirent.d_name);
110: #endif
111: continue;
112: }
113: if ((stbuf.st_mode & S_IFMT) != S_IFREG)
114: {
115: #ifdef DEBUG
116: fprintf(stderr, "mode(%s) = %o\n", dirent.d_name, stbuf.st_mode);
117: #endif
118: continue;
119: }
120: /*
121: * Save the bastard.
122: */
123: ignore(copyout(dirent.d_name));
124: }
125: exit(0);
126: }
127:
128: char pattern[] = usrpath(preserve/Exaa`XXXXX);
129:
130: /*
131: * Copy file name into usrpath(preserve)/...
132: * If name is (char *) 0, then do the standard input.
133: * We make some checks on the input to make sure it is
134: * really an editor temporary, generate a name for the
135: * file (this is the slowest thing since we must stat
136: * to find a unique name), and finally copy the file.
137: */
138: copyout(name)
139: char *name;
140: {
141: int i;
142: static int reenter;
143: char buf[BUFSIZ];
144:
145: /*
146: * The first time we put in the digits of our
147: * process number at the end of the pattern.
148: */
149: if (reenter == 0) {
150: mkdigits(pattern);
151: reenter++;
152: }
153:
154: /*
155: * If a file name was given, make it the standard
156: * input if possible.
157: */
158: if (name != 0) {
159: ignore(close(0));
160: /*
161: * Need read/write access for arcane reasons
162: * (see below).
163: */
164: if (open(name, 2) < 0)
165: {
166: #ifdef DEBUG
167: fprintf(stderr, "can't open %s\n", name);
168: #endif
169: return (-1);
170: }
171: }
172:
173: /*
174: * Get the header block.
175: */
176: ignorl(lseek(0, (long) 0, 0));
177: if (read(0, (char *) &H, sizeof H) != sizeof H) {
178: format:
179: if (name == 0)
180: fprintf(stderr, "Buffer format error\n");
181: return (-1);
182: }
183:
184: /*
185: * Consistency checsks so we don't copy out garbage.
186: */
187: if (H.Flines < 0) {
188: #ifdef DEBUG
189: fprintf(stderr, "Negative number of lines (%d)\n", H.Flines);
190: #endif
191: goto format;
192: }
193: if (H.Blocks[0] != 1 || H.Blocks[1] != 2) {
194: #ifdef DEBUG
195: fprintf(stderr, "Blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
196: #endif
197: goto format;
198: }
199: if (name == 0 && H.Uid != getuid()) {
200: #ifdef DEBUG
201: fprintf(stderr, "Wrong user-id\n");
202: #endif
203: goto format;
204: }
205: if (lseek(0, (long) 0, 0)) {
206: #ifdef DEBUG
207: fprintf(stderr, "Negative number of lines\n");
208: #endif
209: goto format;
210: }
211:
212: /*
213: * If no name was assigned to the file, then give it the name
214: * LOST, by putting this in the header.
215: */
216: if (H.Savedfile[0] == 0) {
217: strcpy(H.Savedfile, "LOST");
218: ignore(write(0, (char *) &H, sizeof H));
219: H.Savedfile[0] = 0;
220: lseek(0, 0l, 0);
221: }
222:
223: /*
224: * File is good. Get a name and create a file for the copy.
225: */
226: mknext(pattern);
227: ignore(close(1));
228: if (creat(pattern, 0600) < 0) {
229: if (name == 0)
230: perror(pattern);
231: return (1);
232: }
233:
234: /*
235: * Make the target be owned by the owner of the file.
236: */
237: ignore(chown(pattern, H.Uid, 0));
238:
239: /*
240: * Copy the file.
241: */
242: for (;;) {
243: i = read(0, buf, BUFSIZ);
244: if (i < 0) {
245: if (name)
246: perror("Buffer read error");
247: ignore(unlink(pattern));
248: return (-1);
249: }
250: if (i == 0) {
251: if (name)
252: ignore(unlink(name));
253: notify(H.Uid, H.Savedfile, (int) name);
254: return (0);
255: }
256: if (write(1, buf, i) != i) {
257: if (name == 0)
258: perror(pattern);
259: unlink(pattern);
260: return (-1);
261: }
262: }
263: }
264:
265: /*
266: * Blast the last 5 characters of cp to be the process number.
267: */
268: mkdigits(cp)
269: char *cp;
270: {
271: register int i, j;
272:
273: for (i = getpid(), j = 5, cp += strlen(cp); j > 0; i /= 10, j--)
274: *--cp = i % 10 | '0';
275: }
276:
277: /*
278: * Make the name in cp be unique by clobbering up to
279: * three alphabetic characters into a sequence of the form 'aab', 'aac', etc.
280: * Mktemp gets weird names too quickly to be useful here.
281: */
282: mknext(cp)
283: char *cp;
284: {
285: char *dcp;
286: struct stat stb;
287:
288: dcp = cp + strlen(cp) - 1;
289: while (isdigit(*dcp))
290: dcp--;
291: whoops:
292: if (dcp[0] == 'z') {
293: dcp[0] = 'a';
294: if (dcp[-1] == 'z') {
295: dcp[-1] = 'a';
296: if (dcp[-2] == 'z')
297: fprintf(stderr, "Can't find a name\n");
298: dcp[-2]++;
299: } else
300: dcp[-1]++;
301: } else
302: dcp[0]++;
303: if (stat(cp, &stb) == 0)
304: goto whoops;
305: }
306:
307: /*
308: * Notify user uid that his file fname has been saved.
309: */
310: notify(uid, fname, flag)
311: int uid;
312: char *fname;
313: {
314: struct passwd *pp = getpwuid(uid);
315: register FILE *mf;
316: char cmd[BUFSIZ];
317:
318: if (pp == NULL)
319: return;
320: sprintf(cmd, "/bin/mail %s", pp->pw_name);
321: mf = popen(cmd, "w");
322: if (mf == NULL)
323: return;
324: setbuf(mf, cmd);
325: if (fname[0] == 0) {
326: fprintf(mf,
327: "A copy of an editor buffer of yours was saved when %s.\n",
328: flag ? "the system went down" : "your phone was hung up");
329: fprintf(mf,
330: "No name was associated with this buffer so it has been named \"LOST\".\n");
331: } else
332: fprintf(mf,
333: "A copy of an editor buffer of your file \"%s\"\nwas saved when %s.\n", fname,
334: flag ? "the system went down" : "your phone was hung up");
335: fprintf(mf,
336: "This buffer can be retrieved using the \"recover\" command of the editor.\n");
337: fprintf(mf,
338: "An easy way to do this is to give the command \"ex -r %s\".\n",fname);
339: fprintf(mf,
340: "This works for \"edit\" and \"vi\" also.\n");
341: pclose(mf);
342: }
343:
344: /*
345: * people making love
346: * never exactly the same
347: * just like a snowflake
348: */
349:
350: #ifdef lint
351: Ignore(a)
352: int a;
353: {
354:
355: a = a;
356: }
357:
358: Ignorl(a)
359: long a;
360: {
361:
362: a = a;
363: }
364: #endif