9519 lines · 233.6 KB · C
RawDownload
/* 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 */ #if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)# include "vimio.h"	/* for lseek(), must be before vim.h */#endif #if defined __EMX__# include "vimio.h"	/* for mktemp(), CJW 1997-12-03 */#endif #include "vim.h" #ifdef HAVE_FCNTL_H# include <fcntl.h>#endif #ifdef __TANDEM# 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# define CRYPT_MAGIC		"VimCrypt~01!"	/* "01" is the version nr */# define CRYPT_MAGIC_LEN	12		/* must be multiple of 4! */#endif /* Is there any system that doesn't have access()? */#define USE_MCH_ACCESS #ifdef FEAT_MBYTEstatic char_u *next_fenc __ARGS((char_u **pp));# ifdef FEAT_EVALstatic char_u *readfile_charconvert __ARGS((char_u *fname, char_u *fenc, int *fdp));# endif#endif#ifdef FEAT_VIMINFOstatic void check_marks_read __ARGS((void));#endif#ifdef FEAT_CRYPTstatic char_u *check_for_cryptkey __ARGS((char_u *cryptkey, char_u *ptr, long *sizep, long *filesizep, int newfile));#endif#ifdef UNIXstatic void set_file_time __ARGS((char_u *fname, time_t atime, time_t mtime));#endifstatic 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_AUTOCMDstatic 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	    /* errornouse 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;	/* lenght 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 */# 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_MBYTEstatic 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 same_encoding __ARGS((char_u *a, char_u *b));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 WIN3264static int get_win_fio_flags __ARGS((char_u *ptr));# endif# ifdef MACOS_Xstatic int get_mac_fio_flags __ARGS((char_u *ptr));# endif#endifstatic int move_lines __ARGS((buf_T *frombuf, buf_T *tobuf));      voidfilemess(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) * * return FAIL for failure, OK otherwise */    intreadfile(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		set_options = newfile || (eap != NULL && eap->read_edit);    int		check_readonly;    int		filtering = (flags & READ_FILTER);    int		read_stdin = (flags & READ_STDIN);    int		read_buffer = (flags & READ_BUFFER);    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;    long	filesize = 0;    int		skip_read = FALSE;#ifdef FEAT_CRYPT    char_u	*cryptkey = NULL;#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     write_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;    }     /* 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						)	{	    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.	 * For RISCOS, also remember the filetype.	 */	if (mch_stat((char *)fname, &st) >= 0)	{	    buf_store_time(curbuf, &st, fname);	    curbuf->b_mtime_read = curbuf->b_mtime; #if defined(RISCOS) && defined(FEAT_OSFILETYPE)	    /* Read the filetype into the buffer local filetype option. */	    mch_read_filetype(fname);#endif#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;

Lines 1–500 of 9519.