⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fileio.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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 + -