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