]> git.eshelyaron.com Git - emacs.git/commitdiff
insert-file-contents file end EOF fixes
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 12 Jul 2025 21:31:18 +0000 (14:31 -0700)
committerEshel Yaron <me@eshelyaron.com>
Thu, 24 Jul 2025 08:48:47 +0000 (10:48 +0200)
* src/fileio.c (Finsert_file_contents):
When counting bytes at file end, don’t trust lseek to seek
to the end of the file.  Instead, read a bit after lseeking,
to make sure we get to the end, give up if we don’t get to EOF.
This works around problems on /proc filesystems
where lseek pretends the file is empty.
Update file_size_hint when reaching EOF.

(cherry picked from commit aee9b598312e282700c4eedf02b174b908479ce7)

src/fileio.c

index 3c371dea98d55f5638ee283f85e04fd156c3b5e8..b7b4320d73e6fff5066651bad9b97aecbfea0ccf 100644 (file)
@@ -4488,13 +4488,39 @@ by calling `format-decode', which see.  */)
       /* 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.  */
+
+      off_t endpos;
+      if (!giveup_match_end)
+       {
+         endpos = end_offset;
+         if (endpos == TYPE_MAXIMUM (off_t))
+           {
+             endpos = emacs_fd_lseek (fd, 0, SEEK_END);
+             giveup_match_end = endpos < 0;
+             if (giveup_match_end)
+               seekable = false;
+             else
+               {
+                 /* Check that read reports EOF soon, to catch platforms
+                    where SEEK_END can report wildly small offsets.  */
+                 ptrdiff_t n = emacs_fd_read (fd, read_buf, sizeof read_buf);
+                 if (n < 0)
+                   report_file_error ("Read error", orig_filename);
+                 endpos += n;
+                 giveup_match_end = 0 < n;
+                 if (!giveup_match_end)
+                   file_size_hint = endpos;
+               }
+           }
+       }
+
       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);
+         curpos = endpos - (ZV_BYTE - same_at_end);
          /* If the entire file matches the buffer tail, stop the scan.  */
          if (curpos == 0)
            break;
@@ -4511,7 +4537,10 @@ by calling `format-decode', which see.  */)
              if (nread < 0)
                report_file_error ("Read error", orig_filename);
              else if (nread == 0)
-               break;
+               {
+                 file_size_hint = curpos - trial + total_read;
+                 break;
+               }
              total_read += nread;
            }
 
@@ -4567,15 +4596,14 @@ by calling `format-decode', which see.  */)
          /* Don't try to reuse the same piece of text twice.  */
          overlap = (same_at_start - BEGV_BYTE
                     - (same_at_end - ZV_BYTE
-                       + (end_offset < TYPE_MAXIMUM (off_t)
-                          ? end_offset : file_size_hint)));
+                       + endpos));
          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;
+         end_offset = endpos - (ZV_BYTE - same_at_end);
 
           if (!NILP (visit) && BEG == BEGV && Z == ZV)
             /* This binding is to avoid ask-user-about-supersession-threat