changeset 13294:4263e09100fb

ftell, ftello: Work around Solaris bug.
author Bruno Haible <bruno@clisp.org>
date Sat, 01 May 2010 20:32:26 +0200
parents d6afdbe59f47
children ed67fbd31da7
files ChangeLog doc/posix-functions/ftell.texi doc/posix-functions/ftello.texi lib/ftello.c m4/ftello.m4 modules/ftello
diffstat 6 files changed, 135 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2010-05-01  Bruno Haible  <bruno@clisp.org>
+
+	ftell, ftello: Work around Solaris bug.
+	* m4/ftello.m4 (gl_FUNC_FTELLO): Detect Solaris bug.
+	* lib/ftello.c: Include stdio-impl.h.
+	(ftello): On Solaris, when _IOWRT is set, compute the result without
+	looking at _IOREAD.
+	* modules/ftello (Files): Add lib/stdio-impl.h.
+	* doc/posix-functions/ftell.texi: Mention Solaris bug.
+	* doc/posix-functions/ftello.texi: Likewise.
+	Reported by Eric Blake.
+
 2010-05-01  Bruno Haible  <bruno@clisp.org>
 
 	freading: Adapt to special meaning of _IOREAD flag on Solaris.
--- a/doc/posix-functions/ftell.texi
+++ b/doc/posix-functions/ftell.texi
@@ -10,6 +10,10 @@
 @itemize
 @item
 This function mistakenly succeeds on pipes on some platforms: mingw.
+@item
+This function produces incorrect results after @code{putc} that followed a
+@code{getc} call that reached EOF on some platforms:
+Solaris 10.
 @end itemize
 
 Portability problems not fixed by Gnulib:
--- a/doc/posix-functions/ftello.texi
+++ b/doc/posix-functions/ftello.texi
@@ -15,6 +15,10 @@
 The declaration of @code{ftello} in @code{<stdio.h>} is not enabled by default
 on some platforms: glibc 2.3.6.
 @item
+This function produces incorrect results after @code{putc} that followed a
+@code{getc} call that reached EOF on some platforms:
+Solaris 10.
+@item
 This function fails on seekable stdin, stdout, and stderr: cygwin <= 1.5.24.
 @end itemize
 
--- a/lib/ftello.c
+++ b/lib/ftello.c
@@ -22,6 +22,8 @@
 /* Get lseek.  */
 #include <unistd.h>
 
+#include "stdio-impl.h"
+
 off_t
 ftello (FILE *fp)
 #undef ftello
@@ -36,6 +38,28 @@
     return -1;
 #endif
 
+#if FTELLO_BROKEN_AFTER_SWITCHING_FROM_READ_TO_WRITE /* Solaris */
+  /* The Solaris stdio leaves the _IOREAD flag set after reading from a file
+     reaches EOF and the program then starts writing to the file.  ftello
+     gets confused by this.  */
+  if (fp_->_flag & _IOWRT)
+    {
+      off_t pos;
+
+      /* Call ftello nevertheless, for the side effects that it does on fp.  */
+      ftello (fp);
+
+      /* Compute the file position ourselves.  */
+      pos = llseek (fileno (fp), (off_t) 0, SEEK_CUR);
+      if (pos >= 0)
+        {
+          if ((fp_->_flag & _IONBF) == 0 && fp_->_base != NULL)
+            pos += fp_->_ptr - fp_->_base;
+        }
+      return pos;
+    }
+#endif
+
 #if defined __SL64 && defined __SCLE /* Cygwin */
   if ((fp->_flags & __SL64) == 0)
     {
--- a/m4/ftello.m4
+++ b/m4/ftello.m4
@@ -1,5 +1,5 @@
-# ftello.m4 serial 6
-dnl Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+# ftello.m4 serial 7
+dnl Copyright (C) 2007-2010 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
@@ -10,7 +10,7 @@
   AC_REQUIRE([AC_PROG_CC])
   AC_REQUIRE([gl_STDIN_LARGE_OFFSET])
 
-  dnl Persuade glibc <stdio.h> to declare fseeko().
+  dnl Persuade glibc <stdio.h> to declare ftello().
   AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
 
   AC_CACHE_CHECK([for ftello], [gl_cv_func_ftello],
@@ -23,6 +23,93 @@
   else
     if test $gl_cv_var_stdin_large_offset = no; then
       REPLACE_FTELLO=1
+    else
+      dnl Detect bug on Solaris.
+      dnl ftell and ftello produce incorrect results after putc that followed a
+      dnl getc call that reached EOF on Solaris. This is because the _IOREAD
+      dnl flag does not get cleared in this case, even though _IOWRT gets set,
+      dnl and ftell and ftello look whether the _IOREAD flag is set.
+      AC_REQUIRE([AC_CANONICAL_HOST])
+      AC_CACHE_CHECK([whether ftello works],
+        [gl_cv_func_ftello_works],
+        [
+          dnl Initial guess, used when cross-compiling or when /dev/tty cannot
+          dnl be opened.
+changequote(,)dnl
+          case "$host_os" in
+                      # Guess no on Solaris.
+            solaris*) gl_cv_func_ftello_works="guessing no" ;;
+                      # Guess yes otherwise.
+            *)        gl_cv_func_ftello_works="guessing yes" ;;
+          esac
+changequote([,])dnl
+          AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define TESTFILE "conftest.tmp"
+int
+main (void)
+{
+  FILE *fp;
+
+  /* Create a file with some contents.  */
+  fp = fopen (TESTFILE, "w");
+  if (fp == NULL)
+    return 70;
+  if (fwrite ("foogarsh", 1, 8, fp) < 8)
+    return 71;
+  if (fclose (fp))
+    return 72;
+
+  /* The file's contents is now "foogarsh".  */
+
+  /* Try writing after reading to EOF.  */
+  fp = fopen (TESTFILE, "r+");
+  if (fp == NULL)
+    return 73;
+  if (fseek (fp, -1, SEEK_END))
+    return 74;
+  if (!(getc (fp) == 'h'))
+    return 1;
+  if (!(getc (fp) == EOF))
+    return 2;
+  if (!(ftell (fp) == 8))
+    return 3;
+  if (!(ftell (fp) == 8))
+    return 4;
+  if (!(putc ('!', fp) == '!'))
+    return 5;
+  if (!(ftell (fp) == 9))
+    return 6;
+  if (!(fclose (fp) == 0))
+    return 7;
+  fp = fopen (TESTFILE, "r");
+  if (fp == NULL)
+    return 75;
+  {
+    char buf[10];
+    if (!(fread (buf, 1, 10, fp) == 9))
+      return 10;
+    if (!(memcmp (buf, "foogarsh!", 9) == 0))
+      return 11;
+  }
+  if (!(fclose (fp) == 0))
+    return 12;
+
+  /* The file's contents is now "foogarsh!".  */
+
+  return 0;
+}], [gl_cv_func_ftello_works=yes], [gl_cv_func_ftello_works=no], [:])
+        ])
+      case "$gl_cv_func_ftello_works" in
+        *yes) ;;
+        *)
+          REPLACE_FTELLO=1
+          AC_DEFINE([FTELLO_BROKEN_AFTER_SWITCHING_FROM_READ_TO_WRITE], [1],
+            [Define to 1 if the system's ftello function has the Solaris bug.])
+          ;;
+      esac
     fi
   fi
   if test $HAVE_FTELLO = 0 || test $REPLACE_FTELLO = 1; then
--- a/modules/ftello
+++ b/modules/ftello
@@ -3,6 +3,7 @@
 
 Files:
 lib/ftello.c
+lib/stdio-impl.h
 m4/ftello.m4
 
 Depends-on: