This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

patch for freopen(NULL,...)


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

POSIX states that freopen(NULL,...) should attempt to change the mode of
the existing underlying file descriptor, with an implementation-defined
list of valid changes, rather than closing the underlying file descriptor:
http://www.opengroup.org/onlinepubs/009695399/functions/freopen.html.

Currently, newlib freopen(NULL) blindly calls open(NULL), which in the
worst case causes a core dump for violating the POSIX interface of open,
and in the best case violates POSIX for returning EFAULT instead of EBADF
(if open has the extension of returning EFAULT).  Even though POSIX allows
an implementation the cop-out of always returning EBADF when filename is
NULL, it is more useful to provide a non-trivial implementation: this
feature of freopen is useful for changing append vs. seek status on files
open for writing; and on cygwin it is useful for changing binary vs. text
modes.  In fact, coreutils 5.93 now uses the idiom freopen(NULL, "rb",
stdin) to ensure that stdin is in binary mode (I have patched the cygwin
release of coreutils 5.93 to use a replacement freopen, but think the
patch belongs better in newlib itself).

Some notes about this patch: __SCLE is currently only defined for cygwin,
and cygwin documents that fcntl(F_SETFL) does not alter binary/text mode,
and that setmode() should not be called on ttys.  Also, POSIX states that
freopen may return EBADF when filename was NULL but the conversion cannot
be performed; and since I know of no easy way to change the mode of an
open file descriptor from O_RDONLY to O_WRONLY, my patch has the
implementation-defined limitation that freopen(NULL) will only succeed if
the access mode (read, write, or read-write) is unchanged.

2005-12-26  Eric Blake  <ebb9@byu.net>

	* libc/stdio/freopen.c (_freopen_r): Accept NULL filename.
	* libc/stdio64/freopen64.c (_freopen64_r): Likewise.

- --
Life is short - so eat dessert first!

Eric Blake             ebb9@byu.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFDsBXX84KuGfSFAYARAiJdAJ0UGksI1Bhu/AZ5BaPz7BPCJ7nJEACeLF6c
Vy1KK8RbAacnrDFmEelx66s=
=PMcl
-----END PGP SIGNATURE-----
Index: libc/stdio/freopen.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/stdio/freopen.c,v
retrieving revision 1.12
diff -u -p -r1.12 freopen.c
--- libc/stdio/freopen.c	8 Feb 2005 01:33:16 -0000	1.12
+++ libc/stdio/freopen.c	26 Dec 2005 15:45:50 -0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1990 The Regents of the University of California.
+ * Copyright (c) 1990, 2005 The Regents of the University of California.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted
@@ -55,6 +55,11 @@ it).
 
 <[file]> and <[mode]> are used just as in <<fopen>>.
 
+If <[file]> is <<NULL>>, the underlying stream is modified rather than
+closed.  The file cannot change access mode (for example, if it was
+previously read-only, <[mode]> must be "r", "rb", or "rt"), but can
+change status such as append or binary mode.
+
 RETURNS
 If successful, the result is the same as the argument <[fp]>.  If the
 file cannot be opened as specified, the result is <<NULL>>.
@@ -70,6 +75,7 @@ Supporting OS subroutines required: <<cl
 #include <reent.h>
 #include <time.h>
 #include <stdio.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <sys/lock.h>
@@ -117,17 +123,51 @@ _DEFUN(_freopen_r, (ptr, file, mode, fp)
     {
       if (fp->_flags & __SWR)
 	_CAST_VOID fflush (fp);
-      /* if close is NULL, closing is a no-op, hence pointless */
-      if (fp->_close != NULL)
+      /*
+       * If close is NULL, closing is a no-op, hence pointless.
+       * If file is NULL, the file should not be closed.
+       */
+      if (fp->_close != NULL && file != NULL)
 	_CAST_VOID (*fp->_close) (fp->_cookie);
     }
 
   /*
-   * Now get a new descriptor to refer to the new file.
+   * Now get a new descriptor to refer to the new file, or reuse the
+   * existing file descriptor if file is NULL.
    */
 
-  f = _open_r (ptr, (char *) file, oflags, 0666);
-  e = ptr->_errno;
+  if (file != NULL)
+    {
+      f = _open_r (ptr, (char *) file, oflags, 0666);
+      e = ptr->_errno;
+    }
+  else
+    {
+      /*
+       * Reuse the file descriptor, but only if the access mode is
+       * unchanged.  F_SETFL correctly ignores creation flags.
+       */
+      f = fp->_file;
+      if ((oflags = _fcntl_r (ptr, f, F_GETFL, 0)) == -1
+          || ((oflags ^ flags) & O_ACCMODE) != 0
+          || _fcntl_r (ptr, f, F_SETFL, flags) == -1)
+        f = -1;
+#ifdef __SCLE
+      /*
+       * F_SETFL doesn't change textmode.  Don't mess with modes of ttys.
+       */
+      if (0 <= f && ! _isatty (f)
+          && setmode (f, flags & (O_BINARY | O_TEXT)) == -1)
+        f = -1;
+#endif
+
+      if (f < 0)
+        {
+          e = EBADF;
+          if (fp->_close != NULL)
+            _CAST_VOID (*fp->_close) (fp->_cookie);
+        }
+    }
 
   /*
    * Finish closing fp.  Even if the open succeeded above,
Index: libc/stdio64/freopen64.c
===================================================================
RCS file: /cvs/src/src/newlib/libc/stdio64/freopen64.c,v
retrieving revision 1.7
diff -u -p -r1.7 freopen64.c
--- libc/stdio64/freopen64.c	8 Feb 2005 01:33:17 -0000	1.7
+++ libc/stdio64/freopen64.c	26 Dec 2005 15:45:50 -0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1990 The Regents of the University of California.
+ * Copyright (c) 1990, 2005 The Regents of the University of California.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted
@@ -55,6 +55,11 @@ it).
 
 <[file]> and <[mode]> are used just as in <<fopen>>.
 
+If <[file]> is <<NULL>>, the underlying stream is modified rather than
+closed.  The file cannot change access mode (for example, if it was
+previously read-only, <[mode]> must be "r", "rb", or "rt"), but can
+change status such as append or binary mode.
+
 RETURNS
 If successful, the result is the same as the argument <[fp]>.  If the
 file cannot be opened as specified, the result is <<NULL>>.
@@ -68,6 +73,7 @@ Supporting OS subroutines required: <<cl
 
 #include <time.h>
 #include <stdio.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <sys/lock.h>
@@ -117,17 +123,51 @@ _DEFUN (_freopen64_r, (ptr, file, mode, 
     {
       if (fp->_flags & __SWR)
 	(void) fflush (fp);
-      /* if close is NULL, closing is a no-op, hence pointless */
-      if (fp->_close != NULL)
+      /*
+       * If close is NULL, closing is a no-op, hence pointless.
+       * If file is NULL, the file should not be closed.
+       */
+      if (fp->_close != NULL && file != NULL)
 	(void) (*fp->_close) (fp->_cookie);
     }
 
   /*
-   * Now get a new descriptor to refer to the new file.
+   * Now get a new descriptor to refer to the new file, or reuse the
+   * existing file descriptor if file is NULL.
    */
 
-  f = _open64_r (ptr, (char *) file, oflags, 0666);
-  e = ptr->_errno;
+  if (file != NULL)
+    {
+      f = _open64_r (ptr, (char *) file, oflags, 0666);
+      e = ptr->_errno;
+    }
+  else
+    {
+      /*
+       * Reuse the file descriptor, but only if the access mode is
+       * unchanged.  F_SETFL correctly ignores creation flags.
+       */
+      f = fp->_file;
+      if ((oflags = _fcntl_r (ptr, f, F_GETFL, 0)) == -1
+          || ((oflags ^ flags) & O_ACCMODE) != 0
+          || _fcntl_r (ptr, f, F_SETFL, flags) == -1)
+        f = -1;
+#ifdef __SCLE
+      /*
+       * F_SETFL doesn't change textmode.  Don't mess with modes of ttys.
+       */
+      if (0 <= f && ! _isatty (f)
+          && setmode (f, flags & (O_BINARY | O_TEXT)) == -1)
+        f = -1;
+#endif
+
+      if (f < 0)
+        {
+          e = EBADF;
+          if (fp->_close != NULL)
+            (void) (*fp->_close) (fp->_cookie);
+        }
+    }
 
   /*
    * Finish closing fp.  Even if the open succeeded above,

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]