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