Commit | Line | Data |
---|---|---|
1e64b3ba JH |
1 | /*- |
2 | * Copyright (c) 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
33 | ||
34 | #ifndef lint | |
35 | static char sccsid[] = "@(#)recover.c 8.40 (Berkeley) 12/21/93"; | |
36 | #endif /* not lint */ | |
37 | ||
38 | #include <sys/param.h> | |
39 | #include <sys/stat.h> | |
40 | #include <sys/time.h> | |
41 | ||
42 | /* | |
43 | * We include <sys/file.h>, because the flock(2) #defines were | |
44 | * found there on historical systems. We also include <fcntl.h> | |
45 | * because the open(2) #defines are found there on newer systems. | |
46 | */ | |
47 | #include <sys/file.h> | |
48 | ||
49 | #include <netdb.h> /* MAXHOSTNAMELEN on some systems. */ | |
50 | ||
51 | #include <dirent.h> | |
52 | #include <errno.h> | |
53 | #include <fcntl.h> | |
54 | #include <pwd.h> | |
55 | #include <stdlib.h> | |
56 | #include <string.h> | |
57 | #include <unistd.h> | |
58 | ||
59 | #include "vi.h" | |
60 | #include "pathnames.h" | |
61 | ||
62 | /* | |
63 | * Recovery code. | |
64 | * | |
65 | * The basic scheme is there's a btree file, whose name we specify. The first | |
66 | * time a file is modified, and then at RCV_PERIOD intervals after that, the | |
67 | * btree file is synced to disk. Each time a keystroke is requested for a file | |
68 | * the terminal routines check to see if the file needs to be synced. This, of | |
69 | * course means that the data structures had better be consistent each time the | |
70 | * key routines are called. | |
71 | * | |
72 | * We don't use timers other than to flag that the file should be synced. This | |
73 | * would require that the SCR and EXF data structures be locked, the dbopen(3) | |
74 | * routines lock out the timers for each update, etc. It's just not worth it. | |
75 | * The only way we can lose in the current scheme is if the file is saved, then | |
76 | * the user types furiously for RCV_PERIOD - 1 seconds, and types nothing more. | |
77 | * Not likely. | |
78 | * | |
79 | * When a file is first modified, a file which can be handed off to the mailer | |
80 | * is created. The file contains normal headers, with two additions, which | |
81 | * occur in THIS order, as the FIRST TWO headers: | |
82 | * | |
83 | * Vi-recover-file: file_name | |
84 | * Vi-recover-path: recover_path | |
85 | * | |
86 | * Since newlines delimit the headers, this means that file names cannot | |
87 | * have newlines in them, but that's probably okay. | |
88 | * | |
89 | * Btree files are named "vi.XXXX" and recovery files are named "recover.XXXX". | |
90 | */ | |
91 | ||
92 | #define VI_FHEADER "Vi-recover-file: " | |
93 | #define VI_PHEADER "Vi-recover-path: " | |
94 | ||
95 | static void rcv_alrm __P((int)); | |
96 | static int rcv_mailfile __P((SCR *, EXF *)); | |
97 | static void rcv_syncit __P((SCR *, int)); | |
98 | ||
99 | /* | |
100 | * rcv_tmp -- | |
101 | * Build a file name that will be used as the recovery file. | |
102 | */ | |
103 | int | |
104 | rcv_tmp(sp, ep, name) | |
105 | SCR *sp; | |
106 | EXF *ep; | |
107 | char *name; | |
108 | { | |
109 | struct stat sb; | |
110 | int fd; | |
111 | char *dp, *p, path[MAXPATHLEN]; | |
112 | ||
113 | /* | |
114 | * If the recovery directory doesn't exist, try and create it. As | |
115 | * the recovery files are themselves protected from reading/writing | |
116 | * by other than the owner, the worst that can happen is that a user | |
117 | * would have permission to remove other users recovery files. If | |
118 | * the sticky bit has the BSD semantics, that too will be impossible. | |
119 | */ | |
120 | dp = O_STR(sp, O_RECDIR); | |
121 | if (stat(dp, &sb)) { | |
122 | if (errno != ENOENT || mkdir(dp, 0)) { | |
123 | msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); | |
124 | return (1); | |
125 | } | |
126 | (void)chmod(dp, S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX); | |
127 | } | |
128 | ||
129 | /* Newlines delimit the mail messages. */ | |
130 | for (p = name; *p; ++p) | |
131 | if (*p == '\n') { | |
132 | msgq(sp, M_ERR, | |
133 | "Files with newlines in the name are unrecoverable."); | |
134 | return (1); | |
135 | } | |
136 | ||
137 | (void)snprintf(path, sizeof(path), "%s/vi.XXXXXX", dp); | |
138 | ||
139 | /* | |
140 | * !!! | |
141 | * We depend on mkstemp(3) setting the permissions correctly. | |
142 | * GP's, we do it ourselves, to keep the window as small as | |
143 | * possible. | |
144 | */ | |
145 | if ((fd = mkstemp(path)) == -1) { | |
146 | msgq(sp, M_ERR, "Error: %s: %s", dp, strerror(errno)); | |
147 | return (1); | |
148 | } | |
149 | (void)chmod(path, S_IRUSR | S_IWUSR); | |
150 | (void)close(fd); | |
151 | ||
152 | if ((ep->rcv_path = strdup(path)) == NULL) { | |
153 | msgq(sp, M_SYSERR, NULL); | |
154 | (void)unlink(path); | |
155 | return (1); | |
156 | } | |
157 | ||
158 | /* We believe the file is recoverable. */ | |
159 | F_SET(ep, F_RCV_ON); | |
160 | return (0); | |
161 | } | |
162 | ||
163 | /* | |
164 | * rcv_init -- | |
165 | * Force the file to be snapshotted for recovery. | |
166 | */ | |
167 | int | |
168 | rcv_init(sp, ep) | |
169 | SCR *sp; | |
170 | EXF *ep; | |
171 | { | |
172 | struct itimerval value; | |
173 | struct sigaction act; | |
174 | recno_t lno; | |
175 | ||
176 | F_CLR(ep, F_FIRSTMODIFY | F_RCV_ON); | |
177 | ||
178 | /* Build file to mail to the user. */ | |
179 | if (rcv_mailfile(sp, ep)) | |
180 | goto err; | |
181 | ||
182 | /* Force read of entire file. */ | |
183 | if (file_lline(sp, ep, &lno)) | |
184 | goto err; | |
185 | ||
186 | /* Turn on a busy message, and sync it to backing store. */ | |
187 | busy_on(sp, 1, "Copying file for recovery..."); | |
188 | if (ep->db->sync(ep->db, R_RECNOSYNC)) { | |
189 | msgq(sp, M_ERR, "Preservation failed: %s: %s", | |
190 | ep->rcv_path, strerror(errno)); | |
191 | busy_off(sp); | |
192 | goto err; | |
193 | } | |
194 | busy_off(sp); | |
195 | ||
196 | if (!F_ISSET(sp->gp, G_RECOVER_SET)) { | |
197 | /* Install the recovery timer handler. */ | |
198 | act.sa_handler = rcv_alrm; | |
199 | sigemptyset(&act.sa_mask); | |
200 | act.sa_flags = 0; | |
201 | (void)sigaction(SIGALRM, &act, NULL); | |
202 | ||
203 | /* Start the recovery timer. */ | |
204 | value.it_interval.tv_sec = value.it_value.tv_sec = RCV_PERIOD; | |
205 | value.it_interval.tv_usec = value.it_value.tv_usec = 0; | |
206 | if (setitimer(ITIMER_REAL, &value, NULL)) { | |
207 | msgq(sp, M_ERR, | |
208 | "Error: setitimer: %s", strerror(errno)); | |
209 | err: msgq(sp, M_ERR, | |
210 | "Recovery after system crash not possible."); | |
211 | return (1); | |
212 | } | |
213 | } | |
214 | ||
215 | /* We believe the file is recoverable. */ | |
216 | F_SET(ep, F_RCV_ON); | |
217 | return (0); | |
218 | } | |
219 | ||
220 | /* | |
221 | * rcv_alrm -- | |
222 | * Recovery timer interrupt handler. | |
223 | */ | |
224 | static void | |
225 | rcv_alrm(signo) | |
226 | int signo; | |
227 | { | |
228 | F_SET(__global_list, G_SIGALRM); | |
229 | } | |
230 | ||
231 | /* | |
232 | * rcv_mailfile -- | |
233 | * Build the file to mail to the user. | |
234 | */ | |
235 | static int | |
236 | rcv_mailfile(sp, ep) | |
237 | SCR *sp; | |
238 | EXF *ep; | |
239 | { | |
240 | struct passwd *pw; | |
241 | uid_t uid; | |
242 | FILE *fp; | |
243 | time_t now; | |
244 | int fd; | |
245 | char *p, *t, host[MAXHOSTNAMELEN], path[MAXPATHLEN]; | |
246 | ||
247 | if ((pw = getpwuid(uid = getuid())) == NULL) { | |
248 | msgq(sp, M_ERR, "Information on user id %u not found.", uid); | |
249 | return (1); | |
250 | } | |
251 | ||
252 | (void)snprintf(path, sizeof(path), | |
253 | "%s/recover.XXXXXX", O_STR(sp, O_RECDIR)); | |
254 | if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w")) == NULL) { | |
255 | msgq(sp, M_ERR, | |
256 | "Error: %s: %s", O_STR(sp, O_RECDIR), strerror(errno)); | |
257 | if (fd != -1) | |
258 | (void)close(fd); | |
259 | return (1); | |
260 | } | |
261 | ||
262 | /* | |
263 | * We keep an open lock on the file so that the recover option can | |
264 | * distinguish between files that are live and those that need to | |
265 | * be recovered. There's an obvious window between the mkstemp call | |
266 | * and the lock, but it's pretty small. | |
267 | */ | |
268 | if ((ep->rcv_fd = dup(fd)) != -1) | |
269 | (void)flock(ep->rcv_fd, LOCK_EX | LOCK_NB); | |
270 | ||
271 | if ((ep->rcv_mpath = strdup(path)) == NULL) { | |
272 | msgq(sp, M_SYSERR, NULL); | |
273 | (void)fclose(fp); | |
274 | return (1); | |
275 | } | |
276 | ||
277 | t = FILENAME(sp->frp); | |
278 | if ((p = strrchr(t, '/')) == NULL) | |
279 | p = t; | |
280 | else | |
281 | ++p; | |
282 | (void)time(&now); | |
283 | (void)gethostname(host, sizeof(host)); | |
284 | (void)fprintf(fp, "%s%s\n%s%s\n%s\n%s\n%s%s\n%s%s\n%s\n\n", | |
285 | VI_FHEADER, p, /* Non-standard. */ | |
286 | VI_PHEADER, ep->rcv_path, /* Non-standard. */ | |
287 | "Reply-To: root", | |
288 | "From: root (Nvi recovery program)", | |
289 | "To: ", pw->pw_name, | |
290 | "Subject: Nvi saved the file ", p, | |
291 | "Precedence: bulk"); /* For vacation(1). */ | |
292 | (void)fprintf(fp, "%s%.24s%s%s\n%s%s", | |
293 | "On ", ctime(&now), | |
294 | ", the user ", pw->pw_name, | |
295 | "was editing a file named ", p); | |
296 | if (p != t) | |
297 | (void)fprintf(fp, " (%s)", t); | |
298 | (void)fprintf(fp, "\n%s%s%s\n", | |
299 | "on the machine ", host, ", when it was saved for\nrecovery."); | |
300 | (void)fprintf(fp, "\n%s\n%s\n%s\n\n", | |
301 | "You can recover most, if not all, of the changes", | |
302 | "to this file using the -l and -r options to nvi(1)", | |
303 | "or nex(1)."); | |
304 | ||
305 | if (fflush(fp) || ferror(fp)) { | |
306 | msgq(sp, M_SYSERR, NULL); | |
307 | (void)fclose(fp); | |
308 | return (1); | |
309 | } | |
310 | return (0); | |
311 | } | |
312 | ||
313 | /* | |
314 | * rcv_sync -- | |
315 | * Sync the backing file. | |
316 | */ | |
317 | int | |
318 | rcv_sync(sp, ep) | |
319 | SCR *sp; | |
320 | EXF *ep; | |
321 | { | |
322 | struct itimerval value; | |
323 | ||
324 | if (ep->db->sync(ep->db, R_RECNOSYNC)) { | |
325 | msgq(sp, M_ERR, "Automatic file backup failed: %s: %s", | |
326 | ep->rcv_path, strerror(errno)); | |
327 | value.it_interval.tv_sec = value.it_interval.tv_usec = 0; | |
328 | value.it_value.tv_sec = value.it_value.tv_usec = 0; | |
329 | (void)setitimer(ITIMER_REAL, &value, NULL); | |
330 | F_CLR(ep, F_RCV_ON); | |
331 | return (1); | |
332 | } | |
333 | return (0); | |
334 | } | |
335 | ||
336 | /* | |
337 | * rcv_hup -- | |
338 | * Recovery SIGHUP interrupt handler. (Modem line dropped, or | |
339 | * xterm window closed.) | |
340 | */ | |
341 | void | |
342 | rcv_hup() | |
343 | { | |
344 | SCR *sp; | |
345 | ||
346 | /* | |
347 | * Walk the lists of screens, sync'ing the files; only sync | |
348 | * each file once. Send email to the user for each file saved. | |
349 | */ | |
350 | for (sp = __global_list->dq.cqh_first; | |
351 | sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) | |
352 | rcv_syncit(sp, 1); | |
353 | for (sp = __global_list->hq.cqh_first; | |
354 | sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) | |
355 | rcv_syncit(sp, 1); | |
356 | ||
357 | /* | |
358 | * Die with the proper exit status. Don't bother using | |
359 | * sigaction(2) 'cause we want the default behavior. | |
360 | */ | |
361 | (void)signal(SIGHUP, SIG_DFL); | |
362 | (void)kill(0, SIGHUP); | |
363 | ||
364 | /* NOTREACHED */ | |
365 | exit (1); | |
366 | } | |
367 | ||
368 | /* | |
369 | * rcv_term -- | |
370 | * Recovery SIGTERM interrupt handler. (Reboot or halt is running.) | |
371 | */ | |
372 | void | |
373 | rcv_term() | |
374 | { | |
375 | SCR *sp; | |
376 | ||
377 | /* | |
378 | * Walk the lists of screens, sync'ing the files; only sync | |
379 | * each file once. | |
380 | */ | |
381 | for (sp = __global_list->dq.cqh_first; | |
382 | sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) | |
383 | rcv_syncit(sp, 0); | |
384 | for (sp = __global_list->hq.cqh_first; | |
385 | sp != (void *)&__global_list->hq; sp = sp->q.cqe_next) | |
386 | rcv_syncit(sp, 0); | |
387 | ||
388 | /* | |
389 | * Die with the proper exit status. Don't bother using | |
390 | * sigaction(2) 'cause we want the default behavior. | |
391 | */ | |
392 | (void)signal(SIGTERM, SIG_DFL); | |
393 | (void)kill(0, SIGTERM); | |
394 | ||
395 | /* NOTREACHED */ | |
396 | exit (1); | |
397 | } | |
398 | ||
399 | /* | |
400 | * rcv_syncit -- | |
401 | * Sync the file, optionally send mail. | |
402 | */ | |
403 | static void | |
404 | rcv_syncit(sp, email) | |
405 | SCR *sp; | |
406 | int email; | |
407 | { | |
408 | EXF *ep; | |
409 | char comm[1024]; | |
410 | ||
411 | if ((ep = sp->ep) == NULL || | |
412 | !F_ISSET(ep, F_MODIFIED) || !F_ISSET(ep, F_RCV_ON)) | |
413 | return; | |
414 | ||
415 | (void)ep->db->sync(ep->db, R_RECNOSYNC); | |
416 | F_SET(ep, F_RCV_NORM); | |
417 | ||
418 | /* | |
419 | * !!! | |
420 | * If you need to port this to a system that doesn't have sendmail, | |
421 | * the -t flag being used causes sendmail to read the message for | |
422 | * the recipients instead of us specifying them some other way. | |
423 | */ | |
424 | if (email) { | |
425 | (void)snprintf(comm, sizeof(comm), | |
426 | "%s -t < %s", _PATH_SENDMAIL, ep->rcv_mpath); | |
427 | (void)system(comm); | |
428 | } | |
429 | (void)file_end(sp, ep, 1); | |
430 | } | |
431 | ||
432 | /* | |
433 | * people making love | |
434 | * never exactly the same | |
435 | * just like a snowflake | |
436 | * | |
437 | * rcv_list -- | |
438 | * List the files that can be recovered by this user. | |
439 | */ | |
440 | int | |
441 | rcv_list(sp) | |
442 | SCR *sp; | |
443 | { | |
444 | struct dirent *dp; | |
445 | struct stat sb; | |
446 | DIR *dirp; | |
447 | FILE *fp; | |
448 | int found; | |
449 | char *p, file[1024]; | |
450 | ||
451 | if (chdir(O_STR(sp, O_RECDIR)) || (dirp = opendir(".")) == NULL) { | |
452 | (void)fprintf(stderr, | |
453 | "vi: %s: %s\n", O_STR(sp, O_RECDIR), strerror(errno)); | |
454 | return (1); | |
455 | } | |
456 | ||
457 | for (found = 0; (dp = readdir(dirp)) != NULL;) { | |
458 | if (strncmp(dp->d_name, "recover.", 8)) | |
459 | continue; | |
460 | ||
461 | /* If it's readable, it's recoverable. */ | |
462 | if ((fp = fopen(dp->d_name, "r")) == NULL) | |
463 | continue; | |
464 | ||
465 | /* If it's locked, it's live. */ | |
466 | if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { | |
467 | (void)fclose(fp); | |
468 | continue; | |
469 | } | |
470 | ||
471 | /* Check the header, get the file name. */ | |
472 | if (fgets(file, sizeof(file), fp) == NULL || | |
473 | strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || | |
474 | (p = strchr(file, '\n')) == NULL) { | |
475 | (void)fprintf(stderr, | |
476 | "vi: %s: malformed recovery file.\n", dp->d_name); | |
477 | goto next; | |
478 | } | |
479 | *p = '\0'; | |
480 | ||
481 | /* Get the last modification time. */ | |
482 | if (fstat(fileno(fp), &sb)) { | |
483 | (void)fprintf(stderr, | |
484 | "vi: %s: %s\n", dp->d_name, strerror(errno)); | |
485 | goto next; | |
486 | } | |
487 | ||
488 | /* Display. */ | |
489 | (void)printf("%s: %s", | |
490 | file + sizeof(VI_FHEADER) - 1, ctime(&sb.st_mtime)); | |
491 | found = 1; | |
492 | ||
493 | next: (void)fclose(fp); | |
494 | } | |
495 | if (found == 0) | |
496 | (void)printf("vi: no files to recover.\n"); | |
497 | (void)closedir(dirp); | |
498 | return (0); | |
499 | } | |
500 | ||
501 | /* | |
502 | * rcv_read -- | |
503 | * Start a recovered file as the file to edit. | |
504 | */ | |
505 | int | |
506 | rcv_read(sp, name) | |
507 | SCR *sp; | |
508 | char *name; | |
509 | { | |
510 | struct dirent *dp; | |
511 | struct stat sb; | |
512 | DIR *dirp; | |
513 | FREF *frp; | |
514 | FILE *fp; | |
515 | time_t rec_mtime; | |
516 | int found, requested; | |
517 | char *p, *t, *recp, *pathp; | |
518 | char recpath[MAXPATHLEN], file[MAXPATHLEN], path[MAXPATHLEN]; | |
519 | ||
520 | if ((dirp = opendir(O_STR(sp, O_RECDIR))) == NULL) { | |
521 | msgq(sp, M_ERR, | |
522 | "%s: %s", O_STR(sp, O_RECDIR), strerror(errno)); | |
523 | return (1); | |
524 | } | |
525 | ||
526 | recp = pathp = NULL; | |
527 | for (found = requested = 0; (dp = readdir(dirp)) != NULL;) { | |
528 | if (strncmp(dp->d_name, "recover.", 8)) | |
529 | continue; | |
530 | ||
531 | /* If it's readable, it's recoverable. */ | |
532 | (void)snprintf(recpath, sizeof(recpath), | |
533 | "%s/%s", O_STR(sp, O_RECDIR), dp->d_name); | |
534 | if ((fp = fopen(recpath, "r")) == NULL) | |
535 | continue; | |
536 | ||
537 | /* If it's locked, it's live. */ | |
538 | if (flock(fileno(fp), LOCK_EX | LOCK_NB)) { | |
539 | (void)fclose(fp); | |
540 | continue; | |
541 | } | |
542 | ||
543 | /* Check the headers. */ | |
544 | if (fgets(file, sizeof(file), fp) == NULL || | |
545 | strncmp(file, VI_FHEADER, sizeof(VI_FHEADER) - 1) || | |
546 | (p = strchr(file, '\n')) == NULL || | |
547 | fgets(path, sizeof(path), fp) == NULL || | |
548 | strncmp(path, VI_PHEADER, sizeof(VI_PHEADER) - 1) || | |
549 | (t = strchr(path, '\n')) == NULL) { | |
550 | msgq(sp, M_ERR, | |
551 | "%s: malformed recovery file.", recpath); | |
552 | goto next; | |
553 | } | |
554 | ++found; | |
555 | *t = *p = '\0'; | |
556 | ||
557 | /* Get the last modification time. */ | |
558 | if (fstat(fileno(fp), &sb)) { | |
559 | msgq(sp, M_ERR, | |
560 | "vi: %s: %s", dp->d_name, strerror(errno)); | |
561 | goto next; | |
562 | } | |
563 | ||
564 | /* Check the file name. */ | |
565 | if (strcmp(file + sizeof(VI_FHEADER) - 1, name)) | |
566 | goto next; | |
567 | ||
568 | ++requested; | |
569 | ||
570 | /* If we've found more than one, take the most recent. */ | |
571 | if (recp == NULL || rec_mtime < sb.st_mtime) { | |
572 | p = recp; | |
573 | t = pathp; | |
574 | if ((recp = strdup(recpath)) == NULL) { | |
575 | msgq(sp, M_ERR, | |
576 | "vi: Error: %s.\n", strerror(errno)); | |
577 | recp = p; | |
578 | goto next; | |
579 | } | |
580 | if ((pathp = strdup(path)) == NULL) { | |
581 | msgq(sp, M_ERR, | |
582 | "vi: Error: %s.\n", strerror(errno)); | |
583 | FREE(recp, strlen(recp) + 1); | |
584 | recp = p; | |
585 | pathp = t; | |
586 | goto next; | |
587 | } | |
588 | if (p != NULL) { | |
589 | FREE(p, strlen(p) + 1); | |
590 | FREE(t, strlen(t) + 1); | |
591 | } | |
592 | rec_mtime = sb.st_mtime; | |
593 | } | |
594 | ||
595 | next: (void)fclose(fp); | |
596 | } | |
597 | (void)closedir(dirp); | |
598 | ||
599 | if (recp == NULL) { | |
600 | msgq(sp, M_INFO, | |
601 | "No files named %s, owned by you, to edit.", name); | |
602 | return (1); | |
603 | } | |
604 | if (found) { | |
605 | if (requested > 1) | |
606 | msgq(sp, M_INFO, | |
607 | "There are older versions of this file for you to recover."); | |
608 | if (found > requested) | |
609 | msgq(sp, M_INFO, | |
610 | "There are other files that you can recover."); | |
611 | } | |
612 | ||
613 | /* Create the FREF structure, start the btree file. */ | |
614 | if ((frp = file_add(sp, NULL, name, 0)) == NULL || | |
615 | file_init(sp, frp, pathp + sizeof(VI_PHEADER) - 1, 0)) { | |
616 | FREE(recp, strlen(recp) + 1); | |
617 | FREE(pathp, strlen(pathp) + 1); | |
618 | return (1); | |
619 | } | |
620 | sp->ep->rcv_mpath = recp; | |
621 | return (0); | |
622 | } |