rework the argument structures to support lengths of arguments
[nvi.git] / common / exf.c
blob324391e54235d08217a2a594e7211c92baa47596
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.54 1993/12/02 10:36:01 bostic Exp $ (Berkeley) $Date: 1993/12/02 10:36:01 $";
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_T *name;
50 int ignore;
52 FREF *frp;
53 char *p;
56 * Return it if it already exists. Note that we test against the
57 * user's current name, whatever that happens to be, including if
58 * it's a temporary file.
60 if (name != NULL)
61 for (frp = sp->frefq.tqh_first;
62 frp != NULL; frp = frp->q.tqe_next) {
63 if ((p = FILENAME(frp)) != NULL && !strcmp(p, name))
64 return (frp);
67 /* Allocate and initialize the FREF structure. */
68 if ((frp = calloc(1, sizeof(FREF))) == NULL)
69 goto mem;
72 * If no file name specified, or if the file name is a request
73 * for something temporary, file_init() will allocate the file
74 * name.
76 #define TEMPORARY_FILE_STRING "/tmp"
77 if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
78 (frp->name = strdup(name)) == NULL) {
79 FREE(frp, sizeof(FREF));
80 mem: msgq(sp, M_SYSERR, NULL);
81 return (NULL);
84 /* Only the initial argument list is "remembered". */
85 if (ignore)
86 F_SET(frp, FR_IGNORE);
88 /* Append into the chain of file names. */
89 if (frp_append != NULL) {
90 TAILQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q);
91 } else
92 TAILQ_INSERT_TAIL(&sp->frefq, frp, q);
94 return (frp);
98 * file_first --
99 * Return the first file name for editing, if any.
101 FREF *
102 file_first(sp, all)
103 SCR *sp;
104 int all;
106 FREF *frp;
108 /* Return the first file name. */
109 for (frp = sp->frefq.tqh_first; frp != NULL; frp = frp->q.tqe_next)
110 if (all || !F_ISSET(frp, FR_IGNORE))
111 return (frp);
112 return (NULL);
116 * file_next --
117 * Return the next file name, if any.
119 FREF *
120 file_next(sp, all)
121 SCR *sp;
122 int all;
124 FREF *frp;
126 /* Return the next file name. */
127 for (frp = sp->frefq.tqh_first; frp != NULL; frp = frp->q.tqe_next)
128 if (all || !F_ISSET(frp, FR_EDITED | FR_IGNORE))
129 return (frp);
130 return (NULL);
134 * file_init --
135 * Start editing a file, based on the FREF structure. If successsful,
136 * let go of any previous file. Don't release the previous file until
137 * absolutely sure we have the new one.
140 file_init(sp, frp, rcv_name, force)
141 SCR *sp;
142 FREF *frp;
143 char *rcv_name;
144 int force;
146 EXF *ep;
147 RECNOINFO oinfo;
148 struct stat sb;
149 size_t psize;
150 int fd;
151 char *p, *oname, tname[sizeof(_PATH_TMPNAME) + 1];
153 /* Create the EXF. */
154 if ((ep = malloc(sizeof(EXF))) == NULL) {
155 msgq(sp, M_SYSERR, NULL);
156 return (1);
160 * Required ep initialization:
161 * Flush the line caches.
162 * Default recover mail file fd to -1.
163 * Set initial EXF flag bits.
165 memset(ep, 0, sizeof(EXF));
166 ep->c_lno = ep->c_nlines = OOBLNO;
167 ep->rcv_fd = -1;
168 LIST_INIT(&ep->marks);
169 F_SET(ep, F_FIRSTMODIFY);
172 * If no name or backing file, create a backing temporary file, saving
173 * the temp file name so can later unlink it. Repoint the name to the
174 * temporary name (we display it to the user until they rename it).
175 * There are some games we play with the FR_FREE_TNAME and FR_NONAME
176 * flags (see ex/ex_file.c) to make sure that the temporary memory gets
177 * free'd up.
179 if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) {
180 (void)strcpy(tname, _PATH_TMPNAME);
181 if ((fd = mkstemp(tname)) == -1) {
182 msgq(sp, M_SYSERR, "Temporary file");
183 goto err;
185 (void)close(fd);
186 if ((frp->tname = strdup(tname)) == NULL) {
187 msgq(sp, M_SYSERR, NULL);
188 (void)unlink(tname);
189 goto err;
191 oname = frp->tname;
192 psize = 4 * 1024;
193 F_SET(frp, FR_NEWFILE);
194 } else {
195 /* Try to keep it at 10 pages or less per file. */
196 if (sb.st_size < 40 * 1024)
197 psize = 4 * 1024;
198 else if (sb.st_size < 320 * 1024)
199 psize = 32 * 1024;
200 else
201 psize = 64 * 1024;
203 frp->mtime = sb.st_mtime;
206 /* Set up recovery. */
207 memset(&oinfo, 0, sizeof(RECNOINFO));
208 oinfo.bval = '\n'; /* Always set. */
209 oinfo.psize = psize;
210 oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0;
211 if (rcv_name == NULL) {
212 if (rcv_tmp(sp, ep, FILENAME(frp)))
213 msgq(sp, M_ERR,
214 "Modifications not recoverable if the system crashes.");
215 else
216 oinfo.bfname = ep->rcv_path;
217 } else if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
218 msgq(sp, M_SYSERR, NULL);
219 goto err;
220 } else {
221 oinfo.bfname = ep->rcv_path;
222 F_SET(ep, F_MODIFIED);
225 /* Open a db structure. */
226 if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL,
227 O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) {
228 msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name);
229 goto err;
232 /* Init file marks. */
233 if (mark_init(sp, ep))
234 goto err;
236 /* Start logging. */
237 if (log_init(sp, ep))
238 goto err;
241 * The -R flag, or doing a "set readonly" during a session causes
242 * all files edited during the session (using an edit command, or
243 * even using tags) to be marked read-only. Changing the file name
244 * (see ex/ex_file.c), clears this flag.
246 * Otherwise, try and figure out if a file is readonly. This is a
247 * dangerous thing to do. The kernel is the only arbiter of whether
248 * or not a file is writeable, and the best that a user program can
249 * do is guess. Obvious loopholes are files that are on a file system
250 * mounted readonly (access catches this one on a few systems), or
251 * alternate protection mechanisms, ACL's for example, that we can't
252 * portably check. Lots of fun, and only here because users whined.
254 * !!!
255 * Historic vi displayed the readonly message if none of the file
256 * write bits were set, or if an an access(2) call on the path
257 * failed. This seems reasonable. If the file is mode 444, root
258 * users may want to know that the owner of the file did not expect
259 * it to be written.
261 * Historic vi set the readonly bit if no write bits were set for
262 * a file, even if the access call would have succeeded. This makes
263 * the superuser force the write even when vi expects that it will
264 * succeed. I'm less supportive of this semantic, but it's historic
265 * practice and the conservative approach to vi'ing files as root.
267 * It would be nice if there was some way to update this when the user
268 * does a "^Z; chmod ...". The problem is that we'd first have to
269 * distinguish between readonly bits set because of file permissions
270 * and those set for other reasons. That's not too hard, but deciding
271 * when to reevaluate the permissions is trickier. An alternative
272 * might be to turn off the readonly bit if the user forces a write
273 * and it succeeds.
275 * XXX
276 * Access(2) doesn't consider the effective uid/gid values. This
277 * probably isn't a problem for vi when it's running standalone.
279 if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) &&
280 (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
281 access(FILENAME(frp), W_OK)))
282 F_SET(frp, FR_RDONLY);
283 else
284 F_CLR(frp, FR_RDONLY);
287 * Close the previous file; if that fails, close the new one
288 * and run for the border.
290 if (sp->ep != NULL && file_end(sp, sp->ep, force)) {
291 (void)file_end(sp, ep, 1);
292 goto err;
296 * 4.4BSD supports locking in the open call, other systems don't.
297 * Since the user can't interrupt us between the open and here,
298 * it's a don't care.
300 * !!!
301 * We need to distinguish a lock not being available for the file
302 * from the file system not supporting locking. Assume that EAGAIN
303 * is the former. There isn't a portable way to do this.
305 * XXX
306 * The locking is flock(2) style, not fcntl(2). The latter is known
307 * to fail badly on some systems, and its only advantage is that it
308 * occasionally works over NFS.
310 if (flock(ep->db->fd(ep->db), LOCK_EX | LOCK_NB))
311 if (errno == EAGAIN) {
312 msgq(sp, M_INFO,
313 "%s already locked, session is read-only", oname);
314 F_SET(frp, FR_RDONLY);
315 } else
316 msgq(sp, M_VINFO, "%s cannot be locked", oname);
319 * Set the previous file pointer and the alternate file name to be
320 * the file we're about to discard.
322 * !!!
323 * If the current file was a temporary file, the call to file_end()
324 * unlinked it and free'd the name. So, there is no previous file,
325 * and there is no alternate file name. This matches historical
326 * practice, although in historical vi it could only happen as the
327 * result of the initial command, i.e. if vi was execute without a
328 * file name.
330 if (sp->frp != NULL) {
331 p = FILENAME(sp->frp);
332 if (p == NULL)
333 sp->p_frp = NULL;
334 else
335 sp->p_frp = sp->frp;
336 set_alt_name(sp, p);
339 /* The new file has now been officially edited. */
340 F_SET(frp, FR_EDITED);
342 /* Switch... */
343 ++ep->refcnt;
344 sp->ep = ep;
345 sp->frp = frp;
346 return (0);
348 err: if (frp->tname != NULL) {
349 (void)unlink(frp->tname);
350 free(frp->tname);
351 frp->tname = NULL;
353 if (ep->rcv_path != NULL) {
354 free(ep->rcv_path);
355 ep->rcv_path = NULL;
357 FREE(ep, sizeof(EXF));
358 return (1);
362 * file_end --
363 * Stop editing a file.
366 file_end(sp, ep, force)
367 SCR *sp;
368 EXF *ep;
369 int force;
371 FREF *frp;
375 * sp->ep MAY NOT BE THE SAME AS THE ARGUMENT ep, SO DON'T USE IT!
377 * Save the cursor location.
379 * XXX
380 * It would be cleaner to do this somewhere else, but by the time
381 * ex or vi knows that we're changing files it's already happened.
383 frp = sp->frp;
384 frp->lno = sp->lno;
385 frp->cno = sp->cno;
386 F_SET(frp, FR_CURSORSET);
388 /* If multiply referenced, just decrement the count and return. */
389 if (--ep->refcnt != 0)
390 return (0);
392 /* Close the db structure. */
393 if (ep->db->close != NULL && ep->db->close(ep->db) && !force) {
394 msgq(sp, M_ERR,
395 "%s: close: %s", FILENAME(frp), strerror(errno));
396 return (1);
399 /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */
401 /* Stop logging. */
402 (void)log_end(sp, ep);
404 /* Free up any marks. */
405 mark_end(sp, ep);
408 * Delete the recovery files, close the open descriptor,
409 * free recovery memory.
411 if (!F_ISSET(ep, F_RCV_NORM)) {
412 if (ep->rcv_path != NULL && unlink(ep->rcv_path))
413 msgq(sp, M_ERR,
414 "%s: remove: %s", ep->rcv_path, strerror(errno));
415 if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
416 msgq(sp, M_ERR,
417 "%s: remove: %s", ep->rcv_mpath, strerror(errno));
419 if (ep->rcv_fd != -1)
420 (void)close(ep->rcv_fd);
421 if (ep->rcv_path != NULL)
422 free(ep->rcv_path);
423 if (ep->rcv_mpath != NULL)
424 free(ep->rcv_mpath);
426 /* Unlink any temporary file, file name. */
427 if (frp->tname != NULL) {
428 if (unlink(frp->tname))
429 msgq(sp, M_ERR,
430 "%s: remove: %s", frp->tname, strerror(errno));
431 free(frp->tname);
432 frp->tname = NULL;
435 /* Free the EXF structure. */
436 FREE(ep, sizeof(EXF));
437 return (0);
441 * file_write --
442 * Write the file to disk. Historic vi had fairly convoluted
443 * semantics for whether or not writes would happen. That's
444 * why all the flags.
447 file_write(sp, ep, fm, tm, name, flags)
448 SCR *sp;
449 EXF *ep;
450 MARK *fm, *tm;
451 char *name;
452 int flags;
454 struct stat sb;
455 FILE *fp;
456 FREF *frp;
457 MARK from, to;
458 u_long nlno, nch;
459 int fd, oflags;
460 char *msg;
463 * Don't permit writing to temporary files. The problem is that
464 * if it's a temp file, and the user does ":wq", we write and quit,
465 * unlinking the temporary file. Not what the user had in mind
466 * at all. This test cannot be forced.
468 frp = sp->frp;
469 if (name == NULL && frp->cname == NULL && frp->name == NULL) {
470 msgq(sp, M_ERR, "No filename to which to write.");
471 return (1);
474 /* Can't write files marked read-only, unless forced. */
475 if (!LF_ISSET(FS_FORCE) &&
476 name == NULL && F_ISSET(frp, FR_RDONLY)) {
477 if (LF_ISSET(FS_POSSIBLE))
478 msgq(sp, M_ERR,
479 "Read-only file, not written; use ! to override.");
480 else
481 msgq(sp, M_ERR,
482 "Read-only file, not written.");
483 return (1);
486 /* If not forced, not appending, and "writeany" not set ... */
487 if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
488 /* Don't overwrite anything but the original file. */
489 if (name != NULL) {
490 if (!stat(name, &sb))
491 goto exists;
492 } else if (frp->cname != NULL &&
493 !F_ISSET(frp, FR_CHANGEWRITE) && !stat(frp->cname, &sb)) {
494 name = frp->cname;
495 exists: if (LF_ISSET(FS_POSSIBLE))
496 msgq(sp, M_ERR,
497 "%s exists, not written; use ! to override.", name);
498 else
499 msgq(sp, M_ERR,
500 "%s exists, not written.", name);
501 return (1);
505 * Don't write part of any existing file. Only test for the
506 * original file, the previous test catches anything else.
508 if (!LF_ISSET(FS_ALL) && name == NULL &&
509 frp->cname == NULL && !stat(frp->name, &sb)) {
510 if (LF_ISSET(FS_POSSIBLE))
511 msgq(sp, M_ERR,
512 "Use ! to write a partial file.");
513 else
514 msgq(sp, M_ERR, "Partial file, not written.");
515 return (1);
520 * Figure out if the file already exists -- if it doesn't, we display
521 * the "new file" message. The stat might not be necessary, but we
522 * just repeat it because it's easier than hacking the previous tests.
523 * The information is only used for the user message and modification
524 * time test, so we can ignore the obvious race condition.
526 * If the user is overwriting a file other than the original file, and
527 * O_WRITEANY was what got us here (neither force nor append was set),
528 * display the "existing file" messsage. Since the FR_CHANGEWRITE flag
529 * is set on a successful write, the message only appears once when the
530 * user changes a file name. This is historic practice.
532 * One final test. If we're not forcing or appending, and we have a
533 * saved modification time, stop the user if it's been written since
534 * we last edited or wrote it, and make them force it.
536 if (stat(name == NULL ? FILENAME(frp) : name, &sb))
537 msg = ": new file";
538 else {
539 msg = "";
540 if (!LF_ISSET(FS_FORCE | FS_APPEND)) {
541 if (frp->mtime && sb.st_mtime > frp->mtime) {
542 msgq(sp, M_ERR,
543 "%s: file modified more recently than this copy%s.",
544 name == NULL ? frp->name : name,
545 LF_ISSET(FS_POSSIBLE) ?
546 "; use ! to override" : "");
547 return (1);
549 if (name != NULL ||
550 !F_ISSET(frp, FR_CHANGEWRITE) && frp->cname != NULL)
551 msg = ": existing file";
555 /* We no longer care where the name came from. */
556 if (name == NULL)
557 name = FILENAME(frp);
559 /* Set flags to either append or truncate. */
560 oflags = O_CREAT | O_WRONLY;
561 if (LF_ISSET(FS_APPEND))
562 oflags |= O_APPEND;
563 else
564 oflags |= O_TRUNC;
566 /* Open the file. */
567 if ((fd = open(name, oflags, DEFFILEMODE)) < 0) {
568 msgq(sp, M_SYSERR, name);
569 return (1);
572 /* Use stdio for buffering. */
573 if ((fp = fdopen(fd, "w")) == NULL) {
574 (void)close(fd);
575 msgq(sp, M_SYSERR, name);
576 return (1);
579 /* Build fake addresses, if necessary. */
580 if (fm == NULL) {
581 from.lno = 1;
582 from.cno = 0;
583 fm = &from;
584 if (file_lline(sp, ep, &to.lno))
585 return (1);
586 to.cno = 0;
587 tm = &to;
590 /* Write the file. */
591 if (ex_writefp(sp, ep, name, fp, fm, tm, &nlno, &nch)) {
592 if (!LF_ISSET(FS_APPEND))
593 msgq(sp, M_ERR, "%s: WARNING: file truncated!", name);
594 return (1);
597 /* Save the new last modification time. */
598 frp->mtime = stat(name, &sb) ? 0 : sb.st_mtime;
601 * Once we've actually written the file, it doesn't matter that the
602 * file name was changed -- if it was, we've already whacked it.
604 F_SET(frp, FR_CHANGEWRITE);
606 /* If wrote the entire file, clear the modified bit. */
607 if (LF_ISSET(FS_ALL))
608 F_CLR(ep, F_MODIFIED);
610 msgq(sp, M_INFO, "%s%s: %lu line%s, %lu characters.",
611 name, msg, nlno, nlno == 1 ? "" : "s", nch);
613 return (0);