Function: insert-file-contents

insert-file-contents is a function defined in fileio.c.

Signature

(insert-file-contents FILENAME &optional VISIT BEG END REPLACE)

Documentation

Insert contents of file FILENAME after point.

Returns list of absolute file name and number of characters inserted. If second argument VISIT is non-nil, the buffer's visited filename and last save file modtime are set, and it is marked unmodified. Signal an error if FILENAME cannot be read or is a directory. If visiting and the file does not exist, visiting is completed before the error is signaled.

The optional third and fourth arguments BEG and END specify what portion of the file to insert. These arguments count bytes in the file, not characters in the buffer. If VISIT is non-nil, BEG and END must be nil.

When inserting data from a special file (e.g., /dev/urandom), you can't specify VISIT or BEG, and END should be specified to avoid inserting unlimited data into the buffer from some special files which otherwise could supply infinite amounts of data.

If optional fifth argument REPLACE is non-nil and FILENAME names a regular file, replace the current buffer contents (in the accessible portion) with the file's contents. This is better than simply deleting and inserting the whole thing because (1) it preserves some marker positions (in unchanged portions at the start and end of the buffer) and (2) it puts less data in the undo list. When REPLACE is non-nil, the second element of the return value is the number of characters that replace the previous buffer contents.

If FILENAME is not a regular file and REPLACE is if-regular, erase the accessible portion of the buffer and insert the new contents. Any other non-nil value of REPLACE will signal an error if FILENAME is not a regular file.

This function does code conversion according to the value of coding-system-for-read or file-coding-system-alist, and sets the variable last-coding-system-used to the coding system actually used.

In addition, this function decodes the inserted text from known formats by calling format-decode, which see.

Other relevant functions are documented in the file group.

View in manual

Probably introduced at or before Emacs version 1.6.

Shortdoc

;; file
(insert-file-contents "/tmp/foo")
    e.g. => ("/tmp/foo" 6)

Source Code

// Defined in /usr/src/emacs/src/fileio.c
// Skipping highlighting due to helpful-max-highlight.
{
  /* A good read blocksize for insert-file-contents.
     It is for reading a big chunk of a file into memory,
     as opposed to coreutils IO_BUFSIZE which is for 'cat'-like stream reads.
     If too small, insert-file-contents has more syscall overhead.
     If too large, insert-file-contents might take too long respond to a quit.
     1 MiB should be reasonable even for older, slower devices circa 2025.  */
  enum { INSERT_READ_SIZE_MAX = min (1024 * 1024, SYS_BUFSIZE_MAX) };

  struct timespec mtime;
  emacs_fd fd;
  ptrdiff_t inserted = 0;
  int unprocessed;
  specpdl_ref count = SPECPDL_INDEX ();
  Lisp_Object handler, val, insval, orig_filename, old_undo;
  Lisp_Object p;
  off_t total = 0;
  bool regular;
  int save_errno = 0;
  char read_buf[min (+MAX_ALLOCA, +INSERT_READ_SIZE_MAX)];
  struct coding_system coding;
  bool replace_handled = false;
  bool set_coding_system = false;
  Lisp_Object coding_system;
  /* errno if read error, 0 if OK so far, negative if quit.  */
  int read_quit = 0;
  /* If the undo log only contains the insertion, there's no point
     keeping it.  It's typically when we first fill a file-buffer.  */
  bool empty_undo_list_p
    = (!NILP (visit) && NILP (BVAR (current_buffer, undo_list))
       && BEG == Z);
  Lisp_Object old_Vdeactivate_mark = Vdeactivate_mark;
  bool we_locked_file = false;
  Lisp_Object window_markers = Qnil;
  /* same_at_start and same_at_end count bytes, because file access counts
     bytes and BEG and END count bytes.  */
  ptrdiff_t same_at_start = BEGV_BYTE;
  ptrdiff_t same_at_end = ZV_BYTE;
  /* SAME_AT_END_CHARPOS counts characters, because
     restore_window_points needs the old character count.  */
  ptrdiff_t same_at_end_charpos = ZV;

  /* The size reported by fstat, or -1 if the file was not found or its
     size is meaningless.  */
  off_t st_size = -1;

  if (current_buffer->base_buffer && ! NILP (visit))
    error ("Cannot do file visiting in an indirect buffer");

  if (!NILP (BVAR (current_buffer, read_only)))
    Fbarf_if_buffer_read_only (Qnil);

  val = Qnil;
  p = Qnil;
  old_undo = Qnil;

  CHECK_STRING (filename);
  filename = Fexpand_file_name (filename, Qnil);
  orig_filename = filename;

  /* The value Qnil means that the coding system is not yet
     decided.  */
  coding_system = Qnil;

  /* If the file name has special constructs in it,
     call the corresponding file name handler.  */
  handler = Ffind_file_name_handler (filename, Qinsert_file_contents);
  if (!NILP (handler))
    {
      val = calln (handler, Qinsert_file_contents, filename,
		   visit, beg, end, replace);
      if (CONSP (val) && CONSP (XCDR (val))
	  && RANGED_FIXNUMP (0, XCAR (XCDR (val)), ZV - PT))
	inserted = XFIXNUM (XCAR (XCDR (val)));
      goto handled;
    }

  if (!NILP (visit))
    {
      if (!NILP (beg) || !NILP (end))
	error ("Attempt to visit less than an entire file");
      if (BEG < Z && NILP (replace))
	error ("Cannot do file visiting in a non-empty buffer");
    }

  off_t beg_offset = !NILP (beg) ? file_offset (beg) : 0;
  off_t end_offset = !NILP (end) ? file_offset (end) : TYPE_MAXIMUM (off_t);

  /* END_OFFSET should never be less than BEG_OFFSET,
     so that code can assume that END_OFFSET - BEG_OFFSET is nonnegative.
     So set END_OFFSET = max (END_OFFSET, BEG_OFFSET)
     whenever setting either variable,
     unless it is already known that BEG_OFFSET <= END_OFFSET.  */
  end_offset = max (end_offset, beg_offset);

  filename = ENCODE_FILE (filename);

  fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0);
  if (!emacs_fd_valid_p (fd))
    {
      save_errno = errno;
      if (NILP (visit))
	report_file_error ("Opening input file", orig_filename);
      mtime = time_error_value (save_errno);
      if (!NILP (Vcoding_system_for_read))
	{
	  /* Don't let invalid values into buffer-file-coding-system.  */
	  CHECK_CODING_SYSTEM (Vcoding_system_for_read);
	  Fset (Qbuffer_file_coding_system, Vcoding_system_for_read);
	}
      eassert (inserted == 0);
      goto notfound;
    }

  specpdl_ref fd_index = SPECPDL_INDEX ();
  record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd);

  /* Replacement should preserve point as it preserves markers.  */
  if (!NILP (replace))
    {
      window_markers = get_window_points_and_markers ();
      record_unwind_protect (restore_point_unwind,
			     XCAR (XCAR (window_markers)));
    }

  {
    struct stat st;
    if (emacs_fd_fstat (fd, &st) < 0)
      report_file_error ("Input file status", orig_filename);

    /* Normally there is no need for an S_ISDIR test here,
       as the first 'read' syscall will fail with EISDIR.
       However, for backwards compatibility to traditional Unix,
       POSIX allows 'read' to succeed on directories.
       So do an explicit S_ISDIR test now, so that callers can rely on
       this function rejecting directories on all platforms.  */
    if (S_ISDIR (st.st_mode))
      report_file_errno ("Read error", orig_filename, EISDIR);

    regular = S_ISREG (st.st_mode) != 0;
    bool memory_object = S_TYPEISSHM (&st) || S_TYPEISTMO (&st);

    if (regular | memory_object)
      {
	st_size = st.st_size;

	/* On some ancient platforms, a regular file with a negative size means
	   the actual file size is greater than the maximum off_t value.
	   Fail now rather than doing a lot of work and exhausting memory.  */
	if (st_size < 0)
	  buffer_overflow ();
      }

    mtime = (memory_object
	     ? make_timespec (0, UNKNOWN_MODTIME_NSECS)
	     : get_stat_mtime (&st));
  }

  /* The REPLACE code will need to be changed in order to work on
     named pipes, and it's probably just not worth it.  So we should
     at least signal an error.  */

  if (!regular)
    {
      if (!NILP (visit) || !NILP (replace))
	{
	  if (!NILP (visit) || !EQ (replace, Qif_regular))
	    xsignal2 (Qfile_error,
		      build_string ("not a regular file"), orig_filename);
	  else
	    /* Set REPLACE to Qunbound, indicating that we are trying
	       to replace the buffer contents with that of a
	       non-regular file.  */
	    replace = Qunbound;
	}

      /* Forbid specifying BEG unless the file is regular, as per
	 the doc string.  */

      if (!NILP (beg))
	xsignal2 (Qfile_error,
		  build_string ("can use a start position"
				" only in a regular file"),
		  orig_filename);
    }

  /* Check now whether the buffer will become too large,
     in the likely case where the file's length is not changing.
     This saves a lot of needless work before a buffer overflow.
     If LIKELY_END is nonnegative, it is likely where we will stop reading.
     We could read more (or less), if the file grows (or shrinks).  */
  off_t likely_end = min (end_offset, st_size);
  if (beg_offset < likely_end)
    {
      ptrdiff_t buf_bytes
	= Z_BYTE - (!NILP (replace) ? ZV_BYTE - BEGV_BYTE  : 0);
      ptrdiff_t buf_growth_max = BUF_BYTES_MAX - buf_bytes;
      off_t likely_growth = likely_end - beg_offset;
      if (buf_growth_max < likely_growth)
	buffer_overflow ();
    }

  /* Prevent redisplay optimizations.  */
  current_buffer->clip_changed = true;

  /* The current input position if known, -1 otherwise.  */
  off_t curpos = -1;

  if (EQ (Vcoding_system_for_read, Qauto_save_coding))
    {
      coding_system = coding_inherit_eol_type (Qutf_8_emacs, Qunix);
      setup_coding_system (coding_system, &coding);
      /* Ensure we set Vlast_coding_system_used.  */
      set_coding_system = true;
    }
  else if (BEG < Z)
    {
      /* Decide the coding system to use for reading the file now
         because we can't use an optimized method for handling
         `coding:' tag if the current buffer is not empty.  */
      if (!NILP (Vcoding_system_for_read))
	coding_system = Vcoding_system_for_read;
      else
	{
	  /* Don't try looking inside a file for a coding system
	     specification if it is not a regular file.  */
	  if (regular && !NILP (Vset_auto_coding_function))
	    {
	      if (!NILP (beg))
		curpos = xlseek (fd, beg_offset, SEEK_SET, orig_filename);
	      else
		{
		  beg_offset = curpos = xlseek (fd, 0, SEEK_CUR, orig_filename);
		  end_offset = max (end_offset, beg_offset);
		}

	      /* Find a coding system specified in the heading two
		 lines or in the tailing several lines of the file.
		 Assume that the 1 KiB and 3 KiB for heading
		 and tailing respectively are sufficient for this
		 purpose.  Because the file may be in /proc,
		 do not use st_size or report any SEEK_END failure.  */
	      static_assert (4 * 1024 < sizeof read_buf);
	      ptrdiff_t
		trial = min (end_offset - beg_offset, 4 * 1024),
		nread = trial ? emacs_full_read (fd, read_buf, trial) : 0;
	      curpos += nread;

	      if (nread == 4 * 1024 && curpos < end_offset)
		{
		  curpos = emacs_fd_lseek (fd, - 3 * 1024, SEEK_END);
		  if (curpos < 0)
		    curpos = beg_offset + nread;
		  else
		    {
		      off_t tailbeg = (curpos <= beg_offset + nread
				       ? beg_offset + nread
				       : min (curpos, end_offset - 3 * 1024));
		      if (tailbeg != curpos)
			curpos = xlseek (fd, tailbeg, SEEK_SET, orig_filename);
		    }

		  /* When appending the last 3 KiB, read extra bytes
		     without trusting SEEK_END, as the file may be growing.
		     Although this may yield more than 4 KiB of data total,
		     and the trailing data may not be from file end if
		     the file is growing, it is good enough.  */
		  ptrdiff_t trial = min (end_offset - curpos,
					 sizeof read_buf - 1024);
		  nread = emacs_full_read (fd, read_buf + 1024, trial);
		  if (0 <= nread)
		    {
		      curpos += nread;
		      nread += 1024;
		    }
		}

	      if (nread < 0)
		report_file_error ("Read error", orig_filename);
	      else if (nread > 0)
		{
		  AUTO_STRING (name, " *code-converting-work*");
		  struct buffer *prev = current_buffer;
		  Lisp_Object workbuf;
		  struct buffer *buf;

		  record_unwind_current_buffer ();

		  workbuf = Fget_buffer_create (name, Qt);
		  buf = XBUFFER (workbuf);

		  delete_all_overlays (buf);
		  bset_directory (buf, BVAR (current_buffer, directory));
		  bset_read_only (buf, Qnil);
		  bset_filename (buf, Qnil);
		  bset_undo_list (buf, Qt);
		  eassert (buf->overlays == NULL);

		  set_buffer_internal (buf);
		  Ferase_buffer ();
		  bset_enable_multibyte_characters (buf, Qnil);

		  insert_1_both ((char *) read_buf, nread, nread, 0, 0, 0);
		  TEMP_SET_PT_BOTH (BEG, BEG_BYTE);
		  coding_system = calln (Vset_auto_coding_function,
					 filename, make_fixnum (nread));
		  set_buffer_internal (prev);

		  /* Discard the unwind protect for recovering the
                     current buffer.  */
		  specpdl_ptr--;
		}
	    }

	  if (NILP (coding_system))
	    {
	      /* If we have not yet decided a coding system, check
                 file-coding-system-alist.  */
	      coding_system = CALLN (Ffind_operation_coding_system,
				     Qinsert_file_contents, orig_filename,
				     visit, beg, end, replace);
	      if (CONSP (coding_system))
		coding_system = XCAR (coding_system);
	    }
	}

      if (NILP (coding_system))
	coding_system = Qundecided;
      else
	CHECK_CODING_SYSTEM (coding_system);

      if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
	/* We must suppress all character code conversion except for
	   end-of-line conversion.  */
	coding_system = raw_text_coding_system (coding_system);

      setup_coding_system (coding_system, &coding);
      /* Ensure we set Vlast_coding_system_used.  */
      set_coding_system = true;
    }

  /* If requested, replace the accessible part of the buffer
     with the file contents.  Avoid replacing text at the
     beginning or end of the buffer that matches the file contents;
     that preserves markers pointing to the unchanged parts.

     Here we implement this feature in an optimized way
     for the case where code conversion is NOT needed.
     The following if-statement handles the case of conversion
     in a less optimal way.

     If the code conversion is "automatic" then we try using this
     method and hope for the best.
     But if we discover the need for conversion, we give up on this method
     and let the following if-statement handle the replace job.  */
  if ((!NILP (replace)
       && !BASE_EQ (replace, Qunbound))
      && BEGV < ZV
      && (NILP (coding_system)
	  || ! CODING_REQUIRE_DECODING (&coding)))
    {
      /* There is still a possibility we will find the need to do code
	 conversion.  If that happens, set this variable to
	 give up on handling REPLACE in the optimized way.  */
      bool giveup_match_end = false;

      if (beg_offset != curpos)
	{
	  if (!NILP (beg) || 0 <= curpos)
	    curpos = xlseek (fd, beg_offset, SEEK_SET, orig_filename);
	  else
	    {
	      beg_offset = curpos = xlseek (fd, 0, SEEK_CUR, orig_filename);
	      end_offset = max (end_offset, beg_offset);
	    }
	}

      /* Count how many chars at the start of the file
	 match the text at the beginning of the buffer.  */
      while (true)
	{
	  ptrdiff_t trial = min (end_offset - curpos, sizeof read_buf);
	  ptrdiff_t nread = trial ? emacs_fd_read (fd, read_buf, trial) : 0;
	  if (nread < 0)
	    report_file_error ("Read error", orig_filename);
	  else if (nread == 0)
	    {
	      /* Data inserted from the file match the buffer's leading bytes,
		 so there's no need to replace anything.  */
	      emacs_fd_close (fd);
	      clear_unwind_protect (fd_index);

	      /* Truncate the buffer to the size of the file.  */
	      del_range_byte (same_at_start, same_at_end);
	      goto handled;
	    }

	  curpos += nread;

	  if (CODING_REQUIRE_DETECTION (&coding))
	    {
	      coding_system = detect_coding_system ((unsigned char *) read_buf,
						    nread, nread, 1, 0,
						    coding_system);
	      setup_coding_system (coding_system, &coding);
	    }

	  if (CODING_REQUIRE_DECODING (&coding))
	    /* We found that the file should be decoded somehow.
               Let's give up here.  */
	    {
	      giveup_match_end = true;
	      break;
	    }

	  ptrdiff_t bufpos = 0;
	  ptrdiff_t bufposlim = min (nread, same_at_end - same_at_start);
	  while (bufpos < bufposlim
		 && FETCH_BYTE (same_at_start) == read_buf[bufpos])
	    same_at_start++, bufpos++;
	  /* If we found a discrepancy, stop the scan.  */
	  if (bufpos != nread)
	    break;
	}

      /* Find the end position, which is end_offset if given,
	 the file's end otherwise.  */

      off_t endpos;
      if (!giveup_match_end)
	{
	  endpos = end_offset;
	  if (endpos == TYPE_MAXIMUM (off_t))
	    {
	      endpos = emacs_fd_lseek (fd, 0, SEEK_END);
	      if (endpos < 0)
		endpos = curpos;

	      /* Check that read reports EOF soon, to catch platforms
		 where SEEK_END fails or reports too-small offsets.  */
	      ptrdiff_t n = emacs_full_read (fd, read_buf, sizeof read_buf);
	      if (n < 0)
		report_file_error ("Read error", orig_filename);
	      curpos = endpos += n;

	      /* Give up if the file extends past the test read.  */
	      giveup_match_end = n == sizeof read_buf;

	      if (!giveup_match_end)
		{
		  /* Shrink the file's head if the file shrank to
		     be smaller than its head.  */
		  off_t offset_from_beg = endpos - beg_offset;
		  if (offset_from_beg < same_at_start - BEGV_BYTE)
		    {
		      /* Give up if the file shrank to less than BEG.  */
		      giveup_match_end = offset_from_beg < 0;

		      if (!giveup_match_end)
			same_at_start = offset_from_beg + BEGV_BYTE;
		    }
		}
	    }
	}

      /* Count how many bytes in the file's end match the buffer's end.
	 However, don't waste time if decoding is necessary.  */

      while (!giveup_match_end)
	{
	  ptrdiff_t nread, bufpos, trial;

	  /* How much can we scan in the next step?  Compare with poslim
	     to prevent overlap of the matching head with the matching tail.
	     The 'same_at_start_pos' limit prevents overlap in the buffer's
	     head and tail, and the 'file_overlap_pos' limit prevents
	     overlap in the inserted file's head and tail.  */
	  off_t same_at_start_pos = beg_offset + (same_at_start - BEGV_BYTE);
	  off_t file_overlap_pos = endpos - (same_at_end - same_at_start);
	  off_t poslim = max (same_at_start_pos, file_overlap_pos);
	  /* Do not scan more than sizeof read_buf at a time, and stop
	     the scan if it can go no more.  */
	  trial = min (curpos - poslim, sizeof read_buf);
	  if (trial == 0)
	    break;

	  curpos = xlseek (fd, -trial, SEEK_CUR, orig_filename);

	  nread = emacs_full_read (fd, read_buf, trial);
	  curpos += nread;
	  if (nread < trial)
	    {
	      if (nread < 0)
		report_file_error ("Read error", orig_filename);
	      /* The file unexpectedly shrank.  */
	      giveup_match_end = true;
	      break;
	    }

	  /* Scan this bufferful from the end, comparing with
	     the Emacs buffer.  */
	  bufpos = nread;

	  while (bufpos > 0
		 && FETCH_BYTE (same_at_end - 1) == read_buf[bufpos - 1])
	    same_at_end--, bufpos--;

	  /* If we found a discrepancy, stop the scan.
	     Otherwise loop around and scan the preceding bufferful.  */
	  if (bufpos != 0)
	    {
	      /* If this discrepancy is because of code conversion,
		 we cannot use this method; giveup and try the other.  */
	      if (same_at_end > same_at_start
		  && FETCH_BYTE (same_at_end - 1) >= 0200
		  && ! NILP (BVAR (current_buffer, enable_multibyte_characters))
		  && (CODING_MAY_REQUIRE_DECODING (&coding)))
		giveup_match_end = true;
	      break;
	    }
	}

      if (! giveup_match_end)
	{
	  ptrdiff_t temp;
          specpdl_ref this_count = SPECPDL_INDEX ();

	  /* We win!  We can handle REPLACE the optimized way.  */

	  /* Extend the start of non-matching text area to multibyte
             character boundary.  */
	  if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
	    while (same_at_start > BEGV_BYTE
		   && ! CHAR_HEAD_P (FETCH_BYTE (same_at_start)))
	      same_at_start--;

	  /* Extend the end of non-matching text area to multibyte
             character boundary.  */
	  if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
	    while (same_at_end < ZV_BYTE
		   && ! CHAR_HEAD_P (FETCH_BYTE (same_at_end)))
	      same_at_end++;

	  same_at_end_charpos = BYTE_TO_CHAR (same_at_end);

	  /* Arrange to read only the nonmatching middle part of the file.  */
	  beg_offset += same_at_start - BEGV_BYTE;
	  end_offset = endpos - (ZV_BYTE - same_at_end);
	  eassert (beg_offset <= end_offset);

          if (!NILP (visit) && BEG == BEGV && Z == ZV)
            /* This binding is to avoid ask-user-about-supersession-threat
	       being called in insert_from_buffer or del_range_bytes (via
	       prepare_to_modify_buffer).
	       Such a prompt makes no sense if we're VISITing the file,
	       since the insertion makes the buffer *more* like the file
	       rather than the reverse.
               AFAICT we could avoid ask-user-about-supersession-threat by
               setting current_buffer->modtime earlier, but we could still
               end up calling ask-user-about-supersession-threat if the file
               is modified while we read it, so we bind buffer-file-name
               instead.  */
            specbind (Qbuffer_file_name, Qnil);
	  del_range_byte (same_at_start, same_at_end);
	  /* Insert from the file at the proper position.  */
	  temp = BYTE_TO_CHAR (same_at_start);
	  SET_PT_BOTH (temp, same_at_start);
          unbind_to (this_count, Qnil);

	  /* If display currently starts at beginning of line,
	     keep it that way.  */
	  if (XBUFFER (XWINDOW (selected_window)->contents) == current_buffer)
	    XWINDOW (selected_window)->start_at_line_beg = !NILP (Fbolp ());

	  replace_handled = true;
	}
    }

  /* If requested, replace the accessible part of the buffer
     with the file contents.  Avoid replacing text at the
     beginning or end of the buffer that matches the file contents;
     that preserves markers pointing to the unchanged parts.

     Here we implement this feature for the case where code conversion
     is needed, in a simple way that needs a lot of memory.
     The preceding if-statement handles the case of no conversion
     in a more optimized way.  */
  if ((!NILP (replace)
       && !BASE_EQ (replace, Qunbound))
      && ! replace_handled && BEGV < ZV)
    {
      ptrdiff_t same_at_start_charpos;
      ptrdiff_t inserted_chars;
      ptrdiff_t bufpos;
      unsigned char *decoded;
      ptrdiff_t temp;
      ptrdiff_t this;
      specpdl_ref this_count = SPECPDL_INDEX ();
      bool multibyte
	= ! NILP (BVAR (current_buffer, enable_multibyte_characters));
      Lisp_Object conversion_buffer;

      conversion_buffer = code_conversion_save (1, multibyte);

      /* First read the whole file, performing code conversion into
	 CONVERSION_BUFFER.  */

      if (beg_offset != curpos)
	{
	  if (!NILP (beg) || 0 <= curpos)
	    curpos = xlseek (fd, beg_offset, SEEK_SET, orig_filename);
	  else
	    {
	      beg_offset = curpos = xlseek (fd, 0, SEEK_CUR, orig_filename);
	      end_offset = max (end_offset, beg_offset);
	    }
	}

      inserted = 0;		/* Bytes put into CONVERSION_BUFFER so far.  */
      unprocessed = 0;		/* Bytes not processed in previous loop.  */

      while (true)
	{
	  /* Read one buffer a time, to allow
	     quitting while reading a huge file.  */

	  ptrdiff_t trial = min (end_offset - curpos,
				 sizeof read_buf - unprocessed);
	  this = trial ? emacs_fd_read (fd, read_buf + unprocessed, trial) : 0;
	  if (this < 0)
	    report_file_error ("Read error", orig_filename);
	  if (this == 0)
	    break;
	  curpos += this;

	  BUF_TEMP_SET_PT (XBUFFER (conversion_buffer),
			   BUF_Z (XBUFFER (conversion_buffer)));
	  decode_coding_c_string (&coding, (unsigned char *) read_buf,
				  unprocessed + this, conversion_buffer);
	  unprocessed = coding.carryover_bytes;
	  if (coding.carryover_bytes > 0)
	    memcpy (read_buf, coding.carryover, unprocessed);
	}

      emacs_fd_close (fd);
      clear_unwind_protect (fd_index);

      if (unprocessed > 0)
	{
	  BUF_TEMP_SET_PT (XBUFFER (conversion_buffer),
			   BUF_Z (XBUFFER (conversion_buffer)));
	  coding.mode |= CODING_MODE_LAST_BLOCK;
	  decode_coding_c_string (&coding, (unsigned char *) read_buf,
				  unprocessed, conversion_buffer);
	  coding.mode &= ~CODING_MODE_LAST_BLOCK;
	}

      coding_system = CODING_ID_NAME (coding.id);
      set_coding_system = true;
      maybe_move_gap (XBUFFER (conversion_buffer));
      decoded = BUF_BEG_ADDR (XBUFFER (conversion_buffer));
      inserted = (BUF_Z_BYTE (XBUFFER (conversion_buffer))
		  - BUF_BEG_BYTE (XBUFFER (conversion_buffer)));

      /* Compare the beginning of the converted string with the buffer
	 text.  */

      bufpos = 0;
      ptrdiff_t bufposlim = min (inserted, same_at_end - same_at_start);
      while (bufpos < bufposlim
	     && FETCH_BYTE (same_at_start) == decoded[bufpos])
	same_at_start++, bufpos++;

      /* If the file matches the head of buffer completely,
	 there's no need to replace anything.  */

      if (bufpos == inserted)
	{
	  /* Truncate the buffer to the size of the file.  */
	  if (same_at_start != same_at_end)
	    {
              if (!NILP (visit) && BEG == BEGV && Z == ZV)
		/* See previous specbind for the reason behind this.  */
		specbind (Qbuffer_file_name, Qnil);
	      del_range_byte (same_at_start, same_at_end);
	    }
	  inserted = 0;

	  unbind_to (this_count, Qnil);
	  goto handled;
	}

      /* Extend the start of non-matching text area to the previous
	 multibyte character boundary.  */
      if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
	while (same_at_start > BEGV_BYTE
	       && ! CHAR_HEAD_P (FETCH_BYTE (same_at_start)))
	  same_at_start--;

      /* Scan this bufferful from the end, comparing with the Emacs
	 buffer.  Compare with bufposlim to prevent overlap of the
	 matching head with the matching tail.  The 'same_at_start -
	 BEGV_BYTE' limit prevents overlap in the buffer's head and
	 tail, and the 'inserted - (same_at_end - same_at_start)' limit
	 prevents overlap in the inserted file's head and tail.  */
      bufposlim = max (same_at_start - BEGV_BYTE,
		       inserted - (same_at_end - same_at_start));
      bufpos = inserted;
      while (bufposlim < bufpos
	     && FETCH_BYTE (same_at_end - 1) == decoded[bufpos - 1])
	same_at_end--, bufpos--;

      /* Extend the end of non-matching text area to the next
	 multibyte character boundary.  */
      if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
	while (same_at_end < ZV_BYTE
	       && ! CHAR_HEAD_P (FETCH_BYTE (same_at_end)))
	  same_at_end++;

      same_at_end_charpos = BYTE_TO_CHAR (same_at_end);

      /* If display currently starts at beginning of line,
	 keep it that way.  */
      if (XBUFFER (XWINDOW (selected_window)->contents) == current_buffer)
	XWINDOW (selected_window)->start_at_line_beg = !NILP (Fbolp ());

      /* Replace the chars that we need to replace,
	 and update INSERTED to equal the number of bytes
	 we are taking from the decoded string.  */
      inserted -= (ZV_BYTE - same_at_end) + (same_at_start - BEGV_BYTE);

      if (!NILP (visit) && BEG == BEGV && Z == ZV)
        /* See previous specbind for the reason behind this.  */
        specbind (Qbuffer_file_name, Qnil);
      if (same_at_end != same_at_start)
	{
	  del_range_byte (same_at_start, same_at_end);
	  temp = GPT;
	  eassert (same_at_start == GPT_BYTE);
	  same_at_start = GPT_BYTE;
	}
      else
	{
	  temp = same_at_end_charpos;
	}
      /* Insert from the file at the proper position.  */
      SET_PT_BOTH (temp, same_at_start);
      same_at_start_charpos
	= buf_bytepos_to_charpos (XBUFFER (conversion_buffer),
				  same_at_start - BEGV_BYTE
				  + BUF_BEG_BYTE (XBUFFER (conversion_buffer)));
      eassert (same_at_start_charpos == temp - (BEGV - BEG));
      inserted_chars
	= (buf_bytepos_to_charpos (XBUFFER (conversion_buffer),
				   same_at_start + inserted - BEGV_BYTE
				  + BUF_BEG_BYTE (XBUFFER (conversion_buffer)))
	   - same_at_start_charpos);
      insert_from_buffer (XBUFFER (conversion_buffer),
			  same_at_start_charpos, inserted_chars, 0);
      /* Set `inserted' to the number of inserted characters.  */
      inserted = PT - temp;
      /* Set point before the inserted characters.  */
      SET_PT_BOTH (temp, same_at_start);

      unbind_to (this_count, Qnil);

      goto handled;
    }

  if (beg_offset != curpos)
    {
      if (!NILP (beg) || 0 <= curpos)
	xlseek (fd, beg_offset, SEEK_SET, orig_filename);
      else if (end_offset != TYPE_MAXIMUM (off_t))
	{
	  curpos = emacs_fd_lseek (fd, 0, SEEK_CUR);
	  beg_offset = max (0, curpos);
	  end_offset = max (end_offset, beg_offset);
	}
    }
  /* curpos effectively goes out of scope now, as it is no longer needed,
     so do not bother to update curpos from now on.  */

  total = end_offset - beg_offset;

  if (NILP (visit) && total > 0)
    {
      if (!NILP (BVAR (current_buffer, file_truename))
	  /* Make binding buffer-file-name to nil effective.  */
	  && !NILP (BVAR (current_buffer, filename))
	  && SAVE_MODIFF >= MODIFF)
	we_locked_file = true;
      prepare_to_modify_buffer (PT, PT, NULL);
    }

  /* If REPLACE is Qunbound, buffer contents are being replaced with
     text read from a FIFO or a device.  Erase the entire accessible
     portion of the buffer.  */

  if (BASE_EQ (replace, Qunbound))
    del_range (BEGV, ZV);

  move_gap_both (PT, PT_BYTE);

  /* Ensure the gap is at least one byte larger than needed for the
     estimated insertion, so that in the usual case we read
     without reallocating.  */
  off_t inserted_estimate = likely_end - beg_offset;
  if (GAP_SIZE <= inserted_estimate)
    {
      ptrdiff_t growth;
      if (ckd_sub (&growth, inserted_estimate, GAP_SIZE - 1))
	buffer_overflow ();
      make_gap (growth);
    }

  /* Total bytes inserted.  */
  inserted = 0;

  /* Here, we don't do code conversion in the loop.  It is done by
     decode_coding_gap after all data are read into the buffer.  */
  {
    ptrdiff_t gap_size = GAP_SIZE;

    while (inserted < total)
      {
	char *buf;
	ptrdiff_t bufsize, this;
	if (gap_size < total - inserted && gap_size < sizeof read_buf)
	  {
	    buf = read_buf;
	    bufsize = sizeof read_buf;
	  }
	else
	  {
	    buf = (char *) BEG_ADDR + PT_BYTE - BEG_BYTE + inserted;
	    bufsize = min (gap_size, INSERT_READ_SIZE_MAX);
	  }
	bufsize = min (bufsize, total - inserted);

	if (!regular)
	  {
	    Lisp_Object nbytes;
	    intmax_t number;

	    /* Read from the file, capturing `quit'.  When an
	       error occurs, end the loop, and arrange for a quit
	       to be signaled after decoding the text we read.
	       This way, we do not lose any data read.  */
	    union read_non_regular data
	      = {{fd, buf == read_buf ? buf : NULL, inserted, bufsize}};
	    nbytes = internal_condition_case_1
	      (read_non_regular, make_pointer_integer (&data),
	       Qerror, read_non_regular_quit);

	    if (NILP (nbytes))
	      {
		read_quit = -1;
		break;
	      }

	    integer_to_intmax (nbytes, &number);
	    this = number;
	  }
	else
	  /* Allow quitting out of the actual I/O.  We don't make text
	     part of the buffer until all the reading is done, so a
	     C-g here doesn't do any harm: though the data read are discarded,
	     the original data is still in the input file.  */
	  {
	    this = emacs_fd_read (fd, buf, bufsize);
	    if (this < 0)
	      this = -errno;
	  }

	if (this <= 0)
	  {
	    read_quit = -this;
	    break;
	  }

	if (buf == read_buf)
	  {
	    if (gap_size < this)
	      {
		/* Size estimate was low.  Make the gap at least 50% larger,
		   and big enough so that the next loop iteration will
		   use the gap directly instead of copying via read_buf.  */
		make_gap (max (GAP_SIZE >> 1,
			       this - gap_size + sizeof read_buf));
		gap_size = GAP_SIZE - inserted;
	      }
	    memcpy (BEG_ADDR + PT_BYTE - BEG_BYTE + inserted,
		    read_buf, this);
	  }
	gap_size -= this;
	inserted += this;
      }
  }

  /* Now we have either read all the file data into the gap,
     or stop reading on I/O error or quit.  If nothing was
     read, undo marking the buffer modified.  */

  if (inserted == 0)
    {
      if (we_locked_file)
	Funlock_file (BVAR (current_buffer, file_truename));
      Vdeactivate_mark = old_Vdeactivate_mark;
    }
  else
    Fset (Qdeactivate_mark, Qt);

  emacs_fd_close (fd);
  clear_unwind_protect (fd_index);

  if (0 < read_quit)
    report_file_errno ("Read error", orig_filename, read_quit);

 notfound:

  if (NILP (coding_system))
    {
      /* The coding system is not yet decided.  Decide it by an
	 optimized method for handling `coding:' tag.

	 Note that we can get here only if the buffer was empty
	 before the insertion.  */
      eassert (Z == BEG);

      if (!NILP (Vcoding_system_for_read))
	coding_system = Vcoding_system_for_read;
      else
	{
	  /* Since we are sure that the current buffer was empty
	     before the insertion, we can toggle
	     enable-multibyte-characters directly here without taking
	     care of marker adjustment.  By this way, we can run Lisp
	     program safely before decoding the inserted text.  */
          Lisp_Object multibyte
            = BVAR (current_buffer, enable_multibyte_characters);
          Lisp_Object unwind_data
            = Fcons (multibyte,
                     Fcons (BVAR (current_buffer, undo_list),
			    Fcurrent_buffer ()));
	  specpdl_ref count1 = SPECPDL_INDEX ();

	  bset_enable_multibyte_characters (current_buffer, Qnil);
	  bset_undo_list (current_buffer, Qt);
	  record_unwind_protect (decide_coding_unwind, unwind_data);

          /* Make the text read part of the buffer.  */
          insert_from_gap_1 (inserted, inserted, false);

	  if (inserted > 0 && ! NILP (Vset_auto_coding_function))
	    {
	      coding_system = calln (Vset_auto_coding_function,
				     filename, make_fixnum (inserted));
	    }

	  if (NILP (coding_system))
	    {
	      /* If the coding system is not yet decided, check
		 file-coding-system-alist.  */
	      coding_system = CALLN (Ffind_operation_coding_system,
				     Qinsert_file_contents, orig_filename,
				     visit, beg, end, Qnil);
	      if (CONSP (coding_system))
		coding_system = XCAR (coding_system);
	    }
          /* Move the text back to the gap.  */
	  unbind_to (count1, Qnil);
          inserted = XFIXNUM (XCAR (unwind_data));
        }

      if (NILP (coding_system))
	coding_system = Qundecided;
      else
	CHECK_CODING_SYSTEM (coding_system);

      if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
	/* We must suppress all character code conversion except for
	   end-of-line conversion.  */
	coding_system = raw_text_coding_system (coding_system);
      setup_coding_system (coding_system, &coding);
      /* Ensure we set Vlast_coding_system_used.  */
      set_coding_system = true;
    }

  if (!NILP (visit))
    {
      /* When we visit a file by raw-text, we change the buffer to
	 unibyte.  */
      if (CODING_FOR_UNIBYTE (&coding)
	  /* Can't do this if part of the buffer might be preserved.  */
	  && NILP (replace))
	{
	  /* Visiting a file with these coding system makes the buffer
	     unibyte.  */
	  if (inserted > 0)
	    bset_enable_multibyte_characters (current_buffer, Qnil);
	  else
	    Fset_buffer_multibyte (Qnil);
	}
    }

  eassert (PT == GPT);

  coding.dst_multibyte
    = !NILP (BVAR (current_buffer, enable_multibyte_characters));
  if (CODING_MAY_REQUIRE_DECODING (&coding)
      && (inserted > 0 || CODING_REQUIRE_FLUSHING (&coding)))
    {
      /* Now we have all the new bytes at the beginning of the gap,
         but `decode_coding_gap` can't have them at the beginning of the gap,
         so we need to move them.  */
      memmove (GAP_END_ADDR - inserted, GPT_ADDR, inserted);
      decode_coding_gap (&coding, inserted);
      inserted = coding.produced_char;
      coding_system = CODING_ID_NAME (coding.id);
    }
  else if (inserted > 0)
    {
      /* Make the text read part of the buffer.  */
      eassert (NILP (BVAR (current_buffer, enable_multibyte_characters)));
      insert_from_gap_1 (inserted, inserted, false);

      invalidate_buffer_caches (current_buffer, PT, PT + inserted);
      adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
			   inserted);
    }

  /* Call after-change hooks for the inserted text, aside from the case
     of normal visiting (not with REPLACE), which is done in a new buffer
     "before" the buffer is changed.  */
  if (inserted > 0 && total > 0
      && (NILP (visit) || !NILP (replace)))
    {
      signal_after_change (PT, 0, inserted);
      update_compositions (PT, PT, CHECK_BORDER);
    }

  /* Now INSERTED is measured in characters.  */

 handled:

  if (inserted > 0)
    restore_window_points (window_markers, inserted,
			   BYTE_TO_CHAR (same_at_start),
			   same_at_end_charpos);

  if (!NILP (visit))
    {
      if (empty_undo_list_p)
	bset_undo_list (current_buffer, Qnil);

      if (NILP (handler))
	{
	  current_buffer->modtime = mtime;
	  current_buffer->modtime_size = st_size;
	  bset_filename (current_buffer, orig_filename);
	}

      SAVE_MODIFF = MODIFF;
      BUF_AUTOSAVE_MODIFF (current_buffer) = MODIFF;
      XSETFASTINT (BVAR (current_buffer, save_length), Z - BEG);
      if (NILP (handler))
	{
	  if (!NILP (BVAR (current_buffer, file_truename)))
	    Funlock_file (BVAR (current_buffer, file_truename));
	  Funlock_file (filename);
	}
    }

  if (set_coding_system)
    Vlast_coding_system_used = coding_system;

  if (! NILP (Ffboundp (Qafter_insert_file_set_coding)))
    {
      insval = calln (Qafter_insert_file_set_coding, make_fixnum (inserted),
		      visit);
      if (! NILP (insval))
	{
	  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
	    wrong_type_argument (Qinserted_chars, insval);
	  inserted = XFIXNAT (insval);
	}
    }

  /* Decode file format.  Don't do this if Qformat_decode is not
     bound, which can happen when called early during loadup.  */

  if (inserted > 0 && !NILP (Ffboundp (Qformat_decode)))
    {
      /* Don't run point motion or modification hooks when decoding.  */
      specpdl_ref count1 = SPECPDL_INDEX ();
      ptrdiff_t old_inserted = inserted;
      specbind (Qinhibit_point_motion_hooks, Qt);
      specbind (Qinhibit_modification_hooks, Qt);

      /* Save old undo list and don't record undo for decoding.  */
      old_undo = BVAR (current_buffer, undo_list);
      bset_undo_list (current_buffer, Qt);

      if (NILP (replace))
	{
	  insval = calln (Qformat_decode,
			  Qnil, make_fixnum (inserted), visit);
	  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
	    wrong_type_argument (Qinserted_chars, insval);
	  inserted = XFIXNAT (insval);
	}
      else
	{
	  /* If REPLACE is non-nil and we succeeded in not replacing the
	     beginning or end of the buffer text with the file's contents,
	     call format-decode with `point' positioned at the beginning
	     of the buffer and `inserted' equaling the number of
	     characters in the buffer.  Otherwise, format-decode might
	     fail to correctly analyze the beginning or end of the buffer.
	     Hence we temporarily save `point' and `inserted' here and
	     restore `point' iff format-decode did not insert or delete
	     any text.  Otherwise we leave `point' at point-min.  */
	  ptrdiff_t opoint = PT;
	  ptrdiff_t opoint_byte = PT_BYTE;
	  ptrdiff_t oinserted = ZV - BEGV;
	  modiff_count ochars_modiff = CHARS_MODIFF;

	  TEMP_SET_PT_BOTH (BEGV, BEGV_BYTE);
	  insval = calln (Qformat_decode,
			  Qnil, make_fixnum (oinserted), visit);
	  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
	    wrong_type_argument (Qinserted_chars, insval);
	  if (ochars_modiff == CHARS_MODIFF)
	    /* format_decode didn't modify buffer's characters => move
	       point back to position before inserted text and leave
	       value of inserted alone.  */
	    SET_PT_BOTH (opoint, opoint_byte);
	  else
	    /* format_decode modified buffer's characters => consider
	       entire buffer changed and leave point at point-min.  */
	    inserted = XFIXNAT (insval);
	}

      /* For consistency with format-decode call these now iff inserted > 0
	 (martin 2007-06-28).  */
      p = Vafter_insert_file_functions;
      FOR_EACH_TAIL (p)
	{
	  if (NILP (replace))
	    {
	      insval = calln (XCAR (p), make_fixnum (inserted));
	      if (!NILP (insval))
		{
		  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
		    wrong_type_argument (Qinserted_chars, insval);
		  inserted = XFIXNAT (insval);
		}
	    }
	  else
	    {
	      /* For the rationale of this see the comment on
		 format-decode above.  */
	      ptrdiff_t opoint = PT;
	      ptrdiff_t opoint_byte = PT_BYTE;
	      ptrdiff_t oinserted = ZV - BEGV;
	      modiff_count ochars_modiff = CHARS_MODIFF;

	      TEMP_SET_PT_BOTH (BEGV, BEGV_BYTE);
	      insval = calln (XCAR (p), make_fixnum (oinserted));
	      if (!NILP (insval))
		{
		  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
		    wrong_type_argument (Qinserted_chars, insval);
		  if (ochars_modiff == CHARS_MODIFF)
		    /* after_insert_file_functions didn't modify
		       buffer's characters => move point back to
		       position before inserted text and leave value of
		       inserted alone.  */
		    SET_PT_BOTH (opoint, opoint_byte);
		  else
		    /* after_insert_file_functions did modify buffer's
                       characters => consider entire buffer changed and
                       leave point at point-min.  */
		    inserted = XFIXNAT (insval);
		}
	    }
	}

      if (!empty_undo_list_p)
	{
	  bset_undo_list (current_buffer, old_undo);
	  if (CONSP (old_undo) && inserted != old_inserted)
	    {
	      /* Adjust the last undo record for the size change during
		 the format conversion.  */
	      Lisp_Object tem = XCAR (old_undo);
	      if (CONSP (tem) && FIXNUMP (XCAR (tem))
		  && FIXNUMP (XCDR (tem))
		  && XFIXNUM (XCDR (tem)) == PT + old_inserted)
		XSETCDR (tem, make_fixnum (PT + inserted));
	    }
	}
      else
	/* If undo_list was Qt before, keep it that way.
	   Otherwise start with an empty undo_list.  */
	bset_undo_list (current_buffer, EQ (old_undo, Qt) ? Qt : Qnil);

      unbind_to (count1, Qnil);
    }

  if (save_errno != 0)
    {
      /* Signal an error if visiting a file that could not be opened.  */
      eassert (!NILP (visit) && NILP (handler));
      report_file_errno ("Opening input file", orig_filename, save_errno);
    }

  /* We made a lot of deletions and insertions above, so invalidate
     the newline cache for the entire region of the inserted
     characters.  */
  if (current_buffer->base_buffer && current_buffer->base_buffer->newline_cache)
    invalidate_region_cache (current_buffer->base_buffer,
                             current_buffer->base_buffer->newline_cache,
                             PT - BEG, Z - PT - inserted);
  else if (current_buffer->newline_cache)
    invalidate_region_cache (current_buffer,
                             current_buffer->newline_cache,
                             PT - BEG, Z - PT - inserted);

  if (read_quit)
    quit ();

  /* Retval needs to be dealt with in all cases consistently.  */
  if (NILP (val))
    val = list2 (orig_filename, make_fixnum (inserted));

  return unbind_to (count, val);
}