📄 fileio.c
字号:
/* vi:set ts=8 sts=4 sw=4:
*
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
* Do ":help credits" in Vim to see a list of people who contributed.
*/
/*
* fileio.c: read from and write to a file
*/
#if defined(MSDOS) || defined(WIN32)
# include <io.h> /* for lseek(), must be before vim.h */
#endif
#if defined __EMX__
# include <io.h> /* for mktemp(), CJW 1997-12-03 */
#endif
#include "vim.h"
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef LATTICE
# include <proto/dos.h> /* for Lock() and UnLock() */
#endif
#define BUFSIZE 8192 /* size of normal write buffer */
#define SMBUFSIZE 256 /* size of emergency write buffer */
#ifdef VIMINFO
static void check_marks_read __ARGS((void));
#endif
#ifdef UNIX
static void set_file_time __ARGS((char_u *fname, time_t atime, time_t mtime));
#endif
static void msg_add_fname __ARGS((BUF *, char_u *));
static int msg_add_fileformat __ARGS((int eol_type));
static void msg_add_lines __ARGS((int, long, long));
static void msg_add_eol __ARGS((void));
static int check_mtime __ARGS((BUF *buf, struct stat *s));
static int write_buf __ARGS((int, char_u *, int));
static linenr_t write_no_eol_lnum = 0; /* non-zero lnum when last line of
next binary write should not have
an end-of-line */
void
filemess(buf, name, s, attr)
BUF *buf;
char_u *name;
char_u *s;
int attr;
{
int msg_scroll_save;
msg_add_fname(buf, name); /* put file name in IObuff with quotes */
STRCAT(IObuff, s);
/*
* For the first message may have to start a new line.
* For further ones overwrite the previous one, reset msg_scroll before
* calling filemess().
*/
msg_scroll_save = msg_scroll;
if (shortmess(SHM_OVERALL))
msg_scroll = FALSE;
msg_start();
msg_scroll = msg_scroll_save;
msg_outtrans_attr(IObuff, attr);
msg_clr_eos();
out_flush();
}
/*
* Read lines from file 'fname' into the buffer after line 'from'.
*
* 1. We allocate blocks with lalloc, as big as possible.
* 2. Each block is filled with characters from the file with a single read().
* 3. The lines are inserted in the buffer with ml_append().
*
* (caller must check that fname != NULL, unless READ_STDIN is used)
*
* lines_to_skip is the number of lines that must be skipped
* lines_to_read is the number of lines that are appended
* When not recovering lines_to_skip is 0 and lines_to_read MAXLNUM.
*
* flags:
* READ_NEW starting to edit a new buffer
* READ_FILTER reading filter output
* READ_STDIN read from stdin instead of a file
*
* return FAIL for failure, OK otherwise
*/
int
readfile(fname, sfname, from, lines_to_skip, lines_to_read, flags)
char_u *fname;
char_u *sfname;
linenr_t from;
linenr_t lines_to_skip;
linenr_t lines_to_read;
int flags;
{
int fd;
int newfile = (flags & READ_NEW);
int check_readonly;
int filtering = (flags & READ_FILTER);
int read_stdin = (flags & READ_STDIN);
char_u c;
linenr_t lnum = from;
char_u *ptr = NULL; /* pointer into read buffer */
char_u *buffer = NULL; /* read buffer */
char_u *new_buffer = NULL; /* init to shut up gcc */
char_u *line_start = NULL; /* init to shut up gcc */
int wasempty; /* buffer was empty before reading */
colnr_t len;
long size;
char_u *p;
long filesize;
int split = 0; /* number of split lines */
#define UNKNOWN 0x0fffffff /* file size is unknown */
linenr_t linecnt;
int error = FALSE; /* errors encountered */
int ff_error = EOL_UNKNOWN; /* file format with errors */
long linerest; /* remaining chars in line */
int perm = 0;
int fileformat; /* end-of-line format */
struct stat st;
int file_readonly;
linenr_t skip_count;
linenr_t read_count;
int msg_save = msg_scroll;
linenr_t read_no_eol_lnum = 0; /* non-zero lnum when last line of
* last read was missing the eol */
int try_mac = (vim_strchr(p_ffs, 'm') != NULL);
int try_dos = (vim_strchr(p_ffs, 'd') != NULL);
int try_unix = (vim_strchr(p_ffs, 'x') != NULL);
#ifdef AUTOCMD
write_no_eol_lnum = 0; /* in case it was set by the previous read */
#endif
/*
* If there is no file name yet, use the one for the read file.
* b_notedited is set to reflect this.
* Don't do this for a read from a filter.
* Only do this when 'cpoptions' contains the 'f' flag.
*/
if (curbuf->b_ffname == NULL && !filtering && !read_stdin &&
vim_strchr(p_cpo, CPO_FNAMER) != NULL)
{
if (setfname(fname, sfname, FALSE) == OK)
curbuf->b_notedited = TRUE;
}
if (shortmess(SHM_OVER) || curbuf->b_help)
msg_scroll = FALSE; /* overwrite previous file message */
else
msg_scroll = TRUE; /* don't overwrite previous file message */
if (sfname == NULL)
sfname = fname;
/*
* For Unix: Use the short file name whenever possible.
* Avoids problems with networks and when directory names are changed.
* Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
* another directory, which we don't detect.
*/
#if defined(UNIX) || defined(__EMX__)
fname = sfname;
#endif
/* set default 'fileformat' */
if (newfile && *p_ffs)
set_fileformat(default_fileformat());
#ifdef UNIX
/*
* On Unix it is possible to read a directory, so we have to
* check for it before the open().
*/
if (!read_stdin)
{
perm = mch_getperm(fname);
if (perm >= 0 && !S_ISREG(perm) /* not a regular file ... */
# ifdef S_ISFIFO
&& !S_ISFIFO(perm) /* ... or fifo */
# endif
# ifdef S_ISSOCK
&& !S_ISSOCK(perm) /* ... or socket */
# endif
)
{
if (S_ISDIR(perm))
filemess(curbuf, fname, (char_u *)"is a directory", 0);
else
filemess(curbuf, fname, (char_u *)"is not a file", 0);
msg_end();
msg_scroll = msg_save;
return FAIL;
}
}
#endif
/*
* When opening a new file we take the readonly flag from the file.
* Default is r/w, can be set to r/o below.
* Don't reset it when in readonly mode
* Only set/reset b_p_ro when BF_CHECK_RO is set.
*/
check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO));
if (check_readonly && !readonlymode) /* default: set file not readonly */
curbuf->b_p_ro = FALSE;
if (newfile && !read_stdin)
{
/* Remember time of file.
* For RISCOS, also remember the filetype.
*/
if (stat((char *)fname, &st) >= 0)
{
curbuf->b_mtime = st.st_mtime;
curbuf->b_mtime_read = st.st_mtime;
#if defined(RISCOS) && defined(WANT_FILETYPE)
/* Read the filetype into the buffer local filetype option. */
ro_read_filetype(fname);
#endif
#ifdef UNIX
/*
* Set the protection bits of the swap file equal to the original
* file. This makes it possible for others to read the name of the
* original file from the swapfile.
*/
if (curbuf->b_ml.ml_mfp->mf_fname != NULL)
(void)mch_setperm(curbuf->b_ml.ml_mfp->mf_fname,
(long)((st.st_mode & 0777) | 0600));
#endif
}
else
{
curbuf->b_mtime = 0;
curbuf->b_mtime_read = 0;
}
}
/*
* for UNIX: check readonly with perm and access()
* for RISCOS: same as Unix, otherwise file gets re-datestamped!
* for MSDOS and Amiga: check readonly by trying to open the file for writing
*/
file_readonly = FALSE;
if (read_stdin)
fd = 0;
else
{
#if defined(UNIX) || defined(DJGPP) || defined(__EMX__) || defined(VMS) || defined(RISCOS)
if (
# ifdef UNIX
!(perm & 0222) ||
# endif
access((char *)fname, W_OK))
file_readonly = TRUE;
fd = open((char *)fname, O_RDONLY | O_EXTRA
#ifndef macintosh
, 0
#endif
);
#else
if (!newfile || readonlymode || (fd =
open((char *)fname, O_RDWR | O_EXTRA
#ifndef macintosh
, 0 /* mode (avoid UMR) */
#endif
)) < 0)
{
file_readonly = TRUE;
fd = open((char *)fname, O_RDONLY | O_EXTRA
#ifndef macintosh
, 0 /* mode (avoid UMR) */
#endif
); /* try to open ro */
}
#endif
}
if (fd < 0) /* cannot open at all */
{
#ifndef UNIX
int isdir_f;
#endif
msg_scroll = msg_save;
#ifndef UNIX
/*
* On MSDOS and Amiga we can't open a directory, check here.
*/
isdir_f = (mch_isdir(fname));
perm = mch_getperm(fname); /* check if the file exists */
fname = sfname; /* use short name now, for the messages */
if (isdir_f)
filemess(curbuf, fname, (char_u *)"is a directory", 0);
else
#endif
if (newfile)
{
if (perm < 0)
{
check_need_swap(newfile); /* may create swap file now */
filemess(curbuf, fname, (char_u *)"[New File]", 0);
#ifdef AUTOCMD
apply_autocmds(EVENT_BUFNEWFILE, fname, fname, FALSE,
curbuf);
#endif
/* remember the current file format */
curbuf->b_start_ffc = *curbuf->b_p_ff;
return OK; /* a new file is not an error */
}
else
filemess(curbuf, fname, (char_u *)"[Permission Denied]", 0);
}
return FAIL;
}
/*
* Only set the 'ro' flag for readonly files the first time they are
* loaded. Help files always get readonly mode
*/
if ((check_readonly && file_readonly) || curbuf->b_help)
curbuf->b_p_ro = TRUE;
if (newfile)
curbuf->b_p_eol = TRUE;
check_need_swap(newfile); /* may create swap file now */
#ifndef UNIX
fname = sfname; /* replace with short name now, for the messages */
#endif
++no_wait_return; /* don't wait for return yet */
/*
* Set '[ mark to the line above where the lines go (line 1 if zero).
*/
curbuf->b_op_start.lnum = ((from == 0) ? 1 : from);
curbuf->b_op_start.col = 0;
#ifdef AUTOCMD
{
int m = msg_scroll;
int n = msg_scrolled;
BUF *old_curbuf = curbuf;
/*
* The file must be closed again, the autocommands may want to change
* the file before reading it.
*/
if (!read_stdin)
close(fd); /* ignore errors */
/*
* The output from the autocommands should not overwrite anything and
* should not be overwritten: Set msg_scroll, restore its value if no
* output was done.
*/
msg_scroll = TRUE;
if (filtering)
apply_autocmds(EVENT_FILTERREADPRE, NULL, fname, FALSE, curbuf);
else if (read_stdin)
apply_autocmds(EVENT_STDINREADPRE, NULL, fname, FALSE, curbuf);
else if (newfile)
apply_autocmds(EVENT_BUFREADPRE, NULL, fname, FALSE, curbuf);
else
apply_autocmds(EVENT_FILEREADPRE, fname, fname, FALSE, NULL);
if (msg_scrolled == n)
msg_scroll = m;
/*
* Don't allow the autocommands to change the current buffer.
* Try to re-open the file.
*/
if (!read_stdin && (curbuf != old_curbuf ||
(fd = open((char *)fname, O_RDONLY | O_EXTRA
#ifndef macintosh
, 0 /* mode (avoid UMR) */
#endif
)) < 0))
{
--no_wait_return;
msg_scroll = msg_save;
if (fd < 0)
EMSG("*ReadPre autocommands made the file unreadable");
else
EMSG("*ReadPre autocommands must not change current buffer");
return FAIL;
}
}
#endif
/* Autocommands may add lines to the file, need to check if it is empty */
wasempty = (curbuf->b_ml.ml_flags & ML_EMPTY);
if (!recoverymode && !filtering && !read_stdin)
filemess(curbuf, fname, (char_u *)"", 0); /* show that we are busy */
msg_scroll = FALSE; /* overwrite the file message */
/*
* Set fileformat and linecnt now, before the "retry" caused by a wrong
* guess for fileformat, and after the autocommands, which may change
* them.
*/
if (curbuf->b_p_bin)
fileformat = EOL_UNIX; /* binary: use Unix format */
else if (*p_ffs == NUL)
fileformat = get_fileformat(curbuf); /* use format from buffer */
else
fileformat = EOL_UNKNOWN; /* detect from file */
linecnt = curbuf->b_ml.ml_line_count;
retry:
linerest = 0;
filesize = 0;
skip_count = lines_to_skip;
read_count = lines_to_read;
while (!error && !got_int)
{
/*
* We allocate as much space for the file as we can get, plus
* space for the old line plus room for one terminating NUL.
* The amount is limited by the fact that read() only can read
* upto max_unsigned characters (and other things).
*/
#if SIZEOF_INT <= 2
if (linerest >= 0x7ff0)
{
++split;
*ptr = NL; /* split line by inserting a NL */
size = 1;
}
else
#endif
{
#if SIZEOF_INT > 2
size = 0x10000L; /* use buffer >= 64K */
#else
size = 0x7ff0L - linerest; /* limit buffer to 32K */
#endif
for ( ; size >= 10; size = (long_u)size >> 1)
{
if ((new_buffer = lalloc((long_u)(size + linerest + 1),
FALSE)) != NULL)
break;
}
if (new_buffer == NULL)
{
do_outofmem_msg();
error = TRUE;
break;
}
if (linerest) /* copy characters from the previous buffer */
mch_memmove(new_buffer, ptr - linerest, (size_t)linerest);
vim_free(buffer);
buffer = new_buffer;
ptr = buffer + linerest;
line_start = buffer;
if ((size = read(fd, (char *)ptr, (size_t)size)) <= 0)
{
if (size < 0) /* read error */
error = TRUE;
break;
}
filesize += size; /* count the number of characters */
/*
* when reading the first part of a file: guess EOL type
*/
if (fileformat == EOL_UNKNOWN)
{
/* First try finding a NL, for Dos and Unix */
if (try_dos || try_unix)
{
for (p = ptr; p < ptr + size; ++p)
if (*p == NL)
{
if (!try_unix
|| (try_dos && p > ptr && p[-1] == CR))
fileformat = EOL_DOS;
else
fileformat = EOL_UNIX;
break;
}
}
/* No NL found: may use Mac format */
if (fileformat == EOL_UNKNOWN && try_mac)
fileformat = EOL_MAC;
/* Still nothing found? Use first format in 'ffs' */
if (fileformat == EOL_UNKNOWN)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -