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