瀏覽代碼

update NFD to https://github.com/mlabbe/nativefiledialog/commit/67345b80ebb429ecc2aeda94c478b3bcc5f7888e

AzaezelX 2 年之前
父節點
當前提交
166128dd73

+ 27 - 14
Engine/lib/nativeFileDialogs/README.md

@@ -47,16 +47,20 @@ int main( void )
 }
 ```
 
-See [NFD.h](src/include/nfd.h) for more options.
+See self-documenting API [NFD.h](src/include/nfd.h) for more options.
 
 # Screenshots #
 
-![Windows 8 rendering an IFileOpenDialog](screens/open_win.png?raw=true)
-![GTK3 on Linux](screens/open_gtk3.png?raw=true)
-![Cocoa on Yosemite](screens/open_cocoa.png?raw=true)
+![Windows rendering a dialog](screens/open_win.png?raw=true)
+![GTK3 on Linux rendering a dialog](screens/open_gtk3.png?raw=true)
+![Cocoa on MacOS rendering a dialog](screens/open_cocoa.png?raw=true)
 
 ## Changelog ##
 
+ - **Major** version increments denote API or ABI departure.
+ - **Minor** version increments denote build or trivial departures.
+ - **Micro** version increments just recompile and drop-in.
+
 release | what's new                  | date
 --------|-----------------------------|---------
 1.0.0   | initial                     | oct 2014
@@ -64,11 +68,15 @@ release | what's new                  | date
 1.1.1   | mingw support, build fixes  | aug 2016
 1.1.2   | test_pickfolder() added     | aug 2016
 1.1.3   | zenity linux backend added  | nov 2017
-1.1.3   | fix char type in decls      | nov 2017
+<i></i> | fix char type in decls      | nov 2017
 1.1.4   | fix win32 memleaks          | dec 2018
-1.1.4   | improve win32 errorhandling | dec 2018
-1.1.4   | macos fix focus bug         | dec 2018
-   
+<i></i> | improve win32 errorhandling | dec 2018
+<i></i> | macos fix focus bug         | dec 2018
+1.1.5   | win32 fix com reinitialize  | aug 2019
+1.1.6   | fix osx filter bug          | aug 2019
+<i></i> | remove deprecated scons     | aug 2019
+<i></i> | fix mingw compilation       | aug 2019
+<i></i> | -Wextra warning cleanup     | aug 2019
 
 ## Building ##
 
@@ -76,13 +84,13 @@ NFD uses [Premake5](https://premake.github.io/download.html) generated Makefiles
 
 If you need to run Premake5 directly, further [build documentation](docs/build.md) is available.
 
-Previously, NFD used SCons to build.  It still works, but is now deprecated; updates to it are discouraged.  Opt to use the native build system where possible.
+Previously, NFD used SCons to build.  As of 1.1.6, SCons support has been removed entirely.
 
 `nfd.a` will be built for release builds, and `nfd_d.a` will be built for debug builds.
 
 ### Makefiles ###
 
-The makefile offers five options, with `release_x64` as the default.
+The makefile offers up to four options, with `release_x64` as the default.
 
     make config=release_x86
     make config=release_x64
@@ -96,6 +104,7 @@ The makefile offers five options, with `release_x64` as the default.
  3. Add `build/<debug|release>/<arch>` to the library search path.
 
 #### Linux GTK ####
+
 `apt-get libgtk-3-dev` installs the gtk dependency for library compilation.
 
 On Linux, you have the option of compiling and linking against GTK.  If you use it, the recommended way to compile is to include the arguments of `pkg-config --cflags --libs gtk+-3.0`.
@@ -105,16 +114,18 @@ On Linux, you have the option of compiling and linking against GTK.  If you use
 Alternatively, you can use the Zenity backend by running the Makefile in `build/gmake_linux_zenity`.  Zenity runs the dialog in its own address space, but requires the user to have Zenity correctly installed and configured on their system.
 
 #### MacOS ####
+
 On Mac OS, add `AppKit` to the list of frameworks.
 
 #### Windows ####
-On Windows, ensure you are building against `comctl32.lib`.
+
+On Windows, ensure you are linking against `comctl32.lib`.
 
 ## Usage ##
 
 See `NFD.h` for API calls.  See `tests/*.c` for example code.
 
-After compiling, `build/bin` contains compiled test programs.
+After compiling, `build/bin` contains compiled test programs.  The appropriate subdirectory under `build/lib` contains the built library.
 
 ## File Filter Syntax ##
 
@@ -141,14 +152,16 @@ See [test_opendialogmultiple.c](test/test_opendialogmultiple.c).
 
 # Known Limitations #
 
-I accept quality code patches, or will resolve these and other matters through support.  See [submitting pull requests](docs/submitting_pull_requests.md) for details.
+I accept quality code patches, or will resolve these and other matters through support.  See [contributing](docs/contributing.md) for details.
 
  - No support for Windows XP's legacy dialogs such as `GetOpenFileName`.
  - No support for file filter names -- ex: "Image Files" (*.png, *.jpg).  Nameless filters are supported, however.
+ - GTK Zenity implementation's process exec error handling does not gracefully handle numerous error cases, choosing to abort rather than cleanup and return.
+ - GTK 3 spams one warning per dialog created.
 
 # Copyright and Credit #
 
-Copyright &copy; 2014-2017 [Frogtoss Games](http://www.frogtoss.com), Inc.
+Copyright &copy; 2014-2019 [Frogtoss Games](http://www.frogtoss.com), Inc.
 File [LICENSE](LICENSE) covers all files in this repo.
 
 Native File Dialog by Michael Labbe

+ 3 - 1
Engine/lib/nativeFileDialogs/nfd_cocoa.m

@@ -22,8 +22,10 @@ static NSArray *BuildAllowedFileTypes( const char *filterList )
     {
         if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' )
         {
-            ++p_typebuf;
+            if (filterList[i] != '\0')
+                ++p_typebuf;
             *p_typebuf = '\0';
+
             NSString *thisType = [NSString stringWithUTF8String: typebuf];
             [buildFilterList addObject:thisType];
             p_typebuf = typebuf;

+ 3 - 3
Engine/lib/nativeFileDialogs/nfd_common.c

@@ -92,13 +92,13 @@ int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy )
 
 
 /* adapted from microutf8 */
-size_t NFDi_UTF8_Strlen( const nfdchar_t *str )
+int32_t NFDi_UTF8_Strlen( const nfdchar_t *str )
 {
 	/* This function doesn't properly check validity of UTF-8 character 
 	sequence, it is supposed to use only with valid UTF-8 strings. */
     
-	size_t character_count = 0;
-	size_t i = 0; /* Counter used to iterate over string. */
+	int32_t character_count = 0;
+	int32_t i = 0; /* Counter used to iterate over string. */
 	nfdchar_t maybe_bom[4];
 	
 	/* If there is UTF-8 BOM ignore it. */

+ 3 - 1
Engine/lib/nativeFileDialogs/nfd_common.h

@@ -12,6 +12,8 @@
 
 #include "nfd.h"
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -26,7 +28,7 @@ void  *NFDi_Malloc( size_t bytes );
 void   NFDi_Free( void *ptr );
 void   NFDi_SetError( const char *msg );
 int    NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy );
-size_t NFDi_UTF8_Strlen( const nfdchar_t *str );
+int32_t NFDi_UTF8_Strlen( const nfdchar_t *str );
 int    NFDi_IsFilterSegmentChar( char ch );
     
 #ifdef __cplusplus

+ 126 - 144
Engine/lib/nativeFileDialogs/nfd_win.cpp

@@ -4,14 +4,6 @@
   http://www.frogtoss.com/labs
  */
 
-#define _CRTDBG_MAP_ALLOC  
-#include <stdlib.h>  
-#include <crtdbg.h>  
-
-/* only locally define UNICODE in this compilation unit */
-#ifndef UNICODE
-#define UNICODE
-#endif
 
 #ifdef __MINGW32__
 // Explicitly setting NTDDI version, this is necessary for the MinGW compiler
@@ -19,6 +11,15 @@
 #define _WIN32_WINNT _WIN32_WINNT_VISTA
 #endif
 
+#define _CRTDBG_MAP_ALLOC
+#include <stdlib.h>
+#include <crtdbg.h>
+
+/* only locally define UNICODE in this compilation unit */
+#ifndef UNICODE
+#define UNICODE
+#endif
+
 #include <wchar.h>
 #include <stdio.h>
 #include <assert.h>
@@ -27,6 +28,33 @@
 #include "nfd_common.h"
 
 
+#define COM_INITFLAGS ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE
+
+static BOOL COMIsInitialized(HRESULT coResult)
+{
+    if (coResult == RPC_E_CHANGED_MODE)
+    {
+        // If COM was previously initialized with different init flags,
+        // NFD still needs to operate. Eat this warning.
+        return TRUE;
+    }
+
+    return SUCCEEDED(coResult);
+}
+
+static HRESULT COMInit(void)
+{
+    return ::CoInitializeEx(NULL, COM_INITFLAGS);
+}
+
+static void COMUninit(HRESULT coResult)
+{
+    // do not uninitialize if RPC_E_CHANGED_MODE occurred -- this
+    // case does not refcount COM.
+    if (SUCCEEDED(coResult))
+        ::CoUninitialize();
+}
+
 // allocs the space in outPath -- call free()
 static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr )
 {
@@ -58,11 +86,9 @@ static size_t GetUTF8ByteCountForWChar( const wchar_t *str )
     return bytesNeeded+1;
 }
 
-// write to outPtr -- no free() necessary.  No memory stomp tests are done -- they must be done
-// before entering this function.
+// write to outPtr -- no free() necessary.
 static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr )
 {
-    int inStrCharacterCount = static_cast<int>(wcslen(inStr));
     int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr ));
 
     /* invocation copies null term */
@@ -120,7 +146,8 @@ static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t spec
 
     char extWildcard[NFD_MAX_STRLEN];
     int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext );
-    assert( bytesWritten == strlen(ext)+2 );
+    assert( bytesWritten == (int)(strlen(ext)+2) );
+    _NFD_UNUSED(bytesWritten);
     
     strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 );
 
@@ -129,7 +156,6 @@ static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t spec
 
 static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList )
 {
-    const wchar_t EMPTY_WSTR[] = L"";
     const wchar_t WILDCARD[] = L"*.*";
 
     if ( !filterList || strlen(filterList) == 0 )
@@ -167,7 +193,6 @@ static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char
     p_filterList = filterList;
     char typebuf[NFD_MAX_STRLEN] = {0};  /* one per comma or semicolon */
     char *p_typebuf = typebuf;
-    char filterName[NFD_MAX_STRLEN] = {0};
 
     char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */
 
@@ -364,22 +389,17 @@ nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,
                             nfdchar_t **outPath )
 {
     nfdresult_t nfdResult = NFD_ERROR;
-    
-    // Init COM library.
-    HRESULT coResult = ::CoInitializeEx(NULL,
-                                        ::COINIT_APARTMENTTHREADED |
-                                        ::COINIT_DISABLE_OLE1DDE );
-
-    ::IFileOpenDialog *fileOpenDialog(NULL);
 
-    if ( !SUCCEEDED(coResult))
-    {
-        fileOpenDialog = NULL;
+    
+    HRESULT coResult = COMInit();
+    if (!COMIsInitialized(coResult))
+    {        
         NFDi_SetError("Could not initialize COM.");
-        goto end;
+        return nfdResult;
     }
 
     // Create dialog
+    ::IFileOpenDialog *fileOpenDialog(NULL);    
     HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
                                         CLSCTX_ALL, ::IID_IFileOpenDialog,
                                         reinterpret_cast<void**>(&fileOpenDialog) );
@@ -449,8 +469,7 @@ end:
     if (fileOpenDialog)
         fileOpenDialog->Release();
 
-    if (SUCCEEDED(coResult))
-        ::CoUninitialize();
+    COMUninit(coResult);
     
     return nfdResult;
 }
@@ -460,20 +479,17 @@ nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
                                     nfdpathset_t *outPaths )
 {
     nfdresult_t nfdResult = NFD_ERROR;
-    
-    // Init COM library.
-    HRESULT coResult = ::CoInitializeEx(NULL,
-                                        ::COINIT_APARTMENTTHREADED |
-                                        ::COINIT_DISABLE_OLE1DDE );
-    if ( !SUCCEEDED(coResult))
+
+
+    HRESULT coResult = COMInit();
+    if (!COMIsInitialized(coResult))
     {
-        NFDi_SetError("Could not initialize COM.");
-        return NFD_ERROR;
+        NFDi_SetError("Could not initialize COM.");        
+        return nfdResult;
     }
 
-    ::IFileOpenDialog *fileOpenDialog(NULL);
-
     // Create dialog
+    ::IFileOpenDialog *fileOpenDialog(NULL);    
     HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
                                         CLSCTX_ALL, ::IID_IFileOpenDialog,
                                         reinterpret_cast<void**>(&fileOpenDialog) );
@@ -547,8 +563,7 @@ end:
     if ( fileOpenDialog )
         fileOpenDialog->Release();
 
-    if (SUCCEEDED(coResult))
-        ::CoUninitialize();
+    COMUninit(coResult);
     
     return nfdResult;
 }
@@ -558,20 +573,16 @@ nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
                             nfdchar_t **outPath )
 {
     nfdresult_t nfdResult = NFD_ERROR;
-    
-    // Init COM library.
-    HRESULT coResult = ::CoInitializeEx(NULL,
-                                        ::COINIT_APARTMENTTHREADED |
-                                        ::COINIT_DISABLE_OLE1DDE );
-    if ( !SUCCEEDED(coResult))
+
+    HRESULT coResult = COMInit();
+    if (!COMIsInitialized(coResult))
     {
         NFDi_SetError("Could not initialize COM.");
-        return NFD_ERROR;
+        return nfdResult;        
     }
-
-    ::IFileSaveDialog *fileSaveDialog(NULL);
-
+    
     // Create dialog
+    ::IFileSaveDialog *fileSaveDialog(NULL);    
     HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL,
                                         CLSCTX_ALL, ::IID_IFileSaveDialog,
                                         reinterpret_cast<void**>(&fileSaveDialog) );
@@ -642,139 +653,110 @@ end:
     if ( fileSaveDialog )
         fileSaveDialog->Release();
 
-    if (SUCCEEDED(coResult))
-        ::CoUninitialize();
-        
+    COMUninit(coResult);
+    
     return nfdResult;
 }
 
-class AutoCoInit
-{
-public:
-    AutoCoInit()
-    {
-        mResult = ::CoInitializeEx(NULL,
-            ::COINIT_APARTMENTTHREADED |
-            ::COINIT_DISABLE_OLE1DDE);
-    }
 
-    ~AutoCoInit()
-    {
-        if (SUCCEEDED(mResult))
-        {
-            ::CoUninitialize();
-        }
-    }
-
-    HRESULT Result() const { return mResult; }
-private:
-    HRESULT mResult;
-};
-
-// VS2010 hasn't got a copy of CComPtr - this was first added in the 2003 SDK, so we make our own small CComPtr instead
-template<class T>
-class ComPtr
-{
-public:
-    ComPtr() : mPtr(NULL) { }
-    ~ComPtr()
-    {
-        if (mPtr)
-        {
-            mPtr->Release();
-        }
-    }
-
-    T* Ptr() const { return mPtr; }
-    T** operator&() { return &mPtr; }
-    T* operator->() const { return mPtr; }
-private:
-    // Don't allow copy or assignment
-    ComPtr(const ComPtr&);
-    ComPtr& operator = (const ComPtr&) const;
-    T* mPtr;
-};
 
 nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
     nfdchar_t **outPath)
 {
-    // Init COM
-    AutoCoInit autoCoInit;
-    if (!SUCCEEDED(autoCoInit.Result()))
+    nfdresult_t nfdResult = NFD_ERROR;
+    DWORD dwOptions = 0;
+
+    HRESULT coResult = COMInit();
+    if (!COMIsInitialized(coResult))
     {
         NFDi_SetError("CoInitializeEx failed.");
-        return NFD_ERROR;
+        return nfdResult;
     }
 
-    // Create the file dialog COM object
-    ComPtr<IFileDialog> pFileDialog;
-    if (!SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog,
-                                    NULL,
-                                    CLSCTX_ALL,
-                                    IID_PPV_ARGS(&pFileDialog))))
-    {
+    // Create dialog
+    ::IFileOpenDialog *fileDialog(NULL);
+    HRESULT result = CoCreateInstance(CLSID_FileOpenDialog,
+                                      NULL,
+                                      CLSCTX_ALL,
+                                      IID_PPV_ARGS(&fileDialog));
+    if ( !SUCCEEDED(result) )
+    {        
         NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed.");
-        return NFD_ERROR;
+        goto end;
     }
 
     // Set the default path
-    if (SetDefaultPath(pFileDialog.Ptr(), defaultPath) != NFD_OKAY)
+    if (SetDefaultPath(fileDialog, defaultPath) != NFD_OKAY)
     {
         NFDi_SetError("SetDefaultPath failed.");
-        return NFD_ERROR;
+        goto end;
     }
 
     // Get the dialogs options
-    DWORD dwOptions = 0;
-    if (!SUCCEEDED(pFileDialog->GetOptions(&dwOptions)))
+    if (!SUCCEEDED(fileDialog->GetOptions(&dwOptions)))
     {
         NFDi_SetError("GetOptions for IFileDialog failed.");
-        return NFD_ERROR;
+        goto end;
     }
 
     // Add in FOS_PICKFOLDERS which hides files and only allows selection of folders
-    if (!SUCCEEDED(pFileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS)))
+    if (!SUCCEEDED(fileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS)))
     {
         NFDi_SetError("SetOptions for IFileDialog failed.");
-        return NFD_ERROR;
+        goto end;
     }
 
     // Show the dialog to the user
-    const HRESULT result = pFileDialog->Show(NULL);
-    if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED))
+    result = fileDialog->Show(NULL);
+    if ( SUCCEEDED(result) )
+    {
+        // Get the folder name
+        ::IShellItem *shellItem(NULL);
+
+        result = fileDialog->GetResult(&shellItem);
+        if ( !SUCCEEDED(result) )
+        {
+            NFDi_SetError("Could not get file path for selected.");
+            shellItem->Release();
+            goto end;
+        }
+
+        wchar_t *path = NULL;
+        result = shellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path);
+        if ( !SUCCEEDED(result) )
+        {
+            NFDi_SetError("GetDisplayName for IShellItem failed.");            
+            shellItem->Release();
+            goto end;
+        }
+
+        CopyWCharToNFDChar(path, outPath);
+        CoTaskMemFree(path);
+        if ( !*outPath )
+        {
+            shellItem->Release();
+            goto end;
+        }
+
+        nfdResult = NFD_OKAY;
+        shellItem->Release();
+    }
+    else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
     {
-        return NFD_CANCEL;
+        nfdResult = NFD_CANCEL;
     }
-    else if (!SUCCEEDED(result))
+    else
     {
         NFDi_SetError("Show for IFileDialog failed.");
-        return NFD_ERROR;
+        nfdResult = NFD_ERROR;
     }
 
-    // Get the shell item result
-    ComPtr<IShellItem> pShellItem;
-    if (!SUCCEEDED(pFileDialog->GetResult(&pShellItem)))
-    {
-        NFDi_SetError("Could not get shell item from dialog.");
-        return NFD_ERROR;
-    }
+ end:
 
-    // Finally get the path
-    wchar_t *path = NULL;
-    if (!SUCCEEDED(pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path)))
-    {
-        NFDi_SetError("GetDisplayName for IShellItem failed.");
-        return NFD_ERROR;
-    }
+    if (fileDialog)
+        fileDialog->Release();
 
-    // Convert string
-    CopyWCharToNFDChar(path, outPath);
-    CoTaskMemFree(path);
-    if (!*outPath)
-    {
-        // error is malloc-based, error message would be redundant
-        return NFD_ERROR;
-    }
+    COMUninit(coResult);
 
-    return NFD_OKAY;
+    return nfdResult;
 }

+ 1 - 5
Engine/lib/nativeFileDialogs/nfd_zenity.c

@@ -92,7 +92,7 @@ static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defa
 {
     if(defaultPath != NULL)
     {
-        char* prefix = "--filename";
+        char* prefix = "--filename=";
         int len = strlen(prefix) + strlen(defaultPath) + 1;
 
         char* tmp = (char*) calloc(len, 1);
@@ -133,10 +133,6 @@ static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defa
 
 static nfdresult_t AllocPathSet(char* zenityList, nfdpathset_t *pathSet )
 {
-    size_t bufSize = 0;
-    nfdchar_t *p_buf;
-    size_t count = 0;
-    
     assert(zenityList);
     assert(pathSet);
     

+ 7 - 3
Engine/lib/nativeFileDialogs/simple_exec.h

@@ -28,7 +28,7 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
 #include <stdarg.h>
 #include <fcntl.h>
 
-#define release_assert(x) do { int __release_assert_tmp__ = (x); assert(__release_assert_tmp__); } while(0)
+#define release_assert(exp) { if (!(exp)) { abort(); } }
 
 enum PIPE_FILE_DESCRIPTORS
 {
@@ -70,6 +70,7 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
         case -1:
         {
             release_assert(0 && "Fork failed");
+            break;
         }
 
         case 0: // child
@@ -96,7 +97,8 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
             execvp(command, allArgs);
 
             char err = 1;
-            write(errPipe[WRITE_FD], &err, 1);
+            ssize_t result = write(errPipe[WRITE_FD], &err, 1);
+            release_assert(result != -1);
             
             close(errPipe[WRITE_FD]);
             close(parentToChild[READ_FD]);
@@ -128,7 +130,8 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
                         release_assert(close(childToParent[READ_FD]) == 0);
 
                         char errChar = 0;
-                        read(errPipe[READ_FD], &errChar, 1);
+                        ssize_t result = read(errPipe[READ_FD], &errChar, 1);
+                        release_assert(result != -1);
                         close(errPipe[READ_FD]);
 
                         if(errChar)
@@ -156,6 +159,7 @@ int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int in
                     case -1:
                     {
                         release_assert(0 && "read() failed");
+                        break;
                     }
 
                     default: