Home | History | Annotate | Line # | Download | only in common
      1  1.8  christos /*	$NetBSD: exf.c,v 1.8 2014/01/26 21:43:45 christos Exp $ */
      2  1.1  christos /*-
      3  1.1  christos  * Copyright (c) 1992, 1993, 1994
      4  1.1  christos  *	The Regents of the University of California.  All rights reserved.
      5  1.1  christos  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6  1.1  christos  *	Keith Bostic.  All rights reserved.
      7  1.1  christos  *
      8  1.1  christos  * See the LICENSE file for redistribution information.
      9  1.1  christos  */
     10  1.1  christos 
     11  1.1  christos #include "config.h"
     12  1.1  christos 
     13  1.8  christos #include <sys/cdefs.h>
     14  1.8  christos #if 0
     15  1.1  christos #ifndef lint
     16  1.1  christos static const char sccsid[] = "Id: exf.c,v 10.72 2003/08/10 09:44:01 skimo Exp  (Berkeley) Date: 2003/08/10 09:44:01 ";
     17  1.1  christos #endif /* not lint */
     18  1.8  christos #else
     19  1.8  christos __RCSID("$NetBSD: exf.c,v 1.8 2014/01/26 21:43:45 christos Exp $");
     20  1.8  christos #endif
     21  1.1  christos 
     22  1.1  christos #include <sys/param.h>
     23  1.1  christos #include <sys/types.h>		/* XXX: param.h may not have included types.h */
     24  1.1  christos #include <sys/queue.h>
     25  1.1  christos #include <sys/stat.h>
     26  1.1  christos 
     27  1.1  christos /*
     28  1.1  christos  * We include <sys/file.h>, because the flock(2) and open(2) #defines
     29  1.1  christos  * were found there on historical systems.  We also include <fcntl.h>
     30  1.1  christos  * because the open(2) #defines are found there on newer systems.
     31  1.1  christos  */
     32  1.1  christos #include <sys/file.h>
     33  1.1  christos 
     34  1.1  christos #include <bitstring.h>
     35  1.1  christos #include <dirent.h>
     36  1.1  christos #include <errno.h>
     37  1.1  christos #include <fcntl.h>
     38  1.1  christos #include <limits.h>
     39  1.1  christos #include <stdio.h>
     40  1.1  christos #include <stdlib.h>
     41  1.1  christos #include <string.h>
     42  1.1  christos #include <unistd.h>
     43  1.1  christos #include <time.h>
     44  1.1  christos 
     45  1.1  christos #include "common.h"
     46  1.2  christos #include "dbinternal.h"
     47  1.1  christos 
     48  1.2  christos static int	file_backup __P((SCR *, const char *, const char *));
     49  1.1  christos static void	file_cinit __P((SCR *));
     50  1.1  christos static void	file_comment __P((SCR *));
     51  1.1  christos static int	file_spath __P((SCR *, FREF *, struct stat *, int *));
     52  1.1  christos 
     53  1.1  christos /*
     54  1.1  christos  * file_add --
     55  1.1  christos  *	Insert a file name into the FREF list, if it doesn't already
     56  1.1  christos  *	appear in it.
     57  1.1  christos  *
     58  1.1  christos  * !!!
     59  1.1  christos  * The "if it doesn't already appear" changes vi's semantics slightly.  If
     60  1.1  christos  * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
     61  1.1  christos  * will reflect the line/column of the previous edit session.  Historic nvi
     62  1.1  christos  * did not do this.  The change is a logical extension of the change where
     63  1.1  christos  * vi now remembers the last location in any file that it has ever edited,
     64  1.1  christos  * not just the previously edited file.
     65  1.1  christos  *
     66  1.2  christos  * PUBLIC: FREF *file_add __P((SCR *, const char *));
     67  1.1  christos  */
     68  1.1  christos FREF *
     69  1.2  christos file_add(SCR *sp, const char *name)
     70  1.1  christos {
     71  1.1  christos 	GS *gp;
     72  1.1  christos 	FREF *frp, *tfrp;
     73  1.1  christos 
     74  1.1  christos 	/*
     75  1.1  christos 	 * Return it if it already exists.  Note that we test against the
     76  1.1  christos 	 * user's name, whatever that happens to be, including if it's a
     77  1.1  christos 	 * temporary file.
     78  1.1  christos 	 *
     79  1.1  christos 	 * If the user added a file but was unable to initialize it, there
     80  1.1  christos 	 * can be file list entries where the name field is NULL.  Discard
     81  1.1  christos 	 * them the next time we see them.
     82  1.1  christos 	 */
     83  1.1  christos 	gp = sp->gp;
     84  1.1  christos 	if (name != NULL)
     85  1.3  christos 		TAILQ_FOREACH_SAFE(frp, &gp->frefq, q, tfrp) {
     86  1.1  christos 			if (frp->name == NULL) {
     87  1.3  christos 				TAILQ_REMOVE(&gp->frefq, frp, q);
     88  1.1  christos 				if (frp->name != NULL)
     89  1.1  christos 					free(frp->name);
     90  1.1  christos 				free(frp);
     91  1.1  christos 				continue;
     92  1.1  christos 			}
     93  1.1  christos 			if (!strcmp(frp->name, name))
     94  1.1  christos 				return (frp);
     95  1.1  christos 		}
     96  1.1  christos 
     97  1.1  christos 	/* Allocate and initialize the FREF structure. */
     98  1.1  christos 	CALLOC(sp, frp, FREF *, 1, sizeof(FREF));
     99  1.1  christos 	if (frp == NULL)
    100  1.1  christos 		return (NULL);
    101  1.1  christos 
    102  1.1  christos 	/*
    103  1.1  christos 	 * If no file name specified, or if the file name is a request
    104  1.1  christos 	 * for something temporary, file_init() will allocate the file
    105  1.1  christos 	 * name.  Temporary files are always ignored.
    106  1.1  christos 	 */
    107  1.1  christos 	if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
    108  1.1  christos 	    (frp->name = strdup(name)) == NULL) {
    109  1.1  christos 		free(frp);
    110  1.1  christos 		msgq(sp, M_SYSERR, NULL);
    111  1.1  christos 		return (NULL);
    112  1.1  christos 	}
    113  1.1  christos 
    114  1.1  christos 	/* Append into the chain of file names. */
    115  1.3  christos 	TAILQ_INSERT_TAIL(&gp->frefq, frp, q);
    116  1.1  christos 
    117  1.1  christos 	return (frp);
    118  1.1  christos }
    119  1.1  christos 
    120  1.1  christos /*
    121  1.1  christos  * file_init --
    122  1.1  christos  *	Start editing a file, based on the FREF structure.  If successsful,
    123  1.1  christos  *	let go of any previous file.  Don't release the previous file until
    124  1.1  christos  *	absolutely sure we have the new one.
    125  1.1  christos  *
    126  1.1  christos  * PUBLIC: int file_init __P((SCR *, FREF *, char *, int));
    127  1.1  christos  */
    128  1.1  christos int
    129  1.1  christos file_init(SCR *sp, FREF *frp, char *rcv_name, int flags)
    130  1.1  christos {
    131  1.1  christos 	EXF *ep;
    132  1.1  christos 	struct stat sb;
    133  1.1  christos 	size_t psize;
    134  1.2  christos 	int fd, exists, open_err, readonly;
    135  1.2  christos 	char *oname = NULL, tname[MAXPATHLEN];
    136  1.1  christos 
    137  1.2  christos 	open_err = readonly = 0;
    138  1.1  christos 
    139  1.1  christos 	/*
    140  1.1  christos 	 * If the file is a recovery file, let the recovery code handle it.
    141  1.1  christos 	 * Clear the FR_RECOVER flag first -- the recovery code does set up,
    142  1.1  christos 	 * and then calls us!  If the recovery call fails, it's probably
    143  1.1  christos 	 * because the named file doesn't exist.  So, move boldly forward,
    144  1.1  christos 	 * presuming that there's an error message the user will get to see.
    145  1.1  christos 	 */
    146  1.1  christos 	if (F_ISSET(frp, FR_RECOVER)) {
    147  1.1  christos 		F_CLR(frp, FR_RECOVER);
    148  1.1  christos 		return (rcv_read(sp, frp));
    149  1.1  christos 	}
    150  1.1  christos 
    151  1.1  christos 	/*
    152  1.1  christos 	 * Required FRP initialization; the only flag we keep is the
    153  1.1  christos 	 * cursor information.
    154  1.1  christos 	 */
    155  1.1  christos 	F_CLR(frp, ~FR_CURSORSET);
    156  1.1  christos 
    157  1.1  christos 	/*
    158  1.1  christos 	 * Scan the user's path to find the file that we're going to
    159  1.1  christos 	 * try and open.
    160  1.1  christos 	 */
    161  1.1  christos 	if (file_spath(sp, frp, &sb, &exists))
    162  1.1  christos 		return (1);
    163  1.1  christos 
    164  1.1  christos 	/*
    165  1.1  christos 	 * Check whether we already have this file opened in some
    166  1.1  christos 	 * other screen.
    167  1.1  christos 	 */
    168  1.1  christos 	if (exists) {
    169  1.1  christos 		EXF *exfp;
    170  1.3  christos 		TAILQ_FOREACH(exfp, &sp->gp->exfq, q) {
    171  1.1  christos 			if (exfp->mdev == sb.st_dev &&
    172  1.1  christos 			    exfp->minode == sb.st_ino &&
    173  1.1  christos 			    (exfp != sp->ep || exfp->refcnt > 1)) {
    174  1.1  christos 				ep = exfp;
    175  1.2  christos 				oname = ep->rcv_path;
    176  1.1  christos 				goto postinit;
    177  1.1  christos 			}
    178  1.1  christos 		}
    179  1.1  christos 	}
    180  1.1  christos 
    181  1.1  christos 	/*
    182  1.1  christos 	 * Required EXF initialization:
    183  1.1  christos 	 *	Flush the line caches.
    184  1.1  christos 	 *	Default recover mail file fd to -1.
    185  1.1  christos 	 *	Set initial EXF flag bits.
    186  1.1  christos 	 */
    187  1.1  christos 	CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
    188  1.3  christos 	TAILQ_INIT(&ep->scrq);
    189  1.1  christos 	sp->c_lno = ep->c_nlines = OOBLNO;
    190  1.2  christos 	ep->fd = ep->rcv_fd = ep->fcntl_fd = -1;
    191  1.1  christos 	F_SET(ep, F_FIRSTMODIFY);
    192  1.1  christos 
    193  1.1  christos 	/*
    194  1.1  christos 	 * If no name or backing file, for whatever reason, create a backing
    195  1.1  christos 	 * temporary file, saving the temp file name so we can later unlink
    196  1.1  christos 	 * it.  If the user never named this file, copy the temporary file name
    197  1.1  christos 	 * to the real name (we display that until the user renames it).
    198  1.1  christos 	 */
    199  1.1  christos 	oname = frp->name;
    200  1.1  christos 	if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
    201  1.1  christos 		if (opts_empty(sp, O_TMP_DIRECTORY, 0))
    202  1.1  christos 			goto err;
    203  1.1  christos 		(void)snprintf(tname, sizeof(tname),
    204  1.1  christos 		    "%s/vi.XXXXXX", O_STR(sp, O_TMP_DIRECTORY));
    205  1.1  christos 		if ((fd = mkstemp(tname)) == -1) {
    206  1.1  christos 			msgq(sp, M_SYSERR,
    207  1.1  christos 			    "237|Unable to create temporary file");
    208  1.1  christos 			goto err;
    209  1.1  christos 		}
    210  1.1  christos 		(void)close(fd);
    211  1.1  christos 
    212  1.1  christos 		if (frp->name == NULL)
    213  1.1  christos 			F_SET(frp, FR_TMPFILE);
    214  1.1  christos 		if ((frp->tname = strdup(tname)) == NULL ||
    215  1.1  christos 		    (frp->name == NULL &&
    216  1.1  christos 		     (frp->name = strdup(tname)) == NULL)) {
    217  1.1  christos 			if (frp->tname != NULL) {
    218  1.1  christos 				free(frp->tname);
    219  1.1  christos 			}
    220  1.1  christos 			msgq(sp, M_SYSERR, NULL);
    221  1.1  christos 			(void)unlink(tname);
    222  1.1  christos 			goto err;
    223  1.1  christos 		}
    224  1.1  christos 		oname = frp->tname;
    225  1.1  christos 		psize = 1024;
    226  1.1  christos 		if (!LF_ISSET(FS_OPENERR))
    227  1.1  christos 			F_SET(frp, FR_NEWFILE);
    228  1.1  christos 
    229  1.1  christos 		time(&ep->mtime);
    230  1.1  christos 	} else {
    231  1.1  christos 		/*
    232  1.1  christos 		 * XXX
    233  1.1  christos 		 * A seat of the pants calculation: try to keep the file in
    234  1.1  christos 		 * 15 pages or less.  Don't use a page size larger than 10K
    235  1.1  christos 		 * (vi should have good locality) or smaller than 1K.
    236  1.1  christos 		 */
    237  1.1  christos 		psize = ((sb.st_size / 15) + 1023) / 1024;
    238  1.1  christos 		if (psize > 10)
    239  1.1  christos 			psize = 10;
    240  1.1  christos 		if (psize == 0)
    241  1.1  christos 			psize = 1;
    242  1.1  christos 		psize *= 1024;
    243  1.1  christos 
    244  1.1  christos 		F_SET(ep, F_DEVSET);
    245  1.1  christos 		ep->mdev = sb.st_dev;
    246  1.1  christos 		ep->minode = sb.st_ino;
    247  1.1  christos 
    248  1.1  christos 		ep->mtime = sb.st_mtime;
    249  1.1  christos 
    250  1.1  christos 		if (!S_ISREG(sb.st_mode))
    251  1.1  christos 			msgq_str(sp, M_ERR, oname,
    252  1.1  christos 			    "238|Warning: %s is not a regular file");
    253  1.1  christos 	}
    254  1.1  christos 
    255  1.1  christos 	/* Set up recovery. */
    256  1.1  christos 	if (rcv_name == NULL) {
    257  1.1  christos 		/* ep->rcv_path NULL if rcv_tmp fails */
    258  1.1  christos 		rcv_tmp(sp, ep, frp->name);
    259  1.1  christos 	} else {
    260  1.1  christos 		if ((ep->rcv_path = strdup(rcv_name)) == NULL) {
    261  1.1  christos 			msgq(sp, M_SYSERR, NULL);
    262  1.1  christos 			goto err;
    263  1.1  christos 		}
    264  1.1  christos 		F_SET(ep, F_MODIFIED);
    265  1.1  christos 	}
    266  1.1  christos 
    267  1.1  christos 	if (db_init(sp, ep, rcv_name, oname, psize, &open_err)) {
    268  1.1  christos 		if (open_err && !LF_ISSET(FS_OPENERR))
    269  1.1  christos 			goto oerr;
    270  1.1  christos 		goto err;
    271  1.1  christos 	}
    272  1.1  christos 
    273  1.1  christos 	/*
    274  1.1  christos 	 * Do the remaining things that can cause failure of the new file,
    275  1.1  christos 	 * mark and logging initialization.
    276  1.1  christos 	 */
    277  1.1  christos 	if (mark_init(sp, ep) || log_init(sp, ep))
    278  1.1  christos 		goto err;
    279  1.1  christos 
    280  1.1  christos postinit:
    281  1.1  christos 	/*
    282  1.1  christos 	 * Set the alternate file name to be the file we're discarding.
    283  1.1  christos 	 *
    284  1.1  christos 	 * !!!
    285  1.1  christos 	 * Temporary files can't become alternate files, so there's no file
    286  1.1  christos 	 * name.  This matches historical practice, although it could only
    287  1.1  christos 	 * happen in historical vi as the result of the initial command, i.e.
    288  1.1  christos 	 * if vi was executed without a file name.
    289  1.1  christos 	 */
    290  1.1  christos 	if (LF_ISSET(FS_SETALT))
    291  1.1  christos 		set_alt_name(sp, sp->frp == NULL ||
    292  1.1  christos 		    F_ISSET(sp->frp, FR_TMPFILE) ? NULL : sp->frp->name);
    293  1.1  christos 
    294  1.1  christos 	/*
    295  1.1  christos 	 * Close the previous file; if that fails, close the new one and run
    296  1.1  christos 	 * for the border.
    297  1.1  christos 	 *
    298  1.1  christos 	 * !!!
    299  1.1  christos 	 * There's a nasty special case.  If the user edits a temporary file,
    300  1.1  christos 	 * and then does an ":e! %", we need to re-initialize the backing
    301  1.1  christos 	 * file, but we can't change the name.  (It's worse -- we're dealing
    302  1.1  christos 	 * with *names* here, we can't even detect that it happened.)  Set a
    303  1.1  christos 	 * flag so that the file_end routine ignores the backing information
    304  1.1  christos 	 * of the old file if it happens to be the same as the new one.
    305  1.1  christos 	 *
    306  1.1  christos 	 * !!!
    307  1.1  christos 	 * Side-effect: after the call to file_end(), sp->frp may be NULL.
    308  1.1  christos 	 */
    309  1.1  christos 	if (sp->ep != NULL) {
    310  1.1  christos 		F_SET(frp, FR_DONTDELETE);
    311  1.1  christos 		if (file_end(sp, NULL, LF_ISSET(FS_FORCE))) {
    312  1.1  christos 			(void)file_end(sp, ep, 1);
    313  1.1  christos 			goto err;
    314  1.1  christos 		}
    315  1.1  christos 		sp->ep = NULL;
    316  1.1  christos 		F_CLR(frp, FR_DONTDELETE);
    317  1.1  christos 	}
    318  1.1  christos 
    319  1.1  christos 	/*
    320  1.1  christos 	 * Lock the file; if it's a recovery file, it should already be
    321  1.1  christos 	 * locked.  Note, we acquire the lock after the previous file
    322  1.1  christos 	 * has been ended, so that we don't get an "already locked" error
    323  1.1  christos 	 * for ":edit!".
    324  1.1  christos 	 *
    325  1.1  christos 	 * XXX
    326  1.1  christos 	 * While the user can't interrupt us between the open and here,
    327  1.1  christos 	 * there's a race between the dbopen() and the lock.  Not much
    328  1.1  christos 	 * we can do about it.
    329  1.1  christos 	 *
    330  1.1  christos 	 * XXX
    331  1.1  christos 	 * We don't make a big deal of not being able to lock the file.  As
    332  1.1  christos 	 * locking rarely works over NFS, and often fails if the file was
    333  1.1  christos 	 * mmap(2)'d, it's far too common to do anything like print an error
    334  1.1  christos 	 * message, let alone make the file readonly.  At some future time,
    335  1.1  christos 	 * when locking is a little more reliable, this should change to be
    336  1.1  christos 	 * an error.
    337  1.1  christos 	 */
    338  1.1  christos 	if (rcv_name == NULL && ep->refcnt == 0) {
    339  1.1  christos 		if ((ep->fd = open(oname, O_RDWR)) == -1)
    340  1.1  christos 		    goto no_lock;
    341  1.1  christos 
    342  1.1  christos 		switch (file_lock(sp, oname, &ep->fcntl_fd, ep->fd, 1)) {
    343  1.1  christos 		case LOCK_FAILED:
    344  1.1  christos no_lock:
    345  1.1  christos 			F_SET(frp, FR_UNLOCKED);
    346  1.1  christos 			break;
    347  1.1  christos 		case LOCK_UNAVAIL:
    348  1.1  christos 			readonly = 1;
    349  1.1  christos 			msgq_str(sp, M_INFO, oname,
    350  1.1  christos 			    "239|%s already locked, session is read-only");
    351  1.1  christos 			break;
    352  1.1  christos 		case LOCK_SUCCESS:
    353  1.1  christos 			break;
    354  1.1  christos 		}
    355  1.1  christos 	}
    356  1.1  christos 
    357  1.1  christos 	/*
    358  1.1  christos          * Historically, the readonly edit option was set per edit buffer in
    359  1.1  christos          * vi, unless the -R command-line option was specified or the program
    360  1.1  christos          * was executed as "view".  (Well, to be truthful, if the letter 'w'
    361  1.1  christos          * occurred anywhere in the program name, but let's not get into that.)
    362  1.1  christos 	 * So, the persistant readonly state has to be stored in the screen
    363  1.1  christos 	 * structure, and the edit option value toggles with the contents of
    364  1.1  christos 	 * the edit buffer.  If the persistant readonly flag is set, set the
    365  1.1  christos 	 * readonly edit option.
    366  1.1  christos 	 *
    367  1.1  christos 	 * Otherwise, try and figure out if a file is readonly.  This is a
    368  1.1  christos 	 * dangerous thing to do.  The kernel is the only arbiter of whether
    369  1.1  christos 	 * or not a file is writeable, and the best that a user program can
    370  1.1  christos 	 * do is guess.  Obvious loopholes are files that are on a file system
    371  1.1  christos 	 * mounted readonly (access catches this one on a few systems), or
    372  1.1  christos 	 * alternate protection mechanisms, ACL's for example, that we can't
    373  1.1  christos 	 * portably check.  Lots of fun, and only here because users whined.
    374  1.1  christos 	 *
    375  1.1  christos 	 * !!!
    376  1.1  christos 	 * Historic vi displayed the readonly message if none of the file
    377  1.1  christos 	 * write bits were set, or if an an access(2) call on the path
    378  1.1  christos 	 * failed.  This seems reasonable.  If the file is mode 444, root
    379  1.1  christos 	 * users may want to know that the owner of the file did not expect
    380  1.1  christos 	 * it to be written.
    381  1.1  christos 	 *
    382  1.1  christos 	 * Historic vi set the readonly bit if no write bits were set for
    383  1.1  christos 	 * a file, even if the access call would have succeeded.  This makes
    384  1.1  christos 	 * the superuser force the write even when vi expects that it will
    385  1.1  christos 	 * succeed.  I'm less supportive of this semantic, but it's historic
    386  1.1  christos 	 * practice and the conservative approach to vi'ing files as root.
    387  1.1  christos 	 *
    388  1.1  christos 	 * It would be nice if there was some way to update this when the user
    389  1.1  christos 	 * does a "^Z; chmod ...".  The problem is that we'd first have to
    390  1.1  christos 	 * distinguish between readonly bits set because of file permissions
    391  1.1  christos 	 * and those set for other reasons.  That's not too hard, but deciding
    392  1.1  christos 	 * when to reevaluate the permissions is trickier.  An alternative
    393  1.1  christos 	 * might be to turn off the readonly bit if the user forces a write
    394  1.1  christos 	 * and it succeeds.
    395  1.1  christos 	 *
    396  1.1  christos 	 * XXX
    397  1.1  christos 	 * Access(2) doesn't consider the effective uid/gid values.  This
    398  1.1  christos 	 * probably isn't a problem for vi when it's running standalone.
    399  1.1  christos 	 */
    400  1.1  christos 	if (readonly || F_ISSET(sp, SC_READONLY) ||
    401  1.1  christos 	    (!F_ISSET(frp, FR_NEWFILE) &&
    402  1.1  christos 	    (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ||
    403  1.1  christos 	    access(frp->name, W_OK))))
    404  1.1  christos 		O_SET(sp, O_READONLY);
    405  1.1  christos 	else
    406  1.1  christos 		O_CLR(sp, O_READONLY);
    407  1.1  christos 
    408  1.1  christos 	/* Switch... */
    409  1.1  christos 	++ep->refcnt;
    410  1.3  christos 	TAILQ_INSERT_HEAD(&ep->scrq, sp, eq);
    411  1.1  christos 	sp->ep = ep;
    412  1.1  christos 	sp->frp = frp;
    413  1.1  christos 
    414  1.1  christos 	/* Set the initial cursor position, queue initial command. */
    415  1.1  christos 	file_cinit(sp);
    416  1.1  christos 
    417  1.1  christos 	/* Report conversion errors again. */
    418  1.1  christos 	F_CLR(sp, SC_CONV_ERROR);
    419  1.1  christos 
    420  1.1  christos 	/* Redraw the screen from scratch, schedule a welcome message. */
    421  1.1  christos 	F_SET(sp, SC_SCR_REFORMAT | SC_STATUS);
    422  1.1  christos 
    423  1.1  christos 	if (frp->lno == OOBLNO)
    424  1.1  christos 		F_SET(sp, SC_SCR_TOP);
    425  1.1  christos 
    426  1.1  christos 	/* Append into the chain of file structures. */
    427  1.1  christos 	if (ep->refcnt == 1)
    428  1.3  christos 		TAILQ_INSERT_TAIL(&sp->gp->exfq, ep, q);
    429  1.1  christos 
    430  1.1  christos 	return (0);
    431  1.1  christos 
    432  1.1  christos err:	if (frp->name != NULL) {
    433  1.1  christos 		free(frp->name);
    434  1.1  christos 		frp->name = NULL;
    435  1.1  christos 	}
    436  1.1  christos 	if (frp->tname != NULL) {
    437  1.1  christos 		(void)unlink(frp->tname);
    438  1.1  christos 		free(frp->tname);
    439  1.1  christos 		frp->tname = NULL;
    440  1.1  christos 	}
    441  1.1  christos 
    442  1.1  christos oerr:	if (F_ISSET(ep, F_RCV_ON))
    443  1.1  christos 		(void)unlink(ep->rcv_path);
    444  1.1  christos 	if (ep->rcv_path != NULL) {
    445  1.1  christos 		free(ep->rcv_path);
    446  1.1  christos 		ep->rcv_path = NULL;
    447  1.1  christos 	}
    448  1.1  christos 	if (ep->db != NULL) {
    449  1.1  christos 		(void)db_close(ep->db);
    450  1.1  christos 		ep->db = NULL;
    451  1.1  christos 	}
    452  1.1  christos 	free(ep);
    453  1.1  christos 
    454  1.1  christos 	return (open_err && !LF_ISSET(FS_OPENERR) ?
    455  1.1  christos 	    file_init(sp, frp, rcv_name, flags | FS_OPENERR) : 1);
    456  1.1  christos }
    457  1.1  christos 
    458  1.1  christos /*
    459  1.1  christos  * file_spath --
    460  1.1  christos  *	Scan the user's path to find the file that we're going to
    461  1.1  christos  *	try and open.
    462  1.1  christos  */
    463  1.1  christos static int
    464  1.1  christos file_spath(SCR *sp, FREF *frp, struct stat *sbp, int *existsp)
    465  1.1  christos {
    466  1.1  christos 	size_t len;
    467  1.1  christos 	int found;
    468  1.2  christos 	char *name, path[MAXPATHLEN];
    469  1.2  christos 	const char *p, *t;
    470  1.1  christos 
    471  1.1  christos 	/*
    472  1.1  christos 	 * If the name is NULL or an explicit reference (i.e., the first
    473  1.1  christos 	 * component is . or ..) ignore the O_PATH option.
    474  1.1  christos 	 */
    475  1.1  christos 	name = frp->name;
    476  1.1  christos 	if (name == NULL) {
    477  1.1  christos 		*existsp = 0;
    478  1.1  christos 		return (0);
    479  1.1  christos 	}
    480  1.1  christos 	if (name[0] == '/' || (name[0] == '.' &&
    481  1.1  christos 	    (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) {
    482  1.1  christos 		*existsp = !stat(name, sbp);
    483  1.1  christos 		return (0);
    484  1.1  christos 	}
    485  1.1  christos 
    486  1.1  christos 	/* Try . */
    487  1.1  christos 	if (!stat(name, sbp)) {
    488  1.1  christos 		*existsp = 1;
    489  1.1  christos 		return (0);
    490  1.1  christos 	}
    491  1.1  christos 
    492  1.1  christos 	/* Try the O_PATH option values. */
    493  1.1  christos 	for (found = 0, p = t = O_STR(sp, O_PATH);; ++p)
    494  1.1  christos 		if (*p == ':' || *p == '\0') {
    495  1.1  christos 			if (t < p - 1) {
    496  1.2  christos 				len = snprintf(path, sizeof(path), "%.*s/%s",
    497  1.2  christos 				    (int)(p - t), t, name);
    498  1.1  christos 				if (!stat(path, sbp)) {
    499  1.1  christos 					found = 1;
    500  1.1  christos 					break;
    501  1.1  christos 				}
    502  1.1  christos 			}
    503  1.1  christos 			t = p + 1;
    504  1.1  christos 			if (*p == '\0')
    505  1.1  christos 				break;
    506  1.1  christos 		}
    507  1.1  christos 
    508  1.1  christos 	/* If we found it, build a new pathname and discard the old one. */
    509  1.1  christos 	if (found) {
    510  1.2  christos 		char *q;
    511  1.2  christos 		MALLOC_RET(sp, q, char *, len + 1);
    512  1.2  christos 		memcpy(q, path, len + 1);
    513  1.1  christos 		free(frp->name);
    514  1.2  christos 		frp->name = q;
    515  1.1  christos 	}
    516  1.1  christos 	*existsp = found;
    517  1.1  christos 	return (0);
    518  1.1  christos }
    519  1.1  christos 
    520  1.1  christos /*
    521  1.1  christos  * file_cinit --
    522  1.1  christos  *	Set up the initial cursor position.
    523  1.1  christos  */
    524  1.1  christos static void
    525  1.1  christos file_cinit(SCR *sp)
    526  1.1  christos {
    527  1.1  christos 	GS *gp;
    528  1.1  christos 	MARK m;
    529  1.1  christos 	size_t len;
    530  1.1  christos 	int nb;
    531  1.2  christos 	const CHAR_T *wp;
    532  1.1  christos 	size_t wlen;
    533  1.1  christos 
    534  1.1  christos 	/* Set some basic defaults. */
    535  1.1  christos 	sp->lno = 1;
    536  1.1  christos 	sp->cno = 0;
    537  1.1  christos 
    538  1.1  christos 	/*
    539  1.1  christos 	 * Historically, initial commands (the -c option) weren't executed
    540  1.1  christos 	 * until a file was loaded, e.g. "vi +10 nofile", followed by an
    541  1.1  christos 	 * :edit or :tag command, would execute the +10 on the file loaded
    542  1.1  christos 	 * by the subsequent command, (assuming that it existed).  This
    543  1.1  christos 	 * applied as well to files loaded using the tag commands, and we
    544  1.1  christos 	 * follow that historic practice.  Also, all initial commands were
    545  1.1  christos 	 * ex commands and were always executed on the last line of the file.
    546  1.1  christos 	 *
    547  1.1  christos 	 * Otherwise, if no initial command for this file:
    548  1.1  christos 	 *    If in ex mode, move to the last line, first nonblank character.
    549  1.1  christos 	 *    If the file has previously been edited, move to the last known
    550  1.1  christos 	 *	  position, and check it for validity.
    551  1.1  christos 	 *    Otherwise, move to the first line, first nonblank.
    552  1.1  christos 	 *
    553  1.1  christos 	 * This gets called by the file init code, because we may be in a
    554  1.1  christos 	 * file of ex commands and we want to execute them from the right
    555  1.1  christos 	 * location in the file.
    556  1.1  christos 	 */
    557  1.1  christos 	nb = 0;
    558  1.1  christos 	gp = sp->gp;
    559  1.1  christos 	if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) {
    560  1.1  christos 		if (db_last(sp, &sp->lno))
    561  1.1  christos 			return;
    562  1.1  christos 		if (sp->lno == 0) {
    563  1.1  christos 			sp->lno = 1;
    564  1.1  christos 			sp->cno = 0;
    565  1.1  christos 		}
    566  1.1  christos 		CHAR2INT(sp, gp->c_option, strlen(gp->c_option) + 1,
    567  1.1  christos 			 wp, wlen);
    568  1.1  christos 		if (ex_run_str(sp, "-c option", wp, wlen - 1, 1, 1))
    569  1.1  christos 			return;
    570  1.1  christos 		gp->c_option = NULL;
    571  1.1  christos 	} else if (F_ISSET(sp, SC_EX)) {
    572  1.1  christos 		if (db_last(sp, &sp->lno))
    573  1.1  christos 			return;
    574  1.1  christos 		if (sp->lno == 0) {
    575  1.1  christos 			sp->lno = 1;
    576  1.1  christos 			sp->cno = 0;
    577  1.1  christos 			return;
    578  1.1  christos 		}
    579  1.1  christos 		nb = 1;
    580  1.1  christos 	} else {
    581  1.1  christos 		if (F_ISSET(sp->frp, FR_CURSORSET)) {
    582  1.1  christos 			sp->lno = sp->frp->lno;
    583  1.1  christos 			sp->cno = sp->frp->cno;
    584  1.1  christos 
    585  1.1  christos 			/* If returning to a file in vi, center the line. */
    586  1.1  christos 			 F_SET(sp, SC_SCR_CENTER);
    587  1.1  christos 		} else {
    588  1.1  christos 			if (O_ISSET(sp, O_COMMENT))
    589  1.1  christos 				file_comment(sp);
    590  1.1  christos 			else
    591  1.1  christos 				sp->lno = 1;
    592  1.1  christos 			nb = 1;
    593  1.1  christos 		}
    594  1.1  christos 		if (db_get(sp, sp->lno, 0, NULL, &len)) {
    595  1.1  christos 			sp->lno = 1;
    596  1.1  christos 			sp->cno = 0;
    597  1.1  christos 			return;
    598  1.1  christos 		}
    599  1.1  christos 		if (!nb && sp->cno > len)
    600  1.1  christos 			nb = 1;
    601  1.1  christos 	}
    602  1.1  christos 	if (nb) {
    603  1.1  christos 		sp->cno = 0;
    604  1.1  christos 		(void)nonblank(sp, sp->lno, &sp->cno);
    605  1.1  christos 	}
    606  1.1  christos 
    607  1.1  christos 	/*
    608  1.1  christos 	 * !!!
    609  1.1  christos 	 * The initial column is also the most attractive column.
    610  1.1  christos 	 */
    611  1.1  christos 	sp->rcm = sp->cno;
    612  1.1  christos 
    613  1.1  christos 	/*
    614  1.1  christos 	 * !!!
    615  1.1  christos 	 * Historically, vi initialized the absolute mark, but ex did not.
    616  1.1  christos 	 * Which meant, that if the first command in ex mode was "visual",
    617  1.1  christos 	 * or if an ex command was executed first (e.g. vi +10 file) vi was
    618  1.1  christos 	 * entered without the mark being initialized.  For consistency, if
    619  1.1  christos 	 * the file isn't empty, we initialize it for everyone, believing
    620  1.1  christos 	 * that it can't hurt, and is generally useful.  Not initializing it
    621  1.1  christos 	 * if the file is empty is historic practice, although it has always
    622  1.1  christos 	 * been possible to set (and use) marks in empty vi files.
    623  1.1  christos 	 */
    624  1.1  christos 	m.lno = sp->lno;
    625  1.1  christos 	m.cno = sp->cno;
    626  1.1  christos 	(void)mark_set(sp, ABSMARK1, &m, 0);
    627  1.1  christos }
    628  1.1  christos 
    629  1.1  christos /*
    630  1.1  christos  * file_end --
    631  1.1  christos  *	Stop editing a file.
    632  1.1  christos  *
    633  1.1  christos  * PUBLIC: int file_end __P((SCR *, EXF *, int));
    634  1.1  christos  */
    635  1.1  christos int
    636  1.1  christos file_end(SCR *sp, EXF *ep, int force)
    637  1.1  christos {
    638  1.1  christos 	FREF *frp;
    639  1.1  christos 
    640  1.1  christos 	/*
    641  1.1  christos 	 * !!!
    642  1.1  christos 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
    643  1.1  christos 	 * (If argument ep is NULL, use sp->ep.)
    644  1.1  christos 	 *
    645  1.1  christos 	 * If multiply referenced, just decrement the count and return.
    646  1.1  christos 	 */
    647  1.1  christos 	if (ep == NULL)
    648  1.1  christos 		ep = sp->ep;
    649  1.3  christos 	TAILQ_REMOVE(&ep->scrq, sp, eq);
    650  1.1  christos 	if (--ep->refcnt != 0)
    651  1.1  christos 		return (0);
    652  1.1  christos 
    653  1.1  christos 	/*
    654  1.1  christos 	 *
    655  1.1  christos 	 * Clean up the FREF structure.
    656  1.1  christos 	 *
    657  1.1  christos 	 * Save the cursor location.
    658  1.1  christos 	 *
    659  1.1  christos 	 * XXX
    660  1.1  christos 	 * It would be cleaner to do this somewhere else, but by the time
    661  1.1  christos 	 * ex or vi knows that we're changing files it's already happened.
    662  1.1  christos 	 */
    663  1.1  christos 	frp = sp->frp;
    664  1.1  christos 	frp->lno = sp->lno;
    665  1.1  christos 	frp->cno = sp->cno;
    666  1.1  christos 	F_SET(frp, FR_CURSORSET);
    667  1.1  christos 
    668  1.1  christos 	/*
    669  1.1  christos 	 * We may no longer need the temporary backing file, so clean it
    670  1.1  christos 	 * up.  We don't need the FREF structure either, if the file was
    671  1.1  christos 	 * never named, so lose it.
    672  1.1  christos 	 *
    673  1.1  christos 	 * !!!
    674  1.1  christos 	 * Re: FR_DONTDELETE, see the comment above in file_init().
    675  1.1  christos 	 */
    676  1.1  christos 	if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) {
    677  1.1  christos 		if (unlink(frp->tname))
    678  1.1  christos 			msgq_str(sp, M_SYSERR, frp->tname, "240|%s: remove");
    679  1.1  christos 		free(frp->tname);
    680  1.1  christos 		frp->tname = NULL;
    681  1.1  christos 		if (F_ISSET(frp, FR_TMPFILE)) {
    682  1.3  christos 			TAILQ_REMOVE(&sp->gp->frefq, frp, q);
    683  1.1  christos 			if (frp->name != NULL)
    684  1.1  christos 				free(frp->name);
    685  1.1  christos 			free(frp);
    686  1.1  christos 		}
    687  1.1  christos 		sp->frp = NULL;
    688  1.1  christos 	}
    689  1.1  christos 
    690  1.1  christos 	/*
    691  1.1  christos 	 * Clean up the EXF structure.
    692  1.1  christos 	 *
    693  1.1  christos 	 * Close the db structure.
    694  1.1  christos 	 */
    695  1.1  christos 	if (ep->db->close != NULL) {
    696  1.1  christos 		if ((sp->db_error = db_close(ep->db)) != 0 &&
    697  1.1  christos 		    !force) {
    698  1.1  christos 			msgq_str(sp, M_DBERR, frp->name, "241|%s: close");
    699  1.3  christos 			TAILQ_INSERT_HEAD(&ep->scrq, sp, eq);
    700  1.1  christos 			++ep->refcnt;
    701  1.1  christos 			return (1);
    702  1.1  christos 		}
    703  1.1  christos 		ep->db = NULL;
    704  1.1  christos 	}
    705  1.1  christos 
    706  1.1  christos 	/* COMMITTED TO THE CLOSE.  THERE'S NO GOING BACK... */
    707  1.1  christos 
    708  1.1  christos 	/* Stop logging. */
    709  1.1  christos 	(void)log_end(sp, ep);
    710  1.1  christos 
    711  1.1  christos 	/* Free up any marks. */
    712  1.1  christos 	(void)mark_end(sp, ep);
    713  1.1  christos 
    714  1.1  christos 	if (ep->env) {
    715  1.1  christos 		DB_ENV *env;
    716  1.1  christos 
    717  1.1  christos 		db_env_close(ep->env, 0);
    718  1.1  christos 		ep->env = 0;
    719  1.5      tron 		if ((sp->db_error = db_env_create(&env, 0)) != 0)
    720  1.1  christos 			msgq(sp, M_DBERR, "env_create");
    721  1.5      tron 		if ((sp->db_error = db_env_remove(env, ep->env_path, 0)) != 0)
    722  1.1  christos 			msgq(sp, M_DBERR, "env->remove");
    723  1.1  christos 		if (ep->env_path != NULL && rmdir(ep->env_path))
    724  1.1  christos 			msgq_str(sp, M_SYSERR, ep->env_path, "242|%s: remove");
    725  1.1  christos 	}
    726  1.1  christos 
    727  1.1  christos 	/*
    728  1.1  christos 	 * Delete recovery files, close the open descriptor, free recovery
    729  1.1  christos 	 * memory.  See recover.c for a description of the protocol.
    730  1.1  christos 	 *
    731  1.1  christos 	 * XXX
    732  1.1  christos 	 * Unlink backup file first, we can detect that the recovery file
    733  1.1  christos 	 * doesn't reference anything when the user tries to recover it.
    734  1.1  christos 	 * There's a race, here, obviously, but it's fairly small.
    735  1.1  christos 	 */
    736  1.1  christos 	if (!F_ISSET(ep, F_RCV_NORM)) {
    737  1.1  christos 		if (ep->rcv_path != NULL && unlink(ep->rcv_path))
    738  1.1  christos 			msgq_str(sp, M_SYSERR, ep->rcv_path, "242|%s: remove");
    739  1.1  christos 		if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath))
    740  1.1  christos 			msgq_str(sp, M_SYSERR, ep->rcv_mpath, "243|%s: remove");
    741  1.1  christos 	}
    742  1.3  christos 	TAILQ_REMOVE(&sp->gp->exfq, ep, q);
    743  1.1  christos 	if (ep->fd != -1)
    744  1.1  christos 		(void)close(ep->fd);
    745  1.1  christos 	if (ep->fcntl_fd != -1)
    746  1.1  christos 		(void)close(ep->fcntl_fd);
    747  1.1  christos 	if (ep->rcv_fd != -1)
    748  1.1  christos 		(void)close(ep->rcv_fd);
    749  1.1  christos 	if (ep->env_path != NULL)
    750  1.1  christos 		free(ep->env_path);
    751  1.2  christos 	if (ep->rcv_path != NULL) {
    752  1.1  christos 		free(ep->rcv_path);
    753  1.2  christos 		ep->rcv_path = NULL;
    754  1.2  christos 	}
    755  1.1  christos 	if (ep->rcv_mpath != NULL)
    756  1.1  christos 		free(ep->rcv_mpath);
    757  1.1  christos 
    758  1.1  christos 	free(ep);
    759  1.1  christos 	return (0);
    760  1.1  christos }
    761  1.1  christos 
    762  1.1  christos /*
    763  1.1  christos  * file_write --
    764  1.1  christos  *	Write the file to disk.  Historic vi had fairly convoluted
    765  1.1  christos  *	semantics for whether or not writes would happen.  That's
    766  1.1  christos  *	why all the flags.
    767  1.1  christos  *
    768  1.1  christos  * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int));
    769  1.1  christos  */
    770  1.1  christos int
    771  1.1  christos file_write(SCR *sp, MARK *fm, MARK *tm, char *name, int flags)
    772  1.1  christos {
    773  1.1  christos 	enum { NEWFILE, OLDFILE } mtype;
    774  1.1  christos 	struct stat sb;
    775  1.1  christos 	EXF *ep;
    776  1.1  christos 	FILE *fp;
    777  1.1  christos 	FREF *frp;
    778  1.1  christos 	MARK from, to;
    779  1.1  christos 	size_t len;
    780  1.1  christos 	u_long nlno, nch;
    781  1.1  christos 	int fd, nf, noname, oflags, rval;
    782  1.1  christos 	char *p, *s, *t, buf[MAXPATHLEN + 64];
    783  1.1  christos 	const char *msgstr;
    784  1.1  christos 
    785  1.1  christos 	ep = sp->ep;
    786  1.1  christos 	frp = sp->frp;
    787  1.1  christos 
    788  1.1  christos 	/*
    789  1.1  christos 	 * Writing '%', or naming the current file explicitly, has the
    790  1.1  christos 	 * same semantics as writing without a name.
    791  1.1  christos 	 */
    792  1.1  christos 	if (name == NULL || !strcmp(name, frp->name)) {
    793  1.1  christos 		noname = 1;
    794  1.1  christos 		name = frp->name;
    795  1.1  christos 	} else
    796  1.1  christos 		noname = 0;
    797  1.1  christos 
    798  1.1  christos 	/* Can't write files marked read-only, unless forced. */
    799  1.1  christos 	if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) {
    800  1.1  christos 		msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
    801  1.1  christos 		    "244|Read-only file, not written; use ! to override" :
    802  1.1  christos 		    "245|Read-only file, not written");
    803  1.1  christos 		return (1);
    804  1.1  christos 	}
    805  1.1  christos 
    806  1.1  christos 	/* If not forced, not appending, and "writeany" not set ... */
    807  1.1  christos 	if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) {
    808  1.1  christos 		/* Don't overwrite anything but the original file. */
    809  1.1  christos 		if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) &&
    810  1.1  christos 		    !stat(name, &sb)) {
    811  1.1  christos 			msgq_str(sp, M_ERR, name,
    812  1.1  christos 			    LF_ISSET(FS_POSSIBLE) ?
    813  1.1  christos 			    "246|%s exists, not written; use ! to override" :
    814  1.1  christos 			    "247|%s exists, not written");
    815  1.1  christos 			return (1);
    816  1.1  christos 		}
    817  1.1  christos 
    818  1.1  christos 		/*
    819  1.1  christos 		 * Don't write part of any existing file.  Only test for the
    820  1.1  christos 		 * original file, the previous test catches anything else.
    821  1.1  christos 		 */
    822  1.1  christos 		if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) {
    823  1.1  christos 			msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
    824  1.1  christos 			    "248|Partial file, not written; use ! to override" :
    825  1.1  christos 			    "249|Partial file, not written");
    826  1.1  christos 			return (1);
    827  1.1  christos 		}
    828  1.1  christos 	}
    829  1.1  christos 
    830  1.1  christos 	/*
    831  1.1  christos 	 * Figure out if the file already exists -- if it doesn't, we display
    832  1.1  christos 	 * the "new file" message.  The stat might not be necessary, but we
    833  1.1  christos 	 * just repeat it because it's easier than hacking the previous tests.
    834  1.1  christos 	 * The information is only used for the user message and modification
    835  1.1  christos 	 * time test, so we can ignore the obvious race condition.
    836  1.1  christos 	 *
    837  1.1  christos 	 * One final test.  If we're not forcing or appending the current file,
    838  1.1  christos 	 * and we have a saved modification time, object if the file changed
    839  1.1  christos 	 * since we last edited or wrote it, and make them force it.
    840  1.1  christos 	 */
    841  1.1  christos 	if (stat(name, &sb))
    842  1.1  christos 		mtype = NEWFILE;
    843  1.1  christos 	else {
    844  1.1  christos 		if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) &&
    845  1.1  christos 		    ((F_ISSET(ep, F_DEVSET) &&
    846  1.1  christos 		    (sb.st_dev != ep->mdev || sb.st_ino != ep->minode)) ||
    847  1.1  christos 		    sb.st_mtime != ep->mtime)) {
    848  1.1  christos 			msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ?
    849  1.1  christos "250|%s: file modified more recently than this copy; use ! to override" :
    850  1.1  christos "251|%s: file modified more recently than this copy");
    851  1.1  christos 			return (1);
    852  1.1  christos 		}
    853  1.1  christos 
    854  1.1  christos 		mtype = OLDFILE;
    855  1.1  christos 	}
    856  1.1  christos 
    857  1.1  christos 	/* Set flags to create, write, and either append or truncate. */
    858  1.1  christos 	oflags = O_CREAT | O_WRONLY |
    859  1.1  christos 	    (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC);
    860  1.1  christos 
    861  1.1  christos 	/* Backup the file if requested. */
    862  1.1  christos 	if (!opts_empty(sp, O_BACKUP, 1) &&
    863  1.1  christos 	    file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE))
    864  1.1  christos 		return (1);
    865  1.1  christos 
    866  1.1  christos 	/* Open the file. */
    867  1.1  christos 	SIGBLOCK;
    868  1.1  christos 	if ((fd = open(name, oflags,
    869  1.1  christos 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) {
    870  1.1  christos 		msgq_str(sp, M_SYSERR, name, "%s");
    871  1.1  christos 		SIGUNBLOCK;
    872  1.1  christos 		return (1);
    873  1.1  christos 	}
    874  1.1  christos 	SIGUNBLOCK;
    875  1.1  christos 
    876  1.1  christos 	/* Try and get a lock. */
    877  1.1  christos 	if (!noname && file_lock(sp, NULL, NULL, fd, 0) == LOCK_UNAVAIL)
    878  1.1  christos 		msgq_str(sp, M_ERR, name,
    879  1.1  christos 		    "252|%s: write lock was unavailable");
    880  1.1  christos 
    881  1.1  christos #if __linux__
    882  1.1  christos 	/*
    883  1.1  christos 	 * XXX
    884  1.1  christos 	 * In libc 4.5.x, fdopen(fd, "w") clears the O_APPEND flag (if set).
    885  1.1  christos 	 * This bug is fixed in libc 4.6.x.
    886  1.1  christos 	 *
    887  1.1  christos 	 * This code works around this problem for libc 4.5.x users.
    888  1.1  christos 	 * Note that this code is harmless if you're using libc 4.6.x.
    889  1.1  christos 	 */
    890  1.1  christos 	if (LF_ISSET(FS_APPEND) && lseek(fd, (off_t)0, SEEK_END) < 0) {
    891  1.2  christos 		msgq(sp, M_SYSERR, "%s", name);
    892  1.1  christos 		return (1);
    893  1.1  christos 	}
    894  1.1  christos #endif
    895  1.1  christos 
    896  1.1  christos 	/*
    897  1.1  christos 	 * Use stdio for buffering.
    898  1.1  christos 	 *
    899  1.1  christos 	 * XXX
    900  1.1  christos 	 * SVR4.2 requires the fdopen mode exactly match the original open
    901  1.1  christos 	 * mode, i.e. you have to open with "a" if appending.
    902  1.1  christos 	 */
    903  1.1  christos 	if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) {
    904  1.1  christos 		msgq_str(sp, M_SYSERR, name, "%s");
    905  1.1  christos 		(void)close(fd);
    906  1.1  christos 		return (1);
    907  1.1  christos 	}
    908  1.1  christos 
    909  1.1  christos 	/* Build fake addresses, if necessary. */
    910  1.1  christos 	if (fm == NULL) {
    911  1.1  christos 		from.lno = 1;
    912  1.1  christos 		from.cno = 0;
    913  1.1  christos 		fm = &from;
    914  1.1  christos 		if (db_last(sp, &to.lno))
    915  1.1  christos 			return (1);
    916  1.1  christos 		to.cno = 0;
    917  1.1  christos 		tm = &to;
    918  1.1  christos 	}
    919  1.1  christos 
    920  1.1  christos 	rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0);
    921  1.1  christos 
    922  1.1  christos 	/*
    923  1.1  christos 	 * Save the new last modification time -- even if the write fails
    924  1.1  christos 	 * we re-init the time.  That way the user can clean up the disk
    925  1.1  christos 	 * and rewrite without having to force it.
    926  1.1  christos 	 */
    927  1.1  christos 	if (noname) {
    928  1.1  christos 		if (stat(name, &sb))
    929  1.1  christos 			time(&ep->mtime);
    930  1.1  christos 		else {
    931  1.1  christos 			F_SET(ep, F_DEVSET);
    932  1.1  christos 			ep->mdev = sb.st_dev;
    933  1.1  christos 			ep->minode = sb.st_ino;
    934  1.1  christos 
    935  1.1  christos 			ep->mtime = sb.st_mtime;
    936  1.1  christos 		}
    937  1.1  christos 	}
    938  1.1  christos 
    939  1.1  christos 	/*
    940  1.1  christos 	 * If the write failed, complain loudly.  ex_writefp() has already
    941  1.1  christos 	 * complained about the actual error, reinforce it if data was lost.
    942  1.1  christos 	 */
    943  1.1  christos 	if (rval) {
    944  1.1  christos 		if (!LF_ISSET(FS_APPEND))
    945  1.1  christos 			msgq_str(sp, M_ERR, name,
    946  1.1  christos 			    "254|%s: WARNING: FILE TRUNCATED");
    947  1.1  christos 		return (1);
    948  1.1  christos 	}
    949  1.1  christos 
    950  1.1  christos 	/*
    951  1.1  christos 	 * Once we've actually written the file, it doesn't matter that the
    952  1.1  christos 	 * file name was changed -- if it was, we've already whacked it.
    953  1.1  christos 	 */
    954  1.1  christos 	F_CLR(frp, FR_NAMECHANGE);
    955  1.1  christos 
    956  1.1  christos 	/*
    957  1.1  christos 	 * If wrote the entire file, and it wasn't by appending it to a file,
    958  1.1  christos 	 * clear the modified bit.  If the file was written to the original
    959  1.1  christos 	 * file name and the file is a temporary, set the "no exit" bit.  This
    960  1.1  christos 	 * permits the user to write the file and use it in the context of the
    961  1.1  christos 	 * filesystem, but still keeps them from discarding their changes by
    962  1.1  christos 	 * exiting.
    963  1.1  christos 	 */
    964  1.1  christos 	if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) {
    965  1.1  christos 		F_CLR(ep, F_MODIFIED);
    966  1.1  christos 		if (F_ISSET(frp, FR_TMPFILE)) {
    967  1.1  christos 			if (noname)
    968  1.1  christos 				F_SET(frp, FR_TMPEXIT);
    969  1.1  christos 			else
    970  1.1  christos 				F_CLR(frp, FR_TMPEXIT);
    971  1.1  christos 		}
    972  1.1  christos 	}
    973  1.1  christos 
    974  1.1  christos 	p = msg_print(sp, name, &nf);
    975  1.1  christos 	switch (mtype) {
    976  1.1  christos 	case NEWFILE:
    977  1.1  christos 		msgstr = msg_cat(sp,
    978  1.1  christos 		    "256|%s: new file: %lu lines, %lu characters", NULL);
    979  1.1  christos 		len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
    980  1.1  christos 		break;
    981  1.1  christos 	case OLDFILE:
    982  1.1  christos 		msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ?
    983  1.1  christos 		    "315|%s: appended: %lu lines, %lu characters" :
    984  1.1  christos 		    "257|%s: %lu lines, %lu characters", NULL);
    985  1.1  christos 		len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch);
    986  1.1  christos 		break;
    987  1.1  christos 	default:
    988  1.1  christos 		abort();
    989  1.1  christos 	}
    990  1.1  christos 
    991  1.1  christos 	/*
    992  1.1  christos 	 * There's a nasty problem with long path names.  Cscope and tags files
    993  1.1  christos 	 * can result in long paths and vi will request a continuation key from
    994  1.1  christos 	 * the user.  Unfortunately, the user has typed ahead, and chaos will
    995  1.1  christos 	 * result.  If we assume that the characters in the filenames only take
    996  1.1  christos 	 * a single screen column each, we can trim the filename.
    997  1.1  christos 	 */
    998  1.1  christos 	s = buf;
    999  1.1  christos 	if (len >= sp->cols) {
   1000  1.1  christos 		for (s = buf, t = buf + strlen(p); s < t &&
   1001  1.1  christos 		    (*s != '/' || len >= sp->cols - 3); ++s, --len);
   1002  1.1  christos 		if (s == t)
   1003  1.1  christos 			s = buf;
   1004  1.1  christos 		else {
   1005  1.1  christos 			*--s = '.';		/* Leading ellipses. */
   1006  1.1  christos 			*--s = '.';
   1007  1.1  christos 			*--s = '.';
   1008  1.1  christos 		}
   1009  1.1  christos 	}
   1010  1.2  christos 	msgq(sp, M_INFO, "%s", s);
   1011  1.1  christos 	if (nf)
   1012  1.1  christos 		FREE_SPACE(sp, p, 0);
   1013  1.1  christos 	return (0);
   1014  1.1  christos }
   1015  1.1  christos 
   1016  1.1  christos /*
   1017  1.1  christos  * file_backup --
   1018  1.1  christos  *	Backup the about-to-be-written file.
   1019  1.1  christos  *
   1020  1.1  christos  * XXX
   1021  1.1  christos  * We do the backup by copying the entire file.  It would be nice to do
   1022  1.1  christos  * a rename instead, but: (1) both files may not fit and we want to fail
   1023  1.1  christos  * before doing the rename; (2) the backup file may not be on the same
   1024  1.1  christos  * disk partition as the file being written; (3) there may be optional
   1025  1.1  christos  * file information (MACs, DACs, whatever) that we won't get right if we
   1026  1.1  christos  * recreate the file.  So, let's not risk it.
   1027  1.1  christos  */
   1028  1.1  christos static int
   1029  1.2  christos file_backup(SCR *sp, const char *name, const char *bname)
   1030  1.1  christos {
   1031  1.1  christos 	struct dirent *dp;
   1032  1.1  christos 	struct stat sb;
   1033  1.1  christos 	DIR *dirp;
   1034  1.1  christos 	EXCMD cmd;
   1035  1.1  christos 	off_t off;
   1036  1.1  christos 	size_t blen;
   1037  1.1  christos 	int flags, maxnum, nr, num, nw, rfd, wfd, version;
   1038  1.2  christos 	char *bp, *pct, *slash, *t, buf[8192];
   1039  1.2  christos 	const char *p, *estr, *wfname;
   1040  1.2  christos 	const CHAR_T *wp;
   1041  1.1  christos 	size_t wlen;
   1042  1.1  christos 	size_t nlen;
   1043  1.1  christos 	char *d = NULL;
   1044  1.1  christos 
   1045  1.1  christos 	rfd = wfd = -1;
   1046  1.2  christos 	estr = wfname = NULL;
   1047  1.2  christos 	bp = NULL;
   1048  1.1  christos 
   1049  1.1  christos 	/*
   1050  1.1  christos 	 * Open the current file for reading.  Do this first, so that
   1051  1.1  christos 	 * we don't exec a shell before the most likely failure point.
   1052  1.1  christos 	 * If it doesn't exist, it's okay, there's just nothing to back
   1053  1.1  christos 	 * up.
   1054  1.1  christos 	 */
   1055  1.1  christos 	errno = 0;
   1056  1.1  christos 	if ((rfd = open(name, O_RDONLY, 0)) < 0) {
   1057  1.1  christos 		if (errno == ENOENT)
   1058  1.1  christos 			return (0);
   1059  1.1  christos 		estr = name;
   1060  1.1  christos 		goto err;
   1061  1.1  christos 	}
   1062  1.1  christos 
   1063  1.1  christos 	/*
   1064  1.1  christos 	 * If the name starts with an 'N' character, add a version number
   1065  1.1  christos 	 * to the name.  Strip the leading N from the string passed to the
   1066  1.1  christos 	 * expansion routines, for no particular reason.  It would be nice
   1067  1.1  christos 	 * to permit users to put the version number anywhere in the backup
   1068  1.1  christos 	 * name, but there isn't a special character that we can use in the
   1069  1.1  christos 	 * name, and giving a new character a special meaning leads to ugly
   1070  1.1  christos 	 * hacks both here and in the supporting ex routines.
   1071  1.1  christos 	 *
   1072  1.1  christos 	 * Shell and file name expand the option's value.
   1073  1.1  christos 	 */
   1074  1.1  christos 	ex_cinit(sp, &cmd, 0, 0, 0, 0, 0);
   1075  1.1  christos 	if (bname[0] == 'N') {
   1076  1.1  christos 		version = 1;
   1077  1.1  christos 		++bname;
   1078  1.1  christos 	} else
   1079  1.1  christos 		version = 0;
   1080  1.1  christos 	CHAR2INT(sp, bname, strlen(bname) + 1, wp, wlen);
   1081  1.1  christos 	if (argv_exp2(sp, &cmd, wp, wlen - 1))
   1082  1.1  christos 		return (1);
   1083  1.1  christos 
   1084  1.1  christos 	/*
   1085  1.1  christos 	 *  0 args: impossible.
   1086  1.1  christos 	 *  1 args: use it.
   1087  1.1  christos 	 * >1 args: object, too many args.
   1088  1.1  christos 	 */
   1089  1.1  christos 	if (cmd.argc != 1) {
   1090  1.1  christos 		msgq_str(sp, M_ERR, bname,
   1091  1.1  christos 		    "258|%s expanded into too many file names");
   1092  1.1  christos 		(void)close(rfd);
   1093  1.1  christos 		return (1);
   1094  1.1  christos 	}
   1095  1.1  christos 
   1096  1.1  christos 	/*
   1097  1.1  christos 	 * If appending a version number, read through the directory, looking
   1098  1.1  christos 	 * for file names that match the name followed by a number.  Make all
   1099  1.1  christos 	 * of the other % characters in name literal, so the user doesn't get
   1100  1.1  christos 	 * surprised and sscanf doesn't drop core indirecting through pointers
   1101  1.1  christos 	 * that don't exist.  If any such files are found, increment its number
   1102  1.1  christos 	 * by one.
   1103  1.1  christos 	 */
   1104  1.1  christos 	if (version) {
   1105  1.1  christos 		GET_SPACE_GOTOC(sp, bp, blen, cmd.argv[0]->len * 2 + 50);
   1106  1.1  christos 		INT2SYS(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
   1107  1.1  christos 			 p, nlen);
   1108  1.1  christos 		d = strdup(p);
   1109  1.1  christos 		p = d;
   1110  1.1  christos 		for (t = bp, slash = NULL;
   1111  1.1  christos 		     p[0] != '\0'; *t++ = *p++)
   1112  1.1  christos 			if (p[0] == '%') {
   1113  1.1  christos 				if (p[1] != '%')
   1114  1.1  christos 					*t++ = '%';
   1115  1.1  christos 			} else if (p[0] == '/')
   1116  1.1  christos 				slash = t;
   1117  1.1  christos 		pct = t;
   1118  1.1  christos 		*t++ = '%';
   1119  1.1  christos 		*t++ = 'd';
   1120  1.1  christos 		*t = '\0';
   1121  1.1  christos 
   1122  1.1  christos 		if (slash == NULL) {
   1123  1.1  christos 			dirp = opendir(".");
   1124  1.1  christos 			p = bp;
   1125  1.1  christos 		} else {
   1126  1.1  christos 			*slash = '\0';
   1127  1.1  christos 			dirp = opendir(bp);
   1128  1.1  christos 			*slash = '/';
   1129  1.1  christos 			p = slash + 1;
   1130  1.1  christos 		}
   1131  1.1  christos 		if (dirp == NULL) {
   1132  1.1  christos 			INT2SYS(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
   1133  1.1  christos 				estr, nlen);
   1134  1.1  christos 			goto err;
   1135  1.1  christos 		}
   1136  1.1  christos 
   1137  1.1  christos 		for (maxnum = 0; (dp = readdir(dirp)) != NULL;)
   1138  1.1  christos 			if (sscanf(dp->d_name, p, &num) == 1 && num > maxnum)
   1139  1.1  christos 				maxnum = num;
   1140  1.1  christos 		(void)closedir(dirp);
   1141  1.1  christos 
   1142  1.1  christos 		/* Format the backup file name. */
   1143  1.1  christos 		(void)snprintf(pct, blen - (pct - bp), "%d", maxnum + 1);
   1144  1.1  christos 		wfname = bp;
   1145  1.1  christos 	} else {
   1146  1.1  christos 		bp = NULL;
   1147  1.1  christos 		INT2SYS(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1,
   1148  1.1  christos 			wfname, nlen);
   1149  1.1  christos 	}
   1150  1.1  christos 
   1151  1.1  christos 	/* Open the backup file, avoiding lurkers. */
   1152  1.1  christos 	if (stat(wfname, &sb) == 0) {
   1153  1.1  christos 		if (!S_ISREG(sb.st_mode)) {
   1154  1.1  christos 			msgq_str(sp, M_ERR, bname,
   1155  1.1  christos 			    "259|%s: not a regular file");
   1156  1.1  christos 			goto err;
   1157  1.1  christos 		}
   1158  1.1  christos 		if (sb.st_uid != getuid()) {
   1159  1.1  christos 			msgq_str(sp, M_ERR, bname, "260|%s: not owned by you");
   1160  1.1  christos 			goto err;
   1161  1.1  christos 		}
   1162  1.1  christos 		if (sb.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) {
   1163  1.1  christos 			msgq_str(sp, M_ERR, bname,
   1164  1.1  christos 			   "261|%s: accessible by a user other than the owner");
   1165  1.1  christos 			goto err;
   1166  1.1  christos 		}
   1167  1.1  christos 		flags = O_TRUNC;
   1168  1.1  christos 	} else
   1169  1.1  christos 		flags = O_CREAT | O_EXCL;
   1170  1.1  christos 	if ((wfd = open(wfname, flags | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) {
   1171  1.1  christos 		estr = bname;
   1172  1.1  christos 		goto err;
   1173  1.1  christos 	}
   1174  1.1  christos 
   1175  1.1  christos 	/* Copy the file's current contents to its backup value. */
   1176  1.1  christos 	while ((nr = read(rfd, buf, sizeof(buf))) > 0)
   1177  1.1  christos 		for (off = 0; nr != 0; nr -= nw, off += nw)
   1178  1.1  christos 			if ((nw = write(wfd, buf + off, nr)) < 0) {
   1179  1.1  christos 				estr = wfname;
   1180  1.1  christos 				goto err;
   1181  1.1  christos 			}
   1182  1.1  christos 	if (nr < 0) {
   1183  1.1  christos 		estr = name;
   1184  1.1  christos 		goto err;
   1185  1.1  christos 	}
   1186  1.1  christos 
   1187  1.1  christos 	if (close(rfd)) {
   1188  1.1  christos 		estr = name;
   1189  1.1  christos 		goto err;
   1190  1.1  christos 	}
   1191  1.1  christos 	if (close(wfd)) {
   1192  1.1  christos 		estr = wfname;
   1193  1.1  christos 		goto err;
   1194  1.1  christos 	}
   1195  1.1  christos 	if (bp != NULL)
   1196  1.1  christos 		FREE_SPACE(sp, bp, blen);
   1197  1.2  christos 	if (d != NULL)
   1198  1.2  christos 		free(d);
   1199  1.1  christos 	return (0);
   1200  1.1  christos 
   1201  1.1  christos alloc_err:
   1202  1.1  christos err:	if (rfd != -1)
   1203  1.1  christos 		(void)close(rfd);
   1204  1.1  christos 	if (wfd != -1) {
   1205  1.1  christos 		(void)unlink(wfname);
   1206  1.1  christos 		(void)close(wfd);
   1207  1.1  christos 	}
   1208  1.1  christos 	if (estr)
   1209  1.1  christos 		msgq_str(sp, M_SYSERR, estr, "%s");
   1210  1.1  christos 	if (d != NULL)
   1211  1.1  christos 		free(d);
   1212  1.1  christos 	if (bp != NULL)
   1213  1.1  christos 		FREE_SPACE(sp, bp, blen);
   1214  1.1  christos 	return (1);
   1215  1.1  christos }
   1216  1.1  christos 
   1217  1.1  christos /*
   1218  1.1  christos  * file_comment --
   1219  1.1  christos  *	Skip the first comment.
   1220  1.1  christos  */
   1221  1.1  christos static void
   1222  1.1  christos file_comment(SCR *sp)
   1223  1.1  christos {
   1224  1.1  christos 	db_recno_t lno;
   1225  1.1  christos 	size_t len;
   1226  1.1  christos 	CHAR_T *p;
   1227  1.1  christos 
   1228  1.1  christos 	for (lno = 1; !db_get(sp, lno, 0, &p, &len) && len == 0; ++lno);
   1229  1.1  christos 	if (p == NULL)
   1230  1.1  christos 		return;
   1231  1.1  christos 	if (p[0] == '#') {
   1232  1.1  christos 		F_SET(sp, SC_SCR_TOP);
   1233  1.1  christos 		while (!db_get(sp, ++lno, 0, &p, &len))
   1234  1.1  christos 			if (len < 1 || p[0] != '#') {
   1235  1.1  christos 				sp->lno = lno;
   1236  1.1  christos 				return;
   1237  1.1  christos 			}
   1238  1.1  christos 	} else if (len > 1 && p[0] == '/' && p[1] == '*') {
   1239  1.1  christos 		F_SET(sp, SC_SCR_TOP);
   1240  1.1  christos 		do {
   1241  1.1  christos 			for (; len > 1; --len, ++p)
   1242  1.1  christos 				if (p[0] == '*' && p[1] == '/') {
   1243  1.1  christos 					sp->lno = lno;
   1244  1.1  christos 					return;
   1245  1.1  christos 				}
   1246  1.1  christos 		} while (!db_get(sp, ++lno, 0, &p, &len));
   1247  1.1  christos 	} else if (len > 1 && p[0] == '/' && p[1] == '/') {
   1248  1.1  christos 		F_SET(sp, SC_SCR_TOP);
   1249  1.1  christos 		while (!db_get(sp, ++lno, 0, &p, &len))
   1250  1.1  christos 			if (len < 1 || p[0] != '/' || p[1] != '/') {
   1251  1.1  christos 				sp->lno = lno;
   1252  1.1  christos 				return;
   1253  1.1  christos 			}
   1254  1.1  christos 	}
   1255  1.1  christos }
   1256  1.1  christos 
   1257  1.1  christos /*
   1258  1.1  christos  * file_m1 --
   1259  1.1  christos  * 	First modification check routine.  The :next, :prev, :rewind, :tag,
   1260  1.1  christos  *	:tagpush, :tagpop, ^^ modifications check.
   1261  1.1  christos  *
   1262  1.1  christos  * PUBLIC: int file_m1 __P((SCR *, int, int));
   1263  1.1  christos  */
   1264  1.1  christos int
   1265  1.1  christos file_m1(SCR *sp, int force, int flags)
   1266  1.1  christos {
   1267  1.1  christos 	EXF *ep;
   1268  1.1  christos 
   1269  1.1  christos 	ep = sp->ep;
   1270  1.1  christos 
   1271  1.1  christos 	/* If no file loaded, return no modifications. */
   1272  1.1  christos 	if (ep == NULL)
   1273  1.1  christos 		return (0);
   1274  1.1  christos 
   1275  1.1  christos 	/*
   1276  1.1  christos 	 * If the file has been modified, we'll want to write it back or
   1277  1.1  christos 	 * fail.  If autowrite is set, we'll write it back automatically,
   1278  1.1  christos 	 * unless force is also set.  Otherwise, we fail unless forced or
   1279  1.1  christos 	 * there's another open screen on this file.
   1280  1.1  christos 	 */
   1281  1.1  christos 	if (F_ISSET(ep, F_MODIFIED)) {
   1282  1.1  christos 		if (O_ISSET(sp, O_AUTOWRITE)) {
   1283  1.1  christos 			if (!force && file_aw(sp, flags))
   1284  1.1  christos 				return (1);
   1285  1.1  christos 		} else if (ep->refcnt <= 1 && !force) {
   1286  1.1  christos 			msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ?
   1287  1.1  christos "262|File modified since last complete write; write or use ! to override" :
   1288  1.1  christos "263|File modified since last complete write; write or use :edit! to override");
   1289  1.1  christos 			return (1);
   1290  1.1  christos 		}
   1291  1.1  christos 	}
   1292  1.1  christos 
   1293  1.1  christos 	return (file_m3(sp, force));
   1294  1.1  christos }
   1295  1.1  christos 
   1296  1.1  christos /*
   1297  1.1  christos  * file_m2 --
   1298  1.1  christos  * 	Second modification check routine.  The :edit, :quit, :recover
   1299  1.1  christos  *	modifications check.
   1300  1.1  christos  *
   1301  1.1  christos  * PUBLIC: int file_m2 __P((SCR *, int));
   1302  1.1  christos  */
   1303  1.1  christos int
   1304  1.1  christos file_m2(SCR *sp, int force)
   1305  1.1  christos {
   1306  1.1  christos 	EXF *ep;
   1307  1.1  christos 
   1308  1.1  christos 	ep = sp->ep;
   1309  1.1  christos 
   1310  1.1  christos 	/* If no file loaded, return no modifications. */
   1311  1.1  christos 	if (ep == NULL)
   1312  1.1  christos 		return (0);
   1313  1.1  christos 
   1314  1.1  christos 	/*
   1315  1.1  christos 	 * If the file has been modified, we'll want to fail, unless forced
   1316  1.1  christos 	 * or there's another open screen on this file.
   1317  1.1  christos 	 */
   1318  1.1  christos 	if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) {
   1319  1.1  christos 		msgq(sp, M_ERR,
   1320  1.1  christos "264|File modified since last complete write; write or use ! to override");
   1321  1.1  christos 		return (1);
   1322  1.1  christos 	}
   1323  1.1  christos 
   1324  1.1  christos 	return (file_m3(sp, force));
   1325  1.1  christos }
   1326  1.1  christos 
   1327  1.1  christos /*
   1328  1.1  christos  * file_m3 --
   1329  1.1  christos  * 	Third modification check routine.
   1330  1.1  christos  *
   1331  1.1  christos  * PUBLIC: int file_m3 __P((SCR *, int));
   1332  1.1  christos  */
   1333  1.1  christos int
   1334  1.1  christos file_m3(SCR *sp, int force)
   1335  1.1  christos {
   1336  1.1  christos 	EXF *ep;
   1337  1.1  christos 
   1338  1.1  christos 	ep = sp->ep;
   1339  1.1  christos 
   1340  1.1  christos 	/* If no file loaded, return no modifications. */
   1341  1.1  christos 	if (ep == NULL)
   1342  1.1  christos 		return (0);
   1343  1.1  christos 
   1344  1.1  christos 	/*
   1345  1.1  christos 	 * Don't exit while in a temporary files if the file was ever modified.
   1346  1.1  christos 	 * The problem is that if the user does a ":wq", we write and quit,
   1347  1.1  christos 	 * unlinking the temporary file.  Not what the user had in mind at all.
   1348  1.1  christos 	 * We permit writing to temporary files, so that user maps using file
   1349  1.1  christos 	 * system names work with temporary files.
   1350  1.1  christos 	 */
   1351  1.1  christos 	if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) {
   1352  1.1  christos 		msgq(sp, M_ERR,
   1353  1.1  christos 		    "265|File is a temporary; exit will discard modifications");
   1354  1.1  christos 		return (1);
   1355  1.1  christos 	}
   1356  1.1  christos 	return (0);
   1357  1.1  christos }
   1358  1.1  christos 
   1359  1.1  christos /*
   1360  1.1  christos  * file_aw --
   1361  1.1  christos  *	Autowrite routine.  If modified, autowrite is set and the readonly bit
   1362  1.1  christos  *	is not set, write the file.  A routine so there's a place to put the
   1363  1.1  christos  *	comment.
   1364  1.1  christos  *
   1365  1.1  christos  * PUBLIC: int file_aw __P((SCR *, int));
   1366  1.1  christos  */
   1367  1.1  christos int
   1368  1.1  christos file_aw(SCR *sp, int flags)
   1369  1.1  christos {
   1370  1.1  christos 	if (!F_ISSET(sp->ep, F_MODIFIED))
   1371  1.1  christos 		return (0);
   1372  1.1  christos 	if (!O_ISSET(sp, O_AUTOWRITE))
   1373  1.1  christos 		return (0);
   1374  1.1  christos 
   1375  1.1  christos 	/*
   1376  1.1  christos 	 * !!!
   1377  1.1  christos 	 * Historic 4BSD vi attempted to write the file if autowrite was set,
   1378  1.1  christos 	 * regardless of the writeability of the file (as defined by the file
   1379  1.1  christos 	 * readonly flag).  System V changed this as some point, not attempting
   1380  1.1  christos 	 * autowrite if the file was readonly.  This feels like a bug fix to
   1381  1.1  christos 	 * me (e.g. the principle of least surprise is violated if readonly is
   1382  1.1  christos 	 * set and vi writes the file), so I'm compatible with System V.
   1383  1.1  christos 	 */
   1384  1.1  christos 	if (O_ISSET(sp, O_READONLY)) {
   1385  1.1  christos 		msgq(sp, M_INFO,
   1386  1.1  christos 		    "266|File readonly, modifications not auto-written");
   1387  1.1  christos 		return (1);
   1388  1.1  christos 	}
   1389  1.1  christos 	return (file_write(sp, NULL, NULL, NULL, flags));
   1390  1.1  christos }
   1391  1.1  christos 
   1392  1.1  christos /*
   1393  1.1  christos  * set_alt_name --
   1394  1.1  christos  *	Set the alternate pathname.
   1395  1.1  christos  *
   1396  1.1  christos  * Set the alternate pathname.  It's a routine because I wanted some place
   1397  1.1  christos  * to hang this comment.  The alternate pathname (normally referenced using
   1398  1.1  christos  * the special character '#' during file expansion and in the vi ^^ command)
   1399  1.1  christos  * is set by almost all ex commands that take file names as arguments.  The
   1400  1.1  christos  * rules go something like this:
   1401  1.1  christos  *
   1402  1.1  christos  *    1: If any ex command takes a file name as an argument (except for the
   1403  1.1  christos  *	 :next command), the alternate pathname is set to that file name.
   1404  1.1  christos  *	 This excludes the command ":e" and ":w !command" as no file name
   1405  1.1  christos  *       was specified.  Note, historically, the :source command did not set
   1406  1.1  christos  *	 the alternate pathname.  It does in nvi, for consistency.
   1407  1.1  christos  *
   1408  1.1  christos  *    2: However, if any ex command sets the current pathname, e.g. the
   1409  1.1  christos  *	 ":e file" or ":rew" commands succeed, then the alternate pathname
   1410  1.1  christos  *	 is set to the previous file's current pathname, if it had one.
   1411  1.1  christos  *	 This includes the ":file" command and excludes the ":e" command.
   1412  1.1  christos  *	 So, by rule #1 and rule #2, if ":edit foo" fails, the alternate
   1413  1.1  christos  *	 pathname will be "foo", if it succeeds, the alternate pathname will
   1414  1.1  christos  *	 be the previous current pathname.  The ":e" command will not set
   1415  1.1  christos  *       the alternate or current pathnames regardless.
   1416  1.1  christos  *
   1417  1.1  christos  *    3: However, if it's a read or write command with a file argument and
   1418  1.1  christos  *	 the current pathname has not yet been set, the file name becomes
   1419  1.1  christos  *	 the current pathname, and the alternate pathname is unchanged.
   1420  1.1  christos  *
   1421  1.1  christos  * If the user edits a temporary file, there may be times when there is no
   1422  1.1  christos  * alternative file name.  A name argument of NULL turns it off.
   1423  1.1  christos  *
   1424  1.2  christos  * PUBLIC: void set_alt_name __P((SCR *, const char *));
   1425  1.1  christos  */
   1426  1.1  christos void
   1427  1.2  christos set_alt_name(SCR *sp, const char *name)
   1428  1.1  christos {
   1429  1.1  christos 	if (sp->alt_name != NULL)
   1430  1.1  christos 		free(sp->alt_name);
   1431  1.1  christos 	if (name == NULL)
   1432  1.1  christos 		sp->alt_name = NULL;
   1433  1.1  christos 	else if ((sp->alt_name = strdup(name)) == NULL)
   1434  1.1  christos 		msgq(sp, M_SYSERR, NULL);
   1435  1.1  christos }
   1436  1.1  christos 
   1437  1.1  christos /*
   1438  1.1  christos  * file_lock --
   1439  1.1  christos  *	Get an exclusive lock on a file and set close-on-exec flag
   1440  1.1  christos  *
   1441  1.1  christos  * XXX
   1442  1.1  christos  * The default locking is flock(2) style, not fcntl(2).  The latter is
   1443  1.1  christos  * known to fail badly on some systems, and its only advantage is that
   1444  1.1  christos  * it occasionally works over NFS.
   1445  1.1  christos  *
   1446  1.1  christos  * Furthermore, the semantics of fcntl(2) are wrong.  The problems are
   1447  1.1  christos  * two-fold: you can't close any file descriptor associated with the file
   1448  1.1  christos  * without losing all of the locks, and you can't get an exclusive lock
   1449  1.1  christos  * unless you have the file open for writing.  Someone ought to be shot,
   1450  1.1  christos  * but it's probably too late, they may already have reproduced.  To get
   1451  1.1  christos  * around these problems, nvi opens the files for writing when it can and
   1452  1.1  christos  * acquires a second file descriptor when it can't.  The recovery files
   1453  1.1  christos  * are examples of the former, they're always opened for writing.  The DB
   1454  1.1  christos  * files can't be opened for writing because the semantics of DB are that
   1455  1.1  christos  * files opened for writing are flushed back to disk when the DB session
   1456  1.1  christos  * is ended. So, in that case we have to acquire an extra file descriptor.
   1457  1.1  christos  *
   1458  1.1  christos  * PUBLIC: lockr_t file_lock __P((SCR *, char *, int *, int, int));
   1459  1.1  christos  */
   1460  1.1  christos lockr_t
   1461  1.1  christos file_lock(SCR *sp, char *name, int *fdp, int fd, int iswrite)
   1462  1.1  christos {
   1463  1.1  christos 	fcntl(fd, F_SETFD, 1);
   1464  1.1  christos 
   1465  1.1  christos 	if (!O_ISSET(sp, O_LOCKFILES))
   1466  1.1  christos 		return (LOCK_SUCCESS);
   1467  1.1  christos 
   1468  1.1  christos #ifdef HAVE_LOCK_FLOCK			/* Hurrah!  We've got flock(2). */
   1469  1.1  christos 	/*
   1470  1.1  christos 	 * !!!
   1471  1.1  christos 	 * We need to distinguish a lock not being available for the file
   1472  1.1  christos 	 * from the file system not supporting locking.  Flock is documented
   1473  1.1  christos 	 * as returning EWOULDBLOCK; add EAGAIN for good measure, and assume
   1474  1.1  christos 	 * they are the former.  There's no portable way to do this.
   1475  1.1  christos 	 */
   1476  1.1  christos 	errno = 0;
   1477  1.1  christos 	return (flock(fd, LOCK_EX | LOCK_NB) ? errno == EAGAIN
   1478  1.1  christos #ifdef EWOULDBLOCK
   1479  1.1  christos 	    || errno == EWOULDBLOCK
   1480  1.1  christos #endif
   1481  1.1  christos 	    ? LOCK_UNAVAIL : LOCK_FAILED : LOCK_SUCCESS);
   1482  1.1  christos #endif
   1483  1.1  christos #ifdef HAVE_LOCK_FCNTL			/* Gag me.  We've got fcntl(2). */
   1484  1.1  christos {
   1485  1.1  christos 	struct flock arg;
   1486  1.1  christos 	int didopen, sverrno;
   1487  1.1  christos 
   1488  1.1  christos 	arg.l_type = F_WRLCK;
   1489  1.1  christos 	arg.l_whence = 0;		/* SEEK_SET */
   1490  1.1  christos 	arg.l_start = arg.l_len = 0;
   1491  1.1  christos 	arg.l_pid = 0;
   1492  1.1  christos 
   1493  1.1  christos 	/*
   1494  1.1  christos 	 * If the file descriptor isn't opened for writing, it must fail.
   1495  1.1  christos 	 * If we fail because we can't get a read/write file descriptor,
   1496  1.1  christos 	 * we return LOCK_SUCCESS, believing that the file is readonly
   1497  1.1  christos 	 * and that will be sufficient to warn the user.
   1498  1.1  christos 	 */
   1499  1.1  christos 	if (!iswrite) {
   1500  1.1  christos 		if (name == NULL || fdp == NULL)
   1501  1.1  christos 			return (LOCK_FAILED);
   1502  1.1  christos 		if ((fd = open(name, O_RDWR, 0)) == -1)
   1503  1.1  christos 			return (LOCK_SUCCESS);
   1504  1.1  christos 		*fdp = fd;
   1505  1.1  christos 		didopen = 1;
   1506  1.1  christos 	}
   1507  1.1  christos 
   1508  1.1  christos 	errno = 0;
   1509  1.1  christos 	if (!fcntl(fd, F_SETLK, &arg))
   1510  1.1  christos 		return (LOCK_SUCCESS);
   1511  1.1  christos 	if (didopen) {
   1512  1.1  christos 		sverrno = errno;
   1513  1.1  christos 		(void)close(fd);
   1514  1.1  christos 		errno = sverrno;
   1515  1.1  christos 	}
   1516  1.1  christos 
   1517  1.1  christos 	/*
   1518  1.1  christos 	 * !!!
   1519  1.1  christos 	 * We need to distinguish a lock not being available for the file
   1520  1.1  christos 	 * from the file system not supporting locking.  Fcntl is documented
   1521  1.1  christos 	 * as returning EACCESS and EAGAIN; add EWOULDBLOCK for good measure,
   1522  1.1  christos 	 * and assume they are the former.  There's no portable way to do this.
   1523  1.1  christos 	 */
   1524  1.1  christos 	return (errno == EACCES || errno == EAGAIN
   1525  1.1  christos #ifdef EWOULDBLOCK
   1526  1.1  christos 	|| errno == EWOULDBLOCK
   1527  1.1  christos #endif
   1528  1.1  christos 	?  LOCK_UNAVAIL : LOCK_FAILED);
   1529  1.1  christos }
   1530  1.1  christos #endif
   1531  1.1  christos #if !defined(HAVE_LOCK_FLOCK) && !defined(HAVE_LOCK_FCNTL)
   1532  1.1  christos 	return (LOCK_SUCCESS);
   1533  1.1  christos #endif
   1534  1.1  christos }
   1535