typo
[nvi.git] / common / exf.c
bloba064259f265b6a1595104ba9acf3b1c381ca00e3
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: exf.c,v 8.51 1993/11/21 16:27:28 bostic Exp $ (Berkeley) $Date: 1993/11/21 16:27:28 $";
10 #endif /* not lint */
12 #include <sys/types.h>
13 #include <sys/stat.h>
16 * We include <sys/file.h>, because the flock(2) #defines were
17 * found there on historical systems. We also include <fcntl.h>
18 * because the open(2) #defines are found there on newer systems.
20 #include <sys/file.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "vi.h"
29 #include "excmd.h"
30 #include "pathnames.h"
33 * file_add --
34 * Insert a file name into the FREF list, if it doesn't already
35 * appear in it.
37 * !!!
38 * The "if it doesn't already appear" changes vi's semantics slightly. If
39 * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
40 * will reflect the line/column of the previous edit session. Historic nvi
41 * did not do this. The change is a logical extension of the change where
42 * vi now remembers the last location in any file that it has ever edited,
43 * not just the previously edited file.
45 FREF *
46 file_add(sp, frp_append, name, ignore)
47 SCR *sp;
48 FREF *frp_append;
49 char *name;
50 int ignore;
52 FREF *frp;
55 * Just return it if it already exists. Note that we do the
56 * test against the user's original name, and not any changed
57 * or temporary name.
59 if (name != NULL)
60 for (frp = sp->frefq.tqh_first;
61 frp != NULL; frp = frp->q.tqe_next)
62 if (frp->name != NULL && !strcmp(frp->name, name))
63 return (frp);
65 /* Allocate and initialize the FREF structure. */
66 if ((frp = malloc(sizeof(FREF))) == NULL)
67 goto mem;
68 memset(frp, 0, sizeof(FREF));
71 * If no file name specified, or if the file name is a request
72 * for something temporary, file_init() will allocate the file
73 * name.
75 #define TEMPORARY_FILE_STRING "/tmp"
76 if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING)) {
77 if ((frp->name = strdup(name)) == NULL) {
78 FREE(frp, sizeof(FREF));
79 mem: msgq(sp, M_SYSERR, NULL);
80 return (NULL);
82 frp->nlen = strlen(name);
85 /* Only the initial argument list is "remembered". */
86 if (ignore)
87 F_SET(frp, FR_IGNORE);
89 /* Append into the chain of file names. */
90 if (frp_append != NULL) {
91 TAILQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q);
92 } else
93 TAILQ_INSERT_TAIL(&sp->frefq, frp, q);
95 return (frp);
99 * file_first --
100 * Return the first file name for editing, if any.
102 FREF *
103 file_first(sp, all)
104 SCR *sp;
105 int all;
107 FREF *frp;
109 /* Return the first file name. */
110 for (frp = sp->frefq.tqh_first; frp != NULL; frp = frp->q.tqe_next)
111 if (all || !F_ISSET(frp, FR_IGNORE))
112 return (frp);
113 return (NULL);
117 * file_next --
118 * Return the next file name, if any.
120 FREF *
121 file_next(sp, all)
122 SCR *sp;
123 int all;
125 FREF *frp;
127 /* Return the next file name. */
128 for (frp = sp->frefq.tqh_first; frp != NULL; frp = frp->q.tqe_next)
129 if (all || !F_ISSET(frp, FR_EDITED | FR_IGNORE))
130 return (frp);
131 return (NULL);
135 * file_init --
136 * Start editing a file, based on the FREF structure. If successsful,
137 * let go of any previous file. Don't release the previous file until
138 * absolutely sure we have the new one.
141 file_init(sp, frp, rcv_name, force)
142 SCR *sp;
143 FREF *frp;
144 char *rcv_name;
145 int force;
147 EXF *ep;
148 RECNOINFO oinfo;
149 struct stat sb;
150 size_t psize;
151 int fd;
152 char *oname, tname[sizeof(_PATH_TMPNAME) + 1];
154 /* Create the EXF. */
155 if ((ep = malloc(sizeof(EXF))) == NULL) {
156 msgq(sp, M_SYSERR, NULL);
157 return (1);
161 * Required ep initialization:
162 * Flush the line caches.
163 * Default recover mail file fd to -1.
164 * Set initial EXF flag bits.
166 memset(ep, 0, sizeof(EXF));
167 ep->c_lno = ep->c_nlines = OOBLNO;
168 ep->rcv_fd = -1;
169 LIST_INIT(&ep->marks);
170 F_SET(ep, F_FIRSTMODIFY);
173 * If no name or backing file, create a backing temporary file, saving
174 * the temp file name so can later unlink it. Repoint the name to the
175 * temporary name (we display it to the user until they rename it).
176 * There are some games we play with the FR_FREE_TNAME and FR_NONAME
177 * flags (see ex/ex_file.c) to make sure that the temporary memory gets
178 * free'd up.
180 if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) {
181 (void)strcpy(tname, _PATH_TMPNAME);
182 if ((fd = mkstemp(tname)) == -1) {
183 msgq(sp, M_SYSERR, "Temporary file");
184 goto err;
186 (void)close(fd);
187 if ((frp->tname = strdup(tname)) == NULL) {
188 msgq(sp, M_SYSERR, NULL);
189 (void)unlink(tname);
190 goto err;
192 frp->tlen = strlen(tname);
194 oname = frp->tname;
195 psize = 4 * 1024;
196 F_SET(frp, FR_NEWFILE);
197 } else {
198 /* Try to keep it at 10 pages or less per file. */
199 if (sb.st_size < 40 * 1024)
200 psize = 4 * 1024;
201 else if (sb.st_size < 320 * 1024)
202 psize = 32 * 1024;
203 else
204 psize = 64 * 1024;
206 frp->mtime = sb.st_mtime;
209 /* Set up recovery. */
210 memset(&oinfo, 0, sizeof(RECNOINFO));
211 oinfo.bval = '\n'; /* Always set. */
212 oinfo.psize = psize;
213 oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0;
214 if (rcv_name == NULL) {
215 if (rcv_tmp(sp, ep, FILENAME(frp)))
216 msgq(sp, M_ERR,
217 "Modifications not recoverable if the system crashes.");
218 else
219 oinfo.bfname = ep->rcv_path;
220 } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
221 msgq(sp, M_SYSERR, NULL);
222 goto err;
223 } else {
224 oinfo.bfname = ep->rcv_path;
225 F_SET(ep, F_MODIFIED);
228 /* Open a db structure. */
229 if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
230 O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) {
231 msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name);
232 goto err;
235 /* Init file marks. */
236 if (mark_init(sp, ep))
237 goto err;
239 /* Start logging. */
240 if (log_init(sp, ep))
241 goto err;
244 * The -R flag, or doing a "set readonly" during a session causes
245 * all files edited during the session (using an edit command, or
246 * even using tags) to be marked read-only. Changing the file name
247 * (see ex/ex_file.c), clears this flag.
249 * Otherwise, try and figure out if a file is readonly. This is a
250 * dangerous thing to do. The kernel is the only arbiter of whether
251 * or not a file is writeable, and the best that a user program can
252 * do is guess. Obvious loopholes are files that are on a file system
253 * mounted readonly (access catches this one on a few systems), or
254 * alternate protection mechanisms, ACL's for example, that we can't
255 * portably check. Lots of fun, and only here because users whined.
257 * !!!
258 * Historic vi displayed the readonly message if none of the file
259 * write bits were set, or if an an access(2) call on the path
260 * failed. This seems reasonable. If the file is mode 444, root
261 * users may want to know that the owner of the file did not expect
262 * it to be written.
264 * Historic vi set the readonly bit if no write bits were set for
265 * a file, even if the access call would have succeeded. This makes
266 * the superuser force the write even when vi expects that it will
267 * succeed. I'm less supportive of this semantic, but it's historic
268 * practice and the conservative approach to vi'ing files as root.
270 * It would be nice if there was some way to update this when the user
271 * does a "^Z; chmod ...". The problem is that we'd first have to
272 * distinguish between readonly bits set because of file permissions
273 * and those set for other reasons. That's not too hard, but deciding
274 * when to reevaluate the permissions is trickier. An alternative
275 * might be to turn off the readonly bit if the user forces a write
276 * and it succeeds.
278 * XXX
279 * Access(2) doesn't consider the effective uid/gid values. This
280 * probably isn't a problem for vi when it's running standalone.
282 if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) &&
283 (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
284 access(FILENAME(frp), W_OK)))
285 F_SET(frp, FR_RDONLY);
286 else
287 F_CLR(frp, FR_RDONLY);
290 * Close the previous file; if that fails, close the new one
291 * and run for the border.
293 if (sp->ep != NULL && file_end(sp, sp->ep, force)) {
294 (void)file_end(sp, ep, 1);
295 goto err;
299 * 4.4BSD supports locking in the open call, other systems don't.
300 * Since the user can't interrupt us between the open and here,
301 * it's a don't care.
303 * !!!
304 * We need to distinguish a lock not being available for the file
305 * from the file system not supporting locking. Assume that EAGAIN
306 * is the former. There isn't a portable way to do this.
308 * XXX
309 * The locking is flock(2) style, not fcntl(2). The latter is known
310 * to fail badly on some systems, and its only advantage is that it
311 * occasionally works over NFS.
313 if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB))
314 if (errno == EAGAIN) {
315 msgq(sp, M_INFO,
316 "%s already locked, session is read-only", oname);
317 F_SET(frp, FR_RDONLY);
318 } else
319 msgq(sp, M_VINFO, "%s cannot be locked", oname);
322 * Set the previous file and alternate file name to be
323 * the current file.
325 if ((sp->p_frp = sp->frp) != NULL)
326 set_alt_name(sp, FILENAME(sp->frp));
328 /* The new file has now been officially edited. */
329 F_SET(frp, FR_EDITED);
331 /* Switch... */
332 ++ep->refcnt;
333 sp->ep = ep;
334 sp->frp = frp;
335 return (0);
337 err: if (frp->tname != NULL) {
338 (void)unlink(frp->tname);
339 FREE(frp->tname, frp->tlen);
340 frp->tname = NULL;
342 if (ep->rcv_path != NULL) {
343 FREE(ep->rcv_path, strlen(ep->rcv_path));
344 ep->rcv_path = NULL;
346 FREE(ep, sizeof(EXF));
347 return (1);
351 * file_end --
352 * Stop editing a file.
355 file_end(sp, ep, force)
356 SCR *sp;
357 EXF *ep;
358 int force;
360 FREF *frp;
364 * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT!
366 * Save the cursor location.
368 * XXX
369 * It would be cleaner to do this somewhere else, but by the time
370 * ex or vi knows that we're changing files it's already happened.
372 frp = sp->frp;
373 frp->lno = sp->lno;
374 frp->cno = sp->cno;
375 F_SET(frp, FR_CURSORSET);
377 /* If multiply referenced, just decrement the count and return. */
378 if (--ep->refcnt != 0)
379 return (0);
381 /* Close the db structure. */
382 if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
383 msgq(sp, M_ERR,
384 "%s: close: %s", FILENAME(frp), strerror(errno));
385 return (1);
388 /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */
390 /* Stop logging. */
391 (void)log_end(sp, ep);
393 /* Free up any marks. */
394 mark_end(sp, ep);
397 * Delete the recovery files, close the open descriptor,
398 * free recovery memory.
400 if (!F_ISSET(ep, F_RCV_NORM)) {
401 if (ep->rcv_path != NULL && unlink(ep->rcv_path))
402 msgq(sp, M_ERR,
403 "%s: remove: %s", ep->rcv_path, strerror(errno));
404 if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
405 msgq(sp, M_ERR,
406 "%s: remove: %s", ep->rcv_mpath, strerror(errno));
408 if (ep->rcv_fd != -1)
409 (void)close(ep->rcv_fd);
410 if (ep->rcv_path != NULL)
411 FREE(ep->rcv_path, strlen(ep->rcv_path));
412 if (ep->rcv_mpath != NULL)
413 FREE(ep->rcv_mpath, strlen(ep->rcv_mpath));
415 /* Unlink any temporary file, file name. */
416 if (frp->tname != NULL) {
417 if (unlink(frp->tname))
418 msgq(sp, M_ERR,
419 "%s: remove: %s", frp->tname, strerror(errno));
420 FREE(frp->tname, frp->tlen);
421 frp->tname = NULL;
424 /* Free the EXF structure. */
425 FREE(ep, sizeof(EXF));
426 return (0);
430 * file_write --
431 * Write the file to disk. Historic vi had fairly convoluted
432 * semantics for whether or not writes would happen. That's
433 * why all the flags.
436 file_write(sp, ep, fm, tm, name, flags)
437 SCR *sp;
438 EXF *ep;
439 MARK *fm, *tm;
440 char *name;
441 int flags;
443 struct stat sb;
444 FILE *fp;
445 FREF *frp;
446 MARK from, to;
447 u_long nlno, nch;
448 int fd, oflags;
449 char *msg;
452 * Don't permit writing to temporary files. The problem is that
453 * if it's a temp file, and the user does ":wq", we write and quit,
454 * unlinking the temporary file. Not what the user had in mind
455 * at all. This test cannot be forced.
457 frp = sp->frp;
458 if (name == NULL && frp->cname == NULL && frp->name == NULL) {
459 msgq(sp, M_ERR, "No filename to which to write.");
460 return (1);
463 /* Can't write files marked read-only, unless forced. */
464 if (!LF_ISSET(FS_FORCE) &&
465 name == NULL && F_ISSET(frp, FR_RDONLY)) {
466 if (LF_ISSET(FS_POSSIBLE))
467 msgq(sp, M_ERR,
468 "Read-only file, not written; use ! to override.");
469 else
470 msgq(sp, M_ERR,
471 "Read-only file, not written.");
472 return (1);
475 /* If not forced, not appending, and "writeany" not set ... */
476 if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
477 /* Don't overwrite anything but the original file. */
478 if (name != NULL) {
479 if (!stat(name, &sb))
480 goto exists;
481 } else if (frp->cname != NULL &&
482 !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) {
483 name = frp->cname;
484 exists: if (LF_ISSET(FS_POSSIBLE))
485 msgq(sp, M_ERR,
486 "%s exists, not written; use ! to override.", name);
487 else
488 msgq(sp, M_ERR,
489 "%s exists, not written.", name);
490 return (1);
494 * Don't write part of any existing file. Only test for the
495 * original file, the previous test catches anything else.
497 if (!LF_ISSET(FS_ALL) && name == NULL &&
498 frp->cname == NULL && !stat(frp->name, &sb)) {
499 if (LF_ISSET(FS_POSSIBLE))
500 msgq(sp, M_ERR,
501 "Use ! to write a partial file.");
502 else
503 msgq(sp, M_ERR, "Partial file, not written.");
504 return (1);
509 * Figure out if the file already exists -- if it doesn't, we display
510 * the "new file" message. The stat might not be necessary, but we
511 * just repeat it because it's easier than hacking the previous tests.
512 * The information is only used for the user message and modification
513 * time test, so we can ignore the obvious race condition.
515 * If the user is overwriting a file other than the original file, and
516 * O_WRITEANY was what got us here (neither force nor append was set),
517 * display the "existing file" messsage. Since the FR_CHANGEWRITE flag
518 * is set on a successful write, the message only appears once when the
519 * user changes a file name. This is historic practice.
521 * One final test. If we're not forcing or appending, and we have a
522 * saved modification time, stop the user if it's been written since
523 * we last edited or wrote it, and make them force it.
525 if (stat(name == NULL ? FILENAME(frp) : name, &sb))
526 msg = ": new file";
527 else {
528 msg = "";
529 if (!LF_ISSET(FS_FORCE | FS_APPEND)) {
530 if (frp->mtime && sb.st_mtime > frp->mtime) {
531 msgq(sp, M_ERR,
532 "%s: file modified more recently than this copy%s.",
533 name == NULL ? frp->name : name,
534 LF_ISSET(FS_POSSIBLE) ?
535 "; use ! to override" : "");
536 return (1);
538 if (name != NULL ||
539 !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL)
540 msg = ": existing file";
544 /* We no longer care where the name came from. */
545 if (name == NULL)
546 name = FILENAME(frp);
548 /* Set flags to either append or truncate. */
549 oflags = O_CREAT | O_WRONLY;
550 if (LF_ISSET(FS_APPEND))
551 oflags |= O_APPEND;
552 else
553 oflags |= O_TRUNC;
555 /* Open the file. */
556 if ((fd = open(name, oflags, DEFFILEMODE)) < 0) {
557 msgq(sp, M_SYSERR, name);
558 return (1);
561 /* Use stdio for buffering. */
562 if ((fp = fdopen(fd, "w")) == NULL) {
563 (void)close(fd);
564 msgq(sp, M_SYSERR, name);
565 return (1);
568 /* Build fake addresses, if necessary. */
569 if (fm == NULL) {
570 from.lno = 1;
571 from.cno = 0;
572 fm = &from;
573 if (file_lline(sp, ep, &to.lno))
574 return (1);
575 to.cno = 0;
576 tm = &to;
579 /* Write the file. */
580 if (ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch)) {
581 if (!LF_ISSET(FS_APPEND))
582 msgq(sp, M_ERR, "%s: WARNING: file truncated!", name);
583 return (1);
586 /* Save the new last modification time. */
587 frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime;
590 * Once we've actually written the file, it doesn't matter that the
591 * file name was changed -- if it was, we've already whacked it.
593 F_SET(frp, FR_CHANGEWRITE);
595 /* If wrote the entire file, clear the modified bit. */
596 if (LF_ISSET(FS_ALL))
597 F_CLR(ep, F_MODIFIED);
599 msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.",
600 name, msg, nlno, nlno == 1 ? "" : "s", nch);
602 return (0);