diff options
| author | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-12 02:25:17 +0200 |
|---|---|---|
| committer | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-15 20:46:01 +0200 |
| commit | da51dc9cf202772f60bd2da975dbef257bd9237c (patch) | |
| tree | 5c16b93238a153f55634e9323077f30c8133970c /src/nvim/fileio.c | |
| parent | ffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff) | |
| download | rneovim-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.c | 8148 |
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; |