Make exit(), fclose(), fflush(), and freopen() comply with POSIX-2008
authorguenther <guenther@openbsd.org>
Mon, 12 Aug 2024 20:53:09 +0000 (20:53 +0000)
committerguenther <guenther@openbsd.org>
Mon, 12 Aug 2024 20:53:09 +0000 (20:53 +0000)
requirements for setting the underlying file position when flushing
read-mode streams, and make an fseek()-after-fflush() not change the
underlying file position.

Much testing, review, and assistance from tb@
ok tb@ millert@

lib/libc/stdio/fclose.3
lib/libc/stdio/fclose.c
lib/libc/stdio/fflush.3
lib/libc/stdio/fflush.c
lib/libc/stdio/freopen.c
lib/libc/stdio/fseek.c
lib/libc/stdio/ftell.c
lib/libc/stdlib/exit.3

index b1f7c53..cedcaed 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: fclose.3,v 1.9 2015/11/04 21:30:13 tedu Exp $
+.\"    $OpenBSD: fclose.3,v 1.10 2024/08/12 20:53:09 guenther Exp $
 .\"
 .\" Copyright (c) 1990, 1991, 1993
 .\"    The Regents of the University of California.  All rights reserved.
@@ -31,7 +31,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: November 4 2015 $
+.Dd $Mdocdate: August 12 2024 $
 .Dt FCLOSE 3
 .Os
 .Sh NAME
@@ -47,8 +47,11 @@ The
 function dissociates the named
 .Fa stream
 from its underlying file or set of functions.
-If the stream was being used for output, any buffered data is written
-first, using
+If the stream was being used for output then any buffered data is written
+first,
+while if the stream was being used for input then the underlying
+file position may be updated,
+as if via
 .Xr fflush 3 .
 .Sh RETURN VALUES
 Upon successful completion 0 is returned.
@@ -83,7 +86,7 @@ or
 The
 .Fn fclose
 function conforms to
-.St -ansiC .
+.St -p1003.1-2024 .
 .Sh HISTORY
 The
 .Fn fclose
index 4bd162a..07026fa 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fclose.c,v 1.10 2015/08/31 02:53:57 guenther Exp $ */
+/*     $OpenBSD: fclose.c,v 1.11 2024/08/12 20:53:09 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
@@ -47,7 +47,7 @@ fclose(FILE *fp)
        }
        FLOCKFILE(fp);
        WCIO_FREE(fp);
-       r = fp->_flags & __SWR ? __sflush(fp) : 0;
+       r = __sflush(fp);
        if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0)
                r = EOF;
        if (fp->_flags & __SMBF)
index 677d087..1e1c780 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: fflush.3,v 1.13 2019/09/07 10:28:27 schwarze Exp $
+.\"    $OpenBSD: fflush.3,v 1.14 2024/08/12 20:53:09 guenther Exp $
 .\"
 .\" Copyright (c) 1990, 1991, 1993
 .\"    The Regents of the University of California.  All rights reserved.
@@ -31,7 +31,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: September 7 2019 $
+.Dd $Mdocdate: August 12 2024 $
 .Dt FFLUSH 3
 .Os
 .Sh NAME
@@ -50,6 +50,20 @@ The function
 forces a write of all buffered data for the given output or update
 .Fa stream
 via the stream's underlying write function.
+If
+.Fa stream
+is a stream opened for reading with
+.Xr fdopen 3 ,
+.Xr fopen 3 ,
+or
+.Xr freopen 3
+of a seekable file and it is not already at EOF then
+.Fn fflush
+sets the seek position of the file to the file position of the
+stream and discards any text pushed back by via
+.Xr ungetc 3 
+or
+.Xr ungetwc 3 .
 The open status of the stream is unaffected.
 .Pp
 If the
@@ -70,7 +84,9 @@ For input streams this discards any input read from the underlying object
 but not yet obtained via
 .Xr getc 3 ;
 this includes any text pushed back via
-.Xr ungetc 3 .
+.Xr ungetc 3
+or
+.Xr ungetwc 3 .
 .Sh RETURN VALUES
 Upon successful completion 0 is returned.
 Otherwise,
@@ -82,9 +98,7 @@ is set to indicate the error.
 .Bl -tag -width Er
 .It Bq Er EBADF
 .Fa stream
-is not an open stream or, in the case of
-.Fn fflush ,
-not a stream open for writing.
+is not an open stream.
 .El
 .Pp
 The function
@@ -102,7 +116,7 @@ for any of the errors specified for the routine
 The
 .Fn fflush
 function conforms to
-.St -ansiC .
+.St -p1003.1-2024 .
 .Sh HISTORY
 A predecessor
 .Fn flush
index fd1a4b3..5888e23 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fflush.c,v 1.9 2015/08/31 02:53:57 guenther Exp $ */
+/*     $OpenBSD: fflush.c,v 1.10 2024/08/12 20:53:09 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
@@ -33,6 +33,7 @@
 
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include "local.h"
 
 /* Flush a single file, or (if fp is NULL) all files.  */
@@ -44,11 +45,7 @@ fflush(FILE *fp)
        if (fp == NULL)
                return (_fwalk(__sflush_locked));
        FLOCKFILE(fp);
-       if ((fp->_flags & (__SWR | __SRW)) == 0) {
-               errno = EBADF;
-               r = EOF;
-       } else
-               r = __sflush(fp);
+       r = __sflush(fp);
        FUNLOCKFILE(fp);
        return (r);
 }
@@ -58,30 +55,51 @@ int
 __sflush(FILE *fp)
 {
        unsigned char *p;
+       fpos_t off;
        int n, t;
 
        t = fp->_flags;
-       if ((t & __SWR) == 0)
-               return (0);
+       if (t & __SWR) {
+               if ((p = fp->_bf._base) == NULL)
+                       return (0);
 
-       if ((p = fp->_bf._base) == NULL)
-               return (0);
+               n = fp->_p - p;         /* write this much */
 
-       n = fp->_p - p;         /* write this much */
+               /*
+                * Set these immediately to avoid problems with longjmp and to
+                * allow exchange buffering (via setvbuf) in user write
+                * function.
+                */
+               fp->_p = p;
+               fp->_w = t & (__SLBF|__SNBF) ? 0 : fp->_bf._size;
 
-       /*
-        * Set these immediately to avoid problems with longjmp and to allow
-        * exchange buffering (via setvbuf) in user write function.
-        */
-       fp->_p = p;
-       fp->_w = t & (__SLBF|__SNBF) ? 0 : fp->_bf._size;
+               for (; n > 0; n -= t, p += t) {
+                       t = (*fp->_write)(fp->_cookie, (char *)p, n);
+                       if (t <= 0) {
+                               fp->_flags |= __SERR;
+                               return (EOF);
+                       }
+               }
+       } else if ((t & __SRD) && !(t & __SEOF)) {
+               if (fp->_seek != __sseek || fp->_file < 0) {
+                       errno = EBADF;
+                       return EOF;
+               }
 
-       for (; n > 0; n -= t, p += t) {
-               t = (*fp->_write)(fp->_cookie, (char *)p, n);
-               if (t <= 0) {
-                       fp->_flags |= __SERR;
-                       return (EOF);
+               off = fp->_r;
+               if (HASUB(fp)) {
+                       off += fp->_ur;
+                       FREEUB(fp);
                }
+               if (t & __SOFF) {
+                       off = fp->_offset - off;
+                       __sseek(fp->_cookie, off, SEEK_SET);
+               } else if (off != 0)
+                       __sseek(fp->_cookie, -off, SEEK_CUR);
+
+               WCIO_FREE(fp);
+               fp->_p = fp->_bf._base;
+               fp->_r = 0;
        }
        return (0);
 }
index 65065e5..2b82300 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: freopen.c,v 1.17 2019/06/29 16:12:21 deraadt Exp $ */
+/*     $OpenBSD: freopen.c,v 1.18 2024/08/12 20:53:09 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
@@ -75,9 +75,8 @@ freopen(const char *file, const char *mode, FILE *fp)
                isopen = 0;
                wantfd = -1;
        } else {
-               /* flush the stream; ANSI doesn't require this. */
-               if (fp->_flags & __SWR)
-                       (void) __sflush(fp);
+               /* flush the stream; POSIX, not ANSI, requires this. */
+               (void) __sflush(fp);
                /* if close is NULL, closing is a no-op, hence pointless */
                isopen = fp->_close != NULL;
                if ((wantfd = fp->_file) < 0 && isopen) {
index 18c5301..a65867d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: fseek.c,v 1.14 2022/05/14 05:06:32 guenther Exp $ */
+/*     $OpenBSD: fseek.c,v 1.15 2024/08/12 20:53:09 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
@@ -116,7 +116,8 @@ fseeko(FILE *fp, off_t offset, int whence)
        /*
         * Can only optimise if:
         *      reading (and not reading-and-writing);
-        *      not unbuffered; and
+        *      not unbuffered;
+        *      not immediately after an fflush(); and
         *      this is a `regular' Unix file (and hence seekfn==__sseek).
         * We must check __NBF first, because it is possible to have __NBF
         * and __SOPT both set.
@@ -125,6 +126,8 @@ fseeko(FILE *fp, off_t offset, int whence)
                __smakebuf(fp);
        if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
                goto dumb;
+       if (fp->_r == 0 && (fp->_p == NULL || fp->_p == fp->_bf._base))
+               goto dumb;
        if ((fp->_flags & __SOPT) == 0) {
                if (seekfn != __sseek ||
                    fp->_file < 0 || fstat(fp->_file, &st) == -1 ||
@@ -228,7 +231,7 @@ fseeko(FILE *fp, off_t offset, int whence)
         * do it.  Allow the seek function to change fp->_bf._base.
         */
 dumb:
-       if (__sflush(fp) ||
+       if (((fp->_flags & __SWR) && __sflush(fp)) ||
            (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) {
                FUNLOCKFILE(fp);
                return (EOF);
index a75ebbb..69e1f0d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ftell.c,v 1.11 2015/08/31 02:53:57 guenther Exp $ */
+/*     $OpenBSD: ftell.c,v 1.12 2024/08/12 20:53:09 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
@@ -55,7 +55,8 @@ ftello(FILE *fp)
         * adjust for buffered bytes.
         */
        FLOCKFILE(fp);
-       __sflush(fp);           /* may adjust seek offset on append stream */
+       if (fp->_flags & __SWR)
+               __sflush(fp);   /* may adjust seek offset on append stream */
        if (fp->_flags & __SOFF)
                pos = fp->_offset;
        else {
index a1c4378..5e006e5 100644 (file)
@@ -29,9 +29,9 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"    $OpenBSD: exit.3,v 1.16 2014/11/30 21:21:59 schwarze Exp $
+.\"    $OpenBSD: exit.3,v 1.17 2024/08/12 20:53:09 guenther Exp $
 .\"
-.Dd $Mdocdate: November 30 2014 $
+.Dd $Mdocdate: August 12 2024 $
 .Dt EXIT 3
 .Os
 .Sh NAME
@@ -54,9 +54,7 @@ Call the functions registered with the
 .Xr atexit 3
 function, in the reverse order of their registration.
 .It
-Flush all open output streams.
-.It
-Close all open streams.
+Flush and close all open streams.
 .It
 Unlink all files created with the
 .Xr tmpfile 3
@@ -79,6 +77,7 @@ function never returns.
 .Sh SEE ALSO
 .Xr _exit 2 ,
 .Xr atexit 3 ,
+.Xr fflush 3 ,
 .Xr intro 3 ,
 .Xr sysexits 3 ,
 .Xr tmpfile 3
@@ -86,7 +85,7 @@ function never returns.
 The
 .Fn exit
 function conforms to
-.St -isoC-99 .
+.St -p1003.1-2024 .
 .Sh HISTORY
 An
 .Fn exit