Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

10402 lines (9671 sloc) 263.383 kB
/* 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.
* See README.txt for an overview of the Vim source code.
*/
/*
* fileio.c: read from and write to a file
*/
#include "vim.h"
#if defined(__TANDEM) || defined(__MINT__)
# include <limits.h> /* for SSIZE_MAX */
#endif
#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 */
#ifdef FEAT_CRYPT
/* 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
#endif
/* Is there any system that doesn't have access()? */
#define USE_MCH_ACCESS
#if defined(sun) && defined(S_ISCHR)
# define OPEN_CHR_FILES
static int is_dev_fd_file(char_u *fname);
#endif
#ifdef FEAT_MBYTE
static char_u *next_fenc __ARGS((char_u **pp));
# ifdef FEAT_EVAL
static char_u *readfile_charconvert __ARGS((char_u *fname, char_u *fenc, int *fdp));
# endif
#endif
#ifdef FEAT_VIMINFO
static void check_marks_read __ARGS((void));
#endif
#ifdef FEAT_CRYPT
static int crypt_method_from_magic __ARGS((char *ptr, int len));
static char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, off_t *filesizep, int newfile, char_u *fname, int *did_ask));
#endif
#ifdef UNIX
static void set_file_time __ARGS((char_u *fname, time_t atime, time_t mtime));
#endif
static int set_rw_fname __ARGS((char_u *fname, char_u *sfname));
static int msg_add_fileformat __ARGS((int eol_type));
static void msg_add_eol __ARGS((void));
static int check_mtime __ARGS((buf_T *buf, struct stat *s));
static int time_differs __ARGS((long t1, long t2));
#ifdef FEAT_AUTOCMD
static int apply_autocmds_exarg __ARGS((event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap));
static int au_find_group __ARGS((char_u *name));
# define AUGROUP_DEFAULT -1 /* default autocmd group */
# define AUGROUP_ERROR -2 /* erroneous autocmd group */
# define AUGROUP_ALL -3 /* all autocmd groups */
#endif
#if defined(FEAT_CRYPT) || defined(FEAT_MBYTE)
# 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 */
# ifdef WIN3264
# define FIO_CODEPAGE 0x20 /* convert MS-Windows codepage */
# define FIO_PUT_CP(x) (((x) & 0xffff) << 16) /* put codepage in top word */
# define FIO_GET_CP(x) (((x)>>16) & 0xffff) /* get codepage from top word */
# endif
# ifdef MACOS_X
# define FIO_MACROMAN 0x20 /* convert MacRoman */
# endif
# 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 */
#endif
/* 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
#ifdef FEAT_MBYTE
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
#endif
};
static int buf_write_bytes __ARGS((struct bw_info *ip));
#ifdef FEAT_MBYTE
static linenr_T readfile_linenr __ARGS((linenr_T linecnt, char_u *p, char_u *endp));
static int ucs2bytes __ARGS((unsigned c, char_u **pp, int flags));
static int need_conversion __ARGS((char_u *fenc));
static int get_fio_flags __ARGS((char_u *ptr));
static char_u *check_for_bom __ARGS((char_u *p, long size, int *lenp, int flags));
static int make_bom __ARGS((char_u *buf, char_u *name));
# ifdef WIN3264
static int get_win_fio_flags __ARGS((char_u *ptr));
# endif
# ifdef MACOS_X
static int get_mac_fio_flags __ARGS((char_u *ptr));
# endif
#endif
static int move_lines __ARGS((buf_T *frombuf, buf_T *tobuf));
#ifdef TEMPDIRNAMES
static void vim_settempdir __ARGS((char_u *tempdir));
#endif
#ifdef FEAT_AUTOCMD
static char *e_auchangedbuf = N_("E812: Autocommands changed buffer or buffer name");
#endif
void
filemess(buf, name, s, attr)
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 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
* 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(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags)
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;
#ifdef FEAT_CRYPT
char_u *cryptkey = NULL;
int did_ask_for_key = FALSE;
int crypt_method_used;
#endif
#ifdef FEAT_PERSISTENT_UNDO
context_sha256_T sha_ctx;
int read_undo_file = FALSE;
#endif
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 = 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;
struct stat st;
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;
#ifdef FEAT_MBYTE
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 */
# ifdef FEAT_EVAL
int did_iconv = FALSE; /* TRUE when iconv() failed and trying
'charconvert' next */
# endif
# 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[] */
#endif
#ifdef FEAT_AUTOCMD
buf_T *old_curbuf;
char_u *old_b_ffname;
char_u *old_b_fname;
int using_b_ffname;
int using_b_fname;
#endif
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;
}
#ifdef FEAT_AUTOCMD
/* 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);
#endif
/* 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) || defined(__EMX__)
fname = sfname;
#endif
#ifdef FEAT_AUTOCMD
/*
* 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))
#ifdef FEAT_EVAL
return aborting() ? FAIL : OK;
#else
return OK;
#endif
}
else if (apply_autocmds_exarg(EVENT_FILEREADCMD, sfname, sfname,
FALSE, NULL, eap))
#ifdef FEAT_EVAL
return aborting() ? FAIL : OK;
#else
return OK;
#endif
curbuf->b_op_start = pos;
}
#endif
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;
}
}
#ifdef UNIX
/*
* On Unix it is possible to read a directory, so we have to
* check for it before the mch_open().
*/
if (!read_stdin && !read_buffer)
{
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
# 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;
}
# if defined(MSDOS) || defined(MSWIN) || defined(OS2)
/*
* MS-Windows allows opening a device, but we will probably get stuck
* trying to read it.
*/
if (!p_odev && mch_nodetype(fname) == NODE_WRITABLE)
{
filemess(curbuf, fname, (char_u *)_("is a device (disabled with 'opendevice' option)"), 0);
msg_end();
msg_scroll = msg_save;
return FAIL;
}
# endif
}
#endif
/* set default 'fileformat' */
if (set_options)
{
if (eap != NULL && eap->force_ff != 0)
set_fileformat(get_fileformat_force(curbuf, eap), OPT_LOCAL);
else if (*p_ffs != NUL)
set_fileformat(default_fileformat(), OPT_LOCAL);
}
/* set or reset 'binary' */
if (eap != NULL && eap->force_bin != 0)
{
int oldval = curbuf->b_p_bin;
curbuf->b_p_bin = (eap->force_bin == FORCE_BIN);
set_options_bin(oldval, curbuf->b_p_bin, OPT_LOCAL);
}
/*
* 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. */
if (mch_stat((char *)fname, &st) >= 0)
{
buf_store_time(curbuf, &st, fname);
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 = (st.st_mode & 0644) | 0600;
#endif
#ifdef FEAT_CW_EDITOR
/* Get the FSSpec on MacOS
* TODO: Update it properly when the buffer name changes
*/
(void)GetFSSpecFromPath(curbuf->b_ffname, &curbuf->b_FSSpec);
#endif
#ifdef VMS
curbuf->b_fab_rfm = st.st_fab_rfm;
curbuf->b_fab_rat = st.st_fab_rat;
curbuf->b_fab_mrs = st.st_fab_mrs;
#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);
}
/*
* for UNIX: check readonly with perm and mch_access()
* for MSDOS and Amiga: check readonly by trying to open the file for writing
*/
file_readonly = FALSE;
if (read_stdin)
{
#if defined(MSDOS) || defined(MSWIN) || defined(OS2)
/* Force binary I/O on stdin to avoid CR-LF -> LF conversion. */
setmode(0, O_BINARY);
#endif
}
else if (!read_buffer)
{
#ifdef USE_MCH_ACCESS
if (
# ifdef UNIX
!(perm & 0222) ||
# endif
mch_access((char *)fname, W_OK))
file_readonly = TRUE;
fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
#else
if (!newfile
|| readonlymode
|| (fd = mch_open((char *)fname, O_RDWR | O_EXTRA, 0)) < 0)
{
file_readonly = TRUE;
/* try to open ro */
fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
}
#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 */
if (isdir_f)
{
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. */
#ifdef FEAT_QUICKFIX
if (!bt_dontwrite(curbuf))
#endif
{
check_need_swap(newfile);
#ifdef FEAT_AUTOCMD
/* 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;
}
#endif
}
if (dir_of_file_exists(fname))
filemess(curbuf, sfname, (char_u *)_("[New File]"), 0);
else
filemess(curbuf, sfname,
(char_u *)_("[New DIRECTORY]"), 0);
#ifdef FEAT_VIMINFO
/* Even though this is a new file, it might have been
* edited before and deleted. Get the old marks. */
check_marks_read();
#endif
#ifdef FEAT_MBYTE
if (eap != NULL && eap->force_enc != 0)
{
/* set forced 'fileencoding' */
fenc = enc_canonize(eap->cmd + eap->force_enc);
if (fenc != NULL)
set_string_option_direct((char_u *)"fenc", -1,
fenc, OPT_FREE|OPT_LOCAL, 0);
vim_free(fenc);
}
#endif
#ifdef FEAT_AUTOCMD
apply_autocmds_exarg(EVENT_BUFNEWFILE, sfname, sfname,
FALSE, curbuf, eap);
#endif
/* remember the current fileformat */
save_file_ff(curbuf);
#if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
if (aborting()) /* autocmds may abort script processing */
return FAIL;
#endif
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;
}
#ifdef FEAT_MBYTE
curbuf->b_p_bomb = FALSE;
curbuf->b_start_bomb = FALSE;
#endif
}
/* 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. */
#ifdef FEAT_QUICKFIX
if (!bt_dontwrite(curbuf))
#endif
{
check_need_swap(newfile);
#ifdef FEAT_AUTOCMD
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;
}
#endif
#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)mch_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;
#ifdef FEAT_AUTOCMD
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;
#ifdef FEAT_EVAL
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;
}
#endif
/*
* 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 | O_EXTRA, 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;
}
}
#endif /* FEAT_AUTOCMD */
/* 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)
{
#ifndef ALWAYS_USE_GUI
mch_msg(_("Vim: Reading from stdin...\n"));
#endif
#ifdef FEAT_GUI
/* Also write a message in the GUI window, if there is one. */
if (gui.in_use && !gui.dying && !gui.starting)
{
p = (char_u *)_("Reading from stdin...");
gui_write(p, (int)STRLEN(p));
}
#endif
}
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;
#ifdef FEAT_MBYTE
/* "++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;
}
#endif
/*
* 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;
#ifdef FEAT_MBYTE
if (set_options)
{
curbuf->b_p_bomb = FALSE;
curbuf->b_start_bomb = FALSE;
}
conv_error = 0;
#endif
}
#ifdef FEAT_CRYPT
if (cryptkey != NULL)
/* Need to reset the state, but keep the key, don't want to ask for it
* again. */
crypt_pop_state();
#endif
/*
* 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 FEAT_MBYTE
# 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)
vim_free(fenc);
fenc = (char_u *)"";
fenc_alloced = FALSE;
}
else
{
if (fenc_alloced)
vim_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)
{
mch_remove(tmpname); /* delete converted file */
vim_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 WIN3264
/*
* Conversion from an MS-Windows codepage to UTF-8 or another codepage
* is handled with MultiByteToWideChar().
*/
if (fio_flags == 0)
fio_flags = get_win_fio_flags(fenc);
# endif
# ifdef MACOS_X
/* Conversion from Apple MacRoman to latin1 or UTF-8 */
if (fio_flags == 0)
fio_flags = get_mac_fio_flags(fenc);
# endif
# ifdef USE_ICONV
/*
* Try using iconv() if we can't convert internally.
*/
if (fio_flags == 0
# ifdef FEAT_EVAL
&& !did_iconv
# endif
)
iconv_fd = (iconv_t)my_iconv_open(
enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc);
# endif
# ifdef FEAT_EVAL
/*
* 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
# endif
{
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);
#endif
if (!skip_read)
{
linerest = 0;
filesize = 0;
skip_count = lines_to_skip;
read_count = lines_to_read;
#ifdef FEAT_MBYTE
conv_restlen = 0;
#endif
#ifdef FEAT_PERSISTENT_UNDO
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);
#endif
}
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 (!skip_read)
{
#if SIZEOF_INT > 2
# if defined(SSIZE_MAX) && (SSIZE_MAX < 0x10000L)
size = SSIZE_MAX; /* use max I/O size, 52K */
# else
size = 0x10000L; /* use buffer >= 64K */
# endif
#else
size = 0x7ff0L - linerest; /* limit buffer to 32K */
#endif
for ( ; size >= 10; size = (long)((long_u)size >> 1))
{
if ((new_buffer = lalloc((long_u)(size + linerest + 1),
FALSE)) != NULL)
break;
}
if (new_buffer == NULL)
{
do_outofmem_msg((long_u)(size * 2 + linerest + 1));
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;
#ifdef FEAT_MBYTE
/* May need room to translate into.
* For iconv() we don't really know the required space, use a
* factor ICONV_MULT.
* latin1 to utf-8: 1 byte becomes up to 2 bytes
* utf-16 to utf-8: 2 bytes become up to 3 bytes, 4 bytes
* become up to 4 bytes, size must be multiple of 2
* ucs-2 to utf-8: 2 bytes become up to 3 bytes, size must be
* multiple of 2
* ucs-4 to utf-8: 4 bytes become up to 6 bytes, size must be
* multiple of 4 */
real_size = (int)size;
# ifdef USE_ICONV
if (iconv_fd != (iconv_t)-1)
size = size / ICONV_MULT;
else
# endif
if (fio_flags & FIO_LATIN1)
size = size / 2;
else if (fio_flags & (FIO_UCS2 | FIO_UTF16))
size = (size * 2 / 3) & ~1;
else if (fio_flags & FIO_UCS4)
size = (size * 2 / 3) & ~3;
else if (fio_flags == FIO_UCSBOM)
size = size / ICONV_MULT; /* worst case */
# ifdef WIN3264
else if (fio_flags & FIO_CODEPAGE)
size = size / ICONV_MULT; /* also worst case */
# endif
# ifdef MACOS_X
else if (fio_flags & FIO_MACROMAN)
size = size / ICONV_MULT; /* also worst case */
# endif
#endif
#ifdef FEAT_MBYTE
if (conv_restlen > 0)
{
/* Insert unconverted bytes from previous line. */
mch_memmove(ptr, conv_rest, conv_restlen);
ptr += conv_restlen;
size -= conv_restlen;
}
#endif
if (read_buffer)
{
/*
* Read bytes from curbuf. Used for converting text read
* from stdin.
*/
if (read_buf_lnum > from)
size = 0;
else
{
int n, ni;
long tlen;
tlen = 0;
for (;;)
{
p = ml_get(read_buf_lnum) + read_buf_col;
n = (int)STRLEN(p);
if ((int)tlen + n + 1 > size)
{
/* Filled up to "size", append partial line.
* Change NL to NUL to reverse the effect done
* below. */
n = (int)(size - tlen);
for (ni = 0; ni < n; ++ni)
{
if (p[ni] == NL)
ptr[tlen++] = NUL;
else
ptr[tlen++] = p[ni];
}
read_buf_col += n;
break;
}
else
{
/* Append whole line and new-line. Change NL
* to NUL to reverse the effect done below. */
for (ni = 0; ni < n; ++ni)
{
if (p[ni] == NL)
ptr[tlen++] = NUL;
else
ptr[tlen++] = p[ni];
}
ptr[tlen++] = NL;
read_buf_col = 0;
if (++read_buf_lnum > from)
{
/* When the last line didn't have an
* end-of-line don't add it now either. */
if (!curbuf->b_p_eol)
--tlen;
size = tlen;
break;
}
}
}
}
}
else
{
/*
* Read bytes from the file.
*/
size = read_eintr(fd, ptr, size);
}
if (size <= 0)
{
if (size < 0) /* read error */
error = TRUE;
#ifdef FEAT_MBYTE
else if (conv_restlen > 0)
{
/*
* Reached end-of-file but some trailing bytes could
* not be converted. Truncated file?
*/
/* When we did a conversion report an error. */
if (fio_flags != 0
# ifdef USE_ICONV
|| iconv_fd != (iconv_t)-1
# endif
)
{
if (conv_error == 0)
conv_error = curbuf->b_ml.ml_line_count
- linecnt + 1;
}
/* Remember the first linenr with an illegal byte */
else if (illegal_byte == 0)
illegal_byte = curbuf->b_ml.ml_line_count
- linecnt + 1;
if (bad_char_behavior == BAD_DROP)
{
*(ptr - conv_restlen) = NUL;
conv_restlen = 0;
}
else
{
/* Replace the trailing bytes with the replacement
* character if we were converting; if we weren't,
* leave the UTF8 checking code to do it, as it
* works slightly differently. */
if (bad_char_behavior != BAD_KEEP && (fio_flags != 0
# ifdef USE_ICONV
|| iconv_fd != (iconv_t)-1
# endif
))
{
while (conv_restlen > 0)
{
*(--ptr) = bad_char_behavior;
--conv_restlen;
}
}
fio_flags = 0; /* don't convert this */
# ifdef USE_ICONV
if (iconv_fd != (iconv_t)-1)
{
iconv_close(iconv_fd);
iconv_fd = (iconv_t)-1;
}
# endif
}
}
#endif
}
#ifdef FEAT_CRYPT
/*
* At start of file: Check for magic number of encryption.
*/
if (filesize == 0)
cryptkey = check_for_cryptkey(cryptkey, ptr, &size,
&filesize, newfile, sfname,
&did_ask_for_key);
/*
* Decrypt the read bytes.
*/
if (cryptkey != NULL && size > 0)
crypt_decode(ptr, size);
#endif
}
skip_read = FALSE;
#ifdef FEAT_MBYTE
/*
* At start of file (or after crypt magic number): Check for BOM.
* Also check for a BOM for other Unicode encodings, but not after
* converting with 'charconvert' or when a BOM has already been
* found.
*/
if ((filesize == 0
# ifdef FEAT_CRYPT
|| (filesize == (CRYPT_MAGIC_LEN
+ crypt_salt_len[use_crypt_method]
+ crypt_seed_len[use_crypt_method])
&& cryptkey != NULL)
# endif
)
&& (fio_flags == FIO_UCSBOM
|| (!curbuf->b_p_bomb
&& tmpname == NULL
&& (*fenc == 'u' || (*fenc == NUL && enc_utf8)))))
{
char_u *ccname;
int blen;
/* no BOM detection in a short file or in binary mode */
if (size < 2 || curbuf->b_p_bin)
ccname = NULL;
else
ccname = check_for_bom(ptr, size, &blen,
fio_flags == FIO_UCSBOM ? FIO_ALL : get_fio_flags(fenc));
if (ccname != NULL)
{
/* Remove BOM from the text */
filesize += blen;
size -= blen;
mch_memmove(ptr, ptr + blen, (size_t)size);
if (set_options)
{
curbuf->b_p_bomb = TRUE;
curbuf->b_start_bomb = TRUE;
}
}
if (fio_flags == FIO_UCSBOM)
{
if (ccname == NULL)
{
/* No BOM detected: retry with next encoding. */
advance_fenc = TRUE;
}
else
{
/* BOM detected: set "fenc" and jump back */
if (fenc_alloced)
vim_free(fenc);
fenc = ccname;
fenc_alloced = FALSE;
}
/* retry reading without getting new bytes or rewinding */
skip_read = TRUE;
goto retry;
}
}
/* Include not converted bytes. */
ptr -= conv_restlen;
size += conv_restlen;
conv_restlen = 0;
#endif
/*
* Break here for a read error or end-of-file.
*/
if (size <= 0)
break;
#ifdef FEAT_MBYTE
# ifdef USE_ICONV
if (iconv_fd != (iconv_t)-1)
{
/*
* Attempt conversion of the read bytes to 'encoding' using
* iconv().
*/
const char *fromp;
char *top;
size_t from_size;
size_t to_size;
fromp = (char *)ptr;
from_size = size;
ptr += size;
top = (char *)ptr;
to_size = real_size - size;
/*
* If there is conversion error or not enough room try using
* another conversion. Except for when there is no
* alternative (help files).
*/
while ((iconv(iconv_fd, (void *)&fromp, &from_size,
&top, &to_size)
== (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL)
|| from_size > CONV_RESTLEN)
{
if (can_retry)
goto rewind_retry;
if (conv_error == 0)
conv_error = readfile_linenr(linecnt,
ptr, (char_u *)top);
/* Deal with a bad byte and continue with the next. */
++fromp;
--from_size;
if (bad_char_behavior == BAD_KEEP)
{
*top++ = *(fromp - 1);
--to_size;
}
else if (bad_char_behavior != BAD_DROP)
{
*top++ = bad_char_behavior;
--to_size;
}
}
if (from_size > 0)
{
/* Some remaining characters, keep them for the next
* round. */
mch_memmove(conv_rest, (char_u *)fromp, from_size);
conv_restlen = (int)from_size;
}
/* move the linerest to before the converted characters */
line_start = ptr - linerest;
mch_memmove(line_start, buffer, (size_t)linerest);
size = (long)((char_u *)top - ptr);
}
# endif
# ifdef WIN3264
if (fio_flags & FIO_CODEPAGE)
{
char_u *src, *dst;
WCHAR ucs2buf[3];
int ucs2len;
int codepage = FIO_GET_CP(fio_flags);
int bytelen;
int found_bad;
char replstr[2];
/*
* Conversion from an MS-Windows codepage or UTF-8 to UTF-8 or
* a codepage, using standard MS-Windows functions. This
* requires two steps:
* 1. convert from 'fileencoding' to ucs-2
* 2. convert from ucs-2 to 'encoding'
*
* Because there may be illegal bytes AND an incomplete byte
* sequence at the end, we may have to do the conversion one
* character at a time to get it right.
*/
/* Replacement string for WideCharToMultiByte(). */
if (bad_char_behavior > 0)
replstr[0] = bad_char_behavior;
else
replstr[0] = '?';
replstr[1] = NUL;
/*
* Move the bytes to the end of the buffer, so that we have
* room to put the result at the start.
*/
src = ptr + real_size - size;
mch_memmove(src, ptr, size);
/*
* Do the conversion.
*/
dst = ptr;
size = size;
while (size > 0)
{
found_bad = FALSE;
# ifdef CP_UTF8 /* VC 4.1 doesn't define CP_UTF8 */
if (codepage == CP_UTF8)
{
/* Handle CP_UTF8 input ourselves to be able to handle
* trailing bytes properly.
* Get one UTF-8 character from src. */
bytelen = (int)utf_ptr2len_len(src, size);
if (bytelen > size)
{
/* Only got some bytes of a character. Normally
* it's put in "conv_rest", but if it's too long
* deal with it as if they were illegal bytes. */
if (bytelen <= CONV_RESTLEN)
break;
/* weird overlong byte sequence */
bytelen = size;
found_bad = TRUE;
}
else
{
int u8c = utf_ptr2char(src);
if (u8c > 0xffff || (*src >= 0x80 && bytelen == 1))
found_bad = TRUE;
ucs2buf[0] = u8c;
ucs2len = 1;
}
}
else
# endif
{
/* We don't know how long the byte sequence is, try
* from one to three bytes. */
for (bytelen = 1; bytelen <= size && bytelen <= 3;
++bytelen)
{
ucs2len = MultiByteToWideChar(codepage,
MB_ERR_INVALID_CHARS,
(LPCSTR)src, bytelen,
ucs2buf, 3);
if (ucs2len > 0)
break;
}
if (ucs2len == 0)
{
/* If we have only one byte then it's probably an
* incomplete byte sequence. Otherwise discard
* one byte as a bad character. */
if (size == 1)
break;
found_bad = TRUE;
bytelen = 1;
}
}
if (!found_bad)
{
int i;
/* Convert "ucs2buf[ucs2len]" to 'enc' in "dst". */
if (enc_utf8)
{
/* From UCS-2 to UTF-8. Cannot fail. */
for (i = 0; i < ucs2len; ++i)
dst += utf_char2bytes(ucs2buf[i], dst);
}
else
{
BOOL bad = FALSE;
int dstlen;
/* From UCS-2 to "enc_codepage". If the
* conversion uses the default character "?",
* the data doesn't fit in this encoding. */
dstlen = WideCharToMultiByte(enc_codepage, 0,
(LPCWSTR)ucs2buf, ucs2len,
(LPSTR)dst, (int)(src - dst),
replstr, &bad);
if (bad)
found_bad = TRUE;
else
dst += dstlen;
}
}
if (found_bad)
{
/* Deal with bytes we can't convert. */
if (can_retry)
goto rewind_retry;
if (conv_error == 0)
conv_error = readfile_linenr(linecnt, ptr, dst);
if (bad_char_behavior != BAD_DROP)
{
if (bad_char_behavior == BAD_KEEP)
{
mch_memmove(dst, src, bytelen);
dst += bytelen;
}
else
*dst++ = bad_char_behavior;
}
}
src += bytelen;
size -= bytelen;
}
if (size > 0)
{
/* An incomplete byte sequence remaining. */
mch_memmove(conv_rest, src, size);
conv_restlen = size;
}
/* The new size is equal to how much "dst" was advanced. */
size = (long)(dst - ptr);
}
else
# endif
# ifdef MACOS_CONVERT
if (fio_flags & FIO_MACROMAN)
{
/*
* Conversion from Apple MacRoman char encoding to UTF-8 or
* latin1. This is in os_mac_conv.c.
*/
if (macroman2enc(ptr, &size, real_size) == FAIL)
goto rewind_retry;
}
else
# endif
if (fio_flags != 0)
{
int u8c;
char_u *dest;
char_u *tail = NULL;
/*
* "enc_utf8" set: Convert Unicode or Latin1 to UTF-8.
* "enc_utf8" not set: Convert Unicode to Latin1.
* Go from end to start through the buffer, because the number
* of bytes may increase.
* "dest" points to after where the UTF-8 bytes go, "p" points
* to after the next character to convert.
*/
dest = ptr + real_size;
if (fio_flags == FIO_LATIN1 || fio_flags == FIO_UTF8)
{
p = ptr + size;
if (fio_flags == FIO_UTF8)
{
/* Check for a trailing incomplete UTF-8 sequence */
tail = ptr + size - 1;
while (tail > ptr && (*tail & 0xc0) == 0x80)
--tail;
if (tail + utf_byte2len(*tail) <= ptr + size)
tail = NULL;
else
p = tail;
}
}
else if (fio_flags & (FIO_UCS2 | FIO_UTF16))
{
/* Check for a trailing byte */
p = ptr + (size & ~1);
if (size & 1)
tail = p;
if ((fio_flags & FIO_UTF16) && p > ptr)
{
/* Check for a trailing leading word */
if (fio_flags & FIO_ENDIAN_L)
{
u8c = (*--p << 8);
u8c += *--p;
}
else
{
u8c = *--p;
u8c += (*--p << 8);
}
if (u8c >= 0xd800 && u8c <= 0xdbff)
tail = p;
else
p += 2;
}
}
else /* FIO_UCS4 */
{
/* Check for trailing 1, 2 or 3 bytes */
p = ptr + (size & ~3);
if (size & 3)
tail = p;
}
/* If there is a trailing incomplete sequence move it to
* conv_rest[]. */
if (tail != NULL)
{
conv_restlen = (int)((ptr + size) - tail);
mch_memmove(conv_rest, (char_u *)tail, conv_restlen);
size -= conv_restlen;
}
while (p > ptr)
{
if (fio_flags & FIO_LATIN1)
u8c = *--p;
else if (fio_flags & (FIO_UCS2 | FIO_UTF16))
{
if (fio_flags & FIO_ENDIAN_L)
{
u8c = (*--p << 8);
u8c += *--p;
}
else
{
u8c = *--p;
u8c += (*--p << 8);
}
if ((fio_flags & FIO_UTF16)
&& u8c >= 0xdc00 && u8c <= 0xdfff)
{
int u16c;
if (p == ptr)
{
/* Missing leading word. */
if (can_retry)
goto rewind_retry;
if (conv_error == 0)
conv_error = readfile_linenr(linecnt,
ptr, p);
if (bad_char_behavior == BAD_DROP)
continue;
if (bad_char_behavior != BAD_KEEP)
u8c = bad_char_behavior;
}
/* found second word of double-word, get the first
* word and compute the resulting character */
if (fio_flags & FIO_ENDIAN_L)
{
u16c = (*--p << 8);
u16c += *--p;
}
else
{
u16c = *--p;
u16c += (*--p << 8);
}
u8c = 0x10000 + ((u16c & 0x3ff) << 10)
+ (u8c & 0x3ff);
/* Check if the word is indeed a leading word. */
if (u16c < 0xd800 || u16c > 0xdbff)
{
if (can_retry)
goto rewind_retry;
if (conv_error == 0)
conv_error = readfile_linenr(linecnt,
ptr, p);
if (bad_char_behavior == BAD_DROP)
continue;
if (bad_char_behavior != BAD_KEEP)
u8c = bad_char_behavior;
}
}
}
else if (fio_flags & FIO_UCS4)
{
if (fio_flags & FIO_ENDIAN_L)
{
u8c = (*--p << 24);
u8c += (*--p << 16);
u8c += (*--p << 8);
u8c += *--p;
}
else /* big endian */
{
u8c = *--p;
u8c += (*--p << 8);
u8c += (*--p << 16);
u8c += (*--p << 24);
}
}
else /* UTF-8 */
{
if (*--p < 0x80)
u8c = *p;
else
{
len = utf_head_off(ptr, p);
p -= len;
u8c = utf_ptr2char(p);
if (len == 0)
{
/* Not a valid UTF-8 character, retry with
* another fenc when possible, otherwise just
* report the error. */
if (can_retry)
goto rewind_retry;
if (conv_error == 0)
conv_error = readfile_linenr(linecnt,
ptr, p);
if (bad_char_behavior == BAD_DROP)
continue;
if (bad_char_behavior != BAD_KEEP)
u8c = bad_char_behavior;
}
}
}
if (enc_utf8) /* produce UTF-8 */
{
dest -= utf_char2len(u8c);
(void)utf_char2bytes(u8c, dest);
}
else /* produce Latin1 */
{
--dest;
if (u8c >= 0x100)
{
/* character doesn't fit in latin1, retry with
* another fenc when possible, otherwise just
* report the error. */
if (can_retry)
goto rewind_retry;
if (conv_error == 0)
conv_error = readfile_linenr(linecnt, ptr, p);
if (bad_char_behavior == BAD_DROP)
++dest;
else if (bad_char_behavior == BAD_KEEP)
*dest = u8c;
else if (eap != NULL && eap->bad_char != 0)
*dest = bad_char_behavior;
else
*dest = 0xBF;
}
else
*dest = u8c;
}
}
/* move the linerest to before the converted characters */
line_start = dest - linerest;
mch_memmove(line_start, buffer, (size_t)linerest);
size = (long)((ptr + real_size) - dest);
ptr = dest;
}
else if (enc_utf8 && !curbuf->b_p_bin)
{
int incomplete_tail = FALSE;
/* Reading UTF-8: Check if the bytes are valid UTF-8. */
for (p = ptr; ; ++p)
{
int todo = (int)((ptr + size) - p);
int l;
if (todo <= 0)
break;
if (*p >= 0x80)
{
/* A length of 1 means it's an illegal byte. Accept
* an incomplete character at the end though, the next
* read() will get the next bytes, we'll check it
* then. */
l = utf_ptr2len_len(p, todo);
if (l > todo && !incomplete_tail)
{
/* Avoid retrying with a different encoding when
* a truncated file is more likely, or attempting
* to read the rest of an incomplete sequence when
* we have already done so. */
if (p > ptr || filesize > 0)
incomplete_tail = TRUE;
/* Incomplete byte sequence, move it to conv_rest[]
* and try to read the rest of it, unless we've
* already done so. */
if (p > ptr)
{
conv_restlen = todo;
mch_memmove(conv_rest, p, conv_restlen);
size -= conv_restlen;
break;
}
}
if (l == 1 || l > todo)
{
/* Illegal byte. If we can try another encoding
* do that, unless at EOF where a truncated
* file is more likely than a conversion error. */
if (can_retry && !incomplete_tail)
break;
# ifdef USE_ICONV
/* When we did a conversion report an error. */
if (iconv_fd != (iconv_t)-1 && conv_error == 0)
conv_error = readfile_linenr(linecnt, ptr, p);
# endif
/* Remember the first linenr with an illegal byte */
if (conv_error == 0 && illegal_byte == 0)
illegal_byte = readfile_linenr(linecnt, ptr, p);
/* Drop, keep or replace the bad byte. */
if (bad_char_behavior == BAD_DROP)
{
mch_memmove(p, p + 1, todo - 1);
--p;
--size;
}
else if (bad_char_behavior != BAD_KEEP)
*p = bad_char_behavior;
}
else
p += l - 1;
}
}
if (p < ptr + size && !incomplete_tail)
{
/* Detected a UTF-8 error. */
rewind_retry:
/* Retry reading with another conversion. */
# if defined(FEAT_EVAL) && defined(USE_ICONV)
if (*p_ccv != NUL && iconv_fd != (iconv_t)-1)
/* iconv() failed, try 'charconvert' */
did_iconv = TRUE;
else
# endif
/* use next item from 'fileencodings' */
advance_fenc = TRUE;
file_rewind = TRUE;
goto retry;
}
}
#endif
/* count the number of characters (after conversion!) */
filesize += size;
/*
* 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] == CAR))
fileformat = EOL_DOS;
else
fileformat = EOL_UNIX;
break;
}
}
/* Don't give in to EOL_UNIX if EOL_MAC is more likely */
if (fileformat == EOL_UNIX && try_mac)
{
/* Need to reset the counters when retrying fenc. */
try_mac = 1;
try_unix = 1;
for (; p >= ptr && *p != CAR; p--)
;
if (p >= ptr)
{
for (p = ptr; p < ptr + size; ++p)
{
if (*p == NL)
try_unix++;
else if (*p == CAR)
try_mac++;
}
if (try_mac > try_unix)
fileformat = EOL_MAC;
}
}
}
/* 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)
fileformat = default_fileformat();
/* if editing a new file: may set p_tx and p_ff */
if (set_options)
set_fileformat(fileformat, OPT_LOCAL);
}
}
/*
* This loop is executed once for every character read.
* Keep it fast!
*/
if (fileformat == EOL_MAC)
{
--ptr;
while (++ptr, --size >= 0)
{
/* catch most common case first */
if ((c = *ptr) != NUL && c != CAR && c != NL)
continue;
if (c == NUL)
*ptr = NL; /* NULs are replaced by newlines! */
else if (c == NL)
*ptr = CAR; /* NLs are replaced by CRs! */
else
{
if (skip_count == 0)
{
*ptr = NUL; /* end of line */
len = (colnr_T) (ptr - line_start + 1);
if (ml_append(lnum, line_start, len, newfile) == FAIL)
{
error = TRUE;
break;
}
#ifdef FEAT_PERSISTENT_UNDO
if (read_undo_file)
sha256_update(&sha_ctx, line_start, len);
#endif
++lnum;
if (--read_count == 0)
{
error = TRUE; /* break loop */
line_start = ptr; /* nothing left to write */
break;
}
}
else
--skip_count;
line_start = ptr + 1;
}
}
}
else
{
--ptr;
while (++ptr, --size >= 0)
{
if ((c = *ptr) != NUL && c != NL) /* catch most common case */
continue;
if (c == NUL)
*ptr = NL; /* NULs are replaced by newlines! */
else
{
if (skip_count == 0)
{
*ptr = NUL; /* end of line */
len = (colnr_T)(ptr - line_start + 1);
if (fileformat == EOL_DOS)
{
if (ptr[-1] == CAR) /* remove CR */
{
ptr[-1] = NUL;
--len;
}
/*
* Reading in Dos format, but no CR-LF found!
* When 'fileformats' includes "unix", delete all
* the lines read so far and start all over again.
* Otherwise give an error message later.
*/
else if (ff_error != EOL_DOS)
{
if ( try_unix
&& !read_stdin
&& (read_buffer
|| lseek(fd, (off_t)0L, SEEK_SET) == 0))
{
fileformat = EOL_UNIX;
if (set_options)
set_fileformat(EOL_UNIX, OPT_LOCAL);
file_rewind = TRUE;
keep_fileformat = TRUE;
goto retry;
}
ff_error = EOL_DOS;
}
}
if (ml_append(lnum, line_start, len, newfile) == FAIL)
{
error = TRUE;
break;
}
#ifdef FEAT_PERSISTENT_UNDO
if (read_undo_file)
sha256_update(&sha_ctx, line_start, len);
#endif
++lnum;
if (--read_count == 0)
{
error = TRUE; /* break loop */
line_start = ptr; /* nothing left to write */
break;
}
}
else
--skip_count;
line_start = ptr + 1;
}
}
}
linerest = (long)(ptr - line_start);
ui_breakcheck();
}
failed:
/* not an error, max. number of lines reached */
if (error && read_count == 0)
error = FALSE;
/*
* If we get EOF in the middle of a line, note the fact and
* complete the line ourselves.
* In Dos format ignore a trailing CTRL-Z, unless 'binary' set.
*/
if (!error
&& !got_int
&& linerest != 0
&& !(!curbuf->b_p_bin
&& fileformat == EOL_DOS
&& *line_start == Ctrl_Z
&& ptr == line_start + 1))
{
/* remember for when writing */
if (set_options)
curbuf->b_p_eol = FALSE;
*ptr = NUL;
len = (colnr_T)(ptr - line_start + 1);
if (ml_append(lnum, line_start, len, newfile) == FAIL)
error = TRUE;
else
{
#ifdef FEAT_PERSISTENT_UNDO
if (read_undo_file)
sha256_update(&sha_ctx, line_start, len);
#endif
read_no_eol_lnum = ++lnum;
}
}
if (set_options)
save_file_ff(curbuf); /* remember the current file format */
#ifdef FEAT_CRYPT
crypt_method_used = use_crypt_method;
if (cryptkey != NULL)
{
crypt_pop_state();
if (cryptkey != curbuf->b_p_key)
free_crypt_key(cryptkey);
/* don't set cryptkey to NULL, it's used below as a flag that
* encryption was used */
}
#endif
#ifdef FEAT_MBYTE
/* If editing a new file: set 'fenc' for the current buffer.
* Also for ":read ++edit file". */
if (set_options)
set_string_option_direct((char_u *)"fenc", -1, fenc,
OPT_FREE|OPT_LOCAL, 0);
if (fenc_alloced)
vim_free(fenc);
# ifdef USE_ICONV
if (iconv_fd != (iconv_t)-1)
{
iconv_close(iconv_fd);
iconv_fd = (iconv_t)-1;
}
# endif
#endif
if (!read_buffer && !read_stdin)
close(fd); /* errors are ignored */
#ifdef HAVE_FD_CLOEXEC
else
{
int fdflags = fcntl(fd, F_GETFD);
if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC);
}
#endif
vim_free(buffer);
#ifdef HAVE_DUP
if (read_stdin)
{
/* Use stderr for stdin, makes shell commands work. */
close(0);
ignored = dup(2);
}
#endif
#ifdef FEAT_MBYTE
if (tmpname != NULL)
{
mch_remove(tmpname); /* delete converted file */
vim_free(tmpname);
}
#endif
--no_wait_return; /* may wait for return now */
/*
* In recovery mode everything but autocommands is skipped.
*/
if (!recoverymode)
{
/* need to delete the last line, which comes from the empty buffer */
if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY))
{
#ifdef FEAT_NETBEANS_INTG
netbeansFireChanges = 0;
#endif
ml_delete(curbuf->b_ml.ml_line_count, FALSE);
#ifdef FEAT_NETBEANS_INTG
netbeansFireChanges = 1;
#endif
--linecnt;
}
linecnt = curbuf->b_ml.ml_line_count - linecnt;
if (filesize == 0)
linecnt = 0;
if (newfile || read_buffer)
{
redraw_curbuf_later(NOT_VALID);
#ifdef FEAT_DIFF
/* After reading the text into the buffer the diff info needs to
* be updated. */
diff_invalidate(curbuf);
#endif
#ifdef FEAT_FOLDING
/* All folds in the window are invalid now. Mark them for update
* before triggering autocommands. */
foldUpdateAll(curwin);
#endif
}
else if (linecnt) /* appended at least one line */
appended_lines_mark(from, linecnt);
#ifndef ALWAYS_USE_GUI
/*
* If we were reading from the same terminal as where messages go,
* the screen will have been messed up.
* Switch on raw mode now and clear the screen.
*/
if (read_stdin)
{
settmode(TMODE_RAW); /* set to raw mode */
starttermcap();
screenclear();
}
#endif
if (got_int)
{
if (!(flags & READ_DUMMY))
{
filemess(curbuf, sfname, (char_u *)_(e_interr), 0);
if (newfile)
curbuf->b_p_ro = TRUE; /* must use "w!" now */
}
msg_scroll = msg_save;
#ifdef FEAT_VIMINFO
check_marks_read();
#endif
return OK; /* an interrupt isn't really an error */
}
if (!filtering && !(flags & READ_DUMMY))
{
msg_add_fname(curbuf, sfname); /* fname in IObuff with quotes */
c = FALSE;
#ifdef UNIX
# ifdef S_ISFIFO
if (S_ISFIFO(perm)) /* fifo or socket */
{
STRCAT(IObuff, _("[fifo/socket]"));
c = TRUE;
}
# else
# ifdef S_IFIFO
if ((perm & S_IFMT) == S_IFIFO) /* fifo */
{
STRCAT(IObuff, _("[fifo]"));
c = TRUE;
}
# endif
# ifdef S_IFSOCK
if ((perm & S_IFMT) == S_IFSOCK) /* or socket */
{
STRCAT(IObuff, _("[socket]"));
c = TRUE;
}
# endif
# endif
# ifdef OPEN_CHR_FILES
if (S_ISCHR(perm)) /* or character special */
{
STRCAT(IObuff, _("[character special]"));
c = TRUE;
}
# endif
#endif
if (curbuf->b_p_ro)
{
STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
c = TRUE;
}
if (read_no_eol_lnum)
{
msg_add_eol();
c = TRUE;
}
if (ff_error == EOL_DOS)
{
STRCAT(IObuff, _("[CR missing]"));
c = TRUE;
}
if (split)
{
STRCAT(IObuff, _("[long lines split]"));
c = TRUE;
}
#ifdef FEAT_MBYTE
if (notconverted)
{
STRCAT(IObuff, _("[NOT converted]"));
c = TRUE;
}
else if (converted)
{
STRCAT(IObuff, _("[converted]"));
c = TRUE;
}
#endif
#ifdef FEAT_CRYPT
if (cryptkey != NULL)
{
if (crypt_method_used == 1)
STRCAT(IObuff, _("[blowfish]"));
else
STRCAT(IObuff, _("[crypted]"));
c = TRUE;
}
#endif
#ifdef FEAT_MBYTE
if (conv_error != 0)
{
sprintf((char *)IObuff + STRLEN(IObuff),
_("[CONVERSION ERROR in line %ld]"), (long)conv_error);
c = TRUE;
}
else if (illegal_byte > 0)
{
sprintf((char *)IObuff + STRLEN(IObuff),
_("[ILLEGAL BYTE in line %ld]"), (long)illegal_byte);
c = TRUE;
}
else
#endif
if (error)
{
STRCAT(IObuff, _("[READ ERRORS]"));
c = TRUE;
}
if (msg_add_fileformat(fileformat))
c = TRUE;
#ifdef FEAT_CRYPT
if (cryptkey != NULL)
msg_add_lines(c, (long)linecnt, filesize
- CRYPT_MAGIC_LEN
- crypt_salt_len[use_crypt_method]
- crypt_seed_len[use_crypt_method]);
else
#endif
msg_add_lines(c, (long)linecnt, filesize);
vim_free(keep_msg);
keep_msg = NULL;
msg_scrolled_ign = TRUE;
#ifdef ALWAYS_USE_GUI
/* Don't show the message when reading stdin, it would end up in a
* message box (which might be shown when exiting!) */
if (read_stdin || read_buffer)
p = msg_may_trunc(FALSE, IObuff);
else
#endif
p = msg_trunc_attr(IObuff, FALSE, 0);
if (read_stdin || read_buffer || restart_edit != 0
|| (msg_scrolled != 0 && !need_wait_return))
/* Need to repeat the message after redrawing when:
* - When reading from stdin (the screen will be cleared next).
* - When restart_edit is set (otherwise there will be a delay
* before redrawing).
* - When the screen was scrolled but there is no wait-return
* prompt. */
set_keep_msg(p, 0);
msg_scrolled_ign = FALSE;
}
/* with errors writing the file requires ":w!" */
if (newfile && (error
#ifdef FEAT_MBYTE
|| conv_error != 0
|| (illegal_byte > 0 && bad_char_behavior != BAD_KEEP)
#endif
))
curbuf->b_p_ro = TRUE;
u_clearline(); /* cannot use "U" command after adding lines */
/*
* In Ex mode: cursor at last new line.
* Otherwise: cursor at first new line.
*/
if (exmode_active)
curwin->w_cursor.lnum = from + linecnt;
else
curwin->w_cursor.lnum = from + 1;
check_cursor_lnum();
beginline(BL_WHITE | BL_FIX); /* on first non-blank */
/*
* Set '[ and '] marks to the newly read lines.
*/
curbuf->b_op_start.lnum = from + 1;
curbuf->b_op_start.col = 0;
curbuf->b_op_end.lnum = from + linecnt;
curbuf->b_op_end.col = 0;
#ifdef WIN32
/*
* Work around a weird problem: When a file has two links (only
* possible on NTFS) and we write through one link, then stat() it
* through the other link, the timestamp information may be wrong.
* It's correct again after reading the file, thus reset the timestamp
* here.
*/
if (newfile && !read_stdin && !read_buffer
&& mch_stat((char *)fname, &st) >= 0)
{
buf_store_time(curbuf, &st, fname);
curbuf->b_mtime_read = curbuf->b_mtime;
}
#endif
}
msg_scroll = msg_save;
#ifdef FEAT_VIMINFO
/*
* Get the marks before executing autocommands, so they can be used there.
*/
check_marks_read();
#endif
/*
* Trick: We remember if the last line of the read didn't have
* an eol even when 'binary' is off, for when writing it again with
* 'binary' on. This is required for
* ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work.
*/
curbuf->b_no_eol_lnum = read_no_eol_lnum;
/* When reloading a buffer put the cursor at the first line that is
* different. */
if (flags & READ_KEEP_UNDO)
u_find_first_changed();
#ifdef FEAT_PERSISTENT_UNDO
/*
* When opening a new file locate undo info and read it.
*/
if (read_undo_file)
{
char_u hash[UNDO_HASH_SIZE];
sha256_finish(&sha_ctx, hash);
u_read_undo(NULL, hash, fname);
}
#endif
#ifdef FEAT_AUTOCMD
if (!read_stdin && !read_buffer)
{
int m = msg_scroll;
int n = msg_scrolled;
/* Save the fileformat now, otherwise the buffer will be considered
* modified if the format/encoding was automatically detected. */
if (set_options)
save_file_ff(curbuf);
/*
* 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_FILTERREADPOST, NULL, sfname,
FALSE, curbuf, eap);
else if (newfile)
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
FALSE, curbuf, eap);
else
apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname,
FALSE, NULL, eap);
if (msg_scrolled == n)
msg_scroll = m;
# ifdef FEAT_EVAL
if (aborting()) /* autocmds may abort script processing */
return FAIL;
# endif
}
#endif
if (recoverymode && error)
return FAIL;
return OK;
}
#ifdef OPEN_CHR_FILES
/*
* Returns TRUE if the file name argument is of the form "/dev/fd/\d\+",
* which is the name of files used for process substitution output by
* some shells on some operating systems, e.g., bash on SunOS.
* Do not accept "/dev/fd/[012]", opening these may hang Vim.
*/
static int
is_dev_fd_file(fname)
char_u *fname;
{
return (STRNCMP(fname, "/dev/fd/", 8) == 0
&& VIM_ISDIGIT(fname[8])
&& *skipdigits(fname + 9) == NUL
&& (fname[9] != NUL
|| (fname[8] != '0' && fname[8] != '1' && fname[8] != '2')));
}
#endif
#ifdef FEAT_MBYTE
/*
* From the current line count and characters read after that, estimate the
* line number where we are now.
* Used for error messages that include a line number.
*/
static linenr_T
readfile_linenr(linecnt, p, endp)
linenr_T linecnt; /* line count before reading more bytes */
char_u *p; /* start of more bytes read */
char_u *endp; /* end of more bytes read */
{
char_u *s;
linenr_T lnum;
lnum = curbuf->b_ml.ml_line_count - linecnt + 1;
for (s = p; s < endp; ++s)
if (*s == '\n')
++lnum;
return lnum;
}
#endif
/*
* Fill "*eap" to force the 'fileencoding', 'fileformat' and 'binary to be
* equal to the buffer "buf". Used for calling readfile().
* Returns OK or FAIL.
*/
int
prep_exarg(eap, buf)
exarg_T *eap;
buf_T *buf;
{
eap->cmd = alloc((unsigned)(STRLEN(buf->b_p_ff)
#ifdef FEAT_MBYTE
+ STRLEN(buf->b_p_fenc)
#endif
+ 15));
if (eap->cmd == NULL)
return FAIL;
#ifdef FEAT_MBYTE
sprintf((char *)eap->cmd, "e ++ff=%s ++enc=%s", buf->b_p_ff, buf->b_p_fenc);
eap->force_enc = 14 + (int)STRLEN(buf->b_p_ff);
eap->bad_char = buf->b_bad_char;
#else
sprintf((char *)eap->cmd, "e ++ff=%s", buf->b_p_ff);
#endif
eap->force_ff = 7;
eap->force_bin = buf->b_p_bin ? FORCE_BIN : FORCE_NOBIN;
eap->read_edit = FALSE;
eap->forceit = FALSE;
return OK;
}
#ifdef FEAT_MBYTE
/*
* Find next fileencoding to use from 'fileencodings'.
* "pp" points to fenc_next. It's advanced to the next item.
* When there are no more items, an empty string is returned and *pp is set to
* NULL.
* When *pp is not set to NULL, the result is in allocated memory.
*/
static char_u *
next_fenc(pp)
char_u **pp;
{
char_u *p;
char_u *r;
if (**pp == NUL)
{
*pp = NULL;
return (char_u *)"";
}
p = vim_strchr(*pp, ',');
if (p == NULL)
{
r = enc_canonize(*pp);
*pp += STRLEN(*pp);
}
else
{
r = vim_strnsave(*pp, (int)(p - *pp));
*pp = p + 1;
if (r != NULL)
{
p = enc_canonize(r);
vim_free(r);
r = p;
}
}
if (r == NULL) /* out of memory */
{
r = (char_u *)"";
*pp = NULL;
}
return r;
}
# ifdef FEAT_EVAL
/*
* Convert a file with the 'charconvert' expression.
* This closes the file which is to be read, converts it and opens the
* resulting file for reading.
* Returns name of the resulting converted file (the caller should delete it
* after reading it).
* Returns NULL if the conversion failed ("*fdp" is not set) .
*/
static char_u *
readfile_charconvert(fname, fenc, fdp)
char_u *fname; /* name of input file */
char_u *fenc; /* converted from */
int *fdp; /* in/out: file descriptor of file */
{
char_u *tmpname;
char_u *errmsg = NULL;
tmpname = vim_tempname('r');
if (tmpname == NULL)
errmsg = (char_u *)_("Can't find temp file for conversion");
else
{
close(*fdp); /* close the input file, ignore errors */
*fdp = -1;
if (eval_charconvert(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc,
fname, tmpname) == FAIL)
errmsg = (char_u *)_("Conversion with 'charconvert' failed");
if (errmsg == NULL && (*fdp = mch_open((char *)tmpname,
O_RDONLY | O_EXTRA, 0)) < 0)
errmsg = (char_u *)_("can't read output of 'charconvert'");
}
if (errmsg != NULL)
{
/* Don't use emsg(), it breaks mappings, the retry with
* another type of conversion might still work. */
MSG(errmsg);
if (tmpname != NULL)
{
mch_remove(tmpname); /* delete converted file */
vim_free(tmpname);
tmpname = NULL;
}
}
/* If the input file is closed, open it (caller should check for error). */
if (*fdp < 0)
*fdp = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
return tmpname;
}
# endif
#endif
#ifdef FEAT_VIMINFO
/*
* Read marks for the current buffer from the viminfo file, when we support
* buffer marks and the buffer has a name.
*/
static void
check_marks_read()
{
if (!curbuf->b_marks_read && get_viminfo_parameter('\'') > 0
&& curbuf->b_ffname != NULL)
read_viminfo(NULL, VIF_WANT_MARKS);
/* Always set b_marks_read; needed when 'viminfo' is changed to include
* the ' parameter after opening a buffer. */
curbuf->b_marks_read = TRUE;
}
#endif
#if defined(FEAT_CRYPT) || defined(PROTO)
/*
* Get the crypt method used for a file from "ptr[len]", the magic text at the
* start of the file.
* Returns -1 when no encryption used.
*/
static int
crypt_method_from_magic(ptr, len)
char *ptr;
int len;
{
int i;
for (i = 0; i < (int)(sizeof(crypt_magic) / sizeof(crypt_magic[0])); i++)
{
if (len < (CRYPT_MAGIC_LEN + crypt_salt_len[i] + crypt_seed_len[i]))
continue;
if (memcmp(ptr, crypt_magic[i], CRYPT_MAGIC_LEN) == 0)
return i;
}
i = (int)STRLEN(crypt_magic_head);
if (len >= i && memcmp(ptr, crypt_magic_head, i) == 0)
EMSG(_("E821: File is encrypted with unknown method"));
return -1;
}
/*
* Check for magic number used for encryption. Applies to the current buffer.
* If found, the magic number is removed from ptr[*sizep] and *sizep and
* *filesizep are updated.
* Return the (new) encryption key, NULL for no encryption.
*/
static char_u *
check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask)
char_u *cryptkey; /* previous encryption key or NULL */
char_u *ptr; /* pointer to read bytes */
long *sizep; /* length of read bytes */
off_t *filesizep; /* nr of bytes used from file */
int newfile; /* editing a new buffer */
char_u *fname; /* file name to display */
int *did_ask; /* flag: whether already asked for key */
{
int method = crypt_method_from_magic((char *)ptr, *sizep);
if (method >= 0)
{
set_crypt_method(curbuf, method);
if (method > 0)
(void)blowfish_self_test();
if (cryptkey == NULL && !*did_ask)
{
if (*curbuf->b_p_key)
cryptkey = curbuf->b_p_key;
else
{
/* When newfile is TRUE, store the typed key in the 'key'
* option and don't free it. bf needs hash of the key saved.
* Don't ask for the key again when first time Enter was hit.
* Happens when retrying to detect encoding. */
smsg((char_u *)_(need_key_msg), fname);
msg_scroll = TRUE;
cryptkey = get_crypt_key(newfile, FALSE);
*did_ask = TRUE;
/* check if empty key entered */
if (cryptkey != NULL && *cryptkey == NUL)
{
if (cryptkey != curbuf->b_p_key)
vim_free(cryptkey);
cryptkey = NULL;
}
}
}
if (cryptkey != NULL)
{
int seed_len = crypt_seed_len[method];
int salt_len = crypt_salt_len[method];
crypt_push_state();
use_crypt_method = method;
if (method == 0)
crypt_init_keys(cryptkey);
else
{
bf_key_init(cryptkey, ptr + CRYPT_MAGIC_LEN, salt_len);
bf_ofb_init(ptr + CRYPT_MAGIC_LEN + salt_len, seed_len);
}
/* Remove magic number from the text */
*filesizep += CRYPT_MAGIC_LEN + salt_len + seed_len;
*sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len;
mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len,
(size_t)*sizep);
}
}
/* When starting to edit a new file which does not have encryption, clear
* the 'key' option, except when starting up (called with -x argument) */
else if (newfile && *curbuf->b_p_key != NUL && !starting)
set_option_value((char_u *)"key", 0L, (char_u *)"", OPT_LOCAL);
return cryptkey;
}
/*
* Check for magic number used for encryption. Applies to the current buffer.
* If found and decryption is possible returns OK;
*/
int
prepare_crypt_read(fp)
FILE *fp;
{
int method;
char_u buffer[CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX
+ CRYPT_SEED_LEN_MAX + 2];
if (fread(buffer, CRYPT_MAGIC_LEN, 1, fp) != 1)
return FAIL;
method = crypt_method_from_magic((char *)buffer,
CRYPT_MAGIC_LEN +
CRYPT_SEED_LEN_MAX +
CRYPT_SALT_LEN_MAX);
if (method < 0 || method != get_crypt_method(curbuf))
return FAIL;
crypt_push_state();
if (method == 0)
crypt_init_keys(curbuf->b_p_key);
else
{
int salt_len = crypt_salt_len[method];
int seed_len = crypt_seed_len[method];
if (fread(buffer, salt_len + seed_len, 1, fp) != 1)
return FAIL;
bf_key_init(curbuf->b_p_key, buffer, salt_len);
bf_ofb_init(buffer + salt_len, seed_len);
}
return OK;
}
/*
* Prepare for writing encrypted bytes for buffer "buf".
* Returns a pointer to an allocated header of length "*lenp".
* When out of memory returns NULL.
* Otherwise calls crypt_push_state(), call crypt_pop_state() later.
*/
char_u *
prepare_crypt_write(buf, lenp)
buf_T *buf;
int *lenp;
{
char_u *header;
int seed_len = crypt_seed_len[get_crypt_method(buf)];
int salt_len = crypt_salt_len[get_crypt_method(buf)];
char_u *salt;
char_u *seed;
header = alloc_clear(CRYPT_MAGIC_LEN + CRYPT_SALT_LEN_MAX
+ CRYPT_SEED_LEN_MAX + 2);
if (header != NULL)
{
crypt_push_state();
use_crypt_method = get_crypt_method(buf); /* select zip or blowfish */
vim_strncpy(header, (char_u *)crypt_magic[use_crypt_method],
CRYPT_MAGIC_LEN);
if (use_crypt_method == 0)
crypt_init_keys(buf->b_p_key);
else
{
/* Using blowfish, add salt and seed. */
salt = header + CRYPT_MAGIC_LEN;
seed = salt + salt_len;
sha2_seed(salt, salt_len, seed, seed_len);
bf_key_init(buf->b_p_key, salt, salt_len);
bf_ofb_init(seed, seed_len);
}
}
*lenp = CRYPT_MAGIC_LEN + salt_len + seed_len;
return header;
}
#endif /* FEAT_CRYPT */
#ifdef UNIX
static void
set_file_time(fname, atime, mtime)
char_u *fname;
time_t atime; /* access time */
time_t mtime; /* modification time */
{
# if defined(HAVE_UTIME) && defined(HAVE_UTIME_H)
struct utimbuf buf;
buf.actime = atime;
buf.modtime = mtime;
(void)utime((char *)fname, &buf);
# else
# if defined(HAVE_UTIMES)
struct timeval tvp[2];
tvp[0].tv_sec = atime;
tvp[0].tv_usec = 0;
tvp[1].tv_sec = mtime;
tvp[1].tv_usec = 0;
# ifdef NeXT
(void)utimes((char *)fname, tvp);
# else
(void)utimes((char *)fname, (const struct timeval *)&tvp);
# endif
# endif
# endif
}
#endif /* UNIX */
#if defined(VMS) && !defined(MIN)
/* Older DECC compiler for VAX doesn't define MIN() */
# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
* Return TRUE if a file appears to be read-only from the file permissions.
*/
int
check_file_readonly(fname, perm)
char_u *fname; /* full path to file */
int perm; /* known permissions on file */
{
#ifndef USE_MCH_ACCESS
int fd = 0;
#endif
return (
#ifdef USE_MCH_ACCESS
# ifdef UNIX
(perm & 0222) == 0 ||
# endif
mch_access((char *)fname, W_OK)
#else
(fd = mch_open((char *)fname, O_RDWR | O_EXTRA, 0)) < 0
? TRUE : (close(fd), FALSE)
#endif
);
}
/*
* buf_write() - write to file "fname" lines "start" through "end"
*
* We do our own buffering here because fwrite() is so slow.
*
* If "forceit" is true, we don't care for errors when attempting backups.
* In case of an error everything possible is done to restore the original
* file. But when "forceit" is TRUE, we risk losing it.
*
* When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and
* "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
*
* This function must NOT use NameBuff (because it's called by autowrite()).
*
* return FAIL for failure, OK otherwise
*/
int
buf_write(buf, fname, sfname, start, end, eap, append, forceit,
reset_changed, filtering)
buf_T *buf;
char_u *fname;
char_u *sfname;
linenr_T start, end;
exarg_T *eap; /* for forced 'ff' and 'fenc', can be
NULL! */
int append; /* append to the file */
int forceit;
int reset_changed;
int filtering;
{
int fd;
char_u *backup = NULL;
int backup_copy = FALSE; /* copy the original file? */
int dobackup;
char_u *ffname;
char_u *wfname = NULL; /* name of file to write to */
char_u *s;
char_u *ptr;
char_u c;
int len;
linenr_T lnum;
long nchars;
char_u *errmsg = NULL;
int errmsg_allocated = FALSE;
char_u *errnum = NULL;
char_u *buffer;
char_u smallbuf[SMBUFSIZE];
char_u *backup_ext;
int bufsize;
long perm; /* file permissions */
int retval = OK;
int newfile = FALSE; /* TRUE if file doesn't exist yet */
int msg_save = msg_scroll;
int overwriting; /* TRUE if writing over original */
int no_eol = FALSE; /* no end-of-line written */
int device = FALSE; /* writing to a device */
struct stat st_old;
int prev_got_int = got_int;
int file_readonly = FALSE; /* overwritten file is read-only */
static char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')";
#if defined(UNIX) || defined(__EMX__XX) /*XXX fix me sometime? */
int made_writable = FALSE; /* 'w' bit has been set */
#endif
/* writing everything */
int whole = (start == 1 && end == buf->b_ml.ml_line_count);
#ifdef FEAT_AUTOCMD
linenr_T old_line_count = buf->b_ml.ml_line_count;
#endif
int attr;
int fileformat;
int write_bin;
struct bw_info write_info; /* info for buf_write_bytes() */
#ifdef FEAT_MBYTE
int converted = FALSE;
int notconverted = FALSE;
char_u *fenc; /* effective 'fileencoding' */
char_u *fenc_tofree = NULL; /* allocated "fenc" */
#endif
#ifdef HAS_BW_FLAGS
int wb_flags = 0;
#endif
#ifdef HAVE_ACL
vim_acl_T acl = NULL; /* ACL copied from original file to
backup or new file */
#endif
#ifdef FEAT_PERSISTENT_UNDO
int write_undo_file = FALSE;
context_sha256_T sha_ctx;
#endif
#ifdef FEAT_CRYPT
int crypt_method_used;
#endif
if (fname == NULL || *fname == NUL) /* safety check */
return FAIL;
if (buf->b_ml.ml_mfp == NULL)
{
/* This can happen during startup when there is a stray "w" in the
* vimrc file. */
EMSG(_(e_emptybuf));
return FAIL;
}
/*
* Disallow writing from .exrc and .vimrc in current directory for
* security reasons.
*/
if (check_secure())
return FAIL;
/* Avoid a crash for a long name. */
if (STRLEN(fname) >= MAXPATHL)
{
EMSG(_(e_longname));
return FAIL;
}
#ifdef FEAT_MBYTE
/* must init bw_conv_buf and bw_iconv_fd before jumping to "fail" */
write_info.bw_conv_buf = NULL;
write_info.bw_conv_error = FALSE;
write_info.bw_conv_error_lnum = 0;
write_info.bw_restlen = 0;
# ifdef USE_ICONV
write_info.bw_iconv_fd = (iconv_t)-1;
# endif
#endif
/* After writing a file changedtick changes but we don't want to display
* the line. */
ex_no_reprint = TRUE;
/*
* If there is no file name yet, use the one for the written file.
* BF_NOTEDITED is set to reflect this (in case the write fails).
* Don't do this when the write is for a filter command.
* Don't do this when appending.
* Only do this when 'cpoptions' contains the 'F' flag.
*/
if (buf->b_ffname == NULL
&& reset_changed
&& whole
&& buf == curbuf
#ifdef FEAT_QUICKFIX
&& !bt_nofile(buf)
#endif
&& !filtering
&& (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)
&& vim_strchr(p_cpo, CPO_FNAMEW) != NULL)
{
if (set_rw_fname(fname, sfname) == FAIL)
return FAIL;
buf = curbuf; /* just in case autocmds made "buf" invalid */
}
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
*/
ffname = fname; /* remember full fname */
#ifdef UNIX
fname = sfname;
#endif
if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0)
overwriting = TRUE;
else
overwriting = FALSE;
if (exiting)
settmode(TMODE_COOK); /* when exiting allow typahead now */
++no_wait_return; /* don't wait for return yet */
/*
* Set '[ and '] marks to the lines to be written.
*/
buf->b_op_start.lnum = start;
buf->b_op_start.col = 0;
buf->b_op_end.lnum = end;
buf->b_op_end.col = 0;
#ifdef FEAT_AUTOCMD
{
aco_save_T aco;
int buf_ffname = FALSE;
int buf_sfname = FALSE;
int buf_fname_f = FALSE;
int buf_fname_s = FALSE;
int did_cmd = FALSE;
int nofile_err = FALSE;
int empty_memline = (buf->b_ml.ml_mfp == NULL);
/*
* Apply PRE aucocommands.
* Set curbuf to the buffer to be written.
* Careful: The autocommands may call buf_write() recursively!
*/
if (ffname == buf->b_ffname)
buf_ffname = TRUE;
if (sfname == buf->b_sfname)
buf_sfname = TRUE;
if (fname == buf->b_ffname)
buf_fname_f = TRUE;
if (fname == buf->b_sfname)
buf_fname_s = TRUE;
/* set curwin/curbuf to buf and save a few things */
aucmd_prepbuf(&aco, buf);
if (append)
{
if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD,
sfname, sfname, FALSE, curbuf, eap)))
{
#ifdef FEAT_QUICKFIX
if (overwriting && bt_nofile(curbuf))
nofile_err = TRUE;
else
#endif
apply_autocmds_exarg(EVENT_FILEAPPENDPRE,
sfname, sfname, FALSE, curbuf, eap);
}
}
else if (filtering)
{
apply_autocmds_exarg(EVENT_FILTERWRITEPRE,
NULL, sfname, FALSE, curbuf, eap);
}
else if (reset_changed && whole)
{
int was_changed = curbufIsChanged();
did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD,
sfname, sfname, FALSE, curbuf, eap);
if (did_cmd)
{
if (was_changed && !curbufIsChanged())
{
/* Written everything correctly and BufWriteCmd has reset
* 'modified': Correct the undo information so that an
* undo now sets 'modified'. */
u_unchanged(curbuf);
u_update_save_nr(curbuf);
}
}
else
{
#ifdef FEAT_QUICKFIX
if (overwriting && bt_nofile(curbuf))
nofile_err = TRUE;
else
#endif
apply_autocmds_exarg(EVENT_BUFWRITEPRE,
sfname, sfname, FALSE, curbuf, eap);
}
}
else
{
if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD,
sfname, sfname, FALSE, curbuf, eap)))
{
#ifdef FEAT_QUICKFIX
if (overwriting && bt_nofile(curbuf))
nofile_err = TRUE;
else
#endif
apply_autocmds_exarg(EVENT_FILEWRITEPRE,
sfname, sfname, FALSE, curbuf, eap);
}
}
/* restore curwin/curbuf and a few other things */
aucmd_restbuf(&aco);
/*
* In three situations we return here and don't write the file:
* 1. the autocommands deleted or unloaded the buffer.
* 2. The autocommands abort script processing.
* 3. If one of the "Cmd" autocommands was executed.
*/
if (!buf_valid(buf))
buf = NULL;
if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline)
|| did_cmd || nofile_err
#ifdef FEAT_EVAL
|| aborting()
#endif
)
{
--no_wait_return;
msg_scroll = msg_save;
if (nofile_err)
EMSG(_("E676: No matching autocommands for acwrite buffer"));
if (nofile_err
#ifdef FEAT_EVAL
|| aborting()
#endif
)
/* An aborting error, interrupt or exception in the
* autocommands. */
return FAIL;
if (did_cmd)
{
if (buf == NULL)
/* The buffer was deleted. We assume it was written
* (can't retry anyway). */
return OK;
if (overwriting)
{
/* Assume the buffer was written, update the timestamp. */
ml_timestamp(buf);
if (append)
buf->b_flags &= ~BF_NEW;
else
buf->b_flags &= ~BF_WRITE_MASK;
}
if (reset_changed && buf->b_changed && !append
&& (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL))
/* Buffer still changed, the autocommands didn't work
* properly. */
return FAIL;
return OK;
}
#ifdef FEAT_EVAL
if (!aborting())
#endif
EMSG(_("E203: Autocommands deleted or unloaded buffer to be written"));
return FAIL;
}
/*
* The autocommands may have changed the number of lines in the file.
* When writing the whole file, adjust the end.
* When writing part of the file, assume that the autocommands only
* changed the number of lines that are to be written (tricky!).
*/
if (buf->b_ml.ml_line_count != old_line_count)
{
if (whole) /* write all */
end = buf->b_ml.ml_line_count;
else if (buf->b_ml.ml_line_count > old_line_count) /* more lines */
end += buf->b_ml.ml_line_count - old_line_count;
else /* less lines */
{
end -= old_line_count - buf->b_ml.ml_line_count;
if (end < start)
{
--no_wait_return;
msg_scroll = msg_save;
EMSG(_("E204: Autocommand changed number of lines in unexpected way"));
return FAIL;
}
}
}
/*
* The autocommands may have changed the name of the buffer, which may
* be kept in fname, ffname and sfname.
*/
if (buf_ffname)
ffname = buf->b_ffname;
if (buf_sfname)
sfname = buf->b_sfname;
if (buf_fname_f)
fname = buf->b_ffname;
if (buf_fname_s)
fname = buf->b_sfname;
}
#endif
#ifdef FEAT_NETBEANS_INTG
if (netbeans_active() && isNetbeansBuffer(buf))
{
if (whole)
{
/*
* b_changed can be 0 after an undo, but we still need to write
* the buffer to NetBeans.
*/
if (buf->b_changed || isNetbeansModified(buf))
{
--no_wait_return; /* may wait for return now */
msg_scroll = msg_save;
netbeans_save_buffer(buf); /* no error checking... */
return retval;
}
else
{
errnum = (char_u *)"E656: ";
errmsg = (char_u *)_("NetBeans disallows writes of unmodified buffers");
buffer = NULL;
goto fail;
}
}
else
{
errnum = (char_u *)"E657: ";
errmsg = (char_u *)_("Partial writes disallowed for NetBeans buffers");
buffer = NULL;
goto fail;
}
}
#endif
if (shortmess(SHM_OVER) && !exiting)
msg_scroll = FALSE; /* overwrite previous file message */
else
msg_scroll = TRUE; /* don't overwrite previous file message */
if (!filtering)
filemess(buf,
#ifndef UNIX
sfname,
#else
fname,
#endif
(char_u *)"", 0); /* show that we are busy */
msg_scroll = FALSE; /* always overwrite the file message now */
buffer = alloc(BUFSIZE);
if (buffer == NULL) /* can't allocate big buffer, use small
* one (to be able to write when out of
* memory) */
{
buffer = smallbuf;
bufsize = SMBUFSIZE;
}
else
bufsize = BUFSIZE;
/*
* Get information about original file (if there is one).
*/
#if defined(UNIX) && !defined(ARCHIE)
st_old.st_dev = 0;
st_old.st_ino = 0;
perm = -1;
if (mch_stat((char *)fname, &st_old) < 0)
newfile = TRUE;
else
{
perm = st_old.st_mode;
if (!S_ISREG(st_old.st_mode)) /* not a file */
{
if (S_ISDIR(st_old.st_mode))
{
errnum = (char_u *)"E502: ";
errmsg = (char_u *)_("is a directory");
goto fail;
}
if (mch_nodetype(fname) != NODE_WRITABLE)
{
errnum = (char_u *)"E503: ";
errmsg = (char_u *)_("is not a file or writable device");
goto fail;
}
/* It's a device of some kind (or a fifo) which we can write to
* but for which we can't make a backup. */
device = TRUE;
newfile = TRUE;
perm = -1;
}
}
#else /* !UNIX */
/*
* Check for a writable device name.
*/
c = mch_nodetype(fname);
if (c == NODE_OTHER)
{
errnum = (char_u *)"E503: ";
errmsg = (char_u *)_("is not a file or writable device");
goto fail;
}
if (c == NODE_WRITABLE)
{
# if defined(MSDOS) || defined(MSWIN) || defined(OS2)
/* MS-Windows allows opening a device, but we will probably get stuck
* trying to write to it. */
if (!p_odev)
{
errnum = (char_u *)"E796: ";
errmsg = (char_u *)_("writing to device disabled with 'opendevice' option");
goto fail;
}
# endif
device = TRUE;
newfile = TRUE;
perm = -1;
}
else
{
perm = mch_getperm(fname);
if (perm < 0)
newfile = TRUE;
else if (mch_isdir(fname))
{
errnum = (char_u *)"E502: ";
errmsg = (char_u *)_("is a directory");
goto fail;
}
if (overwriting)
(void)mch_stat((char *)fname, &st_old);
}
#endif /* !UNIX */
if (!device && !newfile)
{
/*
* Check if the file is really writable (when renaming the file to
* make a backup we won't discover it later).
*/
file_readonly = check_file_readonly(fname, (int)perm);
if (!forceit && file_readonly)
{
if (vim_strchr(p_cpo, CPO_FWRITE) != NULL)
{
errnum = (char_u *)"E504: ";
errmsg = (char_u *)_(err_readonly);
}
else
{
errnum = (char_u *)"E505: ";
errmsg = (char_u *)_("is read-only (add ! to override)");
}
goto fail;
}
/*
* Check if the timestamp hasn't changed since reading the file.
*/
if (overwriting)
{
retval = check_mtime(buf, &st_old);
if (retval == FAIL)
goto fail;
}
}
#ifdef HAVE_ACL
/*
* For systems that support ACL: get the ACL from the original file.
*/
if (!newfile)
acl = mch_get_acl(fname);
#endif
/*
* If 'backupskip' is not empty, don't make a backup for some files.
*/
dobackup = (p_wb || p_bk || *p_pm != NUL);
#ifdef FEAT_WILDIGN
if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname))
dobackup = FALSE;
#endif
/*
* Save the value of got_int and reset it. We don't want a previous
* interruption cancel writing, only hitting CTRL-C while writing should
* abort it.
*/
prev_got_int = got_int;
got_int = FALSE;
/* Mark the buffer as 'being saved' to prevent changed buffer warnings */
buf->b_saving = TRUE;
/*
* If we are not appending or filtering, the file exists, and the
* 'writebackup', 'backup' or 'patchmode' option is set, need a backup.
* When 'patchmode' is set also make a backup when appending.
*
* Do not make any backup, if 'writebackup' and 'backup' are both switched
* off. This helps when editing large files on almost-full disks.
*/
if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup)
{
#if defined(UNIX) || defined(WIN32)
struct stat st;
#endif
if ((bkc_flags & BKC_YES) || append) /* "yes" */
backup_copy = TRUE;
#if defined(UNIX) || defined(WIN32)
else if ((bkc_flags & BKC_AUTO)) /* "auto" */
{
int i;
# ifdef UNIX
/*
* Don't rename the file when:
* - it's a hard link
* - it's a symbolic link
* - we don't have write permission in the directory
* - we can't set the owner/group of the new file
*/
if (st_old.st_nlink > 1
|| mch_lstat((char *)fname, &st) < 0
|| st.st_dev != st_old.st_dev
|| st.st_ino != st_old.st_ino
# ifndef HAVE_FCHOWN
|| st.st_uid != st_old.st_uid
|| st.st_gid != st_old.st_gid
# endif
)
backup_copy = TRUE;
else
# else
# ifdef WIN32
/* On NTFS file systems hard links are possible. */
if (mch_is_linked(fname))
backup_copy = TRUE;
else
# endif
# endif
{
/*
* Check if we can create a file and set the owner/group to
* the ones from the original file.
* First find a file name that doesn't exist yet (use some
* arbitrary numbers).
*/
STRCPY(IObuff, fname);
for (i = 4913; ; i += 123)
{
sprintf((char *)gettail(IObuff), "%d", i);
if (mch_lstat((char *)IObuff, &st) < 0)
break;
}
fd = mch_open((char *)IObuff,
O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
if (fd < 0) /* can't write in directory */
backup_copy = TRUE;
else
{
# ifdef UNIX
# ifdef HAVE_FCHOWN
ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
# endif
if (mch_stat((char *)IObuff, &st) < 0
|| st.st_uid != st_old.st_uid
|| st.st_gid != st_old.st_gid
|| (long)st.st_mode != perm)
backup_copy = TRUE;
# endif
/* Close the file before removing it, on MS-Windows we
* can't delete an open file. */
close(fd);
mch_remove(IObuff);
# ifdef MSWIN
/* MS-Windows may trigger a virus scanner to open the
* file, we can't delete it then. Keep trying for half a
* second. */
{
int try;
for (try = 0; try < 10; ++try)
{
if (mch_lstat((char *)IObuff, &st) < 0)
break;
ui_delay(50L, TRUE); /* wait 50 msec */
mch_remove(IObuff);
}
}
# endif
}
}
}
# ifdef UNIX
/*
* Break symlinks and/or hardlinks if we've been asked to.
*/
if ((bkc_flags & BKC_BREAKSYMLINK) || (bkc_flags & BKC_BREAKHARDLINK))
{
int lstat_res;
lstat_res = mch_lstat((char *)fname, &st);
/* Symlinks. */
if ((bkc_flags & BKC_BREAKSYMLINK)
&& lstat_res == 0
&& st.st_ino != st_old.st_ino)
backup_copy = FALSE;
/* Hardlinks. */
if ((bkc_flags & BKC_BREAKHARDLINK)
&& st_old.st_nlink > 1
&& (lstat_res != 0 || st.st_ino == st_old.st_ino))
backup_copy = FALSE;
}
#endif
#endif
/* make sure we have a valid backup extension to use */
if (*p_bex == NUL)
backup_ext = (char_u *)".bak";
else
backup_ext = p_bex;
if (backup_copy
&& (fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
{
int bfd;
char_u *copybuf, *wp;
int some_error = FALSE;
struct stat st_new;
char_u *dirp;
char_u *rootname;
#if defined(UNIX) && !defined(SHORT_FNAME)
int did_set_shortname;
#endif
copybuf = alloc(BUFSIZE + 1);
if (copybuf == NULL)
{
some_error = TRUE; /* out of memory */
goto nobackup;
}
/*
* Try to make the backup in each directory in the 'bdir' option.
*
* Unix semantics has it, that we may have a writable file,
* that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
* - the directory is not writable,
* - the file may be a symbolic link,
* - the file may belong to another user/group, etc.
*
* For these reasons, the existing writable file must be truncated
* and reused. Creation of a backup COPY will be attempted.
*/
dirp = p_bdir;
while (*dirp)
{
#ifdef UNIX
st_new.st_ino = 0;
st_new.st_dev = 0;
st_new.st_gid = 0;
#endif
/*
* Isolate one directory name, using an entry in 'bdir'.
*/
(void)copy_option_part(&dirp, copybuf, BUFSIZE, ",");
rootname = get_file_in_dir(fname, copybuf);
if (rootname == NULL)
{
some_error = TRUE; /* out of memory */
goto nobackup;
}
#if defined(UNIX) && !defined(SHORT_FNAME)
did_set_shortname = FALSE;
#endif
/*
* May try twice if 'shortname' not set.
*/
for (;;)
{
/*
* Make backup file name.
*/
backup = buf_modname(
#ifdef SHORT_FNAME
TRUE,
#else
(buf->b_p_sn || buf->b_shortname),
#endif
rootname, backup_ext, FALSE);
if (backup == NULL)
{
vim_free(rootname);
some_error = TRUE; /* out of memory */
goto nobackup;
}
/*
* Check if backup file already exists.
*/
if (mch_stat((char *)backup, &st_new) >= 0)
{
#ifdef UNIX
/*
* Check if backup file is same as original file.
* May happen when modname() gave the same file back.
* E.g. silly link, or file name-length reached.
* If we don't check here, we either ruin the file
* when copying or erase it after writing. jw.
*/
if (st_new.st_dev == st_old.st_dev
&& st_new.st_ino == st_old.st_ino)
{
vim_free(backup);
backup = NULL; /* no backup file to delete */
# ifndef SHORT_FNAME
/*
* may try again with 'shortname' set
*/
if (!(buf->b_shortname || buf->b_p_sn))
{
buf->b_shortname = TRUE;
did_set_shortname = TRUE;
continue;
}
/* setting shortname didn't help */
if (did_set_shortname)
buf->b_shortname = FALSE;
# endif
break;
}
#endif
/*
* If we are not going to keep the backup file, don't
* delete an existing one, try to use another name.
* Change one character, just before the extension.
*/
if (!p_bk)
{
wp = backup + STRLEN(backup) - 1
- STRLEN(backup_ext);
if (wp < backup) /* empty file name ??? */
wp = backup;
*wp = 'z';
while (*wp > 'a'
&& mch_stat((char *)backup, &st_new) >= 0)
--*wp;
/* They all exist??? Must be something wrong. */
if (*wp == 'a')
{
vim_free(backup);
backup = NULL;
}
}
}
break;
}
vim_free(rootname);
/*
* Try to create the backup file
*/
if (backup != NULL)
{
/* remove old backup, if present */
mch_remove(backup);
/* Open with O_EXCL to avoid the file being created while
* we were sleeping (symlink hacker attack?) */
bfd = mch_open((char *)backup,
O_WRONLY|O_CREAT|O_EXTRA|O_EXCL|O_NOFOLLOW,
perm & 0777);
if (bfd < 0)
{
vim_free(backup);
backup = NULL;
}
else
{
/* set file protection same as original file, but
* strip s-bit */
(void)mch_setperm(backup, perm & 0777);
#ifdef UNIX
/*
* Try to set the group of the backup same as the
* original file. If this fails, set the protection
* bits for the group same as the protection bits for
* others.
*/
if (st_new.st_gid != st_old.st_gid
# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
&& fchown(bfd, (uid_t)-1, st_old.st_gid) != 0
# endif
)
mch_setperm(backup,
(perm & 0707) | ((perm & 07) << 3));
# ifdef HAVE_SELINUX
mch_copy_sec(fname, backup);
# endif
#endif
/*
* copy the file.
*/
write_info.bw_fd = bfd;
write_info.bw_buf = copybuf;
#ifdef HAS_BW_FLAGS
write_info.bw_flags = FIO_NOCONVERT;
#endif
while ((write_info.bw_len = read_eintr(fd, copybuf,
BUFSIZE)) > 0)
{
if (buf_write_bytes(&write_info) == FAIL)
{
errmsg = (char_u *)_("E506: Can't write to backup file (add ! to override)");
break;
}
ui_breakcheck();
if (got_int)
{
errmsg = (char_u *)_(e_interr);
break;
}
}
if (close(bfd) < 0 && errmsg == NULL)
errmsg = (char_u *)_("E507: Close error for backup file (add ! to override)");
if (write_info.bw_len < 0)
errmsg = (char_u *)_("E508: Can't read file for backup (add ! to override)");
#ifdef UNIX
set_file_time(backup, st_old.st_atime, st_old.st_mtime);
#endif
#ifdef HAVE_ACL
mch_set_acl(backup, acl);
#endif
#ifdef HAVE_SELINUX
mch_copy_sec(fname, backup);
#endif
break;
}
}
}
nobackup:
close(fd); /* ignore errors for closing read file */
vim_free(copybuf);
if (backup == NULL && errmsg == NULL)
errmsg = (char_u *)_("E509: Cannot create backup file (add ! to override)");
/* ignore errors when forceit is TRUE */
if ((some_error || errmsg != NULL) && !forceit)
{
retval = FAIL;
goto fail;
}
errmsg = NULL;
}
else
{
char_u *dirp;
char_u *p;
char_u *rootname;
/*
* Make a backup by renaming the original file.
*/
/*
* If 'cpoptions' includes the "W" flag, we don't want to
* overwrite a read-only file. But rename may be possible
* anyway, thus we need an extra check here.
*/
if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL)
{
errnum = (char_u *)"E504: ";
errmsg = (char_u *)_(err_readonly);
goto fail;
}
/*
*
* Form the backup file name - change path/fo.o.h to
* path/fo.o.h.bak Try all directories in 'backupdir', first one
* that works is used.
*/
dirp = p_bdir;
while (*dirp)
{
/*
* Isolate one directory name and make the backup file name.
*/
(void)copy_option_part(&dirp, IObuff, IOSIZE, ",");
rootname = get_file_in_dir(fname, IObuff);
if (rootname == NULL)
backup = NULL;
else
{
backup = buf_modname(
#ifdef SHORT_FNAME
TRUE,
#else
(buf->b_p_sn || buf->b_shortname),
#endif
rootname, backup_ext, FALSE);
vim_free(rootname);
}
if (backup != NULL)
{
/*
* If we are not going to keep the backup file, don't
* delete an existing one, try to use another name.
* Change one character, just before the extension.
*/
if (!p_bk && mch_getperm(backup) >= 0)
{
p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
if (p < backup) /* empty file name ??? */
p = backup;
*p = 'z';
while (*p > 'a' && mch_getperm(backup) >= 0)
--*p;
/* They all exist??? Must be something wrong! */
if (*p == 'a')
{
vim_free(backup);
backup = NULL;
}
}
}
if (backup != NULL)
{
/*
* Delete any existing backup and move the current version
* to the backup. For safety, we don't remove the backup
* until the write has finished successfully. And if the
* 'backup' option is set, leave it around.
*/
/*
* If the renaming of the original file to the backup file
* works, quit here.
*/
if (vim_rename(fname, backup) == 0)
break;
vim_free(backup); /* don't do the rename below */
backup = NULL;
}
}
if (backup == NULL && !forceit)
{
errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)");
goto fail;
}
}
}
#if defined(UNIX) && !defined(ARCHIE)
/* When using ":w!" and the file was read-only: make it writable */
if (forceit && perm >= 0 && !(perm & 0200) && st_old.st_uid == getuid()
&& vim_strchr(p_cpo, CPO_FWRITE) == NULL)
{
perm |= 0200;
(void)mch_setperm(fname, perm);
made_writable = TRUE;
}
#endif
/* When using ":w!" and writing to the current file, 'readonly' makes no
* sense, reset it, unless 'Z' appears in 'cpoptions'. */
if (forceit && overwriting && vim_strchr(p_cpo, CPO_KEEPRO) == NULL)
{
buf->b_p_ro = FALSE;
#ifdef FEAT_TITLE
need_maketitle = TRUE; /* set window title later */
#endif
#ifdef FEAT_WINDOWS
status_redraw_all(); /* redraw status lines later */
#endif
}
if (end > buf->b_ml.ml_line_count)
end = buf->b_ml.ml_line_count;
if (buf->b_ml.ml_flags & ML_EMPTY)
start = end + 1;
/*
* If the original file is being overwritten, there is a small chance that
* we crash in the middle of writing. Therefore the file is preserved now.
* This makes all block numbers positive so that recovery does not need
* the original file.
* Don't do this if there is a backup file and we are exiting.
*/
if (reset_changed && !newfile && overwriting
&& !(exiting && backup != NULL))
{
ml_preserve(buf, FALSE);
if (got_int)
{
errmsg = (char_u *)_(e_interr);
goto restore_backup;
}
}
#ifdef MACOS_CLASSIC /* TODO: Is it need for MACOS_X? (Dany) */
/*
* Before risking to lose the original file verify if there's