diff --git a/.gitignore b/.gitignore index 282a9f29..62649a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,7 @@ dkms.conf build/ .frontmatter/ frontmatter.json -coroutine-built/ +built/ *.crt *.pem *.key diff --git a/CMakeLists.txt b/CMakeLists.txt index 1890d22d..265c80e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,22 +6,23 @@ set(C_STANDARD 89) include(CMakeDependentOption) include(GNUInstallDirs) -cmake_dependent_option(CO_BUILD_TESTS - "Build the unit tests when BUILD_TESTING is enabled and we are the root project" ON +cmake_dependent_option(BUILD_TESTING + "Build the unit tests when BUILD_TESTING is enabled and we are the root project" OFF "BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}") set(CMAKE_CONFIGURATION_TYPES=Debug;Release) -set(BUILD_DIR ${CMAKE_SOURCE_DIR}/build) +set(BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BUILD_DIR}) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/coroutine-built") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/built") option(LIBUV_BUILD_SHARED "Build shared lib" OFF) + add_subdirectory(deps/libuv) file(GLOB lib_files - ${CMAKE_SOURCE_DIR}/src/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c ) add_definitions(-DCO_SCRAPE_SIZE=64) @@ -48,7 +49,7 @@ else() endif() target_include_directories(coroutine - AFTER PUBLIC $ $) + PUBLIC $ $) if(UNIX) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -D NDEBUG -D CO_USE_VALGRIND ") @@ -61,10 +62,10 @@ if(WIN32) add_definitions("/wd4244 /wd4267 /wd4033 /wd4715") endif() -find_package(Threads REQUIRED) -target_link_libraries(coroutine ${CMAKE_THREAD_LIBS_INIT}) +add_subdirectory(deps/raii) +target_link_libraries(coroutine PUBLIC raii) -if(CO_BUILD_TESTS) +if(BUILD_TESTING) add_subdirectory(examples) endif() diff --git a/README.md b/README.md index 8b96acb3..9fc7633b 100644 --- a/README.md +++ b/README.md @@ -735,14 +735,14 @@ int co_main(int argc, char **argv) ## Installation -The build system uses **cmake**, that produces _single_ **static** library stored under `coroutine-built`, and the complete `include` folder is needed. +The build system uses **cmake**, that produces _single_ **static** library stored under `built`, and the complete `include` folder is needed. **Linux** ```shell mkdir build cd build -cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -DBUILD_TESTING=ON/OFF # use to build files examples folder +cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -DBUILD_TESTING=ON # use to build files in examples folder cmake --build . ``` @@ -751,7 +751,7 @@ cmake --build . ```shell mkdir build cd build -cmake .. -D BUILD_TESTING=ON/OFF # use to build files examples folder +cmake .. -D BUILD_TESTING=ON # use to build files in examples folder cmake --build . --config Debug/Release ``` diff --git a/deps/raii/.cl_32.bat b/deps/raii/.cl_32.bat new file mode 100644 index 00000000..7aa93bf5 --- /dev/null +++ b/deps/raii/.cl_32.bat @@ -0,0 +1,12 @@ +@echo off +if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools" ( + %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat" +) else if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community" ( + %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" +) else if EXIST "C:\Program Files\Microsoft Visual Studio\2022\BuildTools" ( + %comspec% /k "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars32.bat" +) else if EXIST "C:\Program Files\Microsoft Visual Studio\2022\Community" ( + %comspec% /k "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" +) else if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools" ( + %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat" +) diff --git a/deps/raii/.cl_64.bat b/deps/raii/.cl_64.bat new file mode 100644 index 00000000..7eecdb6a --- /dev/null +++ b/deps/raii/.cl_64.bat @@ -0,0 +1,12 @@ +@echo off +if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools" ( + %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat" +) else if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community" ( + %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" +) else if EXIST "C:\Program Files\Microsoft Visual Studio\2022\BuildTools" ( + %comspec% /k "C:\Program Files\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" +) else if EXIST "C:\Program Files\Microsoft Visual Studio\2022\Community" ( + %comspec% /k "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" +) else if EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools" ( + %comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" +) diff --git a/deps/raii/.gitattributes b/deps/raii/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/deps/raii/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/deps/raii/.github/workflows/ci.yml b/deps/raii/.github/workflows/ci.yml new file mode 100644 index 00000000..439153d5 --- /dev/null +++ b/deps/raii/.github/workflows/ci.yml @@ -0,0 +1,81 @@ +name: Windows & Linux & macOS +on: + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + build: + name: Linux ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: amd64 + flags: -m64 + - target: x86 + flags: -m32 + steps: + - uses: actions/checkout@v4 + - name: Prepare + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update -q -y + sudo apt-get install -y gcc-multilib g++-multilib valgrind libc6-dbg libc6-dbg:i386 + - name: Configure & build + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DCMAKE_C_FLAGS=${{ matrix.flags }} .. + cmake --build . + - name: Run test examples + run: | + cd build + ./test-defer + ./test-exceptions + + build-windows: + name: Windows (${{ matrix.arch }}) + runs-on: windows-2019 + strategy: + fail-fast: false + matrix: + arch: [x64, Win32] + steps: + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.arch }} + - uses: actions/checkout@v4 + - name: Configure & build + run: | + mkdir build + cd build + cmake .. -D BUILD_TESTING=ON -A ${{ matrix.arch }} + cmake --build . --config Debug + - name: Run test examples + shell: cmd + run: | + cd build\Debug + .\test-defer.exe + .\test-exceptions.exe + + build-macos: + name: macOS + runs-on: macos-11 + steps: + - uses: actions/checkout@v4 + - name: Setup + run: | + brew install cmake + - name: Configure & build + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON .. + cmake --build . + - name: Run test examples + run: | + cd build + ./test-defer + ./test-exceptions diff --git a/deps/raii/.github/workflows/jekyll-gh-pages.yml b/deps/raii/.github/workflows/jekyll-gh-pages.yml new file mode 100644 index 00000000..185dec75 --- /dev/null +++ b/deps/raii/.github/workflows/jekyll-gh-pages.yml @@ -0,0 +1,53 @@ +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll with GitHub Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + paths: + - "docs/**" + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1 + with: + source: ./docs + destination: ./_site + - name: Upload artifact + uses: actions/upload-pages-artifact@v2 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/deps/raii/.gitignore b/deps/raii/.gitignore new file mode 100644 index 00000000..31c8877c --- /dev/null +++ b/deps/raii/.gitignore @@ -0,0 +1,55 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf +.vscode/settings.json +build/ +built/ diff --git a/deps/raii/CMakeLists.txt b/deps/raii/CMakeLists.txt new file mode 100644 index 00000000..1892d5a7 --- /dev/null +++ b/deps/raii/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required(VERSION 2.8...3.14) + +project(c-raii LANGUAGES C) + +set(C_STANDARD 89) + +include(CMakeDependentOption) +include(GNUInstallDirs) +message("Generated with config types: ${CMAKE_CONFIGURATION_TYPES}") + +set(CMAKE_CONFIGURATION_TYPES=Debug;Release) +set(BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BUILD_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/built") + +cmake_dependent_option(BUILD_TESTING + "Build the unit tests when BUILD_TESTING is enabled and we are the root project" OFF + "BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + +option(BUILD_SHARED_LIBS "Build the library as a shared (dynamically-linked) " OFF) + +file(GLOB raii_files + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c +) + +if(BUILD_SHARED_LIBS) + add_library(raii SHARED ${raii_files}) +else() + add_library(raii STATIC ${raii_files}) +endif() + +set_property(TARGET raii PROPERTY POSITION_INDEPENDENT_CODE True) + +target_include_directories(raii PUBLIC + $ + $ +) + +if(UNIX) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -D NDEBUG") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -fomit-frame-pointer -Wno-return-type") +endif() + +if(WIN32) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /D NDEBUG") + add_definitions(-D_CRT_SECURE_NO_DEPRECATE) + add_definitions("/wd4244 /wd4267 /wd4033 /wd4715") +endif() + +find_package(Threads) +target_link_libraries(raii PUBLIC ${CMAKE_THREAD_LIBS_INIT}) + +if (BUILD_TESTING) + enable_testing() + add_subdirectory(tests) + add_subdirectory(examples) +endif() + +set(_fmt TGZ) +if(WIN32) + set(_fmt ZIP) +endif() + +set(CPACK_GENERATOR ${_fmt}) +set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) +set(CPACK_DEB_COMPONENT_INSTALL ON) +set(CPACK_RPM_COMPONENT_INSTALL ON) +set(CPACK_NUGET_COMPONENT_INSTALL ON) +set(CPACK_WIX_COMPONENT_INSTALL ON) +set(CPACK_NSIS_MODIFY_PATH ON) +set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1) +set(CPACK_VERBATIM_VARIABLES YES) + +set(CPACK_PACKAGE_VENDOR "https://github.com/zelang-dev/c-raii") +set(CPACK_PACKAGE_VERSION 0.0.0) +include(CPack) + +set(CMAKE_INSTALL_CONFIG_NAME ${CMAKE_BUILD_TYPE}) +install(TARGETS ${raii} DESTINATION lib) +install(DIRECTORY include/ DESTINATION include) diff --git a/deps/raii/LICENSE b/deps/raii/LICENSE new file mode 100644 index 00000000..f7d2806c --- /dev/null +++ b/deps/raii/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Lawrence Stubbs + +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 and this permission notice 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 OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/raii/README.md b/deps/raii/README.md new file mode 100644 index 00000000..885ed461 --- /dev/null +++ b/deps/raii/README.md @@ -0,0 +1,410 @@ +# c-raii + +[![windows & linux & macos](https://github.com/zelang-dev/c-raii/actions/workflows/ci.yml/badge.svg)](https://github.com/zelang-dev/c-raii/actions/workflows/ci.yml) + +An robust high-level **Defer**, _RAII_ implementation for `C89`, with automatic smart memory safety. + +* [Synopsis](#synopsis) +* [Installation](#installation) +* [Contributing](#contributing) +* [License](#license) + +This library has been decoupled from [c-coroutine](https://github.com/zelang-dev/c-coroutine) to be independently developed. + +In the effort to find uniform naming of terms, various other packages was discovered [Except](https://github.com/meaning-matters/Except), [exceptions-and-raii-in-c](https://github.com/psevon/exceptions-and-raii-in-c). Choose to settle in on [A defer mechanism for C](https://gustedt.wordpress.com/2020/12/14/a-defer-mechanism-for-c/), an upcoming C standard compiler feature. It's exactly this library's working design and concepts addressed in [c-coroutine](https://github.com/zelang-dev/c-coroutine). The behavior here is as in **Go** and **Zig** _languages_, simple syntax. + +The planned implementation from [defer reference implementation for C](https://gustedt.gitlabpages.inria.fr/defer/): + + + + + + + + + + +
RAII C89Planned C11
+ +```c +// includes "cthread.h" emulated C11 threads +#include "raii.h" + +int main(int argc, char **argv) +guard { + // Panic if p == NULL + // Automatically _defer(free, p) + void *p = _malloc(25); + void *q = _calloc(1, 25); + + if (mtx_lock(&mut)==thrd_error) break; + _defer(mtx_unlock, &mut); + + // all resources acquired +} unguarded(0); +``` + + +```c +guard { + void * const p = malloc(25); + if (!p) break; + defer free(p); + + void * const q = malloc(25); + if (!q) break; + defer free(q); + + if (mtx_lock(&mut)==thrd_error) break; + defer mtx_unlock(&mut); + + // all resources acquired +} +``` +
+ +There C Standard implementation states: **The important interfaces of this tool are:** + +- `guard` prefixes a guarded block +- `defer` prefixes a defer clause +- `break` ends a guarded block and executes all its defer clauses +- `return` unwinds all guarded blocks of the current function and returns to the caller +- `exit` unwinds all defer clauses of all active function calls of the thread and exits normally +- `panic` starts global unwinding of all guarded blocks +- `recover` inside a defer clause stops a panic and provides an error code + +There example from [source](https://gitlab.inria.fr/gustedt/defer/-/blob/master/defer4.c?ref_type=heads) - **gitlab**, outlined in [C Standard WG14 meeting](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2542.pdf) - **pdf** + + + + + + + + + + +
RAII C89Planned C11
+ +```c +#include "raii.h" + +void g_print(void *ptr) { + int i = raii_value(ptr)->integer; + printf("Defer in g = %d.\n", i); +} + +void g(int i) { + char number[20]; + if (i > 3) { + puts("Panicking!"); + snprintf(number, 20, "%ld", i); + _panic(number); + } + guard { + _defer(g_print, &i); + printf("Printing in g = %d.\n", i); + g(i + 1); + } guarded; +} + +void f_print(void *na) { + puts("In defer in f"); + fflush(stdout); + if (_recover(_get_message())) { + printf("Recovered in f = %s\n", _get_message()); + fflush(stdout); + } +} + +void f() +guard { + _defer(f_print, NULL); + puts("Calling g."); + g(0); + puts("Returned normally from g."); +} guarded; + +int main(int argc, char **argv) { + f(); + puts("Returned normally from f."); + return EXIT_SUCCESS; +} +``` + + + +```c +#include +#include +#include +#include "stddefer.h" + +void g(int i) { + if (i > 3) { + puts("Panicking!"); + panic(i); + } + guard { + defer { + printf("Defer in g = %d.\n", i); + } + printf("Printing in g = %d.\n", i); + g(i+1); + } +} + +void f() { + guard { + defer { + puts("In defer in f"); + fflush(stdout); + int err = recover(); + if (err != 0) { + printf("Recovered in f = %d\n", err); + fflush(stdout); + } + } + puts("Calling g."); + g(0); + puts("Returned normally from g."); + } +} + +int main(int argc, char* argv[static argc+1]) { + f(); + puts("Returned normally from f."); + return EXIT_SUCCESS; +} +``` +
+ +_Function_ `​f`​ containing a **​defer​** statement which contains a call to the **​recover** +function. _Function_ `​f`​ invokes _function_ `​g`​ which recursively descends before _panicking_ when the +value of `​i > 3`​. Execution of `​f`​ produces the following **output**: +
+Calling g.
+Printing in g = 0.
+Printing in g = 1.
+Printing in g = 2.
+Printing in g = 3.
+Panicking!
+Defer in g = 3.
+Defer in g = 2.
+Defer in g = 1.
+Defer in g = 0.
+In defer in f
+Recovered in f = 4
+Returned normally from f.
+
+ +## Synopsis + +**C++** has a concept called [unique_ptr)](https://en.cppreference.com/w/cpp/memory/unique_ptr) where **"a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. "** + +Here too the same process is in effect through an **new** _typedef_ `unique_t` aka `memory_t` _structure_. + +### There are _3 ways_ to create an smart memory pointer. + +```c +/* Creates smart memory pointer, this object binds any additional requests to it's lifetime. +for use with `malloc_*` `calloc_*` wrapper functions to request/return raw memory. */ +C_API unique_t *unique_init(void); + +/* Creates smart memory pointer, the allocated `size` memory requested in **arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +The **arena** field will be freed with given `func`. */ +C_API memory_t *raii_malloc_full(size_t size, func_t func); + +/* Creates smart memory pointer, the allocated `size` memory requested in **arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +The **arena** field will be freed with given `func`. */ +C_API memory_t *raii_calloc_full(int count, size_t size, func_t func); +``` + +> This system use macros `RAII_MALLOC`, `RAII_FREE`, `RAII_REALLOC`, and `RAII_CALLOC`. +> If not **defined**, the default __malloc/calloc/realloc/free__ are used when expanded. + +### The following _malloc/calloc_ wrapper functions are used to get an raw memory pointer. + +```c +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be `RAII_FREE` when scope smart pointer panics/returns/exits. */ +C_API void *malloc_by(memory_t *scope, size_t size); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be `RAII_FREE` when scope smart pointer panics/returns/exits. */ +C_API void *calloc_by(memory_t *scope, int count, size_t size); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ +C_API void *malloc_full(memory_t *scope, size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ +C_API void *calloc_full(memory_t *scope, int count, size_t size, func_t func); +``` + +Note the above functions will **panic/throw** if request fails, is `NULL`, +and begin **unwinding**, executing _deferred_ statements. + +### Thereafter, an smart pointer can be use with these _raii___*_ functions. + +```c +/* Defer execution `LIFO` of given function with argument, +to the given `scoped smart pointer` lifetime/destruction. */ +C_API size_t raii_deferred(memory_t *, func_t, void *); + +/* Same as `raii_deferred` but allows recover from an Error condition throw/panic, +you must call `raii_is_caught` inside function to mark Error condition handled. */ +C_API void raii_recover_by(memory_t *, func_t, void *); + +/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ +C_API bool raii_is_caught(memory_t *scope, const char *err); + +/* Get scoped error condition string. */ +C_API const char *raii_message_by(memory_t *scope); + +/* Begin `unwinding`, executing given scope smart pointer `raii_deferred` statements. */ +C_API void raii_deferred_free(memory_t *); + +/* Same as `raii_deferred_free`, but also destroy smart pointer. */ +C_API void raii_delete(memory_t *ptr); +``` + +### Using `thread local storage` for an default smart pointer, the following functions always available. + +```c +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *malloc_default(size_t size); + +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *calloc_default(int count, size_t size); + +/* Defer execution `LIFO` of given function with argument, +to current `thread` scope lifetime/destruction. */ +C_API size_t raii_defer(func_t, void *); + +/* Same as `raii_defer` but allows recover from an Error condition throw/panic, +you must call `raii_caught` inside function to mark Error condition handled. */ +C_API void raii_recover(func_t, void *); + +/* Compare `err` to current error condition, will mark exception handled, if `true`. */ +C_API bool raii_caught(const char *err); + +/* Get current error condition string. */ +C_API const char *raii_message(void); + +/* Begin `unwinding`, executing current `thread` scope `raii_defer` statements. */ +C_API void raii_deferred_clean(void); +``` + +### Fully automatic memory safety, using `guard/unguarded/guarded` macro. + +The full potently of **RAII** is encapsulated in the `guard` macro. +Using `try/catch/catch_any/catch_if/finally/end_try` exception system macros separately will be unnecessary, however see [examples](https://github.com/zelang-dev/c-raii/tree/main/examples) folder for usage. + +The Planned C11 implementation details still holds here, but `defer` not confined to `guard` block, actual function call. + +```c +/* Creates an scoped guard section, it replaces `{`. +Usage of: `_defer`, `_malloc`, `_calloc`, `_assign_ptr` macros +are only valid between these sections. + - Use `_return(x);` macro, or `break;` to exit early. + - Use `_assign_ptr(var)` macro, to make assignment of block scoped smart pointer. */ +#define guard + +/* This ends an scoped guard section, it replaces `}`. +On exit will begin executing deferred functions, +return given `result` when done, use `NONE` for no return. */ +#define unguarded(result) + +/* This ends an scoped guard section, it replaces `}`. +On exit will begin executing deferred functions. */ +#define guarded + +/* Returns protected raw memory pointer, +DO NOT FREE, will `throw/panic` if memory request fails. */ +#define _malloc(size) + +/* Returns protected raw memory pointer, +DO NOT FREE, will `throw/panic` if memory request fails. */ +#define _calloc(count, size) + +/* Defer execution `LIFO` of given function with argument, +execution begins when current `guard` scope exits or panic/throw. */ +#define _defer(func, ptr) + +/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ +#define _recover(err) + +/* Compare `err` to scoped error condition, +will mark exception handled, if `true`. +DO NOT PUT `err` in quote's like "err". */ +#define _is_caught(err) + +/* Get scoped error condition string. */ +#define _get_message() + +/* Stops the ordinary flow of control and begins panicking, +throws an exception of given message. */ +#define _panic(err) + +/* Makes a reference assignment of current scoped smart pointer. */ +#define _assign_ptr(scope) + +/* Exit `guarded` section, begin executing deferred functions, +return given `value` when done, use `NONE` for no return. */ +#define _return(value) +``` + +The idea way of using this library, is to make a **new** _field_ for `unique_t` into your current _typedef_ **object**, +mainly one held throughout application, and setup your wrapper functions to above **raii_** functions. + +There are also `2 global callback` functions that need to be setup for complete integration. + +```c +// Currently an wrapper function that set ctx->data, scoped error, and protection state, working on removing need +typedef void (*ex_setup_func)(ex_context_t *ctx, const char *ex, const char *panic); +// Your wrapper to raii_deferred_free(ctx->data) +typedef void (*ex_unwind_func)(void *); + +ex_setup_func exception_setup_func; +ex_unwind_func exception_unwind_func; +``` + +## Installation + +The build system uses **cmake**, that produces _single_ **static** library stored under `raii-built`, and the complete `include` folder is needed. + +**Linux** + +```shell +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -DBUILD_TESTING=ON # use to build tests and examples +cmake --build . +``` + +**Windows** + +```shell +mkdir build +cd build +cmake .. -D BUILD_TESTING=ON # use to build tests and examples +cmake --build . --config Debug/Release +``` + +## Contributing + +Contributions are encouraged and welcome; I am always happy to get feedback or pull requests on Github :) Create [Github Issues](https://github.com/zelang-dev/c-raii/issues) for bugs and new features and comment on the ones you are interested in. + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/deps/raii/defer-implementation-long.pdf b/deps/raii/defer-implementation-long.pdf new file mode 100644 index 00000000..b0049179 Binary files /dev/null and b/deps/raii/defer-implementation-long.pdf differ diff --git a/deps/raii/docs/_config.yml b/deps/raii/docs/_config.yml new file mode 100644 index 00000000..5b66705c --- /dev/null +++ b/deps/raii/docs/_config.yml @@ -0,0 +1,55 @@ +plugins: + - jekyll-relative-links + - jekyll-feed + - jekyll-readme-index + - jemoji +relative_links: + enabled: true + collections: true +include: + - index.md + - LICENSE.md + - ISSUE_TEMPLATE.md + - examples + - PULL_REQUEST_TEMPLATE.md + +theme: jekyll-theme-hacker +show_downloads: [true] +toc: + enabled: true + h_min: 1 + h_max: 3 +markdown: kramdown +kramdown: + auto_ids: true + input: GFM + math_engine: mathjax + smart_quotes: lsquo,rsquo,ldquo,rdquo + toc_levels: 1..6 + syntax_highlighter: rouge + syntax_highlighter_opts: + guess_lang: true + +syntax_highlighter_style: colorful + +markdown_ext: markdown,mkdown,mkdn,mkd,md + +exclude: + - README.md + - Makefile + - CMakeLists.txt + - CNAME + - LICENSE + - update.sh + - Gemfile + - Gemfile.lock + - requirements.txt + - build + - src + - raii-built + - .github + - valgrind.supp + - package.json + - package-lock.json + - webpack.config.js + - jekyll-rtd-theme.gemspec diff --git a/deps/raii/docs/index.md b/deps/raii/docs/index.md new file mode 100644 index 00000000..885ed461 --- /dev/null +++ b/deps/raii/docs/index.md @@ -0,0 +1,410 @@ +# c-raii + +[![windows & linux & macos](https://github.com/zelang-dev/c-raii/actions/workflows/ci.yml/badge.svg)](https://github.com/zelang-dev/c-raii/actions/workflows/ci.yml) + +An robust high-level **Defer**, _RAII_ implementation for `C89`, with automatic smart memory safety. + +* [Synopsis](#synopsis) +* [Installation](#installation) +* [Contributing](#contributing) +* [License](#license) + +This library has been decoupled from [c-coroutine](https://github.com/zelang-dev/c-coroutine) to be independently developed. + +In the effort to find uniform naming of terms, various other packages was discovered [Except](https://github.com/meaning-matters/Except), [exceptions-and-raii-in-c](https://github.com/psevon/exceptions-and-raii-in-c). Choose to settle in on [A defer mechanism for C](https://gustedt.wordpress.com/2020/12/14/a-defer-mechanism-for-c/), an upcoming C standard compiler feature. It's exactly this library's working design and concepts addressed in [c-coroutine](https://github.com/zelang-dev/c-coroutine). The behavior here is as in **Go** and **Zig** _languages_, simple syntax. + +The planned implementation from [defer reference implementation for C](https://gustedt.gitlabpages.inria.fr/defer/): + + + + + + + + + + +
RAII C89Planned C11
+ +```c +// includes "cthread.h" emulated C11 threads +#include "raii.h" + +int main(int argc, char **argv) +guard { + // Panic if p == NULL + // Automatically _defer(free, p) + void *p = _malloc(25); + void *q = _calloc(1, 25); + + if (mtx_lock(&mut)==thrd_error) break; + _defer(mtx_unlock, &mut); + + // all resources acquired +} unguarded(0); +``` + + +```c +guard { + void * const p = malloc(25); + if (!p) break; + defer free(p); + + void * const q = malloc(25); + if (!q) break; + defer free(q); + + if (mtx_lock(&mut)==thrd_error) break; + defer mtx_unlock(&mut); + + // all resources acquired +} +``` +
+ +There C Standard implementation states: **The important interfaces of this tool are:** + +- `guard` prefixes a guarded block +- `defer` prefixes a defer clause +- `break` ends a guarded block and executes all its defer clauses +- `return` unwinds all guarded blocks of the current function and returns to the caller +- `exit` unwinds all defer clauses of all active function calls of the thread and exits normally +- `panic` starts global unwinding of all guarded blocks +- `recover` inside a defer clause stops a panic and provides an error code + +There example from [source](https://gitlab.inria.fr/gustedt/defer/-/blob/master/defer4.c?ref_type=heads) - **gitlab**, outlined in [C Standard WG14 meeting](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2542.pdf) - **pdf** + + + + + + + + + + +
RAII C89Planned C11
+ +```c +#include "raii.h" + +void g_print(void *ptr) { + int i = raii_value(ptr)->integer; + printf("Defer in g = %d.\n", i); +} + +void g(int i) { + char number[20]; + if (i > 3) { + puts("Panicking!"); + snprintf(number, 20, "%ld", i); + _panic(number); + } + guard { + _defer(g_print, &i); + printf("Printing in g = %d.\n", i); + g(i + 1); + } guarded; +} + +void f_print(void *na) { + puts("In defer in f"); + fflush(stdout); + if (_recover(_get_message())) { + printf("Recovered in f = %s\n", _get_message()); + fflush(stdout); + } +} + +void f() +guard { + _defer(f_print, NULL); + puts("Calling g."); + g(0); + puts("Returned normally from g."); +} guarded; + +int main(int argc, char **argv) { + f(); + puts("Returned normally from f."); + return EXIT_SUCCESS; +} +``` + + + +```c +#include +#include +#include +#include "stddefer.h" + +void g(int i) { + if (i > 3) { + puts("Panicking!"); + panic(i); + } + guard { + defer { + printf("Defer in g = %d.\n", i); + } + printf("Printing in g = %d.\n", i); + g(i+1); + } +} + +void f() { + guard { + defer { + puts("In defer in f"); + fflush(stdout); + int err = recover(); + if (err != 0) { + printf("Recovered in f = %d\n", err); + fflush(stdout); + } + } + puts("Calling g."); + g(0); + puts("Returned normally from g."); + } +} + +int main(int argc, char* argv[static argc+1]) { + f(); + puts("Returned normally from f."); + return EXIT_SUCCESS; +} +``` +
+ +_Function_ `​f`​ containing a **​defer​** statement which contains a call to the **​recover** +function. _Function_ `​f`​ invokes _function_ `​g`​ which recursively descends before _panicking_ when the +value of `​i > 3`​. Execution of `​f`​ produces the following **output**: +
+Calling g.
+Printing in g = 0.
+Printing in g = 1.
+Printing in g = 2.
+Printing in g = 3.
+Panicking!
+Defer in g = 3.
+Defer in g = 2.
+Defer in g = 1.
+Defer in g = 0.
+In defer in f
+Recovered in f = 4
+Returned normally from f.
+
+ +## Synopsis + +**C++** has a concept called [unique_ptr)](https://en.cppreference.com/w/cpp/memory/unique_ptr) where **"a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. "** + +Here too the same process is in effect through an **new** _typedef_ `unique_t` aka `memory_t` _structure_. + +### There are _3 ways_ to create an smart memory pointer. + +```c +/* Creates smart memory pointer, this object binds any additional requests to it's lifetime. +for use with `malloc_*` `calloc_*` wrapper functions to request/return raw memory. */ +C_API unique_t *unique_init(void); + +/* Creates smart memory pointer, the allocated `size` memory requested in **arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +The **arena** field will be freed with given `func`. */ +C_API memory_t *raii_malloc_full(size_t size, func_t func); + +/* Creates smart memory pointer, the allocated `size` memory requested in **arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +The **arena** field will be freed with given `func`. */ +C_API memory_t *raii_calloc_full(int count, size_t size, func_t func); +``` + +> This system use macros `RAII_MALLOC`, `RAII_FREE`, `RAII_REALLOC`, and `RAII_CALLOC`. +> If not **defined**, the default __malloc/calloc/realloc/free__ are used when expanded. + +### The following _malloc/calloc_ wrapper functions are used to get an raw memory pointer. + +```c +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be `RAII_FREE` when scope smart pointer panics/returns/exits. */ +C_API void *malloc_by(memory_t *scope, size_t size); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be `RAII_FREE` when scope smart pointer panics/returns/exits. */ +C_API void *calloc_by(memory_t *scope, int count, size_t size); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ +C_API void *malloc_full(memory_t *scope, size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ +C_API void *calloc_full(memory_t *scope, int count, size_t size, func_t func); +``` + +Note the above functions will **panic/throw** if request fails, is `NULL`, +and begin **unwinding**, executing _deferred_ statements. + +### Thereafter, an smart pointer can be use with these _raii___*_ functions. + +```c +/* Defer execution `LIFO` of given function with argument, +to the given `scoped smart pointer` lifetime/destruction. */ +C_API size_t raii_deferred(memory_t *, func_t, void *); + +/* Same as `raii_deferred` but allows recover from an Error condition throw/panic, +you must call `raii_is_caught` inside function to mark Error condition handled. */ +C_API void raii_recover_by(memory_t *, func_t, void *); + +/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ +C_API bool raii_is_caught(memory_t *scope, const char *err); + +/* Get scoped error condition string. */ +C_API const char *raii_message_by(memory_t *scope); + +/* Begin `unwinding`, executing given scope smart pointer `raii_deferred` statements. */ +C_API void raii_deferred_free(memory_t *); + +/* Same as `raii_deferred_free`, but also destroy smart pointer. */ +C_API void raii_delete(memory_t *ptr); +``` + +### Using `thread local storage` for an default smart pointer, the following functions always available. + +```c +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *malloc_default(size_t size); + +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *calloc_default(int count, size_t size); + +/* Defer execution `LIFO` of given function with argument, +to current `thread` scope lifetime/destruction. */ +C_API size_t raii_defer(func_t, void *); + +/* Same as `raii_defer` but allows recover from an Error condition throw/panic, +you must call `raii_caught` inside function to mark Error condition handled. */ +C_API void raii_recover(func_t, void *); + +/* Compare `err` to current error condition, will mark exception handled, if `true`. */ +C_API bool raii_caught(const char *err); + +/* Get current error condition string. */ +C_API const char *raii_message(void); + +/* Begin `unwinding`, executing current `thread` scope `raii_defer` statements. */ +C_API void raii_deferred_clean(void); +``` + +### Fully automatic memory safety, using `guard/unguarded/guarded` macro. + +The full potently of **RAII** is encapsulated in the `guard` macro. +Using `try/catch/catch_any/catch_if/finally/end_try` exception system macros separately will be unnecessary, however see [examples](https://github.com/zelang-dev/c-raii/tree/main/examples) folder for usage. + +The Planned C11 implementation details still holds here, but `defer` not confined to `guard` block, actual function call. + +```c +/* Creates an scoped guard section, it replaces `{`. +Usage of: `_defer`, `_malloc`, `_calloc`, `_assign_ptr` macros +are only valid between these sections. + - Use `_return(x);` macro, or `break;` to exit early. + - Use `_assign_ptr(var)` macro, to make assignment of block scoped smart pointer. */ +#define guard + +/* This ends an scoped guard section, it replaces `}`. +On exit will begin executing deferred functions, +return given `result` when done, use `NONE` for no return. */ +#define unguarded(result) + +/* This ends an scoped guard section, it replaces `}`. +On exit will begin executing deferred functions. */ +#define guarded + +/* Returns protected raw memory pointer, +DO NOT FREE, will `throw/panic` if memory request fails. */ +#define _malloc(size) + +/* Returns protected raw memory pointer, +DO NOT FREE, will `throw/panic` if memory request fails. */ +#define _calloc(count, size) + +/* Defer execution `LIFO` of given function with argument, +execution begins when current `guard` scope exits or panic/throw. */ +#define _defer(func, ptr) + +/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ +#define _recover(err) + +/* Compare `err` to scoped error condition, +will mark exception handled, if `true`. +DO NOT PUT `err` in quote's like "err". */ +#define _is_caught(err) + +/* Get scoped error condition string. */ +#define _get_message() + +/* Stops the ordinary flow of control and begins panicking, +throws an exception of given message. */ +#define _panic(err) + +/* Makes a reference assignment of current scoped smart pointer. */ +#define _assign_ptr(scope) + +/* Exit `guarded` section, begin executing deferred functions, +return given `value` when done, use `NONE` for no return. */ +#define _return(value) +``` + +The idea way of using this library, is to make a **new** _field_ for `unique_t` into your current _typedef_ **object**, +mainly one held throughout application, and setup your wrapper functions to above **raii_** functions. + +There are also `2 global callback` functions that need to be setup for complete integration. + +```c +// Currently an wrapper function that set ctx->data, scoped error, and protection state, working on removing need +typedef void (*ex_setup_func)(ex_context_t *ctx, const char *ex, const char *panic); +// Your wrapper to raii_deferred_free(ctx->data) +typedef void (*ex_unwind_func)(void *); + +ex_setup_func exception_setup_func; +ex_unwind_func exception_unwind_func; +``` + +## Installation + +The build system uses **cmake**, that produces _single_ **static** library stored under `raii-built`, and the complete `include` folder is needed. + +**Linux** + +```shell +mkdir build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -DBUILD_TESTING=ON # use to build tests and examples +cmake --build . +``` + +**Windows** + +```shell +mkdir build +cd build +cmake .. -D BUILD_TESTING=ON # use to build tests and examples +cmake --build . --config Debug/Release +``` + +## Contributing + +Contributions are encouraged and welcome; I am always happy to get feedback or pull requests on Github :) Create [Github Issues](https://github.com/zelang-dev/c-raii/issues) for bugs and new features and comment on the ones you are interested in. + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/deps/raii/examples/CMakeLists.txt b/deps/raii/examples/CMakeLists.txt new file mode 100644 index 00000000..c2829112 --- /dev/null +++ b/deps/raii/examples/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 2.8...3.14) + +set(TARGET_LIST defer_thread + throw_no_try_protected + throw_no_try + try_catch_any + try_catch_if + try_catch + try_double_rethrow + try_finally + try_free_protected + try_protect + try_protected + try_rethrow + try_signal_sigfpe + try_signal_sigsegv + try_unprotected ) +foreach (TARGET ${TARGET_LIST}) + add_executable(${TARGET} ${TARGET}.c ) + target_link_libraries(${TARGET} raii) +endforeach() diff --git a/deps/raii/examples/defer_thread.c b/deps/raii/examples/defer_thread.c new file mode 100644 index 00000000..2bbb22c7 --- /dev/null +++ b/deps/raii/examples/defer_thread.c @@ -0,0 +1,85 @@ +#include "raii.h" + +/** + ** @file + ** + ** This is a test program for the defer mechanism that v + ** shows some compliant solutions using defer + ** for https://wiki.sei.cmu.edu/confluence/x/LdYxBQ + **/ + +mtx_t lock; + +unsigned int completed = 0; +enum { max_threads = 5 }; +char number[20]; + +static unsigned int names[max_threads]; +static thrd_t threads[max_threads]; + +void defer_capture(void *arg) { + thrd_t thrd = (thrd_t)raii_value(arg)->max_size; + if (thrd_success != thrd_join(thrd, NULL)) { + puts("main: thrd_join failure"); + thrd_exit(EXIT_FAILURE); + } + + printf("main: thread %lu joined.\n", thrd); +} + +void do_print(void *name) { + printf("thread %u deferred mtx_unlock = %.03u\n", raii_value(name)->integer, completed); + if (thrd_success != mtx_unlock(&lock)) { + snprintf(number, 20, "%d", thrd_error); + _panic(number); + } +} + +int do_work(void *namep) { + guard{ + unsigned int *name = namep; + printf("thread #%lu . %u: starting do_work guarded block\n", thrd_current(), *name); + if (thrd_success != mtx_lock(&lock)) { + thrd_exit(thrd_error); + } + _defer(do_print, name); + /* Access data protected by the lock */ + completed += 1; + } guarded; + + // these last two lines should do the same thing + // return thrd_success; + thrd_exit(EXIT_SUCCESS); + return EXIT_SUCCESS; +} + +static void cleanup_threads(void) { + printf(".cleanup: %p\n", (void *)threads); + *threads = 0; +} + +int main(int argc, char **argv) { + // install the overall cleanup handlers + atexit(cleanup_threads); + // compliant solution using threads array + guard{ + puts("main: starting main guarded block"); + if (thrd_success != mtx_init(&lock, mtx_plain)) { + exit(EXIT_FAILURE); + } + _defer(mtx_destroy, &lock); + // In that case, a panic will be triggered, but + // the above `mtx_destroy` should still be called, followed by the + // atexit cleanup. + for (unsigned int i = 0; i < max_threads; i++) { + names[i] = i; + if (thrd_success != thrd_create(&threads[i], do_work, &names[i])) { + exit(EXIT_FAILURE); + } + _defer(defer_capture, &threads[i]); + } // end for + } guarded;// end guard + + printf("main: completed = %u.\n", completed); + return EXIT_SUCCESS; +} diff --git a/deps/raii/examples/throw_no_try.c b/deps/raii/examples/throw_no_try.c new file mode 100644 index 00000000..e24bb965 --- /dev/null +++ b/deps/raii/examples/throw_no_try.c @@ -0,0 +1,16 @@ +#include "raii.h" + +static int div_err(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +int main(int argc, char **argv) { + ex_signal_setup(); + div_err(1, 0); + printf("never reached\n"); + + return 0; +} diff --git a/deps/raii/examples/throw_no_try_protected.c b/deps/raii/examples/throw_no_try_protected.c new file mode 100644 index 00000000..6592378c --- /dev/null +++ b/deps/raii/examples/throw_no_try_protected.c @@ -0,0 +1,27 @@ +#include "raii.h" + +static int div_err(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +static void pfree(void *p) { + printf("freeing protected memory pointed by %s\n", (char *)p); + free(p); +} + +int main(int argc, char **argv) { + void *p = 0; + + p = calloc_full(raii_init(), 1, 3, pfree); + if (p) + strcpy(p, "p"); + div_err(1, 0); + printf("never reached\n"); + + free(p); + + return 0; +} diff --git a/deps/raii/examples/try_catch.c b/deps/raii/examples/try_catch.c new file mode 100644 index 00000000..c5f2b58a --- /dev/null +++ b/deps/raii/examples/try_catch.c @@ -0,0 +1,19 @@ +#include "raii.h" + +static int idiv(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +int main(void) { + try { + idiv(1, 0); + printf("never reached\n"); + } catch (division_by_zero) { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_catch_any.c b/deps/raii/examples/try_catch_any.c new file mode 100644 index 00000000..31faa7d7 --- /dev/null +++ b/deps/raii/examples/try_catch_any.c @@ -0,0 +1,16 @@ +#include "raii.h" + +static int idiv(int a, int b) { + *(int *)0 = b; +} + +int main(int argc, char **argv) { + try { + idiv(1, 0); + printf("never reached\n"); + } catch_any { + printf("catch_any: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_catch_if.c b/deps/raii/examples/try_catch_if.c new file mode 100644 index 00000000..a711ea92 --- /dev/null +++ b/deps/raii/examples/try_catch_if.c @@ -0,0 +1,28 @@ +#include "raii.h" + +static int idiv(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +int main(void) { + try { + idiv(1, 0); + printf("never reached\n"); + } catch_if { + if (caught(bad_alloc)) { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + } else if (caught(division_by_zero)) { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + } + } finally { + if (err) + printf("finally: try failed -> %s (%s:%d)\n", err, err_file, err_line); + else + printf("finally: try succeeded\n"); + } end_try; + + return 0; +} diff --git a/deps/raii/examples/try_double_rethrow.c b/deps/raii/examples/try_double_rethrow.c new file mode 100644 index 00000000..020c3516 --- /dev/null +++ b/deps/raii/examples/try_double_rethrow.c @@ -0,0 +1,29 @@ + +#include "raii.h" + +static int div_err(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +int main(int argc, char **argv) { + try { + div_err(1, 0); + printf("never reached\n"); + } catch (division_by_zero) { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + rethrow(); + printf("never reached\n"); + } finally { + if (err) { + printf("finally: try failed -> %s (%s:%d)\n", err, err_file, err_line); + rethrow(); + printf("never reached\n"); + } else + printf("finally: try succeeded\n"); + } end_try; + + return 0; +} diff --git a/deps/raii/examples/try_finally.c b/deps/raii/examples/try_finally.c new file mode 100644 index 00000000..1713e26f --- /dev/null +++ b/deps/raii/examples/try_finally.c @@ -0,0 +1,24 @@ +#include "raii.h" + +static int div_err(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +int main(int argc, char **argv) { + try { + div_err(1, 0); + printf("never reached\n"); + } catch (out_of_memory) { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + } finally { + if (err) + printf("finally: try failed -> %s (%s:%d)\n", err, err_file, err_line); + else + printf("finally: try succeeded\n"); + } end_try; + + return 0; +} diff --git a/deps/raii/examples/try_free_protected.c b/deps/raii/examples/try_free_protected.c new file mode 100644 index 00000000..697c784e --- /dev/null +++ b/deps/raii/examples/try_free_protected.c @@ -0,0 +1,32 @@ +#include "raii.h" + +static int idiv(int a, int b) { + return a / b; +} + +static void pfree(void *p) { + printf("freeing protected memory pointed by '%s'\n", (char *)p); + free(p); +} + +int main(void) { + try { + char *p1 = 0; protected(p1, pfree); + char *p2 = 0; protected(p2, pfree); + + p1 = malloc(sizeof("p1")); + if (p1) strcpy(p1, "p1"); + + p2 = malloc(sizeof("p2")); + if (p2) strcpy(p2, "p2"); + + free(p1); free(p2); unprotected(p1); /* unprotected p2 too */ + + idiv(1, 0); + printf("never reached\n"); + } catch_any { + printf("catch_any: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_protect.c b/deps/raii/examples/try_protect.c new file mode 100644 index 00000000..932f5ff4 --- /dev/null +++ b/deps/raii/examples/try_protect.c @@ -0,0 +1,30 @@ +#include "raii.h" + +static int idiv(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +static void pfree(void *p) { + printf("freeing protected memory pointed by '%s'\n", (char *)p); + free(p); +} + +int main(void) { + try { + char *p = 0; protected(p, pfree); + + p = malloc(sizeof("p")); + if (p) strcpy(p, "p"); + free(p); unprotected(p); + + idiv(1, 0); + printf("never reached\n"); + } catch_any{ + printf("catch_any: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_protected.c b/deps/raii/examples/try_protected.c new file mode 100644 index 00000000..14096ba2 --- /dev/null +++ b/deps/raii/examples/try_protected.c @@ -0,0 +1,36 @@ +#include "raii.h" + +static int div_err(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +static void pfree(void *p) { + printf("freeing protected memory pointed by '%s'\n", (char *)p); + free(p); +} + +int main(int argc, char **argv) { + try { + char *p = 0; + + p = calloc_full(raii_init(), 1, 3, pfree); + if (p) + strcpy(p, "p"); + + p = calloc_full(raii_init(), 1, 4, pfree); /* still protected */ + if (p) + strcpy(p, "p2"); + + div_err(1, 0); + printf("never reached\n"); + + free(p); + } catch_any { + printf("catch_any: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_rethrow.c b/deps/raii/examples/try_rethrow.c new file mode 100644 index 00000000..3f351f3f --- /dev/null +++ b/deps/raii/examples/try_rethrow.c @@ -0,0 +1,21 @@ +#include "raii.h" + +static int idiv(int a, int b) { + if (b == 0) + throw(division_by_zero); + + return a / b; +} + +int main(void) { + try { + idiv(1, 0); + printf("never reached\n"); + } catch (division_by_zero) { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + rethrow(); + printf("never reached\n"); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_signal_sigfpe.c b/deps/raii/examples/try_signal_sigfpe.c new file mode 100644 index 00000000..04bfe99f --- /dev/null +++ b/deps/raii/examples/try_signal_sigfpe.c @@ -0,0 +1,26 @@ +#include "raii.h" + +static void pfree(void *p) { + printf("freeing protected memory pointed by '%s'\n", (char *)p); + free(p); +} + +int main(int argc, char **argv) { + try { + char *p = 0; + p = calloc_full(raii_init(), 1, 3, pfree); + if (p) + strcpy(p, "p"); + + int a = 1; + int b = 0.00000; + float x = a / b; + printf("never reached %f\n", x); + + free(p); + } catch_any { + printf("catch_any: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_signal_sigsegv.c b/deps/raii/examples/try_signal_sigsegv.c new file mode 100644 index 00000000..a66a5c5e --- /dev/null +++ b/deps/raii/examples/try_signal_sigsegv.c @@ -0,0 +1,24 @@ +#include "raii.h" + +static void pfree(void *p) { + printf("freeing protected memory pointed by '%s'\n", (char *)p); + free(p); +} + +int main(int argc, char **argv) { + try { + char *p = 0; + p = malloc_full(raii_init(), 3, pfree); + if (p) + strcpy(p, "p"); + + *(int *)0 = 0; + printf("never reached\n"); + + free(p); + } catch_any { + printf("catch: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/examples/try_unprotected.c b/deps/raii/examples/try_unprotected.c new file mode 100644 index 00000000..64ab097c --- /dev/null +++ b/deps/raii/examples/try_unprotected.c @@ -0,0 +1,38 @@ +#include "raii.h" + +static int div_err(int a, int b) { + *(int *)0 = b; +} + +static void pfree(void *p) { + printf("freeing protected memory pointed by '%s'\n", (char *)p); + free(p); +} + +int main(int argc, char **argv) { + try { + char *p1 = 0; + protected(p1, pfree); + char *p2 = 0; + protected(p2, pfree); + + p1 = malloc(sizeof("p1")); + if (p1) + strcpy(p1, "p1"); + + p2 = malloc(sizeof("p2")); + if (p2) + strcpy(p2, "p2"); + + free(p1); + free(p2); + unprotected(p1); /* unprotected p2 too */ + + div_err(1, 0); + printf("never reached\n"); + } catch_any { + printf("catch_any: exception %s (%s:%d) caught\n", err, err_file, err_line); + } end_trying; + + return 0; +} diff --git a/deps/raii/include/compat/_ptw32.h b/deps/raii/include/compat/_ptw32.h new file mode 100644 index 00000000..34ed4131 --- /dev/null +++ b/deps/raii/include/compat/_ptw32.h @@ -0,0 +1,232 @@ +/* + * Module: _ptw32.h + * + * Purpose: + * pthreads-win32 internal macros, to be shared by other headers + * comprising the pthreads4w package. + * + * -------------------------------------------------------------------------- + * + * pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999-2021 pthreads-win32 / pthreads4w contributors + * + * Homepage1: http://sourceware.org/pthreads-win32/ + * Homepage2: http://sourceforge.net/projects/pthreads4w/ + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * -------------------------------------------------------------------------- + */ + +#ifndef PTW32_H +#define PTW32_H + +/* See the README file for an explanation of the pthreads4w + * version numbering scheme and how the DLL is named etc. + */ +#define PTW32_VERSION_MAJOR 3 +#define PTW32_VERSION_MINOR 0 +#define PTW32_VERSION_MICRO 3 +#define PTW32_VERION_BUILD 1 +#define PTW32_VERSION 3,0,3,1 +#define PTW32_VERSION_STRING "3, 0, 3, 1\0" + +// skip the rest when running this through the Microsoft Resource Compiler, which is a VERY brittle piece of machinery! -> RC2188 & RC1116 cryptic failures will be your part! +#ifndef RC_INVOKED + +#if defined(__GNUC__) +# pragma GCC system_header +# if ! defined __declspec +# error "Please upgrade your GNU compiler to one that supports __declspec." +# endif +#endif + +#if defined (__cplusplus) +# define PTW32_BEGIN_C_DECLS extern "C" { +# define PTW32_END_C_DECLS } +#else +# define PTW32_BEGIN_C_DECLS +# define PTW32_END_C_DECLS +#endif + +#if defined PTW32_STATIC_LIB +# define PTW32_DLLPORT + +#elif defined PTW32_BUILD +# define PTW32_DLLPORT __declspec (dllexport) +#else +# define PTW32_DLLPORT /*__declspec (dllimport)*/ +#endif + +#ifndef PTW32_CDECL +/* Nominally, we prefer to use __cdecl calling convention for all our + * functions, but we map it through this macro alias to facilitate the + * possible choice of alternatives; for example: + */ +# ifdef _OPEN_WATCOM_SOURCE + /* The Open Watcom C/C++ compiler uses a non-standard default calling + * convention, (similar to __fastcall), which passes function arguments + * in registers, unless the __cdecl convention is explicitly specified + * in exposed function prototypes. + * + * Our preference is to specify the __cdecl convention for all calls, + * even though this could slow Watcom code down slightly. If you know + * that the Watcom compiler will be used to build both the DLL and your + * application, then you may #define _OPEN_WATCOM_SOURCE, so disabling + * the forced specification of __cdecl for all function declarations; + * remember that this must be defined consistently, for both the DLL + * build, and the application build. + */ +# define PTW32_CDECL +# else +# define PTW32_CDECL __cdecl +# endif +#endif + +/* + * This is more or less a duplicate of what is in the autoconf config.h, + * which is only used when building the pthreads4w libraries. + */ + +#if !defined (PTW32_CONFIG_H) && !defined(PTW32_PSEUDO_CONFIG_H_SOURCED) +# define PTW32_PSEUDO_CONFIG_H_SOURCED +# if defined(WINCE) +# undef HAVE_CPU_AFFINITY +# define NEED_DUPLICATEHANDLE +# define NEED_CREATETHREAD +# define NEED_ERRNO +# define NEED_CALLOC +# define NEED_UNICODE_CONSTS +# define NEED_PROCESS_AFFINITY_MASK +/* This may not be needed */ +# define RETAIN_WSALASTERROR +# elif defined(_MSC_VER) +# if _MSC_VER >= 1900 +# define HAVE_STRUCT_TIMESPEC +# elif _MSC_VER < 1300 +# define PTW32_CONFIG_MSVC6 +# elif _MSC_VER < 1400 +# define PTW32_CONFIG_MSVC7 +# endif +# elif defined(_UWIN) +# define HAVE_MODE_T +# define HAVE_STRUCT_TIMESPEC +# define HAVE_SIGNAL_H +# elif defined(__MINGW64__) +# define HAVE_STRUCT_TIMESPEC +# define HAVE_MODE_T +# elif defined(__MINGW32__) +# define HAVE_MODE_T +# endif +#endif + +#if !defined(_WIN32_WINNT) +# define _WIN32_WINNT 0x0400 +#endif + +/* + * If HAVE_ERRNO_H is defined then assume that autoconf has been used + * to overwrite config.h, otherwise the original config.h is in use + * at build-time or the above block of defines is in use otherwise + * and NEED_ERRNO is either defined or not defined. + */ +#if defined(HAVE_ERRNO_H) || !defined(NEED_ERRNO) +# include +#else +# include "need_errno.h" +#endif + +#if defined(__BORLANDC__) +# define int64_t LONGLONG +# define uint64_t ULONGLONG +#elif !defined(__MINGW32__) + typedef _int64 int64_t; + typedef unsigned _int64 uint64_t; +# if defined (PTW32_CONFIG_MSVC6) + typedef long intptr_t; +# endif +#elif defined(HAVE_STDINT_H) && HAVE_STDINT_H == 1 +# include +#endif + +/* + * In case ETIMEDOUT hasn't been defined above somehow. + */ +#if !defined(ETIMEDOUT) + /* + * note: ETIMEDOUT is no longer defined in winsock.h + * WSAETIMEDOUT is so use its value. + */ +# include +# if defined(WSAETIMEDOUT) +# define ETIMEDOUT WSAETIMEDOUT +# else +# define ETIMEDOUT 10060 /* This is the value of WSAETIMEDOUT in winsock.h. */ +# endif +#endif + +/* + * Several systems may not define some error numbers; + * defining those which are likely to be missing here will let + * us complete the library builds. + */ +#if !defined(ENOTSUP) +# define ENOTSUP 48 /* This is the value in Solaris. */ +#endif + +#if !defined(ETIMEDOUT) +# define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */ +#endif + +#if !defined(ENOSYS) +# define ENOSYS 140 /* Semi-arbitrary value */ +#endif + +#if !defined(EDEADLK) +# if defined(EDEADLOCK) +# define EDEADLK EDEADLOCK +# else +# define EDEADLK 36 /* This is the value in MSVC. */ +# endif +#endif + +/* POSIX 2008 - related to robust mutexes */ +#if PTW32_VERSION_MAJOR > 2 +# if !defined(EOWNERDEAD) +# define EOWNERDEAD 1000 +# endif +# if !defined(ENOTRECOVERABLE) +# define ENOTRECOVERABLE 1001 +# endif +#else +# if !defined(EOWNERDEAD) +# define EOWNERDEAD 42 +# endif +# if !defined(ENOTRECOVERABLE) +# define ENOTRECOVERABLE 43 +# endif +#endif + +#endif // RC_INVOKED + +#endif /* !PTW32_H */ diff --git a/deps/raii/include/compat/arpa/inet.h b/deps/raii/include/compat/arpa/inet.h new file mode 100644 index 00000000..4422f418 --- /dev/null +++ b/deps/raii/include/compat/arpa/inet.h @@ -0,0 +1,15 @@ +/* + * Public domain + * arpa/inet.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include + +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0x00000400 +#endif + +#endif diff --git a/deps/raii/include/compat/arpa/nameser.h b/deps/raii/include/compat/arpa/nameser.h new file mode 100644 index 00000000..eff3b0d9 --- /dev/null +++ b/deps/raii/include/compat/arpa/nameser.h @@ -0,0 +1,25 @@ +/* + * Public domain + * arpa/inet.h compatibility shim + */ + +#ifndef _WIN32 +#ifdef HAVE_ARPA_NAMESER_H +#include_next +#endif +#else +#include + +#ifndef INADDRSZ +#define INADDRSZ 4 +#endif + +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 +#endif + +#ifndef INT16SZ +#define INT16SZ 2 +#endif + +#endif diff --git a/deps/raii/include/compat/dirent.h b/deps/raii/include/compat/dirent.h new file mode 100644 index 00000000..753e4a08 --- /dev/null +++ b/deps/raii/include/compat/dirent.h @@ -0,0 +1,17 @@ +/* + * Public domain + * dirent.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_DIRENT_H +#define LIBCRYPTOCOMPAT_DIRENT_H + +#ifdef _MSC_VER +#include +#include +#else +#include_next +#endif + +#endif + diff --git a/deps/raii/include/compat/dirent_msvc.h b/deps/raii/include/compat/dirent_msvc.h new file mode 100644 index 00000000..67f295f1 --- /dev/null +++ b/deps/raii/include/compat/dirent_msvc.h @@ -0,0 +1,611 @@ +/* + * dirent.h - dirent API for Microsoft Visual Studio + * + * Copyright (C) 2006-2012 Toni Ronkko + * + * 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 and this permission notice 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 TONI RONKKO 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 OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * $Id: dirent.h,v 1.20 2014/03/19 17:52:23 tronkko Exp $ + */ +#ifndef DIRENT_MSVC_H +#define DIRENT_MSVC_H + +#include + +#if _MSC_VER >= 1900 +#include <../ucrt/stdio.h> +#include <../ucrt/wchar.h> +#include <../ucrt/string.h> +#include <../ucrt/stdlib.h> +#include <../ucrt/sys/types.h> +#include <../ucrt/errno.h> +#else +#include <../include/stdio.h> +#include <../include/wchar.h> +#include <../include/string.h> +#include <../include/stdlib.h> +#include <../include/sys/types.h> +#include <../include/errno.h> +#endif + +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* Return the exact length of d_namlen without zero terminator */ +#define _D_EXACT_NAMLEN(p)((p)->d_namlen) + +/* Return number of bytes needed to store d_namlen */ +#define _D_ALLOC_NAMLEN(p)(PATH_MAX) + +/* Wide-character version */ +struct _wdirent { + long d_ino; /* Always zero */ + unsigned short d_reclen; /* Structure size */ + size_t d_namlen; /* Length of name without \0 */ + int d_type; /* File type */ + wchar_t d_name[PATH_MAX]; /* File name */ +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + struct _wdirent ent; /* Current directory entry */ + WIN32_FIND_DATAW data; /* Private file data */ + int cached; /* True if data is valid */ + HANDLE handle; /* Win32 search handle */ + wchar_t *patt; /* Initial directory name */ +}; +typedef struct _WDIR _WDIR; + +static _WDIR *_wopendir(const wchar_t *dirname); +static struct _wdirent *_wreaddir(_WDIR *dirp); +static int _wclosedir(_WDIR *dirp); +static void _wrewinddir(_WDIR* dirp); + +/* Multi-byte character versions */ +struct dirent { + long d_ino; /* Always zero */ + unsigned short d_reclen; /* Structure size */ + size_t d_namlen; /* Length of name without \0 */ + int d_type; /* File type */ + char d_name[PATH_MAX]; /* File name */ +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + +static DIR *opendir(const char *dirname); +static struct dirent *readdir(DIR *dirp); +static int closedir(DIR *dirp); +static void rewinddir(DIR* dirp); + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir(const wchar_t *dirname) +{ + _WDIR *dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + _set_errno(ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp =(_WDIR*) malloc(sizeof(struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator */ + n = GetFullPathNameW(dirname, 0, NULL, NULL); + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt =(wchar_t*) malloc(sizeof(wchar_t) * n + 16); + if (dirp->patt) { + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + */ + n = GetFullPathNameW(dirname, n, dirp->patt, NULL); + if (n > 0) { + wchar_t *p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch(p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first(dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + _set_errno(ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + _set_errno(ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir(dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. The directory entry is returned in dirent + * structure in the d_name field. Individual directory entries returned by + * this function include regular files, sub-directories, pseudo-directories + * "." and ".." as well as volume labels, hidden files and system files. + */ +static struct _wdirent* +_wreaddir(_WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + struct _wdirent *entp; + + /* Read next directory entry */ + datap = dirent_next(dirp); + if (datap) { + size_t n; + DWORD attr; + + /* Pointer to directory entry to return */ + entp = &dirp->ent; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while(n + 1 < PATH_MAX && datap->cFileName[n] != 0) { + entp->d_name[n] = datap->cFileName[n]; + n++; + } + dirp->ent.d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof(struct _wdirent); + + } else { + + /* Last directory entry read */ + entp = NULL; + + } + + return entp; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir(_WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + free(dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free(dirp); + ok = /*success*/0; + + } else { + /* Invalid directory stream */ + _set_errno(EBADF); + ok = /*failure*/-1; + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir(_WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose(dirp->handle); + } + + /* Open new search handle */ + dirent_first(dirp); + } +} + +/* Get first directory entry(internal) */ +static WIN32_FIND_DATAW* +dirent_first(_WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileW(dirp->patt, &dirp->data); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* Get next directory entry(internal) */ +static WIN32_FIND_DATAW* +dirent_next(_WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occured */ + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir(const char *dirname) +{ + struct DIR *dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + _set_errno(ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp =(DIR*) malloc(sizeof(struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s(&n, wname, PATH_MAX, dirname, PATH_MAX); + if (!error) { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + free(dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * When working with text consoles, please note that file names returned by + * readdir() are represented in the default ANSI code page while any output to + * console is typically formatted on another code page. Thus, non-ASCII + * characters in file names will not usually display correctly on console. The + * problem can be fixed in two ways:(1) change the character set of console + * to 1252 using chcp utility and use Lucida Console font, or(2) use + * _cprintf function when writing to console. The _cprinf() will re-encode + * ANSI strings to the console code page so many non-ASCII characters will + * display correcly. + */ +static struct dirent* +readdir(DIR *dirp) +{ + WIN32_FIND_DATAW *datap; + struct dirent *entp; + + /* Read next directory entry */ + datap = dirent_next(dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, PATH_MAX, + datap->cAlternateFileName, PATH_MAX); + } + + if (!error) { + DWORD attr; + + /* Initialize directory entry for return */ + entp = &dirp->ent; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof(struct dirent); + + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an errornous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entp = &dirp->ent; + entp->d_name[0] = '?'; + entp->d_name[1] = '\0'; + entp->d_namlen = 1; + entp->d_type = DT_UNKNOWN; + entp->d_ino = 0; + entp->d_reclen = 0; + } + + } else { + /* No more directory entries */ + entp = NULL; + } + + return entp; +} + +/* + * Close directory stream. + */ +static int +closedir(DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free(dirp); + + } else { + + /* Invalid directory stream */ + _set_errno(EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir(DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir(dirp->wdirp); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s(size_t *pReturnValue, wchar_t *wcstr, + size_t sizeInWords, const char *mbstr, size_t count) +{ + return mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s(size_t *pReturnValue, char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, size_t count) +{ + return wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); +} + +#endif /*DIRENT_H*/ diff --git a/deps/raii/include/compat/endian.h b/deps/raii/include/compat/endian.h new file mode 100644 index 00000000..d9f7eb2a --- /dev/null +++ b/deps/raii/include/compat/endian.h @@ -0,0 +1,121 @@ +/* + * Public domain + * endian.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_BYTE_ORDER_H_ +#define LIBCRYPTOCOMPAT_BYTE_ORDER_H_ + +#if defined(_WIN32) + +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#define PDP_ENDIAN 3412 + +/* + * Use GCC and Visual Studio compiler defines to determine endian. + */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define BYTE_ORDER LITTLE_ENDIAN +#else +#define BYTE_ORDER BIG_ENDIAN +#endif + +#elif defined(HAVE_ENDIAN_H) +#include_next + +#elif defined(HAVE_MACHINE_ENDIAN_H) +#include_next + +#elif defined(__sun) || defined(_AIX) || defined(__hpux) +#include +#include + +#elif defined(__sgi) +#include +#include + +#endif + +#ifndef __STRICT_ALIGNMENT +#define __STRICT_ALIGNMENT +#if defined(__i386) || defined(__i386__) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(__s390__) || defined(__s390x__) || \ + defined(__aarch64__) || \ + ((defined(__arm__) || defined(__arm)) && __ARM_ARCH >= 6) +#undef __STRICT_ALIGNMENT +#endif +#endif + +#if defined(__APPLE__) && !defined(HAVE_ENDIAN_H) +#include +#define be16toh(x) OSSwapBigToHostInt16((x)) +#define htobe16(x) OSSwapHostToBigInt16((x)) +#define be32toh(x) OSSwapBigToHostInt32((x)) +#define htobe32(x) OSSwapHostToBigInt32(x) +#define htole64(x) OSSwapHostToLittleInt64(x) +#define htobe64(x) OSSwapHostToBigInt64(x) +#define le64toh(x) OSSwapLittleToHostInt64(x) +#define be64toh(x) OSSwapBigToHostInt64(x) +#endif /* __APPLE__ && !HAVE_ENDIAN_H */ + +#if defined(_WIN32) && !defined(HAVE_ENDIAN_H) +#include + +#define be16toh(x) ntohs((x)) +#define htobe16(x) htons((x)) +#define be32toh(x) ntohl((x)) +#define htobe32(x) ntohl((x)) +#define be64toh(x) ntohll((x)) + +#if !defined(ntohll) +#define ntohll(x) ((1==htonl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) +#endif +#if !defined(htonll) +#define htonll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#endif + +#define htobe64(x) ntohll((x)) +#endif /* _WIN32 && !HAVE_ENDIAN_H */ + +#ifdef __linux__ +#if !defined(betoh16) +#define betoh16 be16toh +#endif +#if !defined(betoh32) +#define betoh32 be32toh +#endif +#if !defined(betoh64) +#define betoh64 be64toh +#endif +#endif /* __linux__ */ + +#if defined(__FreeBSD__) +#if !defined(HAVE_ENDIAN_H) +#include +#endif +#if !defined(betoh16) +#define betoh16 be16toh +#endif +#if !defined(betoh32) +#define betoh32 be32toh +#endif +#if !defined(betoh64) +#define betoh64 be64toh +#endif +#endif + +#if defined(__NetBSD__) +#if !defined(betoh16) +#define betoh16 be16toh +#endif +#if !defined(betoh32) +#define betoh32 be32toh +#endif +#if !defined(betoh64) +#define betoh64 be64toh +#endif +#endif + +#endif diff --git a/deps/raii/include/compat/err.h b/deps/raii/include/compat/err.h new file mode 100644 index 00000000..945a75d6 --- /dev/null +++ b/deps/raii/include/compat/err.h @@ -0,0 +1,95 @@ +/* + * Public domain + * err.h compatibility shim + */ + +#ifdef HAVE_ERR_H + +#include_next + +#else + +#ifndef LIBCRYPTOCOMPAT_ERR_H +#define LIBCRYPTOCOMPAT_ERR_H + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +__declspec(noreturn) +#else +__attribute__((noreturn)) +#endif +static inline void +err(int eval, const char *fmt, ...) +{ + int sverrno = errno; + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + va_end(ap); + fprintf(stderr, "%s\n", strerror(sverrno)); + exit(eval); +} + +#if defined(_MSC_VER) +__declspec(noreturn) +#else +__attribute__((noreturn)) +#endif +static inline void +errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(eval); +} + +static inline void +warn(const char *fmt, ...) +{ + int sverrno = errno; + va_list ap; + + va_start(ap, fmt); + if (fmt != NULL) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, ": "); + } + va_end(ap); + fprintf(stderr, "%s\n", strerror(sverrno)); +} + +static inline void +vwarnx(const char *fmt, va_list args) +{ + if (fmt != NULL) + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); +} + +static inline void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} + +#endif + +#endif diff --git a/deps/raii/include/compat/fcntl.h b/deps/raii/include/compat/fcntl.h new file mode 100644 index 00000000..6532f269 --- /dev/null +++ b/deps/raii/include/compat/fcntl.h @@ -0,0 +1,34 @@ +/* + * Public domain + * fcntl.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/fcntl.h> +#else +#include <../include/fcntl.h> +#endif +#else +#include_next +#endif + +#endif + +/* Perform file control operations on FD. */ +int _fcntl(int fd, int cmd, ...); +#ifndef O_NONBLOCK +#define O_NONBLOCK 0x100000 +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0x200000 +#endif + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif diff --git a/deps/raii/include/compat/getopt.c b/deps/raii/include/compat/getopt.c new file mode 100644 index 00000000..c56791a2 --- /dev/null +++ b/deps/raii/include/compat/getopt.c @@ -0,0 +1,1258 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC +# include +# ifndef _ +# define _(msgid) gettext (msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +# if defined _LIBC && defined USE_IN_LIBIO +# include +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +# if HAVE_STRING_H || WIN32 /* Pete Wilson mod 7/28/02 */ +# include +# else +# include +# endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT and LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (argv[optind - 1][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf (&buf, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#else + fprintf (stderr, _("\ +%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf (&buf, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], + pfound->name); +#else + fprintf (stderr, _("\ +%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#endif + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (argv[optind][1] == '-') + { + /* --option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); +#endif + } + else + { + /* +option or -option */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#else + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#endif + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; +#endif + + if (posixly_correct) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf (&buf, _("%s: illegal option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); +#endif + } + else + { +#if defined _LIBC && defined USE_IN_LIBIO + __asprintf (&buf, _("%s: invalid option -- %c\n"), + argv[0], c); +#else + fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); +#endif + } + +#if defined _LIBC && defined USE_IN_LIBIO + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#endif + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, _("%s: option requires an argument -- %c\n"), + argv[0], c); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); +#endif + } + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); +#endif + } + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + { +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, _("\ +%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); +#endif + } + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ +#if defined _LIBC && defined USE_IN_LIBIO + char *buf; + + __asprintf (&buf, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + + if (_IO_fwide (stderr, 0) > 0) + __fwprintf (stderr, L"%s", buf); + else + fputs (buf, stderr); + + free (buf); +#else + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +/* #define TEST */ /* Pete Wilson mod 7/28/02 */ +#ifdef TEST + +#ifndef exit /* Pete Wilson mod 7/28/02 */ + int exit(int); /* Pete Wilson mod 7/28/02 */ +#endif /* Pete Wilson mod 7/28/02 */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/deps/raii/include/compat/getopt.h b/deps/raii/include/compat/getopt.h new file mode 100644 index 00000000..8a45c8b1 --- /dev/null +++ b/deps/raii/include/compat/getopt.h @@ -0,0 +1,188 @@ + +/* getopt.h */ +/* Declarations for getopt. + Copyright (C) 1989-1994, 1996-1999, 2001 Free Software + Foundation, Inc. This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute + it and/or modify it under the terms of the GNU Lesser + General Public License as published by the Free Software + Foundation; either version 2.1 of the License, or + (at your option) any later version. + + The GNU C Library is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with the GNU C Library; if not, write + to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA. */ + + + + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +# if (defined __STDC__ && __STDC__) || defined __cplusplus + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if (defined __STDC__ && __STDC__) || defined __cplusplus +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int getopt_long (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/deps/raii/include/compat/limits.h b/deps/raii/include/compat/limits.h new file mode 100644 index 00000000..73cccfc1 --- /dev/null +++ b/deps/raii/include/compat/limits.h @@ -0,0 +1,25 @@ +/* + * Public domain + * limits.h compatibility shim + */ + +#ifdef _MSC_VER +#include <../include/limits.h> +#if _MSC_VER >= 1900 +#include <../ucrt/stdlib.h> +#else +#include <../include/stdlib.h> +#endif +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif +#else +#include_next +#endif + +#ifdef __hpux +#include +#ifndef PATH_MAX +#define PATH_MAX MAXPATHLEN +#endif +#endif diff --git a/deps/raii/include/compat/need_errno.h b/deps/raii/include/compat/need_errno.h new file mode 100644 index 00000000..c58e467d --- /dev/null +++ b/deps/raii/include/compat/need_errno.h @@ -0,0 +1,167 @@ +/*** +* errno.h - system wide error numbers (set by system calls) +* +* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved. +* +* Purpose: +* This file defines the system-wide error numbers (set by +* system calls). Conforms to the XENIX standard. Extended +* for compatibility with Uniforum standard. +* [System V] +* +* [Public] +* +****/ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#if !defined(_INC_ERRNO) +#define _INC_ERRNO + +#if !defined(_WIN32) +#error ERROR: Only Win32 targets supported! +#endif + +//#include + +#if defined(__cplusplus) +extern "C" { +#endif + + + +/* Define _CRTIMP */ + +#ifndef _CRTIMP +#if defined(_DLL) +#define _CRTIMP __declspec(dllimport) +#else /* ndef _DLL */ +#define _CRTIMP +#endif /* _DLL */ +#endif /* _CRTIMP */ + + +/* Define __cdecl for non-Microsoft compilers */ + +#if ( !defined(_MSC_VER) && !defined(__cdecl) ) +#define __cdecl +#endif + +/* Define _CRTAPI1 (for compatibility with the NT SDK) */ + +#if !defined(_CRTAPI1) +#if _MSC_VER >= 800 && _M_IX86 >= 300 +#define _CRTAPI1 __cdecl +#else +#define _CRTAPI1 +#endif +#endif + +#if defined(PTW32_STATIC_LIB) && defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_WINDLL) +# undef PTW32_STATIC_LIB +# define PTW32_STATIC_TLSLIB +#endif + +#if defined(PTW32_STATIC_LIB) || defined(PTW32_STATIC_TLSLIB) +# define PTW32_DLLPORT +#elif defined(PTW32_BUILD) +# define PTW32_DLLPORT __declspec (dllexport) +# else +# define PTW32_DLLPORT __declspec (dllimport) +# endif + +/* declare reference to errno */ + +#if (defined(_MT) || defined(_MD) || defined(_DLL)) && !defined(_MAC) +PTW32_DLLPORT int * __cdecl _errno(void); +#define errno (*_errno()) +#else /* ndef _MT && ndef _MD && ndef _DLL */ +_CRTIMP extern int errno; +#endif /* _MT || _MD || _DLL */ + +/* Error Codes */ + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 36 + +/* defined differently in winsock.h on WinCE + * We don't use this value. + */ +//#if !defined(ENAMETOOLONG) +//#define ENAMETOOLONG 38 +//#endif + +#define ENOLCK 39 +#define ENOSYS 40 + +/* defined differently in winsock.h on WinCE + * We don't use this value. + */ +//#if !defined(ENOTEMPTY) +//#define ENOTEMPTY 41 +//#endif + +#define EILSEQ 42 + +/* + * POSIX 2008 - robust mutexes. + */ +#if PTW32_VERSION_MAJOR > 2 +# if !defined(EOWNERDEAD) +# define EOWNERDEAD 1000 +# endif +# if !defined(ENOTRECOVERABLE) +# define ENOTRECOVERABLE 1001 +# endif +#else +# if !defined(EOWNERDEAD) +# define EOWNERDEAD 43 +# endif +# if !defined(ENOTRECOVERABLE) +# define ENOTRECOVERABLE 44 +# endif +#endif + +/* + * Support EDEADLOCK for compatibility with older MS-C versions. + */ +#define EDEADLOCK EDEADLK + +#if defined(__cplusplus) +} +#endif + +#endif /* _INC_ERRNO */ diff --git a/deps/raii/include/compat/netdb.h b/deps/raii/include/compat/netdb.h new file mode 100644 index 00000000..d36b91db --- /dev/null +++ b/deps/raii/include/compat/netdb.h @@ -0,0 +1,10 @@ +/* + * Public domain + * netdb.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include +#endif diff --git a/deps/raii/include/compat/netinet/in.h b/deps/raii/include/compat/netinet/in.h new file mode 100644 index 00000000..d1afb27d --- /dev/null +++ b/deps/raii/include/compat/netinet/in.h @@ -0,0 +1,19 @@ +/* + * Public domain + * netinet/in.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include +#endif + +#ifndef LIBCRYPTOCOMPAT_NETINET_IN_H +#define LIBCRYPTOCOMPAT_NETINET_IN_H + +#ifdef __ANDROID__ +typedef uint16_t in_port_t; +#endif + +#endif diff --git a/deps/raii/include/compat/netinet/ip.h b/deps/raii/include/compat/netinet/ip.h new file mode 100644 index 00000000..29f17f3f --- /dev/null +++ b/deps/raii/include/compat/netinet/ip.h @@ -0,0 +1,49 @@ +/* + * Public domain + * netinet/ip.h compatibility shim + */ + +#if defined(__hpux) +#include +#endif + +#ifndef _WIN32 +#ifdef HAVE_NETINET_IP_H +#include_next +#endif +#else +#include +#endif + +/* + * Definitions for DiffServ Codepoints as per RFC2474 + */ +#ifndef IPTOS_DSCP_CS0 +#define IPTOS_DSCP_CS0 0x00 +#define IPTOS_DSCP_CS1 0x20 +#define IPTOS_DSCP_CS2 0x40 +#define IPTOS_DSCP_CS3 0x60 +#define IPTOS_DSCP_CS4 0x80 +#define IPTOS_DSCP_CS5 0xa0 +#define IPTOS_DSCP_CS6 0xc0 +#define IPTOS_DSCP_CS7 0xe0 +#endif + +#ifndef IPTOS_DSCP_AF11 +#define IPTOS_DSCP_AF11 0x28 +#define IPTOS_DSCP_AF12 0x30 +#define IPTOS_DSCP_AF13 0x38 +#define IPTOS_DSCP_AF21 0x48 +#define IPTOS_DSCP_AF22 0x50 +#define IPTOS_DSCP_AF23 0x58 +#define IPTOS_DSCP_AF31 0x68 +#define IPTOS_DSCP_AF32 0x70 +#define IPTOS_DSCP_AF33 0x78 +#define IPTOS_DSCP_AF41 0x88 +#define IPTOS_DSCP_AF42 0x90 +#define IPTOS_DSCP_AF43 0x98 +#endif + +#ifndef IPTOS_DSCP_EF +#define IPTOS_DSCP_EF 0xb8 +#endif diff --git a/deps/raii/include/compat/netinet/tcp.h b/deps/raii/include/compat/netinet/tcp.h new file mode 100644 index 00000000..c98cf741 --- /dev/null +++ b/deps/raii/include/compat/netinet/tcp.h @@ -0,0 +1,10 @@ +/* + * Public domain + * netinet/tcp.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include +#endif diff --git a/deps/raii/include/compat/poll.h b/deps/raii/include/compat/poll.h new file mode 100644 index 00000000..d81a4f7b --- /dev/null +++ b/deps/raii/include/compat/poll.h @@ -0,0 +1,60 @@ +/* + * Public domain + * + * poll(2) emulation for Windows + * + * This emulates just-enough poll functionality on Windows to work in the + * context of the openssl(1) program. This is not a replacement for + * POSIX.1-2001 poll(2). + * + * Dongsheng Song + * Brent Cook + */ + +#ifndef LIBCRYPTOCOMPAT_POLL_H +#define LIBCRYPTOCOMPAT_POLL_H + +#ifndef _WIN32 +#include_next +#else + + +/* Type used for the number of file descriptors. */ +typedef unsigned long int nfds_t; + +/* Data structure describing a polling request. */ +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; + +/* Event types that can be polled */ +#define POLLIN 0x001 /* There is data to read. */ +#define POLLPRI 0x002 /* There is urgent data to read. */ +#define POLLOUT 0x004 /* Writing now will not block. */ + +# define POLLRDNORM 0x040 /* Normal data may be read. */ +# define POLLRDBAND 0x080 /* Priority data may be read. */ +# define POLLWRNORM 0x100 /* Writing now will not block. */ +# define POLLWRBAND 0x200 /* Priority data may be written. */ + +/* Event types always implicitly polled. */ +#define POLLERR 0x008 /* Error condition. */ +#define POLLHUP 0x010 /* Hung up. */ +#define POLLNVAL 0x020 /* Invalid polling request. */ + + +#ifdef __cplusplus +extern "C" { +#endif + +int poll(struct pollfd *pfds, nfds_t nfds, int timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_POLL */ + +#endif /* LIBCRYPTOCOMPAT_POLL_H */ diff --git a/deps/raii/include/compat/pthread.h b/deps/raii/include/compat/pthread.h new file mode 100644 index 00000000..d57a38d4 --- /dev/null +++ b/deps/raii/include/compat/pthread.h @@ -0,0 +1,1264 @@ +/* This is an implementation of the threads API of the Single Unix Specification. + * + * -------------------------------------------------------------------------- + * + * pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999-2021 pthreads-win32 / pthreads4w contributors + * + * Homepage1: http://sourceware.org/pthreads-win32/ + * Homepage2: http://sourceforge.net/projects/pthreads4w/ + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * -------------------------------------------------------------------------- + */ +#pragma once +#if !defined( PTHREAD_H ) +#define PTHREAD_H + +/* There are three implementations of cancel cleanup. + * Note that pthread.h is included in both application + * compilation units and also internally for the library. + * The code here and within the library aims to work + * for all reasonable combinations of environments. + * + * The three implementations are: + * + * WIN32 SEH + * C + * C++ + * + * Please note that exiting a push/pop block via + * "return", "exit", "break", or "continue" will + * lead to different behaviour amongst applications + * depending upon whether the library was built + * using SEH, C++, or C. For example, a library built + * with SEH will call the cleanup routine, while both + * C++ and C built versions will not. + */ + +/* + * Define defaults for cleanup code. + * Note: Unless the build explicitly defines one of the following, then + * we default to standard C style cleanup. This style uses setjmp/longjmp + * in the cancellation and thread exit implementations and therefore won't + * do stack unwinding if linked to applications that have it (e.g. + * C++ apps). This is currently consistent with most/all commercial Unix + * POSIX threads implementations. + */ +#if !defined( PTW32_CLEANUP_SEH ) && !defined( PTW32_CLEANUP_CXX ) && !defined( PTW32_CLEANUP_C ) +/* + [i_a] fix for apps using pthreads-Win32: when they do not define PTW32_CLEANUP_SEH themselves, + they're screwed as they'll receive the 'PTW32_CLEANUP_C' macros which do NOT work when + the pthreads library code itself has actually been build with PTW32_CLEANUP_SEH, + which is the case when building this stuff in MSVC. + + Hence this section is made to 'sensibly autodetect' the cleanup mode, when it hasn't + been hardwired in the makefiles / project files. + + After all, who expects he MUST define one of these PTW32_CLEANUP_XXX defines in his own + code when using pthreads-Win32, for whatever reason. + */ +#if (defined(_MSC_VER) || defined(PTW32_RC_MSC)) +#define PTW32_CLEANUP_SEH +#elif defined(__cplusplus) +#define PTW32_CLEANUP_CXX +#else +# define PTW32_CLEANUP_C +#endif +#endif // SEH || CXX || C + +#if defined( PTW32_CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined (PTW32_RC_MSC)) +#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler. +#endif + +#include "_ptw32.h" + +/* + * Stop here if we are being included by the resource compiler. + */ +#if !defined(RC_INVOKED) + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX +#define PTW32_LEVEL_MAX 3 + +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L /* POSIX.1-2001 and later */ +# define PTW32_LEVEL PTW32_LEVEL_MAX /* include everything */ + +#elif defined INCLUDE_NP /* earlier than POSIX.1-2001, but... */ +# define PTW32_LEVEL 2 /* include non-portable extensions */ + +#elif defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L /* POSIX.1-1993 */ +# define PTW32_LEVEL 1 /* include 1b, 1c, and 1d */ + +#elif defined _POSIX_SOURCE /* early POSIX */ +# define PTW32_LEVEL 0 /* minimal support */ + +#else /* unspecified support level */ +# define PTW32_LEVEL PTW32_LEVEL_MAX /* include everything anyway */ +#endif + +/* + * ------------------------------------------------------------- + * + * + * Module: pthread.h + * + * Purpose: + * Provides an implementation of PThreads based upon the + * standard: + * + * POSIX 1003.1-2001 + * and + * The Single Unix Specification version 3 + * + * (these two are equivalent) + * + * in order to enhance code portability between Windows, + * various commercial Unix implementations, and Linux. + * + * See the ANNOUNCE file for a full list of conforming + * routines and defined constants, and a list of missing + * routines and constants not defined in this implementation. + * + * Authors: + * There have been many contributors to this library. + * The initial implementation was contributed by + * John Bossom, and several others have provided major + * sections or revisions of parts of the implementation. + * Often significant effort has been contributed to + * find and fix important bugs and other problems to + * improve the reliability of the library, which sometimes + * is not reflected in the amount of code which changed as + * result. + * As much as possible, the contributors are acknowledged + * in the ChangeLog file in the source code distribution + * where their changes are noted in detail. + * + * Contributors are listed in the CONTRIBUTORS file. + * + * As usual, all bouquets go to the contributors, and all + * brickbats go to the project maintainer. + * + * Maintainer: + * The code base for this project is coordinated and + * eventually pre-tested, packaged, and made available by + * + * Ross Johnson + * + * QA Testers: + * Ultimately, the library is tested in the real world by + * a host of competent and demanding scientists and + * engineers who report bugs and/or provide solutions + * which are then fixed or incorporated into subsequent + * versions of the library. Each time a bug is fixed, a + * test case is written to prove the fix and ensure + * that later changes to the code don't reintroduce the + * same error. The number of test cases is slowly growing + * and therefore so is the code reliability. + * + * Compliance: + * See the file ANNOUNCE for the list of implemented + * and not-implemented routines and defined options. + * Of course, these are all defined is this file as well. + * + * Web site: + * The source code and other information about this library + * are available from + * + * http://sources.redhat.com/pthreads-win32/ + * + * ------------------------------------------------------------- + */ + +/* + * Boolean values to make us independent of system includes. + */ +enum +{ + PTW32_FALSE = 0, + PTW32_TRUE = (! PTW32_FALSE) +}; + +#include +#include "sched.h" + +#if defined(_MSC_VER) && _MSC_VER >= 1900 +# define HAVE_STRUCT_TIMESPEC +# ifndef _TIMESPEC_DEFINED +# define _TIMESPEC_DEFINED +# endif +#endif + +/* + * ------------------------------------------------------------- + * + * POSIX 1003.1-2001 Options + * ========================= + * + * Options are normally set in , which is not provided + * with pthreads-win32. + * + * For conformance with the Single Unix Specification (version 3), all of the + * options below are defined, and have a value of either -1 (not supported) + * or yyyymm[dd]L (supported). + * + * These options can neither be left undefined nor have a value of 0, because + * either indicates that sysconf(), which is not implemented, may be used at + * runtime to check the status of the option. + * + * _POSIX_THREADS (== 20080912L) + * If == 20080912L, you can use threads + * + * _POSIX_THREAD_ATTR_STACKSIZE (== 200809L) + * If == 200809L, you can control the size of a thread's + * stack + * pthread_attr_getstacksize + * pthread_attr_setstacksize + * + * _POSIX_THREAD_ATTR_STACKADDR (== -1) + * If == 200809L, you can allocate and control a thread's + * stack. If not supported, the following functions + * will return ENOSYS, indicating they are not + * supported: + * pthread_attr_getstackaddr + * pthread_attr_setstackaddr + * + * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1) + * If == 200112L, you can use realtime scheduling. + * This option indicates that the behaviour of some + * implemented functions conforms to the additional TPS + * requirements in the standard. E.g. rwlocks favour + * writers over readers when threads have equal priority. + * + * _POSIX_THREAD_PRIO_INHERIT (== -1) + * If == 200809L, you can create priority inheritance + * mutexes. + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PRIO_PROTECT (== -1) + * If == 200809L, you can create priority ceiling mutexes + * Indicates the availability of: + * pthread_mutex_getprioceiling + * pthread_mutex_setprioceiling + * pthread_mutexattr_getprioceiling + * pthread_mutexattr_getprotocol + + * pthread_mutexattr_setprioceiling + * pthread_mutexattr_setprotocol + + * + * _POSIX_THREAD_PROCESS_SHARED (== -1) + * If set, you can create mutexes and condition + * variables that can be shared with another + * process.If set, indicates the availability + * of: + * pthread_mutexattr_getpshared + * pthread_mutexattr_setpshared + * pthread_condattr_getpshared + * pthread_condattr_setpshared + * + * _POSIX_THREAD_SAFE_FUNCTIONS (== 200809L) + * If == 200809L you can use the special *_r library + * functions that provide thread-safe behaviour + * + * _POSIX_READER_WRITER_LOCKS (== 200809L) + * If == 200809L, you can use read/write locks + * + * _POSIX_SPIN_LOCKS (== 200809L) + * If == 200809L, you can use spin locks + * + * _POSIX_BARRIERS (== 200809L) + * If == 200809L, you can use barriers + * + * _POSIX_ROBUST_MUTEXES (== 200809L) + * If == 200809L, you can use robust mutexes + * Officially this should also imply + * _POSIX_THREAD_PROCESS_SHARED != -1 however + * not here yet. + * + * ------------------------------------------------------------- + */ + +/* + * POSIX Options + */ +#undef _POSIX_THREADS +#define _POSIX_THREADS 200809L + +#undef _POSIX_READER_WRITER_LOCKS +#define _POSIX_READER_WRITER_LOCKS 200809L + +#undef _POSIX_SPIN_LOCKS +#define _POSIX_SPIN_LOCKS 200809L + +#undef _POSIX_BARRIERS +#define _POSIX_BARRIERS 200809L + +#undef _POSIX_THREAD_SAFE_FUNCTIONS +#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L + +#undef _POSIX_THREAD_ATTR_STACKSIZE +#define _POSIX_THREAD_ATTR_STACKSIZE 200809L + +#undef _POSIX_ROBUST_MUTEXES +#define _POSIX_ROBUST_MUTEXES 200809L + +/* + * The following options are not supported + */ +#undef _POSIX_THREAD_ATTR_STACKADDR +#define _POSIX_THREAD_ATTR_STACKADDR -1 + +#undef _POSIX_THREAD_PRIO_INHERIT +#define _POSIX_THREAD_PRIO_INHERIT -1 + +#undef _POSIX_THREAD_PRIO_PROTECT +#define _POSIX_THREAD_PRIO_PROTECT -1 + +/* TPS is not fully supported. */ +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#define _POSIX_THREAD_PRIORITY_SCHEDULING -1 + +#undef _POSIX_THREAD_PROCESS_SHARED +#define _POSIX_THREAD_PROCESS_SHARED -1 + + +/* + * POSIX 1003.1-2001 Limits + * =========================== + * + * These limits are normally set in , which is not provided with + * pthreads-win32. + * + * PTHREAD_DESTRUCTOR_ITERATIONS + * Maximum number of attempts to destroy + * a thread's thread-specific data on + * termination (must be at least 4) + * + * PTHREAD_KEYS_MAX + * Maximum number of thread-specific data keys + * available per process (must be at least 128) + * + * PTHREAD_STACK_MIN + * Minimum supported stack size for a thread + * + * PTHREAD_THREADS_MAX + * Maximum number of threads supported per + * process (must be at least 64). + * + * SEM_NSEMS_MAX + * The maximum number of semaphores a process can have. + * (must be at least 256) + * + * SEM_VALUE_MAX + * The maximum value a semaphore can have. + * (must be at least 32767) + * + */ +#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS +#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS 4 + +#undef PTHREAD_DESTRUCTOR_ITERATIONS +#define PTHREAD_DESTRUCTOR_ITERATIONS _POSIX_THREAD_DESTRUCTOR_ITERATIONS + +#undef _POSIX_THREAD_KEYS_MAX +#define _POSIX_THREAD_KEYS_MAX 128 + +#undef PTHREAD_KEYS_MAX +#define PTHREAD_KEYS_MAX _POSIX_THREAD_KEYS_MAX + +#undef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 0 + +#undef _POSIX_THREAD_THREADS_MAX +#define _POSIX_THREAD_THREADS_MAX 64 + +/* Arbitrary value */ +#undef PTHREAD_THREADS_MAX +#define PTHREAD_THREADS_MAX 2019 + +#undef _POSIX_SEM_NSEMS_MAX +#define _POSIX_SEM_NSEMS_MAX 256 + +/* Arbitrary value */ +#undef SEM_NSEMS_MAX +#define SEM_NSEMS_MAX 1024 + +#undef _POSIX_SEM_VALUE_MAX +#define _POSIX_SEM_VALUE_MAX 32767 + +#undef SEM_VALUE_MAX +#define SEM_VALUE_MAX INT_MAX + + +#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX +# include +#else +/* Generic handle type - intended to provide the lifetime-uniqueness that + * a simple pointer can't. It should scale for either + * 32 or 64 bit systems. + * + * The constraint with this approach is that applications must + * strictly comply with POSIX, e.g. not assume scalar type, only + * compare pthread_t using the API function pthread_equal(), etc. + * + * Non-conforming applications could use the element 'p' to compare, + * e.g. for sorting, but it will be up to the application to determine + * if handles are live or dead, or resurrected for an entirely + * new/different thread. I.e. the thread is valid iff + * x == p->ptHandle.x + */ +typedef struct +{ + void * p; /* Pointer to actual object */ + size_t x; /* Extra information - reuse count etc */ +} ptw32_handle_t; + +typedef ptw32_handle_t pthread_t; +typedef struct pthread_attr_t_ * pthread_attr_t; +typedef struct pthread_once_t_ pthread_once_t; +typedef struct pthread_key_t_ * pthread_key_t; +typedef struct pthread_mutex_t_ * pthread_mutex_t; +typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t; +typedef struct pthread_cond_t_ * pthread_cond_t; +typedef struct pthread_condattr_t_ * pthread_condattr_t; +#endif + +typedef struct pthread_rwlock_t_ * pthread_rwlock_t; +typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t; +typedef struct pthread_spinlock_t_ * pthread_spinlock_t; +typedef struct pthread_barrier_t_ * pthread_barrier_t; +typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t; + +/* + * ==================== + * ==================== + * POSIX Threads + * ==================== + * ==================== + */ + +enum +{ + /* + * pthread_attr_{get,set}detachstate + */ + PTHREAD_CREATE_JOINABLE = 0, /* Default */ + PTHREAD_CREATE_DETACHED = 1, + /* + * pthread_attr_{get,set}inheritsched + */ + PTHREAD_INHERIT_SCHED = 0, + PTHREAD_EXPLICIT_SCHED = 1, /* Default */ + /* + * pthread_{get,set}scope + */ + PTHREAD_SCOPE_PROCESS = 0, + PTHREAD_SCOPE_SYSTEM = 1, /* Default */ + /* + * pthread_setcancelstate paramters + */ + PTHREAD_CANCEL_ENABLE = 0, /* Default */ + PTHREAD_CANCEL_DISABLE = 1, + /* + * pthread_setcanceltype parameters + */ + PTHREAD_CANCEL_ASYNCHRONOUS = 0, + PTHREAD_CANCEL_DEFERRED = 1, /* Default */ + /* + * pthread_mutexattr_{get,set}pshared + * pthread_condattr_{get,set}pshared + */ + PTHREAD_PROCESS_PRIVATE = 0, + PTHREAD_PROCESS_SHARED = 1, + /* + * pthread_mutexattr_{get,set}robust + */ + PTHREAD_MUTEX_STALLED = 0, /* Default */ + PTHREAD_MUTEX_ROBUST = 1, + /* + * pthread_barrier_wait + */ + PTHREAD_BARRIER_SERIAL_THREAD = -1 +}; + +/* + * ==================== + * ==================== + * Cancellation + * ==================== + * ==================== + */ +#define PTHREAD_CANCELED ((void *)(size_t) -1) + + +/* + * ==================== + * ==================== + * Once Key + * ==================== + * ==================== + */ +#if PTW32_VERSION_MAJOR > 2 + +#define PTHREAD_ONCE_INIT { 0, PTW32_FALSE } + +struct pthread_once_t_ +{ + void * lock; /* MCS lock */ + int done; /* indicates if user function has been executed */ +}; + +#else + +#define PTHREAD_ONCE_INIT { PTW32_FALSE, 0 } + +struct pthread_once_t_ +{ + int done; /* indicates if user function has been executed */ + void * lock; /* MCS lock */ +}; + +#endif + + +/* + * ==================== + * ==================== + * Object initialisers + * ==================== + * ==================== + */ +#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1) +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2) +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3) + +/* + * Compatibility with LinuxThreads + */ +#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER +#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER + +#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1) + +#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1) + +#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1) + + +/* + * Mutex types. + */ +enum +{ + /* Compatibility with LinuxThreads */ + PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP, + /* For compatibility with POSIX */ + PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP, + PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP, + PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP, + PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL +}; + + +typedef struct ptw32_cleanup_t ptw32_cleanup_t; + +#if defined(_MSC_VER) +/* Disable MSVC 'anachronism used' warning */ +#pragma warning( push ) +#pragma warning( disable : 4229 ) +#endif + +typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *); + +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif + +struct ptw32_cleanup_t +{ + ptw32_cleanup_callback_t routine; + void *arg; + struct ptw32_cleanup_t *prev; +}; + +#if defined(PTW32_CLEANUP_SEH) + /* + * WIN32 SEH version of cancel cleanup. + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + _cleanup.routine = (ptw32_cleanup_callback_t)(_rout); \ + _cleanup.arg = (_arg); \ + __try \ + { \ + +#define pthread_cleanup_pop( _execute ) \ + } \ + __finally \ + { \ + if( _execute || AbnormalTermination()) \ + { \ + (*(_cleanup.routine))( _cleanup.arg ); \ + } \ + } \ + } + +#else /* PTW32_CLEANUP_SEH */ + +#if defined(PTW32_CLEANUP_C) + + /* + * C implementation of PThreads cancel cleanup + */ + +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + ptw32_cleanup_t _cleanup; \ + \ + ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \ + +#define pthread_cleanup_pop( _execute ) \ + (void) ptw32_pop_cleanup( _execute ); \ + } + +#else /* PTW32_CLEANUP_C */ + +#if defined(PTW32_CLEANUP_CXX) + +#if defined(__cplusplus) + + /* + * C++ version of cancel cleanup. + * - John E. Bossom. + */ + + class PThreadCleanup { + /* + * PThreadCleanup + * + * Purpose + * This class is a C++ helper class that is + * used to implement pthread_cleanup_push/ + * pthread_cleanup_pop. + * The destructor of this class automatically + * pops the pushed cleanup routine regardless + * of how the code exits the scope + * (i.e. such as by an exception) + */ + ptw32_cleanup_callback_t cleanUpRout; + void * obj; + int executeIt; + + public: + PThreadCleanup() : + cleanUpRout( 0 ), + obj( 0 ), + executeIt( 0 ) + /* + * No cleanup performed + */ + { + } + + PThreadCleanup( + ptw32_cleanup_callback_t routine, + void * arg ) : + cleanUpRout( routine ), + obj( arg ), + executeIt( 1 ) + /* + * Registers a cleanup routine for 'arg' + */ + { + } + + ~PThreadCleanup() + { + if ( executeIt && ((void *) cleanUpRout != (void *) 0) ) + { + (void) (*cleanUpRout)( obj ); + } + } + + void execute( int exec ) + { + executeIt = exec; + } + }; + + /* + * C++ implementation of PThreads cancel cleanup; + * This implementation takes advantage of a helper + * class who's destructor automatically calls the + * cleanup routine if we exit our scope weirdly + */ +#define pthread_cleanup_push( _rout, _arg ) \ + { \ + PThreadCleanup cleanup((ptw32_cleanup_callback_t)(_rout), \ + (void *) (_arg) ); + +#define pthread_cleanup_pop( _execute ) \ + cleanup.execute( _execute ); \ + } + +#endif // __cplusplus + +#else + +#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined. + +#endif /* PTW32_CLEANUP_CXX */ + +#endif /* PTW32_CLEANUP_C */ + +#endif /* PTW32_CLEANUP_SEH */ + + +/* + * =============== + * =============== + * Methods + * =============== + * =============== + */ + +PTW32_BEGIN_C_DECLS + +/* + * PThread Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getaffinity_np (const pthread_attr_t * attr, + size_t cpusetsize, + cpu_set_t * cpuset); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr, + int *detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr, + void **stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr, + size_t * stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setaffinity_np (pthread_attr_t * attr, + size_t cpusetsize, + const cpu_set_t * cpuset); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr, + int detachstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr, + void *stackaddr); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr, + size_t stacksize); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *, + int *); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr, + int inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr, + int * inheritsched); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *, + int); + +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *, + int *); + +/* + * PThread Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, + const pthread_attr_t * attr, + void * (PTW32_CDECL *start) (void *), + void *arg); + +PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid); + +PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1, + pthread_t t2); + +PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr); + +PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread, + void **value_ptr); + +PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state, + int *oldstate); + +PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type, + int *oldtype); + +PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void); + +PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control, + void (PTW32_CDECL *init_routine) (void)); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX +PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute); + +PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup, + ptw32_cleanup_callback_t routine, + void *arg); +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Thread Specific Data Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key, + void (PTW32_CDECL *destructor) (void *)); + +PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key); + +PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key, + const void *value); + +PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key); + + +/* + * Mutex Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, + int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust( + pthread_mutexattr_t *attr, + int robust); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust( + const pthread_mutexattr_t * attr, + int * robust); + +/* + * Barrier Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t + * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr, + int pshared); + +/* + * Mutex Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex, + const pthread_mutexattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex); + +/* + * Spinlock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock); + +/* + * Barrier Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier, + const pthread_barrierattr_t * attr, + unsigned int count); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier); + +PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier); + +/* + * Condition Variable Attribute Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr, + int pshared); + +/* + * Condition Variable Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond, + const pthread_condattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond, + pthread_mutex_t * mutex); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond, + pthread_mutex_t * mutex, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond); + +PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond); + +/* + * Scheduling + */ +PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread, + int policy, + const struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread, + int *policy, + struct sched_param *param); + +PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int); + +PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void); + +/* + * Read-Write Lock Functions + */ +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock, + const pthread_rwlockattr_t *attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock, + const struct timespec *abstime); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr, + int *pshared); + +PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, + int pshared); + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 + +/* + * Signal Functions. Should be defined in but MSVC and MinGW32 + * already have signal.h that don't define these. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig); + +/* + * Non-portable functions + */ + +/* + * Compatibility with Linux. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, + int kind); +PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, + int *kind); +PTW32_DLLPORT int PTW32_CDECL pthread_timedjoin_np(pthread_t thread, + void **value_ptr, + const struct timespec *abstime); +PTW32_DLLPORT int PTW32_CDECL pthread_tryjoin_np(pthread_t thread, + void **value_ptr); +PTW32_DLLPORT int PTW32_CDECL pthread_setaffinity_np(pthread_t thread, + size_t cpusetsize, + const cpu_set_t *cpuset); +PTW32_DLLPORT int PTW32_CDECL pthread_getaffinity_np(pthread_t thread, + size_t cpusetsize, + cpu_set_t *cpuset); + +/* + * Possibly supported by other POSIX threads implementations + */ +PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval); +PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void); +PTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread); + +/* + * Useful if an application wants to statically link + * the lib rather than load the DLL at run-time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void); +PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void); + +/* + * Returns the first parameter "abstime" modified to represent the current system time. + * If "relative" is not NULL it represents an interval to add to "abstime". + */ + +PTW32_DLLPORT struct timespec * PTW32_CDECL pthread_win32_getabstime_np( + struct timespec * abstime, + const struct timespec * relative); + +/* + * Features that are auto-detected at load/run time. + */ +PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int); +enum ptw32_features +{ + PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */ + PTW32_ALERTABLE_ASYNC_CANCEL = 0x0002 /* Can cancel blocked threads. */ +}; + +/* + * Register a system time change with the library. + * Causes the library to perform various functions + * in response to the change. Should be called whenever + * the application's top level window receives a + * WM_TIMECHANGE message. It can be passed directly to + * pthread_create() as a new thread if desired. + */ +PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */ + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* + * Returns the Win32 HANDLE for the POSIX thread. + */ +PTW32_DLLPORT void * PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread); +/* + * Returns the win32 thread ID for POSIX thread. + */ +PTW32_DLLPORT unsigned long PTW32_CDECL pthread_getw32threadid_np (pthread_t thread); + +/* + * Sets the POSIX thread name. If _MSC_VER is defined the name should be displayed by + * the MSVS debugger. + */ +#if defined(PTW32_COMPATIBILITY_BSD) || defined(PTW32_COMPATIBILITY_TRU64) +#define PTHREAD_MAX_NAMELEN_NP 16 +PTW32_DLLPORT int PTW32_CDECL pthread_setname_np (pthread_t thr, const char * name, void * arg); +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setname_np (pthread_attr_t * attr, const char * name, void * arg); +#else +PTW32_DLLPORT int PTW32_CDECL pthread_setname_np (pthread_t thr, const char * name); +PTW32_DLLPORT int PTW32_CDECL pthread_attr_setname_np (pthread_attr_t * attr, const char * name); +#endif + +PTW32_DLLPORT int PTW32_CDECL pthread_getname_np (pthread_t thr, char * name, int len); +PTW32_DLLPORT int PTW32_CDECL pthread_attr_getname_np (pthread_attr_t * attr, char * name, int len); + + +/* + * Protected Methods + * + * This function blocks until the given WIN32 handle + * is signalled or pthread_cancel had been called. + * This function allows the caller to hook into the + * PThreads cancel mechanism. It is implemented using + * + * WaitForMultipleObjects + * + * on 'waitHandle' and a manually reset WIN32 Event + * used to implement pthread_cancel. The 'timeout' + * argument to TimedWait is simply passed to + * WaitForMultipleObjects. + */ +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (void *waitHandle); +PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (void *waitHandle, + unsigned long timeout); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +/* + * Declare a thread-safe errno for Open Watcom + * (note: this has not been tested in a long time) + */ +#if defined(__WATCOMC__) && !defined(errno) +# if defined(_MT) || defined(_DLL) + __declspec(dllimport) extern int * __cdecl _errno(void); +# define errno (*_errno()) +# endif +#endif + +#if defined (PTW32_USES_SEPARATE_CRT) && (defined(PTW32_CLEANUP_CXX) || defined(PTW32_CLEANUP_SEH)) +typedef void (*ptw32_terminate_handler)(); +PTW32_DLLPORT ptw32_terminate_handler PTW32_CDECL pthread_win32_set_terminate_np(ptw32_terminate_handler termFunction); +#endif + +#if defined(__cplusplus) + +/* + * Internal exceptions + */ +class ptw32_exception {}; +class ptw32_exception_cancel : public ptw32_exception {}; +class ptw32_exception_exit : public ptw32_exception {}; + +#endif + +#if PTW32_LEVEL >= PTW32_LEVEL_MAX + +/* FIXME: This is only required if the library was built using SEH */ +/* + * Get internal SEH tag + */ +PTW32_DLLPORT unsigned long PTW32_CDECL ptw32_get_exception_services_code(void); + +#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */ + +#if !defined(PTW32_BUILD) + +#if defined(PTW32_CLEANUP_SEH) + +/* + * Redefine the SEH __except keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#define __except( E ) \ + __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \ + ? EXCEPTION_CONTINUE_SEARCH : ( E ) ) + +#endif /* PTW32_CLEANUP_SEH */ + +#if defined(PTW32_CLEANUP_CXX) + +/* + * Redefine the C++ catch keyword to ensure that applications + * propagate our internal exceptions up to the library's internal handlers. + */ +#if defined(_MSC_VER) + /* + * WARNING: Replace any 'catch( ... )' with '__PtW32CatchAll' + * if you want Pthread-Win32 cancellation and pthread_exit to work. + */ + +#if !defined(__PtW32NoCatchWarn) + +#pragma message("Specify \"/D__PtW32NoCatchWarn\" compiler flag to skip this message.") +#pragma message("------------------------------------------------------------------") +#pragma message("When compiling applications with MSVC++ and C++ exception handling:") +#pragma message(" Replace any 'catch( ... )' in routines called from POSIX threads") +#pragma message(" with '__PtW32CatchAll' or 'CATCHALL' if you want POSIX thread") +#pragma message(" cancellation and pthread_exit to work. For example:") +#pragma message("") +#pragma message(" #if defined(__PtW32CatchAll)") +#pragma message(" __PtW32CatchAll") +#pragma message(" #else") +#pragma message(" catch(...)") +#pragma message(" #endif") +#pragma message(" {") +#pragma message(" /* Catchall block processing */") +#pragma message(" }") +#pragma message("------------------------------------------------------------------") + +#endif + +#define __PtW32CatchAll \ + catch( ptw32_exception & ) { throw; } \ + catch( ... ) + +#else /* _MSC_VER */ + +#define catch( E ) \ + catch( ptw32_exception & ) { throw; } \ + catch( E ) + +#endif /* _MSC_VER */ + +#endif /* PTW32_CLEANUP_CXX */ + +#endif /* ! PTW32_BUILD */ + +PTW32_END_C_DECLS + +#undef PTW32_LEVEL +#undef PTW32_LEVEL_MAX + +#endif /* ! RC_INVOKED */ + +#endif /* PTHREAD_H */ diff --git a/deps/raii/include/compat/readpassphrase.h b/deps/raii/include/compat/readpassphrase.h new file mode 100644 index 00000000..34169194 --- /dev/null +++ b/deps/raii/include/compat/readpassphrase.h @@ -0,0 +1,44 @@ +/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ + +/* + * Copyright (c) 2000, 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +#ifdef HAVE_READPASSPHRASE_H + +#include_next + +#else + +#ifndef _READPASSPHRASE_H_ +#define _READPASSPHRASE_H_ + +#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ +#define RPP_ECHO_ON 0x01 /* Leave echo on. */ +#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ +#define RPP_FORCELOWER 0x04 /* Force input to lower case. */ +#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ +#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ +#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ + +char * readpassphrase(const char *, char *, size_t, int); + +#endif /* !_READPASSPHRASE_H_ */ + +#endif diff --git a/deps/raii/include/compat/resolv.h b/deps/raii/include/compat/resolv.h new file mode 100644 index 00000000..b8044605 --- /dev/null +++ b/deps/raii/include/compat/resolv.h @@ -0,0 +1,24 @@ +/* + * Public domain + * resolv.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_RESOLV_H +#define LIBCRYPTOCOMPAT_RESOLV_H + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/resolv.h> +#else +#include <../include/resolv.h> +#endif +#elif defined(HAVE_RESOLV_H) +#include_next +#endif + +#ifndef HAVE_B64_NTOP +int b64_ntop(unsigned char const *, size_t, char *, size_t); +int b64_pton(char const *, unsigned char *, size_t); +#endif + +#endif diff --git a/deps/raii/include/compat/sched.h b/deps/raii/include/compat/sched.h new file mode 100644 index 00000000..26948c13 --- /dev/null +++ b/deps/raii/include/compat/sched.h @@ -0,0 +1,246 @@ +/* + * Module: sched.h + * + * Purpose: + * Provides an implementation of POSIX realtime extensions + * as defined in + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999-2021 pthreads-win32 / pthreads4w contributors + * + * Homepage1: http://sourceware.org/pthreads-win32/ + * Homepage2: http://sourceforge.net/projects/pthreads4w/ + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * -------------------------------------------------------------------------- + */ +#pragma once +#if !defined(_SCHED_H) +#define _SCHED_H +#define __SCHED_H_SOURCED__ + +#include "_ptw32.h" + +/* We need a typedef for pid_t, (and POSIX requires to + * define it, as it is defined in , but it does NOT + * sanction exposure of everything from ); there is + * no pid_t in Windows anyway, (except that MinGW does define it + * in their ), so just provide a suitable typedef, + * but note that we must do so cautiously, to avoid a typedef + * conflict if MinGW's is also #included: + */ +#if ! defined __MINGW32__ || ! defined __have_typedef_pid_t + +# if defined __MINGW64__ || defined WIN64 || defined _WIN64 + typedef __int64 pid_t; +# else + typedef int pid_t; +#endif + +#if ! defined __GNUC__ || __GNUC__ < 4 +/* GCC v4.0 and later, (as used by MinGW), allows us to repeat a + * typedef, provided every duplicate is consistent; only set this + * multiple definition guard when we cannot be certain that it is + * permissable to repeat typedefs. + */ +#define __have_typedef_pid_t 1 +#endif +#endif + +/* POSIX.1-1993 says that WILL expose all of + */ +#undef __SCHED_H_SOURCED__ +#if _POSIX_C_SOURCE >= 200112L +/* POSIX.1-2001 and later revises this to say only that it MAY do so; + * only struct timespec, and associated time_t are actually required, + * so prefer to be selective; (MinGW.org's offers an option + * for selective #inclusion, when __SCHED_H_SOURCED__ is defined): + */ +#define __SCHED_H_SOURCED__ +#define __need_struct_timespec +#define __need_time_t +#endif +#include + +#if defined __MINGW64__ || _MSC_VER >= 1900 +/* These are known to define struct timespec, when has been + * #included, but may not, (probably don't), follow the convention of + * defining __struct_timespec_defined, as adopted by MinGW.org; for + * these cases, we unconditionally assume that struct timespec has + * been defined, otherwise, if MinGW.org's criterion has not been + * satisfied... + */ +#elif ! defined __struct_timespec_defined +# ifndef _TIMESPEC_DEFINED +# define _TIMESPEC_DEFINED +struct timespec +{ /* ...we fall back on this explicit definition. + */ + time_t tv_sec; + int tv_nsec; +}; +# endif +#endif + +/* + * Microsoft VC++6.0 lacks these *_PTR types + */ +#if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(PTW32_HAVE_DWORD_PTR) +typedef unsigned long ULONG_PTR; +typedef ULONG_PTR DWORD_PTR; +#endif + +/* Thread scheduling policies */ + +enum +{ + SCHED_OTHER = 0, + SCHED_FIFO, + SCHED_RR, + SCHED_MIN = SCHED_OTHER, + SCHED_MAX = SCHED_RR +}; + +struct sched_param +{ + int sched_priority; +}; + +/* + * CPU affinity + * + * cpu_set_t: + * Considered opaque but cannot be an opaque pointer due to the need for + * compatibility with GNU systems and sched_setaffinity() et.al., which + * include the cpusetsize parameter "normally set to sizeof(cpu_set_t)". + * + * FIXME: These are GNU, and NOT specified by POSIX; maybe consider + * occluding them within a _GNU_SOURCE (or similar) feature test. + */ + +#define CPU_SETSIZE (sizeof(size_t)*8) + +#define CPU_COUNT(setptr) (_sched_affinitycpucount(setptr)) + +#define CPU_ZERO(setptr) (_sched_affinitycpuzero(setptr)) + +#define CPU_SET(cpu, setptr) (_sched_affinitycpuset((cpu),(setptr))) + +#define CPU_CLR(cpu, setptr) (_sched_affinitycpuclr((cpu),(setptr))) + +#define CPU_ISSET(cpu, setptr) (_sched_affinitycpuisset((cpu),(setptr))) + +#define CPU_AND(destsetptr, srcset1ptr, srcset2ptr) (_sched_affinitycpuand((destsetptr),(srcset1ptr),(srcset2ptr))) + +#define CPU_OR(destsetptr, srcset1ptr, srcset2ptr) (_sched_affinitycpuor((destsetptr),(srcset1ptr),(srcset2ptr))) + +#define CPU_XOR(destsetptr, srcset1ptr, srcset2ptr) \ + (_sched_affinitycpuxor((destsetptr),(srcset1ptr),(srcset2ptr))) + +#define CPU_EQUAL(set1ptr, set2ptr) (_sched_affinitycpuequal((set1ptr),(set2ptr))) + +typedef union +{ + char cpuset[CPU_SETSIZE/8]; + size_t _align; +} cpu_set_t; + +PTW32_BEGIN_C_DECLS + +PTW32_DLLPORT int PTW32_CDECL sched_yield (void); + +PTW32_DLLPORT int PTW32_CDECL sched_get_priority_min (int policy); + +PTW32_DLLPORT int PTW32_CDECL sched_get_priority_max (int policy); + +/* FIXME: this declaration of sched_setscheduler() is NOT as prescribed + * by POSIX; it lacks const struct sched_param * as third argument. + */ +PTW32_DLLPORT int PTW32_CDECL sched_setscheduler (pid_t pid, int policy); + +PTW32_DLLPORT int PTW32_CDECL sched_getscheduler (pid_t pid); + +/* FIXME: In addition to the above five functions, POSIX also requires: + * + * int sched_getparam (pid_t, struct sched_param *); + * int sched_setparam (pid_t, const struct sched_param *); + * + * both of which are conspicuous by their absence here! + */ + +/* Compatibility with Linux - not standard in POSIX + * FIXME: consider occluding within a _GNU_SOURCE (or similar) feature test. + */ +PTW32_DLLPORT int PTW32_CDECL sched_setaffinity (pid_t pid, size_t cpusetsize, cpu_set_t *mask); + +PTW32_DLLPORT int PTW32_CDECL sched_getaffinity (pid_t pid, size_t cpusetsize, cpu_set_t *mask); + +/* + * Support routines and macros for cpu_set_t + */ +PTW32_DLLPORT int PTW32_CDECL _sched_affinitycpucount (const cpu_set_t *set); + +PTW32_DLLPORT void PTW32_CDECL _sched_affinitycpuzero (cpu_set_t *pset); + +PTW32_DLLPORT void PTW32_CDECL _sched_affinitycpuset (int cpu, cpu_set_t *pset); + +PTW32_DLLPORT void PTW32_CDECL _sched_affinitycpuclr (int cpu, cpu_set_t *pset); + +PTW32_DLLPORT int PTW32_CDECL _sched_affinitycpuisset (int cpu, const cpu_set_t *pset); + +PTW32_DLLPORT void PTW32_CDECL _sched_affinitycpuand(cpu_set_t *pdestset, const cpu_set_t *psrcset1, const cpu_set_t *psrcset2); + +PTW32_DLLPORT void PTW32_CDECL _sched_affinitycpuor(cpu_set_t *pdestset, const cpu_set_t *psrcset1, const cpu_set_t *psrcset2); + +PTW32_DLLPORT void PTW32_CDECL _sched_affinitycpuxor(cpu_set_t *pdestset, const cpu_set_t *psrcset1, const cpu_set_t *psrcset2); + +PTW32_DLLPORT int PTW32_CDECL _sched_affinitycpuequal (const cpu_set_t *pset1, const cpu_set_t *pset2); + +/* Note that this macro returns ENOTSUP rather than ENOSYS, as + * might be expected. However, returning ENOSYS should mean that + * sched_get_priority_{min,max} are not implemented as well as + * sched_rr_get_interval. This is not the case, since we just + * don't support round-robin scheduling. Therefore I have chosen + * to return the same value as sched_setscheduler when SCHED_RR + * is passed to it. + * + * FIXME: POSIX requires this to be defined as a function; this + * macro implementation is permitted IN ADDITION to the function, + * but the macro alone is not POSIX compliant! Worse still, it + * imposes a requirement on the caller, to ensure that both the + * declaration of errno, and the definition of ENOTSUP, are in + * scope at point of call, (which it may wish to do anyway, but + * POSIX imposes no such constraint)! + */ +#define sched_rr_get_interval(_pid, _interval) \ + ( errno = ENOTSUP, (int) -1 ) + +PTW32_END_C_DECLS + +#undef __SCHED_H_SOURCED__ +#endif /* !_SCHED_H */ diff --git a/deps/raii/include/compat/semaphore.h b/deps/raii/include/compat/semaphore.h new file mode 100644 index 00000000..dd620982 --- /dev/null +++ b/deps/raii/include/compat/semaphore.h @@ -0,0 +1,122 @@ +/* + * Module: semaphore.h + * + * Purpose: + * Semaphores aren't actually part of the PThreads standard. + * They are defined by the POSIX Standard: + * + * POSIX 1003.1b-1993 (POSIX.1b) + * + * -------------------------------------------------------------------------- + * + * pthreads-win32 - POSIX Threads Library for Win32 + * Copyright(C) 1998 John E. Bossom + * Copyright(C) 1999-2021 pthreads-win32 / pthreads4w contributors + * + * Homepage1: http://sourceware.org/pthreads-win32/ + * Homepage2: http://sourceforge.net/projects/pthreads4w/ + * + * The current list of contributors is contained + * in the file CONTRIBUTORS included with the source + * code distribution. The list can also be seen at the + * following World Wide Web location: + * http://sources.redhat.com/pthreads-win32/contributors.html + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library in the file COPYING.LIB; + * if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * -------------------------------------------------------------------------- + */ +#pragma once +#if !defined( SEMAPHORE_H ) +#define SEMAPHORE_H + +/* FIXME: POSIX.1 says that _POSIX_SEMAPHORES should be defined + * in , not here; for later POSIX.1 versions, its value + * should match the corresponding _POSIX_VERSION number, but in + * the case of POSIX.1b-1993, the value is unspecified. + * + * Notwithstanding the above, since POSIX semaphores, (and indeed + * having any to #include), are not a standard feature + * on MS-Windows, it is convenient to retain this definition here; + * we may consider adding a hook, to make it selectively available + * for inclusion by , in those cases (e.g. MinGW) where + * is provided. + */ +#define _POSIX_SEMAPHORES + +/* Internal macros, common to the public interfaces for various + * pthreads-win32 components, are defined in <_ptw32.h>; we must + * include them here. + */ +#include "_ptw32.h" + +/* The sem_timedwait() function was added in POSIX.1-2001; it + * requires struct timespec to be defined, at least as a partial + * (a.k.a. incomplete) data type. Forward declare it as such, + * then include selectively, to acquire a complete + * definition, (if available). + */ +struct timespec; +#define __need_struct_timespec +#include + +/* The data type used to represent our semaphore implementation, + * as required by POSIX.1; FIXME: consider renaming the underlying + * structure tag, to avoid possible pollution of user namespace. + */ +typedef struct sem_t_ * sem_t; + +/* POSIX.1b (and later) mandates SEM_FAILED as the value to be + * returned on failure of sem_open(); (our implementation is a + * stub, which will always return this). + */ +#define SEM_FAILED (sem_t *)(int)(-1) + +PTW32_BEGIN_C_DECLS + +/* Function prototypes: some are implemented as stubs, which + * always fail; (FIXME: identify them). + */ +PTW32_DLLPORT int PTW32_CDECL sem_init (sem_t * sem, + int pshared, + unsigned int value); + +PTW32_DLLPORT int PTW32_CDECL sem_destroy (sem_t * sem); + +PTW32_DLLPORT int PTW32_CDECL sem_trywait (sem_t * sem); + +PTW32_DLLPORT int PTW32_CDECL sem_wait (sem_t * sem); + +PTW32_DLLPORT int PTW32_CDECL sem_timedwait (sem_t * sem, + const struct timespec * abstime); + +PTW32_DLLPORT int PTW32_CDECL sem_post (sem_t * sem); + +PTW32_DLLPORT int PTW32_CDECL sem_post_multiple (sem_t * sem, + int count); + +PTW32_DLLPORT sem_t * PTW32_CDECL sem_open (const char * name, int oflag, ...); + +PTW32_DLLPORT int PTW32_CDECL sem_close (sem_t * sem); + +PTW32_DLLPORT int PTW32_CDECL sem_unlink (const char * name); + +PTW32_DLLPORT int PTW32_CDECL sem_getvalue (sem_t * sem, + int * sval); + +PTW32_END_C_DECLS + +#endif /* !SEMAPHORE_H */ diff --git a/deps/raii/include/compat/stdint.h b/deps/raii/include/compat/stdint.h new file mode 100644 index 00000000..28291518 --- /dev/null +++ b/deps/raii/include/compat/stdint.h @@ -0,0 +1,19 @@ +/* + * Public domain + * stdint.h compatibility shim + */ + +#ifdef _MSC_VER +#include <../include/stdint.h> +#else +#include_next +#endif + +#ifndef LIBCRYPTOCOMPAT_STDINT_H +#define LIBCRYPTOCOMPAT_STDINT_H + +#ifndef SIZE_MAX +#include +#endif + +#endif diff --git a/deps/raii/include/compat/stdio.h b/deps/raii/include/compat/stdio.h new file mode 100644 index 00000000..d5725c9a --- /dev/null +++ b/deps/raii/include/compat/stdio.h @@ -0,0 +1,51 @@ +/* + * Public domain + * stdio.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_STDIO_H +#define LIBCRYPTOCOMPAT_STDIO_H + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/stdlib.h> +#include <../ucrt/corecrt_io.h> +#include <../ucrt/stdio.h> +#else +#include <../include/stdio.h> +#endif +#else +#include_next +#endif + +#ifndef HAVE_ASPRINTF +#include +int vasprintf(char **str, const char *fmt, va_list ap); +int asprintf(char **str, const char *fmt, ...); +#endif + +#ifdef _WIN32 + +#if defined(_MSC_VER) +#define __func__ __FUNCTION__ +#endif + +void posix_perror(const char *s); +FILE * posix_fopen(const char *path, const char *mode); +char * posix_fgets(char *s, int size, FILE *stream); +int posix_rename(const char *oldpath, const char *newpath); + +#ifndef NO_REDEF_POSIX_FUNCTIONS +#define perror(errnum) posix_perror(errnum) +#define fopen(path, mode) posix_fopen(path, mode) +#define fgets(s, size, stream) posix_fgets(s, size, stream) +#define rename(oldpath, newpath) posix_rename(oldpath, newpath) +#endif + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + +#endif + +#endif diff --git a/deps/raii/include/compat/stdlib.h b/deps/raii/include/compat/stdlib.h new file mode 100644 index 00000000..2eaea244 --- /dev/null +++ b/deps/raii/include/compat/stdlib.h @@ -0,0 +1,47 @@ +/* + * stdlib.h compatibility shim + * Public domain + */ + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/stdlib.h> +#else +#include <../include/stdlib.h> +#endif +#else +#include_next +#endif + +#ifndef LIBCRYPTOCOMPAT_STDLIB_H +#define LIBCRYPTOCOMPAT_STDLIB_H + +#include +#include + +#ifndef HAVE_ARC4RANDOM_BUF +uint32_t arc4random(void); +void arc4random_buf(void *_buf, size_t n); +uint32_t arc4random_uniform(uint32_t upper_bound); +#endif + +#ifndef HAVE_FREEZERO +void freezero(void *ptr, size_t sz); +#endif + +#ifndef HAVE_GETPROGNAME +const char * getprogname(void); +#endif + +void *reallocarray(void *, size_t, size_t); + +#ifndef HAVE_RECALLOCARRAY +void *recallocarray(void *, size_t, size_t, size_t); +#endif + +#ifndef HAVE_STRTONUM +long long strtonum(const char *nptr, long long minval, + long long maxval, const char **errstr); +#endif + +#endif diff --git a/deps/raii/include/compat/string.h b/deps/raii/include/compat/string.h new file mode 100644 index 00000000..4bf7519b --- /dev/null +++ b/deps/raii/include/compat/string.h @@ -0,0 +1,87 @@ +/* + * Public domain + * string.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_STRING_H +#define LIBCRYPTOCOMPAT_STRING_H + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/string.h> +#else +#include <../include/string.h> +#endif +#else +#include_next +#endif + +#include + +#if defined(__sun) || defined(_AIX) || defined(__hpux) +/* Some functions historically defined in string.h were placed in strings.h by + * SUS. Use the same hack as OS X and FreeBSD use to work around on AIX, + * Solaris, and HPUX. + */ +#include +#endif + +#ifndef HAVE_STRCASECMP +int strcasecmp(const char *s1, const char *s2); +int strncasecmp(const char *s1, const char *s2, size_t len); +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_STRLCAT +size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#ifndef HAVE_STRNDUP +char * strndup(const char *str, size_t maxlen); +/* the only user of strnlen is strndup, so only build it if needed */ +#ifndef HAVE_STRNLEN +size_t strnlen(const char *str, size_t maxlen); +#endif +#endif + +#ifndef HAVE_STRSEP +char *strsep(char **stringp, const char *delim); +#endif + +#ifndef HAVE_EXPLICIT_BZERO +void explicit_bzero(void *, size_t); +#endif + +#ifndef HAVE_TIMINGSAFE_BCMP +int timingsafe_bcmp(const void *b1, const void *b2, size_t n); +#endif + +#ifndef HAVE_TIMINGSAFE_MEMCMP +int timingsafe_memcmp(const void *b1, const void *b2, size_t len); +#endif + +#ifndef HAVE_MEMMEM +void * memmem(const void *big, size_t big_len, const void *little, + size_t little_len); +#endif + +#ifdef _WIN32 +#include + +static inline char * +posix_strerror(int errnum) +{ + if (errnum == ECONNREFUSED) { + return "Connection refused"; + } + return strerror(errnum); +} + +#define strerror(errnum) posix_strerror(errnum) + +#endif + +#endif diff --git a/deps/raii/include/compat/sys/_null.h b/deps/raii/include/compat/sys/_null.h new file mode 100644 index 00000000..5d154019 --- /dev/null +++ b/deps/raii/include/compat/sys/_null.h @@ -0,0 +1,18 @@ +/* $OpenBSD: _null.h,v 1.2 2016/09/09 22:07:58 millert Exp $ */ + +/* + * Written by Todd C. Miller, September 9, 2016 + * Public domain. + */ + +#ifndef NULL +#if !defined(__cplusplus) +#define NULL ((void *)0) +#elif __cplusplus >= 201103L +#define NULL nullptr +#elif defined(__GNUG__) +#define NULL __null +#else +#define NULL 0L +#endif +#endif diff --git a/deps/raii/include/compat/sys/ioctl.h b/deps/raii/include/compat/sys/ioctl.h new file mode 100644 index 00000000..a255506c --- /dev/null +++ b/deps/raii/include/compat/sys/ioctl.h @@ -0,0 +1,11 @@ +/* + * Public domain + * sys/ioctl.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include +#define ioctl(fd, type, arg) ioctlsocket(fd, type, arg) +#endif diff --git a/deps/raii/include/compat/sys/mman.h b/deps/raii/include/compat/sys/mman.h new file mode 100644 index 00000000..d9eb6a95 --- /dev/null +++ b/deps/raii/include/compat/sys/mman.h @@ -0,0 +1,19 @@ +/* + * Public domain + * sys/mman.h compatibility shim + */ + +#include_next + +#ifndef LIBCRYPTOCOMPAT_MMAN_H +#define LIBCRYPTOCOMPAT_MMAN_H + +#ifndef MAP_ANON +#ifdef MAP_ANONYMOUS +#define MAP_ANON MAP_ANONYMOUS +#else +#error "System does not support mapping anonymous pages?" +#endif +#endif + +#endif diff --git a/deps/raii/include/compat/sys/param.h b/deps/raii/include/compat/sys/param.h new file mode 100644 index 00000000..70488f82 --- /dev/null +++ b/deps/raii/include/compat/sys/param.h @@ -0,0 +1,15 @@ +/* + * Public domain + * sys/param.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_SYS_PARAM_H +#define LIBCRYPTOCOMPAT_SYS_PARAM_H + +#ifdef _MSC_VER +#include +#else +#include_next +#endif + +#endif diff --git a/deps/raii/include/compat/sys/queue.h b/deps/raii/include/compat/sys/queue.h new file mode 100644 index 00000000..f28ba894 --- /dev/null +++ b/deps/raii/include/compat/sys/queue.h @@ -0,0 +1,536 @@ +/* $OpenBSD: queue.h,v 1.45 2018/07/12 14:22:54 sashan Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#include + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues and XOR simple queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * An XOR simple queue is used in the same way as a regular simple queue. + * The difference is that the head structure also includes a "cookie" that + * is XOR'd with the queue pointer (first, last or next) to generate the + * real pointer value. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALID ((void *)-1) +#define _Q_INVALIDATE(a) (a) = _Q_INVALID +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + _Q_INVALIDATE((elm)->field.sle_next); \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/deps/raii/include/compat/sys/select.h b/deps/raii/include/compat/sys/select.h new file mode 100644 index 00000000..7fff4833 --- /dev/null +++ b/deps/raii/include/compat/sys/select.h @@ -0,0 +1,10 @@ +/* + * Public domain + * sys/select.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include "../win32netcompat.h" +#endif diff --git a/deps/raii/include/compat/sys/socket.h b/deps/raii/include/compat/sys/socket.h new file mode 100644 index 00000000..dd0d85a1 --- /dev/null +++ b/deps/raii/include/compat/sys/socket.h @@ -0,0 +1,18 @@ +/* + * Public domain + * sys/socket.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else +#include "../win32netcompat.h" +#endif + +#if !defined(SOCK_NONBLOCK) || !defined(SOCK_CLOEXEC) +#define NEED_SOCKET_FLAGS +#define SOCK_CLOEXEC 0x8000 /* set FD_CLOEXEC */ +#define SOCK_NONBLOCK 0x4000 /* set O_NONBLOCK */ +int bsd_socketpair(int domain, int type, int protocol, int socket_vector[2]); +#define socketpair(d,t,p,sv) bsd_socketpair(d,t,p,sv) +#endif diff --git a/deps/raii/include/compat/sys/stat.h b/deps/raii/include/compat/sys/stat.h new file mode 100644 index 00000000..b88da1d5 --- /dev/null +++ b/deps/raii/include/compat/sys/stat.h @@ -0,0 +1,121 @@ +/* + * Public domain + * sys/stat.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_SYS_STAT_H +#define LIBCRYPTOCOMPAT_SYS_STAT_H + +#ifndef _MSC_VER +#include_next + +/* for old MinGW */ +#ifndef S_IRWXU +#define S_IRWXU 0 +#endif +#ifndef S_IRWXG +#define S_IRWXG 0 +#endif +#ifndef S_IRGRP +#define S_IRGRP 0 +#endif +#ifndef S_IRWXO +#define S_IRWXO 0 +#endif +#ifndef S_IROTH +#define S_IROTH 0 +#endif + +#else + +#include +#if _MSC_VER >= 1900 +#include <../ucrt/sys/stat.h> +#else +#include <../include/sys/stat.h> +#endif + +/* File type and permission flags for stat() */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT /* File type mask */ +#endif +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR /* Directory */ +#endif +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR /* Character device */ +#endif +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO /* Pipe */ +#endif +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG /* Regular file */ +#endif +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD /* Read permission */ +#endif +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE /* Write permission */ +#endif +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC /* Execute permission */ +#endif +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO /* Pipe */ +#endif +#if !defined(S_IFBLK) +# define S_IFBLK 0 /* Block device */ +#endif +#if !defined(S_IFLNK) +# define S_IFLNK 0 /* Link */ +#endif +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 /* Socket */ +#endif + +#if defined(_MSC_VER) +# define S_IRWXU 0 /* RWX user */ +# define S_IRUSR S_IREAD /* Read user */ +# define S_IWUSR S_IWRITE /* Write user */ +# define S_IXUSR 0 /* Execute user */ +# define S_IRWXG 0 /* RWX group */ +# define S_IRGRP 0 /* Read group */ +# define S_IWGRP 0 /* Write group */ +# define S_IXGRP 0 /* Execute group */ +# define S_IRWXO 0 /* RWX others */ +# define S_IROTH 0 /* Read others */ +# define S_IWOTH 0 /* Write others */ +# define S_IXOTH 0 /* Execute others */ +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) + +#endif + +#endif diff --git a/deps/raii/include/compat/sys/time.h b/deps/raii/include/compat/sys/time.h new file mode 100644 index 00000000..76428c19 --- /dev/null +++ b/deps/raii/include/compat/sys/time.h @@ -0,0 +1,28 @@ +/* + * Public domain + * sys/time.h compatibility shim + */ + +#ifndef LIBCRYPTOCOMPAT_SYS_TIME_H +#define LIBCRYPTOCOMPAT_SYS_TIME_H + +#ifdef _MSC_VER +#include +int gettimeofday(struct timeval *tp, void *tzp); +#else +#include_next +#endif + +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +#endif diff --git a/deps/raii/include/compat/sys/tree.h b/deps/raii/include/compat/sys/tree.h new file mode 100644 index 00000000..ffcac902 --- /dev/null +++ b/deps/raii/include/compat/sys/tree.h @@ -0,0 +1,1006 @@ +/* $OpenBSD: tree.h,v 1.29 2017/07/30 19:27:20 deraadt Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +#include + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __unused __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __unused __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + + +/* + * Copyright (c) 2016 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct rb_type { + int (*t_compare)(const void *, const void *); + void (*t_augment)(void *); + unsigned int t_offset; /* offset of rb_entry in type */ +}; + +struct rb_tree { + struct rb_entry *rbt_root; +}; + +struct rb_entry { + struct rb_entry *rbt_parent; + struct rb_entry *rbt_left; + struct rb_entry *rbt_right; + unsigned int rbt_color; +}; + +#define RBT_HEAD(_name, _type) \ +struct _name { \ + struct rb_tree rbh_root; \ +} + +#define RBT_ENTRY(_type) struct rb_entry + +static inline void +_rb_init(struct rb_tree *rbt) +{ + rbt->rbt_root = NULL; +} + +static inline int +_rb_empty(struct rb_tree *rbt) +{ + return (rbt->rbt_root == NULL); +} + +void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); +void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); +void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); +void *_rb_root(const struct rb_type *, struct rb_tree *); +void *_rb_min(const struct rb_type *, struct rb_tree *); +void *_rb_max(const struct rb_type *, struct rb_tree *); +void *_rb_next(const struct rb_type *, void *); +void *_rb_prev(const struct rb_type *, void *); +void *_rb_left(const struct rb_type *, void *); +void *_rb_right(const struct rb_type *, void *); +void *_rb_parent(const struct rb_type *, void *); +void _rb_set_left(const struct rb_type *, void *, void *); +void _rb_set_right(const struct rb_type *, void *, void *); +void _rb_set_parent(const struct rb_type *, void *, void *); +void _rb_poison(const struct rb_type *, void *, unsigned long); +int _rb_check(const struct rb_type *, void *, unsigned long); + +#define RBT_INITIALIZER(_head) { { NULL } } + +#define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ +extern const struct rb_type *const _name##_RBT_TYPE; \ + \ +__unused static inline void \ +_name##_RBT_INIT(struct _name *head) \ +{ \ + _rb_init(&head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_INSERT(struct _name *head, struct _type *elm) \ +{ \ + return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ +{ \ + return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_FIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_NFIND(struct _name *head, const struct _type *key) \ +{ \ + return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_ROOT(struct _name *head) \ +{ \ + return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline int \ +_name##_RBT_EMPTY(struct _name *head) \ +{ \ + return _rb_empty(&head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_MIN(struct _name *head) \ +{ \ + return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_MAX(struct _name *head) \ +{ \ + return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_NEXT(struct _type *elm) \ +{ \ + return _rb_next(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_PREV(struct _type *elm) \ +{ \ + return _rb_prev(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_LEFT(struct _type *elm) \ +{ \ + return _rb_left(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_RIGHT(struct _type *elm) \ +{ \ + return _rb_right(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline struct _type * \ +_name##_RBT_PARENT(struct _type *elm) \ +{ \ + return _rb_parent(_name##_RBT_TYPE, elm); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ +{ \ + return _rb_set_left(_name##_RBT_TYPE, elm, left); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ +{ \ + return _rb_set_right(_name##_RBT_TYPE, elm, right); \ +} \ + \ +__unused static inline void \ +_name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ +{ \ + return _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ +} \ + \ +__unused static inline void \ +_name##_RBT_POISON(struct _type *elm, unsigned long poison) \ +{ \ + return _rb_poison(_name##_RBT_TYPE, elm, poison); \ +} \ + \ +__unused static inline int \ +_name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ +{ \ + return _rb_check(_name##_RBT_TYPE, elm, poison); \ +} + +#define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ +static int \ +_name##_RBT_COMPARE(const void *lptr, const void *rptr) \ +{ \ + const struct _type *l = lptr, *r = rptr; \ + return _cmp(l, r); \ +} \ +static const struct rb_type _name##_RBT_INFO = { \ + _name##_RBT_COMPARE, \ + _aug, \ + offsetof(struct _type, _field), \ +}; \ +const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO + +#define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ +static void \ +_name##_RBT_AUGMENT(void *ptr) \ +{ \ + struct _type *p = ptr; \ + return _aug(p); \ +} \ +RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) + +#define RBT_GENERATE(_name, _type, _field, _cmp) \ + RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) + +#define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) +#define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) +#define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) +#define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) +#define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) +#define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) +#define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) +#define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) +#define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) +#define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) +#define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) +#define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) +#define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) +#define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) +#define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) +#define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) +#define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) +#define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) +#define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) + +#define RBT_FOREACH(_e, _name, _head) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_NEXT(_name, (_e))) + +#define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MIN(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ + (_e) = (_n)) + +#define RBT_FOREACH_REVERSE(_e, _name, _head) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL; \ + (_e) = RBT_PREV(_name, (_e))) + +#define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ + for ((_e) = RBT_MAX(_name, (_head)); \ + (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ + (_e) = (_n)) + +#endif /* _SYS_TREE_H_ */ diff --git a/deps/raii/include/compat/sys/types.h b/deps/raii/include/compat/sys/types.h new file mode 100644 index 00000000..49678439 --- /dev/null +++ b/deps/raii/include/compat/sys/types.h @@ -0,0 +1,81 @@ +/* + * Public domain + * sys/types.h compatibility shim + */ + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/sys/types.h> +#else +#include <../include/sys/types.h> +#endif +#else +#include_next +#endif + +#ifndef LIBCRYPTOCOMPAT_SYS_TYPES_H +#define LIBCRYPTOCOMPAT_SYS_TYPES_H + +#include + +#ifdef __MINGW32__ +#include <_bsd_types.h> +typedef uint32_t in_addr_t; +typedef uint32_t uid_t; +#endif + +#ifdef _MSC_VER +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef uint32_t in_addr_t; +typedef uint32_t mode_t; +typedef uint32_t uid_t; + +#include +typedef SSIZE_T ssize_t; + +#ifndef SSIZE_MAX +#ifdef _WIN64 +#define SSIZE_MAX _I64_MAX +#else +#define SSIZE_MAX INT_MAX +#endif +#endif + +#endif + +#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) +# define __bounded__(x, y, z) +#endif + +#if !defined(HAVE_ATTRIBUTE__DEAD) && !defined(__dead) +#ifdef _MSC_VER +#define __dead __declspec(noreturn) +#else +#define __dead __attribute__((__noreturn__)) +#endif +#endif + +#ifdef _WIN32 +#define __warn_references(sym,msg) +#else + +#ifndef __warn_references + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) +#define __warn_references(sym,msg) \ + __asm__(".section .gnu.warning." __STRING(sym) \ + "\n\t.ascii \"" msg "\"\n\t.text"); +#else +#define __warn_references(sym,msg) +#endif + +#endif /* __warn_references */ +#endif /* _WIN32 */ + +#endif diff --git a/deps/raii/include/compat/sys/uio.h b/deps/raii/include/compat/sys/uio.h new file mode 100644 index 00000000..b4aee9e3 --- /dev/null +++ b/deps/raii/include/compat/sys/uio.h @@ -0,0 +1,17 @@ +/* + * Public domain + * sys/select.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#else + +#include + +struct iovec { + void *iov_base; + size_t iov_len; +}; + +#endif diff --git a/deps/raii/include/compat/syslog.h b/deps/raii/include/compat/syslog.h new file mode 100644 index 00000000..f400ff66 --- /dev/null +++ b/deps/raii/include/compat/syslog.h @@ -0,0 +1,37 @@ +/* + * Public domain + * syslog.h compatibility shim + */ + +#ifndef _WIN32 +#include_next +#endif + +#ifndef LIBCRYPTOCOMPAT_SYSLOG_H +#define LIBCRYPTOCOMPAT_SYSLOG_H + +#ifndef HAVE_SYSLOG_R + +#include + +#ifdef _WIN32 +#define LOG_INFO 6 /* informational */ +#define LOG_USER (1<<3) /* random user-level messages */ +#define LOG_LOCAL2 (18<<3) /* reserved for local use */ +#endif + +struct syslog_data { + int log_stat; + const char *log_tag; + int log_fac; + int log_mask; +}; + +#define SYSLOG_DATA_INIT {0, (const char *)0, LOG_USER, 0xff} + +void syslog_r(int, struct syslog_data *, const char *, ...); +void vsyslog_r(int, struct syslog_data *, const char *, va_list); + +#endif + +#endif diff --git a/deps/raii/include/compat/time.h b/deps/raii/include/compat/time.h new file mode 100644 index 00000000..540807dd --- /dev/null +++ b/deps/raii/include/compat/time.h @@ -0,0 +1,60 @@ +/* + * Public domain + * sys/time.h compatibility shim + */ + +#ifdef _MSC_VER +#if _MSC_VER >= 1900 +#include <../ucrt/time.h> +#else +#include <../include/time.h> +#endif +#else +#include_next +#endif + +#ifndef LIBCRYPTOCOMPAT_TIME_H +#define LIBCRYPTOCOMPAT_TIME_H + +#ifdef _WIN32 +struct tm *__gmtime_r(const time_t * t, struct tm * tm); +#define gmtime_r(tp, tm) __gmtime_r(tp, tm) +#endif + +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *tm); +#endif + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC CLOCK_REALTIME +#endif + +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif + +#ifndef _WIN32 +#ifndef HAVE_CLOCK_GETTIME +typedef int clockid_t; +int clock_gettime(clockid_t clock_id, struct timespec *tp); +#endif + +#ifdef timespecsub +#define HAVE_TIMESPECSUB +#endif + +#ifndef HAVE_TIMESPECSUB +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif + +#endif + +#endif diff --git a/deps/raii/include/compat/unistd.h b/deps/raii/include/compat/unistd.h new file mode 100644 index 00000000..46add7cd --- /dev/null +++ b/deps/raii/include/compat/unistd.h @@ -0,0 +1,63 @@ +#ifndef _UNISTD_H +#define _UNISTD_H 1 + +/* This is intended as a drop-in replacement for unistd.h on Windows. + * Please add functionality as needed. + * https://stackoverflow.com/a/826027/1202830 + */ + +#include +#include +// #include "getopt.h" /* getopt at: https://gist.github.com/ashelly/7776712 */ +#include /* for getpid() and the exec..() family */ +#include /* for _getcwd() and _chdir() */ + +#define srandom srand +#define random rand + +/* Values for the second argument to access. + These may be OR'd together. */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +// #define X_OK 1 /* execute permission - unsupported in windows*/ +#define F_OK 0 /* Test for existence. */ + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define access _access +#define dup2 _dup2 +#define execve _execve +#define ftruncate _chsize +#define unlink _unlink +#define fileno _fileno +#define getcwd _getcwd +#define chdir _chdir +#define isatty _isatty +#define lseek _lseek +#define write _write +#define close _close +#define read _read +/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */ + +#ifdef _WIN64 +#define ssize_t __int64 +#else +#define ssize_t long +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +/* should be in some equivalent to */ +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#endif /* unistd.h */ diff --git a/deps/raii/include/compat/utsname.h b/deps/raii/include/compat/utsname.h new file mode 100644 index 00000000..730cb731 --- /dev/null +++ b/deps/raii/include/compat/utsname.h @@ -0,0 +1,38 @@ +/* sys/utsname.h + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _SYS_UTSNAME_H +#define _SYS_UTSNAME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define _UTSNAME_LENGTH 65 + +struct utsname +{ + char sysname[_UTSNAME_LENGTH + 1]; + char nodename[_UTSNAME_LENGTH]; + char release[_UTSNAME_LENGTH]; + char version[_UTSNAME_LENGTH]; + char machine[_UTSNAME_LENGTH]; +#if __GNU_VISIBLE + char domainname[_UTSNAME_LENGTH]; +#else + char __domainname[_UTSNAME_LENGTH]; +#endif +}; + +int uname (struct utsname *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/raii/include/compat/wait.h b/deps/raii/include/compat/wait.h new file mode 100644 index 00000000..4649bb8c --- /dev/null +++ b/deps/raii/include/compat/wait.h @@ -0,0 +1,520 @@ +/* +MIT License + +Copyright (c) 2019 win32ports + +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 and this permission notice 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 OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once + +#ifndef __SYS_WAIT_H_B07AB843_5FF0_4FCF_B6F0_8CB97D28F337__ +#define __SYS_WAIT_H_B07AB843_5FF0_4FCF_B6F0_8CB97D28F337__ + +#ifndef _WIN32 + +#pragma message("this sys/wait.h implementation is for Windows only!") + +#else /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef __GNUC__ +#include /* for timeval */ +#endif /* __GNUC__ */ + +#include +#include + +#ifndef _INC_WINDOWS + +typedef unsigned long DWORD; +typedef DWORD * LPDWORD; + +#ifdef _WIN64 + +typedef long long LONG_PTR; +typedef unsigned long long ULONG_PTR; + +#else /* _WIN64 */ + +typedef long LONG_PTR; +typedef unsigned long ULONG_PTR; + +#endif + +typedef long LONG; +typedef wchar_t WCHAR; +typedef int BOOL; +typedef void VOID; +typedef void * HANDLE; + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif /* MAX_PATH */ + +#ifndef WINAPI +#define WINAPI __stdcall +#endif /* WINAPI */ + +#ifndef DECLSPEC_DLLIMPORT +#ifdef _MSC_VER +#define DECLSPEC_DLLIMPORT __declspec(dllimport) +#else /* _MSC_VER */ +#define DECLSPEC_DLLIMPORT +#endif /* _MSC_VER */ +#endif /* DECLSPEC_DLLIMPORT */ + +#ifndef WINBASEAPI +#define WINBASEAPI DECLSPEC_DLLIMPORT +#endif /* WINBASEAPI */ + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) +#endif /* INVALID_HANDLE_VALUE */ + +#ifndef SYNCHRONIZE +#define SYNCHRONIZE (0x00100000L) +#endif /* SYNCHRONIZE */ + +#ifndef PROCESS_QUERY_INFORMATION +#define PROCESS_QUERY_INFORMATION (0x0400) +#endif /* PROCESS_QUERY_INFORMATION */ + +#ifndef INFINITE +#define INFINITE 0xFFFFFFFF +#endif /* INFINITE */ + +#ifndef WAIT_OBJECT_0 +#define WAIT_OBJECT_0 0 +#endif /* WAIT_OBJECT_0 */ + +#ifndef WAIT_TIMEOUT +#define WAIT_TIMEOUT 258L +#endif /* WAIT_TIMEOUT */ + +/* +FIXME: causes FILETIME to conflict +#ifndef _FILETIME_ +#define _FILETIME_ + +typedef struct FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +} FILETIME, *PFILETIME, *LPFILETIME; + +#endif +*/ + +WINBASEAPI DWORD WINAPI GetCurrentProcessId(VOID); +WINBASEAPI BOOL WINAPI CloseHandle(HANDLE hObject); +WINBASEAPI HANDLE WINAPI OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); +WINBASEAPI DWORD WINAPI WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); +WINBASEAPI BOOL WINAPI GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode); + +/* +FIXME: causes FILETIME to conflict +WINBASEAPI BOOL WINAPI GetProcessTimes(HANDLE hProcess, LPFILETIME lpCreationTime, LPFILETIME lpExitTime, LPFILETIME lpKernelTime, LPFILETIME lpUserTime); +*/ + +#endif /* _INC_WINDOWS */ + +#ifndef _WINSOCKAPI_ + +struct private_timeval { + long tv_sec; + long tv_usec; +}; + +#define timeval private_timeval + +#endif /* _WINSOCKAPI_ */ + +#ifndef _INC_TOOLHELP32 + +typedef struct tagPROCESSENTRY32W +{ + DWORD dwSize; + DWORD cntUsage; + DWORD th32ProcessID; + ULONG_PTR th32DefaultHeapID; + DWORD th32ModuleID; + DWORD cntThreads; + DWORD th32ParentProcessID; + LONG pcPriClassBase; + DWORD dwFlags; + WCHAR szExeFile[MAX_PATH]; +} PROCESSENTRY32W; +typedef PROCESSENTRY32W * PPROCESSENTRY32W; +typedef PROCESSENTRY32W * LPPROCESSENTRY32W; + +#ifndef TH32CS_SNAPPROCESS +#define TH32CS_SNAPPROCESS 2 +#endif /* TH32CS_SNAPPROCESS */ + +HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID); +BOOL WINAPI Process32FirstW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe); +BOOL WINAPI Process32NextW(HANDLE hSnapshot, LPPROCESSENTRY32W lppe); + +#endif /* _INC_TOOLHELP32 */ + +#ifndef WNOHANG +#define WNOHANG 1 +#endif /* WNOHANG */ + +#ifndef WUNTRACED +#define WUNTRACED 2 +#endif /* WUNTRACED */ + +#ifndef __WEXITSTATUS +#define __WEXITSTATUS(status) (((status) & 0xFF00) >> 8) +#endif /* __WEXITSTATUS */ + +#ifndef __WIFEXITED +#define __WIFEXITED(status) (__WTERMSIG(status) == 0) +#endif /* __WIFEXITED */ + +#ifndef __WTERMSIG +#define __WTERMSIG(status) ((status) & 0x7F) +#endif /* __WTERMSIG */ + +#ifndef __WIFSIGNALED +#define __WIFSIGNALED(status) (((signed char)(__WTERMSIG(status) + 1) >> 1) > 0) +#endif /* __WIFSIGNALED */ + +#ifndef __WIFSTOPPED +#define __WIFSTOPPED(status) (((status) & 0xFF) == 0x7F) +#endif /* __WIFSTOPPED */ + +#ifndef __WSTOPSIG +#define __WSTOPSIG(status) __WEXITSTATUS(status) +#endif /* __WSTOPSIG */ + +#ifndef __WCONTINUED +#define __WCONTINUED 8 +#endif /* __WCONTINUED */ + +#ifndef __WNOWAIT +#define __WNOWAIT 0x01000000 +#endif /* __WNOWAIT */ + +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) __WEXITSTATUS(status) +#endif /* WEXITSTATUS */ + +#ifndef WIFEXITED +#define WIFEXITED(status) __WIFEXITED(status) +#endif /* WIFEXITED */ + +#ifndef WIFSIGNALED +#define WIFSIGNALED(status) __WIFSIGNALED(status) +#endif /* WIFSIGNALED */ + +#ifndef WTERMSIG +#define WTERMSIG(status) __WTERMSIG(status) +#endif /* WTERMSIG */ + +#ifndef WIFSTOPPED +#define WIFSTOPPED(status) __WIFSTOPPED(status) +#endif /* WIFSTOPPED */ + +#ifndef WSTOPSIG +#define WSTOPSIG(status) __WSTOPSIG(status) +#endif /* WSTOPSIG */ + +#if !defined(__pid_t_defined) && !defined(_PID_T_) && !defined(pid_t) +#define __pid_t_defined 1 +#define _PID_T_ +typedef int __pid_t; +typedef __pid_t pid_t; +#endif /* !defined(__pid_t_defined) && !defined(_PID_T_) && !defined(pid_t) */ + +#ifndef __id_t_defined +#define __id_t_defined 1 +typedef unsigned __id_t; +typedef __id_t id_t; +#endif /* __id_t_defined */ + +#ifndef __uid_t_defined +#define __uid_t_defined 1 +typedef unsigned __uid_t; +typedef __uid_t uid_t; +#endif /* __uid_t_defined */ + +#ifndef __siginfo_t_defined +#define __siginfo_t_defined 1 +typedef struct +{ + int si_signo; /* signal number */ + int si_code; /* signal code */ + int si_errno; /* if non-zero, errno associated with this signal, as defined in */ + pid_t si_pid; /* sending process ID */ + uid_t si_uid; /* real user ID of sending process */ + void * si_addr; /* address of faulting instruction */ + int si_status; /* exit value of signal */ + long si_band; /* band event of SIGPOLL */ +} +siginfo_t; +#endif /* __siginfo_t_defined */ + +struct rusage +{ + struct timeval ru_utime; /* user time used */ + struct timeval ru_stime; /* system time used */ +}; + +#ifdef _XOPEN_SOURCE + +#ifndef __W_CONTINUED +#define __W_CONTINUED 0xFFFF +#endif /* __W_CONTINUED */ + +#ifndef __WIFCONTINUED +#define __WIFCONTINUED(status) ((status) == __W_CONTINUED) +#endif /* __WIFCONTINUED */ + +#ifndef WIFCONTINUED +#define WIFCONTINUED(status) __WIFCONTINUED(status) +#endif /* WIFCONTINUED */ + +#ifndef WSTOPPED +#define WSTOPPED 2 +#endif /* WSTOPPED */ + +#ifndef WEXITED +#define WEXITED 4 +#endif /* WEXITED */ + +#ifndef WCONTINUED +#define WCONTINUED __WCONTINUED +#endif /* WCONTINUED */ + +#ifndef WNOWAIT +#define WNOWAIT __WNOWAIT +#endif /* WNOWAIT */ + +typedef enum +{ + P_ALL, + P_PID, + P_PGID +} +idtype_t; + +#endif /* _XOPEN_SOURCE */ + +static int __filter_anychild(PROCESSENTRY32W * pe, DWORD pid) +{ + return pe->th32ParentProcessID == GetCurrentProcessId(); +} + +static int __filter_pid(PROCESSENTRY32W * pe, DWORD pid) +{ + return pe->th32ProcessID == pid; +} + +/* +FIXME: causes FILETIME to conflict +static void __filetime2timeval(FILETIME time, struct timeval * out) +{ + unsigned long long value = time.dwHighDateTime; + value = (value << 32) | time.dwLowDateTime; + out->tv_sec = (long)(value / 1000000); + out->tv_usec = (long)(value % 1000000); +} +*/ + +static int __waitpid_internal(pid_t pid, int * status, int options, siginfo_t * infop, struct rusage * rusage) +{ + int saved_status = 0; + HANDLE hProcess = INVALID_HANDLE_VALUE, hSnapshot = INVALID_HANDLE_VALUE; + int (*filter)(PROCESSENTRY32W*, DWORD); + PROCESSENTRY32W pe; + DWORD wait_status = 0, exit_code = 0; + int nohang = WNOHANG == (WNOHANG & options); + options &= ~(WUNTRACED | __WNOWAIT | __WCONTINUED | WNOHANG); + if (options) + { + errno = -EINVAL; + return -1; + } + + if (pid == -1) + { + /* wait for any child */ + filter = __filter_anychild; + } + else if (pid < -1) + { + /* wait for any process from the group */ + abort(); /* not implemented */ + } + else if (pid == 0) + { + /* wait for any process from the current group */ + abort(); /* not implemented */ + } + else + { + /* wait for process with given pid */ + filter = __filter_pid; + } + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (INVALID_HANDLE_VALUE == hSnapshot) + { + errno = ECHILD; + return -1; + } + pe.dwSize = sizeof(pe); + if (!Process32FirstW(hSnapshot, &pe)) + { + CloseHandle(hSnapshot); + errno = ECHILD; + return -1; + } + do + { + if (filter(&pe, pid)) + { + hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, 0, pe.th32ProcessID); + if (INVALID_HANDLE_VALUE == hProcess) + { + CloseHandle(hSnapshot); + errno = ECHILD; + return -1; + } + break; + } + } + while (Process32NextW(hSnapshot, &pe)); + if (INVALID_HANDLE_VALUE == hProcess) + { + CloseHandle(hSnapshot); + errno = ECHILD; + return -1; + } + + wait_status = WaitForSingleObject(hProcess, nohang ? 0 : INFINITE); + + if (WAIT_OBJECT_0 == wait_status) + { + if (GetExitCodeProcess(hProcess, &exit_code)) + saved_status |= (exit_code & 0xFF) << 8; + } + else if (WAIT_TIMEOUT == wait_status && nohang) + { + return 0; + } + else + { + CloseHandle(hProcess); + CloseHandle(hSnapshot); + errno = ECHILD; + return -1; + } + if (rusage) + { + memset(rusage, 0, sizeof(*rusage)); + /* + FIXME: causes FILETIME to conflict + FILETIME creation_time, exit_time, kernel_time, user_time; + if (GetProcessTimes(hProcess, &creation_time, &exit_time, &kernel_time, &user_time)) + { + __filetime2timeval(kernel_time, &rusage->ru_stime); + __filetime2timeval(user_time, &rusage->ru_utime); + } + */ + } + if (infop) + { + memset(infop, 0, sizeof(*infop)); + } + + CloseHandle(hProcess); + CloseHandle(hSnapshot); + + if (status) + *status = saved_status; + + return pe.th32ParentProcessID; +} + +static int waitpid(pid_t pid, int * status, int options) +{ + return __waitpid_internal(pid, status, options, NULL, NULL); +} + +static int wait(int *status) +{ + return __waitpid_internal(-1, status, 0, NULL, NULL); +} + +#ifdef _XOPEN_SOURCE + +static int waitid(idtype_t idtype, id_t id, siginfo_t * infop, int options) +{ + pid_t pid; + switch (idtype) + { + case P_PID: pid = id; break; + case P_PGID: pid = -(pid_t)id; break; + case P_ALL: pid = 0; break; + default: errno = EINVAL; return -1; + } + return __waitpid_internal(pid, NULL, options, infop, NULL); +} + +static pid_t wait3(int * status, int options, struct rusage * rusage) +{ + return __waitpid_internal(-1, status, options, NULL, rusage); +} + +static pid_t wait4(pid_t pid, int * status, int options, struct rusage * rusage) +{ + return __waitpid_internal(pid, status, options, NULL, rusage); +} + +#endif /* _XOPEN_SOURCE */ + +#ifndef _INC_WINDOWS + +#undef WAIT_OBJECT_0 + +#undef FILETIME +#undef PFILETIME +#undef LPFILETIME + +#endif /* _INC_WINDOWS */ + +#ifndef _WINSOCKAPI_ + +#undef timeval + +#endif /* _WINSOCKAPI_ */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _WIN32 */ + +#endif /* __SYS_WAIT_H_B07AB843_5FF0_4FCF_B6F0_8CB97D28F337__ */ diff --git a/deps/raii/include/compat/win32netcompat.h b/deps/raii/include/compat/win32netcompat.h new file mode 100644 index 00000000..3efb7ae3 --- /dev/null +++ b/deps/raii/include/compat/win32netcompat.h @@ -0,0 +1,58 @@ +/* + * Public domain + * + * BSD socket emulation code for Winsock2 + * Brent Cook + */ + +#ifndef LIBCRYPTOCOMPAT_WIN32NETCOMPAT_H +#define LIBCRYPTOCOMPAT_WIN32NETCOMPAT_H + +#ifdef _WIN32 + +#include +#include +#include "unistd.h" + + +#ifndef SHUT_RDWR +#define SHUT_RDWR SD_BOTH +#endif +#ifndef SHUT_RD +#define SHUT_RD SD_RECEIVE +#endif +#ifndef SHUT_WR +#define SHUT_WR SD_SEND +#endif + +int posix_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + +int posix_open(const char *path, ...); + +int posix_close(int fd); + +ssize_t posix_read(int fd, void *buf, size_t count); + +ssize_t posix_write(int fd, const void *buf, size_t count); + +int posix_getsockopt(int sockfd, int level, int optname, + void *optval, socklen_t *optlen); + +int posix_setsockopt(int sockfd, int level, int optname, + const void *optval, socklen_t optlen); + +#ifndef NO_REDEF_POSIX_FUNCTIONS +#define connect(sockfd, addr, addrlen) posix_connect(sockfd, addr, addrlen) +#define open(path, ...) posix_open(path, __VA_ARGS__) +#define close(fd) posix_close(fd) +#define read(fd, buf, count) posix_read(fd, buf, count) +#define write(fd, buf, count) posix_write(fd, buf, count) +#define getsockopt(sockfd, level, optname, optval, optlen) \ + posix_getsockopt(sockfd, level, optname, optval, optlen) +#define setsockopt(sockfd, level, optname, optval, optlen) \ + posix_setsockopt(sockfd, level, optname, optval, optlen) +#endif + +#endif + +#endif diff --git a/deps/raii/include/cthread.h b/deps/raii/include/cthread.h new file mode 100644 index 00000000..b55105ff --- /dev/null +++ b/deps/raii/include/cthread.h @@ -0,0 +1,485 @@ +/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2012 Marcus Geelnard +Copyright (c) 2013-2016 Evan Nemerson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + +SPDX-License-Identifier: Zlib +*/ + +#ifndef _CTHREAD_H_ +#define _CTHREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file +* @mainpage CThread API Reference +* +* @section intro_sec Introduction +* CThread is a minimal, portable implementation of basic threading +* classes for C. +* +* They closely mimic the functionality and naming of the C11 standard, and +* should be easily replaceable with the corresponding standard variants. +* +* @section port_sec Portability +* The Win32 variant uses the native Win32 API for implementing the thread +* classes, while for other systems, the POSIX threads API (pthread) is used. +* +* @section misc_sec Miscellaneous +* The following special keywords are available: #_Thread_local. +* +* For more detailed information, browse the different sections of this +* documentation. A good place to start is: +* cthread.h. +*/ + +/* Which platform are we on? */ +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */ +#if defined(_TTHREAD_POSIX_) + #undef _FEATURES_H + #if !defined(_GNU_SOURCE) + #define _GNU_SOURCE + #endif + #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L + #endif + #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500) + #undef _XOPEN_SOURCE + #define _XOPEN_SOURCE 500 + #endif + #define _XPG6 +#endif + +/* Generic includes */ +#include + +/* Platform specific includes */ +#if defined(_TTHREAD_POSIX_) + #include +#elif defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include + #ifdef __UNDEF_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN + #undef __UNDEF_LEAN_AND_MEAN + #endif + #if defined (__MINGW32__) && !defined(__MINGW64__) + /* Older versions of MinGW have no _ftime_s symbol */ + #define _ftime_s _ftime + #endif +#endif + +/* Compiler-specific information */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define TTHREAD_NORETURN _Noreturn +#elif defined(__GNUC__) + #define TTHREAD_NORETURN __attribute__((__noreturn__)) +#else + #define TTHREAD_NORETURN +#endif + +/* If TIME_UTC is missing, provide it and provide a wrapper for + timespec_get. */ +#ifndef TIME_UTC +#define TIME_UTC 1 +#define _TTHREAD_EMULATE_TIMESPEC_GET_ + +#if defined(_TTHREAD_WIN32_) +struct _tthread_timespec { + time_t tv_sec; + long tv_nsec; +}; +#define timespec _tthread_timespec +#endif + +int _tthread_timespec_get(struct timespec *ts, int base); +#define timespec_get _tthread_timespec_get +#endif + +/** +* @def _Thread_local +* Thread local storage keyword. +* A variable that is declared with the @c _Thread_local keyword makes the +* value of the variable local to each thread (known as thread-local storage, +* or TLS). Example usage: +* @code +* // This variable is local to each thread. +* _Thread_local int variable; +* @endcode +* @note The @c _Thread_local keyword is a macro that maps to the corresponding +* compiler directive (e.g. @c __declspec(thread)). +* @note This directive is currently not supported on Mac OS X (it will give +* a compiler error), since compile-time TLS is not supported in the Mac OS X +* executable format. Also, some older versions of MinGW (before GCC 4.x) do +* not support this directive, nor does the Tiny C Compiler. +* @hideinitializer +*/ + +#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L) && !defined(__STDC_NO_THREADS__)) && !defined(_Thread_local) + #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) + #define _Thread_local __thread + #else + #define _Thread_local __declspec(thread) + #endif +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9)) + #define _Thread_local __thread +#endif + +/* Macros */ +#if defined(_TTHREAD_WIN32_) +#define TSS_DTOR_ITERATIONS (4) +#else +#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS +#endif + +/* Function return values */ +enum +{ + thrd_success = 0, /**< The requested operation succeeded */ + thrd_busy = 1, /**< The requested operation failed because a resource requested by a test and return function is already in use */ + thrd_error = 2, /**< The requested operation failed */ + thrd_nomem = 3, /**< The requested operation failed because it was unable to allocate memory */ + thrd_timedout = 4 /**< The time specified in the call was reached without acquiring the requested resource */ +}; + +/* Mutex types */ +enum +{ + mtx_plain = 0, + mtx_recursive = 1, + mtx_timed = 2 +}; + +/* Mutex */ +#if defined(_TTHREAD_WIN32_) +typedef struct { + union { + CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */ + HANDLE mut; /* Mutex handle (used for timed mutex) */ + } mHandle; /* Mutex handle */ + int mAlreadyLocked; /* TRUE if the mutex is already locked */ + int mRecursive; /* TRUE if the mutex is recursive */ + int mTimed; /* TRUE if the mutex is timed */ +} mtx_t; +#else +typedef pthread_mutex_t mtx_t; +#endif + +/** Create a mutex object. +* @param mtx A mutex object. +* @param type Bit-mask that must have one of the following six values: +* @li @c mtx_plain for a simple non-recursive mutex +* @li @c mtx_timed for a non-recursive mutex that supports timeout +* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive) +* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive) +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_init(mtx_t *mtx, int type); + +/** Release any resources used by the given mutex. +* @param mtx A mutex object. +*/ +void mtx_destroy(mtx_t *mtx); + +/** Lock the given mutex. +* Blocks until the given mutex can be locked. If the mutex is non-recursive, and +* the calling thread already has a lock on the mutex, this call will block +* forever. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_lock(mtx_t *mtx); + +/** Lock the given mutex, or block until a specific point in time. +* Blocks until either the given mutex can be locked, or the specified TIME_UTC +* based time. +* @param mtx A mutex object. +* @param ts A UTC based calendar time +* @return @ref The mtx_timedlock function returns thrd_success on success, or +* thrd_timedout if the time specified was reached without acquiring the +* requested resource, or thrd_error if the request could not be honored. +*/ +int mtx_timedlock(mtx_t *mtx, const struct timespec *ts); + +/** Try to lock the given mutex. +* The specified mutex shall support either test and return or timeout. If the +* mutex is already locked, the function returns without blocking. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_busy if the resource +* requested is already in use, or @ref thrd_error if the request could not be +* honored. +*/ +int mtx_trylock(mtx_t *mtx); + +/** Unlock the given mutex. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int mtx_unlock(mtx_t *mtx); + +/* Condition variable */ +#if defined(_TTHREAD_WIN32_) +typedef struct { + HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */ + unsigned int mWaitersCount; /* Count of the number of waiters. */ + CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */ +} cnd_t; +#else +typedef pthread_cond_t cnd_t; +#endif + +/** Create a condition variable object. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_init(cnd_t *cond); + +/** Release any resources used by the given condition variable. +* @param cond A condition variable object. +*/ +void cnd_destroy(cnd_t *cond); + +/** Signal a condition variable. +* Unblocks one of the threads that are blocked on the given condition variable +* at the time of the call. If no threads are blocked on the condition variable +* at the time of the call, the function does nothing and return success. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_signal(cnd_t *cond); + +/** Broadcast a condition variable. +* Unblocks all of the threads that are blocked on the given condition variable +* at the time of the call. If no threads are blocked on the condition variable +* at the time of the call, the function does nothing and return success. +* @param cond A condition variable object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_broadcast(cnd_t *cond); + +/** Wait for a condition variable to become signaled. +* The function atomically unlocks the given mutex and endeavors to block until +* the given condition variable is signaled by a call to cnd_signal or to +* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex +* before it returns. +* @param cond A condition variable object. +* @param mtx A mutex object. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int cnd_wait(cnd_t *cond, mtx_t *mtx); + +/** Wait for a condition variable to become signaled. +* The function atomically unlocks the given mutex and endeavors to block until +* the given condition variable is signaled by a call to cnd_signal or to +* cnd_broadcast, or until after the specified time. When the calling thread +* becomes unblocked it locks the mutex before it returns. +* @param cond A condition variable object. +* @param mtx A mutex object. +* @param xt A point in time at which the request will time out (absolute time). +* @return @ref thrd_success upon success, or @ref thrd_timeout if the time +* specified in the call was reached without acquiring the requested resource, or +* @ref thrd_error if the request could not be honored. +*/ +int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); + +/* Thread */ +#if defined(_TTHREAD_WIN32_) +typedef DWORD thrd_t; +#else +typedef pthread_t thrd_t; +#endif + +/** Thread start function. +* Any thread that is started with the @ref thrd_create() function must be +* started through a function of this type. +* @param arg The thread argument (the @c arg argument of the corresponding +* @ref thrd_create() call). +* @return The thread return value, which can be obtained by another thread +* by using the @ref thrd_join() function. +*/ +typedef int (*thrd_start_t)(void *arg); + +/** Create a new thread. +* @param thr Identifier of the newly created thread. +* @param stacksize The initial size of the stack, in bytes. +* @param func A function pointer to the function that will be executed in +* the new thread. +* @param arg An argument to the thread function. +* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could +* be allocated for the thread requested, or @ref thrd_error if the request +* could not be honored. +* @note A thread’s identifier may be reused for a different thread once the +* original thread has exited and either been detached or joined to another +* thread. +*/ +int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); + +/** Identify the calling thread. +* @return The identifier of the calling thread. +*/ +thrd_t thrd_current(void); + +/** Dispose of any resources allocated to the thread when that thread exits. + * @return thrd_success, or thrd_error on error +*/ +int thrd_detach(thrd_t thr); + +/** Compare two thread identifiers. +* The function determines if two thread identifiers refer to the same thread. +* @return Zero if the two thread identifiers refer to different threads. +* Otherwise a nonzero value is returned. +*/ +int thrd_equal(thrd_t thr0, thrd_t thr1); + +/** Terminate execution of the calling thread. +* @param res Result code of the calling thread. +*/ +TTHREAD_NORETURN void thrd_exit(int res); + +/** Wait for a thread to terminate. +* The function joins the given thread with the current thread by blocking +* until the other thread has terminated. +* @param thr The thread to join with. +* @param res If this pointer is not NULL, the function will store the result +* code of the given thread in the integer pointed to by @c res. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int thrd_join(thrd_t thr, int *res); + +/** Put the calling thread to sleep. +* Suspend execution of the calling thread. +* @param duration Interval to sleep for +* @param remaining If non-NULL, this parameter will hold the remaining +* time until time_point upon return. This will +* typically be zero, but if the thread was woken up +* by a signal that is not ignored before duration was +* reached @c remaining will hold a positive time. +* @return 0 (zero) on successful sleep, -1 if an interrupt occurred, +* or a negative value if the operation fails. +*/ +int thrd_sleep(const struct timespec *duration, struct timespec *remaining); + +/** Yield execution to another thread. +* Permit other threads to run, even if the current thread would ordinarily +* continue to run. +*/ +void thrd_yield(void); + +/* Thread local storage */ +#if defined(_TTHREAD_WIN32_) +typedef DWORD tss_t; +#else +typedef pthread_key_t tss_t; +#endif + +/** Destructor function for a thread-specific storage. +* @param val The value of the destructed thread-specific storage. +*/ +typedef void (*tss_dtor_t)(void *val); + +/** Create a thread-specific storage. +* @param key The unique key identifier that will be set if the function is +* successful. +* @param dtor Destructor function. This can be NULL. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +* @note On Windows, the @c dtor will definitely be called when +* appropriate for threads created with @ref thrd_create. It will be +* called for other threads in most cases, the possible exception being +* for DLLs loaded with LoadLibraryEx. In order to be certain, you +* should use @ref thrd_create whenever possible. +*/ +int tss_create(tss_t *key, tss_dtor_t dtor); + +/** Delete a thread-specific storage. +* The function releases any resources used by the given thread-specific +* storage. +* @param key The key that shall be deleted. +*/ +void tss_delete(tss_t key); + +/** Get the value for a thread-specific storage. +* @param key The thread-specific storage identifier. +* @return The value for the current thread held in the given thread-specific +* storage. +*/ +void *tss_get(tss_t key); + +/** Set the value for a thread-specific storage. +* @param key The thread-specific storage identifier. +* @param val The value of the thread-specific storage to set for the current +* thread. +* @return @ref thrd_success on success, or @ref thrd_error if the request could +* not be honored. +*/ +int tss_set(tss_t key, void *val); + +#if defined(_TTHREAD_WIN32_) + typedef struct { + LONG volatile status; + CRITICAL_SECTION lock; + } once_flag; + #define ONCE_FLAG_INIT {0,} +#else + #define once_flag pthread_once_t + #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT +#endif + +/** Invoke a callback exactly once + * @param flag Flag used to ensure the callback is invoked exactly + * once. + * @param func Callback to invoke. + */ +#if defined(_TTHREAD_WIN32_) + void call_once(once_flag *flag, void (*func)(void)); +#else + #define call_once(flag,func) pthread_once(flag,func) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _CTHREAD_H_ */ diff --git a/deps/raii/include/exception.h b/deps/raii/include/exception.h new file mode 100644 index 00000000..2b75450f --- /dev/null +++ b/deps/raii/include/exception.h @@ -0,0 +1,487 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(_WIN32) + #include + #include /* setrlimit() */ +#endif + +/* exception config + */ +#if defined(__GNUC__) && (!defined(_WIN32) || !defined(_WIN64)) + #define _GNU_SOURCE +#endif + +#if defined(_WIN32) || defined(_WIN64) + #include + #if !defined(__cplusplus) + #define __STDC__ 1 + #endif +#endif + +#if !defined(RAII_MALLOC) || !defined(RAII_FREE) || !defined(RAII_REALLOC)|| !defined(RAII_CALLOC) + #include + #define RAII_MALLOC malloc + #define RAII_FREE free + #define RAII_REALLOC realloc + #define RAII_CALLOC calloc +#endif + +#ifndef RAII_INLINE + #ifdef _MSC_VER + #define RAII_INLINE __forceinline + #elif defined(__GNUC__) + #if defined(__STRICT_ANSI__) + #define RAII_INLINE __inline__ __attribute__((always_inline)) + #else + #define RAII_INLINE inline __attribute__((always_inline)) + #endif + #elif defined(__BORLANDC__) || defined(__DMC__) || defined(__SC__) || defined(__WATCOMC__) || defined(__LCC__) || defined(__DECC) + #define RAII_INLINE __inline + #else /* No inline support. */ + #define RAII_INLINE + #endif +#endif + +#ifndef RAII_NO_INLINE + #ifdef __GNUC__ + #define RAII_NO_INLINE __attribute__((noinline)) + #elif defined(_MSC_VER) + #define RAII_NO_INLINE __declspec(noinline) + #else + #define RAII_NO_INLINE + #endif +#endif + +#ifndef __builtin_expect + #define __builtin_expect(x, y) (x) +#endif + +#ifndef LIKELY_IS + #define LIKELY_IS(x, y) __builtin_expect((x), (y)) + #define LIKELY(x) LIKELY_IS(!!(x), 1) + #define UNLIKELY(x) LIKELY_IS((x), 0) + #ifndef INCREMENT + #define INCREMENT 16 + #endif +#endif + +#ifndef RAII_ASSERT + #if defined(NDEBUG) + #include + #define RAII_ASSERT(c) assert(c) + #else + #define RAII_ASSERT(c) + #endif +#endif + +#ifdef NDEBUG + #define RAII_LOG(s) puts(s) + #define RAII_INFO(s, ...) printf(s, __VA_ARGS__ ) + #define RAII_HERE() fprintf(stderr, "Here %s:%d\n", __FILE__, __LINE__) +#else + #define RAII_LOG(s) (void)s + #define RAII_INFO(s, ...) (void)s + #define RAII_HERE() (void)0 +#endif + + /* Public API qualifier. */ +#ifndef C_API + #define C_API extern +#endif + +#if !defined(thread_local) /* User can override thread_local for obscure compilers */ + /* Running in multi-threaded environment */ + #if defined(__STDC__) /* Compiling as C Language */ + #if defined(_MSC_VER) /* Don't rely on MSVC's C11 support */ + #define thread_local __declspec(thread) + #elif __STDC_VERSION__ < 201112L /* If we are on C90/99 */ + #if defined(__clang__) || defined(__GNUC__) /* Clang and GCC */ + #define thread_local __thread + #else /* Otherwise, we ignore the directive (unless user provides their own) */ + #define thread_local + #endif + #elif __APPLE__ && __MACH__ + #define thread_local __thread + #else /* C11 and newer define thread_local in threads.h */ + #undef HAS_C11_THREADS + #define HAS_C11_THREADS 1 + #include + #endif + #elif defined(__cplusplus) /* Compiling as C++ Language */ + #if __cplusplus < 201103L /* thread_local is a C++11 feature */ + #if defined(_MSC_VER) + #define thread_local __declspec(thread) + #elif defined(__clang__) || defined(__GNUC__) + #define thread_local __thread + #else /* Otherwise, we ignore the directive (unless user provides their own) */ + #define thread_local + #endif + #else /* In C++ >= 11, thread_local in a builtin keyword */ + /* Don't do anything */ + #undef HAS_C11_THREADS + #define HAS_C11_THREADS 1 + #endif + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ex_ptr_s ex_ptr_t; +typedef struct ex_context_s ex_context_t; +typedef void (*ex_setup_func)(ex_context_t *, const char *, const char *); +typedef void (*ex_terminate_func)(void); +typedef void (*ex_unwind_func)(void *); + +/* low-level api + */ +C_API void ex_throw(const char *ex, const char *file, int, const char *line, const char *message); +C_API int ex_uncaught_exception(void); +C_API void ex_terminate(void); +C_API ex_context_t *ex_init(void); +C_API void ex_unwind_set(ex_context_t *ctx, bool flag); +C_API void ex_data_set(ex_context_t *ctx, void *data); +C_API void ex_swap_set(ex_context_t *ctx, void *data); +C_API void ex_swap_reset(ex_context_t *ctx); +C_API void *ex_data_get(ex_context_t *ctx, void *data); +C_API void ex_flags_reset(void); +C_API void ex_signal(int sig, const char *ex); + +/* Convert signals into exceptions */ +C_API void ex_signal_setup(void); + +/* Reset signal handler to default */ +C_API void ex_signal_default(void); + +#ifdef _WIN32 +#define EXCEPTION_PANIC 0xE0000001 +C_API void ex_signal_seh(DWORD sig, const char *ex); +C_API int catch_seh(const char *exception, DWORD code, struct _EXCEPTION_POINTERS *ep); +C_API int catch_filter_seh(DWORD code, struct _EXCEPTION_POINTERS *ep); +#endif + +/*********************************************************** + * Implementation + */ + +/* states +*/ +enum { + ex_try_st, + ex_throw_st, + ex_catch_st, + ex_protected_st, + ex_context_st +}; + +/* some useful macros +*/ +#define EX_CAT(a, b) a ## b + +#define EX_STR_(a) #a +#define EX_STR(a) EX_STR_(a) + +#define EX_NAME(e) EX_CAT(ex_err_, e) +#define EX_PNAME(p) EX_CAT(ex_protected_, p) + +#define EX_MAKE() \ + const char *const err = ((void)err, ex_err.ex); \ + const char *const err_file = ((void)err_file, ex_err.file); \ + const int err_line = ((void)err_line, ex_err.line) + +#define EX_MAKE_IF() \ + const char *const err = ((void)err, !is_empty((void *)ex_err.panic) ? ex_err.panic : ex_err.ex); \ + const char *const err_file = ((void)err_file, ex_err.file); \ + const int err_line = ((void)err_line, ex_err.line) + +/* macros + */ +#define EX_EXCEPTION(E) \ + const char EX_NAME(E)[] = EX_STR(E) + + /* context savings +*/ +#ifdef sigsetjmp +#define ex_jmp_buf sigjmp_buf +#define ex_setjmp(buf) sigsetjmp(buf,1) +#define ex_longjmp(buf,st) siglongjmp(buf,st) +#else +#define ex_jmp_buf jmp_buf +#define ex_setjmp(buf) setjmp(buf) +#define ex_longjmp(buf,st) longjmp(buf,st) +#endif + +#define ex_throw_loc(E, F, L, C) \ + do \ + { \ + C_API const char EX_NAME(E)[]; \ + ex_throw(EX_NAME(E), F, L, C, NULL); \ + } while (0) + +/* An macro that stops the ordinary flow of control and begins panicking, +throws an exception of given message. */ +#define raii_panic(message) \ + do \ + { \ + C_API const char EX_NAME(panic)[]; \ + ex_throw(EX_NAME(panic), __FILE__, __LINE__, __FUNCTION__, (message)); \ + } while (0) + +#ifdef _WIN32 +#define throw(E) raii_panic(EX_STR(E)) +#define rethrow() \ + if (ex_err.caught > -1 || ex_err.is_rethrown) { \ + ex_err.is_rethrown = false; \ + ex_longjmp(ex_err.buf, ex_err.state | ex_throw_st); \ + } else if (true) { \ + ex_throw(ex_err.ex, ex_err.file, ex_err.line, ex_err.function, ex_err.panic); \ + } + +#define ex_signal_block(ctrl) \ + CRITICAL_SECTION ctrl##__FUNCTION__; \ + InitializeCriticalSection(&ctrl##__FUNCTION__); \ + EnterCriticalSection(&ctrl##__FUNCTION__); + +#define ex_signal_unblock(ctrl) \ + LeaveCriticalSection(&ctrl##__FUNCTION__); \ + DeleteCriticalSection(&ctrl##__FUNCTION__); + +#define ex_try \ +{ \ + if (!exception_signal_set) \ + ex_signal_setup(); \ + /* local context */ \ + ex_context_t ex_err; \ + if (!ex_context) \ + ex_init(); \ + ex_err.next = ex_context; \ + ex_err.stack = 0; \ + ex_err.ex = 0; \ + ex_err.unstack = 0; \ + ex_err.is_rethrown = false; \ + /* global context updated */ \ + ex_context = &ex_err; \ + /* save jump location */ \ + ex_err.state = ex_setjmp(ex_err.buf); \ + if (ex_err.state == ex_try_st) { \ + __try { + +#define ex_catch(E) \ + } __except(catch_seh(EX_STR(E), GetExceptionCode(), GetExceptionInformation())) { \ + if (ex_err.state == ex_throw_st) { \ + EX_MAKE_IF(); \ + ex_err.state = ex_catch_st; + +#define ex_finally \ + } \ + } \ + } \ + { \ + EX_MAKE_IF(); \ + /* global context updated */\ + ex_context = ex_err.next; + +#define ex_end_try \ + } \ + if (ex_context == &ex_err) \ + /* global context updated */ \ + ex_context = ex_err.next; \ + ex_err.caught = -1; \ + ex_err.is_rethrown = false; \ + if ((ex_err.state & ex_throw_st) != 0) \ + rethrow(); \ +} + +#define ex_catch_any \ + } __except(catch_filter_seh(GetExceptionCode(), GetExceptionInformation())) { \ + if (ex_err.state == ex_throw_st) { \ + EX_MAKE_IF(); \ + ex_err.state = ex_catch_st; + +#define ex_catch_if \ + } __except(catch_filter_seh(GetExceptionCode(), GetExceptionInformation())) { \ + if (ex_err.state == ex_throw_st) { \ + EX_MAKE_IF(); + +#define ex_finality \ + } __finally { \ + EX_MAKE_IF(); \ + /* global context updated */ \ + ex_context = ex_err.next; + +#define ex_end_trying ex_finally ex_end_try + +#else +#define ex_signal_block(ctrl) \ + sigset_t ctrl##__FUNCTION__, ctrl_all##__FUNCTION__; \ + sigfillset(&ctrl##__FUNCTION__); \ + pthread_sigmask(SIG_SETMASK, &ctrl##__FUNCTION__, &ctrl_all##__FUNCTION__); + +#define ex_signal_unblock(ctrl) \ + pthread_sigmask(SIG_SETMASK, &ctrl_all##__FUNCTION__, NULL); + +#define rethrow() \ + ex_throw(ex_err.ex, ex_err.file, ex_err.line, ex_err.function, ex_err.panic) + +#define throw(E) \ + ex_throw_loc(E, __FILE__, __LINE__, __FUNCTION__) +#define ex_try \ +{ \ + if (!exception_signal_set) \ + ex_signal_setup(); \ + /* local context */ \ + ex_context_t ex_err; \ + if (!ex_context) \ + ex_init(); \ + ex_err.next = ex_context; \ + ex_err.stack = 0; \ + ex_err.ex = 0; \ + ex_err.unstack = 0; \ + /* global context updated */ \ + ex_context = &ex_err; \ + /* save jump location */ \ + ex_err.state = ex_setjmp(ex_err.buf); \ + if (ex_err.state == ex_try_st) \ + { \ + { + +#define ex_catch_any \ + } \ + } \ + if (ex_err.state == ex_throw_st) \ + { \ + { \ + EX_MAKE(); \ + ex_err.state = ex_catch_st; + +#define ex_catch_if \ + } \ + } \ + if (ex_err.state == ex_throw_st) \ + { \ + { \ + EX_MAKE(); \ + +#define ex_finally \ + } \ + } \ + { \ + { \ + EX_MAKE(); \ + /* global context updated */ \ + ex_context = ex_err.next; + +#define ex_end_try \ + } \ + } \ + if (ex_context == &ex_err) \ + /* global context updated */ \ + ex_context = ex_err.next; \ + if ((ex_err.state & ex_throw_st) != 0) \ + rethrow(); \ + } + +#define ex_catch(E) \ + } \ + } \ + if (ex_err.state == ex_throw_st) \ + { \ + C_API const char EX_NAME(E)[]; \ + if (ex_err.ex == EX_NAME(E)) \ + { \ + EX_MAKE(); \ + ex_err.state = ex_catch_st; +#endif + +/* types +*/ + +/* stack of exception */ +struct ex_context_s { + int type; + void *data; + void *prev; + bool is_unwind; + bool is_rethrown; + bool is_guarded; + bool is_raii; + int volatile caught; + + /* The handler in the stack (which is a FILO container). */ + ex_context_t *next; + ex_ptr_t *stack; + + /** The panic error message thrown */ + const char *volatile panic; + + /** The function from which the exception was thrown */ + const char *volatile function; + + /** The name of this exception */ + const char *volatile ex; + + /* The file from whence this handler was made, in order to backtrace it later (if we want to). */ + const char *volatile file; + + /* The line from whence this handler was made, in order to backtrace it later (if we want to). */ + int volatile line; + + /* Which is our active state? */ + int volatile state; + + int unstack; + + /* The program state in which the handler was created, and the one to which it shall return. */ + ex_jmp_buf buf; +}; + +/* stack of protected pointer */ +struct ex_ptr_s { + int type; + ex_ptr_t *next; + ex_unwind_func func; + void **ptr; +}; + +/* extern declaration +*/ +C_API thread_local ex_context_t *ex_context; +C_API thread_local char ex_message[256]; +C_API ex_setup_func exception_setup_func; +C_API ex_unwind_func exception_unwind_func; +C_API ex_terminate_func exception_terminate_func; +C_API ex_terminate_func exception_ctrl_c_func; +C_API bool exception_signal_set; + +/* pointer protection +*/ +C_API ex_ptr_t ex_protect_ptr(ex_ptr_t *const_ptr, void *ptr, void (*func)(void *)); +C_API void ex_unprotected_ptr(ex_ptr_t *const_ptr); + +/* Protects dynamically allocated memory against exceptions. +If the object pointed by `ptr` changes before `unprotected()`, +the new object will be automatically protected. + +If `ptr` is not null, `func(ptr)` will be invoked during stack unwinding. */ +#define protected(ptr, func) ex_ptr_t EX_PNAME(ptr) = ex_protect_ptr(&EX_PNAME(ptr), &ptr, func) + +/* Remove memory pointer protection, does not free the memory. */ +#define unprotected(p) (ex_context->stack = EX_PNAME(p).next) + +#ifdef __cplusplus +} +#endif + +#endif /* EXCEPTION_H */ diff --git a/deps/raii/include/raii.h b/deps/raii/include/raii.h new file mode 100644 index 00000000..f0ec3177 --- /dev/null +++ b/deps/raii/include/raii.h @@ -0,0 +1,417 @@ +#ifndef RAII_H +#define RAII_H + +#if defined(_WIN32) || defined(_WIN64) + #include "compat/sys/time.h" + #include + #ifndef SYS_CONSOLE + /* O.S. physical ~input/output~ console `DEVICE`. */ + #define SYS_CONSOLE "\\\\?\\CON" + /* O.S. physical ~null~ `DEVICE`. */ + #define SYS_NULL "\\\\?\\NUL" + /* O.S. physical ~pipe~ prefix `string name` including trailing slash. */ + #define SYS_PIPE "\\\\.\\pipe\\" + #endif +#else + #ifndef SYS_CONSOLE + /* O.S. physical ~input/output~ console `DEVICE`. */ + #define SYS_CONSOLE "/dev/tty" + /* O.S. physical ~null~ `DEVICE`. */ + #define SYS_NULL "/dev/null" + /* O.S. physical ~pipe~ prefix `string name` including trailing slash. */ + #define SYS_PIPE "./" + #endif + #include +#endif + +#include "exception.h" +#include +#include + +#ifndef HAS_C11_THREADS + #include "cthread.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Smart memory pointer, the allocated memory requested in `arena` field, +all other fields private, this object binds any additional requests to it's lifetime. */ +typedef struct memory_s memory_t; +typedef memory_t unique_t; +typedef void (*func_t)(void *); + +typedef enum { + RAII_NULL, + RAII_INT, + RAII_ENUM, + RAII_INTEGER, + RAII_UINT, + RAII_SLONG, + RAII_ULONG, + RAII_LLONG, + RAII_MAXSIZE, + RAII_FLOAT, + RAII_DOUBLE, + RAII_BOOL, + RAII_SHORT, + RAII_USHORT, + RAII_CHAR, + RAII_UCHAR, + RAII_UCHAR_P, + RAII_CHAR_P, + RAII_CONST_CHAR, + RAII_STRING, + RAII_ARRAY, + RAII_HASH, + RAII_OBJ, + RAII_PTR, + RAII_FUNC, + RAII_NAN, + RAII_DEF_ARR, + RAII_DEF_FUNC, + RAII_ROUTINE, + RAII_OA_HASH, + RAII_REFLECT_TYPE, + RAII_REFLECT_INFO, + RAII_REFLECT_VALUE, + RAII_MAP_VALUE, + RAII_MAP_STRUCT, + RAII_MAP_ITER, + RAII_MAP_ARR, + RAII_ERR_PTR, + RAII_ERR_CONTEXT, + RAII_PROMISE, + RAII_FUTURE, + RAII_FUTURE_ARG, + RAII_EVENT_ARG, + RAII_ARGS, + RAII_PROCESS, + RAII_SCHED, + RAII_CHANNEL, + RAII_STRUCT, + RAII_UNION, + RAII_VALUE, + RAII_NO_INSTANCE, + RAII_GUARDED_STATUS +} raii_type; + +enum { + RAII_OK = 0, + RAII_ERR = -1, +}; + +/* Generic simple union storage types. */ +typedef union { + int integer; + unsigned int u_int; + signed long s_long; + unsigned long u_long; + long long long_long; + size_t max_size; + float point; + double precision; + bool boolean; + signed short s_short; + unsigned short u_short; + signed char schar; + unsigned char uchar; + unsigned char *uchar_ptr; + char *char_ptr; + char **array; + void *object; + func_t func; + const char const_char[256]; +} values_type; + +typedef struct { + values_type value[1]; + raii_type type; +} raii_values_t; + +typedef struct { + raii_type type; + void *value; +} var_t; + +typedef struct { + raii_type type; + void *value; + func_t dtor; +} object_t; + +typedef struct { + raii_type type; + void *base; + size_t elements; +} raii_array_t; + +typedef struct { + raii_type type; + raii_array_t base; +} defer_t; + +typedef struct { + raii_type type; + void (*func)(void *); + void *data; + void *check; +} defer_func_t; + +struct memory_s { + void *arena; + int status; + bool is_recovered; + void *volatile err; + const char *volatile panic; + bool is_protected; + ex_ptr_t *protector; + defer_t defer; + size_t mid; +}; + +/* Return current `thread` smart memory pointer. */ +C_API memory_t *raii_init(void); +C_API void raii_unwind_set(ex_context_t *ctx, const char *ex, const char *message); +C_API int raii_deferred_init(defer_t *array); + +C_API size_t raii_mid(void); +C_API size_t raii_last_mid(memory_t *scope); + +/* Defer execution `LIFO` of given function with argument, +to current `thread` scope lifetime/destruction. */ +C_API size_t raii_defer(func_t, void *); + +C_API void raii_defer_cancel(size_t index); +C_API void raii_deferred_cancel(memory_t *scope, size_t index); + +C_API void raii_defer_fire(size_t index); +C_API void raii_deferred_fire(memory_t *scope, size_t index); + +/* Same as `raii_defer` but allows recover from an Error condition throw/panic, +you must call `raii_caught` inside function to mark Error condition handled. */ +C_API void raii_recover(func_t, void *); + +/* Same as `defer` but allows recover from an Error condition throw/panic, +you must call `raii_is_caught` inside function to mark Error condition handled. */ +C_API void raii_recover_by(memory_t *, func_t, void *); + +/* Compare `err` to current error condition, will mark exception handled, if `true`. */ +C_API bool raii_caught(const char *err); + +/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ +C_API bool raii_is_caught(memory_t *scope, const char *err); + +/* Get current error condition string. */ +C_API const char *raii_message(void); + +/* Get scoped error condition string. */ +C_API const char *raii_message_by(memory_t *scope); + +/* Defer execution `LIFO` of given function with argument, +to the given `scoped smart pointer` lifetime/destruction. */ +C_API size_t raii_deferred(memory_t *, func_t, void *); +C_API size_t raii_deferred_count(memory_t *); + +/* Smart memory pointer, the allocated `size` memory requested in **->arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +Will be freed with given `func`. */ +C_API memory_t *raii_malloc_full(size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ +C_API void *malloc_full(memory_t *scope, size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed when scope smart pointer panics/returns/exits. */ +C_API void *malloc_by(memory_t *scope, size_t size); + +/* Smart memory pointer, the allocated `size` memory requested in **->arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +Will be freed with given `func`. */ +C_API memory_t *raii_calloc_full(int count, size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ +C_API void *calloc_full(memory_t *scope, int count, size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed when scope smart pointer panics/returns/exits. */ +C_API void *calloc_by(memory_t *scope, int count, size_t size); + +/* Same as `raii_deferred_free`, but also destroy smart pointer. */ +C_API void raii_delete(memory_t *ptr); + +/* Same as `raii_deferred_clean`, but also +reset/clear current `thread` smart pointer. */ +C_API void raii_destroy(void); + +/* Begin `unwinding`, executing given scope smart pointer `raii_deferred` statements. */ +C_API void raii_deferred_free(memory_t *); + +/* Begin `unwinding`, executing current `thread` scope `raii_defer` statements. */ +C_API void raii_deferred_clean(void); + +/* Creates smart memory pointer, this object binds any additional requests to it's lifetime. +for use with `malloc_*` `calloc_*` wrapper functions to request/return raw memory. */ +C_API unique_t *unique_init(void); + +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *malloc_default(size_t size); + +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *calloc_default(int count, size_t size); + +C_API values_type *raii_value(void *); +C_API raii_type type_of(void *); +C_API bool is_type(void *, raii_type); +C_API bool is_instance_of(void *, void *); +C_API bool is_value(void *); +C_API bool is_instance(void *); +C_API bool is_valid(void *); +C_API bool is_zero(size_t); +C_API bool is_empty(void *); +C_API bool is_str_in(const char *text, char *pattern); +C_API bool is_str_eq(const char *str, const char *str2); +C_API bool is_str_empty(const char *str); +C_API bool is_guard(void *self); + +C_API void *try_calloc(int, size_t); +C_API void *try_malloc(size_t); + +C_API void guard_set(ex_context_t *ctx, const char *ex, const char *message); +C_API void guard_reset(void *scope, ex_setup_func set, ex_unwind_func unwind); +C_API void guard_delete(memory_t *ptr); + +#define NONE + +/* Returns protected raw memory pointer, +DO NOT FREE, will `throw/panic` if memory request fails. */ +#define _malloc(size) malloc_full(_$##__FUNCTION__, size, RAII_FREE) + +/* Returns protected raw memory pointer, +DO NOT FREE, will `throw/panic` if memory request fails. */ +#define _calloc(count, size) calloc_full(_$##__FUNCTION__, count, size, RAII_FREE) + +/* Defer execution `LIFO` of given function with argument, +execution begins when current `guard` scope exits or panic/throw. */ +#define _defer(func, ptr) raii_recover_by(_$##__FUNCTION__, (func_t)func, ptr) + +/* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ +#define _recover(err) raii_is_caught(raii_init()->arena, err) + +/* Compare `err` to scoped error condition, +will mark exception handled, if `true`. +DO NOT PUT `err` in quote's like "err". */ +#define _is_caught(err) raii_is_caught(raii_init()->arena, EX_STR(err)) + +/* Get scoped error condition string. */ +#define _get_message() raii_message_by(raii_init()->arena) + +/* Stops the ordinary flow of control and begins panicking, +throws an exception of given message. */ +#define _panic raii_panic + +/* Makes a reference assignment of current scoped smart pointer. */ +#define _assign_ptr(scope) unique_t *scope = _$##__FUNCTION__ + +/* Exit `guarded` section, begin executing deferred functions, +return given `value` when done, use `NONE` for no return. */ +#define _return(value) \ + raii_delete(_$##__FUNCTION__); \ + guard_reset(s##__FUNCTION__, sf##__FUNCTION__, uf##__FUNCTION__); \ + ex_context = ex_err.next; \ + if (ex_context == &ex_err) \ + ex_context = ex_err.next; \ + return value; + +/* Creates an scoped guard section, it replaces `{`. +Usage of: `_defer`, `_malloc`, `_calloc`, `_assign_ptr` macros +are only valid between these sections. + - Use `_return(x);` macro, or `break;` to exit early. + - Use `_assign_ptr(var)` macro, to make assignment of block scoped smart pointer. */ +#define guard \ +{ \ + if (!exception_signal_set) \ + ex_signal_setup(); \ + void *s##__FUNCTION__ = raii_init()->arena; \ + ex_setup_func sf##__FUNCTION__ = exception_setup_func; \ + ex_unwind_func uf##__FUNCTION__ = exception_unwind_func; \ + exception_unwind_func = (ex_unwind_func)raii_deferred_free; \ + exception_setup_func = guard_set; \ + unique_t *_$##__FUNCTION__ = unique_init(); \ + (_$##__FUNCTION__)->status = RAII_GUARDED_STATUS; \ + raii_init()->arena = (void *)_$##__FUNCTION__; \ + ex_try { \ + do { + +/* This ends an scoped guard section, it replaces `}`. +On exit will begin executing deferred functions, +return given `result` when done, use `NONE` for no return. */ +#define unguarded(result) \ + } while (false); \ + raii_deferred_free(_$##__FUNCTION__); \ + } ex_catch_if { \ + if ((is_type(&(_$##__FUNCTION__)->defer, RAII_DEF_ARR))) \ + raii_deferred_free(_$##__FUNCTION__); \ + } ex_finally { \ + guard_reset(s##__FUNCTION__, sf##__FUNCTION__, uf##__FUNCTION__); \ + guard_delete(_$##__FUNCTION__); \ + } ex_end_try; \ + return result; \ +} + +/* This ends an scoped guard section, it replaces `}`. +On exit will begin executing deferred functions. */ +#define guarded \ + } while (false); \ + raii_deferred_free(_$##__FUNCTION__); \ + } ex_catch_if { \ + if ((is_type(&(_$##__FUNCTION__)->defer, RAII_DEF_ARR))) \ + raii_deferred_free(_$##__FUNCTION__); \ + } ex_finally { \ + guard_reset(s##__FUNCTION__, sf##__FUNCTION__, uf##__FUNCTION__); \ + guard_delete(_$##__FUNCTION__); \ + } ex_end_try; \ +} + + +#define block(type, name, ...) \ + type name(__VA_ARGS__) \ + guard + +#define unblocked(result) \ + unguarded(result) + +#define blocked \ + guarded + +#define try ex_try +#define catch_any ex_catch_any +#define catch_if ex_catch_if +#define catch(e) ex_catch(e) +#define end_try ex_end_try +#define finally ex_finally +#define caught(err) raii_caught(EX_STR(err)) + +#ifdef _WIN32 + #define finality ex_finality + #define end_trying ex_end_trying +#else + #define finality catch_any ex_finally + #define end_trying ex_end_try +#endif + +C_API thread_local memory_t *raii_context; + +#ifdef __cplusplus + } +#endif +#endif /* RAII_H */ diff --git a/deps/raii/n2542.pdf b/deps/raii/n2542.pdf new file mode 100644 index 00000000..6bec8149 Binary files /dev/null and b/deps/raii/n2542.pdf differ diff --git a/src/cthread.c b/deps/raii/src/cthread.c similarity index 100% rename from src/cthread.c rename to deps/raii/src/cthread.c diff --git a/src/exception.c b/deps/raii/src/exception.c similarity index 95% rename from src/exception.c rename to deps/raii/src/exception.c index 8d8a227e..80c0b154 100644 --- a/src/exception.c +++ b/deps/raii/src/exception.c @@ -103,9 +103,11 @@ static void ex_unwind_stack(ex_context_t *ctx) { if (ctx->is_unwind && exception_unwind_func) { exception_unwind_func(ctx->data); + } else if (ctx->is_unwind && ctx->is_raii && ctx->data == (void *)raii_context) { + raii_deferred_clean(); } else { while (p && p->type == ex_protected_st) { - if (got_uncaught_exception = (temp == *p->ptr)) + if ((got_uncaught_exception = (temp == *p->ptr))) break; if (*p->ptr) { @@ -175,6 +177,7 @@ ex_context_t *ex_init(void) { ex_context->is_unwind = false; ex_context->is_rethrown = false; ex_context->is_guarded = false; + ex_context->is_raii = false; ex_context->caught = -1; ex_context->type = ex_context_st; ex_signal_unblock(all); @@ -187,6 +190,9 @@ int ex_uncaught_exception(void) { } void ex_terminate(void) { + if (ex_init()->is_raii) + raii_destroy(); + if (ex_uncaught_exception() || got_uncaught_exception) ex_print(ex_context, "\nException during stack unwinding leading to an undefined behavior"); else @@ -223,6 +229,8 @@ void ex_throw(const char *exception, const char *file, int line, const char *fun if (exception_setup_func) exception_setup_func(ctx, ctx->ex, ctx->panic); + else if (ctx->is_raii) + raii_unwind_set(ctx, ctx->ex, ctx->panic); ex_unwind_stack(ctx); ex_signal_unblock(all); @@ -242,13 +250,13 @@ int catch_seh(const char *exception, DWORD code, struct _EXCEPTION_POINTERS *ep) const char *ex = 0; int i; - if (!is_str_eq(ctx->panic, exception)) + if (!is_str_eq(ctx->ex, exception)) return EXCEPTION_EXECUTE_HANDLER; for (i = 0; i < max_ex_sig; i++) { if (ex_sig[i].seh == code || ctx->caught == ex_sig[i].seh - || is_str_eq(ctx->panic, exception) + || is_str_eq(ctx->ex, exception) ) { ctx->state = ex_throw_st; ctx->is_rethrown = true; @@ -261,6 +269,8 @@ int catch_seh(const char *exception, DWORD code, struct _EXCEPTION_POINTERS *ep) if (exception_setup_func) exception_setup_func(ctx, ctx->ex, ctx->panic); + else if (ctx->is_raii) + raii_unwind_set(ctx, ctx->ex, ctx->panic); return EXCEPTION_EXECUTE_HANDLER; } } @@ -290,6 +300,8 @@ int catch_filter_seh(DWORD code, struct _EXCEPTION_POINTERS *ep) { if (exception_setup_func) exception_setup_func(ctx, ctx->ex, ctx->panic); + else if (ctx->is_raii) + raii_unwind_set(ctx, ctx->ex, ctx->panic); if (!ctx->is_guarded) ex_unwind_stack(ctx); diff --git a/src/raii.c b/deps/raii/src/raii.c similarity index 93% rename from src/raii.c rename to deps/raii/src/raii.c index f22073ea..341fdc9d 100644 --- a/src/raii.c +++ b/deps/raii/src/raii.c @@ -21,7 +21,7 @@ int raii_array_reset(raii_array_t *a) { RAII_FREE(a->base); a->base = NULL; a->elements = 0; - memset(a, 0, sizeof(a)); + memset(a, 0, sizeof(raii_array_t)); return 0; } @@ -89,6 +89,14 @@ RAII_INLINE int raii_deferred_init(defer_t *array) { return raii_array_init((raii_array_t *)array); } +void raii_unwind_set(ex_context_t *ctx, const char *ex, const char *message) { + memory_t *scope = raii_init(); + scope->err = (void *)ex; + scope->panic = message; + ex_swap_set(ctx, (void *)scope); + ex_unwind_set(ctx, scope->is_protected); +} + memory_t *raii_init(void) { if (raii_context == NULL) { raii_context = &raii_context_buffer; @@ -101,6 +109,13 @@ memory_t *raii_init(void) { raii_context->mid = -1; if (UNLIKELY(raii_deferred_init(&raii_context->defer) < 0)) raii_panic("Deferred initialization failed!"); + + ex_context_t *ctx = ex_init(); + ctx->data = (void *)raii_context; + ctx->prev = (void *)raii_context; + ctx->is_raii = true; + if (!exception_signal_set) + ex_signal_setup(); } return raii_context; @@ -173,15 +188,11 @@ void *malloc_full(memory_t *scope, size_t size, func_t func) { return arena; } -RAII_INLINE void *malloc_arena(size_t size) { +RAII_INLINE void *malloc_default(size_t size) { return malloc_full(raii_init(), size, RAII_FREE); } -RAII_INLINE memory_t *raii_new(size_t size) { - return raii_malloc_full(size, RAII_FREE); -} - -RAII_INLINE void *new_arena(memory_t *scope, size_t size) { +RAII_INLINE void *malloc_by(memory_t *scope, size_t size) { return malloc_full(scope, size, RAII_FREE); } @@ -212,14 +223,10 @@ void *calloc_full(memory_t *scope, int count, size_t size, func_t func) { return arena; } -RAII_INLINE void *calloc_arena(int count, size_t size) { +RAII_INLINE void *calloc_default(int count, size_t size) { return calloc_full(raii_init(), count, size, RAII_FREE); } -RAII_INLINE memory_t *raii_calloc(int count, size_t size) { - return raii_calloc_full(count, size, RAII_FREE); -} - RAII_INLINE void *calloc_by(memory_t *scope, int count, size_t size) { return calloc_full(scope, count, size, RAII_FREE); } @@ -230,7 +237,7 @@ void raii_delete(memory_t *ptr) { raii_deferred_free(ptr); if (ptr != &raii_context_buffer) { - memset(ptr, -1, sizeof(ptr)); + memset(ptr, -1, sizeof(memory_t)); RAII_FREE(ptr); } @@ -245,7 +252,7 @@ static void raii_array_free(void *data) { raii_array_t *array = data; raii_array_reset(array); - memset(array, 0, sizeof(array)); + memset(array, 0, sizeof(raii_array_t)); data = NULL; } @@ -417,15 +424,21 @@ RAII_INLINE void raii_recover_by(memory_t *scope, func_t func, void *data) { bool raii_caught(const char *err) { memory_t *scope = raii_init(); const char *exception = (const char *)(!is_empty((void *)scope->panic) ? scope->panic : scope->err); - if (scope->is_recovered = is_str_eq(err, exception)) + + if (exception == NULL && is_str_eq(err, ex_init()->ex)) { + ex_init()->state = ex_catch_st; + return true; + } + + if ((scope->is_recovered = is_str_eq(err, exception))) ex_init()->state = ex_catch_st; return scope->is_recovered; } -bool raii_is_caught(unique_t *scope, const char *err) { +bool raii_is_caught(memory_t *scope, const char *err) { const char *exception = (const char *)(!is_empty((void *)scope->panic) ? scope->panic : scope->err); - if (scope->is_recovered = is_str_eq(err, exception)) + if ((scope->is_recovered = is_str_eq(err, exception))) ex_init()->state = ex_catch_st; return scope->is_recovered; @@ -433,7 +446,8 @@ bool raii_is_caught(unique_t *scope, const char *err) { const char *raii_message(void) { memory_t *scope = raii_init(); - return !is_empty((void *)scope->panic) ? scope->panic : scope->err; + const char *exception = (const char *)(!is_empty((void *)scope->panic) ? scope->panic : scope->err); + return exception == NULL ? ex_init()->ex : exception; } RAII_INLINE const char *raii_message_by(memory_t *scope) { @@ -481,20 +495,6 @@ values_type *raii_value(void *data) { return; } -static void raii_unwind_set(ex_context_t *ctx, const char *ex, const char *message) { - memory_t *scope = raii_init(); - scope->err = (void *)ex; - scope->panic = message; - ex_swap_set(ctx, (void *)scope); - ex_unwind_set(ctx, scope->is_protected); -} - -void raii_setup(void) { - exception_unwind_func = (ex_unwind_func)raii_deferred_free; - exception_setup_func = raii_unwind_set; - ex_signal_setup(); -} - int strpos(const char *text, char *pattern) { size_t c, d, e, text_length, pattern_length, position = -1; diff --git a/deps/raii/tests/CMakeLists.txt b/deps/raii/tests/CMakeLists.txt new file mode 100644 index 00000000..d96505c5 --- /dev/null +++ b/deps/raii/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8...3.14) + +set(TARGET_LIST test-defer test-exceptions ) +foreach (TARGET ${TARGET_LIST}) + add_executable(${TARGET} ${TARGET}.c ) + target_link_libraries(${TARGET} raii) +endforeach() diff --git a/deps/raii/tests/test-defer.c b/deps/raii/tests/test-defer.c new file mode 100644 index 00000000..2686aef0 --- /dev/null +++ b/deps/raii/tests/test-defer.c @@ -0,0 +1,57 @@ +#include "raii.h" +#include "test_assert.h" + +char number[20]; +int g_print(void *args) { + ASSERT_NOTNULL(args); + int arg = raii_value(args)->integer; + ASSERT_EQ(true, arg >= 0); + printf("Defer in g = %d.\n\n", arg); +} + +int f_print(void *args) { + ASSERT_NULL(args); + const char *err = _get_message(); + ASSERT_STR("4", err); + puts("In defer in f"); + fflush(stdout); + if (_recover(err)) { + printf("Recovered in f = %s\n\n", err); + fflush(stdout); + } +} + +void g(int i) { + if (i > 3) { + puts("Panicking!\n"); + snprintf(number, 20, "%d", i); + _panic(number); + } + + guard { + _defer(g_print, &i); + printf("Printing in g = %d.\n", i); + g(i + 1); + } guarded; +} + +void f() +guard { + _defer(f_print, NULL); + puts("Calling g."); + g(0); + puts("Returned normally from g."); +} guarded; + +int test_main() { + f(); + puts("Returned normally from f."); + return 0; +} + +int main(int argc, char **argv) { + + ASSERT_FUNC(test_main()); + + return EXIT_SUCCESS; +} diff --git a/deps/raii/tests/test-exceptions.c b/deps/raii/tests/test-exceptions.c new file mode 100644 index 00000000..15ad9034 --- /dev/null +++ b/deps/raii/tests/test-exceptions.c @@ -0,0 +1,212 @@ +#include "raii.h" +#include "test_assert.h" +#include + +/* Basic try.. catch */ +int test_basic_catch(void) { + int caught = 0; + + try { + raise(SIGINT); + } catch (sig_int) { + caught = 1; + } end_trying; + + ASSERT_EQ(1, caught); + return 0; +} + +void test_types_pt2(int *caught) { + *caught = 0; + + try { + raise(SIGILL); + } catch (sig_segv) { + *caught = 1; + } end_trying; +} + +int test_types(void) { + int caught; + + try { + test_types_pt2(&caught); + } catch (sig_ill) { + } end_trying; + + /* Different type: should not be caught */ + ASSERT_EQ(0, caught); + return 0; +} + +/* test of the exception any subtyped system */ +int test_subtypes(void) { + int caught = 0; + + try { + raise(SIGABRT); + } catch_any { + caught = 1; + } end_trying; + + ASSERT_EQ(1, caught); + return 0; +} + +/* test the finally {} functionality */ +void test_finally_pt2(int *ran_finally) { + try { + raise(SIGSEGV); + } catch (sig_ill) { + } finally { + *ran_finally = 1; + } end_try; +} + +/* test rethrowing an exception after it is caught */ +int test_rethrow_pt2(int *caught_1, int *finally_1) { + try { + raise(SIGSEGV); + } catch (sig_segv) { + *caught_1 = 1; + rethrow(); + } finally { + *finally_1 = 1; + } end_try; + + assert(0); + return 0; +} + +int test_rethrow(void) { + int caught_1, caught_2; + int finally_1, finally_2; + + caught_1 = 0; + caught_2 = 0; + finally_1 = 0; + finally_2 = 0; + + try { + test_rethrow_pt2(&caught_1, &finally_1); + } catch_any{ + caught_2 = 1; + } finally { + finally_2 = 1; + } end_try; + + ASSERT_EQ( true, caught_1 && caught_2 && finally_1 && finally_2); + return 0; +} + +/* test throw inside finally block */ +int test_throw_in_finally_pt2(int *caught) { + try { + raise(SIGSEGV); + } catch (sig_segv) { + *caught = 1; + } finally { + raise(SIGILL); + } end_try; + + assert(0); + return 0; +} + +int test_throw_in_finally(void) { + int caught_1, caught_2; + int i; + + for (i = 0; i < 10; ++i) { + + caught_1 = 0; + caught_2 = 0; + + try { + test_throw_in_finally_pt2(&caught_1); + } catch (sig_ill) { + caught_2 = 1; + } end_trying; + + ASSERT_EQ(true, caught_1 && caught_2); + } + return 0; +} + +/* test for assert */ +int test_assert(void) { + int caught = 0; + + try { + assert(1 == 2); + raise(SIGABRT); + } catch (sig_abrt) { + caught = 1; + } end_trying; + + ASSERT_EQ(1, caught); + return 0; +} + +int test_finally(void) { + int ran_finally; + int caught; + + /* In normal conditions, finally should be run after the try block */ + + ran_finally = 0; + + try { + } finality { + ran_finally = 1; + } end_try; + ASSERT_EQ(1, ran_finally); + + /* If we catch an exception, finally should run */ + + ran_finally = 0; + + try { + raise(SIGILL); + } catch (sig_ill) { + } finally { + ran_finally = 1; + } end_try; + + ASSERT_EQ(1, ran_finally); + + /* If we have try .. finally with no catch, the finally block + * should run and the exception be passed higher up the stack. */ + + caught = 0; + ran_finally = 0; + + try { + test_finally_pt2(&ran_finally); + } finality { + caught = 1; + } end_trying; + + ASSERT_EQ( true, caught && ran_finally); + return 0; +} + +int test_list(void) +{ + test_basic_catch(); + test_types(); + test_subtypes(); + test_finally(); + test_rethrow(); + test_throw_in_finally(); + test_assert(); + + return 0; +} + +int main(int argc, char **argv) { + + ASSERT_FUNC(test_list()); + + return 0; +} diff --git a/deps/raii/tests/test_assert.h b/deps/raii/tests/test_assert.h new file mode 100644 index 00000000..3aaefc6e --- /dev/null +++ b/deps/raii/tests/test_assert.h @@ -0,0 +1,61 @@ +#ifndef TEST_ASSERT_H_ +#define TEST_ASSERT_H_ + +#include +#include +#include + +#ifdef __linux__ +# define PRINT_COLOR +#endif + +#ifdef PRINT_COLOR +# define COLOR_RED "\x1B[31m" +# define COLOR_GREEN "\x1B[32m" +# define COLOR_RESET "\033[0m" +#else +# define COLOR_RED +# define COLOR_GREEN +# define COLOR_RESET +#endif + +#define PRINT_ERR(...) printf(COLOR_RED "Failure" COLOR_RESET __VA_ARGS__) +#define PRINT_OK(...) printf(COLOR_GREEN "Passed" COLOR_RESET __VA_ARGS__) + +#define ASSERT_EQ_(expected, actual, cmp, print_op) do { \ + if (!(cmp)) \ + { \ + PRINT_ERR(" %s %d:\n * %s != %s\n * Expected: " print_op \ + "\n * Actual: " print_op "\n", __FILE__, __LINE__, \ + #expected, #actual, expected, actual); \ + return 1; \ + } \ + PRINT_OK(" %s == %s\n", #expected, #actual); \ + } while (0) + +#define ASSERT_NEQ_(expected, actual, cmp, print_op) do { \ + if (!(cmp)) \ + { \ + PRINT_ERR(" %s %d:\n * %s == %s\n * Expected: " print_op \ + "\n * Actual: " print_op "\n", __FILE__, __LINE__, \ + #expected, #actual, expected, actual); \ + return 1; \ + } \ + PRINT_OK(" %s != %s\n", #expected, #actual); \ + } while (0) + +#define ASSERT_STR(expected, actual) ASSERT_EQ_(expected, actual, strcmp(expected, actual) == 0, "%s") +#define ASSERT_PTR(expected, actual) ASSERT_EQ_(expected, actual, memcmp(expected, actual, sizeof(actual)) == 0, "%p") +#define ASSERT_UEQ(expected, actual) ASSERT_EQ_(expected, actual, expected == actual, "%zu") +#define ASSERT_EQ(expected, actual) ASSERT_EQ_(expected, actual, expected == actual, "%d") +#define ASSERT_XEQ(expected, actual) ASSERT_EQ_((int)(expected), (int)(actual), expected == actual, "%d") +#define ASSERT_NULL(actual) ASSERT_EQ_(NULL, actual, NULL == actual, "%p") +#define ASSERT_NOTNULL(actual) ASSERT_NEQ_(NULL, actual, NULL != actual, "%p") + +#define ASSERT_FUNC(FNC_CALL) do { \ + if (FNC_CALL) { \ + return 1; \ + } \ + } while (0) + +#endif diff --git a/docs/index.md b/docs/index.md index c545d4d4..169920cd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1018,14 +1018,14 @@ Coroutine scheduler exited ## Installation -The build system uses **cmake**, that produces _single_ **static** library stored under `coroutine-built`, and the complete `include` folder is needed. +The build system uses **cmake**, that produces _single_ **static** library stored under `built`, and the complete `include` folder is needed. **Linux** ```shell mkdir build cd build -cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -DBUILD_TESTING=ON/OFF # use to build files in examples folder +cmake .. -DCMAKE_BUILD_TYPE=Debug/Release -DBUILD_TESTING=ON # use to build files in examples folder cmake --build . ``` @@ -1034,7 +1034,7 @@ cmake --build . ```shell mkdir build cd build -cmake .. -D BUILD_TESTING=ON/OFF # use to build files in examples folder +cmake .. -D BUILD_TESTING=ON # use to build files in examples folder cmake --build . --config Debug/Release ``` diff --git a/include/coroutine.h b/include/coroutine.h index 8b4844dc..83c93d60 100644 --- a/include/coroutine.h +++ b/include/coroutine.h @@ -24,7 +24,6 @@ #include "uv_routine.h" #include "raii.h" -#include "cthread.h" #if defined(_MSC_VER) #define CO_MPROTECT 1 diff --git a/include/uv/evt_tls.h b/include/evt_tls.h similarity index 99% rename from include/uv/evt_tls.h rename to include/evt_tls.h index 35cf66e7..82314181 100644 --- a/include/uv/evt_tls.h +++ b/include/evt_tls.h @@ -19,7 +19,7 @@ extern "C" { #include #include #include -#include "queue.h" +#include "uv/queue.h" typedef struct evt_tls_s evt_tls_t; diff --git a/include/exception.h b/include/exception.h index 4af25b4d..2b75450f 100644 --- a/include/exception.h +++ b/include/exception.h @@ -245,7 +245,7 @@ throws an exception of given message. */ if (ex_err.caught > -1 || ex_err.is_rethrown) { \ ex_err.is_rethrown = false; \ ex_longjmp(ex_err.buf, ex_err.state | ex_throw_st); \ - } else { \ + } else if (true) { \ ex_throw(ex_err.ex, ex_err.file, ex_err.line, ex_err.function, ex_err.panic); \ } @@ -416,6 +416,7 @@ struct ex_context_s { bool is_unwind; bool is_rethrown; bool is_guarded; + bool is_raii; int volatile caught; /* The handler in the stack (which is a FILO container). */ diff --git a/include/raii.h b/include/raii.h index 38548c79..f0ec3177 100644 --- a/include/raii.h +++ b/include/raii.h @@ -36,7 +36,7 @@ extern "C" { #endif -/* Smart memory pointer, the alloacted memory requested in `arena` field, +/* Smart memory pointer, the allocated memory requested in `arena` field, all other fields private, this object binds any additional requests to it's lifetime. */ typedef struct memory_s memory_t; typedef memory_t unique_t; @@ -171,14 +171,16 @@ struct memory_s { size_t mid; }; +/* Return current `thread` smart memory pointer. */ C_API memory_t *raii_init(void); +C_API void raii_unwind_set(ex_context_t *ctx, const char *ex, const char *message); C_API int raii_deferred_init(defer_t *array); C_API size_t raii_mid(void); C_API size_t raii_last_mid(memory_t *scope); /* Defer execution `LIFO` of given function with argument, -to when current scope exits. */ +to current `thread` scope lifetime/destruction. */ C_API size_t raii_defer(func_t, void *); C_API void raii_defer_cancel(size_t index); @@ -187,19 +189,19 @@ C_API void raii_deferred_cancel(memory_t *scope, size_t index); C_API void raii_defer_fire(size_t index); C_API void raii_deferred_fire(memory_t *scope, size_t index); -/* Same as `defer` but allows recover from an Error condition throw/panic, -you must call `raii_catch` inside function to mark Error condition handled. */ +/* Same as `raii_defer` but allows recover from an Error condition throw/panic, +you must call `raii_caught` inside function to mark Error condition handled. */ C_API void raii_recover(func_t, void *); /* Same as `defer` but allows recover from an Error condition throw/panic, -you must call `raii_catch` inside function to mark Error condition handled. */ +you must call `raii_is_caught` inside function to mark Error condition handled. */ C_API void raii_recover_by(memory_t *, func_t, void *); /* Compare `err` to current error condition, will mark exception handled, if `true`. */ C_API bool raii_caught(const char *err); /* Compare `err` to scoped error condition, will mark exception handled, if `true`. */ -C_API bool raii_is_caught(unique_t *scope, const char *err); +C_API bool raii_is_caught(memory_t *scope, const char *err); /* Get current error condition string. */ C_API const char *raii_message(void); @@ -208,34 +210,66 @@ C_API const char *raii_message(void); C_API const char *raii_message_by(memory_t *scope); /* Defer execution `LIFO` of given function with argument, -to the given `scoped smart pointer` destruction. */ +to the given `scoped smart pointer` lifetime/destruction. */ C_API size_t raii_deferred(memory_t *, func_t, void *); C_API size_t raii_deferred_count(memory_t *); +/* Smart memory pointer, the allocated `size` memory requested in **->arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +Will be freed with given `func`. */ C_API memory_t *raii_malloc_full(size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ C_API void *malloc_full(memory_t *scope, size_t size, func_t func); -C_API memory_t *raii_new(size_t size); -C_API void *new_arena(memory_t *scope, size_t size); +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed when scope smart pointer panics/returns/exits. */ +C_API void *malloc_by(memory_t *scope, size_t size); + +/* Smart memory pointer, the allocated `size` memory requested in **->arena** field, +all other fields private, this object binds any additional requests to it's lifetime. +Will be freed with given `func`. */ C_API memory_t *raii_calloc_full(int count, size_t size, func_t func); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed with given `func`, when scope smart pointer panics/returns/exits. */ C_API void *calloc_full(memory_t *scope, int count, size_t size, func_t func); -C_API memory_t *raii_calloc(int count, size_t size); + +/* Request/return raw memory of given `size`, using smart memory pointer's lifetime scope handle. +DO NOT `free`, will be freed when scope smart pointer panics/returns/exits. */ C_API void *calloc_by(memory_t *scope, int count, size_t size); +/* Same as `raii_deferred_free`, but also destroy smart pointer. */ C_API void raii_delete(memory_t *ptr); + +/* Same as `raii_deferred_clean`, but also +reset/clear current `thread` smart pointer. */ C_API void raii_destroy(void); +/* Begin `unwinding`, executing given scope smart pointer `raii_deferred` statements. */ C_API void raii_deferred_free(memory_t *); -C_API void raii_deferred_clean(void); +/* Begin `unwinding`, executing current `thread` scope `raii_defer` statements. */ +C_API void raii_deferred_clean(void); +/* Creates smart memory pointer, this object binds any additional requests to it's lifetime. +for use with `malloc_*` `calloc_*` wrapper functions to request/return raw memory. */ C_API unique_t *unique_init(void); -C_API void *malloc_arena(size_t size); -C_API void *calloc_arena(int count, size_t size); +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *malloc_default(size_t size); + +/* Request/return raw memory of given `size`, +uses current `thread` smart pointer, +DO NOT `free`, will be `RAII_FREE` +when `raii_deferred_clean` is called. */ +C_API void *calloc_default(int count, size_t size); C_API values_type *raii_value(void *); -C_API void raii_setup(void); C_API raii_type type_of(void *); C_API bool is_type(void *, raii_type); C_API bool is_instance_of(void *, void *); @@ -280,6 +314,9 @@ DO NOT PUT `err` in quote's like "err". */ /* Get scoped error condition string. */ #define _get_message() raii_message_by(raii_init()->arena) + +/* Stops the ordinary flow of control and begins panicking, +throws an exception of given message. */ #define _panic raii_panic /* Makes a reference assignment of current scoped smart pointer. */ @@ -295,13 +332,11 @@ return given `value` when done, use `NONE` for no return. */ ex_context = ex_err.next; \ return value; - -/* This creates an scoped guard section, it replaces `{`. -Usage of: `_defer`, `_malloc`, `_calloc`, `_assign_ptr` - - Macros are only valid between these sections. - - Use `_return();` macro, or `break;` to exit early. - - Use `_assign_ptr` macro, for scope pointer for `_defer` -to pass scope to `_recover`. */ +/* Creates an scoped guard section, it replaces `{`. +Usage of: `_defer`, `_malloc`, `_calloc`, `_assign_ptr` macros +are only valid between these sections. + - Use `_return(x);` macro, or `break;` to exit early. + - Use `_assign_ptr(var)` macro, to make assignment of block scoped smart pointer. */ #define guard \ { \ if (!exception_signal_set) \ @@ -334,9 +369,7 @@ return given `result` when done, use `NONE` for no return. */ } /* This ends an scoped guard section, it replaces `}`. -On exit will begin executing deferred functions. - if (scope->is_recovered = is_str_eq(err, exception)) - ex_init()->state = ex_catch_st; */ +On exit will begin executing deferred functions. */ #define guarded \ } while (false); \ raii_deferred_free(_$##__FUNCTION__); \ diff --git a/include/uv_tls.h b/include/uv_tls.h index def7d229..8e0b6a2f 100644 --- a/include/uv_tls.h +++ b/include/uv_tls.h @@ -14,7 +14,7 @@ #ifdef __cplusplus extern "C" { #endif -#include "uv/evt_tls.h" +#include "evt_tls.h" #include "uv.h" #if defined(_WIN32) || defined(_WIN64) #include "compat/unistd.h" diff --git a/src/channel.c b/src/channel.c index 58c59052..71aab3b3 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" static thread_local int channel_id_generate = 0; static thread_local gc_channel_t *channel_list = NULL; diff --git a/src/coroutine.c b/src/coroutine.c index 0d1d16bd..1adedcce 100644 --- a/src/coroutine.c +++ b/src/coroutine.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" static thread_local gc_coroutine_t *coroutine_list = NULL; @@ -385,16 +385,12 @@ CO_FORCE_INLINE void co_recover(func_t func, void_t data) { raii_recover_by(co_active()->scope, func, data); } -bool co_catch(string_t err) { - routine_t *co = co_active(); - string_t exception = (string_t)(!is_empty((void_t)co->scope->panic) ? co->scope->panic : co->scope->err); - co->scope->is_recovered = is_str_eq(err, exception); - return co->scope->is_recovered; +CO_FORCE_INLINE bool co_catch(string_t err) { + return raii_is_caught(co_active()->scope, err); } -string_t co_message(void) { - routine_t *co = co_active(); - return !is_empty((void_t)co->scope->panic) ? co->scope->panic : co->scope->err; +CO_FORCE_INLINE string_t co_message(void) { + return raii_message_by(co_active()->scope); } CO_FORCE_INLINE size_t co_deferred(routine_t *coro, func_t func, void_t data) { diff --git a/src/future.c b/src/future.c index 5d0610ae..45af0da1 100644 --- a/src/future.c +++ b/src/future.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" /* Small future and promise library in C using emulated C11 threads diff --git a/src/hash.c b/src/hash.c index eedfebe3..81b59276 100644 --- a/src/hash.c +++ b/src/hash.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" /* MIT License diff --git a/src/scheduler.c b/src/scheduler.c index 3d4029cb..9d12b7bc 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -135,12 +135,10 @@ static void co_awaitable() { co_result_set(co, co->func(co->args)); co_deferred_free(co); } - } catch_any { + } catch_if { co_deferred_free(co); - if (!co->scope->is_recovered) - rethrow(); - else - ex_flags_reset(); + if (co->scope->is_recovered) + ex_flags_reset(); } finally { if (co->loop_erred) { co->halt = true; diff --git a/src/strings.c b/src/strings.c index ea512b4f..79487a1f 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" string_t co_itoa(int64_t number) { #ifdef _WIN32 diff --git a/src/uv_http.c b/src/uv_http.c index 2f353935..6bb685f1 100644 --- a/src/uv_http.c +++ b/src/uv_http.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" static string_t method_strings[] = { "DELETE", "GET", "HEAD", "POST", "PUT", "CONNECT", "OPTIONS", "TRACE", "COPY", "LOCK", "MKCOL", "MOVE", "PROPFIND", "PROPPATCH", "SEARCH", "UNLOCK", "REPORT", "MKACTIVITY", "CHECKOUT", "MERGE", "M-SEARCH", "NOTIFY", "SUBSCRIBE", "UNSUBSCRIBE", "PATCH", "PURGE" diff --git a/src/uv_tls.c b/src/uv_tls.c index d57cfdc5..ce8e1da8 100644 --- a/src/uv_tls.c +++ b/src/uv_tls.c @@ -8,7 +8,7 @@ // //%/////////////////////////////////////////////////////////////////////////// -#include "../include/uv_routine.h" +#include "uv_routine.h" #ifndef CO_ASSERT #if defined(NDEBUG) #include diff --git a/src/uv_url.c b/src/uv_url.c index 4a8dbfe5..34404aa4 100644 --- a/src/uv_url.c +++ b/src/uv_url.c @@ -1,4 +1,4 @@ -#include "../include/coroutine.h" +#include "coroutine.h" uv_handle_type scheme_type(string scheme) { if (is_str_eq(scheme, "https")