aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/fileio.c
diff options
context:
space:
mode:
authorEliseo Martínez <eliseomarmol@gmail.com>2014-05-12 02:25:17 +0200
committerEliseo Martínez <eliseomarmol@gmail.com>2014-05-15 20:46:01 +0200
commitda51dc9cf202772f60bd2da975dbef257bd9237c (patch)
tree5c16b93238a153f55634e9323077f30c8133970c /src/nvim/fileio.c
parentffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff)
downloadrneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz
rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2
rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/nvim/fileio.c')
-rw-r--r--src/nvim/fileio.c8148
1 files changed, 8148 insertions, 0 deletions
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
new file mode 100644
index 0000000000..4fc73d8ebf
--- /dev/null
+++ b/src/nvim/fileio.c
@@ -0,0 +1,8148 @@
+/*
+ * 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.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * fileio.c: read from and write to a file
+ */
+
+#include <string.h>
+
+#include "vim.h"
+#include "fileio.h"
+#include "blowfish.h"
+#include "buffer.h"
+#include "charset.h"
+#include "diff.h"
+#include "edit.h"
+#include "eval.h"
+#include "ex_cmds.h"
+#include "ex_docmd.h"
+#include "ex_eval.h"
+#include "fold.h"
+#include "getchar.h"
+#include "hashtab.h"
+#include "mbyte.h"
+#include "memfile.h"
+#include "memline.h"
+#include "memory.h"
+#include "message.h"
+#include "misc1.h"
+#include "misc2.h"
+#include "crypt.h"
+#include "garray.h"
+#include "move.h"
+#include "normal.h"
+#include "option.h"
+#include "os_unix.h"
+#include "path.h"
+#include "quickfix.h"
+#include "regexp.h"
+#include "screen.h"
+#include "search.h"
+#include "sha256.h"
+#include "term.h"
+#include "ui.h"
+#include "undo.h"
+#include "window.h"
+#include "os/os.h"
+
+
+#if defined(HAVE_UTIME) && defined(HAVE_UTIME_H)
+# include <utime.h> /* for struct utimbuf */
+#endif
+
+#define BUFSIZE 8192 /* size of normal write buffer */
+#define SMBUFSIZE 256 /* size of emergency write buffer */
+
+/* crypt_magic[0] is pkzip crypt, crypt_magic[1] is sha2+blowfish */
+static char *crypt_magic[] = {"VimCrypt~01!", "VimCrypt~02!"};
+static char crypt_magic_head[] = "VimCrypt~";
+# define CRYPT_MAGIC_LEN 12 /* must be multiple of 4! */
+
+/* For blowfish, after the magic header, we store 8 bytes of salt and then 8
+ * bytes of seed (initialisation vector). */
+static int crypt_salt_len[] = {0, 8};
+static int crypt_seed_len[] = {0, 8};
+#define CRYPT_SALT_LEN_MAX 8
+#define CRYPT_SEED_LEN_MAX 8
+
+static char_u *next_fenc(char_u **pp);
+static char_u *readfile_charconvert(char_u *fname, char_u *fenc,
+ int *fdp);
+static void check_marks_read(void);
+static int crypt_method_from_magic(char *ptr, int len);
+static char_u *check_for_cryptkey(char_u *cryptkey, char_u *ptr,
+ long *sizep, off_t *filesizep,
+ int newfile, char_u *fname,
+ int *did_ask);
+#ifdef UNIX
+static void set_file_time(char_u *fname, time_t atime, time_t mtime);
+#endif
+static int set_rw_fname(char_u *fname, char_u *sfname);
+static int msg_add_fileformat(int eol_type);
+static void msg_add_eol(void);
+static int check_mtime(buf_T *buf, FileInfo *file_info);
+static int time_differs(long t1, long t2);
+static int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io,
+ int force, buf_T *buf,
+ exarg_T *eap);
+static int au_find_group(char_u *name);
+
+# define AUGROUP_DEFAULT -1 /* default autocmd group */
+# define AUGROUP_ERROR -2 /* erroneous autocmd group */
+# define AUGROUP_ALL -3 /* all autocmd groups */
+
+# define HAS_BW_FLAGS
+# define FIO_LATIN1 0x01 /* convert Latin1 */
+# define FIO_UTF8 0x02 /* convert UTF-8 */
+# define FIO_UCS2 0x04 /* convert UCS-2 */
+# define FIO_UCS4 0x08 /* convert UCS-4 */
+# define FIO_UTF16 0x10 /* convert UTF-16 */
+# define FIO_ENDIAN_L 0x80 /* little endian */
+# define FIO_ENCRYPTED 0x1000 /* encrypt written bytes */
+# define FIO_NOCONVERT 0x2000 /* skip encoding conversion */
+# define FIO_UCSBOM 0x4000 /* check for BOM at start of file */
+# define FIO_ALL -1 /* allow all formats */
+
+/* When converting, a read() or write() may leave some bytes to be converted
+ * for the next call. The value is guessed... */
+#define CONV_RESTLEN 30
+
+/* We have to guess how much a sequence of bytes may expand when converting
+ * with iconv() to be able to allocate a buffer. */
+#define ICONV_MULT 8
+
+/*
+ * Structure to pass arguments from buf_write() to buf_write_bytes().
+ */
+struct bw_info {
+ int bw_fd; /* file descriptor */
+ char_u *bw_buf; /* buffer with data to be written */
+ int bw_len; /* length of data */
+#ifdef HAS_BW_FLAGS
+ int bw_flags; /* FIO_ flags */
+#endif
+ char_u bw_rest[CONV_RESTLEN]; /* not converted bytes */
+ int bw_restlen; /* nr of bytes in bw_rest[] */
+ int bw_first; /* first write call */
+ char_u *bw_conv_buf; /* buffer for writing converted chars */
+ int bw_conv_buflen; /* size of bw_conv_buf */
+ int bw_conv_error; /* set for conversion error */
+ linenr_T bw_conv_error_lnum; /* first line with error or zero */
+ linenr_T bw_start_lnum; /* line number at start of buffer */
+# ifdef USE_ICONV
+ iconv_t bw_iconv_fd; /* descriptor for iconv() or -1 */
+# endif
+};
+
+static int buf_write_bytes(struct bw_info *ip);
+
+static linenr_T readfile_linenr(linenr_T linecnt, char_u *p,
+ char_u *endp);
+static int ucs2bytes(unsigned c, char_u **pp, int flags);
+static int need_conversion(char_u *fenc);
+static int get_fio_flags(char_u *ptr);
+static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags);
+static int make_bom(char_u *buf, char_u *name);
+static int move_lines(buf_T *frombuf, buf_T *tobuf);
+#ifdef TEMPDIRNAMES
+static void vim_settempdir(char_u *tempdir);
+#endif
+static char *e_auchangedbuf = N_(
+ "E812: Autocommands changed buffer or buffer name");
+
+void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
+{
+ int msg_scroll_save;
+
+ if (msg_silent != 0)
+ return;
+ msg_add_fname(buf, name); /* put file name in IObuff with quotes */
+ /* If it's extremely long, truncate it. */
+ if (STRLEN(IObuff) > IOSIZE - 80)
+ IObuff[IOSIZE - 80] = NUL;
+ 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) && !exiting && p_verbose == 0)
+ msg_scroll = FALSE;
+ if (!msg_scroll) /* wait a bit when overwriting an error msg */
+ check_for_delay(FALSE);
+ msg_start();
+ msg_scroll = msg_scroll_save;
+ msg_scrolled_ign = TRUE;
+ /* may truncate the message to avoid a hit-return prompt */
+ msg_outtrans_attr(msg_may_trunc(FALSE, IObuff), attr);
+ msg_clr_eos();
+ out_flush();
+ msg_scrolled_ign = FALSE;
+}
+
+/*
+ * Read lines from file "fname" into the buffer after line "from".
+ *
+ * 1. We allocate blocks with try_malloc, 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
+ * READ_BUFFER read from curbuf instead of a file (converting after reading
+ * stdin)
+ * READ_DUMMY read into a dummy buffer (to check if file contents changed)
+ * READ_KEEP_UNDO don't clear undo info or read it from a file
+ *
+ * return FAIL for failure, OK otherwise
+ */
+int
+readfile (
+ char_u *fname,
+ char_u *sfname,
+ linenr_T from,
+ linenr_T lines_to_skip,
+ linenr_T lines_to_read,
+ exarg_T *eap, /* can be NULL! */
+ int flags
+)
+{
+ int fd = 0;
+ int newfile = (flags & READ_NEW);
+ int check_readonly;
+ int filtering = (flags & READ_FILTER);
+ int read_stdin = (flags & READ_STDIN);
+ int read_buffer = (flags & READ_BUFFER);
+ int set_options = newfile || read_buffer
+ || (eap != NULL && eap->read_edit);
+ linenr_T read_buf_lnum = 1; /* next line to read from curbuf */
+ colnr_T read_buf_col = 0; /* next char to read from this line */
+ 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 = 0;
+ char_u *p;
+ off_t filesize = 0;
+ int skip_read = FALSE;
+ char_u *cryptkey = NULL;
+ int did_ask_for_key = FALSE;
+ int crypt_method_used;
+ context_sha256_T sha_ctx;
+ int read_undo_file = FALSE;
+ int split = 0; /* number of split lines */
+ linenr_T linecnt;
+ int error = FALSE; /* errors encountered */
+ int ff_error = EOL_UNKNOWN; /* file format with errors */
+ long linerest = 0; /* remaining chars in line */
+#ifdef UNIX
+ int perm = 0;
+ int swap_mode = -1; /* protection bits for swap file */
+#else
+ int perm;
+#endif
+ int fileformat = 0; /* end-of-line format */
+ int keep_fileformat = FALSE;
+ int file_readonly;
+ linenr_T skip_count = 0;
+ linenr_T read_count = 0;
+ 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);
+ int file_rewind = FALSE;
+ int can_retry;
+ linenr_T conv_error = 0; /* line nr with conversion error */
+ linenr_T illegal_byte = 0; /* line nr with illegal byte */
+ int keep_dest_enc = FALSE; /* don't retry when char doesn't fit
+ in destination encoding */
+ int bad_char_behavior = BAD_REPLACE;
+ /* BAD_KEEP, BAD_DROP or character to
+ * replace with */
+ char_u *tmpname = NULL; /* name of 'charconvert' output file */
+ int fio_flags = 0;
+ char_u *fenc; /* fileencoding to use */
+ int fenc_alloced; /* fenc_next is in allocated memory */
+ char_u *fenc_next = NULL; /* next item in 'fencs' or NULL */
+ int advance_fenc = FALSE;
+ long real_size = 0;
+# ifdef USE_ICONV
+ iconv_t iconv_fd = (iconv_t)-1; /* descriptor for iconv() or -1 */
+ int did_iconv = FALSE; /* TRUE when iconv() failed and trying
+ 'charconvert' next */
+# endif
+ int converted = FALSE; /* TRUE if conversion done */
+ int notconverted = FALSE; /* TRUE if conversion wanted but it
+ wasn't possible */
+ char_u conv_rest[CONV_RESTLEN];
+ int conv_restlen = 0; /* nr of bytes in conv_rest[] */
+ buf_T *old_curbuf;
+ char_u *old_b_ffname;
+ char_u *old_b_fname;
+ int using_b_ffname;
+ int using_b_fname;
+
+ curbuf->b_no_eol_lnum = 0; /* in case it was set by the previous read */
+
+ /*
+ * If there is no file name yet, use the one for the read file.
+ * BF_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
+ && fname != NULL
+ && vim_strchr(p_cpo, CPO_FNAMER) != NULL
+ && !(flags & READ_DUMMY)) {
+ if (set_rw_fname(fname, sfname) == FAIL)
+ return FAIL;
+ }
+
+ /* Remember the initial values of curbuf, curbuf->b_ffname and
+ * curbuf->b_fname to detect whether they are altered as a result of
+ * executing nasty autocommands. Also check if "fname" and "sfname"
+ * point to one of these values. */
+ old_curbuf = curbuf;
+ old_b_ffname = curbuf->b_ffname;
+ old_b_fname = curbuf->b_fname;
+ using_b_ffname = (fname == curbuf->b_ffname)
+ || (sfname == curbuf->b_ffname);
+ using_b_fname = (fname == curbuf->b_fname) || (sfname == curbuf->b_fname);
+
+ /* After reading a file the cursor line changes but we don't want to
+ * display the line. */
+ ex_no_reprint = TRUE;
+
+ /* don't display the file info for another buffer now */
+ need_fileinfo = FALSE;
+
+ /*
+ * 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 (sfname == NULL)
+ sfname = fname;
+#if defined(UNIX)
+ fname = sfname;
+#endif
+
+ /*
+ * The BufReadCmd and FileReadCmd events intercept the reading process by
+ * executing the associated commands instead.
+ */
+ if (!filtering && !read_stdin && !read_buffer) {
+ pos_T pos;
+
+ pos = curbuf->b_op_start;
+
+ /* 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;
+
+ if (newfile) {
+ if (apply_autocmds_exarg(EVENT_BUFREADCMD, NULL, sfname,
+ FALSE, curbuf, eap))
+ return aborting() ? FAIL : OK;
+ } else if (apply_autocmds_exarg(EVENT_FILEREADCMD, sfname, sfname,
+ FALSE, NULL, eap))
+ return aborting() ? FAIL : OK;
+
+ curbuf->b_op_start = pos;
+ }
+
+ if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0)
+ msg_scroll = FALSE; /* overwrite previous file message */
+ else
+ msg_scroll = TRUE; /* don't overwrite previous file message */
+
+ /*
+ * If the name ends in a path separator, we can't open it. Check here,
+ * because reading the file may actually work, but then creating the swap
+ * file may destroy it! Reported on MS-DOS and Win 95.
+ * If the name is too long we might crash further on, quit here.
+ */
+ if (fname != NULL && *fname != NUL) {
+ p = fname + STRLEN(fname);
+ if (after_pathsep(fname, p) || STRLEN(fname) >= MAXPATHL) {
+ filemess(curbuf, fname, (char_u *)_("Illegal file name"), 0);
+ msg_end();
+ msg_scroll = msg_save;
+ return FAIL;
+ }
+ }
+
+ if (!read_stdin && !read_buffer) {
+#ifdef UNIX
+ /*
+ * On Unix it is possible to read a directory, so we have to
+ * check for it before the mch_open().
+ */
+ perm = os_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
+# ifdef OPEN_CHR_FILES
+ && !(S_ISCHR(perm) && is_dev_fd_file(fname))
+ /* ... or a character special file named /dev/fd/<n> */
+# 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
+ }
+
+ /* Set default or forced 'fileformat' and 'binary'. */
+ set_file_options(set_options, eap);
+
+ /*
+ * 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)
+ curbuf->b_p_ro = FALSE;
+
+ if (newfile && !read_stdin && !read_buffer) {
+ /* Remember time of file. */
+ FileInfo file_info;
+ if (os_get_file_info((char *)fname, &file_info)) {
+ buf_store_file_info(curbuf, &file_info);
+ curbuf->b_mtime_read = curbuf->b_mtime;
+#ifdef UNIX
+ /*
+ * Use the protection bits of the original file for the swap file.
+ * This makes it possible for others to read the name of the
+ * edited file from the swapfile, but only if they can read the
+ * edited file.
+ * Remove the "write" and "execute" bits for group and others
+ * (they must not write the swapfile).
+ * Add the "read" and "write" bits for the user, otherwise we may
+ * not be able to write to the file ourselves.
+ * Setting the bits is done below, after creating the swap file.
+ */
+ swap_mode = (file_info.stat.st_mode & 0644) | 0600;
+#endif
+ } else {
+ curbuf->b_mtime = 0;
+ curbuf->b_mtime_read = 0;
+ curbuf->b_orig_size = 0;
+ curbuf->b_orig_mode = 0;
+ }
+
+ /* Reset the "new file" flag. It will be set again below when the
+ * file doesn't exist. */
+ curbuf->b_flags &= ~(BF_NEW | BF_NEW_W);
+ }
+
+ /*
+ * Check readonly by trying to open the file for writing.
+ * If this fails, we know that the file is readonly.
+ */
+ file_readonly = FALSE;
+ if (!read_buffer && !read_stdin) {
+ if (!newfile || readonlymode) {
+ file_readonly = TRUE;
+ } else if ((fd = mch_open((char *)fname, O_RDWR, 0)) < 0) {
+ // opening in readwrite mode failed => file is readonly
+ file_readonly = TRUE;
+ }
+ if (file_readonly == TRUE) {
+ // try to open readonly
+ fd = mch_open((char *)fname, O_RDONLY, 0);
+ }
+ }
+
+ if (fd < 0) { /* cannot open at all */
+ msg_scroll = msg_save;
+#ifndef UNIX
+ /*
+ * On non-unix systems we can't open a directory, check here.
+ */
+ perm = os_getperm(fname); /* check if the file exists */
+ if (os_isdir(fname)) {
+ filemess(curbuf, sfname, (char_u *)_("is a directory"), 0);
+ curbuf->b_p_ro = TRUE; /* must use "w!" now */
+ } else
+#endif
+ if (newfile) {
+ if (perm < 0
+#ifdef ENOENT
+ && errno == ENOENT
+#endif
+ ) {
+ /*
+ * Set the 'new-file' flag, so that when the file has
+ * been created by someone else, a ":w" will complain.
+ */
+ curbuf->b_flags |= BF_NEW;
+
+ /* Create a swap file now, so that other Vims are warned
+ * that we are editing this file. Don't do this for a
+ * "nofile" or "nowrite" buffer type. */
+ if (!bt_dontwrite(curbuf)) {
+ check_need_swap(newfile);
+ /* SwapExists autocommand may mess things up */
+ if (curbuf != old_curbuf
+ || (using_b_ffname
+ && (old_b_ffname != curbuf->b_ffname))
+ || (using_b_fname
+ && (old_b_fname != curbuf->b_fname))) {
+ EMSG(_(e_auchangedbuf));
+ return FAIL;
+ }
+ }
+ if (dir_of_file_exists(fname))
+ filemess(curbuf, sfname, (char_u *)_("[New File]"), 0);
+ else
+ filemess(curbuf, sfname,
+ (char_u *)_("[New DIRECTORY]"), 0);
+ /* Even though this is a new file, it might have been
+ * edited before and deleted. Get the old marks. */
+ check_marks_read();
+ /* Set forced 'fileencoding'. */
+ if (eap != NULL)
+ set_forced_fenc(eap);
+ apply_autocmds_exarg(EVENT_BUFNEWFILE, sfname, sfname,
+ FALSE, curbuf, eap);
+ /* remember the current fileformat */
+ save_file_ff(curbuf);
+
+ if (aborting()) /* autocmds may abort script processing */
+ return FAIL;
+ return OK; /* a new file is not an error */
+ } else {
+ filemess(curbuf, sfname, (char_u *)(
+# ifdef EFBIG
+ (errno == EFBIG) ? _("[File too big]") :
+# endif
+# ifdef EOVERFLOW
+ (errno == EOVERFLOW) ? _("[File too big]") :
+# endif
+ _("[Permission Denied]")), 0);
+ curbuf->b_p_ro = TRUE; /* must use "w!" now */
+ }
+ }
+
+ 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 (set_options) {
+ /* Don't change 'eol' if reading from buffer as it will already be
+ * correctly set when reading stdin. */
+ if (!read_buffer) {
+ curbuf->b_p_eol = TRUE;
+ curbuf->b_start_eol = TRUE;
+ }
+ curbuf->b_p_bomb = FALSE;
+ curbuf->b_start_bomb = FALSE;
+ }
+
+ /* Create a swap file now, so that other Vims are warned that we are
+ * editing this file.
+ * Don't do this for a "nofile" or "nowrite" buffer type. */
+ if (!bt_dontwrite(curbuf)) {
+ check_need_swap(newfile);
+ if (!read_stdin && (curbuf != old_curbuf
+ || (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
+ || (using_b_fname &&
+ (old_b_fname != curbuf->b_fname)))) {
+ EMSG(_(e_auchangedbuf));
+ if (!read_buffer)
+ close(fd);
+ return FAIL;
+ }
+#ifdef UNIX
+ /* Set swap file protection bits after creating it. */
+ if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL
+ && curbuf->b_ml.ml_mfp->mf_fname != NULL)
+ (void)os_setperm(curbuf->b_ml.ml_mfp->mf_fname, (long)swap_mode);
+#endif
+ }
+
+#if defined(HAS_SWAP_EXISTS_ACTION)
+ /* If "Quit" selected at ATTENTION dialog, don't load the file */
+ if (swap_exists_action == SEA_QUIT) {
+ if (!read_buffer && !read_stdin)
+ close(fd);
+ return FAIL;
+ }
+#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;
+
+ if (!read_buffer) {
+ int m = msg_scroll;
+ int n = msg_scrolled;
+
+ /*
+ * 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_exarg(EVENT_FILTERREADPRE, NULL, sfname,
+ FALSE, curbuf, eap);
+ else if (read_stdin)
+ apply_autocmds_exarg(EVENT_STDINREADPRE, NULL, sfname,
+ FALSE, curbuf, eap);
+ else if (newfile)
+ apply_autocmds_exarg(EVENT_BUFREADPRE, NULL, sfname,
+ FALSE, curbuf, eap);
+ else
+ apply_autocmds_exarg(EVENT_FILEREADPRE, sfname, sfname,
+ FALSE, NULL, eap);
+ if (msg_scrolled == n)
+ msg_scroll = m;
+
+ if (aborting()) { /* autocmds may abort script processing */
+ --no_wait_return;
+ msg_scroll = msg_save;
+ curbuf->b_p_ro = TRUE; /* must use "w!" now */
+ return FAIL;
+ }
+ /*
+ * Don't allow the autocommands to change the current buffer.
+ * Try to re-open the file.
+ *
+ * Don't allow the autocommands to change the buffer name either
+ * (cd for example) if it invalidates fname or sfname.
+ */
+ if (!read_stdin && (curbuf != old_curbuf
+ || (using_b_ffname && (old_b_ffname != curbuf->b_ffname))
+ || (using_b_fname && (old_b_fname != curbuf->b_fname))
+ || (fd =
+ mch_open((char *)fname, O_RDONLY,
+ 0)) < 0)) {
+ --no_wait_return;
+ msg_scroll = msg_save;
+ if (fd < 0)
+ EMSG(_("E200: *ReadPre autocommands made the file unreadable"));
+ else
+ EMSG(_("E201: *ReadPre autocommands must not change current buffer"));
+ curbuf->b_p_ro = TRUE; /* must use "w!" now */
+ return FAIL;
+ }
+ }
+
+ /* 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 && !(flags & READ_DUMMY)) {
+ /*
+ * Show the user that we are busy reading the input. Sometimes this
+ * may take a while. When reading from stdin another program may
+ * still be running, don't move the cursor to the last line, unless
+ * always using the GUI.
+ */
+ if (read_stdin) {
+ mch_msg(_("Vim: Reading from stdin...\n"));
+ } else if (!read_buffer)
+ filemess(curbuf, sfname, (char_u *)"", 0);
+ }
+
+ msg_scroll = FALSE; /* overwrite the file message */
+
+ /*
+ * Set linecnt now, before the "retry" caused by a wrong guess for
+ * fileformat, and after the autocommands, which may change them.
+ */
+ linecnt = curbuf->b_ml.ml_line_count;
+
+ /* "++bad=" argument. */
+ if (eap != NULL && eap->bad_char != 0) {
+ bad_char_behavior = eap->bad_char;
+ if (set_options)
+ curbuf->b_bad_char = eap->bad_char;
+ } else
+ curbuf->b_bad_char = 0;
+
+ /*
+ * Decide which 'encoding' to use or use first.
+ */
+ if (eap != NULL && eap->force_enc != 0) {
+ fenc = enc_canonize(eap->cmd + eap->force_enc);
+ fenc_alloced = TRUE;
+ keep_dest_enc = TRUE;
+ } else if (curbuf->b_p_bin) {
+ fenc = (char_u *)""; /* binary: don't convert */
+ fenc_alloced = FALSE;
+ } else if (curbuf->b_help) {
+ char_u firstline[80];
+ int fc;
+
+ /* Help files are either utf-8 or latin1. Try utf-8 first, if this
+ * fails it must be latin1.
+ * Always do this when 'encoding' is "utf-8". Otherwise only do
+ * this when needed to avoid [converted] remarks all the time.
+ * It is needed when the first line contains non-ASCII characters.
+ * That is only in *.??x files. */
+ fenc = (char_u *)"latin1";
+ c = enc_utf8;
+ if (!c && !read_stdin) {
+ fc = fname[STRLEN(fname) - 1];
+ if (TOLOWER_ASC(fc) == 'x') {
+ /* Read the first line (and a bit more). Immediately rewind to
+ * the start of the file. If the read() fails "len" is -1. */
+ len = read_eintr(fd, firstline, 80);
+ lseek(fd, (off_t)0L, SEEK_SET);
+ for (p = firstline; p < firstline + len; ++p)
+ if (*p >= 0x80) {
+ c = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (c) {
+ fenc_next = fenc;
+ fenc = (char_u *)"utf-8";
+
+ /* When the file is utf-8 but a character doesn't fit in
+ * 'encoding' don't retry. In help text editing utf-8 bytes
+ * doesn't make sense. */
+ if (!enc_utf8)
+ keep_dest_enc = TRUE;
+ }
+ fenc_alloced = FALSE;
+ } else if (*p_fencs == NUL) {
+ fenc = curbuf->b_p_fenc; /* use format from buffer */
+ fenc_alloced = FALSE;
+ } else {
+ fenc_next = p_fencs; /* try items in 'fileencodings' */
+ fenc = next_fenc(&fenc_next);
+ fenc_alloced = TRUE;
+ }
+
+ /*
+ * Jump back here to retry reading the file in different ways.
+ * Reasons to retry:
+ * - encoding conversion failed: try another one from "fenc_next"
+ * - BOM detected and fenc was set, need to setup conversion
+ * - "fileformat" check failed: try another
+ *
+ * Variables set for special retry actions:
+ * "file_rewind" Rewind the file to start reading it again.
+ * "advance_fenc" Advance "fenc" using "fenc_next".
+ * "skip_read" Re-use already read bytes (BOM detected).
+ * "did_iconv" iconv() conversion failed, try 'charconvert'.
+ * "keep_fileformat" Don't reset "fileformat".
+ *
+ * Other status indicators:
+ * "tmpname" When != NULL did conversion with 'charconvert'.
+ * Output file has to be deleted afterwards.
+ * "iconv_fd" When != -1 did conversion with iconv().
+ */
+retry:
+
+ if (file_rewind) {
+ if (read_buffer) {
+ read_buf_lnum = 1;
+ read_buf_col = 0;
+ } else if (read_stdin || lseek(fd, (off_t)0L, SEEK_SET) != 0) {
+ /* Can't rewind the file, give up. */
+ error = TRUE;
+ goto failed;
+ }
+ /* Delete the previously read lines. */
+ while (lnum > from)
+ ml_delete(lnum--, FALSE);
+ file_rewind = FALSE;
+ if (set_options) {
+ curbuf->b_p_bomb = FALSE;
+ curbuf->b_start_bomb = FALSE;
+ }
+ conv_error = 0;
+ }
+
+ if (cryptkey != NULL)
+ /* Need to reset the state, but keep the key, don't want to ask for it
+ * again. */
+ crypt_pop_state();
+
+ /*
+ * When retrying with another "fenc" and the first time "fileformat"
+ * will be reset.
+ */
+ if (keep_fileformat)
+ keep_fileformat = FALSE;
+ else {
+ if (eap != NULL && eap->force_ff != 0) {
+ fileformat = get_fileformat_force(curbuf, eap);
+ try_unix = try_dos = try_mac = FALSE;
+ } else 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 */
+ }
+
+# ifdef USE_ICONV
+ if (iconv_fd != (iconv_t)-1) {
+ /* aborted conversion with iconv(), close the descriptor */
+ iconv_close(iconv_fd);
+ iconv_fd = (iconv_t)-1;
+ }
+# endif
+
+ if (advance_fenc) {
+ /*
+ * Try the next entry in 'fileencodings'.
+ */
+ advance_fenc = FALSE;
+
+ if (eap != NULL && eap->force_enc != 0) {
+ /* Conversion given with "++cc=" wasn't possible, read
+ * without conversion. */
+ notconverted = TRUE;
+ conv_error = 0;
+ if (fenc_alloced)
+ free(fenc);
+ fenc = (char_u *)"";
+ fenc_alloced = FALSE;
+ } else {
+ if (fenc_alloced)
+ free(fenc);
+ if (fenc_next != NULL) {
+ fenc = next_fenc(&fenc_next);
+ fenc_alloced = (fenc_next != NULL);
+ } else {
+ fenc = (char_u *)"";
+ fenc_alloced = FALSE;
+ }
+ }
+ if (tmpname != NULL) {
+ os_remove((char *)tmpname); // delete converted file
+ free(tmpname);
+ tmpname = NULL;
+ }
+ }
+
+ /*
+ * Conversion may be required when the encoding of the file is different
+ * from 'encoding' or 'encoding' is UTF-16, UCS-2 or UCS-4.
+ */
+ fio_flags = 0;
+ converted = need_conversion(fenc);
+ if (converted) {
+
+ /* "ucs-bom" means we need to check the first bytes of the file
+ * for a BOM. */
+ if (STRCMP(fenc, ENC_UCSBOM) == 0)
+ fio_flags = FIO_UCSBOM;
+
+ /*
+ * Check if UCS-2/4 or Latin1 to UTF-8 conversion needs to be
+ * done. This is handled below after read(). Prepare the
+ * fio_flags to avoid having to parse the string each time.
+ * Also check for Unicode to Latin1 conversion, because iconv()
+ * appears not to handle this correctly. This works just like
+ * conversion to UTF-8 except how the resulting character is put in
+ * the buffer.
+ */
+ else if (enc_utf8 || STRCMP(p_enc, "latin1") == 0)
+ fio_flags = get_fio_flags(fenc);
+
+
+
+# ifdef USE_ICONV
+ /*
+ * Try using iconv() if we can't convert internally.
+ */
+ if (fio_flags == 0
+ && !did_iconv
+ )
+ iconv_fd = (iconv_t)my_iconv_open(
+ enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc);
+# endif
+
+ /*
+ * Use the 'charconvert' expression when conversion is required
+ * and we can't do it internally or with iconv().
+ */
+ if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL
+# ifdef USE_ICONV
+ && iconv_fd == (iconv_t)-1
+# endif
+ ) {
+# ifdef USE_ICONV
+ did_iconv = FALSE;
+# endif
+ /* Skip conversion when it's already done (retry for wrong
+ * "fileformat"). */
+ if (tmpname == NULL) {
+ tmpname = readfile_charconvert(fname, fenc, &fd);
+ if (tmpname == NULL) {
+ /* Conversion failed. Try another one. */
+ advance_fenc = TRUE;
+ if (fd < 0) {
+ /* Re-opening the original file failed! */
+ EMSG(_("E202: Conversion made file unreadable!"));
+ error = TRUE;
+ goto failed;
+ }
+ goto retry;
+ }
+ }
+ } else {
+ if (fio_flags == 0
+# ifdef USE_ICONV
+ && iconv_fd == (iconv_t)-1
+# endif
+ ) {
+ /* Conversion wanted but we can't.
+ * Try the next conversion in 'fileencodings' */
+ advance_fenc = TRUE;
+ goto retry;
+ }
+ }
+ }
+
+ /* Set "can_retry" when it's possible to rewind the file and try with
+ * another "fenc" value. It's FALSE when no other "fenc" to try, reading
+ * stdin or fixed at a specific encoding. */
+ can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc);
+
+ if (!skip_read) {
+ linerest = 0;
+ filesize = 0;
+ skip_count = lines_to_skip;
+ read_count = lines_to_read;
+ conv_restlen = 0;
+ read_undo_file = (newfile && (flags & READ_KEEP_UNDO) == 0
+ && curbuf->b_ffname != NULL
+ && curbuf->b_p_udf
+ && !filtering
+ && !read_stdin
+ && !read_buffer);
+ if (read_undo_file)
+ sha256_start(&sha_ctx);
+ }
+
+ 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 (!skip_read) {
+ size = 0x10000L; /* use buffer >= 64K */
+
+ for (; size >= 10; size /= 2) {
+ new_buffer = verbose_try_malloc((size_t)size + (size_t)linerest + 1);
+ if (new_buffer) {
+ break;
+ }
+ }
+ if (new_buffer == NULL) {
+ error = TRUE;
+ break;