Subject: Re: bug#12216: peek-char incorrectly *CONSUMES* eof

From: Mark H Weaver <[email-masked]>
Date: Sun, 31 Mar 2013 22:05:45 -0400

Mark H Weaver <[email-masked]> writes:

> Andy Wingo <[email-masked]> writes:
>> Maybe if it is somehow confined to scm_peek_char and scm_fill_input it
>> could be doable.
>
> I think I now see a reasonable way to fix this.
>
> First of all, all interfaces that do something like 'peek' would, when
> returning an EOF, set a 'pending_eof' flag in the port structure.  Then
> 'scm_fill_input' would start by checking this flag; if it is set, then
> it would clear the flag and return EOF.
>
> We would also need to clear the 'pending_eof' flag in a few other
> places, most notably in 'scm_end_input'.  In theory, that might be
> enough, but to be on the safe side we should also clear the flag in any
> procedure that writes or seeks.

Upon further thought, I decided that we shouldn't clear it on writes.
If 'rw_random' is set, then 'scm_end_input' will be called anyway.  If
'rw_random' is not set, then we should *not* clear the flag on writes,
because it indicates that the read and write streams are independent,
e.g. a terminal or socket.

On the other hand, the 'pending_eof' flag should of course be cleared
when putting characters back (unget).

I've attached a proposed patch.  It depends on the three preliminary
patches posted here:

  http://lists.gnu.org/archive/html/guile-devel/2013-03/msg00221.html

Thoughts?

   Regards,
     Mark

>From e732d035b86a990ea0cfb56a710ee9224d8dbd31 Mon Sep 17 00:00:00 2001
From: Mark H Weaver <[email-masked]>
Date: Sun, 31 Mar 2013 19:06:51 -0400
Subject: [PATCH 4/5] Peeks do not consume EOFs.

* libguile/ports-internal.h (struct scm_port_internal): Add
  'pending_eof' flag.

* libguile/inline.h (scm_peek_byte_or_eof): Set 'pending_eof' flag
  before returning EOF.

* libguile/ports.c (scm_i_set_pending_eof): New function.
  (scm_i_clear_pending_eof): New static function.
  (scm_new_port_table_entry): Initialize 'pending_eof'.
  (scm_fill_input): Check for 'pending_eof'.
  (scm_end_input, scm_unget_byte, scm_seek): Clear 'pending_eof'.
  (scm_peek_char): Set 'pending_eof' flag before returning EOF.

* libguile/ports.h (scm_i_set_pending_eof): Add prototype.
---
 libguile/inline.h         |    5 ++++-
 libguile/ports-internal.h |    1 +
 libguile/ports.c          |   33 +++++++++++++++++++++++++++++++--
 libguile/ports.h          |    1 +
 4 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/libguile/inline.h b/libguile/inline.h
index 88ba7f7..fffe101 100644
--- a/libguile/inline.h
+++ b/libguile/inline.h
@@ -134,7 +134,10 @@ scm_peek_byte_or_eof (SCM port)
   if (pt->read_pos >= pt->read_end)
     {
       if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
-	return EOF;
+        {
+          scm_i_set_pending_eof (port);
+          return EOF;
+        }
     }
 
   c = *pt->read_pos;
diff --git a/libguile/ports-internal.h b/libguile/ports-internal.h
index d52eab2..5bde9d0 100644
--- a/libguile/ports-internal.h
+++ b/libguile/ports-internal.h
@@ -48,6 +48,7 @@ struct scm_port_internal
 {
   scm_t_port_encoding_mode encoding_mode;
   scm_t_iconv_descriptors *iconv_descriptors;
+  int pending_eof;
 };
 
 typedef struct scm_port_internal scm_t_port_internal;
diff --git a/libguile/ports.c b/libguile/ports.c
index 1ac9106..89e1714 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -241,6 +241,18 @@ scm_set_port_input_waiting (scm_t_bits tc, int (*input_waiting) (SCM))
   scm_ptobs[SCM_TC2PTOBNUM (tc)].input_waiting = input_waiting;
 }
 
+void
+scm_i_set_pending_eof (SCM port)
+{
+  SCM_INTERNAL_PTAB_ENTRY (port)->pending_eof = 1;
+}
+
+static void
+scm_i_clear_pending_eof (SCM port)
+{
+  SCM_INTERNAL_PTAB_ENTRY (port)->pending_eof = 0;
+}
+
 
 
 SCM_DEFINE (scm_char_ready_p, "char-ready?", 0, 1, 0, 
@@ -634,6 +646,8 @@ scm_new_port_table_entry (scm_t_bits tag)
   entry->input_cd = pti;   /* XXX pointer to the internal port structure */
   entry->output_cd = NULL; /* XXX unused */
 
+  pti->pending_eof = 0;
+
   SCM_SET_CELL_TYPE (z, tag);
   SCM_SETPTAB_ENTRY (z, entry);
 
@@ -1410,9 +1424,16 @@ int
 scm_fill_input (SCM port)
 {
   scm_t_port *pt = SCM_PTAB_ENTRY (port);
+  scm_t_port_internal *pti = SCM_INTERNAL_PTAB_ENTRY (port);
 
   assert (pt->read_pos == pt->read_end);
 
+  if (pti->pending_eof)
+    {
+      pti->pending_eof = 0;
+      return EOF;
+    }
+
   if (pt->read_buf == pt->putback_buf)
     {
       /* finished reading put-back chars.  */
@@ -1652,6 +1673,7 @@ scm_end_input (SCM port)
   long offset;
   scm_t_port *pt = SCM_PTAB_ENTRY (port);
 
+  scm_i_clear_pending_eof (port);
   if (pt->read_buf == pt->putback_buf)
     {
       offset = pt->read_end - pt->read_pos;
@@ -1675,6 +1697,7 @@ scm_unget_byte (int c, SCM port)
 {
   scm_t_port *pt = SCM_PTAB_ENTRY (port);
 
+  scm_i_clear_pending_eof (port);
   if (pt->read_buf == pt->putback_buf)
     /* already using the put-back buffer.  */
     {
@@ -1846,7 +1869,10 @@ SCM_DEFINE (scm_peek_char, "peek-char", 0, 1, 0,
       result = SCM_BOOL_F;
     }
   else if (c == EOF)
-    result = SCM_EOF_VAL;
+    {
+      scm_i_set_pending_eof (port);
+      result = SCM_EOF_VAL;
+    }
   else
     result = SCM_MAKE_CHAR (c);
 
@@ -1945,7 +1971,10 @@ SCM_DEFINE (scm_seek, "seek", 3, 0, 0,
 	SCM_MISC_ERROR ("port is not seekable", 
                         scm_cons (fd_port, SCM_EOL));
       else
-	rv = ptob->seek (fd_port, off, how);
+        {
+          scm_i_clear_pending_eof (fd_port);
+          rv = ptob->seek (fd_port, off, how);
+        }
       return scm_from_off_t_or_off64_t (rv);
     }
   else /* file descriptor?.  */
diff --git a/libguile/ports.h b/libguile/ports.h
index 95545cd..5aca26d 100644
--- a/libguile/ports.h
+++ b/libguile/ports.h
@@ -319,6 +319,7 @@ SCM_API SCM scm_set_port_filename_x (SCM port, SCM filename);
 SCM_INTERNAL const char *scm_i_default_port_encoding (void);
 SCM_INTERNAL void scm_i_set_default_port_encoding (const char *);
 SCM_INTERNAL void scm_i_set_port_encoding_x (SCM port, const char *str);
+SCM_INTERNAL void scm_i_set_pending_eof (SCM port);
 SCM_API SCM scm_port_encoding (SCM port);
 SCM_API SCM scm_set_port_encoding_x (SCM port, SCM encoding);
 SCM_INTERNAL scm_t_string_failed_conversion_handler
-- 
1.7.10.4