Subject: bug#23539: tail -f file name headings displayed too many times after suspend

From: Janne Snabb <[email-masked]>
Date: Sun, 15 May 2016 02:06:02 +0300

The attached patch fixes this bug for me.

The patch moves the write_header() call in the inotify case to
dump_remainder() where write_header() is called only if some file
content is going to be output.

I am not sure if this is the best approach. Another approach would be to
change the inotify event handling so that all queued events are read at
once and duplicates are eliminated before acting on them. Now it reads
and acts on them one.

-- 
Janne Snabb
[email-masked]
--- src/tail.c.dist	2016-01-19 18:09:56.000000000 +0200
+++ src/tail.c	2016-05-15 01:58:47.219808135 +0300
@@ -398,7 +398,7 @@
    Return the number of bytes read from the file.  */
 
 static uintmax_t
-dump_remainder (const char *pretty_filename, int fd, uintmax_t n_bytes)
+dump_remainder (bool want_header, const char *pretty_filename, int fd, uintmax_t n_bytes)
 {
   uintmax_t n_written;
   uintmax_t n_remaining = n_bytes;
@@ -418,6 +418,10 @@
         }
       if (bytes_read == 0)
         break;
+      if (want_header) {
+        write_header(pretty_filename);
+        want_header = false;
+      }
       xwrite_stdout (buffer, bytes_read);
       n_written += bytes_read;
       if (n_bytes != COPY_TO_EOF)
@@ -527,7 +531,7 @@
                  output the part that is after it.  */
               if (n != bytes_read - 1)
                 xwrite_stdout (nl + 1, bytes_read - (n + 1));
-              *read_pos += dump_remainder (pretty_filename, fd,
+              *read_pos += dump_remainder (false, pretty_filename, fd,
                                            end_pos - (pos + bytes_read));
               return true;
             }
@@ -539,7 +543,7 @@
           /* Not enough lines in the file; print everything from
              start_pos to the end.  */
           xlseek (fd, start_pos, SEEK_SET, pretty_filename);
-          *read_pos = start_pos + dump_remainder (pretty_filename, fd,
+          *read_pos = start_pos + dump_remainder (false, pretty_filename, fd,
                                                   end_pos);
           return true;
         }
@@ -1216,7 +1220,7 @@
                 }
             }
 
-          bytes_read = dump_remainder (name, fd,
+          bytes_read = dump_remainder (false, name, fd,
                                        (f[i].blocking
                                         ? COPY_A_BUFFER : COPY_TO_EOF));
           any_input |= (bytes_read != 0);
@@ -1365,14 +1369,15 @@
            && timespec_cmp (fspec->mtime, get_stat_mtime (&stats)) == 0)
     return;
 
+  bool want_header = false;
   if (fspec != *prev_fspec)
     {
       if (print_headers)
-        write_header (name);
+        want_header = true;
       *prev_fspec = fspec;
     }
 
-  uintmax_t bytes_read = dump_remainder (name, fspec->fd, COPY_TO_EOF);
+  uintmax_t bytes_read = dump_remainder (want_header, name, fspec->fd, COPY_TO_EOF);
   fspec->size += bytes_read;
 
   if (fflush (stdout) != 0)
@@ -1786,7 +1791,7 @@
       *read_pos = current_pos;
     }
 
-  *read_pos += dump_remainder (pretty_filename, fd, n_bytes);
+  *read_pos += dump_remainder (false, pretty_filename, fd, n_bytes);
   return true;
 }
 
@@ -1810,7 +1815,7 @@
       int t = start_lines (pretty_filename, fd, n_lines, read_pos);
       if (t)
         return t < 0;
-      *read_pos += dump_remainder (pretty_filename, fd, COPY_TO_EOF);
+      *read_pos += dump_remainder (false, pretty_filename, fd, COPY_TO_EOF);
     }
   else
     {