📄 exf.c
字号:
/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)exf.c 8.72 (Berkeley) 3/25/94";#endif /* not lint */#include <sys/param.h>#include <sys/queue.h>#include <sys/stat.h>#include <sys/time.h>/* * We include <sys/file.h>, because the flock(2) #defines were * found there on historical systems. We also include <fcntl.h> * because the open(2) #defines are found there on newer systems. */#include <sys/file.h>#include <bitstring.h>#include <errno.h>#include <fcntl.h>#include <limits.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <termios.h>#include <unistd.h>#include "compat.h"#include <db.h>#include <regex.h>#include <pathnames.h>#include "vi.h"#include "excmd.h"/* * file_add -- * Insert a file name into the FREF list, if it doesn't already * appear in it. * * !!! * The "if it doesn't already appear" changes vi's semantics slightly. If * you do a "vi foo bar", and then execute "next bar baz", the edit of bar * will reflect the line/column of the previous edit session. Historic nvi * did not do this. The change is a logical extension of the change where * vi now remembers the last location in any file that it has ever edited, * not just the previously edited file. */FREF *file_add(sp, frp_append, name, ignore) SCR *sp; FREF *frp_append; CHAR_T *name; int ignore;{ FREF *frp; char *p; /* * Return it if it already exists. Note that we test against the * user's current name, whatever that happens to be, including if * it's a temporary file. If the user is trying to set an argument * list, the ignore argument will be on -- if we're ignoring the * file turn off the ignore bit, so it's back in the argument list. */ if (name != NULL) for (frp = sp->frefq.cqh_first; frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) if ((p = FILENAME(frp)) != NULL && !strcmp(p, name)) { if (!ignore) F_CLR(frp, FR_IGNORE); return (frp); } /* Allocate and initialize the FREF structure. */ CALLOC(sp, frp, FREF *, 1, sizeof(FREF)); if (frp == NULL) return (NULL); /* * If no file name specified, or if the file name is a request * for something temporary, file_init() will allocate the file * name. Temporary files are always ignored. */#define TEMPORARY_FILE_STRING "/tmp" if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) && (frp->name = strdup(name)) == NULL) { FREE(frp, sizeof(FREF)); msgq(sp, M_SYSERR, NULL); return (NULL); } /* Only the initial argument list is "remembered". */ if (ignore) F_SET(frp, FR_IGNORE); /* Append into the chain of file names. */ if (frp_append != NULL) { CIRCLEQ_INSERT_AFTER(&sp->frefq, frp_append, frp, q); } else CIRCLEQ_INSERT_TAIL(&sp->frefq, frp, q); return (frp);}/* * file_first -- * Return the first file name for editing, if any. */FREF *file_first(sp) SCR *sp;{ FREF *frp; /* Return the first file name. */ for (frp = sp->frefq.cqh_first; frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) if (!F_ISSET(frp, FR_IGNORE)) return (frp); return (NULL);}/* * file_next -- * Return the next file name, if any. */FREF *file_next(sp, frp) SCR *sp; FREF *frp;{ while ((frp = frp->q.cqe_next) != (FREF *)&sp->frefq) if (!F_ISSET(frp, FR_IGNORE)) return (frp); return (NULL);}/* * file_prev -- * Return the previous file name, if any. */FREF *file_prev(sp, frp) SCR *sp; FREF *frp;{ while ((frp = frp->q.cqe_prev) != (FREF *)&sp->frefq) if (!F_ISSET(frp, FR_IGNORE)) return (frp); return (NULL);}/* * file_unedited -- * Return if there are files that aren't ignored and are unedited. */FREF *file_unedited(sp) SCR *sp;{ FREF *frp; /* Return the next file name. */ for (frp = sp->frefq.cqh_first; frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) if (!F_ISSET(frp, FR_EDITED | FR_IGNORE)) return (frp); return (NULL);}/* * file_init -- * Start editing a file, based on the FREF structure. If successsful, * let go of any previous file. Don't release the previous file until * absolutely sure we have the new one. */intfile_init(sp, frp, rcv_name, force) SCR *sp; FREF *frp; char *rcv_name; int force;{ EXF *ep; RECNOINFO oinfo; struct stat sb; size_t psize; int fd; char *p, *oname, tname[MAXPATHLEN]; /* * Required ep initialization: * Flush the line caches. * Default recover mail file fd to -1. * Set initial EXF flag bits. */ CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF)); ep->c_lno = ep->c_nlines = OOBLNO; ep->rcv_fd = -1; LIST_INIT(&ep->marks); F_SET(ep, F_FIRSTMODIFY); /* * If no name or backing file, create a backing temporary file, saving * the temp file name so can later unlink it. Repoint the name to the * temporary name (we display it to the user until they rename it). * There are some games we play with the FR_FREE_TNAME and FR_NONAME * flags (see ex/ex_file.c) to make sure that the temporary memory gets * free'd up. */ if ((oname = FILENAME(frp)) == NULL || stat(oname, &sb)) { (void)snprintf(tname, sizeof(tname), "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY)); if ((fd = mkstemp(tname)) == -1) { msgq(sp, M_SYSERR, "Temporary file"); goto err; } (void)close(fd); if ((frp->tname = strdup(tname)) == NULL) { msgq(sp, M_SYSERR, NULL); (void)unlink(tname); goto err; } oname = frp->tname; psize = 4 * 1024; F_SET(frp, FR_NEWFILE); } else { /* Try to keep it at 10 pages or less per file. */ if (sb.st_size < 40 * 1024) psize = 4 * 1024; else if (sb.st_size < 320 * 1024) psize = 32 * 1024; else psize = 64 * 1024; frp->mtime = sb.st_mtime; if (!S_ISREG(sb.st_mode)) msgq(sp, M_ERR, "Warning: %s is not a regular file.", oname); } /* Set up recovery. */ memset(&oinfo, 0, sizeof(RECNOINFO)); oinfo.bval = '\n'; /* Always set. */ oinfo.psize = psize; oinfo.flags = F_ISSET(sp->gp, G_SNAPSHOT) ? R_SNAPSHOT : 0; if (rcv_name == NULL) { if (!rcv_tmp(sp, ep, FILENAME(frp))) oinfo.bfname = ep->rcv_path; } else { if ((ep->rcv_path = strdup(rcv_name)) == NULL) { msgq(sp, M_SYSERR, NULL); goto err; } oinfo.bfname = ep->rcv_path; F_SET(ep, F_MODIFIED); } /* Open a db structure. */ if ((ep->db = dbopen(rcv_name == NULL ? oname : NULL, O_NONBLOCK | O_RDONLY, DEFFILEMODE, DB_RECNO, &oinfo)) == NULL) { msgq(sp, M_SYSERR, rcv_name == NULL ? oname : rcv_name); goto err; } /* Init file marks. */ if (mark_init(sp, ep)) goto err; /* Start logging. */ if (log_init(sp, ep)) goto err; /* * The -R flag, or doing a "set readonly" during a session causes * all files edited during the session (using an edit command, or * even using tags) to be marked read-only. Changing the file name * (see ex/ex_file.c), clears this flag. * * Otherwise, try and figure out if a file is readonly. This is a * dangerous thing to do. The kernel is the only arbiter of whether * or not a file is writeable, and the best that a user program can * do is guess. Obvious loopholes are files that are on a file system * mounted readonly (access catches this one on a few systems), or * alternate protection mechanisms, ACL's for example, that we can't * portably check. Lots of fun, and only here because users whined. * * !!! * Historic vi displayed the readonly message if none of the file * write bits were set, or if an an access(2) call on the path * failed. This seems reasonable. If the file is mode 444, root * users may want to know that the owner of the file did not expect * it to be written. * * Historic vi set the readonly bit if no write bits were set for * a file, even if the access call would have succeeded. This makes * the superuser force the write even when vi expects that it will * succeed. I'm less supportive of this semantic, but it's historic * practice and the conservative approach to vi'ing files as root. * * It would be nice if there was some way to update this when the user * does a "^Z; chmod ...". The problem is that we'd first have to * distinguish between readonly bits set because of file permissions * and those set for other reasons. That's not too hard, but deciding * when to reevaluate the permissions is trickier. An alternative * might be to turn off the readonly bit if the user forces a write * and it succeeds. * * XXX * Access(2) doesn't consider the effective uid/gid values. This * probably isn't a problem for vi when it's running standalone. */ if (O_ISSET(sp, O_READONLY) || !F_ISSET(frp, FR_NEWFILE) && (!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) || access(FILENAME(frp), W_OK))) F_SET(frp, FR_RDONLY); else F_CLR(frp, FR_RDONLY);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -