119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: exf.c,v 10.62 2013/07/01 23:28:13 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 16254225Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/stat.h> 19254225Speter#include <sys/time.h> 2019304Speter 2119304Speter/* 2219304Speter * We include <sys/file.h>, because the flock(2) and open(2) #defines 2319304Speter * were found there on historical systems. We also include <fcntl.h> 2419304Speter * because the open(2) #defines are found there on newer systems. 2519304Speter */ 2619304Speter#include <sys/file.h> 2719304Speter 2819304Speter#include <bitstring.h> 2919304Speter#include <dirent.h> 3019304Speter#include <errno.h> 3119304Speter#include <fcntl.h> 3219304Speter#include <limits.h> 3319304Speter#include <stdio.h> 3419304Speter#include <stdlib.h> 3519304Speter#include <string.h> 3619304Speter#include <unistd.h> 3719304Speter 3819304Speter#include "common.h" 3919304Speter 4019304Speterstatic int file_backup __P((SCR *, char *, char *)); 4119304Speterstatic void file_cinit __P((SCR *)); 42254225Speterstatic void file_encinit __P((SCR *)); 4319304Speterstatic void file_comment __P((SCR *)); 4419304Speterstatic int file_spath __P((SCR *, FREF *, struct stat *, int *)); 4519304Speter 4619304Speter/* 4719304Speter * file_add -- 4819304Speter * Insert a file name into the FREF list, if it doesn't already 4919304Speter * appear in it. 5019304Speter * 5119304Speter * !!! 5219304Speter * The "if it doesn't already appear" changes vi's semantics slightly. If 5319304Speter * you do a "vi foo bar", and then execute "next bar baz", the edit of bar 5419304Speter * will reflect the line/column of the previous edit session. Historic nvi 5519304Speter * did not do this. The change is a logical extension of the change where 5619304Speter * vi now remembers the last location in any file that it has ever edited, 5719304Speter * not just the previously edited file. 5819304Speter * 59254225Speter * PUBLIC: FREF *file_add __P((SCR *, char *)); 6019304Speter */ 6119304SpeterFREF * 62254225Speterfile_add( 63254225Speter SCR *sp, 64254225Speter char *name) 6519304Speter{ 6619304Speter GS *gp; 6719304Speter FREF *frp, *tfrp; 6819304Speter 6919304Speter /* 7019304Speter * Return it if it already exists. Note that we test against the 7119304Speter * user's name, whatever that happens to be, including if it's a 7219304Speter * temporary file. 7319304Speter * 7419304Speter * If the user added a file but was unable to initialize it, there 7519304Speter * can be file list entries where the name field is NULL. Discard 7619304Speter * them the next time we see them. 7719304Speter */ 7819304Speter gp = sp->gp; 7919304Speter if (name != NULL) 80254225Speter TAILQ_FOREACH_SAFE(frp, gp->frefq, q, tfrp) { 8119304Speter if (frp->name == NULL) { 82254225Speter TAILQ_REMOVE(gp->frefq, frp, q); 8319304Speter if (frp->name != NULL) 8419304Speter free(frp->name); 8519304Speter free(frp); 8619304Speter continue; 8719304Speter } 8819304Speter if (!strcmp(frp->name, name)) 8919304Speter return (frp); 9019304Speter } 9119304Speter 9219304Speter /* Allocate and initialize the FREF structure. */ 9319304Speter CALLOC(sp, frp, FREF *, 1, sizeof(FREF)); 9419304Speter if (frp == NULL) 9519304Speter return (NULL); 9619304Speter 9719304Speter /* 9819304Speter * If no file name specified, or if the file name is a request 9919304Speter * for something temporary, file_init() will allocate the file 10019304Speter * name. Temporary files are always ignored. 10119304Speter */ 10219304Speter if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) && 10319304Speter (frp->name = strdup(name)) == NULL) { 10419304Speter free(frp); 10519304Speter msgq(sp, M_SYSERR, NULL); 10619304Speter return (NULL); 10719304Speter } 10819304Speter 10919304Speter /* Append into the chain of file names. */ 110254225Speter TAILQ_INSERT_TAIL(gp->frefq, frp, q); 11119304Speter 11219304Speter return (frp); 11319304Speter} 11419304Speter 11519304Speter/* 11619304Speter * file_init -- 11719304Speter * Start editing a file, based on the FREF structure. If successsful, 11819304Speter * let go of any previous file. Don't release the previous file until 11919304Speter * absolutely sure we have the new one. 12019304Speter * 12119304Speter * PUBLIC: int file_init __P((SCR *, FREF *, char *, int)); 12219304Speter */ 12319304Speterint 124254225Speterfile_init( 125254225Speter SCR *sp, 126254225Speter FREF *frp, 127254225Speter char *rcv_name, 128254225Speter int flags) 12919304Speter{ 13019304Speter EXF *ep; 131254225Speter RECNOINFO oinfo = { 0 }; 13219304Speter struct stat sb; 13319304Speter size_t psize; 13419304Speter int fd, exists, open_err, readonly; 135254225Speter char *oname, *tname; 13619304Speter 13719304Speter open_err = readonly = 0; 13819304Speter 13919304Speter /* 14019304Speter * If the file is a recovery file, let the recovery code handle it. 14119304Speter * Clear the FR_RECOVER flag first -- the recovery code does set up, 14219304Speter * and then calls us! If the recovery call fails, it's probably 14319304Speter * because the named file doesn't exist. So, move boldly forward, 14419304Speter * presuming that there's an error message the user will get to see. 14519304Speter */ 14619304Speter if (F_ISSET(frp, FR_RECOVER)) { 14719304Speter F_CLR(frp, FR_RECOVER); 14819304Speter return (rcv_read(sp, frp)); 14919304Speter } 15019304Speter 15119304Speter /* 15219304Speter * Required FRP initialization; the only flag we keep is the 15319304Speter * cursor information. 15419304Speter */ 15519304Speter F_CLR(frp, ~FR_CURSORSET); 15619304Speter 15719304Speter /* 15819304Speter * Required EXF initialization: 15919304Speter * Flush the line caches. 16019304Speter * Default recover mail file fd to -1. 16119304Speter * Set initial EXF flag bits. 16219304Speter */ 16319304Speter CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF)); 16419304Speter ep->c_lno = ep->c_nlines = OOBLNO; 165254225Speter ep->rcv_fd = -1; 16619304Speter F_SET(ep, F_FIRSTMODIFY); 16719304Speter 16819304Speter /* 16919304Speter * Scan the user's path to find the file that we're going to 17019304Speter * try and open. 17119304Speter */ 17219304Speter if (file_spath(sp, frp, &sb, &exists)) 17319304Speter return (1); 17419304Speter 17519304Speter /* 17619304Speter * If no name or backing file, for whatever reason, create a backing 17719304Speter * temporary file, saving the temp file name so we can later unlink 17819304Speter * it. If the user never named this file, copy the temporary file name 17919304Speter * to the real name (we display that until the user renames it). 18019304Speter */ 18119304Speter oname = frp->name; 18219304Speter if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) { 183254225Speter struct stat sb; 184254225Speter 185254225Speter if (opts_empty(sp, O_TMPDIR, 0)) 18619304Speter goto err; 187254225Speter if ((tname = 188254225Speter join(O_STR(sp, O_TMPDIR), "vi.XXXXXXXXXX")) == NULL) { 189254225Speter msgq(sp, M_SYSERR, NULL); 190254225Speter goto err; 191254225Speter } 192254225Speter if ((fd = mkstemp(tname)) == -1 || fstat(fd, &sb)) { 193254225Speter free(tname); 19419304Speter msgq(sp, M_SYSERR, 19519304Speter "237|Unable to create temporary file"); 19619304Speter goto err; 19719304Speter } 19819304Speter (void)close(fd); 19919304Speter 200254225Speter frp->tname = tname; 201254225Speter if (frp->name == NULL) { 20219304Speter F_SET(frp, FR_TMPFILE); 203254225Speter if ((frp->name = strdup(tname)) == NULL) { 204254225Speter msgq(sp, M_SYSERR, NULL); 205254225Speter goto err; 206254225Speter } 20719304Speter } 20819304Speter oname = frp->tname; 20919304Speter psize = 1024; 21019304Speter if (!LF_ISSET(FS_OPENERR)) 21119304Speter F_SET(frp, FR_NEWFILE); 21219304Speter 213254225Speter ep->mtim = sb.st_mtimespec; 21419304Speter } else { 21519304Speter /* 21619304Speter * XXX 21719304Speter * A seat of the pants calculation: try to keep the file in 218254225Speter * 15 pages or less. Don't use a page size larger than 16K 21919304Speter * (vi should have good locality) or smaller than 1K. 22019304Speter */ 22119304Speter psize = ((sb.st_size / 15) + 1023) / 1024; 222254225Speter if (psize > 16) 223254225Speter psize = 16; 22419304Speter if (psize == 0) 22519304Speter psize = 1; 226254225Speter psize = p2roundup(psize) << 10; 22719304Speter 22819304Speter F_SET(ep, F_DEVSET); 22919304Speter ep->mdev = sb.st_dev; 23019304Speter ep->minode = sb.st_ino; 23119304Speter 232254225Speter ep->mtim = sb.st_mtimespec; 23319304Speter 23419304Speter if (!S_ISREG(sb.st_mode)) 23519304Speter msgq_str(sp, M_ERR, oname, 23619304Speter "238|Warning: %s is not a regular file"); 23719304Speter } 23819304Speter 23919304Speter /* Set up recovery. */ 24019304Speter oinfo.bval = '\n'; /* Always set. */ 24119304Speter oinfo.psize = psize; 24219304Speter oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0; 24319304Speter if (rcv_name == NULL) { 24419304Speter if (!rcv_tmp(sp, ep, frp->name)) 24519304Speter oinfo.bfname = ep->rcv_path; 24619304Speter } else { 24719304Speter if ((ep->rcv_path = strdup(rcv_name)) == NULL) { 24819304Speter msgq(sp, M_SYSERR, NULL); 24919304Speter goto err; 25019304Speter } 25119304Speter oinfo.bfname = ep->rcv_path; 25219304Speter F_SET(ep, F_MODIFIED); 25319304Speter } 25419304Speter 25519304Speter /* Open a db structure. */ 25619304Speter if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL, 25719304Speter O_NONBLOCK | O_RDONLY, 25819304Speter S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, 25919304Speter DB_RECNO, &oinfo)) == NULL) { 26019304Speter msgq_str(sp, 26119304Speter M_SYSERR, rcv_name == NULL ? oname : rcv_name, "%s"); 26219304Speter /* 26319304Speter * !!! 26419304Speter * Historically, vi permitted users to edit files that couldn't 26519304Speter * be read. This isn't useful for single files from a command 26619304Speter * line, but it's quite useful for "vi *.c", since you can skip 26719304Speter * past files that you can't read. 26819304Speter */ 26919304Speter open_err = 1; 27019304Speter goto oerr; 27119304Speter } 27219304Speter 27319304Speter /* 27419304Speter * Do the remaining things that can cause failure of the new file, 27519304Speter * mark and logging initialization. 27619304Speter */ 27719304Speter if (mark_init(sp, ep) || log_init(sp, ep)) 27819304Speter goto err; 27919304Speter 28019304Speter /* 28119304Speter * Set the alternate file name to be the file we're discarding. 28219304Speter * 28319304Speter * !!! 28419304Speter * Temporary files can't become alternate files, so there's no file 28519304Speter * name. This matches historical practice, although it could only 28619304Speter * happen in historical vi as the result of the initial command, i.e. 28719304Speter * if vi was executed without a file name. 28819304Speter */ 28919304Speter if (LF_ISSET(FS_SETALT)) 29019304Speter set_alt_name(sp, sp->frp == NULL || 29119304Speter F_ISSET(sp->frp, FR_TMPFILE) ? NULL : sp->frp->name); 29219304Speter 29319304Speter /* 29419304Speter * Close the previous file; if that fails, close the new one and run 29519304Speter * for the border. 29619304Speter * 29719304Speter * !!! 29819304Speter * There's a nasty special case. If the user edits a temporary file, 29919304Speter * and then does an ":e! %", we need to re-initialize the backing 30019304Speter * file, but we can't change the name. (It's worse -- we're dealing 30119304Speter * with *names* here, we can't even detect that it happened.) Set a 30219304Speter * flag so that the file_end routine ignores the backing information 30319304Speter * of the old file if it happens to be the same as the new one. 30419304Speter * 30519304Speter * !!! 30619304Speter * Side-effect: after the call to file_end(), sp->frp may be NULL. 30719304Speter */ 30819304Speter if (sp->ep != NULL) { 30919304Speter F_SET(frp, FR_DONTDELETE); 31019304Speter if (file_end(sp, NULL, LF_ISSET(FS_FORCE))) { 31119304Speter (void)file_end(sp, ep, 1); 31219304Speter goto err; 31319304Speter } 31419304Speter F_CLR(frp, FR_DONTDELETE); 31519304Speter } 31619304Speter 31719304Speter /* 31819304Speter * Lock the file; if it's a recovery file, it should already be 31919304Speter * locked. Note, we acquire the lock after the previous file 32019304Speter * has been ended, so that we don't get an "already locked" error 32119304Speter * for ":edit!". 32219304Speter * 32319304Speter * XXX 32419304Speter * While the user can't interrupt us between the open and here, 32519304Speter * there's a race between the dbopen() and the lock. Not much 32619304Speter * we can do about it. 32719304Speter * 32819304Speter * XXX 32919304Speter * We don't make a big deal of not being able to lock the file. As 33019304Speter * locking rarely works over NFS, and often fails if the file was 33119304Speter * mmap(2)'d, it's far too common to do anything like print an error 33219304Speter * message, let alone make the file readonly. At some future time, 33319304Speter * when locking is a little more reliable, this should change to be 33419304Speter * an error. 33519304Speter */ 33619304Speter if (rcv_name == NULL) 337254225Speter switch (file_lock(sp, oname, ep->db->fd(ep->db), 0)) { 33819304Speter case LOCK_FAILED: 33919304Speter F_SET(frp, FR_UNLOCKED); 34019304Speter break; 34119304Speter case LOCK_UNAVAIL: 34219304Speter readonly = 1; 34319304Speter msgq_str(sp, M_INFO, oname, 34419304Speter "239|%s already locked, session is read-only"); 34519304Speter break; 34619304Speter case LOCK_SUCCESS: 34719304Speter break; 34819304Speter } 34919304Speter 35019304Speter /* 35119304Speter * Historically, the readonly edit option was set per edit buffer in 35219304Speter * vi, unless the -R command-line option was specified or the program 35319304Speter * was executed as "view". (Well, to be truthful, if the letter 'w' 35419304Speter * occurred anywhere in the program name, but let's not get into that.) 35519304Speter * So, the persistant readonly state has to be stored in the screen 35619304Speter * structure, and the edit option value toggles with the contents of 35719304Speter * the edit buffer. If the persistant readonly flag is set, set the 35819304Speter * readonly edit option. 35919304Speter * 36019304Speter * Otherwise, try and figure out if a file is readonly. This is a 36119304Speter * dangerous thing to do. The kernel is the only arbiter of whether 36219304Speter * or not a file is writeable, and the best that a user program can 36319304Speter * do is guess. Obvious loopholes are files that are on a file system 36419304Speter * mounted readonly (access catches this one on a few systems), or 36519304Speter * alternate protection mechanisms, ACL's for example, that we can't 36619304Speter * portably check. Lots of fun, and only here because users whined. 36719304Speter * 36819304Speter * !!! 36919304Speter * Historic vi displayed the readonly message if none of the file 37019304Speter * write bits were set, or if an an access(2) call on the path 37119304Speter * failed. This seems reasonable. If the file is mode 444, root 37219304Speter * users may want to know that the owner of the file did not expect 37319304Speter * it to be written. 37419304Speter * 37519304Speter * Historic vi set the readonly bit if no write bits were set for 37619304Speter * a file, even if the access call would have succeeded. This makes 37719304Speter * the superuser force the write even when vi expects that it will 37819304Speter * succeed. I'm less supportive of this semantic, but it's historic 37919304Speter * practice and the conservative approach to vi'ing files as root. 38019304Speter * 38119304Speter * It would be nice if there was some way to update this when the user 38219304Speter * does a "^Z; chmod ...". The problem is that we'd first have to 38319304Speter * distinguish between readonly bits set because of file permissions 38419304Speter * and those set for other reasons. That's not too hard, but deciding 38519304Speter * when to reevaluate the permissions is trickier. An alternative 38619304Speter * might be to turn off the readonly bit if the user forces a write 38719304Speter * and it succeeds. 38819304Speter * 38919304Speter * XXX 39019304Speter * Access(2) doesn't consider the effective uid/gid values. This 39119304Speter * probably isn't a problem for vi when it's running standalone. 39219304Speter */ 39319304Speter if (readonly || F_ISSET(sp, SC_READONLY) || 394254225Speter (!F_ISSET(frp, FR_NEWFILE) && 39519304Speter (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || 396254225Speter access(frp->name, W_OK)))) 39719304Speter O_SET(sp, O_READONLY); 39819304Speter else 39919304Speter O_CLR(sp, O_READONLY); 40019304Speter 40119304Speter /* Switch... */ 40219304Speter ++ep->refcnt; 40319304Speter sp->ep = ep; 40419304Speter sp->frp = frp; 40519304Speter 406254225Speter /* Detect and set the file encoding */ 407254225Speter file_encinit(sp); 408254225Speter 40919304Speter /* Set the initial cursor position, queue initial command. */ 41019304Speter file_cinit(sp); 41119304Speter 41219304Speter /* Redraw the screen from scratch, schedule a welcome message. */ 41319304Speter F_SET(sp, SC_SCR_REFORMAT | SC_STATUS); 41419304Speter 41519304Speter return (0); 41619304Speter 41719304Spetererr: if (frp->name != NULL) { 41819304Speter free(frp->name); 41919304Speter frp->name = NULL; 42019304Speter } 42119304Speter if (frp->tname != NULL) { 42219304Speter (void)unlink(frp->tname); 42319304Speter free(frp->tname); 42419304Speter frp->tname = NULL; 42519304Speter } 42619304Speter 42719304Speteroerr: if (F_ISSET(ep, F_RCV_ON)) 42819304Speter (void)unlink(ep->rcv_path); 42919304Speter if (ep->rcv_path != NULL) { 43019304Speter free(ep->rcv_path); 43119304Speter ep->rcv_path = NULL; 43219304Speter } 43319304Speter if (ep->db != NULL) 43419304Speter (void)ep->db->close(ep->db); 43519304Speter free(ep); 43619304Speter 43719304Speter return (open_err ? 43819304Speter file_init(sp, frp, rcv_name, flags | FS_OPENERR) : 1); 43919304Speter} 44019304Speter 44119304Speter/* 44219304Speter * file_spath -- 44319304Speter * Scan the user's path to find the file that we're going to 44419304Speter * try and open. 44519304Speter */ 44619304Speterstatic int 447254225Speterfile_spath( 448254225Speter SCR *sp, 449254225Speter FREF *frp, 450254225Speter struct stat *sbp, 451254225Speter int *existsp) 45219304Speter{ 453254225Speter int savech; 45419304Speter size_t len; 45519304Speter int found; 456254225Speter char *name, *p, *t, *path; 45719304Speter 45819304Speter /* 45919304Speter * If the name is NULL or an explicit reference (i.e., the first 46019304Speter * component is . or ..) ignore the O_PATH option. 46119304Speter */ 46219304Speter name = frp->name; 46319304Speter if (name == NULL) { 46419304Speter *existsp = 0; 46519304Speter return (0); 46619304Speter } 467254225Speter if (name[0] == '/' || (name[0] == '.' && 468254225Speter (name[1] == '/' || (name[1] == '.' && name[2] == '/')))) { 46919304Speter *existsp = !stat(name, sbp); 47019304Speter return (0); 47119304Speter } 47219304Speter 47319304Speter /* Try . */ 47419304Speter if (!stat(name, sbp)) { 47519304Speter *existsp = 1; 47619304Speter return (0); 47719304Speter } 47819304Speter 47919304Speter /* Try the O_PATH option values. */ 48019304Speter for (found = 0, p = t = O_STR(sp, O_PATH);; ++p) 48119304Speter if (*p == ':' || *p == '\0') { 482254225Speter /* 483254225Speter * Ignore the empty strings and ".", since we've already 484254225Speter * tried the current directory. 485254225Speter */ 486254225Speter if (t < p && (p - t != 1 || *t != '.')) { 48719304Speter savech = *p; 48819304Speter *p = '\0'; 489254225Speter if ((path = join(t, name)) == NULL) { 490254225Speter msgq(sp, M_SYSERR, NULL); 491254225Speter break; 492254225Speter } 493254225Speter len = strlen(path); 49419304Speter *p = savech; 49519304Speter if (!stat(path, sbp)) { 49619304Speter found = 1; 49719304Speter break; 49819304Speter } 499254225Speter free(path); 50019304Speter } 50119304Speter t = p + 1; 50219304Speter if (*p == '\0') 50319304Speter break; 50419304Speter } 50519304Speter 50619304Speter /* If we found it, build a new pathname and discard the old one. */ 50719304Speter if (found) { 50819304Speter free(frp->name); 509254225Speter frp->name = path; 51019304Speter } 51119304Speter *existsp = found; 51219304Speter return (0); 51319304Speter} 51419304Speter 51519304Speter/* 51619304Speter * file_cinit -- 51719304Speter * Set up the initial cursor position. 51819304Speter */ 51919304Speterstatic void 520254225Speterfile_cinit(SCR *sp) 52119304Speter{ 52219304Speter GS *gp; 52319304Speter MARK m; 52419304Speter size_t len; 52519304Speter int nb; 526254225Speter CHAR_T *wp; 527254225Speter size_t wlen; 52819304Speter 52919304Speter /* Set some basic defaults. */ 53019304Speter sp->lno = 1; 53119304Speter sp->cno = 0; 53219304Speter 53319304Speter /* 53419304Speter * Historically, initial commands (the -c option) weren't executed 53519304Speter * until a file was loaded, e.g. "vi +10 nofile", followed by an 53619304Speter * :edit or :tag command, would execute the +10 on the file loaded 53719304Speter * by the subsequent command, (assuming that it existed). This 53819304Speter * applied as well to files loaded using the tag commands, and we 53919304Speter * follow that historic practice. Also, all initial commands were 54019304Speter * ex commands and were always executed on the last line of the file. 54119304Speter * 54219304Speter * Otherwise, if no initial command for this file: 54319304Speter * If in ex mode, move to the last line, first nonblank character. 54419304Speter * If the file has previously been edited, move to the last known 54519304Speter * position, and check it for validity. 54619304Speter * Otherwise, move to the first line, first nonblank. 54719304Speter * 54819304Speter * This gets called by the file init code, because we may be in a 54919304Speter * file of ex commands and we want to execute them from the right 55019304Speter * location in the file. 55119304Speter */ 55219304Speter nb = 0; 55319304Speter gp = sp->gp; 55419304Speter if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) { 55519304Speter if (db_last(sp, &sp->lno)) 55619304Speter return; 55719304Speter if (sp->lno == 0) { 55819304Speter sp->lno = 1; 55919304Speter sp->cno = 0; 56019304Speter } 561254225Speter CHAR2INT(sp, gp->c_option, strlen(gp->c_option) + 1, 562254225Speter wp, wlen); 563254225Speter if (ex_run_str(sp, "-c option", wp, wlen - 1, 1, 1)) 56419304Speter return; 56519304Speter gp->c_option = NULL; 56619304Speter } else if (F_ISSET(sp, SC_EX)) { 56719304Speter if (db_last(sp, &sp->lno)) 56819304Speter return; 56919304Speter if (sp->lno == 0) { 57019304Speter sp->lno = 1; 57119304Speter sp->cno = 0; 57219304Speter return; 57319304Speter } 57419304Speter nb = 1; 57519304Speter } else { 57619304Speter if (F_ISSET(sp->frp, FR_CURSORSET)) { 57719304Speter sp->lno = sp->frp->lno; 57819304Speter sp->cno = sp->frp->cno; 57919304Speter 58019304Speter /* If returning to a file in vi, center the line. */ 58119304Speter F_SET(sp, SC_SCR_CENTER); 58219304Speter } else { 58319304Speter if (O_ISSET(sp, O_COMMENT)) 58419304Speter file_comment(sp); 58519304Speter else 58619304Speter sp->lno = 1; 58719304Speter nb = 1; 58819304Speter } 58919304Speter if (db_get(sp, sp->lno, 0, NULL, &len)) { 59019304Speter sp->lno = 1; 59119304Speter sp->cno = 0; 59219304Speter return; 59319304Speter } 59419304Speter if (!nb && sp->cno > len) 59519304Speter nb = 1; 59619304Speter } 59719304Speter if (nb) { 59819304Speter sp->cno = 0; 59919304Speter (void)nonblank(sp, sp->lno, &sp->cno); 60019304Speter } 60119304Speter 60219304Speter /* 60319304Speter * !!! 60419304Speter * The initial column is also the most attractive column. 60519304Speter */ 60619304Speter sp->rcm = sp->cno; 60719304Speter 60819304Speter /* 60919304Speter * !!! 61019304Speter * Historically, vi initialized the absolute mark, but ex did not. 61119304Speter * Which meant, that if the first command in ex mode was "visual", 61219304Speter * or if an ex command was executed first (e.g. vi +10 file) vi was 61319304Speter * entered without the mark being initialized. For consistency, if 61419304Speter * the file isn't empty, we initialize it for everyone, believing 61519304Speter * that it can't hurt, and is generally useful. Not initializing it 61619304Speter * if the file is empty is historic practice, although it has always 61719304Speter * been possible to set (and use) marks in empty vi files. 61819304Speter */ 61919304Speter m.lno = sp->lno; 62019304Speter m.cno = sp->cno; 62119304Speter (void)mark_set(sp, ABSMARK1, &m, 0); 62219304Speter} 62319304Speter 62419304Speter/* 62519304Speter * file_end -- 62619304Speter * Stop editing a file. 62719304Speter * 62819304Speter * PUBLIC: int file_end __P((SCR *, EXF *, int)); 62919304Speter */ 63019304Speterint 631254225Speterfile_end( 632254225Speter SCR *sp, 633254225Speter EXF *ep, 634254225Speter int force) 63519304Speter{ 63619304Speter FREF *frp; 63719304Speter 63819304Speter /* 63919304Speter * !!! 64019304Speter * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 64119304Speter * (If argument ep is NULL, use sp->ep.) 64219304Speter * 64319304Speter * If multiply referenced, just decrement the count and return. 64419304Speter */ 64519304Speter if (ep == NULL) 64619304Speter ep = sp->ep; 64719304Speter if (--ep->refcnt != 0) 64819304Speter return (0); 64919304Speter 65019304Speter /* 65119304Speter * 65219304Speter * Clean up the FREF structure. 65319304Speter * 65419304Speter * Save the cursor location. 65519304Speter * 65619304Speter * XXX 65719304Speter * It would be cleaner to do this somewhere else, but by the time 65819304Speter * ex or vi knows that we're changing files it's already happened. 65919304Speter */ 66019304Speter frp = sp->frp; 66119304Speter frp->lno = sp->lno; 66219304Speter frp->cno = sp->cno; 66319304Speter F_SET(frp, FR_CURSORSET); 66419304Speter 66519304Speter /* 66619304Speter * We may no longer need the temporary backing file, so clean it 66719304Speter * up. We don't need the FREF structure either, if the file was 66819304Speter * never named, so lose it. 66919304Speter * 67019304Speter * !!! 67119304Speter * Re: FR_DONTDELETE, see the comment above in file_init(). 67219304Speter */ 67319304Speter if (!F_ISSET(frp, FR_DONTDELETE) && frp->tname != NULL) { 67419304Speter if (unlink(frp->tname)) 67519304Speter msgq_str(sp, M_SYSERR, frp->tname, "240|%s: remove"); 67619304Speter free(frp->tname); 67719304Speter frp->tname = NULL; 67819304Speter if (F_ISSET(frp, FR_TMPFILE)) { 679254225Speter TAILQ_REMOVE(sp->gp->frefq, frp, q); 68019304Speter if (frp->name != NULL) 68119304Speter free(frp->name); 68219304Speter free(frp); 68319304Speter } 68419304Speter sp->frp = NULL; 68519304Speter } 68619304Speter 68719304Speter /* 68819304Speter * Clean up the EXF structure. 68919304Speter * 69019304Speter * Close the db structure. 69119304Speter */ 69219304Speter if (ep->db->close != NULL && ep->db->close(ep->db) && !force) { 69319304Speter msgq_str(sp, M_SYSERR, frp->name, "241|%s: close"); 69419304Speter ++ep->refcnt; 69519304Speter return (1); 69619304Speter } 69719304Speter 69819304Speter /* COMMITTED TO THE CLOSE. THERE'S NO GOING BACK... */ 69919304Speter 70019304Speter /* Stop logging. */ 70119304Speter (void)log_end(sp, ep); 70219304Speter 70319304Speter /* Free up any marks. */ 70419304Speter (void)mark_end(sp, ep); 70519304Speter 70619304Speter /* 70719304Speter * Delete recovery files, close the open descriptor, free recovery 70819304Speter * memory. See recover.c for a description of the protocol. 70919304Speter * 71019304Speter * XXX 71119304Speter * Unlink backup file first, we can detect that the recovery file 71219304Speter * doesn't reference anything when the user tries to recover it. 71319304Speter * There's a race, here, obviously, but it's fairly small. 71419304Speter */ 71519304Speter if (!F_ISSET(ep, F_RCV_NORM)) { 71619304Speter if (ep->rcv_path != NULL && unlink(ep->rcv_path)) 71719304Speter msgq_str(sp, M_SYSERR, ep->rcv_path, "242|%s: remove"); 71819304Speter if (ep->rcv_mpath != NULL && unlink(ep->rcv_mpath)) 71919304Speter msgq_str(sp, M_SYSERR, ep->rcv_mpath, "243|%s: remove"); 72019304Speter } 72119304Speter if (ep->rcv_fd != -1) 72219304Speter (void)close(ep->rcv_fd); 72319304Speter if (ep->rcv_path != NULL) 72419304Speter free(ep->rcv_path); 72519304Speter if (ep->rcv_mpath != NULL) 72619304Speter free(ep->rcv_mpath); 727254225Speter if (ep->c_blen > 0) 728254225Speter free(ep->c_lp); 72919304Speter 73019304Speter free(ep); 73119304Speter return (0); 73219304Speter} 73319304Speter 73419304Speter/* 73519304Speter * file_write -- 73619304Speter * Write the file to disk. Historic vi had fairly convoluted 73719304Speter * semantics for whether or not writes would happen. That's 73819304Speter * why all the flags. 73919304Speter * 74019304Speter * PUBLIC: int file_write __P((SCR *, MARK *, MARK *, char *, int)); 74119304Speter */ 74219304Speterint 743254225Speterfile_write( 744254225Speter SCR *sp, 745254225Speter MARK *fm, 746254225Speter MARK *tm, 747254225Speter char *name, 748254225Speter int flags) 74919304Speter{ 75019304Speter enum { NEWFILE, OLDFILE } mtype; 75119304Speter struct stat sb; 75219304Speter EXF *ep; 75319304Speter FILE *fp; 75419304Speter FREF *frp; 75519304Speter MARK from, to; 75619304Speter size_t len; 75719304Speter u_long nlno, nch; 75819304Speter int fd, nf, noname, oflags, rval; 759254225Speter char *p, *s, *t, buf[1024]; 76019304Speter const char *msgstr; 76119304Speter 76219304Speter ep = sp->ep; 76319304Speter frp = sp->frp; 76419304Speter 76519304Speter /* 76619304Speter * Writing '%', or naming the current file explicitly, has the 76719304Speter * same semantics as writing without a name. 76819304Speter */ 76919304Speter if (name == NULL || !strcmp(name, frp->name)) { 77019304Speter noname = 1; 77119304Speter name = frp->name; 77219304Speter } else 77319304Speter noname = 0; 77419304Speter 77519304Speter /* Can't write files marked read-only, unless forced. */ 77619304Speter if (!LF_ISSET(FS_FORCE) && noname && O_ISSET(sp, O_READONLY)) { 77719304Speter msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? 77819304Speter "244|Read-only file, not written; use ! to override" : 77919304Speter "245|Read-only file, not written"); 78019304Speter return (1); 78119304Speter } 78219304Speter 78319304Speter /* If not forced, not appending, and "writeany" not set ... */ 78419304Speter if (!LF_ISSET(FS_FORCE | FS_APPEND) && !O_ISSET(sp, O_WRITEANY)) { 78519304Speter /* Don't overwrite anything but the original file. */ 78619304Speter if ((!noname || F_ISSET(frp, FR_NAMECHANGE)) && 78719304Speter !stat(name, &sb)) { 78819304Speter msgq_str(sp, M_ERR, name, 78919304Speter LF_ISSET(FS_POSSIBLE) ? 79019304Speter "246|%s exists, not written; use ! to override" : 79119304Speter "247|%s exists, not written"); 79219304Speter return (1); 79319304Speter } 79419304Speter 79519304Speter /* 79619304Speter * Don't write part of any existing file. Only test for the 79719304Speter * original file, the previous test catches anything else. 79819304Speter */ 79919304Speter if (!LF_ISSET(FS_ALL) && noname && !stat(name, &sb)) { 80019304Speter msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? 80119304Speter "248|Partial file, not written; use ! to override" : 80219304Speter "249|Partial file, not written"); 80319304Speter return (1); 80419304Speter } 80519304Speter } 80619304Speter 80719304Speter /* 80819304Speter * Figure out if the file already exists -- if it doesn't, we display 80919304Speter * the "new file" message. The stat might not be necessary, but we 81019304Speter * just repeat it because it's easier than hacking the previous tests. 81119304Speter * The information is only used for the user message and modification 81219304Speter * time test, so we can ignore the obvious race condition. 81319304Speter * 81419304Speter * One final test. If we're not forcing or appending the current file, 81519304Speter * and we have a saved modification time, object if the file changed 81619304Speter * since we last edited or wrote it, and make them force it. 81719304Speter */ 81819304Speter if (stat(name, &sb)) 81919304Speter mtype = NEWFILE; 82019304Speter else { 82119304Speter if (noname && !LF_ISSET(FS_FORCE | FS_APPEND) && 822254225Speter ((F_ISSET(ep, F_DEVSET) && 823254225Speter (sb.st_dev != ep->mdev || sb.st_ino != ep->minode)) || 824254225Speter timespeccmp(&sb.st_mtimespec, &ep->mtim, !=))) { 82519304Speter msgq_str(sp, M_ERR, name, LF_ISSET(FS_POSSIBLE) ? 82619304Speter"250|%s: file modified more recently than this copy; use ! to override" : 82719304Speter"251|%s: file modified more recently than this copy"); 82819304Speter return (1); 82919304Speter } 83019304Speter 83119304Speter mtype = OLDFILE; 83219304Speter } 83319304Speter 83419304Speter /* Set flags to create, write, and either append or truncate. */ 83519304Speter oflags = O_CREAT | O_WRONLY | 83619304Speter (LF_ISSET(FS_APPEND) ? O_APPEND : O_TRUNC); 83719304Speter 83819304Speter /* Backup the file if requested. */ 83919304Speter if (!opts_empty(sp, O_BACKUP, 1) && 84019304Speter file_backup(sp, name, O_STR(sp, O_BACKUP)) && !LF_ISSET(FS_FORCE)) 84119304Speter return (1); 84219304Speter 84319304Speter /* Open the file. */ 84419304Speter SIGBLOCK; 84519304Speter if ((fd = open(name, oflags, 84619304Speter S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) < 0) { 84779444Sobrien if (errno == EACCES && LF_ISSET(FS_FORCE)) { 84879444Sobrien /* 84979444Sobrien * If the user owns the file but does not 85079444Sobrien * have write permission on it, grant it 85179444Sobrien * automatically for the duration of the 85279444Sobrien * opening of the file, if possible. 85379444Sobrien */ 85479444Sobrien struct stat sb; 85579444Sobrien mode_t fmode; 85679444Sobrien 85779444Sobrien if (stat(name, &sb) != 0) 85879444Sobrien goto fail_open; 85979444Sobrien fmode = sb.st_mode; 86079444Sobrien if (!(sb.st_mode & S_IWUSR) && sb.st_uid == getuid()) 86179444Sobrien fmode |= S_IWUSR; 86279444Sobrien else 86379444Sobrien goto fail_open; 86479444Sobrien if (chmod(name, fmode) != 0) 86579444Sobrien goto fail_open; 86679444Sobrien fd = open(name, oflags, S_IRUSR | S_IWUSR | 86779444Sobrien S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 86879444Sobrien if (fd == -1) 86979444Sobrien goto fail_open; 87079444Sobrien (void)fchmod(fd, sb.st_mode); 87179444Sobrien goto success_open; 87279444Sobrien fail_open: 87379444Sobrien errno = EACCES; 87479444Sobrien } 87519304Speter msgq_str(sp, M_SYSERR, name, "%s"); 87619304Speter SIGUNBLOCK; 87719304Speter return (1); 87819304Speter } 87979444Sobriensuccess_open: 88019304Speter SIGUNBLOCK; 88119304Speter 88219304Speter /* Try and get a lock. */ 883254225Speter if (!noname && file_lock(sp, NULL, fd, 0) == LOCK_UNAVAIL) 88419304Speter msgq_str(sp, M_ERR, name, 88519304Speter "252|%s: write lock was unavailable"); 88619304Speter 88719304Speter /* 88819304Speter * Use stdio for buffering. 88919304Speter * 89019304Speter * XXX 89119304Speter * SVR4.2 requires the fdopen mode exactly match the original open 89219304Speter * mode, i.e. you have to open with "a" if appending. 89319304Speter */ 89419304Speter if ((fp = fdopen(fd, LF_ISSET(FS_APPEND) ? "a" : "w")) == NULL) { 89519304Speter msgq_str(sp, M_SYSERR, name, "%s"); 89619304Speter (void)close(fd); 89719304Speter return (1); 89819304Speter } 89919304Speter 90019304Speter /* Build fake addresses, if necessary. */ 90119304Speter if (fm == NULL) { 90219304Speter from.lno = 1; 90319304Speter from.cno = 0; 90419304Speter fm = &from; 90519304Speter if (db_last(sp, &to.lno)) 90619304Speter return (1); 90719304Speter to.cno = 0; 90819304Speter tm = &to; 90919304Speter } 91019304Speter 91119304Speter rval = ex_writefp(sp, name, fp, fm, tm, &nlno, &nch, 0); 91219304Speter 91319304Speter /* 91419304Speter * Save the new last modification time -- even if the write fails 91519304Speter * we re-init the time. That way the user can clean up the disk 91619304Speter * and rewrite without having to force it. 91719304Speter */ 91819304Speter if (noname) 91919304Speter if (stat(name, &sb)) 920254225Speter timepoint_system(&ep->mtim); 92119304Speter else { 92219304Speter F_SET(ep, F_DEVSET); 92319304Speter ep->mdev = sb.st_dev; 92419304Speter ep->minode = sb.st_ino; 92519304Speter 926254225Speter ep->mtim = sb.st_mtimespec; 92719304Speter } 92819304Speter 92919304Speter /* 93019304Speter * If the write failed, complain loudly. ex_writefp() has already 93119304Speter * complained about the actual error, reinforce it if data was lost. 93219304Speter */ 93319304Speter if (rval) { 93419304Speter if (!LF_ISSET(FS_APPEND)) 93519304Speter msgq_str(sp, M_ERR, name, 93619304Speter "254|%s: WARNING: FILE TRUNCATED"); 93719304Speter return (1); 93819304Speter } 93919304Speter 94019304Speter /* 94119304Speter * Once we've actually written the file, it doesn't matter that the 94219304Speter * file name was changed -- if it was, we've already whacked it. 94319304Speter */ 94419304Speter F_CLR(frp, FR_NAMECHANGE); 94519304Speter 94619304Speter /* 94719304Speter * If wrote the entire file, and it wasn't by appending it to a file, 94819304Speter * clear the modified bit. If the file was written to the original 94919304Speter * file name and the file is a temporary, set the "no exit" bit. This 95019304Speter * permits the user to write the file and use it in the context of the 95119304Speter * filesystem, but still keeps them from discarding their changes by 95219304Speter * exiting. 95319304Speter */ 95419304Speter if (LF_ISSET(FS_ALL) && !LF_ISSET(FS_APPEND)) { 95519304Speter F_CLR(ep, F_MODIFIED); 95619304Speter if (F_ISSET(frp, FR_TMPFILE)) 95719304Speter if (noname) 95819304Speter F_SET(frp, FR_TMPEXIT); 95919304Speter else 96019304Speter F_CLR(frp, FR_TMPEXIT); 96119304Speter } 96219304Speter 96319304Speter p = msg_print(sp, name, &nf); 96419304Speter switch (mtype) { 96519304Speter case NEWFILE: 96619304Speter msgstr = msg_cat(sp, 96719304Speter "256|%s: new file: %lu lines, %lu characters", NULL); 96819304Speter len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch); 96919304Speter break; 97019304Speter case OLDFILE: 97119304Speter msgstr = msg_cat(sp, LF_ISSET(FS_APPEND) ? 97219304Speter "315|%s: appended: %lu lines, %lu characters" : 97319304Speter "257|%s: %lu lines, %lu characters", NULL); 97419304Speter len = snprintf(buf, sizeof(buf), msgstr, p, nlno, nch); 97519304Speter break; 97619304Speter default: 97719304Speter abort(); 97819304Speter } 97919304Speter 98019304Speter /* 98119304Speter * There's a nasty problem with long path names. Cscope and tags files 98219304Speter * can result in long paths and vi will request a continuation key from 98319304Speter * the user. Unfortunately, the user has typed ahead, and chaos will 98419304Speter * result. If we assume that the characters in the filenames only take 98519304Speter * a single screen column each, we can trim the filename. 98619304Speter */ 98719304Speter s = buf; 98819304Speter if (len >= sp->cols) { 98919304Speter for (s = buf, t = buf + strlen(p); s < t && 99019304Speter (*s != '/' || len >= sp->cols - 3); ++s, --len); 99119304Speter if (s == t) 99219304Speter s = buf; 99319304Speter else { 99419304Speter *--s = '.'; /* Leading ellipses. */ 99519304Speter *--s = '.'; 99619304Speter *--s = '.'; 99719304Speter } 99819304Speter } 99985526Sjkh msgq(sp, M_INFO, "%s", s); 100019304Speter if (nf) 100119304Speter FREE_SPACE(sp, p, 0); 100219304Speter return (0); 100319304Speter} 100419304Speter 100519304Speter/* 100619304Speter * file_backup -- 100719304Speter * Backup the about-to-be-written file. 100819304Speter * 100919304Speter * XXX 101019304Speter * We do the backup by copying the entire file. It would be nice to do 101119304Speter * a rename instead, but: (1) both files may not fit and we want to fail 101219304Speter * before doing the rename; (2) the backup file may not be on the same 101319304Speter * disk partition as the file being written; (3) there may be optional 101419304Speter * file information (MACs, DACs, whatever) that we won't get right if we 101519304Speter * recreate the file. So, let's not risk it. 101619304Speter */ 101719304Speterstatic int 1018254225Speterfile_backup( 1019254225Speter SCR *sp, 1020254225Speter char *name, 1021254225Speter char *bname) 102219304Speter{ 102319304Speter struct dirent *dp; 102419304Speter struct stat sb; 102519304Speter DIR *dirp; 102619304Speter EXCMD cmd; 102719304Speter off_t off; 102819304Speter size_t blen; 102919304Speter int flags, maxnum, nr, num, nw, rfd, wfd, version; 103019304Speter char *bp, *estr, *p, *pct, *slash, *t, *wfname, buf[8192]; 1031254225Speter CHAR_T *wp; 1032254225Speter size_t wlen; 1033254225Speter size_t nlen; 1034254225Speter char *d = NULL; 103519304Speter 103619304Speter rfd = wfd = -1; 103719304Speter bp = estr = wfname = NULL; 103819304Speter 103919304Speter /* 104019304Speter * Open the current file for reading. Do this first, so that 104119304Speter * we don't exec a shell before the most likely failure point. 104219304Speter * If it doesn't exist, it's okay, there's just nothing to back 104319304Speter * up. 104419304Speter */ 104519304Speter errno = 0; 104619304Speter if ((rfd = open(name, O_RDONLY, 0)) < 0) { 104719304Speter if (errno == ENOENT) 104819304Speter return (0); 104919304Speter estr = name; 105019304Speter goto err; 105119304Speter } 105219304Speter 105319304Speter /* 105419304Speter * If the name starts with an 'N' character, add a version number 105519304Speter * to the name. Strip the leading N from the string passed to the 105619304Speter * expansion routines, for no particular reason. It would be nice 105719304Speter * to permit users to put the version number anywhere in the backup 105819304Speter * name, but there isn't a special character that we can use in the 105919304Speter * name, and giving a new character a special meaning leads to ugly 106019304Speter * hacks both here and in the supporting ex routines. 106119304Speter * 106219304Speter * Shell and file name expand the option's value. 106319304Speter */ 1064254225Speter ex_cinit(sp, &cmd, 0, 0, 0, 0, 0); 106519304Speter if (bname[0] == 'N') { 106619304Speter version = 1; 106719304Speter ++bname; 106819304Speter } else 106919304Speter version = 0; 1070254225Speter CHAR2INT(sp, bname, strlen(bname), wp, wlen); 1071254225Speter if ((wp = v_wstrdup(sp, wp, wlen)) == NULL) 107219304Speter return (1); 1073254225Speter if (argv_exp2(sp, &cmd, wp, wlen)) { 1074254225Speter free(wp); 1075254225Speter return (1); 1076254225Speter } 1077254225Speter free(wp); 107819304Speter 107919304Speter /* 108019304Speter * 0 args: impossible. 108119304Speter * 1 args: use it. 108219304Speter * >1 args: object, too many args. 108319304Speter */ 108419304Speter if (cmd.argc != 1) { 108519304Speter msgq_str(sp, M_ERR, bname, 108619304Speter "258|%s expanded into too many file names"); 108719304Speter (void)close(rfd); 108819304Speter return (1); 108919304Speter } 109019304Speter 109119304Speter /* 109219304Speter * If appending a version number, read through the directory, looking 109319304Speter * for file names that match the name followed by a number. Make all 109419304Speter * of the other % characters in name literal, so the user doesn't get 109519304Speter * surprised and sscanf doesn't drop core indirecting through pointers 109619304Speter * that don't exist. If any such files are found, increment its number 109719304Speter * by one. 109819304Speter */ 109919304Speter if (version) { 1100254225Speter GET_SPACE_GOTOC(sp, bp, blen, cmd.argv[0]->len * 2 + 50); 1101254225Speter INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, 1102254225Speter p, nlen); 1103254225Speter d = strdup(p); 1104254225Speter p = d; 1105254225Speter for (t = bp, slash = NULL; 1106254225Speter p[0] != '\0'; *t++ = *p++) 110719304Speter if (p[0] == '%') { 110819304Speter if (p[1] != '%') 110919304Speter *t++ = '%'; 111019304Speter } else if (p[0] == '/') 111119304Speter slash = t; 111219304Speter pct = t; 111319304Speter *t++ = '%'; 111419304Speter *t++ = 'd'; 111519304Speter *t = '\0'; 111619304Speter 111719304Speter if (slash == NULL) { 111819304Speter dirp = opendir("."); 111919304Speter p = bp; 112019304Speter } else { 112119304Speter *slash = '\0'; 112219304Speter dirp = opendir(bp); 112319304Speter *slash = '/'; 112419304Speter p = slash + 1; 112519304Speter } 112619304Speter if (dirp == NULL) { 1127254225Speter INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, 1128254225Speter estr, nlen); 112919304Speter goto err; 113019304Speter } 113119304Speter 113219304Speter for (maxnum = 0; (dp = readdir(dirp)) != NULL;) 113319304Speter if (sscanf(dp->d_name, p, &num) == 1 && num > maxnum) 113419304Speter maxnum = num; 113519304Speter (void)closedir(dirp); 113619304Speter 113719304Speter /* Format the backup file name. */ 113819304Speter (void)snprintf(pct, blen - (pct - bp), "%d", maxnum + 1); 113919304Speter wfname = bp; 114019304Speter } else { 114119304Speter bp = NULL; 1142254225Speter INT2CHAR(sp, cmd.argv[0]->bp, cmd.argv[0]->len + 1, 1143254225Speter wfname, nlen); 114419304Speter } 114519304Speter 114619304Speter /* Open the backup file, avoiding lurkers. */ 114719304Speter if (stat(wfname, &sb) == 0) { 114819304Speter if (!S_ISREG(sb.st_mode)) { 114919304Speter msgq_str(sp, M_ERR, bname, 115019304Speter "259|%s: not a regular file"); 115119304Speter goto err; 115219304Speter } 115319304Speter if (sb.st_uid != getuid()) { 115419304Speter msgq_str(sp, M_ERR, bname, "260|%s: not owned by you"); 115519304Speter goto err; 115619304Speter } 115719304Speter if (sb.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) { 115819304Speter msgq_str(sp, M_ERR, bname, 115919304Speter "261|%s: accessible by a user other than the owner"); 116019304Speter goto err; 116119304Speter } 116219304Speter flags = O_TRUNC; 116319304Speter } else 116419304Speter flags = O_CREAT | O_EXCL; 116519304Speter if ((wfd = open(wfname, flags | O_WRONLY, S_IRUSR | S_IWUSR)) < 0) { 116619304Speter estr = bname; 116719304Speter goto err; 116819304Speter } 116919304Speter 117019304Speter /* Copy the file's current contents to its backup value. */ 117119304Speter while ((nr = read(rfd, buf, sizeof(buf))) > 0) 117219304Speter for (off = 0; nr != 0; nr -= nw, off += nw) 117319304Speter if ((nw = write(wfd, buf + off, nr)) < 0) { 117419304Speter estr = wfname; 117519304Speter goto err; 117619304Speter } 117719304Speter if (nr < 0) { 117819304Speter estr = name; 117919304Speter goto err; 118019304Speter } 118119304Speter 118219304Speter if (close(rfd)) { 118319304Speter estr = name; 118419304Speter goto err; 118519304Speter } 118619304Speter if (close(wfd)) { 118719304Speter estr = wfname; 118819304Speter goto err; 118919304Speter } 119019304Speter if (bp != NULL) 119119304Speter FREE_SPACE(sp, bp, blen); 119219304Speter return (0); 119319304Speter 119419304Speteralloc_err: 119519304Spetererr: if (rfd != -1) 119619304Speter (void)close(rfd); 119719304Speter if (wfd != -1) { 119819304Speter (void)unlink(wfname); 119919304Speter (void)close(wfd); 120019304Speter } 120119304Speter if (estr) 120219304Speter msgq_str(sp, M_SYSERR, estr, "%s"); 1203254225Speter if (d != NULL) 1204254225Speter free(d); 120519304Speter if (bp != NULL) 120619304Speter FREE_SPACE(sp, bp, blen); 120719304Speter return (1); 120819304Speter} 120919304Speter 121019304Speter/* 1211254225Speter * file_encinit -- 1212254225Speter * Read the first line and set the O_FILEENCODING. 1213254225Speter */ 1214254225Speterstatic void 1215254225Speterfile_encinit(SCR *sp) 1216254225Speter{ 1217254225Speter#if defined(USE_WIDECHAR) && defined(USE_ICONV) 1218254225Speter size_t len; 1219254225Speter char *p; 1220254225Speter size_t blen = 0; 1221254225Speter char buf[4096]; /* not need to be '\0'-terminated */ 1222254225Speter recno_t ln = 1; 1223254225Speter EXF *ep; 1224254225Speter 1225254225Speter ep = sp->ep; 1226254225Speter 1227254225Speter while (!db_rget(sp, ln++, &p, &len)) { 1228254225Speter if (blen + len > sizeof(buf)) 1229254225Speter len = sizeof(buf) - blen; 1230254225Speter memcpy(buf + blen, p, len); 1231254225Speter blen += len; 1232254225Speter if (blen == sizeof(buf)) 1233254225Speter break; 1234254225Speter else 1235254225Speter buf[blen++] = '\n'; 1236254225Speter } 1237254225Speter 1238254225Speter /* 1239254225Speter * Detect UTF-8 and fallback to the locale/preset encoding. 1240254225Speter * 1241254225Speter * XXX 1242254225Speter * A manually set O_FILEENCODING indicates the "fallback 1243254225Speter * encoding", but UTF-8, which can be safely detected, is not 1244254225Speter * inherited from the old screen. 1245254225Speter */ 1246254225Speter if (looks_utf8(buf, blen) > 1) 1247254225Speter o_set(sp, O_FILEENCODING, OS_STRDUP, "utf-8", 0); 1248254225Speter else if (!O_ISSET(sp, O_FILEENCODING) || 1249254225Speter !strncasecmp(O_STR(sp, O_FILEENCODING), "utf-8", 5)) 1250254225Speter o_set(sp, O_FILEENCODING, OS_STRDUP, codeset(), 0); 1251254225Speter 1252254225Speter conv_enc(sp, O_FILEENCODING, 0); 1253254225Speter#endif 1254254225Speter} 1255254225Speter 1256254225Speter/* 125719304Speter * file_comment -- 125819304Speter * Skip the first comment. 125919304Speter */ 126019304Speterstatic void 1261254225Speterfile_comment(SCR *sp) 126219304Speter{ 126319304Speter recno_t lno; 126419304Speter size_t len; 1265254225Speter CHAR_T *p; 126619304Speter 126719304Speter for (lno = 1; !db_get(sp, lno, 0, &p, &len) && len == 0; ++lno); 126819304Speter if (p == NULL) 126919304Speter return; 127019304Speter if (p[0] == '#') { 127119304Speter F_SET(sp, SC_SCR_TOP); 127219304Speter while (!db_get(sp, ++lno, 0, &p, &len)) 127319304Speter if (len < 1 || p[0] != '#') { 127419304Speter sp->lno = lno; 127519304Speter return; 127619304Speter } 127719304Speter } else if (len > 1 && p[0] == '/' && p[1] == '*') { 127819304Speter F_SET(sp, SC_SCR_TOP); 127919304Speter do { 128019304Speter for (; len > 1; --len, ++p) 128119304Speter if (p[0] == '*' && p[1] == '/') { 128219304Speter sp->lno = lno; 128319304Speter return; 128419304Speter } 128519304Speter } while (!db_get(sp, ++lno, 0, &p, &len)); 128619304Speter } else if (len > 1 && p[0] == '/' && p[1] == '/') { 128719304Speter F_SET(sp, SC_SCR_TOP); 128819304Speter p += 2; 128919304Speter len -= 2; 129019304Speter do { 129119304Speter for (; len > 1; --len, ++p) 129219304Speter if (p[0] == '/' && p[1] == '/') { 129319304Speter sp->lno = lno; 129419304Speter return; 129519304Speter } 129619304Speter } while (!db_get(sp, ++lno, 0, &p, &len)); 129719304Speter } 129819304Speter} 129919304Speter 130019304Speter/* 130119304Speter * file_m1 -- 130219304Speter * First modification check routine. The :next, :prev, :rewind, :tag, 130319304Speter * :tagpush, :tagpop, ^^ modifications check. 130419304Speter * 130519304Speter * PUBLIC: int file_m1 __P((SCR *, int, int)); 130619304Speter */ 130719304Speterint 1308254225Speterfile_m1( 1309254225Speter SCR *sp, 1310254225Speter int force, 1311254225Speter int flags) 131219304Speter{ 131319304Speter EXF *ep; 131419304Speter 131519304Speter ep = sp->ep; 131619304Speter 131719304Speter /* If no file loaded, return no modifications. */ 131819304Speter if (ep == NULL) 131919304Speter return (0); 132019304Speter 132119304Speter /* 132219304Speter * If the file has been modified, we'll want to write it back or 132319304Speter * fail. If autowrite is set, we'll write it back automatically, 132419304Speter * unless force is also set. Otherwise, we fail unless forced or 132519304Speter * there's another open screen on this file. 132619304Speter */ 132719304Speter if (F_ISSET(ep, F_MODIFIED)) 132819304Speter if (O_ISSET(sp, O_AUTOWRITE)) { 132919304Speter if (!force && file_aw(sp, flags)) 133019304Speter return (1); 133119304Speter } else if (ep->refcnt <= 1 && !force) { 133219304Speter msgq(sp, M_ERR, LF_ISSET(FS_POSSIBLE) ? 133319304Speter"262|File modified since last complete write; write or use ! to override" : 133419304Speter"263|File modified since last complete write; write or use :edit! to override"); 133519304Speter return (1); 133619304Speter } 133719304Speter 133819304Speter return (file_m3(sp, force)); 133919304Speter} 134019304Speter 134119304Speter/* 134219304Speter * file_m2 -- 134319304Speter * Second modification check routine. The :edit, :quit, :recover 134419304Speter * modifications check. 134519304Speter * 134619304Speter * PUBLIC: int file_m2 __P((SCR *, int)); 134719304Speter */ 134819304Speterint 1349254225Speterfile_m2( 1350254225Speter SCR *sp, 1351254225Speter int force) 135219304Speter{ 135319304Speter EXF *ep; 135419304Speter 135519304Speter ep = sp->ep; 135619304Speter 135719304Speter /* If no file loaded, return no modifications. */ 135819304Speter if (ep == NULL) 135919304Speter return (0); 136019304Speter 136119304Speter /* 136219304Speter * If the file has been modified, we'll want to fail, unless forced 136319304Speter * or there's another open screen on this file. 136419304Speter */ 136519304Speter if (F_ISSET(ep, F_MODIFIED) && ep->refcnt <= 1 && !force) { 136619304Speter msgq(sp, M_ERR, 136719304Speter"264|File modified since last complete write; write or use ! to override"); 136819304Speter return (1); 136919304Speter } 137019304Speter 137119304Speter return (file_m3(sp, force)); 137219304Speter} 137319304Speter 137419304Speter/* 137519304Speter * file_m3 -- 137619304Speter * Third modification check routine. 137719304Speter * 137819304Speter * PUBLIC: int file_m3 __P((SCR *, int)); 137919304Speter */ 138019304Speterint 1381254225Speterfile_m3( 1382254225Speter SCR *sp, 1383254225Speter int force) 138419304Speter{ 138519304Speter EXF *ep; 138619304Speter 138719304Speter ep = sp->ep; 138819304Speter 138919304Speter /* If no file loaded, return no modifications. */ 139019304Speter if (ep == NULL) 139119304Speter return (0); 139219304Speter 139319304Speter /* 139419304Speter * Don't exit while in a temporary files if the file was ever modified. 139519304Speter * The problem is that if the user does a ":wq", we write and quit, 139619304Speter * unlinking the temporary file. Not what the user had in mind at all. 139719304Speter * We permit writing to temporary files, so that user maps using file 139819304Speter * system names work with temporary files. 139919304Speter */ 140019304Speter if (F_ISSET(sp->frp, FR_TMPEXIT) && ep->refcnt <= 1 && !force) { 140119304Speter msgq(sp, M_ERR, 140219304Speter "265|File is a temporary; exit will discard modifications"); 140319304Speter return (1); 140419304Speter } 140519304Speter return (0); 140619304Speter} 140719304Speter 140819304Speter/* 140919304Speter * file_aw -- 141019304Speter * Autowrite routine. If modified, autowrite is set and the readonly bit 141119304Speter * is not set, write the file. A routine so there's a place to put the 141219304Speter * comment. 141319304Speter * 141419304Speter * PUBLIC: int file_aw __P((SCR *, int)); 141519304Speter */ 141619304Speterint 1417254225Speterfile_aw( 1418254225Speter SCR *sp, 1419254225Speter int flags) 142019304Speter{ 142119304Speter if (!F_ISSET(sp->ep, F_MODIFIED)) 142219304Speter return (0); 142319304Speter if (!O_ISSET(sp, O_AUTOWRITE)) 142419304Speter return (0); 142519304Speter 142619304Speter /* 142719304Speter * !!! 142819304Speter * Historic 4BSD vi attempted to write the file if autowrite was set, 142919304Speter * regardless of the writeability of the file (as defined by the file 143019304Speter * readonly flag). System V changed this as some point, not attempting 143119304Speter * autowrite if the file was readonly. This feels like a bug fix to 143219304Speter * me (e.g. the principle of least surprise is violated if readonly is 143319304Speter * set and vi writes the file), so I'm compatible with System V. 143419304Speter */ 143519304Speter if (O_ISSET(sp, O_READONLY)) { 143619304Speter msgq(sp, M_INFO, 143719304Speter "266|File readonly, modifications not auto-written"); 143819304Speter return (1); 143919304Speter } 144019304Speter return (file_write(sp, NULL, NULL, NULL, flags)); 144119304Speter} 144219304Speter 144319304Speter/* 144419304Speter * set_alt_name -- 144519304Speter * Set the alternate pathname. 144619304Speter * 144719304Speter * Set the alternate pathname. It's a routine because I wanted some place 144819304Speter * to hang this comment. The alternate pathname (normally referenced using 144919304Speter * the special character '#' during file expansion and in the vi ^^ command) 145019304Speter * is set by almost all ex commands that take file names as arguments. The 145119304Speter * rules go something like this: 145219304Speter * 145319304Speter * 1: If any ex command takes a file name as an argument (except for the 145419304Speter * :next command), the alternate pathname is set to that file name. 145519304Speter * This excludes the command ":e" and ":w !command" as no file name 145619304Speter * was specified. Note, historically, the :source command did not set 145719304Speter * the alternate pathname. It does in nvi, for consistency. 145819304Speter * 145919304Speter * 2: However, if any ex command sets the current pathname, e.g. the 146019304Speter * ":e file" or ":rew" commands succeed, then the alternate pathname 146119304Speter * is set to the previous file's current pathname, if it had one. 146219304Speter * This includes the ":file" command and excludes the ":e" command. 146319304Speter * So, by rule #1 and rule #2, if ":edit foo" fails, the alternate 146419304Speter * pathname will be "foo", if it succeeds, the alternate pathname will 146519304Speter * be the previous current pathname. The ":e" command will not set 146619304Speter * the alternate or current pathnames regardless. 146719304Speter * 146819304Speter * 3: However, if it's a read or write command with a file argument and 146919304Speter * the current pathname has not yet been set, the file name becomes 147019304Speter * the current pathname, and the alternate pathname is unchanged. 147119304Speter * 147219304Speter * If the user edits a temporary file, there may be times when there is no 147319304Speter * alternative file name. A name argument of NULL turns it off. 147419304Speter * 147519304Speter * PUBLIC: void set_alt_name __P((SCR *, char *)); 147619304Speter */ 147719304Spetervoid 1478254225Speterset_alt_name( 1479254225Speter SCR *sp, 1480254225Speter char *name) 148119304Speter{ 148219304Speter if (sp->alt_name != NULL) 148319304Speter free(sp->alt_name); 148419304Speter if (name == NULL) 148519304Speter sp->alt_name = NULL; 148619304Speter else if ((sp->alt_name = strdup(name)) == NULL) 148719304Speter msgq(sp, M_SYSERR, NULL); 148819304Speter} 148919304Speter 149019304Speter/* 149119304Speter * file_lock -- 149219304Speter * Get an exclusive lock on a file. 149319304Speter * 1494254225Speter * PUBLIC: lockr_t file_lock __P((SCR *, char *, int, int)); 149519304Speter */ 149619304Speterlockr_t 1497254225Speterfile_lock( 1498254225Speter SCR *sp, 1499254225Speter char *name, 1500254225Speter int fd, 1501254225Speter int iswrite) 150219304Speter{ 150319304Speter if (!O_ISSET(sp, O_LOCKFILES)) 150419304Speter return (LOCK_SUCCESS); 150519304Speter 150619304Speter /* 150719304Speter * !!! 150819304Speter * We need to distinguish a lock not being available for the file 150919304Speter * from the file system not supporting locking. Flock is documented 151019304Speter * as returning EWOULDBLOCK; add EAGAIN for good measure, and assume 151119304Speter * they are the former. There's no portable way to do this. 151219304Speter */ 151319304Speter errno = 0; 151449457Sbrian if (!flock(fd, LOCK_EX | LOCK_NB)) { 151549457Sbrian fcntl(fd, F_SETFD, 1); 151649457Sbrian return (LOCK_SUCCESS); 151749457Sbrian } 151849457Sbrian return (errno == EAGAIN 151919304Speter#ifdef EWOULDBLOCK 152019304Speter || errno == EWOULDBLOCK 152119304Speter#endif 152249457Sbrian ? LOCK_UNAVAIL : LOCK_FAILED); 152319304Speter} 1524