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. 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.

If optional fifth argument REPLACE is non-nil, replace the current buffer contents (in the accessible portion) with the file 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 return value is the number of characters that replace previous buffer contents.

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.
{
  struct stat st;
  struct timespec mtime;
  int fd;
  ptrdiff_t inserted = 0;
  ptrdiff_t how_much;
  off_t beg_offset, end_offset;
  int unprocessed;
  specpdl_ref count = SPECPDL_INDEX ();
  Lisp_Object handler, val, insval, orig_filename, old_undo;
  Lisp_Object p;
  ptrdiff_t total = 0;
  bool regular = true;
  int save_errno = 0;
  char read_buf[READ_BUF_SIZE];
  struct coding_system coding;
  bool replace_handled = false;
  bool set_coding_system = false;
  Lisp_Object coding_system;
  bool read_quit = false;
  /* 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;
  bool seekable = true;

  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;
  orig_filename = Qnil;
  old_undo = Qnil;

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

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

  orig_filename = filename;
  filename = ENCODE_FILE (filename);

  fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
  if (fd < 0)
    {
      save_errno = errno;
      if (NILP (visit))
	report_file_error ("Opening input file", orig_filename);
      mtime = time_error_value (save_errno);
      st.st_size = -1;
      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_int (close_file_unwind, 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)));
    }

  if (fstat (fd, &st) != 0)
    report_file_error ("Input file status", orig_filename);
  mtime = get_stat_mtime (&st);

  /* This 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 (!S_ISREG (st.st_mode))
    {
      regular = false;
      seekable = lseek (fd, 0, SEEK_CUR) != (off_t) -1;

      if (! NILP (visit))
        {
          eassert (inserted == 0);
	  goto notfound;
        }

      if (!NILP (beg) && !seekable)
	xsignal2 (Qfile_error,
		  build_string ("cannot use a start position in a non-seekable file/device"),
		  orig_filename);

      if (!NILP (replace))
	xsignal2 (Qfile_error,
		  build_string ("not a regular file"), orig_filename);
    }

  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");
    }

  if (!NILP (beg))
    beg_offset = file_offset (beg);
  else
    beg_offset = 0;

  if (!NILP (end))
    end_offset = file_offset (end);
  else
    {
      if (!regular)
	end_offset = TYPE_MAXIMUM (off_t);
      else
	{
	  end_offset = st.st_size;

	  /* A negative size can happen on a platform that allows file
	     sizes greater than the maximum off_t value.  */
	  if (end_offset < 0)
	    buffer_overflow ();

	  /* The file size returned from fstat may be zero, but data
	     may be readable nonetheless, for example when this is a
	     file in the /proc filesystem.  */
	  if (end_offset == 0)
	    end_offset = READ_BUF_SIZE;
	}
    }

  /* 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 (regular)
    {
      /* The likely offset where we will stop reading.  We could read
	 more (or less), if the file grows (or shrinks) as we read it.  */
      off_t likely_end = min (end_offset, st.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;

  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 seekable.  */
	  if (regular && !NILP (Vset_auto_coding_function))
	    {
	      /* Find a coding system specified in the heading two
		 lines or in the tailing several lines of the file.
		 We assume that the 1K-byte and 3K-byte for heading
		 and tailing respectively are sufficient for this
		 purpose.  */
	      int nread;

	      if (st.st_size <= (1024 * 4))
		nread = emacs_read_quit (fd, read_buf, 1024 * 4);
	      else
		{
		  nread = emacs_read_quit (fd, read_buf, 1024);
		  if (nread == 1024)
		    {
		      int ntail;
		      if (lseek (fd, - (1024 * 3), SEEK_END) < 0)
			report_file_error ("Setting file position",
					   orig_filename);
		      ntail = emacs_read_quit (fd, read_buf + nread, 1024 * 3);
		      nread = ntail < 0 ? ntail : nread + ntail;
		    }
		}

	      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 = call2 (Vset_auto_coding_function,
					 filename, make_fixnum (nread));
		  set_buffer_internal (prev);

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

		  /* Rewind the file for the actual read done later.  */
		  if (lseek (fd, 0, SEEK_SET) < 0)
		    report_file_error ("Setting file position", orig_filename);
		}
	    }

	  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)
      && BEGV < ZV
      && (NILP (coding_system)
	  || ! CODING_REQUIRE_DECODING (&coding)))
    {
      ptrdiff_t overlap;
      /* 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 != 0)
	{
	  if (lseek (fd, beg_offset, SEEK_SET) < 0)
	    report_file_error ("Setting file position", orig_filename);
	}

      /* Count how many chars at the start of the file
	 match the text at the beginning of the buffer.  */
      while (true)
	{
	  int nread = emacs_read_quit (fd, read_buf, sizeof read_buf);
	  if (nread < 0)
	    report_file_error ("Read error", orig_filename);
	  else if (nread == 0)
	    break;

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

	  int bufpos = 0;
	  while (bufpos < nread && same_at_start < ZV_BYTE
		 && FETCH_BYTE (same_at_start) == read_buf[bufpos])
	    same_at_start++, bufpos++;
	  /* If we found a discrepancy, stop the scan.
	     Otherwise loop around and scan the next bufferful.  */
	  if (bufpos != nread)
	    break;
	}
      /* If the file matches the buffer completely,
	 there's no need to replace anything.  */
      if (same_at_start - BEGV_BYTE == end_offset - beg_offset)
	{
	  emacs_close (fd);
	  clear_unwind_protect (fd_index);

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

      /* Count how many chars at the end of the file
	 match the text at the end of the buffer.  But, if we have
	 already found that decoding is necessary, don't waste time.  */
      while (!giveup_match_end)
	{
	  int total_read, nread, bufpos, trial;
	  off_t curpos;

	  /* At what file position are we now scanning?  */
	  curpos = end_offset - (ZV_BYTE - same_at_end);
	  /* If the entire file matches the buffer tail, stop the scan.  */
	  if (curpos == 0)
	    break;
	  /* How much can we scan in the next step?  */
	  trial = min (curpos, sizeof read_buf);
	  if (lseek (fd, curpos - trial, SEEK_SET) < 0)
	    report_file_error ("Setting file position", orig_filename);

	  total_read = nread = 0;
	  while (total_read < trial)
	    {
	      nread = emacs_read_quit (fd, read_buf + total_read,
				       trial - total_read);
	      if (nread < 0)
		report_file_error ("Read error", orig_filename);
	      else if (nread == 0)
		break;
	      total_read += nread;
	    }

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

	  /* Compare with same_at_start to avoid counting some buffer text
	     as matching both at the file's beginning and at the end.  */
	  while (bufpos > 0 && same_at_end > same_at_start
		 && 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 (nread == 0)
	    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++;

	  /* Don't try to reuse the same piece of text twice.  */
	  overlap = (same_at_start - BEGV_BYTE
		     - (same_at_end
			+ (! NILP (end) ? end_offset : st.st_size) - ZV_BYTE));
	  if (overlap > 0)
	    same_at_end += overlap;
	  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 -= ZV_BYTE - same_at_end;

          /* 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).
             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 (intern ("buffer-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) && ! replace_handled && BEGV < ZV)
    {
      ptrdiff_t same_at_start_charpos;
      ptrdiff_t inserted_chars;
      ptrdiff_t overlap;
      ptrdiff_t bufpos;
      unsigned char *decoded;
      ptrdiff_t temp;
      ptrdiff_t this = 0;
      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 (lseek (fd, beg_offset, SEEK_SET) < 0)
	report_file_error ("Setting file position", orig_filename);

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

      while (true)
	{
	  /* Read at most READ_BUF_SIZE bytes at a time, to allow
	     quitting while reading a huge file.  */

	  this = emacs_read_quit (fd, read_buf + unprocessed,
				  READ_BUF_SIZE - unprocessed);
	  if (this <= 0)
	    break;

	  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);
	}

      if (this < 0)
	report_file_error ("Read error", orig_filename);
      emacs_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;
      while (bufpos < inserted && same_at_start < same_at_end
	     && 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)
	    {
              /* See previous specbind for the reason behind this.  */
              specbind (intern ("buffer-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.  */
      bufpos = inserted;

      /* Compare with same_at_start to avoid counting some buffer text
	 as matching both at the file's beginning and at the end.  */
      while (bufpos > 0 && same_at_end > same_at_start
	     && 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++;

      /* Don't try to reuse the same piece of text twice.  */
      overlap = same_at_start - BEGV_BYTE - (same_at_end + inserted - ZV_BYTE);
      if (overlap > 0)
	same_at_end += overlap;
      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);

      /* See previous specbind for the reason behind this.  */
      specbind (intern ("buffer-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 (seekable || !NILP (end))
    total = end_offset - beg_offset;
  else
    /* For a special file, all we can do is guess.  */
    total = READ_BUF_SIZE;

  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);
    }

  move_gap_both (PT, PT_BYTE);
  if (GAP_SIZE < total)
    make_gap (total - GAP_SIZE);

  if (beg_offset != 0 || !NILP (replace))
    {
      if (lseek (fd, beg_offset, SEEK_SET) < 0)
	report_file_error ("Setting file position", orig_filename);
    }

  /* In the following loop, HOW_MUCH contains the total bytes read so
     far for a regular file, and not changed for a special file.  But,
     before exiting the loop, it is set to a negative value if I/O
     error occurs.  */
  how_much = 0;

  /* 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 (how_much < total)
      {
	/* `try' is reserved in some compilers (Microsoft C).  */
	ptrdiff_t trytry = min (total - how_much, READ_BUF_SIZE);
	ptrdiff_t this;

	if (!seekable && NILP (end))
	  {
	    Lisp_Object nbytes;

	    /* Maybe make more room.  */
	    if (gap_size < trytry)
	      {
		make_gap (trytry - gap_size);
		gap_size = GAP_SIZE - inserted;
	      }

	    /* 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.  */
	    union read_non_regular data = {{fd, inserted, trytry}};
	    nbytes = internal_condition_case_1
	      (read_non_regular, make_pointer_integer (&data),
	       Qerror, read_non_regular_quit);

	    if (NILP (nbytes))
	      {
		read_quit = true;
		break;
	      }

	    this = XFIXNUM (nbytes);
	  }
	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.  */
	    this = emacs_read_quit (fd,
				    ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
				     + inserted),
				    trytry);
	  }

	if (this <= 0)
	  {
	    how_much = this;
	    break;
	  }

	gap_size -= this;

	/* For a regular file, where TOTAL is the real size,
	   count HOW_MUCH to compare with it.
	   For a special file, where TOTAL is just a buffer size,
	   so don't bother counting in HOW_MUCH.
	   (INSERTED is where we count the number of characters inserted.)  */
	if (seekable || !NILP (end))
	  how_much += 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_close (fd);
  clear_unwind_protect (fd_index);

  if (how_much < 0)
    report_file_error ("Read error", orig_filename);

 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 = call2 (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.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 (!regular)
	xsignal2 (Qfile_error,
		  build_string ("not a regular file"), orig_filename);
    }

  if (set_coding_system)
    Vlast_coding_system_used = coding_system;

  if (! NILP (Ffboundp (Qafter_insert_file_set_coding)))
    {
      insval = call2 (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.  */
  if (inserted > 0)
    {
      /* 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 = call3 (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 = call3 (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 = call1 (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 = call1 (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);
}