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