Bladeren bron

iostream: Properly support the "x" mode for SDL_IOFromFile()

The "x" mode for `fopen()` (open file only if it doesn't exist) used to
be a glibc-exclusive extension, but was later standardized in C11, and
is now also implemented as part of every other widely-used libc:

	* musl: https://git.musl-libc.org/cgit/musl/tree/src/stdio/__fmodeflags.c?id=0ccaf0572e9cccda2cced0f7ee659af4c1c6679a
	* Android Bionic / OpenBSD: https://android.googlesource.com/platform/bionic/+/731631f300090436d7f5df80d50b6275c8c60a93/libc/upstream-openbsd/lib/libc/stdio/flags.c#86
	* Apple / FreeBSD: https://github.com/apple-oss-distributions/Libc/blob/63976b830a836a22649b806fe62e8614fe3e5555/stdio/FreeBSD/flags.c#L91-L92

As a result, "x" has already been working on all our automatically
tested platforms that implement `SDL_IOFromFile()` via `fopen()`. So
all we'd be missing for proper support is a Windows implementation
using `CREATE_NEW`, and the documentation that this mode exists and is
intended to work.
nmlgc 2 dagen geleden
bovenliggende
commit
8df057fafc
3 gewijzigde bestanden met toevoegingen van 18 en 3 verwijderingen
  1. 4 0
      include/SDL3/SDL_iostream.h
  2. 6 0
      src/io/SDL_iostream.c
  3. 8 3
      test/testautomation_iostream.c

+ 4 - 0
include/SDL3/SDL_iostream.h

@@ -203,6 +203,8 @@ typedef struct SDL_IOStream SDL_IOStream;
  * - "w": Create an empty file for writing. If a file with the same name
  *   already exists its content is erased and the file is treated as a new
  *   empty file.
+ * - "wx": Create an empty file for writing. If a file with the same name
+ *   already exists, the call fails.
  * - "a": Append to a file. Writing operations append data at the end of the
  *   file. The file is created if it does not exist.
  * - "r+": Open a file for update both reading and writing. The file must
@@ -210,6 +212,8 @@ typedef struct SDL_IOStream SDL_IOStream;
  * - "w+": Create an empty file for both reading and writing. If a file with
  *   the same name already exists its content is erased and the file is
  *   treated as a new empty file.
+ * - "w+x": Create an empty file for both reading and writing. If a file with
+ *    the same name already exists, the call fails.
  * - "a+": Open a file for reading and appending. All writing operations are
  *   performed at the end of the file, protecting the previous content to be
  *   overwritten. You can reposition (fseek, rewind) the internal pointer to

+ 6 - 0
src/io/SDL_iostream.c

@@ -94,10 +94,12 @@ static HANDLE SDLCALL windows_file_open(const char *filename, const char *mode)
 
     // "r" = reading, file must exist
     // "w" = writing, truncate existing, file may not exist
+    // "wx"= writing, file must not exist
     // "r+"= reading or writing, file must exist
     // "a" = writing, append file may not exist
     // "a+"= append + read, file may not exist
     // "w+" = read, write, truncate. file may not exist
+    // "w+x"= read, write, file must not exist
 
     must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
     truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
@@ -105,6 +107,10 @@ static HANDLE SDLCALL windows_file_open(const char *filename, const char *mode)
     a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
     w_right = (a_mode || SDL_strchr(mode, '+') || truncate) ? GENERIC_WRITE : 0;
 
+    if (truncate && (SDL_strchr(mode, 'x') != NULL)) {
+        truncate = CREATE_NEW;
+    }
+
     if (!r_right && !w_right) {
         return INVALID_HANDLE_VALUE; // inconsistent mode
     }

+ 8 - 3
test/testautomation_iostream.c

@@ -615,9 +615,9 @@ static int SDLCALL iostrm_testFileWrite(void *arg)
     int result;
 
     /* Write test. */
-    rw = SDL_IOFromFile(IOStreamWriteTestFilename, "w+");
-    SDLTest_AssertPass("Call to SDL_IOFromFile(..,\"w+\") succeeded");
-    SDLTest_AssertCheck(rw != NULL, "Verify opening file with SDL_IOFromFile in write mode does not return NULL");
+    rw = SDL_IOFromFile(IOStreamWriteTestFilename, "w+x");
+    SDLTest_AssertPass("Call to SDL_IOFromFile(..,\"w+x\") succeeded");
+    SDLTest_AssertCheck(rw != NULL, "Verify opening file with SDL_IOFromFile in exclusive write mode does not return NULL");
 
     /* Bail out if NULL */
     if (rw == NULL) {
@@ -632,6 +632,11 @@ static int SDLCALL iostrm_testFileWrite(void *arg)
     SDLTest_AssertPass("Call to SDL_CloseIO() succeeded");
     SDLTest_AssertCheck(result == true, "Verify result value is true; got: %d", result);
 
+    /* Exclusively opening an existing file should fail. */
+    rw = SDL_IOFromFile(IOStreamWriteTestFilename, "wx");
+    SDLTest_AssertPass("Call to SDL_IOFromFile(..,\"wx\") succeeded");
+    SDLTest_AssertCheck(rw == NULL, "Verify opening existing file with SDL_IOFromFile in exclusive write mode returns NULL");
+
     return TEST_COMPLETED;
 }