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

📄 fileio.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
    else
	backup_ext = p_bex;

    if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0)
	overwriting = TRUE;
    else
	overwriting = FALSE;

    /*
     * Disallow writing from .exrc and .vimrc in current directory for
     * security reasons.
     */
    if (check_secure())
	return FAIL;

    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 AUTOCMD
    /*
     * Apply PRE aucocommands.
     * Set curbuf to the buffer to be written.
     * Careful: The autocommands may call buf_write() recursively!
     */
    save_buf = curbuf;
    curbuf = buf;
    curwin->w_buffer = buf;
    if (append)
	apply_autocmds(EVENT_FILEAPPENDPRE, fname, fname, FALSE, curbuf);
    else if (filtering)
	apply_autocmds(EVENT_FILTERWRITEPRE, NULL, fname, FALSE, curbuf);
    else if (reset_changed && whole)
	apply_autocmds(EVENT_BUFWRITEPRE, fname, fname, FALSE, curbuf);
    else
	apply_autocmds(EVENT_FILEWRITEPRE, fname, fname, FALSE, curbuf);
    /*
     * If the autocommands deleted or unloaded the buffer, give an error
     * message.
     */
    if (!buf_valid(buf) || buf->b_ml.ml_mfp == NULL)
    {
	--no_wait_return;
	msg_scroll = msg_save;
	EMSG("Autocommands deleted or unloaded buffer to be written");
	return FAIL;
    }
    /*
     * If the autocommands didn't change the current buffer, go back to the
     * original current buffer, if it still exists.
     */
    if (curbuf == buf && buf_valid(save_buf))
    {
	curbuf = save_buf;
	curwin->w_buffer = save_buf;
    }

    /*
     * 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)					    /* writing 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("Autocommand changed number of lines in unexpected way");
		return FAIL;
	    }
	}
    }
#endif

    if (shortmess(SHM_OVER))
	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;

#if defined(UNIX) && !defined(ARCHIE)
	/* get information about original file (if there is one) */
    st_old.st_dev = st_old.st_ino = 0;
    perm = -1;
    if (stat((char *)fname, &st_old))
	newfile = TRUE;
    else
    {
	if (!S_ISREG(st_old.st_mode))		/* not a file */
	{
	    if (S_ISDIR(st_old.st_mode))
		errmsg = (char_u *)"is a directory";
	    else
		errmsg = (char_u *)"is not a file";
	    goto fail;
	}
	if (overwriting)
	{
	    retval = check_mtime(buf, &st_old);
	    if (retval == FAIL)
		goto fail;
	}
	perm = st_old.st_mode;
    }
/*
 * If we are not appending or filtering, the file exists, and the
 * 'writebackup', 'backup' or 'patchmode' option is set, try to make a backup
 * copy of the file.
 */
    if (!append && !filtering && perm >= 0 && (p_wb || p_bk || *p_pm != NUL)
	    && (fd = open((char *)fname, O_RDONLY | O_EXTRA
#ifndef macintosh
				   , 0	    /* mode (avoid UMR) */
#endif
		)) >= 0)
    {
	int		bfd, buflen;
	char_u		copybuf[BUFSIZE + 1], *wp;
	int		some_error = FALSE;
	struct stat	st_new;
	char_u		*dirp;
	char_u		*rootname;
#ifndef SHORT_FNAME
	int		did_set_shortname;
#endif

	/*
	 * 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)
	{
	    st_new.st_dev = st_new.st_ino = 0;
	    st_new.st_gid = 0;

	    /*
	     * 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;
	    }


#ifndef 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)
		{
		    some_error = TRUE;		/* out of memory */
		    vim_free(rootname);
		    goto nobackup;
		}

		/*
		 * Check if backup file already exists.
		 */
		if (!stat((char *)backup, &st_new))
		{
		    /*
		     * 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;	/* there is 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;
		    }

		    /*
		     * 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' && !stat((char *)backup, &st_new))
			    --*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);
		bfd = open((char *)backup, O_WRONLY | O_CREAT | O_EXTRA, 0666);
		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);

		    /*
		     * 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
#else
			  chown((char *)backup, (uid_t)-1, st_old.st_gid) != 0
#endif
					    )
			mch_setperm(backup, (perm & 0707) | ((perm & 07) << 3));

		    /* copy the file. */
		    while ((buflen = read(fd, (char *)copybuf, BUFSIZE)) > 0)
		    {
			if (write_buf(bfd, copybuf, buflen) == FAIL)
			{
			    errmsg = (char_u *)"Can't write to backup file (use ! to override)";
			    break;
			}
		    }
		    if (close(bfd) < 0 && errmsg == NULL)
			errmsg = (char_u *)"Close error for backup file (use ! to override)";
		    if (buflen < 0)
			errmsg = (char_u *)"Can't read file for backup (use ! to override)";
		    set_file_time(backup, st_old.st_atime, st_old.st_mtime);
		    break;
		}
	    }
	}
nobackup:
	close(fd);		/* ignore errors for closing read file */

	if (backup == NULL && errmsg == NULL)
	    errmsg = (char_u *)"Cannot create backup file (use ! to override)";
	/* ignore errors when forceit is TRUE */
	if ((some_error || errmsg) && !forceit)
	{
	    retval = FAIL;
	    goto fail;
	}
	errmsg = NULL;
    }
    /* When using ":w!" and the file was read-only: make it writable */
    if (forceit && st_old.st_uid == getuid() && perm >= 0 && !(perm & 0200)
				     && vim_strchr(p_cpo, CPO_FWRITE) == NULL)
    {
	perm |= 0200;
	(void)mch_setperm(fname, perm);
	made_writable = TRUE;
    }

#else /* end of UNIX, start of the rest */

/*
 * If we are not appending, the file exists, and the 'writebackup' or
 * 'backup' option is set, make a backup.
 * Do not make any backup, if "writebackup" and "backup" are
 * both switched off. This helps when editing large files on
 * almost-full disks. (jw)
 */
    perm = mch_getperm(fname);
    if (perm < 0)
	newfile = TRUE;
    else if (mch_isdir(fname))
    {
	errmsg = (char_u *)"is a directory";
	goto fail;
    }
    else if (overwriting)
    {
	struct stat	st;

	if (stat((char *)fname, &st) >= 0)
	{
	    retval = check_mtime(buf, &st);
	    if (retval == FAIL)
		goto fail;
	}
    }

    if (!append && !filtering && perm >= 0 && (p_wb || p_bk || *p_pm != NUL))
    {
	char_u		*dirp;
	char_u		*p;
	char_u		*rootname;

	/*
	 * 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.
		 */
#ifdef AMIGA
		/*
		 * With MSDOS-compatible filesystems (crossdos, messydos) it is
		 * possible that the name of the backup file is the same as the
		 * original file. To avoid the chance of accidently deleting the
		 * original file (horror!) we lock it during the remove.
		 * This should not happen with ":w", because startscript()
		 * should detect this problem and set buf->b_shortname,
		 * causing modname to return a correct ".bak" file name. This
		 * problem does exist with ":w file name", but then the
		 * original file will be somewhere else so the backup isn't
		 * really important. If autoscripting is off the rename may
		 * fail.
		 */
		flock = Lock((UBYTE *)fname, (long)ACCESS_READ);
#endif
		mch_remove(backup);
#ifdef AMIGA
		if (flock)
		    UnLock(flock);
#endif
		/*
		 * 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 *)"Can't make backup file (use ! to override)";
	    goto fail;
	}
    }
#endif /* UNIX */

    /* When using ":w!" and writing to the current file, readonly makes no
     * sense, reset it */
    if (forceit && overwriting)
	buf->b_p_ro = FALSE;

    /*
     * 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 && !otherfile(ffname) &&
					    !(exiting && backup != NULL))
	ml_preserve(buf, FALSE);

    /*
     * We may try to open the file twice: If we can't write to the
     * file and forceit is TRUE we delete the existing file and try to create

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -