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