OSDN Git Service

Reimplement Win9x specific fseek()/fwrite() redirector.
authorKeith Marshall <keith@users.osdn.me>
Tue, 4 Dec 2018 19:00:29 +0000 (19:00 +0000)
committerKeith Marshall <keith@users.osdn.me>
Tue, 4 Dec 2018 19:00:29 +0000 (19:00 +0000)
mingwrt/ChangeLog
mingwrt/Makefile.in
mingwrt/include/stdio.h
mingwrt/mingwex/mingw-fseek.c [deleted file]
mingwrt/mingwex/stdio/fwrite.c [new file with mode: 0644]

index ef1bbcf..8419b5f 100644 (file)
@@ -1,5 +1,28 @@
 2018-12-04  Keith Marshall  <keith@users.osdn.me>
 
+       Reimplement Win9x specific fseek()/fwrite() redirector.
+
+       * include/stdio.h [__USE_MINGW_FSEEK]: Deprecate support for direct
+       user definition; deduce it, as an internal private definition, from...
+       [_WIN32_WINDOWS]: ...this, when appropriately defined for Win9x.
+       [__USE_MINGW_FSEEK] (__mingw_fseek): Declare new prototype; map it as
+       multiple __CRT_ALIAS inline function overrides for each of...
+       (fseek, _fseeki64, fseeko64): ...these.
+       [__USE_MINGW_FSEEK] (__mingw_fwrite): Likewise, overriding...
+       (fwrite): ...this.
+       (__mingw_fseeko64): Delete all references; it is no longer provided,
+       nor required.
+
+       * mingwex/stdio/fwrite.c: New file; it reimplements the Win9x
+       fseek()/fwrite() function redirector interface, and thus replaces...
+       * mingex/ming-fseek.c: ...this; delete it.
+
+       * Makefile.in (libmingwex.a): Remove dependency on...
+       (mingw-fseek.$OBJEXT): ...this object module; replace it with...
+       (fwrite.$OBJEXT): ...this alternative dependency.
+
+2018-12-04  Keith Marshall  <keith@users.osdn.me>
+
        Implement POSIX.1-1996 linked-list queue management API.
 
        * mingwex/insque.c mingwex/remque.c: New files; they implement the
index 5c308bd..cd26940 100644 (file)
@@ -446,7 +446,7 @@ $(addsuffix fmt.$(OBJEXT),varo crto geto seto crtn getn setn): %.$(OBJEXT): ofmt
 
 # Some additional miscellaneous functions, in libmingwex.a
 #
-libmingwex.a: $(addsuffix .$(OBJEXT), mingw-aligned-malloc mingw-fseek)
+libmingwex.a: $(addsuffix .$(OBJEXT), mingw-aligned-malloc fwrite)
 libmingwex.a: $(addsuffix .$(OBJEXT), glob getopt basename dirname nsleep)
 libmingwex.a: $(addsuffix .$(OBJEXT), clockapi clockres clockset clocktime)
 libmingwex.a: $(addsuffix .$(OBJEXT), insque remque tdelete tfind tsearch twalk)
index 91ea4a1..e2f1dfc 100644 (file)
@@ -742,14 +742,37 @@ _CRTIMP __cdecl __MINGW_NOTHROW  void   rewind (FILE *);
 
 #ifdef __USE_MINGW_FSEEK
 /* Workaround for a limitation on Win9x where a file is not zero padded
- * on write, following a seek beyond the original end of file; these are
- * implemented in libmingwex.a
+ * on write, following a seek beyond the original end of file; supporting
+ * redirector functions are implemented in libmingwex.a
+ *
+ * Note: this is improper usage.  __USE_MINGW_FSEEK exhibits the form of a
+ * private (system reserved) feature test macro; as such, users should not
+ * define it directly, and thus, it really should not have been defined at
+ * this point; discourage this practice.
  */
-__cdecl __MINGW_NOTHROW  int    __mingw_fseek (FILE *, long, int);
-__cdecl __MINGW_NOTHROW  size_t __mingw_fwrite (const void *, size_t, size_t, FILE *);
+#warning "The __USE_MINGW_FSEEK feature test is deprecated"
+#pragma info "Define _WIN32_WINDOWS, instead of __USE_MINGW_FSEEK"
+
+#elif _WIN32_WINDOWS >= _WIN32_WINDOWS_95 && _WIN32_WINDOWS < _WIN32_WINNT_WIN2K
+/* This is correct usage; the private __USE_MINGW_FSEEK feature affects only
+ * Win9x, so enable it implicitly when the _WIN32_WINDOWS feature is specified,
+ * thus indicating the user's intent to target a Win9x platform.
+ */
+#define __USE_MINGW_FSEEK
+#endif
 
-#define fwrite(buffer, size, count, fp)  __mingw_fwrite(buffer, size, count, fp)
-#define fseek(fp, offset, whence)        __mingw_fseek(fp, offset, whence)
+#ifdef __USE_MINGW_FSEEK
+/* Regardless of how it may have become defined, when __USE_MINGW_FSEEK has
+ * been defined, we must redirect calls to fseek() and fwrite(), so that the
+ * Win9x zero padding limitation can be mitigated.
+ */
+__cdecl __MINGW_NOTHROW  int __mingw_fseek (FILE *, __off64_t, int);
+__CRT_ALIAS int fseek( FILE *__fp, long __offset, int __whence )
+{ return __mingw_fseek( __fp, (__off64_t)(__offset), __whence ); }
+
+__cdecl __MINGW_NOTHROW  size_t __mingw_fwrite (const void *, size_t, size_t, FILE *);
+__CRT_ALIAS size_t fwrite( const void *__buf, size_t __len, size_t __cnt, FILE *__fp )
+{ return __mingw_fwrite( __buf, __len, __cnt, __fp ); }
 #endif /* __USE_MINGW_FSEEK */
 
 /* An opaque data type used for storing file positions...  The contents
@@ -965,12 +988,11 @@ int __cdecl __MINGW_NOTHROW  fseeko64 (FILE *, __off64_t, int);
  * argument, (and both types are effectively 64-bit signed ints anyway),
  * the same wrapper will suffice for both.
  */
-int __cdecl __MINGW_NOTHROW __mingw_fseeko64 (FILE *, __off64_t, int);
-__CRT_ALIAS int __cdecl __MINGW_NOTHROW fseeko64 (FILE *__f, __off64_t __o, int __w)
-{ return __mingw_fseeko64 (__f, __o, __w); }
+__CRT_ALIAS int _fseeki64( FILE *__fp, __int64 __offset, int __whence )
+{ return __mingw_fseek( __fp, (__off64_t)(__offset), __whence ); }
 
-__CRT_ALIAS int __cdecl __MINGW_NOTHROW _fseeki64 (FILE *__f, __off64_t __o, int __w)
-{ return __mingw_fseeko64 (__f, (__off64_t)(__o), __w); }
+__CRT_ALIAS int fseeko64( FILE *__fp, __off64_t __offset, int __whence )
+{ return __mingw_fseek( __fp, __offset, __whence ); }
 #endif
 
 __off64_t __cdecl __MINGW_NOTHROW ftello64 (FILE *);
diff --git a/mingwrt/mingwex/mingw-fseek.c b/mingwrt/mingwex/mingw-fseek.c
deleted file mode 100644 (file)
index 27b08ee..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * mingw-fseek.c
- *
- * Workaround for limitations on Win9x where extended file content
- * is not zeroed out if you seek past the end and then write.
- *
- * $Id$
- *
- * Written by Mumit Khan  <khan@xraylith.wisc.edu>
- * Copyright (C) 1999, 2002-2005, 2011, 2015, MinGW.org Project.
- *
- * Abstracted from MinGW local patch to binutils/bfd/libbfd.c
- *
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice, this permission notice, and the following
- * disclaimer shall be included in all copies or substantial portions of
- * the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- */
-#define WIN32_LEAN_AND_MEAN
-
-#include <windows.h>
-#include <stdio.h>
-#include <io.h>
-#include <stdlib.h>
-
-#define ZEROBLOCKSIZE 512
-static int __mingw_fseek_called;
-
-/* The fseek in Win9x runtime does not zero out the file if seeking past
- * the end; if you don't want random stuff from your disk included in your
- * output DLL/executable, use this version instead. On WinNT/Win2k, it
- * just calls runtime fseek().
- *
- * CHECK/FIXME: Does this work for both text and binary modes??
- */
-int
-__mingw_fseek (FILE *fp, long offset, int whence)
-{
-# undef fseek
-  __mingw_fseek_called = 1;
-  return fseek (fp, offset, whence);
-}
-
-int
-__mingw_fseeko64 (FILE *fp, __off64_t offset, int whence)
-{
-# undef fseeko64
-  __mingw_fseek_called = 1;
-  return fseeko64 (fp, offset, whence);
-}
-
-size_t
-__mingw_fwrite (const void *buffer, size_t size, size_t count, FILE *fp)
-{
-# undef fwrite
-  if ((_osver & 0x8000) &&  __mingw_fseek_called)
-  {
-    ULARGE_INTEGER actual_length;
-    LARGE_INTEGER current_position = {{0LL}};
-    __mingw_fseek_called = 0;
-    fflush (fp);
-    actual_length.u.LowPart = GetFileSize
-      ( (HANDLE) _get_osfhandle (fileno (fp)), &actual_length.u.HighPart
-      );
-    if (actual_length.u.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
-      return -1;
-    current_position.u.LowPart = SetFilePointer
-      ( (HANDLE) _get_osfhandle (fileno (fp)), current_position.u.LowPart,
-       &current_position.u.HighPart, FILE_CURRENT
-      );
-    if (current_position.u.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR )
-      return -1;
-
-#   ifdef DEBUG
-      printf( "__mingw_fwrite: current %I64u, actual %I64u\n",
-         current_position.QuadPart, actual_length.QuadPart
-       );
-#   endif /* DEBUG */
-    if( current_position.QuadPart > actual_length.QuadPart )
-    {
-      static char __mingw_zeros[ZEROBLOCKSIZE];
-      long long numleft;
-
-      SetFilePointer( (HANDLE) _get_osfhandle (fileno (fp)), 0, 0, FILE_END );
-      numleft = current_position.QuadPart - actual_length.QuadPart;
-
-#     ifdef DEBUG
-       printf( "__mingw_fwrite: Seeking %I64d bytes past end\n", numleft );
-#     endif /* DEBUG */
-      while( numleft > 0LL )
-      {
-       DWORD nzeros = (numleft > ZEROBLOCKSIZE)
-         ? ZEROBLOCKSIZE
-         : numleft;
-       DWORD written;
-       if( ! WriteFile ((HANDLE) _get_osfhandle (fileno (fp)),
-             __mingw_zeros, nzeros, &written, NULL)
-         )
-         { /* Best we can hope for, or at least DJ says so.
-            */
-           SetFilePointer( (HANDLE) _get_osfhandle (fileno (fp)),
-               0, 0, FILE_BEGIN
-             );
-           return -1;
-         }
-       if( written < nzeros )
-       {
-         /* Likewise. */
-         SetFilePointer( (HANDLE) _get_osfhandle (fileno (fp)),
-             0, 0, FILE_BEGIN
-           );
-         return -1;
-       }
-       numleft -= written;
-      }
-      FlushFileBuffers ((HANDLE) _get_osfhandle (fileno (fp)));
-    }
-  }
-  return (fwrite)(buffer, size, count, fp);
-}
-
-/* $RCSfile$: end of file */
diff --git a/mingwrt/mingwex/stdio/fwrite.c b/mingwrt/mingwex/stdio/fwrite.c
new file mode 100644 (file)
index 0000000..a95c1dc
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * fwrite.c
+ *
+ * Workaround for limitations on Win9x where extended file content
+ * is not zeroed out if you seek past the end and then write.
+ *
+ *
+ * $Id$
+ *
+ * Written by Keith Marshall <keith@users.osdn.me>
+ * Copyright (C) 2018, MinGW.org Project.
+ *
+ *
+ * Replaces mingw-fseek.c implementation
+ * Written by Mumit Khan <khan@xraylith.wisc.edu>
+ * Copyright (C) 1999, 2002-2005, 2011, 2015, MinGW.org Project.
+ *
+ * Originally abstracted from MinGW local patch to binutils/bfd/libbfd.c
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice, this permission notice, and the following
+ * disclaimer shall be included in all copies or substantial portions of
+ * the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * Although specifically providing support for Win9x, we must compile this
+ * with the associated feature test disabled, so that the wrappers provided
+ * herein can access the underlying functions which they wrap.
+ *
+ */
+#undef _WIN32_WINDOWS
+#undef __USE_MINGW_FSEEK
+
+#include <io.h>
+#include <search.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/* The fseek() handler control structures.
+ */
+static void fseek_handler_init( FILE * );
+struct fseek_pending { struct fseek_pending *fwd, *bkwd; FILE *fp; };
+static struct { struct fseek_pending *avail, *active; void (*action)(FILE *); }
+fseek_handler = { NULL, NULL, fseek_handler_init };
+
+/* The fseek() handler function redirector implementation.
+ */
+static __attribute__((__noinline__))
+void *fseek_handler_trap_pending( FILE *fp )
+{ /* Local helper, to check for any pending fseek trap associated
+   * with "fp"; used from multiple locations, so we prefer to deny
+   * GCC any choice to in-line it.
+   *
+   * Note that, if "fseek_handler.active" is NULL, there are no
+   * pending stream traps, so there is nothing to check...
+   */
+  if( fseek_handler.active != NULL )
+  { /* ...but when there is at least one active trap...
+     */
+    struct fseek_pending *trap = fseek_handler.active;
+    do { /* ...we walk the queue of active traps, until we find
+         * (and immediately return) one associated with "fp", or
+         * we have examined all entries in the circular queue.
+         */
+        if( trap->fp == fp ) return trap;
+       } while( (trap = trap->fwd) != fseek_handler.active );
+  }
+  /* If we get to here, there is no trap associated with "fp".
+   */
+  return NULL;
+}
+
+/* On WinNT, the fseek() handler is required to take no action; by
+ * installing the following "no-op" handler, on first-time call, we
+ * reduce the fseek() and fwrite() wrappers to operate as simple
+ * pass-through filters...
+ */
+static void fseek_handler_nop( FILE *fp __attribute__((__unused__)) ){}
+
+/* ...whereas, on Win9x, we install this active fseek() handler.
+ */
+static void fseek_handler_set_trap( FILE *fp )
+{ /* This records fseek() requests, on a per-stream basis, such that
+   * any subsequent fwrite() request can apply corrective action, to
+   * ensure that any "holes" in the file stream are properly filled
+   * with zeros, in the event that the fseek() has moved the file
+   * pointer beyond EOF; however, we never assign more than one
+   * active trap per stream.
+   */
+  if( fseek_handler_trap_pending( fp ) == NULL )
+  { /* There is no active trap currently associated with "fp"; take
+     * an unused trap record from the "avail" queue...
+     */
+    struct fseek_pending *avail;
+    if( (avail = fseek_handler.avail) == NULL )
+    { /* ...creating a new block of eight such records, if none are
+       * currently available...
+       */
+      int avail_index = 8;
+      avail = malloc( 8 * sizeof (struct fseek_pending) );
+
+      /* ...and linking them into a linear queue.
+       */
+      avail->fwd = avail->bkwd = NULL;
+      while( --avail_index > 0 ) insque( avail + avail_index, avail );
+    }
+    /* The taken record is popped from the front of the queue; thus
+     * we must follow its forward link, to update the front-of-queue
+     * pointer to the next available unused trap record.
+     */
+    remque( avail ); fseek_handler.avail = avail->fwd;
+
+    /* Now, we must insert the "avail" record we've just acquired
+     * into the "active" queue; this is managed as a circular queue,
+     * so, if it is currently empty...
+     */
+    if( fseek_handler.active == NULL )
+      /* ...then we must assign this new record as its sole entry,
+       * with both links referring to itself...
+       */
+      fseek_handler.active = avail->fwd = avail->bkwd = avail;
+
+    /* ...otherwise, the POSIX.1 insque() API will take care of it.
+     */
+    else insque( avail, fseek_handler.active );
+
+    /* Finally, we must associate this new trap record with "fp".
+     */
+    avail->fp = fp;
+  }
+}
+
+static void fseek_handler_init( FILE *fp )
+{ /* fseek() handler initialization routine; invoked only the first time
+   * that the fseek() handler itself is invoked, it checks whether we are
+   * running on Win9x, and if not...
+   */
+  if( (_osver & 0x8000) == 0 )
+    /* ...it installs a no-op handler for future calls, (since WinNT
+     * doesn't require any further handling...
+     */
+    fseek_handler.action = fseek_handler_nop;
+
+  else
+  { /* ...otherwise, it installs the Win9x specific handler, which
+     * traps seek requests to enable corrective action, which may be
+     * required in a subsequent fwrite() call...
+     */
+    fseek_handler.action = fseek_handler_set_trap;
+
+    /* ...and immediately sets a trap for this first-time call.
+     */
+    fseek_handler_set_trap( fp );
+  }
+}
+
+/* Public API entry to the Win9x function redirector for the system
+ * fseek() APIs; implemented in terms of fseeko64(), it is suitable as
+ * a transparent wrapper for any of the fseek()-alike functions.
+ */
+int __mingw_fseek( FILE *fp, __off64_t offset, int whence )
+{ fseek_handler.action( fp ); return fseeko64( fp, offset, whence ); }
+
+static __off64_t fseek_handler_reset( struct fseek_pending *trap )
+{ /* Bridging function, called exclusively by __mingw_fwrite(), to disarm
+   * any fseek trap which it has identified as being active and associated
+   * with its target stream; since it is called only from one location, we
+   * may safely allow GCC to in-line it.
+   */
+  if( trap == fseek_handler.active )
+  { /* The active trap is currently the lead entry, in the active trap
+     * queue, then we must move the lead entry onward; if it continues
+     * to refer to the same trap, then we must clear the queue...
+     */
+    if( fseek_handler.active->fwd == trap ) fseek_handler.active = NULL;
+
+    /* ...otherwise, we simply remove the trap entry from the queue...
+     */
+    else remque( trap );
+  }
+  /* ...and likewise, if the trap entry is not the queue's lead entry.
+   */
+  else remque( trap );
+
+  /* Having removed the trap from the "active" queue, we must return it
+   * to the "avail" queue...
+   */
+  insque( trap, fseek_handler.avail );
+
+  /* ...and, if that queue was previously empty, update its lead entry
+   * reference pointer to match.
+   */
+  if( fseek_handler.avail == NULL ) fseek_handler.avail = trap;
+
+  /* Finally, tell fwrite() where it must begin data transfer, after it
+   * has completed any Win9x corrective action which may be required.
+   */
+  return __mingw_ftelli64( trap->fp );
+}
+
+size_t __mingw_fwrite( const void *buffer, size_t size, size_t count, FILE *fp )
+{ /* A wrapper around the system fwrite() API; it ensures that padding
+   * zero bytes are inserted, following EOF, when fwrite() is called on
+   * Win9x, after any seek request which moves the file pointer to any
+   * position which lies beyond the existing EOF.
+   */
+  struct fseek_pending *trap;
+  if( (trap = fseek_handler_trap_pending( fp )) != NULL )
+  { /* The fseek handler has determined that we are running on Win9x,
+     * and that this fwrite operation was preceded by a seek; we don't
+     * yet know if that seek has moved the position beyond the current
+     * end of file, in which case Win9x may leave random garbage after
+     * EOF, in the intervening space, (where ISO-C requires the effect
+     * of zero bytes); check for this anomaly now.
+     */
+    __off64_t eof_pos, fwrite_pos = fseek_handler_reset( trap );
+    if( fwrite_pos > (eof_pos = _lseeki64( fileno( fp ), 0LL, SEEK_END )) )
+    { /* The original seek request HAD moved the fwrite position to
+       * some point beyond EOF!  We've now moved it back to EOF, so
+       * prepare to fill with zeros, to pad out the file until we
+       * return to the original seek position.
+       */
+      char zero_bytes[BUFSIZ] = {'\0'};
+      __int64 fill_len = fwrite_pos - eof_pos;
+
+      /* Emit the requisite number of padding zeros, in blocks of
+       * no more than BUFSIZ bytes...
+       */
+      while( fill_len > 0LL )
+      { size_t len = (fill_len > BUFSIZ) ? BUFSIZ : fill_len;
+       if( fwrite( zero_bytes, 1, len, fp ) != len )
+       { /* ...but, if any block falls short of its expected size,
+          * then an error has occurred; attempt to restore to the
+          * original seek position, and abort the fwrite request,
+          * having written NONE of its requested data.
+          */
+         __mingw_fseeki64( fp, fwrite_pos, SEEK_SET );
+         return 0;
+       }
+       /* A padding block has been successfully written; adjust
+        * the residual padding length, to account for it.
+        */
+       fill_len -= len;
+      }
+    }
+    else
+      /* The preceding seek was not beyond end of file, so there is no
+       * danger of leaving random garbage, but our check has moved the
+       * fwrite position to end of file; move it back to the position
+       * set by the original seek request.
+       */
+      __mingw_fseeki64( fp, fwrite_pos, SEEK_SET );
+  }
+  /* Ultimately, complete the original fwrite request, at the expected
+   * position within the output file.
+   */
+  return fwrite( buffer, size, count, fp );
+}
+
+/* $RCSfile$: end of file */