m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

seekable stdin


From: Eric Blake
Subject: seekable stdin
Date: Fri, 15 Dec 2006 04:22:23 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

I fixed this on the branch a while ago, but never ported it to head, in part 
because cygwin inherited a newlib libc bug that prevented the test from working 
(basically, exit and fclose were not flushing unprocessed data from input 
streams).  But as of today, the bug in newlib is fixed in CVS.

2006-12-14  Eric Blake  <address@hidden>

        * modules/m4.c (m4_sysval_flush): Flush stdin before exiting, as
        required by POSIX.
        * tests/others.at (stdin seekable): New test.

Index: modules/m4.c
===================================================================
RCS file: /sources/m4/m4/modules/m4.c,v
retrieving revision 1.93
diff -u -r1.93 m4.c
--- modules/m4.c        11 Nov 2006 16:21:25 -0000      1.93
+++ modules/m4.c        15 Dec 2006 04:15:46 -0000
@@ -486,9 +486,33 @@
 {
   FILE *debug_file = m4_get_debug_file (context);
 
-  if (debug_file != stdout) fflush (stdout);
-  if (debug_file != stderr) fflush (stderr);
-  if (debug_file != NULL)   fflush (debug_file);
+  if (debug_file != stdout)
+    fflush (stdout);
+  if (debug_file != stderr)
+    fflush (stderr);
+  if (debug_file != NULL)
+    fflush (debug_file);
+  /* POSIX requires that if m4 doesn't consume all input, but stdin is
+     opened on a seekable file, that the file pointer be left at the
+     next character on exit (but places no restrictions on the file
+     pointer location on a non-seekable file).  It also requires that
+     fflush() followed by fseek() on an input file set the underlying
+     file pointer.  However, fflush() on a non-seekable file can lose
+     buffered data, which we might otherwise want to process after
+     syscmd.  Hence, we must check whether stdin is seekable.  We must
+     also be tolerant of operating with stdin closed, so we don't
+     report any failures in this attempt.  The stdio-safer module and
+     friends are essential, so that if stdin was closed, this lseek is
+     not on some other file that we have since opened.  Mingw has bugs
+     when using fseek on text files, so we only strive for POSIX
+     behavior when we detect a UNIX environment.  */
+#if UNIX
+  if (lseek (STDIN_FILENO, 0, SEEK_CUR) >= 0
+      && fflush (stdin) == 0)
+    {
+      fseek (stdin, 0, SEEK_CUR);
+    }
+#endif /* UNIX */
 }
 
 M4BUILTIN_HANDLER (syscmd)
Index: tests/others.at
===================================================================
RCS file: /sources/m4/m4/tests/others.at,v
retrieving revision 1.27
diff -u -r1.27 others.at
--- tests/others.at     16 Nov 2006 14:42:38 -0000      1.27
+++ tests/others.at     15 Dec 2006 04:15:46 -0000
@@ -451,6 +451,64 @@
 
 AT_CLEANUP
 
+## -------------- ##
+## stdin seekable ##
+## -------------- ##
+
+AT_SETUP([stdin seekable])
+
+dnl POSIX requires that if stdin is seekable, m4 must seek to the location
+dnl of unprocessed data for the benefit of other copies of the fd.
+
+dnl Check internal follow-on process.
+AT_DATA([in.m4], [[syscmd(`cat')m4exit(15)
+]])
+AT_CHECK_M4([], [0], [[m4exit(15)
+]], [], [in.m4])
+
+dnl Check external follow-on process, after m4exit.
+AT_DATA([in.m4], [[m4exit(
+0)trailing data
+]])
+AT_CHECK([(m4; cat) < in.m4], [0], [[trailing data
+]])
+
+dnl Not all OS's have a libc that gets the remaining tests right (for
+dnl example, cygwin 1.5.22 and earlier).
+AT_CHECK([(sed -ne 1q; cat) < in.m4], [0], [stdout])
+AT_CHECK([test "x`cat stdout`" = "x0)trailing data" || \
+  { echo "skipping: sed is too greedy on seekable stdin"; exit 77; }])
+
+dnl Check external follow-on process, after fatal error.
+AT_DATA([in.m4], [[dnl(
+0)trailing data
+]])
+AT_CHECK([(m4 -E; cat) < in.m4], [0], [[trailing data
+]], [[m4:stdin:1: Warning: dnl: extra arguments ignored: 1 > 0
+]])
+
+dnl Ensure that esyscmd resumes parsing where the child process left off.
+AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo
+esyscmd(`sed -e "s/foo/bar/;q"')sed foo
+m4 foo
+]])
+AT_CHECK_M4([], [0], [[m4 FOO
+sed bar
+m4 FOO
+]], [], [in.m4])
+
+dnl Ensure that syscmd resumes parsing where the child process left off.
+AT_DATA([in.m4], [[define(`foo', `FOO')m4 foo
+syscmd(`sed -e "s/foo/bar/;q"')sed foo
+m4 foo
+]])
+AT_CHECK_M4([], [0], [[m4 FOO
+sed bar
+m4 FOO
+]], [], [in.m4])
+
+AT_CLEANUP
+
 ## ------------- ##
 ## stdout closed ##
 ## ------------- ##






reply via email to

[Prev in Thread] Current Thread [Next in Thread]