diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/main.c b/test/main.c
index 0f4329c..22803e0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,10 +24,10 @@
// For more information, please refer to
#include "utest.h"
-#include "process.h"
+#include "subprocess.h"
UTEST(utest_cmdline, filter_with_list) {
- struct process_s process;
+ struct subprocess_s process;
const char *command[3] = {"utest_test", "--list-tests", 0};
int return_code;
FILE *stdout_file;
@@ -41,10 +41,10 @@
hits = (char *)malloc(utest_state.tests_length);
memset(hits, 0, utest_state.tests_length);
- ASSERT_EQ(0, process_create(command, process_option_combined_stdout_stderr,
+ ASSERT_EQ(0, subprocess_create(command, subprocess_option_combined_stdout_stderr,
&process));
- stdout_file = process_stdout(&process);
+ stdout_file = subprocess_stdout(&process);
for (index = 0; index < utest_state.tests_length; index++) {
if (buffer != fgets(buffer, MAX_CHARS, stdout_file)) {
@@ -82,10 +82,10 @@
#endif
}
- ASSERT_EQ(0, process_join(&process, &return_code));
+ ASSERT_EQ(0, subprocess_join(&process, &return_code));
ASSERT_EQ(0, return_code);
- ASSERT_EQ(0, process_destroy(&process));
+ ASSERT_EQ(0, subprocess_destroy(&process));
// Run through all the hits and make sure we got exactly one for each.
for (kndex = 0; kndex < utest_state.tests_length; kndex++) {
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/main.c b/test/main.c
index 0f4329c..22803e0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,10 +24,10 @@
// For more information, please refer to
#include "utest.h"
-#include "process.h"
+#include "subprocess.h"
UTEST(utest_cmdline, filter_with_list) {
- struct process_s process;
+ struct subprocess_s process;
const char *command[3] = {"utest_test", "--list-tests", 0};
int return_code;
FILE *stdout_file;
@@ -41,10 +41,10 @@
hits = (char *)malloc(utest_state.tests_length);
memset(hits, 0, utest_state.tests_length);
- ASSERT_EQ(0, process_create(command, process_option_combined_stdout_stderr,
+ ASSERT_EQ(0, subprocess_create(command, subprocess_option_combined_stdout_stderr,
&process));
- stdout_file = process_stdout(&process);
+ stdout_file = subprocess_stdout(&process);
for (index = 0; index < utest_state.tests_length; index++) {
if (buffer != fgets(buffer, MAX_CHARS, stdout_file)) {
@@ -82,10 +82,10 @@
#endif
}
- ASSERT_EQ(0, process_join(&process, &return_code));
+ ASSERT_EQ(0, subprocess_join(&process, &return_code));
ASSERT_EQ(0, return_code);
- ASSERT_EQ(0, process_destroy(&process));
+ ASSERT_EQ(0, subprocess_destroy(&process));
// Run through all the hits and make sure we got exactly one for each.
for (kndex = 0; kndex < utest_state.tests_length; kndex++) {
diff --git a/test/process.h b/test/process.h
deleted file mode 100644
index d3e1a97..0000000
--- a/test/process.h
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/process.h
-*/
-
-/*
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
-
- 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 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.
-
- For more information, please refer to
-*/
-
-#ifndef SHEREDOM_PROCESS_H_INCLUDED
-#define SHEREDOM_PROCESS_H_INCLUDED
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning(push, 1)
-#endif
-
-#include
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if !defined(_MSC_VER)
-#include
-#include
-#include
-#include
-#endif
-
-#if defined(_MSC_VER)
-#ifdef _WIN64
-typedef __int64 intptr_t;
-typedef unsigned __int64 size_t;
-#else
-typedef int intptr_t;
-typedef unsigned int size_t;
-#endif
-
-typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
-typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
-typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
-
-#pragma warning(push, 1)
-struct process_process_information_s {
- void *hProcess;
- void *hThread;
- unsigned long dwProcessId;
- unsigned long dwThreadId;
-};
-
-struct process_security_attributes_s {
- unsigned long nLength;
- void *lpSecurityDescriptor;
- int bInheritHandle;
-};
-
-struct process_startup_info_s {
- unsigned long cb;
- char *lpReserved;
- char *lpDesktop;
- char *lpTitle;
- unsigned long dwX;
- unsigned long dwY;
- unsigned long dwXSize;
- unsigned long dwYSize;
- unsigned long dwXCountChars;
- unsigned long dwYCountChars;
- unsigned long dwFillAttribute;
- unsigned long dwFlags;
- unsigned short wShowWindow;
- unsigned short cbReserved2;
- unsigned char *lpReserved2;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-};
-#pragma warning(pop)
-
-__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
- unsigned long);
-__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
- LPSECURITY_ATTRIBUTES,
- unsigned long);
-__declspec(dllimport) int __stdcall CreateProcessA(
- const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
- unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
-__declspec(dllimport) int __stdcall CloseHandle(void *);
-__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
- void *, unsigned long);
-__declspec(dllimport) int __stdcall GetExitCodeProcess(
- void *, unsigned long *lpExitCode);
-__declspec(dllimport) int __cdecl _open_osfhandle(intptr_t, int);
-void *__cdecl _alloca(size_t);
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#define process_pure __attribute__((pure))
-#define process_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define process_pure
-#define process_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-struct process_s {
- FILE *stdin_file;
- FILE *stdout_file;
- FILE *stderr_file;
-
-#if defined(_MSC_VER)
- void *hProcess;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-#else
- pid_t child;
-#endif
-};
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-enum process_option_e {
- // stdout and stderr are the same FILE.
- process_option_combined_stdout_stderr = 0x1,
-
- // The child process should inherit the environment variables of the parent.
- process_option_inherit_environment = 0x2
-};
-
-/// @brief Create a process.
-/// @param command_line An array of strings for the command line to execute for
-/// this process. The last element must be NULL to signify the end of the array.
-/// @param options A bit field of process_option_e's to pass.
-/// @param out_process The newly created process.
-/// @return On success 0 is returned.
-process_weak int process_create(const char *const command_line[], int options,
- struct process_s *const out_process);
-
-/// @brief Get the standard input file for a process.
-/// @param process The process to query.
-/// @return The file for standard input of the process.
-///
-/// The file returned can be written to by the parent process to feed data to
-/// the standard input of the process.
-process_pure process_weak FILE *
-process_stdin(const struct process_s *const process);
-
-/// @brief Get the standard output file for a process.
-/// @param process The process to query.
-/// @return The file for standard output of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard output of the child process.
-process_pure process_weak FILE *
-process_stdout(const struct process_s *const process);
-
-/// @brief Get the standard error file for a process.
-/// @param process The process to query.
-/// @return The file for standard error of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard error of the child process.
-///
-/// If the process was created with the process_option_combined_stdout_stderr
-/// option bit set, this function will return NULL, and the process_stdout
-/// function should be used for both the standard output and error combined.
-process_pure process_weak FILE *
-process_stderr(const struct process_s *const process);
-
-/// @brief Wait for a process to finish execution.
-/// @param process The process to wait for.
-/// @param out_return_code The return code of the returned process (can be
-/// NULL).
-/// @return On success 0 is returned.
-process_weak int process_join(struct process_s *const process,
- int *const out_return_code);
-
-/// @brief Destroy a previously created process.
-/// @param process The process to destroy.
-///
-/// If the process to be destroyed had not finished execution, it may out live
-/// the parent process.
-process_weak int process_destroy(struct process_s *const process);
-
-int process_create(const char *const commandLine[], int options,
- struct process_s *const out_process) {
-#if defined(_MSC_VER)
- int fd;
- void *rd, *wr;
- char *commandLineCombined;
- size_t len;
- int i, j;
- const unsigned long startFUseStdHandles = 0x00000100;
- const unsigned long handleFlagInherit = 0x00000001;
- struct process_process_information_s processInfo;
- struct process_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
- char *environment = 0;
- struct process_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
- startInfo.cb = sizeof(startInfo);
- startInfo.dwFlags = startFUseStdHandles;
-
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- environment = "\0\0";
- }
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)wr, 0);
-
- if (-1 != fd) {
- out_process->stdin_file = _fdopen(fd, "wb");
-
- if (0 == out_process->stdin_file) {
- return -1;
- }
- }
-
- startInfo.hStdInput = rd;
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stdout_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stdout_file) {
- return -1;
- }
- }
-
- startInfo.hStdOutput = wr;
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- startInfo.hStdError = startInfo.hStdOutput;
- } else {
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stderr_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stderr_file) {
- return -1;
- }
- }
-
- startInfo.hStdError = wr;
- }
-
- // Combine commandLine together into a single string
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- // For the ' ' between items and trailing '\0'
- len++;
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- len++;
- }
- }
-
- commandLineCombined = (char *)_alloca(len);
-
- if (!commandLineCombined) {
- return -1;
- }
-
- // Gonna re-use len to store the write index into commandLineCombined
- len = 0;
-
- for (i = 0; commandLine[i]; i++) {
- if (0 != i) {
- commandLineCombined[len++] = ' ';
- }
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- commandLineCombined[len++] = commandLine[i][j];
- }
- }
-
- commandLineCombined[len] = '\0';
-
- if (!CreateProcessA(NULL,
- commandLineCombined, // command line
- NULL, // process security attributes
- NULL, // primary thread security attributes
- 1, // handles are inherited
- 0, // creation flags
- environment, // use parent's environment
- NULL, // use parent's current directory
- (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
- (LPPROCESS_INFORMATION)&processInfo)) {
- return -1;
- }
-
- out_process->hProcess = processInfo.hProcess;
-
- out_process->hStdInput = startInfo.hStdInput;
- out_process->hStdOutput = startInfo.hStdOutput;
- out_process->hStdError = startInfo.hStdError;
-
- // We don't need the handle of the primary thread in the called process.
- CloseHandle(processInfo.hThread);
-
- return 0;
-#else
- int stdinfd[2];
- int stdoutfd[2];
- int stderrfd[2];
- pid_t child;
-
- if (0 != pipe(stdinfd)) {
- return -1;
- }
-
- if (0 != pipe(stdoutfd)) {
- return -1;
- }
-
- if (process_option_combined_stdout_stderr !=
- (options & process_option_combined_stdout_stderr)) {
- if (0 != pipe(stderrfd)) {
- return -1;
- }
- }
-
- child = fork();
-
- if (-1 == child) {
- return -1;
- }
-
- if (0 == child) {
- // Close the stdin write end
- close(stdinfd[1]);
- // Map the read end to stdin
- dup2(stdinfd[0], STDIN_FILENO);
-
- // Close the stdout read end
- close(stdoutfd[0]);
- // Map the write end to stdout
- dup2(stdoutfd[1], STDOUT_FILENO);
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- dup2(STDOUT_FILENO, STDERR_FILENO);
- } else {
- // Close the stderr read end
- close(stderrfd[0]);
- // Map the write end to stdout
- dup2(stderrfd[1], STDERR_FILENO);
- }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#endif
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- char *const environment[1] = {0};
- exit(execve(commandLine[0], (char *const *)commandLine, environment));
- } else {
- exit(execvp(commandLine[0], (char *const *)commandLine));
- }
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- } else {
- // Close the stdin read end
- close(stdinfd[0]);
- // Store the stdin write end
- out_process->stdin_file = fdopen(stdinfd[1], "wb");
-
- // Close the stdout write end
- close(stdoutfd[1]);
- // Store the stdout read end
- out_process->stdout_file = fdopen(stdoutfd[0], "rb");
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- } else {
- // Close the stderr write end
- close(stderrfd[1]);
- // Store the stderr read end
- out_process->stderr_file = fdopen(stderrfd[0], "rb");
- }
-
- // Store the child's pid
- out_process->child = child;
-
- return 0;
- }
-#endif
-}
-
-FILE *process_stdin(const struct process_s *const process) {
- return process->stdin_file;
-}
-
-FILE *process_stdout(const struct process_s *const process) {
- return process->stdout_file;
-}
-
-FILE *process_stderr(const struct process_s *const process) {
- if (process->stdout_file != process->stderr_file) {
- return process->stderr_file;
- } else {
- return 0;
- }
-}
-
-int process_join(struct process_s *const process, int *const out_return_code) {
-#if defined(_MSC_VER)
- const unsigned long infinite = 0xFFFFFFFF;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
-
- WaitForSingleObject(process->hProcess, infinite);
-
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-
- if (out_return_code) {
- if (!GetExitCodeProcess(process->hProcess,
- (unsigned long *)out_return_code)) {
- return -1;
- }
- }
-
- return 0;
-#else
- int status;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
-
- if (process->child != waitpid(process->child, &status, 0)) {
- return -1;
- }
-
- if (out_return_code) {
- if (WIFEXITED(status)) {
- *out_return_code = WEXITSTATUS(status);
- } else {
- *out_return_code = 0;
- }
- }
-
- return 0;
-#endif
-}
-
-int process_destroy(struct process_s *const process) {
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- }
-
- fclose(process->stdout_file);
-
- if (process->stdout_file != process->stderr_file) {
- fclose(process->stderr_file);
- }
-
-#if defined(_MSC_VER)
- CloseHandle(process->hProcess);
-
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-#endif
-
- return 0;
-}
-
-#if defined(__cplusplus)
-} // extern "C"
-#endif
-
-#endif /* SHEREDOM_PROCESS_H_INCLUDED */
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/main.c b/test/main.c
index 0f4329c..22803e0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,10 +24,10 @@
// For more information, please refer to
#include "utest.h"
-#include "process.h"
+#include "subprocess.h"
UTEST(utest_cmdline, filter_with_list) {
- struct process_s process;
+ struct subprocess_s process;
const char *command[3] = {"utest_test", "--list-tests", 0};
int return_code;
FILE *stdout_file;
@@ -41,10 +41,10 @@
hits = (char *)malloc(utest_state.tests_length);
memset(hits, 0, utest_state.tests_length);
- ASSERT_EQ(0, process_create(command, process_option_combined_stdout_stderr,
+ ASSERT_EQ(0, subprocess_create(command, subprocess_option_combined_stdout_stderr,
&process));
- stdout_file = process_stdout(&process);
+ stdout_file = subprocess_stdout(&process);
for (index = 0; index < utest_state.tests_length; index++) {
if (buffer != fgets(buffer, MAX_CHARS, stdout_file)) {
@@ -82,10 +82,10 @@
#endif
}
- ASSERT_EQ(0, process_join(&process, &return_code));
+ ASSERT_EQ(0, subprocess_join(&process, &return_code));
ASSERT_EQ(0, return_code);
- ASSERT_EQ(0, process_destroy(&process));
+ ASSERT_EQ(0, subprocess_destroy(&process));
// Run through all the hits and make sure we got exactly one for each.
for (kndex = 0; kndex < utest_state.tests_length; kndex++) {
diff --git a/test/process.h b/test/process.h
deleted file mode 100644
index d3e1a97..0000000
--- a/test/process.h
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/process.h
-*/
-
-/*
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
-
- 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 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.
-
- For more information, please refer to
-*/
-
-#ifndef SHEREDOM_PROCESS_H_INCLUDED
-#define SHEREDOM_PROCESS_H_INCLUDED
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning(push, 1)
-#endif
-
-#include
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if !defined(_MSC_VER)
-#include
-#include
-#include
-#include
-#endif
-
-#if defined(_MSC_VER)
-#ifdef _WIN64
-typedef __int64 intptr_t;
-typedef unsigned __int64 size_t;
-#else
-typedef int intptr_t;
-typedef unsigned int size_t;
-#endif
-
-typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
-typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
-typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
-
-#pragma warning(push, 1)
-struct process_process_information_s {
- void *hProcess;
- void *hThread;
- unsigned long dwProcessId;
- unsigned long dwThreadId;
-};
-
-struct process_security_attributes_s {
- unsigned long nLength;
- void *lpSecurityDescriptor;
- int bInheritHandle;
-};
-
-struct process_startup_info_s {
- unsigned long cb;
- char *lpReserved;
- char *lpDesktop;
- char *lpTitle;
- unsigned long dwX;
- unsigned long dwY;
- unsigned long dwXSize;
- unsigned long dwYSize;
- unsigned long dwXCountChars;
- unsigned long dwYCountChars;
- unsigned long dwFillAttribute;
- unsigned long dwFlags;
- unsigned short wShowWindow;
- unsigned short cbReserved2;
- unsigned char *lpReserved2;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-};
-#pragma warning(pop)
-
-__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
- unsigned long);
-__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
- LPSECURITY_ATTRIBUTES,
- unsigned long);
-__declspec(dllimport) int __stdcall CreateProcessA(
- const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
- unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
-__declspec(dllimport) int __stdcall CloseHandle(void *);
-__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
- void *, unsigned long);
-__declspec(dllimport) int __stdcall GetExitCodeProcess(
- void *, unsigned long *lpExitCode);
-__declspec(dllimport) int __cdecl _open_osfhandle(intptr_t, int);
-void *__cdecl _alloca(size_t);
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#define process_pure __attribute__((pure))
-#define process_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define process_pure
-#define process_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-struct process_s {
- FILE *stdin_file;
- FILE *stdout_file;
- FILE *stderr_file;
-
-#if defined(_MSC_VER)
- void *hProcess;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-#else
- pid_t child;
-#endif
-};
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-enum process_option_e {
- // stdout and stderr are the same FILE.
- process_option_combined_stdout_stderr = 0x1,
-
- // The child process should inherit the environment variables of the parent.
- process_option_inherit_environment = 0x2
-};
-
-/// @brief Create a process.
-/// @param command_line An array of strings for the command line to execute for
-/// this process. The last element must be NULL to signify the end of the array.
-/// @param options A bit field of process_option_e's to pass.
-/// @param out_process The newly created process.
-/// @return On success 0 is returned.
-process_weak int process_create(const char *const command_line[], int options,
- struct process_s *const out_process);
-
-/// @brief Get the standard input file for a process.
-/// @param process The process to query.
-/// @return The file for standard input of the process.
-///
-/// The file returned can be written to by the parent process to feed data to
-/// the standard input of the process.
-process_pure process_weak FILE *
-process_stdin(const struct process_s *const process);
-
-/// @brief Get the standard output file for a process.
-/// @param process The process to query.
-/// @return The file for standard output of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard output of the child process.
-process_pure process_weak FILE *
-process_stdout(const struct process_s *const process);
-
-/// @brief Get the standard error file for a process.
-/// @param process The process to query.
-/// @return The file for standard error of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard error of the child process.
-///
-/// If the process was created with the process_option_combined_stdout_stderr
-/// option bit set, this function will return NULL, and the process_stdout
-/// function should be used for both the standard output and error combined.
-process_pure process_weak FILE *
-process_stderr(const struct process_s *const process);
-
-/// @brief Wait for a process to finish execution.
-/// @param process The process to wait for.
-/// @param out_return_code The return code of the returned process (can be
-/// NULL).
-/// @return On success 0 is returned.
-process_weak int process_join(struct process_s *const process,
- int *const out_return_code);
-
-/// @brief Destroy a previously created process.
-/// @param process The process to destroy.
-///
-/// If the process to be destroyed had not finished execution, it may out live
-/// the parent process.
-process_weak int process_destroy(struct process_s *const process);
-
-int process_create(const char *const commandLine[], int options,
- struct process_s *const out_process) {
-#if defined(_MSC_VER)
- int fd;
- void *rd, *wr;
- char *commandLineCombined;
- size_t len;
- int i, j;
- const unsigned long startFUseStdHandles = 0x00000100;
- const unsigned long handleFlagInherit = 0x00000001;
- struct process_process_information_s processInfo;
- struct process_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
- char *environment = 0;
- struct process_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
- startInfo.cb = sizeof(startInfo);
- startInfo.dwFlags = startFUseStdHandles;
-
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- environment = "\0\0";
- }
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)wr, 0);
-
- if (-1 != fd) {
- out_process->stdin_file = _fdopen(fd, "wb");
-
- if (0 == out_process->stdin_file) {
- return -1;
- }
- }
-
- startInfo.hStdInput = rd;
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stdout_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stdout_file) {
- return -1;
- }
- }
-
- startInfo.hStdOutput = wr;
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- startInfo.hStdError = startInfo.hStdOutput;
- } else {
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stderr_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stderr_file) {
- return -1;
- }
- }
-
- startInfo.hStdError = wr;
- }
-
- // Combine commandLine together into a single string
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- // For the ' ' between items and trailing '\0'
- len++;
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- len++;
- }
- }
-
- commandLineCombined = (char *)_alloca(len);
-
- if (!commandLineCombined) {
- return -1;
- }
-
- // Gonna re-use len to store the write index into commandLineCombined
- len = 0;
-
- for (i = 0; commandLine[i]; i++) {
- if (0 != i) {
- commandLineCombined[len++] = ' ';
- }
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- commandLineCombined[len++] = commandLine[i][j];
- }
- }
-
- commandLineCombined[len] = '\0';
-
- if (!CreateProcessA(NULL,
- commandLineCombined, // command line
- NULL, // process security attributes
- NULL, // primary thread security attributes
- 1, // handles are inherited
- 0, // creation flags
- environment, // use parent's environment
- NULL, // use parent's current directory
- (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
- (LPPROCESS_INFORMATION)&processInfo)) {
- return -1;
- }
-
- out_process->hProcess = processInfo.hProcess;
-
- out_process->hStdInput = startInfo.hStdInput;
- out_process->hStdOutput = startInfo.hStdOutput;
- out_process->hStdError = startInfo.hStdError;
-
- // We don't need the handle of the primary thread in the called process.
- CloseHandle(processInfo.hThread);
-
- return 0;
-#else
- int stdinfd[2];
- int stdoutfd[2];
- int stderrfd[2];
- pid_t child;
-
- if (0 != pipe(stdinfd)) {
- return -1;
- }
-
- if (0 != pipe(stdoutfd)) {
- return -1;
- }
-
- if (process_option_combined_stdout_stderr !=
- (options & process_option_combined_stdout_stderr)) {
- if (0 != pipe(stderrfd)) {
- return -1;
- }
- }
-
- child = fork();
-
- if (-1 == child) {
- return -1;
- }
-
- if (0 == child) {
- // Close the stdin write end
- close(stdinfd[1]);
- // Map the read end to stdin
- dup2(stdinfd[0], STDIN_FILENO);
-
- // Close the stdout read end
- close(stdoutfd[0]);
- // Map the write end to stdout
- dup2(stdoutfd[1], STDOUT_FILENO);
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- dup2(STDOUT_FILENO, STDERR_FILENO);
- } else {
- // Close the stderr read end
- close(stderrfd[0]);
- // Map the write end to stdout
- dup2(stderrfd[1], STDERR_FILENO);
- }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#endif
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- char *const environment[1] = {0};
- exit(execve(commandLine[0], (char *const *)commandLine, environment));
- } else {
- exit(execvp(commandLine[0], (char *const *)commandLine));
- }
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- } else {
- // Close the stdin read end
- close(stdinfd[0]);
- // Store the stdin write end
- out_process->stdin_file = fdopen(stdinfd[1], "wb");
-
- // Close the stdout write end
- close(stdoutfd[1]);
- // Store the stdout read end
- out_process->stdout_file = fdopen(stdoutfd[0], "rb");
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- } else {
- // Close the stderr write end
- close(stderrfd[1]);
- // Store the stderr read end
- out_process->stderr_file = fdopen(stderrfd[0], "rb");
- }
-
- // Store the child's pid
- out_process->child = child;
-
- return 0;
- }
-#endif
-}
-
-FILE *process_stdin(const struct process_s *const process) {
- return process->stdin_file;
-}
-
-FILE *process_stdout(const struct process_s *const process) {
- return process->stdout_file;
-}
-
-FILE *process_stderr(const struct process_s *const process) {
- if (process->stdout_file != process->stderr_file) {
- return process->stderr_file;
- } else {
- return 0;
- }
-}
-
-int process_join(struct process_s *const process, int *const out_return_code) {
-#if defined(_MSC_VER)
- const unsigned long infinite = 0xFFFFFFFF;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
-
- WaitForSingleObject(process->hProcess, infinite);
-
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-
- if (out_return_code) {
- if (!GetExitCodeProcess(process->hProcess,
- (unsigned long *)out_return_code)) {
- return -1;
- }
- }
-
- return 0;
-#else
- int status;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
-
- if (process->child != waitpid(process->child, &status, 0)) {
- return -1;
- }
-
- if (out_return_code) {
- if (WIFEXITED(status)) {
- *out_return_code = WEXITSTATUS(status);
- } else {
- *out_return_code = 0;
- }
- }
-
- return 0;
-#endif
-}
-
-int process_destroy(struct process_s *const process) {
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- }
-
- fclose(process->stdout_file);
-
- if (process->stdout_file != process->stderr_file) {
- fclose(process->stderr_file);
- }
-
-#if defined(_MSC_VER)
- CloseHandle(process->hProcess);
-
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-#endif
-
- return 0;
-}
-
-#if defined(__cplusplus)
-} // extern "C"
-#endif
-
-#endif /* SHEREDOM_PROCESS_H_INCLUDED */
diff --git a/test/subprocess.h b/test/subprocess.h
new file mode 100644
index 0000000..8f63657
--- /dev/null
+++ b/test/subprocess.h
@@ -0,0 +1,843 @@
+/*
+ The latest version of this library is available on GitHub;
+ https://github.com/sheredom/subprocess.h
+*/
+
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ 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 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.
+
+ For more information, please refer to
+*/
+
+#ifndef SHEREDOM_SUBPROCESS_H_INCLUDED
+#define SHEREDOM_SUBPROCESS_H_INCLUDED
+
+#if defined(_MSC_VER)
+#pragma warning(push, 1)
+#endif
+
+#include
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+#define subprocess_pure __attribute__((pure))
+#define subprocess_weak __attribute__((weak))
+#elif defined(_MSC_VER)
+#define subprocess_pure
+#define subprocess_weak __inline
+#else
+#error Non clang, non gcc, non MSVC compiler found!
+#endif
+
+struct subprocess_s;
+
+enum subprocess_option_e {
+ // stdout and stderr are the same FILE.
+ subprocess_option_combined_stdout_stderr = 0x1,
+
+ // The child process should inherit the environment variables of the parent.
+ subprocess_option_inherit_environment = 0x2,
+
+ // Enable asynchronous reading of stdout/stderr before it has completed.
+ subprocess_option_enable_async = 0x4
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/// @brief Create a process.
+/// @param command_line An array of strings for the command line to execute for
+/// this process. The last element must be NULL to signify the end of the array.
+/// @param options A bit field of subprocess_option_e's to pass.
+/// @param out_process The newly created process.
+/// @return On success 0 is returned.
+subprocess_weak int subprocess_create(const char *const command_line[],
+ int options,
+ struct subprocess_s *const out_process);
+
+/// @brief Get the standard input file for a process.
+/// @param process The process to query.
+/// @return The file for standard input of the process.
+///
+/// The file returned can be written to by the parent process to feed data to
+/// the standard input of the process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdin(const struct subprocess_s *const process);
+
+/// @brief Get the standard output file for a process.
+/// @param process The process to query.
+/// @return The file for standard output of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard output of the child process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdout(const struct subprocess_s *const process);
+
+/// @brief Get the standard error file for a process.
+/// @param process The process to query.
+/// @return The file for standard error of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard error of the child process.
+///
+/// If the process was created with the subprocess_option_combined_stdout_stderr
+/// option bit set, this function will return NULL, and the subprocess_stdout
+/// function should be used for both the standard output and error combined.
+subprocess_pure subprocess_weak FILE *
+subprocess_stderr(const struct subprocess_s *const process);
+
+/// @brief Wait for a process to finish execution.
+/// @param process The process to wait for.
+/// @param out_return_code The return code of the returned process (can be
+/// NULL).
+/// @return On success 0 is returned.
+///
+/// Joining a process will close the stdin pipe to the process.
+subprocess_weak int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code);
+
+/// @brief Destroy a previously created process.
+/// @param process The process to destroy.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it may out live
+/// the parent process.
+subprocess_weak int subprocess_destroy(struct subprocess_s *const process);
+
+/// @brief Terminate a previously created process.
+/// @param process The process to terminate.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it will be
+/// terminated (i.e killed).
+subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
+
+/// @brief Read the standard output from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard output of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+/// @brief Read the standard error from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard error of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stderr(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+#if defined(__cplusplus)
+#define SUBPROCESS_CAST(type, x) static_cast(x)
+#else
+#define SUBPROCESS_CAST(type, x) ((type)x)
+#endif
+
+#if !defined(_MSC_VER)
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#if defined(_MSC_VER)
+#ifdef _WIN64
+typedef __int64 subprocess_intptr_t;
+typedef unsigned __int64 subprocess_size_t;
+#else
+typedef int subprocess_intptr_t;
+typedef unsigned int subprocess_size_t;
+#endif
+
+typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
+typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
+typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
+typedef struct _OVERLAPPED *LPOVERLAPPED;
+
+#pragma warning(push, 1)
+struct subprocess_subprocess_information_s {
+ void *hProcess;
+ void *hThread;
+ unsigned long dwProcessId;
+ unsigned long dwThreadId;
+};
+
+struct subprocess_security_attributes_s {
+ unsigned long nLength;
+ void *lpSecurityDescriptor;
+ int bInheritHandle;
+};
+
+struct subprocess_startup_info_s {
+ unsigned long cb;
+ char *lpReserved;
+ char *lpDesktop;
+ char *lpTitle;
+ unsigned long dwX;
+ unsigned long dwY;
+ unsigned long dwXSize;
+ unsigned long dwYSize;
+ unsigned long dwXCountChars;
+ unsigned long dwYCountChars;
+ unsigned long dwFillAttribute;
+ unsigned long dwFlags;
+ unsigned short wShowWindow;
+ unsigned short cbReserved2;
+ unsigned char *lpReserved2;
+ void *hStdInput;
+ void *hStdOutput;
+ void *hStdError;
+};
+
+struct subprocess_overlapped_s {
+ uintptr_t Internal;
+ uintptr_t InternalHigh;
+ union {
+ struct {
+ unsigned long Offset;
+ unsigned long OffsetHigh;
+ } DUMMYSTRUCTNAME;
+ void *Pointer;
+ } DUMMYUNIONNAME;
+
+ void *hEvent;
+};
+
+#pragma warning(pop)
+
+__declspec(dllimport) unsigned long __stdcall GetLastError(void);
+__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
+ unsigned long);
+__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long);
+__declspec(dllimport) void *__stdcall CreateNamedPipeA(
+ const char *, unsigned long, unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long, LPSECURITY_ATTRIBUTES);
+__declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long,
+ unsigned long *, LPOVERLAPPED);
+__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void);
+__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
+__declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
+ unsigned long,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long, unsigned long,
+ void *);
+__declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
+ int, const char *);
+__declspec(dllimport) int __stdcall CreateProcessA(
+ const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
+ unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
+__declspec(dllimport) int __stdcall CloseHandle(void *);
+__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
+ void *, unsigned long);
+__declspec(dllimport) int __stdcall GetExitCodeProcess(
+ void *, unsigned long *lpExitCode);
+__declspec(dllimport) int __stdcall TerminateProcess(
+ void *, unsigned int);
+__declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
+ unsigned long, void *const *, int, unsigned long);
+__declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
+ unsigned long *, int);
+
+#if defined(_DLL) && (_DLL == 1)
+#define SUBPROCESS_DLLIMPORT __declspec(dllimport)
+#else
+#define SUBPROCESS_DLLIMPORT
+#endif
+
+SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
+SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
+SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
+
+void *__cdecl _alloca(subprocess_size_t);
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+struct subprocess_s {
+ FILE *stdin_file;
+ FILE *stdout_file;
+ FILE *stderr_file;
+
+#if defined(_MSC_VER)
+ void *hProcess;
+ void *hStdInput;
+ void *hEventOutput;
+ void *hEventError;
+#else
+ pid_t child;
+#endif
+};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#if defined(_MSC_VER)
+subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
+int subprocess_create_named_pipe_helper(void **rd, void **wr) {
+ const unsigned long pipeAccessInbound = 0x00000001;
+ const unsigned long fileFlagOverlapped = 0x40000000;
+ const unsigned long pipeTypeByte = 0x00000000;
+ const unsigned long pipeWait = 0x00000000;
+ const unsigned long genericWrite = 0x40000000;
+ const unsigned long openExisting = 3;
+ const unsigned long fileAttributeNormal = 0x00000080;
+ const void *const invalidHandleValue = (void *)~((subprocess_intptr_t)0);
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char name[256] = {0};
+ __declspec(thread) static long index = 0;
+ const long unique = index++;
+
+#if _MSC_VER < 1900
+#pragma warning(disable : 4996)
+#pragma warning(push, 1)
+
+ _snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+
+#pragma warning(pop)
+#else
+#pragma warning(disable : 4710)
+#pragma warning(push, 1)
+ snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+#pragma warning(pop)
+#endif
+
+ *rd = CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
+ pipeTypeByte | pipeWait, 1, 4096, 4096, 0,
+ (LPSECURITY_ATTRIBUTES)&saAttr);
+
+ if (invalidHandleValue == rd) {
+ return -1;
+ }
+
+ *wr = CreateFileA(name, genericWrite, 0, (LPSECURITY_ATTRIBUTES)&saAttr,
+ openExisting, fileAttributeNormal, 0);
+
+ if (invalidHandleValue == wr) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+int subprocess_create(const char *const commandLine[], int options,
+ struct subprocess_s *const out_process) {
+#if defined(_MSC_VER)
+ int fd;
+ void *rd, *wr;
+ char *commandLineCombined;
+ subprocess_size_t len;
+ int i, j;
+ const unsigned long startFUseStdHandles = 0x00000100;
+ const unsigned long handleFlagInherit = 0x00000001;
+ struct subprocess_subprocess_information_s processInfo;
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char *environment = 0;
+ struct subprocess_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ startInfo.cb = sizeof(startInfo);
+ startInfo.dwFlags = startFUseStdHandles;
+
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ environment = "\0\0";
+ }
+
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+
+ if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)wr, 0);
+
+ if (-1 != fd) {
+ out_process->stdin_file = _fdopen(fd, "wb");
+
+ if (0 == out_process->stdin_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdInput = rd;
+
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stdout_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stdout_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdOutput = wr;
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ startInfo.hStdError = startInfo.hStdOutput;
+ } else {
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stderr_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stderr_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdError = wr;
+ }
+
+ if (options & subprocess_option_enable_async) {
+ out_process->hEventOutput =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ out_process->hEventError =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ } else {
+ out_process->hEventOutput = 0;
+ out_process->hEventError = 0;
+ }
+
+ // Combine commandLine together into a single string
+ len = 0;
+ for (i = 0; commandLine[i]; i++) {
+ // For the ' ' between items and trailing '\0'
+ len++;
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ len++;
+ }
+ }
+
+ commandLineCombined = (char *)_alloca(len);
+
+ if (!commandLineCombined) {
+ return -1;
+ }
+
+ // Gonna re-use len to store the write index into commandLineCombined
+ len = 0;
+
+ for (i = 0; commandLine[i]; i++) {
+ if (0 != i) {
+ commandLineCombined[len++] = ' ';
+ }
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ commandLineCombined[len++] = commandLine[i][j];
+ }
+ }
+
+ commandLineCombined[len] = '\0';
+
+ if (!CreateProcessA(NULL,
+ commandLineCombined, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ 1, // handles are inherited
+ 0, // creation flags
+ environment, // use parent's environment
+ NULL, // use parent's current directory
+ (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
+ (LPPROCESS_INFORMATION)&processInfo)) {
+ return -1;
+ }
+
+ out_process->hProcess = processInfo.hProcess;
+
+ out_process->hStdInput = startInfo.hStdInput;
+
+ // We don't need the handle of the primary thread in the called process.
+ CloseHandle(processInfo.hThread);
+
+ if (0 != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdOutput);
+
+ if (startInfo.hStdError != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdError);
+ }
+ }
+
+ return 0;
+#else
+ int stdinfd[2];
+ int stdoutfd[2];
+ int stderrfd[2];
+ pid_t child;
+
+ if (0 != pipe(stdinfd)) {
+ return -1;
+ }
+
+ if (0 != pipe(stdoutfd)) {
+ return -1;
+ }
+
+ if (subprocess_option_combined_stdout_stderr !=
+ (options & subprocess_option_combined_stdout_stderr)) {
+ if (0 != pipe(stderrfd)) {
+ return -1;
+ }
+ }
+
+ child = fork();
+
+ if (-1 == child) {
+ return -1;
+ }
+
+ if (0 == child) {
+ // Close the stdin write end
+ close(stdinfd[1]);
+ // Map the read end to stdin
+ dup2(stdinfd[0], STDIN_FILENO);
+
+ // Close the stdout read end
+ close(stdoutfd[0]);
+ // Map the write end to stdout
+ dup2(stdoutfd[1], STDOUT_FILENO);
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ } else {
+ // Close the stderr read end
+ close(stderrfd[0]);
+ // Map the write end to stdout
+ dup2(stderrfd[1], STDERR_FILENO);
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#endif
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ char *const environment[1] = {0};
+ exit(execve(commandLine[0], (char *const *)commandLine, environment));
+ } else {
+ exit(execvp(commandLine[0], (char *const *)commandLine));
+ }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ } else {
+ // Close the stdin read end
+ close(stdinfd[0]);
+ // Store the stdin write end
+ out_process->stdin_file = fdopen(stdinfd[1], "wb");
+
+ // Close the stdout write end
+ close(stdoutfd[1]);
+ // Store the stdout read end
+ out_process->stdout_file = fdopen(stdoutfd[0], "rb");
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ } else {
+ // Close the stderr write end
+ close(stderrfd[1]);
+ // Store the stderr read end
+ out_process->stderr_file = fdopen(stderrfd[0], "rb");
+ }
+
+ // Store the child's pid
+ out_process->child = child;
+
+ return 0;
+ }
+#endif
+}
+
+FILE *subprocess_stdin(const struct subprocess_s *const process) {
+ return process->stdin_file;
+}
+
+FILE *subprocess_stdout(const struct subprocess_s *const process) {
+ return process->stdout_file;
+}
+
+FILE *subprocess_stderr(const struct subprocess_s *const process) {
+ if (process->stdout_file != process->stderr_file) {
+ return process->stderr_file;
+ } else {
+ return 0;
+ }
+}
+
+int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code) {
+#if defined(_MSC_VER)
+ const unsigned long infinite = 0xFFFFFFFF;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ process->hStdInput = NULL;
+ }
+
+ WaitForSingleObject(process->hProcess, infinite);
+
+ if (out_return_code) {
+ if (!GetExitCodeProcess(process->hProcess,
+ (unsigned long *)out_return_code)) {
+ return -1;
+ }
+ }
+
+ return 0;
+#else
+ int status;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (process->child != waitpid(process->child, &status, 0)) {
+ return -1;
+ }
+
+ if (out_return_code) {
+ if (WIFEXITED(status)) {
+ *out_return_code = WEXITSTATUS(status);
+ } else {
+ *out_return_code = EXIT_FAILURE;
+ }
+ }
+
+ return 0;
+#endif
+}
+
+int subprocess_destroy(struct subprocess_s *const process) {
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (0 != process->stdout_file) {
+ fclose(process->stdout_file);
+
+ if (process->stdout_file != process->stderr_file) {
+ fclose(process->stderr_file);
+ }
+
+ process->stdout_file = 0;
+ process->stderr_file = 0;
+ }
+
+#if defined(_MSC_VER)
+ if (process->hProcess) {
+ CloseHandle(process->hProcess);
+ process->hProcess = 0;
+
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ }
+
+ if (0 != process->hEventOutput) {
+ CloseHandle(process->hEventOutput);
+ }
+
+ if (0 != process->hEventError) {
+ CloseHandle(process->hEventError);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int subprocess_terminate(struct subprocess_s *const process) {
+#if defined(_MSC_VER)
+ unsigned int killed_process_exit_code;
+ int success_terminate;
+ int windows_call_result;
+
+ killed_process_exit_code = 99;
+ windows_call_result = TerminateProcess(process->hProcess, killed_process_exit_code);
+ success_terminate = (windows_call_result== 0) ? 1 : 0;
+ return success_terminate;
+#else
+ int result;
+ result = kill(process->child, 9);
+ return result;
+#endif
+}
+
+unsigned subprocess_read_stdout(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventOutput;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stdout_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stdout_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+unsigned subprocess_read_stderr(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventError;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stderr_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stderr_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/main.c b/test/main.c
index 0f4329c..22803e0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,10 +24,10 @@
// For more information, please refer to
#include "utest.h"
-#include "process.h"
+#include "subprocess.h"
UTEST(utest_cmdline, filter_with_list) {
- struct process_s process;
+ struct subprocess_s process;
const char *command[3] = {"utest_test", "--list-tests", 0};
int return_code;
FILE *stdout_file;
@@ -41,10 +41,10 @@
hits = (char *)malloc(utest_state.tests_length);
memset(hits, 0, utest_state.tests_length);
- ASSERT_EQ(0, process_create(command, process_option_combined_stdout_stderr,
+ ASSERT_EQ(0, subprocess_create(command, subprocess_option_combined_stdout_stderr,
&process));
- stdout_file = process_stdout(&process);
+ stdout_file = subprocess_stdout(&process);
for (index = 0; index < utest_state.tests_length; index++) {
if (buffer != fgets(buffer, MAX_CHARS, stdout_file)) {
@@ -82,10 +82,10 @@
#endif
}
- ASSERT_EQ(0, process_join(&process, &return_code));
+ ASSERT_EQ(0, subprocess_join(&process, &return_code));
ASSERT_EQ(0, return_code);
- ASSERT_EQ(0, process_destroy(&process));
+ ASSERT_EQ(0, subprocess_destroy(&process));
// Run through all the hits and make sure we got exactly one for each.
for (kndex = 0; kndex < utest_state.tests_length; kndex++) {
diff --git a/test/process.h b/test/process.h
deleted file mode 100644
index d3e1a97..0000000
--- a/test/process.h
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/process.h
-*/
-
-/*
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
-
- 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 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.
-
- For more information, please refer to
-*/
-
-#ifndef SHEREDOM_PROCESS_H_INCLUDED
-#define SHEREDOM_PROCESS_H_INCLUDED
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning(push, 1)
-#endif
-
-#include
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if !defined(_MSC_VER)
-#include
-#include
-#include
-#include
-#endif
-
-#if defined(_MSC_VER)
-#ifdef _WIN64
-typedef __int64 intptr_t;
-typedef unsigned __int64 size_t;
-#else
-typedef int intptr_t;
-typedef unsigned int size_t;
-#endif
-
-typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
-typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
-typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
-
-#pragma warning(push, 1)
-struct process_process_information_s {
- void *hProcess;
- void *hThread;
- unsigned long dwProcessId;
- unsigned long dwThreadId;
-};
-
-struct process_security_attributes_s {
- unsigned long nLength;
- void *lpSecurityDescriptor;
- int bInheritHandle;
-};
-
-struct process_startup_info_s {
- unsigned long cb;
- char *lpReserved;
- char *lpDesktop;
- char *lpTitle;
- unsigned long dwX;
- unsigned long dwY;
- unsigned long dwXSize;
- unsigned long dwYSize;
- unsigned long dwXCountChars;
- unsigned long dwYCountChars;
- unsigned long dwFillAttribute;
- unsigned long dwFlags;
- unsigned short wShowWindow;
- unsigned short cbReserved2;
- unsigned char *lpReserved2;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-};
-#pragma warning(pop)
-
-__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
- unsigned long);
-__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
- LPSECURITY_ATTRIBUTES,
- unsigned long);
-__declspec(dllimport) int __stdcall CreateProcessA(
- const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
- unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
-__declspec(dllimport) int __stdcall CloseHandle(void *);
-__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
- void *, unsigned long);
-__declspec(dllimport) int __stdcall GetExitCodeProcess(
- void *, unsigned long *lpExitCode);
-__declspec(dllimport) int __cdecl _open_osfhandle(intptr_t, int);
-void *__cdecl _alloca(size_t);
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#define process_pure __attribute__((pure))
-#define process_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define process_pure
-#define process_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-struct process_s {
- FILE *stdin_file;
- FILE *stdout_file;
- FILE *stderr_file;
-
-#if defined(_MSC_VER)
- void *hProcess;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-#else
- pid_t child;
-#endif
-};
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-enum process_option_e {
- // stdout and stderr are the same FILE.
- process_option_combined_stdout_stderr = 0x1,
-
- // The child process should inherit the environment variables of the parent.
- process_option_inherit_environment = 0x2
-};
-
-/// @brief Create a process.
-/// @param command_line An array of strings for the command line to execute for
-/// this process. The last element must be NULL to signify the end of the array.
-/// @param options A bit field of process_option_e's to pass.
-/// @param out_process The newly created process.
-/// @return On success 0 is returned.
-process_weak int process_create(const char *const command_line[], int options,
- struct process_s *const out_process);
-
-/// @brief Get the standard input file for a process.
-/// @param process The process to query.
-/// @return The file for standard input of the process.
-///
-/// The file returned can be written to by the parent process to feed data to
-/// the standard input of the process.
-process_pure process_weak FILE *
-process_stdin(const struct process_s *const process);
-
-/// @brief Get the standard output file for a process.
-/// @param process The process to query.
-/// @return The file for standard output of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard output of the child process.
-process_pure process_weak FILE *
-process_stdout(const struct process_s *const process);
-
-/// @brief Get the standard error file for a process.
-/// @param process The process to query.
-/// @return The file for standard error of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard error of the child process.
-///
-/// If the process was created with the process_option_combined_stdout_stderr
-/// option bit set, this function will return NULL, and the process_stdout
-/// function should be used for both the standard output and error combined.
-process_pure process_weak FILE *
-process_stderr(const struct process_s *const process);
-
-/// @brief Wait for a process to finish execution.
-/// @param process The process to wait for.
-/// @param out_return_code The return code of the returned process (can be
-/// NULL).
-/// @return On success 0 is returned.
-process_weak int process_join(struct process_s *const process,
- int *const out_return_code);
-
-/// @brief Destroy a previously created process.
-/// @param process The process to destroy.
-///
-/// If the process to be destroyed had not finished execution, it may out live
-/// the parent process.
-process_weak int process_destroy(struct process_s *const process);
-
-int process_create(const char *const commandLine[], int options,
- struct process_s *const out_process) {
-#if defined(_MSC_VER)
- int fd;
- void *rd, *wr;
- char *commandLineCombined;
- size_t len;
- int i, j;
- const unsigned long startFUseStdHandles = 0x00000100;
- const unsigned long handleFlagInherit = 0x00000001;
- struct process_process_information_s processInfo;
- struct process_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
- char *environment = 0;
- struct process_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
- startInfo.cb = sizeof(startInfo);
- startInfo.dwFlags = startFUseStdHandles;
-
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- environment = "\0\0";
- }
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)wr, 0);
-
- if (-1 != fd) {
- out_process->stdin_file = _fdopen(fd, "wb");
-
- if (0 == out_process->stdin_file) {
- return -1;
- }
- }
-
- startInfo.hStdInput = rd;
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stdout_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stdout_file) {
- return -1;
- }
- }
-
- startInfo.hStdOutput = wr;
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- startInfo.hStdError = startInfo.hStdOutput;
- } else {
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stderr_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stderr_file) {
- return -1;
- }
- }
-
- startInfo.hStdError = wr;
- }
-
- // Combine commandLine together into a single string
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- // For the ' ' between items and trailing '\0'
- len++;
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- len++;
- }
- }
-
- commandLineCombined = (char *)_alloca(len);
-
- if (!commandLineCombined) {
- return -1;
- }
-
- // Gonna re-use len to store the write index into commandLineCombined
- len = 0;
-
- for (i = 0; commandLine[i]; i++) {
- if (0 != i) {
- commandLineCombined[len++] = ' ';
- }
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- commandLineCombined[len++] = commandLine[i][j];
- }
- }
-
- commandLineCombined[len] = '\0';
-
- if (!CreateProcessA(NULL,
- commandLineCombined, // command line
- NULL, // process security attributes
- NULL, // primary thread security attributes
- 1, // handles are inherited
- 0, // creation flags
- environment, // use parent's environment
- NULL, // use parent's current directory
- (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
- (LPPROCESS_INFORMATION)&processInfo)) {
- return -1;
- }
-
- out_process->hProcess = processInfo.hProcess;
-
- out_process->hStdInput = startInfo.hStdInput;
- out_process->hStdOutput = startInfo.hStdOutput;
- out_process->hStdError = startInfo.hStdError;
-
- // We don't need the handle of the primary thread in the called process.
- CloseHandle(processInfo.hThread);
-
- return 0;
-#else
- int stdinfd[2];
- int stdoutfd[2];
- int stderrfd[2];
- pid_t child;
-
- if (0 != pipe(stdinfd)) {
- return -1;
- }
-
- if (0 != pipe(stdoutfd)) {
- return -1;
- }
-
- if (process_option_combined_stdout_stderr !=
- (options & process_option_combined_stdout_stderr)) {
- if (0 != pipe(stderrfd)) {
- return -1;
- }
- }
-
- child = fork();
-
- if (-1 == child) {
- return -1;
- }
-
- if (0 == child) {
- // Close the stdin write end
- close(stdinfd[1]);
- // Map the read end to stdin
- dup2(stdinfd[0], STDIN_FILENO);
-
- // Close the stdout read end
- close(stdoutfd[0]);
- // Map the write end to stdout
- dup2(stdoutfd[1], STDOUT_FILENO);
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- dup2(STDOUT_FILENO, STDERR_FILENO);
- } else {
- // Close the stderr read end
- close(stderrfd[0]);
- // Map the write end to stdout
- dup2(stderrfd[1], STDERR_FILENO);
- }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#endif
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- char *const environment[1] = {0};
- exit(execve(commandLine[0], (char *const *)commandLine, environment));
- } else {
- exit(execvp(commandLine[0], (char *const *)commandLine));
- }
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- } else {
- // Close the stdin read end
- close(stdinfd[0]);
- // Store the stdin write end
- out_process->stdin_file = fdopen(stdinfd[1], "wb");
-
- // Close the stdout write end
- close(stdoutfd[1]);
- // Store the stdout read end
- out_process->stdout_file = fdopen(stdoutfd[0], "rb");
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- } else {
- // Close the stderr write end
- close(stderrfd[1]);
- // Store the stderr read end
- out_process->stderr_file = fdopen(stderrfd[0], "rb");
- }
-
- // Store the child's pid
- out_process->child = child;
-
- return 0;
- }
-#endif
-}
-
-FILE *process_stdin(const struct process_s *const process) {
- return process->stdin_file;
-}
-
-FILE *process_stdout(const struct process_s *const process) {
- return process->stdout_file;
-}
-
-FILE *process_stderr(const struct process_s *const process) {
- if (process->stdout_file != process->stderr_file) {
- return process->stderr_file;
- } else {
- return 0;
- }
-}
-
-int process_join(struct process_s *const process, int *const out_return_code) {
-#if defined(_MSC_VER)
- const unsigned long infinite = 0xFFFFFFFF;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
-
- WaitForSingleObject(process->hProcess, infinite);
-
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-
- if (out_return_code) {
- if (!GetExitCodeProcess(process->hProcess,
- (unsigned long *)out_return_code)) {
- return -1;
- }
- }
-
- return 0;
-#else
- int status;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
-
- if (process->child != waitpid(process->child, &status, 0)) {
- return -1;
- }
-
- if (out_return_code) {
- if (WIFEXITED(status)) {
- *out_return_code = WEXITSTATUS(status);
- } else {
- *out_return_code = 0;
- }
- }
-
- return 0;
-#endif
-}
-
-int process_destroy(struct process_s *const process) {
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- }
-
- fclose(process->stdout_file);
-
- if (process->stdout_file != process->stderr_file) {
- fclose(process->stderr_file);
- }
-
-#if defined(_MSC_VER)
- CloseHandle(process->hProcess);
-
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-#endif
-
- return 0;
-}
-
-#if defined(__cplusplus)
-} // extern "C"
-#endif
-
-#endif /* SHEREDOM_PROCESS_H_INCLUDED */
diff --git a/test/subprocess.h b/test/subprocess.h
new file mode 100644
index 0000000..8f63657
--- /dev/null
+++ b/test/subprocess.h
@@ -0,0 +1,843 @@
+/*
+ The latest version of this library is available on GitHub;
+ https://github.com/sheredom/subprocess.h
+*/
+
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ 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 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.
+
+ For more information, please refer to
+*/
+
+#ifndef SHEREDOM_SUBPROCESS_H_INCLUDED
+#define SHEREDOM_SUBPROCESS_H_INCLUDED
+
+#if defined(_MSC_VER)
+#pragma warning(push, 1)
+#endif
+
+#include
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+#define subprocess_pure __attribute__((pure))
+#define subprocess_weak __attribute__((weak))
+#elif defined(_MSC_VER)
+#define subprocess_pure
+#define subprocess_weak __inline
+#else
+#error Non clang, non gcc, non MSVC compiler found!
+#endif
+
+struct subprocess_s;
+
+enum subprocess_option_e {
+ // stdout and stderr are the same FILE.
+ subprocess_option_combined_stdout_stderr = 0x1,
+
+ // The child process should inherit the environment variables of the parent.
+ subprocess_option_inherit_environment = 0x2,
+
+ // Enable asynchronous reading of stdout/stderr before it has completed.
+ subprocess_option_enable_async = 0x4
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/// @brief Create a process.
+/// @param command_line An array of strings for the command line to execute for
+/// this process. The last element must be NULL to signify the end of the array.
+/// @param options A bit field of subprocess_option_e's to pass.
+/// @param out_process The newly created process.
+/// @return On success 0 is returned.
+subprocess_weak int subprocess_create(const char *const command_line[],
+ int options,
+ struct subprocess_s *const out_process);
+
+/// @brief Get the standard input file for a process.
+/// @param process The process to query.
+/// @return The file for standard input of the process.
+///
+/// The file returned can be written to by the parent process to feed data to
+/// the standard input of the process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdin(const struct subprocess_s *const process);
+
+/// @brief Get the standard output file for a process.
+/// @param process The process to query.
+/// @return The file for standard output of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard output of the child process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdout(const struct subprocess_s *const process);
+
+/// @brief Get the standard error file for a process.
+/// @param process The process to query.
+/// @return The file for standard error of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard error of the child process.
+///
+/// If the process was created with the subprocess_option_combined_stdout_stderr
+/// option bit set, this function will return NULL, and the subprocess_stdout
+/// function should be used for both the standard output and error combined.
+subprocess_pure subprocess_weak FILE *
+subprocess_stderr(const struct subprocess_s *const process);
+
+/// @brief Wait for a process to finish execution.
+/// @param process The process to wait for.
+/// @param out_return_code The return code of the returned process (can be
+/// NULL).
+/// @return On success 0 is returned.
+///
+/// Joining a process will close the stdin pipe to the process.
+subprocess_weak int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code);
+
+/// @brief Destroy a previously created process.
+/// @param process The process to destroy.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it may out live
+/// the parent process.
+subprocess_weak int subprocess_destroy(struct subprocess_s *const process);
+
+/// @brief Terminate a previously created process.
+/// @param process The process to terminate.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it will be
+/// terminated (i.e killed).
+subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
+
+/// @brief Read the standard output from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard output of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+/// @brief Read the standard error from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard error of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stderr(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+#if defined(__cplusplus)
+#define SUBPROCESS_CAST(type, x) static_cast(x)
+#else
+#define SUBPROCESS_CAST(type, x) ((type)x)
+#endif
+
+#if !defined(_MSC_VER)
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#if defined(_MSC_VER)
+#ifdef _WIN64
+typedef __int64 subprocess_intptr_t;
+typedef unsigned __int64 subprocess_size_t;
+#else
+typedef int subprocess_intptr_t;
+typedef unsigned int subprocess_size_t;
+#endif
+
+typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
+typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
+typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
+typedef struct _OVERLAPPED *LPOVERLAPPED;
+
+#pragma warning(push, 1)
+struct subprocess_subprocess_information_s {
+ void *hProcess;
+ void *hThread;
+ unsigned long dwProcessId;
+ unsigned long dwThreadId;
+};
+
+struct subprocess_security_attributes_s {
+ unsigned long nLength;
+ void *lpSecurityDescriptor;
+ int bInheritHandle;
+};
+
+struct subprocess_startup_info_s {
+ unsigned long cb;
+ char *lpReserved;
+ char *lpDesktop;
+ char *lpTitle;
+ unsigned long dwX;
+ unsigned long dwY;
+ unsigned long dwXSize;
+ unsigned long dwYSize;
+ unsigned long dwXCountChars;
+ unsigned long dwYCountChars;
+ unsigned long dwFillAttribute;
+ unsigned long dwFlags;
+ unsigned short wShowWindow;
+ unsigned short cbReserved2;
+ unsigned char *lpReserved2;
+ void *hStdInput;
+ void *hStdOutput;
+ void *hStdError;
+};
+
+struct subprocess_overlapped_s {
+ uintptr_t Internal;
+ uintptr_t InternalHigh;
+ union {
+ struct {
+ unsigned long Offset;
+ unsigned long OffsetHigh;
+ } DUMMYSTRUCTNAME;
+ void *Pointer;
+ } DUMMYUNIONNAME;
+
+ void *hEvent;
+};
+
+#pragma warning(pop)
+
+__declspec(dllimport) unsigned long __stdcall GetLastError(void);
+__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
+ unsigned long);
+__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long);
+__declspec(dllimport) void *__stdcall CreateNamedPipeA(
+ const char *, unsigned long, unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long, LPSECURITY_ATTRIBUTES);
+__declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long,
+ unsigned long *, LPOVERLAPPED);
+__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void);
+__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
+__declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
+ unsigned long,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long, unsigned long,
+ void *);
+__declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
+ int, const char *);
+__declspec(dllimport) int __stdcall CreateProcessA(
+ const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
+ unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
+__declspec(dllimport) int __stdcall CloseHandle(void *);
+__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
+ void *, unsigned long);
+__declspec(dllimport) int __stdcall GetExitCodeProcess(
+ void *, unsigned long *lpExitCode);
+__declspec(dllimport) int __stdcall TerminateProcess(
+ void *, unsigned int);
+__declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
+ unsigned long, void *const *, int, unsigned long);
+__declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
+ unsigned long *, int);
+
+#if defined(_DLL) && (_DLL == 1)
+#define SUBPROCESS_DLLIMPORT __declspec(dllimport)
+#else
+#define SUBPROCESS_DLLIMPORT
+#endif
+
+SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
+SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
+SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
+
+void *__cdecl _alloca(subprocess_size_t);
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+struct subprocess_s {
+ FILE *stdin_file;
+ FILE *stdout_file;
+ FILE *stderr_file;
+
+#if defined(_MSC_VER)
+ void *hProcess;
+ void *hStdInput;
+ void *hEventOutput;
+ void *hEventError;
+#else
+ pid_t child;
+#endif
+};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#if defined(_MSC_VER)
+subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
+int subprocess_create_named_pipe_helper(void **rd, void **wr) {
+ const unsigned long pipeAccessInbound = 0x00000001;
+ const unsigned long fileFlagOverlapped = 0x40000000;
+ const unsigned long pipeTypeByte = 0x00000000;
+ const unsigned long pipeWait = 0x00000000;
+ const unsigned long genericWrite = 0x40000000;
+ const unsigned long openExisting = 3;
+ const unsigned long fileAttributeNormal = 0x00000080;
+ const void *const invalidHandleValue = (void *)~((subprocess_intptr_t)0);
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char name[256] = {0};
+ __declspec(thread) static long index = 0;
+ const long unique = index++;
+
+#if _MSC_VER < 1900
+#pragma warning(disable : 4996)
+#pragma warning(push, 1)
+
+ _snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+
+#pragma warning(pop)
+#else
+#pragma warning(disable : 4710)
+#pragma warning(push, 1)
+ snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+#pragma warning(pop)
+#endif
+
+ *rd = CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
+ pipeTypeByte | pipeWait, 1, 4096, 4096, 0,
+ (LPSECURITY_ATTRIBUTES)&saAttr);
+
+ if (invalidHandleValue == rd) {
+ return -1;
+ }
+
+ *wr = CreateFileA(name, genericWrite, 0, (LPSECURITY_ATTRIBUTES)&saAttr,
+ openExisting, fileAttributeNormal, 0);
+
+ if (invalidHandleValue == wr) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+int subprocess_create(const char *const commandLine[], int options,
+ struct subprocess_s *const out_process) {
+#if defined(_MSC_VER)
+ int fd;
+ void *rd, *wr;
+ char *commandLineCombined;
+ subprocess_size_t len;
+ int i, j;
+ const unsigned long startFUseStdHandles = 0x00000100;
+ const unsigned long handleFlagInherit = 0x00000001;
+ struct subprocess_subprocess_information_s processInfo;
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char *environment = 0;
+ struct subprocess_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ startInfo.cb = sizeof(startInfo);
+ startInfo.dwFlags = startFUseStdHandles;
+
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ environment = "\0\0";
+ }
+
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+
+ if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)wr, 0);
+
+ if (-1 != fd) {
+ out_process->stdin_file = _fdopen(fd, "wb");
+
+ if (0 == out_process->stdin_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdInput = rd;
+
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stdout_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stdout_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdOutput = wr;
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ startInfo.hStdError = startInfo.hStdOutput;
+ } else {
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stderr_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stderr_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdError = wr;
+ }
+
+ if (options & subprocess_option_enable_async) {
+ out_process->hEventOutput =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ out_process->hEventError =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ } else {
+ out_process->hEventOutput = 0;
+ out_process->hEventError = 0;
+ }
+
+ // Combine commandLine together into a single string
+ len = 0;
+ for (i = 0; commandLine[i]; i++) {
+ // For the ' ' between items and trailing '\0'
+ len++;
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ len++;
+ }
+ }
+
+ commandLineCombined = (char *)_alloca(len);
+
+ if (!commandLineCombined) {
+ return -1;
+ }
+
+ // Gonna re-use len to store the write index into commandLineCombined
+ len = 0;
+
+ for (i = 0; commandLine[i]; i++) {
+ if (0 != i) {
+ commandLineCombined[len++] = ' ';
+ }
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ commandLineCombined[len++] = commandLine[i][j];
+ }
+ }
+
+ commandLineCombined[len] = '\0';
+
+ if (!CreateProcessA(NULL,
+ commandLineCombined, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ 1, // handles are inherited
+ 0, // creation flags
+ environment, // use parent's environment
+ NULL, // use parent's current directory
+ (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
+ (LPPROCESS_INFORMATION)&processInfo)) {
+ return -1;
+ }
+
+ out_process->hProcess = processInfo.hProcess;
+
+ out_process->hStdInput = startInfo.hStdInput;
+
+ // We don't need the handle of the primary thread in the called process.
+ CloseHandle(processInfo.hThread);
+
+ if (0 != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdOutput);
+
+ if (startInfo.hStdError != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdError);
+ }
+ }
+
+ return 0;
+#else
+ int stdinfd[2];
+ int stdoutfd[2];
+ int stderrfd[2];
+ pid_t child;
+
+ if (0 != pipe(stdinfd)) {
+ return -1;
+ }
+
+ if (0 != pipe(stdoutfd)) {
+ return -1;
+ }
+
+ if (subprocess_option_combined_stdout_stderr !=
+ (options & subprocess_option_combined_stdout_stderr)) {
+ if (0 != pipe(stderrfd)) {
+ return -1;
+ }
+ }
+
+ child = fork();
+
+ if (-1 == child) {
+ return -1;
+ }
+
+ if (0 == child) {
+ // Close the stdin write end
+ close(stdinfd[1]);
+ // Map the read end to stdin
+ dup2(stdinfd[0], STDIN_FILENO);
+
+ // Close the stdout read end
+ close(stdoutfd[0]);
+ // Map the write end to stdout
+ dup2(stdoutfd[1], STDOUT_FILENO);
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ } else {
+ // Close the stderr read end
+ close(stderrfd[0]);
+ // Map the write end to stdout
+ dup2(stderrfd[1], STDERR_FILENO);
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#endif
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ char *const environment[1] = {0};
+ exit(execve(commandLine[0], (char *const *)commandLine, environment));
+ } else {
+ exit(execvp(commandLine[0], (char *const *)commandLine));
+ }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ } else {
+ // Close the stdin read end
+ close(stdinfd[0]);
+ // Store the stdin write end
+ out_process->stdin_file = fdopen(stdinfd[1], "wb");
+
+ // Close the stdout write end
+ close(stdoutfd[1]);
+ // Store the stdout read end
+ out_process->stdout_file = fdopen(stdoutfd[0], "rb");
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ } else {
+ // Close the stderr write end
+ close(stderrfd[1]);
+ // Store the stderr read end
+ out_process->stderr_file = fdopen(stderrfd[0], "rb");
+ }
+
+ // Store the child's pid
+ out_process->child = child;
+
+ return 0;
+ }
+#endif
+}
+
+FILE *subprocess_stdin(const struct subprocess_s *const process) {
+ return process->stdin_file;
+}
+
+FILE *subprocess_stdout(const struct subprocess_s *const process) {
+ return process->stdout_file;
+}
+
+FILE *subprocess_stderr(const struct subprocess_s *const process) {
+ if (process->stdout_file != process->stderr_file) {
+ return process->stderr_file;
+ } else {
+ return 0;
+ }
+}
+
+int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code) {
+#if defined(_MSC_VER)
+ const unsigned long infinite = 0xFFFFFFFF;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ process->hStdInput = NULL;
+ }
+
+ WaitForSingleObject(process->hProcess, infinite);
+
+ if (out_return_code) {
+ if (!GetExitCodeProcess(process->hProcess,
+ (unsigned long *)out_return_code)) {
+ return -1;
+ }
+ }
+
+ return 0;
+#else
+ int status;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (process->child != waitpid(process->child, &status, 0)) {
+ return -1;
+ }
+
+ if (out_return_code) {
+ if (WIFEXITED(status)) {
+ *out_return_code = WEXITSTATUS(status);
+ } else {
+ *out_return_code = EXIT_FAILURE;
+ }
+ }
+
+ return 0;
+#endif
+}
+
+int subprocess_destroy(struct subprocess_s *const process) {
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (0 != process->stdout_file) {
+ fclose(process->stdout_file);
+
+ if (process->stdout_file != process->stderr_file) {
+ fclose(process->stderr_file);
+ }
+
+ process->stdout_file = 0;
+ process->stderr_file = 0;
+ }
+
+#if defined(_MSC_VER)
+ if (process->hProcess) {
+ CloseHandle(process->hProcess);
+ process->hProcess = 0;
+
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ }
+
+ if (0 != process->hEventOutput) {
+ CloseHandle(process->hEventOutput);
+ }
+
+ if (0 != process->hEventError) {
+ CloseHandle(process->hEventError);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int subprocess_terminate(struct subprocess_s *const process) {
+#if defined(_MSC_VER)
+ unsigned int killed_process_exit_code;
+ int success_terminate;
+ int windows_call_result;
+
+ killed_process_exit_code = 99;
+ windows_call_result = TerminateProcess(process->hProcess, killed_process_exit_code);
+ success_terminate = (windows_call_result== 0) ? 1 : 0;
+ return success_terminate;
+#else
+ int result;
+ result = kill(process->child, 9);
+ return result;
+#endif
+}
+
+unsigned subprocess_read_stdout(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventOutput;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stdout_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stdout_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+unsigned subprocess_read_stderr(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventError;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stderr_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stderr_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */
diff --git a/test/test11.c b/test/test11.c
index eab366b..4de85c4 100644
--- a/test/test11.c
+++ b/test/test11.c
@@ -103,12 +103,12 @@
UTEST_F_TEARDOWN(MyTestF) { ASSERT_EQ(13, utest_fixture->foo); }
-UTEST_F(MyTestF, c) {
+UTEST_F(MyTestF, c11) {
ASSERT_EQ(42, utest_fixture->foo);
utest_fixture->foo = 13;
}
-UTEST_F(MyTestF, c2) {
+UTEST_F(MyTestF, c11_2) {
ASSERT_EQ(42, utest_fixture->foo);
utest_fixture->foo = 13;
}
@@ -130,12 +130,12 @@
ASSERT_EQ(utest_index, utest_fixture->bar);
}
-UTEST_I(MyTestI, c, 2) {
+UTEST_I(MyTestI, c11, 2) {
ASSERT_GT(2u, utest_fixture->bar);
utest_fixture->foo = 13;
}
-UTEST_I(MyTestI, c2, 128) {
+UTEST_I(MyTestI, c11_2, 128) {
ASSERT_GT(128u, utest_fixture->bar);
utest_fixture->foo = 13;
}
@@ -224,7 +224,7 @@
UTEST(c11, VoidPtr) {
void *foo = 0;
- EXPECT_NE(foo, (c11har *)foo + 1);
+ EXPECT_NE(foo, (char *)foo + 1);
}
static const int data[4] = {42, 13, 6, -53};
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/main.c b/test/main.c
index 0f4329c..22803e0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,10 +24,10 @@
// For more information, please refer to
#include "utest.h"
-#include "process.h"
+#include "subprocess.h"
UTEST(utest_cmdline, filter_with_list) {
- struct process_s process;
+ struct subprocess_s process;
const char *command[3] = {"utest_test", "--list-tests", 0};
int return_code;
FILE *stdout_file;
@@ -41,10 +41,10 @@
hits = (char *)malloc(utest_state.tests_length);
memset(hits, 0, utest_state.tests_length);
- ASSERT_EQ(0, process_create(command, process_option_combined_stdout_stderr,
+ ASSERT_EQ(0, subprocess_create(command, subprocess_option_combined_stdout_stderr,
&process));
- stdout_file = process_stdout(&process);
+ stdout_file = subprocess_stdout(&process);
for (index = 0; index < utest_state.tests_length; index++) {
if (buffer != fgets(buffer, MAX_CHARS, stdout_file)) {
@@ -82,10 +82,10 @@
#endif
}
- ASSERT_EQ(0, process_join(&process, &return_code));
+ ASSERT_EQ(0, subprocess_join(&process, &return_code));
ASSERT_EQ(0, return_code);
- ASSERT_EQ(0, process_destroy(&process));
+ ASSERT_EQ(0, subprocess_destroy(&process));
// Run through all the hits and make sure we got exactly one for each.
for (kndex = 0; kndex < utest_state.tests_length; kndex++) {
diff --git a/test/process.h b/test/process.h
deleted file mode 100644
index d3e1a97..0000000
--- a/test/process.h
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/process.h
-*/
-
-/*
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
-
- 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 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.
-
- For more information, please refer to
-*/
-
-#ifndef SHEREDOM_PROCESS_H_INCLUDED
-#define SHEREDOM_PROCESS_H_INCLUDED
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning(push, 1)
-#endif
-
-#include
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if !defined(_MSC_VER)
-#include
-#include
-#include
-#include
-#endif
-
-#if defined(_MSC_VER)
-#ifdef _WIN64
-typedef __int64 intptr_t;
-typedef unsigned __int64 size_t;
-#else
-typedef int intptr_t;
-typedef unsigned int size_t;
-#endif
-
-typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
-typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
-typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
-
-#pragma warning(push, 1)
-struct process_process_information_s {
- void *hProcess;
- void *hThread;
- unsigned long dwProcessId;
- unsigned long dwThreadId;
-};
-
-struct process_security_attributes_s {
- unsigned long nLength;
- void *lpSecurityDescriptor;
- int bInheritHandle;
-};
-
-struct process_startup_info_s {
- unsigned long cb;
- char *lpReserved;
- char *lpDesktop;
- char *lpTitle;
- unsigned long dwX;
- unsigned long dwY;
- unsigned long dwXSize;
- unsigned long dwYSize;
- unsigned long dwXCountChars;
- unsigned long dwYCountChars;
- unsigned long dwFillAttribute;
- unsigned long dwFlags;
- unsigned short wShowWindow;
- unsigned short cbReserved2;
- unsigned char *lpReserved2;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-};
-#pragma warning(pop)
-
-__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
- unsigned long);
-__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
- LPSECURITY_ATTRIBUTES,
- unsigned long);
-__declspec(dllimport) int __stdcall CreateProcessA(
- const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
- unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
-__declspec(dllimport) int __stdcall CloseHandle(void *);
-__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
- void *, unsigned long);
-__declspec(dllimport) int __stdcall GetExitCodeProcess(
- void *, unsigned long *lpExitCode);
-__declspec(dllimport) int __cdecl _open_osfhandle(intptr_t, int);
-void *__cdecl _alloca(size_t);
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#define process_pure __attribute__((pure))
-#define process_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define process_pure
-#define process_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-struct process_s {
- FILE *stdin_file;
- FILE *stdout_file;
- FILE *stderr_file;
-
-#if defined(_MSC_VER)
- void *hProcess;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-#else
- pid_t child;
-#endif
-};
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-enum process_option_e {
- // stdout and stderr are the same FILE.
- process_option_combined_stdout_stderr = 0x1,
-
- // The child process should inherit the environment variables of the parent.
- process_option_inherit_environment = 0x2
-};
-
-/// @brief Create a process.
-/// @param command_line An array of strings for the command line to execute for
-/// this process. The last element must be NULL to signify the end of the array.
-/// @param options A bit field of process_option_e's to pass.
-/// @param out_process The newly created process.
-/// @return On success 0 is returned.
-process_weak int process_create(const char *const command_line[], int options,
- struct process_s *const out_process);
-
-/// @brief Get the standard input file for a process.
-/// @param process The process to query.
-/// @return The file for standard input of the process.
-///
-/// The file returned can be written to by the parent process to feed data to
-/// the standard input of the process.
-process_pure process_weak FILE *
-process_stdin(const struct process_s *const process);
-
-/// @brief Get the standard output file for a process.
-/// @param process The process to query.
-/// @return The file for standard output of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard output of the child process.
-process_pure process_weak FILE *
-process_stdout(const struct process_s *const process);
-
-/// @brief Get the standard error file for a process.
-/// @param process The process to query.
-/// @return The file for standard error of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard error of the child process.
-///
-/// If the process was created with the process_option_combined_stdout_stderr
-/// option bit set, this function will return NULL, and the process_stdout
-/// function should be used for both the standard output and error combined.
-process_pure process_weak FILE *
-process_stderr(const struct process_s *const process);
-
-/// @brief Wait for a process to finish execution.
-/// @param process The process to wait for.
-/// @param out_return_code The return code of the returned process (can be
-/// NULL).
-/// @return On success 0 is returned.
-process_weak int process_join(struct process_s *const process,
- int *const out_return_code);
-
-/// @brief Destroy a previously created process.
-/// @param process The process to destroy.
-///
-/// If the process to be destroyed had not finished execution, it may out live
-/// the parent process.
-process_weak int process_destroy(struct process_s *const process);
-
-int process_create(const char *const commandLine[], int options,
- struct process_s *const out_process) {
-#if defined(_MSC_VER)
- int fd;
- void *rd, *wr;
- char *commandLineCombined;
- size_t len;
- int i, j;
- const unsigned long startFUseStdHandles = 0x00000100;
- const unsigned long handleFlagInherit = 0x00000001;
- struct process_process_information_s processInfo;
- struct process_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
- char *environment = 0;
- struct process_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
- startInfo.cb = sizeof(startInfo);
- startInfo.dwFlags = startFUseStdHandles;
-
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- environment = "\0\0";
- }
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)wr, 0);
-
- if (-1 != fd) {
- out_process->stdin_file = _fdopen(fd, "wb");
-
- if (0 == out_process->stdin_file) {
- return -1;
- }
- }
-
- startInfo.hStdInput = rd;
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stdout_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stdout_file) {
- return -1;
- }
- }
-
- startInfo.hStdOutput = wr;
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- startInfo.hStdError = startInfo.hStdOutput;
- } else {
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stderr_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stderr_file) {
- return -1;
- }
- }
-
- startInfo.hStdError = wr;
- }
-
- // Combine commandLine together into a single string
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- // For the ' ' between items and trailing '\0'
- len++;
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- len++;
- }
- }
-
- commandLineCombined = (char *)_alloca(len);
-
- if (!commandLineCombined) {
- return -1;
- }
-
- // Gonna re-use len to store the write index into commandLineCombined
- len = 0;
-
- for (i = 0; commandLine[i]; i++) {
- if (0 != i) {
- commandLineCombined[len++] = ' ';
- }
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- commandLineCombined[len++] = commandLine[i][j];
- }
- }
-
- commandLineCombined[len] = '\0';
-
- if (!CreateProcessA(NULL,
- commandLineCombined, // command line
- NULL, // process security attributes
- NULL, // primary thread security attributes
- 1, // handles are inherited
- 0, // creation flags
- environment, // use parent's environment
- NULL, // use parent's current directory
- (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
- (LPPROCESS_INFORMATION)&processInfo)) {
- return -1;
- }
-
- out_process->hProcess = processInfo.hProcess;
-
- out_process->hStdInput = startInfo.hStdInput;
- out_process->hStdOutput = startInfo.hStdOutput;
- out_process->hStdError = startInfo.hStdError;
-
- // We don't need the handle of the primary thread in the called process.
- CloseHandle(processInfo.hThread);
-
- return 0;
-#else
- int stdinfd[2];
- int stdoutfd[2];
- int stderrfd[2];
- pid_t child;
-
- if (0 != pipe(stdinfd)) {
- return -1;
- }
-
- if (0 != pipe(stdoutfd)) {
- return -1;
- }
-
- if (process_option_combined_stdout_stderr !=
- (options & process_option_combined_stdout_stderr)) {
- if (0 != pipe(stderrfd)) {
- return -1;
- }
- }
-
- child = fork();
-
- if (-1 == child) {
- return -1;
- }
-
- if (0 == child) {
- // Close the stdin write end
- close(stdinfd[1]);
- // Map the read end to stdin
- dup2(stdinfd[0], STDIN_FILENO);
-
- // Close the stdout read end
- close(stdoutfd[0]);
- // Map the write end to stdout
- dup2(stdoutfd[1], STDOUT_FILENO);
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- dup2(STDOUT_FILENO, STDERR_FILENO);
- } else {
- // Close the stderr read end
- close(stderrfd[0]);
- // Map the write end to stdout
- dup2(stderrfd[1], STDERR_FILENO);
- }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#endif
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- char *const environment[1] = {0};
- exit(execve(commandLine[0], (char *const *)commandLine, environment));
- } else {
- exit(execvp(commandLine[0], (char *const *)commandLine));
- }
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- } else {
- // Close the stdin read end
- close(stdinfd[0]);
- // Store the stdin write end
- out_process->stdin_file = fdopen(stdinfd[1], "wb");
-
- // Close the stdout write end
- close(stdoutfd[1]);
- // Store the stdout read end
- out_process->stdout_file = fdopen(stdoutfd[0], "rb");
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- } else {
- // Close the stderr write end
- close(stderrfd[1]);
- // Store the stderr read end
- out_process->stderr_file = fdopen(stderrfd[0], "rb");
- }
-
- // Store the child's pid
- out_process->child = child;
-
- return 0;
- }
-#endif
-}
-
-FILE *process_stdin(const struct process_s *const process) {
- return process->stdin_file;
-}
-
-FILE *process_stdout(const struct process_s *const process) {
- return process->stdout_file;
-}
-
-FILE *process_stderr(const struct process_s *const process) {
- if (process->stdout_file != process->stderr_file) {
- return process->stderr_file;
- } else {
- return 0;
- }
-}
-
-int process_join(struct process_s *const process, int *const out_return_code) {
-#if defined(_MSC_VER)
- const unsigned long infinite = 0xFFFFFFFF;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
-
- WaitForSingleObject(process->hProcess, infinite);
-
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-
- if (out_return_code) {
- if (!GetExitCodeProcess(process->hProcess,
- (unsigned long *)out_return_code)) {
- return -1;
- }
- }
-
- return 0;
-#else
- int status;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
-
- if (process->child != waitpid(process->child, &status, 0)) {
- return -1;
- }
-
- if (out_return_code) {
- if (WIFEXITED(status)) {
- *out_return_code = WEXITSTATUS(status);
- } else {
- *out_return_code = 0;
- }
- }
-
- return 0;
-#endif
-}
-
-int process_destroy(struct process_s *const process) {
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- }
-
- fclose(process->stdout_file);
-
- if (process->stdout_file != process->stderr_file) {
- fclose(process->stderr_file);
- }
-
-#if defined(_MSC_VER)
- CloseHandle(process->hProcess);
-
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-#endif
-
- return 0;
-}
-
-#if defined(__cplusplus)
-} // extern "C"
-#endif
-
-#endif /* SHEREDOM_PROCESS_H_INCLUDED */
diff --git a/test/subprocess.h b/test/subprocess.h
new file mode 100644
index 0000000..8f63657
--- /dev/null
+++ b/test/subprocess.h
@@ -0,0 +1,843 @@
+/*
+ The latest version of this library is available on GitHub;
+ https://github.com/sheredom/subprocess.h
+*/
+
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ 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 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.
+
+ For more information, please refer to
+*/
+
+#ifndef SHEREDOM_SUBPROCESS_H_INCLUDED
+#define SHEREDOM_SUBPROCESS_H_INCLUDED
+
+#if defined(_MSC_VER)
+#pragma warning(push, 1)
+#endif
+
+#include
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+#define subprocess_pure __attribute__((pure))
+#define subprocess_weak __attribute__((weak))
+#elif defined(_MSC_VER)
+#define subprocess_pure
+#define subprocess_weak __inline
+#else
+#error Non clang, non gcc, non MSVC compiler found!
+#endif
+
+struct subprocess_s;
+
+enum subprocess_option_e {
+ // stdout and stderr are the same FILE.
+ subprocess_option_combined_stdout_stderr = 0x1,
+
+ // The child process should inherit the environment variables of the parent.
+ subprocess_option_inherit_environment = 0x2,
+
+ // Enable asynchronous reading of stdout/stderr before it has completed.
+ subprocess_option_enable_async = 0x4
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/// @brief Create a process.
+/// @param command_line An array of strings for the command line to execute for
+/// this process. The last element must be NULL to signify the end of the array.
+/// @param options A bit field of subprocess_option_e's to pass.
+/// @param out_process The newly created process.
+/// @return On success 0 is returned.
+subprocess_weak int subprocess_create(const char *const command_line[],
+ int options,
+ struct subprocess_s *const out_process);
+
+/// @brief Get the standard input file for a process.
+/// @param process The process to query.
+/// @return The file for standard input of the process.
+///
+/// The file returned can be written to by the parent process to feed data to
+/// the standard input of the process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdin(const struct subprocess_s *const process);
+
+/// @brief Get the standard output file for a process.
+/// @param process The process to query.
+/// @return The file for standard output of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard output of the child process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdout(const struct subprocess_s *const process);
+
+/// @brief Get the standard error file for a process.
+/// @param process The process to query.
+/// @return The file for standard error of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard error of the child process.
+///
+/// If the process was created with the subprocess_option_combined_stdout_stderr
+/// option bit set, this function will return NULL, and the subprocess_stdout
+/// function should be used for both the standard output and error combined.
+subprocess_pure subprocess_weak FILE *
+subprocess_stderr(const struct subprocess_s *const process);
+
+/// @brief Wait for a process to finish execution.
+/// @param process The process to wait for.
+/// @param out_return_code The return code of the returned process (can be
+/// NULL).
+/// @return On success 0 is returned.
+///
+/// Joining a process will close the stdin pipe to the process.
+subprocess_weak int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code);
+
+/// @brief Destroy a previously created process.
+/// @param process The process to destroy.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it may out live
+/// the parent process.
+subprocess_weak int subprocess_destroy(struct subprocess_s *const process);
+
+/// @brief Terminate a previously created process.
+/// @param process The process to terminate.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it will be
+/// terminated (i.e killed).
+subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
+
+/// @brief Read the standard output from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard output of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+/// @brief Read the standard error from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard error of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stderr(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+#if defined(__cplusplus)
+#define SUBPROCESS_CAST(type, x) static_cast(x)
+#else
+#define SUBPROCESS_CAST(type, x) ((type)x)
+#endif
+
+#if !defined(_MSC_VER)
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#if defined(_MSC_VER)
+#ifdef _WIN64
+typedef __int64 subprocess_intptr_t;
+typedef unsigned __int64 subprocess_size_t;
+#else
+typedef int subprocess_intptr_t;
+typedef unsigned int subprocess_size_t;
+#endif
+
+typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
+typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
+typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
+typedef struct _OVERLAPPED *LPOVERLAPPED;
+
+#pragma warning(push, 1)
+struct subprocess_subprocess_information_s {
+ void *hProcess;
+ void *hThread;
+ unsigned long dwProcessId;
+ unsigned long dwThreadId;
+};
+
+struct subprocess_security_attributes_s {
+ unsigned long nLength;
+ void *lpSecurityDescriptor;
+ int bInheritHandle;
+};
+
+struct subprocess_startup_info_s {
+ unsigned long cb;
+ char *lpReserved;
+ char *lpDesktop;
+ char *lpTitle;
+ unsigned long dwX;
+ unsigned long dwY;
+ unsigned long dwXSize;
+ unsigned long dwYSize;
+ unsigned long dwXCountChars;
+ unsigned long dwYCountChars;
+ unsigned long dwFillAttribute;
+ unsigned long dwFlags;
+ unsigned short wShowWindow;
+ unsigned short cbReserved2;
+ unsigned char *lpReserved2;
+ void *hStdInput;
+ void *hStdOutput;
+ void *hStdError;
+};
+
+struct subprocess_overlapped_s {
+ uintptr_t Internal;
+ uintptr_t InternalHigh;
+ union {
+ struct {
+ unsigned long Offset;
+ unsigned long OffsetHigh;
+ } DUMMYSTRUCTNAME;
+ void *Pointer;
+ } DUMMYUNIONNAME;
+
+ void *hEvent;
+};
+
+#pragma warning(pop)
+
+__declspec(dllimport) unsigned long __stdcall GetLastError(void);
+__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
+ unsigned long);
+__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long);
+__declspec(dllimport) void *__stdcall CreateNamedPipeA(
+ const char *, unsigned long, unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long, LPSECURITY_ATTRIBUTES);
+__declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long,
+ unsigned long *, LPOVERLAPPED);
+__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void);
+__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
+__declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
+ unsigned long,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long, unsigned long,
+ void *);
+__declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
+ int, const char *);
+__declspec(dllimport) int __stdcall CreateProcessA(
+ const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
+ unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
+__declspec(dllimport) int __stdcall CloseHandle(void *);
+__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
+ void *, unsigned long);
+__declspec(dllimport) int __stdcall GetExitCodeProcess(
+ void *, unsigned long *lpExitCode);
+__declspec(dllimport) int __stdcall TerminateProcess(
+ void *, unsigned int);
+__declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
+ unsigned long, void *const *, int, unsigned long);
+__declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
+ unsigned long *, int);
+
+#if defined(_DLL) && (_DLL == 1)
+#define SUBPROCESS_DLLIMPORT __declspec(dllimport)
+#else
+#define SUBPROCESS_DLLIMPORT
+#endif
+
+SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
+SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
+SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
+
+void *__cdecl _alloca(subprocess_size_t);
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+struct subprocess_s {
+ FILE *stdin_file;
+ FILE *stdout_file;
+ FILE *stderr_file;
+
+#if defined(_MSC_VER)
+ void *hProcess;
+ void *hStdInput;
+ void *hEventOutput;
+ void *hEventError;
+#else
+ pid_t child;
+#endif
+};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#if defined(_MSC_VER)
+subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
+int subprocess_create_named_pipe_helper(void **rd, void **wr) {
+ const unsigned long pipeAccessInbound = 0x00000001;
+ const unsigned long fileFlagOverlapped = 0x40000000;
+ const unsigned long pipeTypeByte = 0x00000000;
+ const unsigned long pipeWait = 0x00000000;
+ const unsigned long genericWrite = 0x40000000;
+ const unsigned long openExisting = 3;
+ const unsigned long fileAttributeNormal = 0x00000080;
+ const void *const invalidHandleValue = (void *)~((subprocess_intptr_t)0);
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char name[256] = {0};
+ __declspec(thread) static long index = 0;
+ const long unique = index++;
+
+#if _MSC_VER < 1900
+#pragma warning(disable : 4996)
+#pragma warning(push, 1)
+
+ _snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+
+#pragma warning(pop)
+#else
+#pragma warning(disable : 4710)
+#pragma warning(push, 1)
+ snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+#pragma warning(pop)
+#endif
+
+ *rd = CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
+ pipeTypeByte | pipeWait, 1, 4096, 4096, 0,
+ (LPSECURITY_ATTRIBUTES)&saAttr);
+
+ if (invalidHandleValue == rd) {
+ return -1;
+ }
+
+ *wr = CreateFileA(name, genericWrite, 0, (LPSECURITY_ATTRIBUTES)&saAttr,
+ openExisting, fileAttributeNormal, 0);
+
+ if (invalidHandleValue == wr) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+int subprocess_create(const char *const commandLine[], int options,
+ struct subprocess_s *const out_process) {
+#if defined(_MSC_VER)
+ int fd;
+ void *rd, *wr;
+ char *commandLineCombined;
+ subprocess_size_t len;
+ int i, j;
+ const unsigned long startFUseStdHandles = 0x00000100;
+ const unsigned long handleFlagInherit = 0x00000001;
+ struct subprocess_subprocess_information_s processInfo;
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char *environment = 0;
+ struct subprocess_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ startInfo.cb = sizeof(startInfo);
+ startInfo.dwFlags = startFUseStdHandles;
+
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ environment = "\0\0";
+ }
+
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+
+ if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)wr, 0);
+
+ if (-1 != fd) {
+ out_process->stdin_file = _fdopen(fd, "wb");
+
+ if (0 == out_process->stdin_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdInput = rd;
+
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stdout_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stdout_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdOutput = wr;
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ startInfo.hStdError = startInfo.hStdOutput;
+ } else {
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stderr_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stderr_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdError = wr;
+ }
+
+ if (options & subprocess_option_enable_async) {
+ out_process->hEventOutput =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ out_process->hEventError =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ } else {
+ out_process->hEventOutput = 0;
+ out_process->hEventError = 0;
+ }
+
+ // Combine commandLine together into a single string
+ len = 0;
+ for (i = 0; commandLine[i]; i++) {
+ // For the ' ' between items and trailing '\0'
+ len++;
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ len++;
+ }
+ }
+
+ commandLineCombined = (char *)_alloca(len);
+
+ if (!commandLineCombined) {
+ return -1;
+ }
+
+ // Gonna re-use len to store the write index into commandLineCombined
+ len = 0;
+
+ for (i = 0; commandLine[i]; i++) {
+ if (0 != i) {
+ commandLineCombined[len++] = ' ';
+ }
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ commandLineCombined[len++] = commandLine[i][j];
+ }
+ }
+
+ commandLineCombined[len] = '\0';
+
+ if (!CreateProcessA(NULL,
+ commandLineCombined, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ 1, // handles are inherited
+ 0, // creation flags
+ environment, // use parent's environment
+ NULL, // use parent's current directory
+ (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
+ (LPPROCESS_INFORMATION)&processInfo)) {
+ return -1;
+ }
+
+ out_process->hProcess = processInfo.hProcess;
+
+ out_process->hStdInput = startInfo.hStdInput;
+
+ // We don't need the handle of the primary thread in the called process.
+ CloseHandle(processInfo.hThread);
+
+ if (0 != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdOutput);
+
+ if (startInfo.hStdError != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdError);
+ }
+ }
+
+ return 0;
+#else
+ int stdinfd[2];
+ int stdoutfd[2];
+ int stderrfd[2];
+ pid_t child;
+
+ if (0 != pipe(stdinfd)) {
+ return -1;
+ }
+
+ if (0 != pipe(stdoutfd)) {
+ return -1;
+ }
+
+ if (subprocess_option_combined_stdout_stderr !=
+ (options & subprocess_option_combined_stdout_stderr)) {
+ if (0 != pipe(stderrfd)) {
+ return -1;
+ }
+ }
+
+ child = fork();
+
+ if (-1 == child) {
+ return -1;
+ }
+
+ if (0 == child) {
+ // Close the stdin write end
+ close(stdinfd[1]);
+ // Map the read end to stdin
+ dup2(stdinfd[0], STDIN_FILENO);
+
+ // Close the stdout read end
+ close(stdoutfd[0]);
+ // Map the write end to stdout
+ dup2(stdoutfd[1], STDOUT_FILENO);
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ } else {
+ // Close the stderr read end
+ close(stderrfd[0]);
+ // Map the write end to stdout
+ dup2(stderrfd[1], STDERR_FILENO);
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#endif
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ char *const environment[1] = {0};
+ exit(execve(commandLine[0], (char *const *)commandLine, environment));
+ } else {
+ exit(execvp(commandLine[0], (char *const *)commandLine));
+ }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ } else {
+ // Close the stdin read end
+ close(stdinfd[0]);
+ // Store the stdin write end
+ out_process->stdin_file = fdopen(stdinfd[1], "wb");
+
+ // Close the stdout write end
+ close(stdoutfd[1]);
+ // Store the stdout read end
+ out_process->stdout_file = fdopen(stdoutfd[0], "rb");
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ } else {
+ // Close the stderr write end
+ close(stderrfd[1]);
+ // Store the stderr read end
+ out_process->stderr_file = fdopen(stderrfd[0], "rb");
+ }
+
+ // Store the child's pid
+ out_process->child = child;
+
+ return 0;
+ }
+#endif
+}
+
+FILE *subprocess_stdin(const struct subprocess_s *const process) {
+ return process->stdin_file;
+}
+
+FILE *subprocess_stdout(const struct subprocess_s *const process) {
+ return process->stdout_file;
+}
+
+FILE *subprocess_stderr(const struct subprocess_s *const process) {
+ if (process->stdout_file != process->stderr_file) {
+ return process->stderr_file;
+ } else {
+ return 0;
+ }
+}
+
+int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code) {
+#if defined(_MSC_VER)
+ const unsigned long infinite = 0xFFFFFFFF;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ process->hStdInput = NULL;
+ }
+
+ WaitForSingleObject(process->hProcess, infinite);
+
+ if (out_return_code) {
+ if (!GetExitCodeProcess(process->hProcess,
+ (unsigned long *)out_return_code)) {
+ return -1;
+ }
+ }
+
+ return 0;
+#else
+ int status;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (process->child != waitpid(process->child, &status, 0)) {
+ return -1;
+ }
+
+ if (out_return_code) {
+ if (WIFEXITED(status)) {
+ *out_return_code = WEXITSTATUS(status);
+ } else {
+ *out_return_code = EXIT_FAILURE;
+ }
+ }
+
+ return 0;
+#endif
+}
+
+int subprocess_destroy(struct subprocess_s *const process) {
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (0 != process->stdout_file) {
+ fclose(process->stdout_file);
+
+ if (process->stdout_file != process->stderr_file) {
+ fclose(process->stderr_file);
+ }
+
+ process->stdout_file = 0;
+ process->stderr_file = 0;
+ }
+
+#if defined(_MSC_VER)
+ if (process->hProcess) {
+ CloseHandle(process->hProcess);
+ process->hProcess = 0;
+
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ }
+
+ if (0 != process->hEventOutput) {
+ CloseHandle(process->hEventOutput);
+ }
+
+ if (0 != process->hEventError) {
+ CloseHandle(process->hEventError);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int subprocess_terminate(struct subprocess_s *const process) {
+#if defined(_MSC_VER)
+ unsigned int killed_process_exit_code;
+ int success_terminate;
+ int windows_call_result;
+
+ killed_process_exit_code = 99;
+ windows_call_result = TerminateProcess(process->hProcess, killed_process_exit_code);
+ success_terminate = (windows_call_result== 0) ? 1 : 0;
+ return success_terminate;
+#else
+ int result;
+ result = kill(process->child, 9);
+ return result;
+#endif
+}
+
+unsigned subprocess_read_stdout(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventOutput;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stdout_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stdout_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+unsigned subprocess_read_stderr(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventError;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stderr_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stderr_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */
diff --git a/test/test11.c b/test/test11.c
index eab366b..4de85c4 100644
--- a/test/test11.c
+++ b/test/test11.c
@@ -103,12 +103,12 @@
UTEST_F_TEARDOWN(MyTestF) { ASSERT_EQ(13, utest_fixture->foo); }
-UTEST_F(MyTestF, c) {
+UTEST_F(MyTestF, c11) {
ASSERT_EQ(42, utest_fixture->foo);
utest_fixture->foo = 13;
}
-UTEST_F(MyTestF, c2) {
+UTEST_F(MyTestF, c11_2) {
ASSERT_EQ(42, utest_fixture->foo);
utest_fixture->foo = 13;
}
@@ -130,12 +130,12 @@
ASSERT_EQ(utest_index, utest_fixture->bar);
}
-UTEST_I(MyTestI, c, 2) {
+UTEST_I(MyTestI, c11, 2) {
ASSERT_GT(2u, utest_fixture->bar);
utest_fixture->foo = 13;
}
-UTEST_I(MyTestI, c2, 128) {
+UTEST_I(MyTestI, c11_2, 128) {
ASSERT_GT(128u, utest_fixture->bar);
utest_fixture->foo = 13;
}
@@ -224,7 +224,7 @@
UTEST(c11, VoidPtr) {
void *foo = 0;
- EXPECT_NE(foo, (c11har *)foo + 1);
+ EXPECT_NE(foo, (char *)foo + 1);
}
static const int data[4] = {42, 13, 6, -53};
diff --git a/test/test99.c b/test/test99.c
new file mode 100644
index 0000000..2531f73
--- /dev/null
+++ b/test/test99.c
@@ -0,0 +1,232 @@
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ 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 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.
+
+ For more information, please refer to
+*/
+
+#include "utest.h"
+
+#ifdef _MSC_VER
+/* disable 'conditional expression is constant' - our examples below use this!
+ */
+#pragma warning(disable : 4127)
+#pragma
+#endif
+
+UTEST(c99, ASSERT_TRUE) { ASSERT_TRUE(1); }
+
+UTEST(c99, ASSERT_FALSE) { ASSERT_FALSE(0); }
+
+UTEST(c99, ASSERT_EQ) { ASSERT_EQ(1, 1); }
+
+UTEST(c99, ASSERT_NE) { ASSERT_NE(1, 2); }
+
+UTEST(c99, ASSERT_LT) { ASSERT_LT(1, 2); }
+
+UTEST(c99, ASSERT_LE) {
+ ASSERT_LE(1, 1);
+ ASSERT_LE(1, 2);
+}
+
+UTEST(c99, ASSERT_GT) { ASSERT_GT(2, 1); }
+
+UTEST(c99, ASSERT_GE) {
+ ASSERT_GE(1, 1);
+ ASSERT_GE(2, 1);
+}
+
+UTEST(c99, ASSERT_STREQ) { ASSERT_STREQ("foo", "foo"); }
+
+UTEST(c99, ASSERT_STRNE) { ASSERT_STRNE("foo", "bar"); }
+
+UTEST(c99, EXPECT_TRUE) { EXPECT_TRUE(1); }
+
+UTEST(c99, EXPECT_FALSE) { EXPECT_FALSE(0); }
+
+UTEST(c99, EXPECT_EQ) { EXPECT_EQ(1, 1); }
+
+UTEST(c99, EXPECT_NE) { EXPECT_NE(1, 2); }
+
+UTEST(c99, EXPECT_LT) { EXPECT_LT(1, 2); }
+
+UTEST(c99, EXPECT_LE) {
+ EXPECT_LE(1, 1);
+ EXPECT_LE(1, 2);
+}
+
+UTEST(c99, EXPECT_GT) { EXPECT_GT(2, 1); }
+
+UTEST(c99, EXPECT_GE) {
+ EXPECT_GE(1, 1);
+ EXPECT_GE(2, 1);
+}
+
+UTEST(c99, EXPECT_STREQ) { EXPECT_STREQ("foo", "foo"); }
+
+UTEST(c99, EXPECT_STRNE) { EXPECT_STRNE("foo", "bar"); }
+
+UTEST(c99, no_double_eval) {
+ int i = 0;
+ ASSERT_EQ(i++, 0);
+ ASSERT_EQ(i, 1);
+}
+
+struct MyTestF {
+ int foo;
+};
+
+UTEST_F_SETUP(MyTestF) {
+ ASSERT_EQ(0, utest_fixture->foo);
+ utest_fixture->foo = 42;
+}
+
+UTEST_F_TEARDOWN(MyTestF) { ASSERT_EQ(13, utest_fixture->foo); }
+
+UTEST_F(MyTestF, c99) {
+ ASSERT_EQ(42, utest_fixture->foo);
+ utest_fixture->foo = 13;
+}
+
+UTEST_F(MyTestF, c99_2) {
+ ASSERT_EQ(42, utest_fixture->foo);
+ utest_fixture->foo = 13;
+}
+
+struct MyTestI {
+ size_t foo;
+ size_t bar;
+};
+
+UTEST_I_SETUP(MyTestI) {
+ ASSERT_EQ(0u, utest_fixture->foo);
+ ASSERT_EQ(0u, utest_fixture->bar);
+ utest_fixture->foo = 42;
+ utest_fixture->bar = utest_index;
+}
+
+UTEST_I_TEARDOWN(MyTestI) {
+ ASSERT_EQ(13u, utest_fixture->foo);
+ ASSERT_EQ(utest_index, utest_fixture->bar);
+}
+
+UTEST_I(MyTestI, c99, 2) {
+ ASSERT_GT(2u, utest_fixture->bar);
+ utest_fixture->foo = 13;
+}
+
+UTEST_I(MyTestI, c99_2, 128) {
+ ASSERT_GT(128u, utest_fixture->bar);
+ utest_fixture->foo = 13;
+}
+
+UTEST(c99, Float) {
+ float a = 1;
+ float b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Double) {
+ double a = 1;
+ double b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, LongDouble) {
+ long double a = 1;
+ long double b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Char) {
+ signed char a = 1;
+ signed char b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, UChar) {
+ unsigned char a = 1;
+ unsigned char b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Short) {
+ short a = 1;
+ short b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, UShort) {
+ unsigned short a = 1;
+ unsigned short b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Int) {
+ int a = 1;
+ int b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, UInt) {
+ unsigned int a = 1;
+ unsigned int b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Long) {
+ long a = 1;
+ long b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, ULong) {
+ unsigned long a = 1;
+ unsigned long b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Ptr) {
+ char foo = 42;
+ EXPECT_NE(&foo, &foo + 1);
+}
+
+UTEST(c99, VoidPtr) {
+ void *foo = 0;
+ EXPECT_NE(foo, (char *)foo + 1);
+}
+
+static const int data[4] = {42, 13, 6, -53};
+
+UTEST(c99, Array) { EXPECT_NE(data, data + 1); }
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 50c0175..c27da2a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -44,6 +44,22 @@
message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
endif()
+if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "-Wall -Wextra -Weverything -Werror -std=c99"
+ )
+elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ set_source_files_properties(test99.c PROPERTIES
+ COMPILE_FLAGS "/Wall /WX /wd4514 /wd5045"
+ )
+else()
+ message(WARNING "Unknown compiler '${CMAKE_C_COMPILER_ID}'!")
+endif()
+
list(FIND CMAKE_C_COMPILE_FEATURES c_std_11 IDX)
if (${IDX} GREATER -1)
if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
@@ -102,6 +118,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
@@ -113,6 +131,8 @@
../utest.h
main.c
test.c
+ test11.c
+ test99.c
test.cpp
test11.cpp
stdint_include.c
diff --git a/test/main.c b/test/main.c
index 0f4329c..22803e0 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,10 +24,10 @@
// For more information, please refer to
#include "utest.h"
-#include "process.h"
+#include "subprocess.h"
UTEST(utest_cmdline, filter_with_list) {
- struct process_s process;
+ struct subprocess_s process;
const char *command[3] = {"utest_test", "--list-tests", 0};
int return_code;
FILE *stdout_file;
@@ -41,10 +41,10 @@
hits = (char *)malloc(utest_state.tests_length);
memset(hits, 0, utest_state.tests_length);
- ASSERT_EQ(0, process_create(command, process_option_combined_stdout_stderr,
+ ASSERT_EQ(0, subprocess_create(command, subprocess_option_combined_stdout_stderr,
&process));
- stdout_file = process_stdout(&process);
+ stdout_file = subprocess_stdout(&process);
for (index = 0; index < utest_state.tests_length; index++) {
if (buffer != fgets(buffer, MAX_CHARS, stdout_file)) {
@@ -82,10 +82,10 @@
#endif
}
- ASSERT_EQ(0, process_join(&process, &return_code));
+ ASSERT_EQ(0, subprocess_join(&process, &return_code));
ASSERT_EQ(0, return_code);
- ASSERT_EQ(0, process_destroy(&process));
+ ASSERT_EQ(0, subprocess_destroy(&process));
// Run through all the hits and make sure we got exactly one for each.
for (kndex = 0; kndex < utest_state.tests_length; kndex++) {
diff --git a/test/process.h b/test/process.h
deleted file mode 100644
index d3e1a97..0000000
--- a/test/process.h
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/process.h
-*/
-
-/*
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
-
- 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 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.
-
- For more information, please refer to
-*/
-
-#ifndef SHEREDOM_PROCESS_H_INCLUDED
-#define SHEREDOM_PROCESS_H_INCLUDED
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning(push, 1)
-#endif
-
-#include
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#if !defined(_MSC_VER)
-#include
-#include
-#include
-#include
-#endif
-
-#if defined(_MSC_VER)
-#ifdef _WIN64
-typedef __int64 intptr_t;
-typedef unsigned __int64 size_t;
-#else
-typedef int intptr_t;
-typedef unsigned int size_t;
-#endif
-
-typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
-typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
-typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
-
-#pragma warning(push, 1)
-struct process_process_information_s {
- void *hProcess;
- void *hThread;
- unsigned long dwProcessId;
- unsigned long dwThreadId;
-};
-
-struct process_security_attributes_s {
- unsigned long nLength;
- void *lpSecurityDescriptor;
- int bInheritHandle;
-};
-
-struct process_startup_info_s {
- unsigned long cb;
- char *lpReserved;
- char *lpDesktop;
- char *lpTitle;
- unsigned long dwX;
- unsigned long dwY;
- unsigned long dwXSize;
- unsigned long dwYSize;
- unsigned long dwXCountChars;
- unsigned long dwYCountChars;
- unsigned long dwFillAttribute;
- unsigned long dwFlags;
- unsigned short wShowWindow;
- unsigned short cbReserved2;
- unsigned char *lpReserved2;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-};
-#pragma warning(pop)
-
-__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
- unsigned long);
-__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
- LPSECURITY_ATTRIBUTES,
- unsigned long);
-__declspec(dllimport) int __stdcall CreateProcessA(
- const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
- unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
-__declspec(dllimport) int __stdcall CloseHandle(void *);
-__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
- void *, unsigned long);
-__declspec(dllimport) int __stdcall GetExitCodeProcess(
- void *, unsigned long *lpExitCode);
-__declspec(dllimport) int __cdecl _open_osfhandle(intptr_t, int);
-void *__cdecl _alloca(size_t);
-#endif
-
-#if defined(__clang__) || defined(__GNUC__)
-#define process_pure __attribute__((pure))
-#define process_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define process_pure
-#define process_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-struct process_s {
- FILE *stdin_file;
- FILE *stdout_file;
- FILE *stderr_file;
-
-#if defined(_MSC_VER)
- void *hProcess;
- void *hStdInput;
- void *hStdOutput;
- void *hStdError;
-#else
- pid_t child;
-#endif
-};
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-enum process_option_e {
- // stdout and stderr are the same FILE.
- process_option_combined_stdout_stderr = 0x1,
-
- // The child process should inherit the environment variables of the parent.
- process_option_inherit_environment = 0x2
-};
-
-/// @brief Create a process.
-/// @param command_line An array of strings for the command line to execute for
-/// this process. The last element must be NULL to signify the end of the array.
-/// @param options A bit field of process_option_e's to pass.
-/// @param out_process The newly created process.
-/// @return On success 0 is returned.
-process_weak int process_create(const char *const command_line[], int options,
- struct process_s *const out_process);
-
-/// @brief Get the standard input file for a process.
-/// @param process The process to query.
-/// @return The file for standard input of the process.
-///
-/// The file returned can be written to by the parent process to feed data to
-/// the standard input of the process.
-process_pure process_weak FILE *
-process_stdin(const struct process_s *const process);
-
-/// @brief Get the standard output file for a process.
-/// @param process The process to query.
-/// @return The file for standard output of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard output of the child process.
-process_pure process_weak FILE *
-process_stdout(const struct process_s *const process);
-
-/// @brief Get the standard error file for a process.
-/// @param process The process to query.
-/// @return The file for standard error of the process.
-///
-/// The file returned can be read from by the parent process to read data from
-/// the standard error of the child process.
-///
-/// If the process was created with the process_option_combined_stdout_stderr
-/// option bit set, this function will return NULL, and the process_stdout
-/// function should be used for both the standard output and error combined.
-process_pure process_weak FILE *
-process_stderr(const struct process_s *const process);
-
-/// @brief Wait for a process to finish execution.
-/// @param process The process to wait for.
-/// @param out_return_code The return code of the returned process (can be
-/// NULL).
-/// @return On success 0 is returned.
-process_weak int process_join(struct process_s *const process,
- int *const out_return_code);
-
-/// @brief Destroy a previously created process.
-/// @param process The process to destroy.
-///
-/// If the process to be destroyed had not finished execution, it may out live
-/// the parent process.
-process_weak int process_destroy(struct process_s *const process);
-
-int process_create(const char *const commandLine[], int options,
- struct process_s *const out_process) {
-#if defined(_MSC_VER)
- int fd;
- void *rd, *wr;
- char *commandLineCombined;
- size_t len;
- int i, j;
- const unsigned long startFUseStdHandles = 0x00000100;
- const unsigned long handleFlagInherit = 0x00000001;
- struct process_process_information_s processInfo;
- struct process_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
- char *environment = 0;
- struct process_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
- startInfo.cb = sizeof(startInfo);
- startInfo.dwFlags = startFUseStdHandles;
-
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- environment = "\0\0";
- }
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)wr, 0);
-
- if (-1 != fd) {
- out_process->stdin_file = _fdopen(fd, "wb");
-
- if (0 == out_process->stdin_file) {
- return -1;
- }
- }
-
- startInfo.hStdInput = rd;
-
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stdout_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stdout_file) {
- return -1;
- }
- }
-
- startInfo.hStdOutput = wr;
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- startInfo.hStdError = startInfo.hStdOutput;
- } else {
- if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
- return -1;
- }
-
- if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
- return -1;
- }
-
- fd = _open_osfhandle((intptr_t)rd, 0);
-
- if (-1 != fd) {
- out_process->stderr_file = _fdopen(fd, "rb");
-
- if (0 == out_process->stderr_file) {
- return -1;
- }
- }
-
- startInfo.hStdError = wr;
- }
-
- // Combine commandLine together into a single string
- len = 0;
- for (i = 0; commandLine[i]; i++) {
- // For the ' ' between items and trailing '\0'
- len++;
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- len++;
- }
- }
-
- commandLineCombined = (char *)_alloca(len);
-
- if (!commandLineCombined) {
- return -1;
- }
-
- // Gonna re-use len to store the write index into commandLineCombined
- len = 0;
-
- for (i = 0; commandLine[i]; i++) {
- if (0 != i) {
- commandLineCombined[len++] = ' ';
- }
-
- for (j = 0; '\0' != commandLine[i][j]; j++) {
- commandLineCombined[len++] = commandLine[i][j];
- }
- }
-
- commandLineCombined[len] = '\0';
-
- if (!CreateProcessA(NULL,
- commandLineCombined, // command line
- NULL, // process security attributes
- NULL, // primary thread security attributes
- 1, // handles are inherited
- 0, // creation flags
- environment, // use parent's environment
- NULL, // use parent's current directory
- (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
- (LPPROCESS_INFORMATION)&processInfo)) {
- return -1;
- }
-
- out_process->hProcess = processInfo.hProcess;
-
- out_process->hStdInput = startInfo.hStdInput;
- out_process->hStdOutput = startInfo.hStdOutput;
- out_process->hStdError = startInfo.hStdError;
-
- // We don't need the handle of the primary thread in the called process.
- CloseHandle(processInfo.hThread);
-
- return 0;
-#else
- int stdinfd[2];
- int stdoutfd[2];
- int stderrfd[2];
- pid_t child;
-
- if (0 != pipe(stdinfd)) {
- return -1;
- }
-
- if (0 != pipe(stdoutfd)) {
- return -1;
- }
-
- if (process_option_combined_stdout_stderr !=
- (options & process_option_combined_stdout_stderr)) {
- if (0 != pipe(stderrfd)) {
- return -1;
- }
- }
-
- child = fork();
-
- if (-1 == child) {
- return -1;
- }
-
- if (0 == child) {
- // Close the stdin write end
- close(stdinfd[1]);
- // Map the read end to stdin
- dup2(stdinfd[0], STDIN_FILENO);
-
- // Close the stdout read end
- close(stdoutfd[0]);
- // Map the write end to stdout
- dup2(stdoutfd[1], STDOUT_FILENO);
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- dup2(STDOUT_FILENO, STDERR_FILENO);
- } else {
- // Close the stderr read end
- close(stderrfd[0]);
- // Map the write end to stdout
- dup2(stderrfd[1], STDERR_FILENO);
- }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wcast-qual"
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#endif
- if (process_option_inherit_environment !=
- (options & process_option_inherit_environment)) {
- char *const environment[1] = {0};
- exit(execve(commandLine[0], (char *const *)commandLine, environment));
- } else {
- exit(execvp(commandLine[0], (char *const *)commandLine));
- }
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- } else {
- // Close the stdin read end
- close(stdinfd[0]);
- // Store the stdin write end
- out_process->stdin_file = fdopen(stdinfd[1], "wb");
-
- // Close the stdout write end
- close(stdoutfd[1]);
- // Store the stdout read end
- out_process->stdout_file = fdopen(stdoutfd[0], "rb");
-
- if (process_option_combined_stdout_stderr ==
- (options & process_option_combined_stdout_stderr)) {
- out_process->stderr_file = out_process->stdout_file;
- } else {
- // Close the stderr write end
- close(stderrfd[1]);
- // Store the stderr read end
- out_process->stderr_file = fdopen(stderrfd[0], "rb");
- }
-
- // Store the child's pid
- out_process->child = child;
-
- return 0;
- }
-#endif
-}
-
-FILE *process_stdin(const struct process_s *const process) {
- return process->stdin_file;
-}
-
-FILE *process_stdout(const struct process_s *const process) {
- return process->stdout_file;
-}
-
-FILE *process_stderr(const struct process_s *const process) {
- if (process->stdout_file != process->stderr_file) {
- return process->stderr_file;
- } else {
- return 0;
- }
-}
-
-int process_join(struct process_s *const process, int *const out_return_code) {
-#if defined(_MSC_VER)
- const unsigned long infinite = 0xFFFFFFFF;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
-
- WaitForSingleObject(process->hProcess, infinite);
-
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-
- if (out_return_code) {
- if (!GetExitCodeProcess(process->hProcess,
- (unsigned long *)out_return_code)) {
- return -1;
- }
- }
-
- return 0;
-#else
- int status;
-
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- process->stdin_file = 0;
- }
-
- if (process->child != waitpid(process->child, &status, 0)) {
- return -1;
- }
-
- if (out_return_code) {
- if (WIFEXITED(status)) {
- *out_return_code = WEXITSTATUS(status);
- } else {
- *out_return_code = 0;
- }
- }
-
- return 0;
-#endif
-}
-
-int process_destroy(struct process_s *const process) {
- if (0 != process->stdin_file) {
- fclose(process->stdin_file);
- }
-
- fclose(process->stdout_file);
-
- if (process->stdout_file != process->stderr_file) {
- fclose(process->stderr_file);
- }
-
-#if defined(_MSC_VER)
- CloseHandle(process->hProcess);
-
- if (0 != process->hStdInput) {
- CloseHandle(process->hStdInput);
- process->hStdInput = NULL;
- }
- if (0 != process->hStdOutput) {
- CloseHandle(process->hStdOutput);
- if (process->hStdError == process->hStdOutput) {
- process->hStdError = NULL;
- }
- process->hStdOutput = NULL;
- }
- if (0 != process->hStdError) {
- CloseHandle(process->hStdError);
- process->hStdError = NULL;
- }
-#endif
-
- return 0;
-}
-
-#if defined(__cplusplus)
-} // extern "C"
-#endif
-
-#endif /* SHEREDOM_PROCESS_H_INCLUDED */
diff --git a/test/subprocess.h b/test/subprocess.h
new file mode 100644
index 0000000..8f63657
--- /dev/null
+++ b/test/subprocess.h
@@ -0,0 +1,843 @@
+/*
+ The latest version of this library is available on GitHub;
+ https://github.com/sheredom/subprocess.h
+*/
+
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ 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 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.
+
+ For more information, please refer to
+*/
+
+#ifndef SHEREDOM_SUBPROCESS_H_INCLUDED
+#define SHEREDOM_SUBPROCESS_H_INCLUDED
+
+#if defined(_MSC_VER)
+#pragma warning(push, 1)
+#endif
+
+#include
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+#define subprocess_pure __attribute__((pure))
+#define subprocess_weak __attribute__((weak))
+#elif defined(_MSC_VER)
+#define subprocess_pure
+#define subprocess_weak __inline
+#else
+#error Non clang, non gcc, non MSVC compiler found!
+#endif
+
+struct subprocess_s;
+
+enum subprocess_option_e {
+ // stdout and stderr are the same FILE.
+ subprocess_option_combined_stdout_stderr = 0x1,
+
+ // The child process should inherit the environment variables of the parent.
+ subprocess_option_inherit_environment = 0x2,
+
+ // Enable asynchronous reading of stdout/stderr before it has completed.
+ subprocess_option_enable_async = 0x4
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/// @brief Create a process.
+/// @param command_line An array of strings for the command line to execute for
+/// this process. The last element must be NULL to signify the end of the array.
+/// @param options A bit field of subprocess_option_e's to pass.
+/// @param out_process The newly created process.
+/// @return On success 0 is returned.
+subprocess_weak int subprocess_create(const char *const command_line[],
+ int options,
+ struct subprocess_s *const out_process);
+
+/// @brief Get the standard input file for a process.
+/// @param process The process to query.
+/// @return The file for standard input of the process.
+///
+/// The file returned can be written to by the parent process to feed data to
+/// the standard input of the process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdin(const struct subprocess_s *const process);
+
+/// @brief Get the standard output file for a process.
+/// @param process The process to query.
+/// @return The file for standard output of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard output of the child process.
+subprocess_pure subprocess_weak FILE *
+subprocess_stdout(const struct subprocess_s *const process);
+
+/// @brief Get the standard error file for a process.
+/// @param process The process to query.
+/// @return The file for standard error of the process.
+///
+/// The file returned can be read from by the parent process to read data from
+/// the standard error of the child process.
+///
+/// If the process was created with the subprocess_option_combined_stdout_stderr
+/// option bit set, this function will return NULL, and the subprocess_stdout
+/// function should be used for both the standard output and error combined.
+subprocess_pure subprocess_weak FILE *
+subprocess_stderr(const struct subprocess_s *const process);
+
+/// @brief Wait for a process to finish execution.
+/// @param process The process to wait for.
+/// @param out_return_code The return code of the returned process (can be
+/// NULL).
+/// @return On success 0 is returned.
+///
+/// Joining a process will close the stdin pipe to the process.
+subprocess_weak int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code);
+
+/// @brief Destroy a previously created process.
+/// @param process The process to destroy.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it may out live
+/// the parent process.
+subprocess_weak int subprocess_destroy(struct subprocess_s *const process);
+
+/// @brief Terminate a previously created process.
+/// @param process The process to terminate.
+/// @return On success 0 is returned.
+///
+/// If the process to be destroyed had not finished execution, it will be
+/// terminated (i.e killed).
+subprocess_weak int subprocess_terminate(struct subprocess_s *const process);
+
+/// @brief Read the standard output from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard output of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stdout(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+/// @brief Read the standard error from the child process.
+/// @param process The process to read from.
+/// @param buffer The buffer to read into.
+/// @param size The maximum number of bytes to read.
+/// @return The number of bytes actually read into buffer. Can only be 0 if the
+/// process has complete.
+///
+/// The only safe way to read from the standard error of a process during it's
+/// execution is to use the `subprocess_option_enable_async` option in
+/// conjuction with this method.
+subprocess_weak unsigned
+subprocess_read_stderr(struct subprocess_s *const process, char *const buffer,
+ unsigned size);
+
+#if defined(__cplusplus)
+#define SUBPROCESS_CAST(type, x) static_cast(x)
+#else
+#define SUBPROCESS_CAST(type, x) ((type)x)
+#endif
+
+#if !defined(_MSC_VER)
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#if defined(_MSC_VER)
+#ifdef _WIN64
+typedef __int64 subprocess_intptr_t;
+typedef unsigned __int64 subprocess_size_t;
+#else
+typedef int subprocess_intptr_t;
+typedef unsigned int subprocess_size_t;
+#endif
+
+typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
+typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
+typedef struct _STARTUPINFOA *LPSTARTUPINFOA;
+typedef struct _OVERLAPPED *LPOVERLAPPED;
+
+#pragma warning(push, 1)
+struct subprocess_subprocess_information_s {
+ void *hProcess;
+ void *hThread;
+ unsigned long dwProcessId;
+ unsigned long dwThreadId;
+};
+
+struct subprocess_security_attributes_s {
+ unsigned long nLength;
+ void *lpSecurityDescriptor;
+ int bInheritHandle;
+};
+
+struct subprocess_startup_info_s {
+ unsigned long cb;
+ char *lpReserved;
+ char *lpDesktop;
+ char *lpTitle;
+ unsigned long dwX;
+ unsigned long dwY;
+ unsigned long dwXSize;
+ unsigned long dwYSize;
+ unsigned long dwXCountChars;
+ unsigned long dwYCountChars;
+ unsigned long dwFillAttribute;
+ unsigned long dwFlags;
+ unsigned short wShowWindow;
+ unsigned short cbReserved2;
+ unsigned char *lpReserved2;
+ void *hStdInput;
+ void *hStdOutput;
+ void *hStdError;
+};
+
+struct subprocess_overlapped_s {
+ uintptr_t Internal;
+ uintptr_t InternalHigh;
+ union {
+ struct {
+ unsigned long Offset;
+ unsigned long OffsetHigh;
+ } DUMMYSTRUCTNAME;
+ void *Pointer;
+ } DUMMYUNIONNAME;
+
+ void *hEvent;
+};
+
+#pragma warning(pop)
+
+__declspec(dllimport) unsigned long __stdcall GetLastError(void);
+__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long,
+ unsigned long);
+__declspec(dllimport) int __stdcall CreatePipe(void **, void **,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long);
+__declspec(dllimport) void *__stdcall CreateNamedPipeA(
+ const char *, unsigned long, unsigned long, unsigned long, unsigned long,
+ unsigned long, unsigned long, LPSECURITY_ATTRIBUTES);
+__declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long,
+ unsigned long *, LPOVERLAPPED);
+__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void);
+__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
+__declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long,
+ unsigned long,
+ LPSECURITY_ATTRIBUTES,
+ unsigned long, unsigned long,
+ void *);
+__declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int,
+ int, const char *);
+__declspec(dllimport) int __stdcall CreateProcessA(
+ const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int,
+ unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
+__declspec(dllimport) int __stdcall CloseHandle(void *);
+__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(
+ void *, unsigned long);
+__declspec(dllimport) int __stdcall GetExitCodeProcess(
+ void *, unsigned long *lpExitCode);
+__declspec(dllimport) int __stdcall TerminateProcess(
+ void *, unsigned int);
+__declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects(
+ unsigned long, void *const *, int, unsigned long);
+__declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
+ unsigned long *, int);
+
+#if defined(_DLL) && (_DLL == 1)
+#define SUBPROCESS_DLLIMPORT __declspec(dllimport)
+#else
+#define SUBPROCESS_DLLIMPORT
+#endif
+
+SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *);
+SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int);
+SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int);
+
+void *__cdecl _alloca(subprocess_size_t);
+#endif
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+struct subprocess_s {
+ FILE *stdin_file;
+ FILE *stdout_file;
+ FILE *stderr_file;
+
+#if defined(_MSC_VER)
+ void *hProcess;
+ void *hStdInput;
+ void *hEventOutput;
+ void *hEventError;
+#else
+ pid_t child;
+#endif
+};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#if defined(_MSC_VER)
+subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr);
+int subprocess_create_named_pipe_helper(void **rd, void **wr) {
+ const unsigned long pipeAccessInbound = 0x00000001;
+ const unsigned long fileFlagOverlapped = 0x40000000;
+ const unsigned long pipeTypeByte = 0x00000000;
+ const unsigned long pipeWait = 0x00000000;
+ const unsigned long genericWrite = 0x40000000;
+ const unsigned long openExisting = 3;
+ const unsigned long fileAttributeNormal = 0x00000080;
+ const void *const invalidHandleValue = (void *)~((subprocess_intptr_t)0);
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char name[256] = {0};
+ __declspec(thread) static long index = 0;
+ const long unique = index++;
+
+#if _MSC_VER < 1900
+#pragma warning(disable : 4996)
+#pragma warning(push, 1)
+
+ _snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+
+#pragma warning(pop)
+#else
+#pragma warning(disable : 4710)
+#pragma warning(push, 1)
+ snprintf(name, sizeof(name) - 1,
+ "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld",
+ GetCurrentProcessId(), GetCurrentThreadId(), unique);
+#pragma warning(pop)
+#endif
+
+ *rd = CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped,
+ pipeTypeByte | pipeWait, 1, 4096, 4096, 0,
+ (LPSECURITY_ATTRIBUTES)&saAttr);
+
+ if (invalidHandleValue == rd) {
+ return -1;
+ }
+
+ *wr = CreateFileA(name, genericWrite, 0, (LPSECURITY_ATTRIBUTES)&saAttr,
+ openExisting, fileAttributeNormal, 0);
+
+ if (invalidHandleValue == wr) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+int subprocess_create(const char *const commandLine[], int options,
+ struct subprocess_s *const out_process) {
+#if defined(_MSC_VER)
+ int fd;
+ void *rd, *wr;
+ char *commandLineCombined;
+ subprocess_size_t len;
+ int i, j;
+ const unsigned long startFUseStdHandles = 0x00000100;
+ const unsigned long handleFlagInherit = 0x00000001;
+ struct subprocess_subprocess_information_s processInfo;
+ struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), 0, 1};
+ char *environment = 0;
+ struct subprocess_startup_info_s startInfo = {0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ startInfo.cb = sizeof(startInfo);
+ startInfo.dwFlags = startFUseStdHandles;
+
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ environment = "\0\0";
+ }
+
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+
+ if (!SetHandleInformation(wr, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)wr, 0);
+
+ if (-1 != fd) {
+ out_process->stdin_file = _fdopen(fd, "wb");
+
+ if (0 == out_process->stdin_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdInput = rd;
+
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stdout_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stdout_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdOutput = wr;
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ startInfo.hStdError = startInfo.hStdOutput;
+ } else {
+ if (options & subprocess_option_enable_async) {
+ if (subprocess_create_named_pipe_helper(&rd, &wr)) {
+ return -1;
+ }
+ } else {
+ if (!CreatePipe(&rd, &wr, (LPSECURITY_ATTRIBUTES)&saAttr, 0)) {
+ return -1;
+ }
+ }
+
+ if (!SetHandleInformation(rd, handleFlagInherit, 0)) {
+ return -1;
+ }
+
+ fd = _open_osfhandle((subprocess_intptr_t)rd, 0);
+
+ if (-1 != fd) {
+ out_process->stderr_file = _fdopen(fd, "rb");
+
+ if (0 == out_process->stderr_file) {
+ return -1;
+ }
+ }
+
+ startInfo.hStdError = wr;
+ }
+
+ if (options & subprocess_option_enable_async) {
+ out_process->hEventOutput =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ out_process->hEventError =
+ CreateEventA((LPSECURITY_ATTRIBUTES)&saAttr, 1, 1, 0);
+ } else {
+ out_process->hEventOutput = 0;
+ out_process->hEventError = 0;
+ }
+
+ // Combine commandLine together into a single string
+ len = 0;
+ for (i = 0; commandLine[i]; i++) {
+ // For the ' ' between items and trailing '\0'
+ len++;
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ len++;
+ }
+ }
+
+ commandLineCombined = (char *)_alloca(len);
+
+ if (!commandLineCombined) {
+ return -1;
+ }
+
+ // Gonna re-use len to store the write index into commandLineCombined
+ len = 0;
+
+ for (i = 0; commandLine[i]; i++) {
+ if (0 != i) {
+ commandLineCombined[len++] = ' ';
+ }
+
+ for (j = 0; '\0' != commandLine[i][j]; j++) {
+ commandLineCombined[len++] = commandLine[i][j];
+ }
+ }
+
+ commandLineCombined[len] = '\0';
+
+ if (!CreateProcessA(NULL,
+ commandLineCombined, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ 1, // handles are inherited
+ 0, // creation flags
+ environment, // use parent's environment
+ NULL, // use parent's current directory
+ (LPSTARTUPINFOA)&startInfo, // STARTUPINFO pointer
+ (LPPROCESS_INFORMATION)&processInfo)) {
+ return -1;
+ }
+
+ out_process->hProcess = processInfo.hProcess;
+
+ out_process->hStdInput = startInfo.hStdInput;
+
+ // We don't need the handle of the primary thread in the called process.
+ CloseHandle(processInfo.hThread);
+
+ if (0 != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdOutput);
+
+ if (startInfo.hStdError != startInfo.hStdOutput) {
+ CloseHandle(startInfo.hStdError);
+ }
+ }
+
+ return 0;
+#else
+ int stdinfd[2];
+ int stdoutfd[2];
+ int stderrfd[2];
+ pid_t child;
+
+ if (0 != pipe(stdinfd)) {
+ return -1;
+ }
+
+ if (0 != pipe(stdoutfd)) {
+ return -1;
+ }
+
+ if (subprocess_option_combined_stdout_stderr !=
+ (options & subprocess_option_combined_stdout_stderr)) {
+ if (0 != pipe(stderrfd)) {
+ return -1;
+ }
+ }
+
+ child = fork();
+
+ if (-1 == child) {
+ return -1;
+ }
+
+ if (0 == child) {
+ // Close the stdin write end
+ close(stdinfd[1]);
+ // Map the read end to stdin
+ dup2(stdinfd[0], STDIN_FILENO);
+
+ // Close the stdout read end
+ close(stdoutfd[0]);
+ // Map the write end to stdout
+ dup2(stdoutfd[1], STDOUT_FILENO);
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ } else {
+ // Close the stderr read end
+ close(stderrfd[0]);
+ // Map the write end to stdout
+ dup2(stderrfd[1], STDERR_FILENO);
+ }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-qual"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#endif
+ if (subprocess_option_inherit_environment !=
+ (options & subprocess_option_inherit_environment)) {
+ char *const environment[1] = {0};
+ exit(execve(commandLine[0], (char *const *)commandLine, environment));
+ } else {
+ exit(execvp(commandLine[0], (char *const *)commandLine));
+ }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ } else {
+ // Close the stdin read end
+ close(stdinfd[0]);
+ // Store the stdin write end
+ out_process->stdin_file = fdopen(stdinfd[1], "wb");
+
+ // Close the stdout write end
+ close(stdoutfd[1]);
+ // Store the stdout read end
+ out_process->stdout_file = fdopen(stdoutfd[0], "rb");
+
+ if (subprocess_option_combined_stdout_stderr ==
+ (options & subprocess_option_combined_stdout_stderr)) {
+ out_process->stderr_file = out_process->stdout_file;
+ } else {
+ // Close the stderr write end
+ close(stderrfd[1]);
+ // Store the stderr read end
+ out_process->stderr_file = fdopen(stderrfd[0], "rb");
+ }
+
+ // Store the child's pid
+ out_process->child = child;
+
+ return 0;
+ }
+#endif
+}
+
+FILE *subprocess_stdin(const struct subprocess_s *const process) {
+ return process->stdin_file;
+}
+
+FILE *subprocess_stdout(const struct subprocess_s *const process) {
+ return process->stdout_file;
+}
+
+FILE *subprocess_stderr(const struct subprocess_s *const process) {
+ if (process->stdout_file != process->stderr_file) {
+ return process->stderr_file;
+ } else {
+ return 0;
+ }
+}
+
+int subprocess_join(struct subprocess_s *const process,
+ int *const out_return_code) {
+#if defined(_MSC_VER)
+ const unsigned long infinite = 0xFFFFFFFF;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ process->hStdInput = NULL;
+ }
+
+ WaitForSingleObject(process->hProcess, infinite);
+
+ if (out_return_code) {
+ if (!GetExitCodeProcess(process->hProcess,
+ (unsigned long *)out_return_code)) {
+ return -1;
+ }
+ }
+
+ return 0;
+#else
+ int status;
+
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (process->child != waitpid(process->child, &status, 0)) {
+ return -1;
+ }
+
+ if (out_return_code) {
+ if (WIFEXITED(status)) {
+ *out_return_code = WEXITSTATUS(status);
+ } else {
+ *out_return_code = EXIT_FAILURE;
+ }
+ }
+
+ return 0;
+#endif
+}
+
+int subprocess_destroy(struct subprocess_s *const process) {
+ if (0 != process->stdin_file) {
+ fclose(process->stdin_file);
+ process->stdin_file = 0;
+ }
+
+ if (0 != process->stdout_file) {
+ fclose(process->stdout_file);
+
+ if (process->stdout_file != process->stderr_file) {
+ fclose(process->stderr_file);
+ }
+
+ process->stdout_file = 0;
+ process->stderr_file = 0;
+ }
+
+#if defined(_MSC_VER)
+ if (process->hProcess) {
+ CloseHandle(process->hProcess);
+ process->hProcess = 0;
+
+ if (0 != process->hStdInput) {
+ CloseHandle(process->hStdInput);
+ }
+
+ if (0 != process->hEventOutput) {
+ CloseHandle(process->hEventOutput);
+ }
+
+ if (0 != process->hEventError) {
+ CloseHandle(process->hEventError);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int subprocess_terminate(struct subprocess_s *const process) {
+#if defined(_MSC_VER)
+ unsigned int killed_process_exit_code;
+ int success_terminate;
+ int windows_call_result;
+
+ killed_process_exit_code = 99;
+ windows_call_result = TerminateProcess(process->hProcess, killed_process_exit_code);
+ success_terminate = (windows_call_result== 0) ? 1 : 0;
+ return success_terminate;
+#else
+ int result;
+ result = kill(process->child, 9);
+ return result;
+#endif
+}
+
+unsigned subprocess_read_stdout(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventOutput;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stdout_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stdout_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+unsigned subprocess_read_stderr(struct subprocess_s *const process,
+ char *const buffer, unsigned size) {
+#if defined(_MSC_VER)
+ void *handle;
+ unsigned long bytes_read = 0;
+ struct subprocess_overlapped_s overlapped = {0};
+ overlapped.hEvent = process->hEventError;
+
+ handle = (void *)_get_osfhandle(_fileno(process->stderr_file));
+
+ if (!ReadFile(handle, buffer, size, &bytes_read, (LPOVERLAPPED)&overlapped)) {
+ const unsigned long errorIoPending = 997;
+ unsigned long error = GetLastError();
+
+ // Means we've got an async read!
+ if (error == errorIoPending) {
+ if (!GetOverlappedResult(handle, (LPOVERLAPPED)&overlapped, &bytes_read,
+ 1)) {
+ const unsigned long errorIoIncomplete = 996;
+ const unsigned long errorHandleEOF = 38;
+ error = GetLastError();
+
+ if ((error != errorIoIncomplete) && (error != errorHandleEOF)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return (unsigned)bytes_read;
+#else
+ const int fd = fileno(process->stderr_file);
+ const ssize_t bytes_read = read(fd, buffer, size);
+
+ if (bytes_read < 0) {
+ return 0;
+ }
+
+ return SUBPROCESS_CAST(unsigned, bytes_read);
+#endif
+}
+
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */
diff --git a/test/test11.c b/test/test11.c
index eab366b..4de85c4 100644
--- a/test/test11.c
+++ b/test/test11.c
@@ -103,12 +103,12 @@
UTEST_F_TEARDOWN(MyTestF) { ASSERT_EQ(13, utest_fixture->foo); }
-UTEST_F(MyTestF, c) {
+UTEST_F(MyTestF, c11) {
ASSERT_EQ(42, utest_fixture->foo);
utest_fixture->foo = 13;
}
-UTEST_F(MyTestF, c2) {
+UTEST_F(MyTestF, c11_2) {
ASSERT_EQ(42, utest_fixture->foo);
utest_fixture->foo = 13;
}
@@ -130,12 +130,12 @@
ASSERT_EQ(utest_index, utest_fixture->bar);
}
-UTEST_I(MyTestI, c, 2) {
+UTEST_I(MyTestI, c11, 2) {
ASSERT_GT(2u, utest_fixture->bar);
utest_fixture->foo = 13;
}
-UTEST_I(MyTestI, c2, 128) {
+UTEST_I(MyTestI, c11_2, 128) {
ASSERT_GT(128u, utest_fixture->bar);
utest_fixture->foo = 13;
}
@@ -224,7 +224,7 @@
UTEST(c11, VoidPtr) {
void *foo = 0;
- EXPECT_NE(foo, (c11har *)foo + 1);
+ EXPECT_NE(foo, (char *)foo + 1);
}
static const int data[4] = {42, 13, 6, -53};
diff --git a/test/test99.c b/test/test99.c
new file mode 100644
index 0000000..2531f73
--- /dev/null
+++ b/test/test99.c
@@ -0,0 +1,232 @@
+/*
+ This is free and unencumbered software released into the public domain.
+
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+
+ 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 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.
+
+ For more information, please refer to
+*/
+
+#include "utest.h"
+
+#ifdef _MSC_VER
+/* disable 'conditional expression is constant' - our examples below use this!
+ */
+#pragma warning(disable : 4127)
+#pragma
+#endif
+
+UTEST(c99, ASSERT_TRUE) { ASSERT_TRUE(1); }
+
+UTEST(c99, ASSERT_FALSE) { ASSERT_FALSE(0); }
+
+UTEST(c99, ASSERT_EQ) { ASSERT_EQ(1, 1); }
+
+UTEST(c99, ASSERT_NE) { ASSERT_NE(1, 2); }
+
+UTEST(c99, ASSERT_LT) { ASSERT_LT(1, 2); }
+
+UTEST(c99, ASSERT_LE) {
+ ASSERT_LE(1, 1);
+ ASSERT_LE(1, 2);
+}
+
+UTEST(c99, ASSERT_GT) { ASSERT_GT(2, 1); }
+
+UTEST(c99, ASSERT_GE) {
+ ASSERT_GE(1, 1);
+ ASSERT_GE(2, 1);
+}
+
+UTEST(c99, ASSERT_STREQ) { ASSERT_STREQ("foo", "foo"); }
+
+UTEST(c99, ASSERT_STRNE) { ASSERT_STRNE("foo", "bar"); }
+
+UTEST(c99, EXPECT_TRUE) { EXPECT_TRUE(1); }
+
+UTEST(c99, EXPECT_FALSE) { EXPECT_FALSE(0); }
+
+UTEST(c99, EXPECT_EQ) { EXPECT_EQ(1, 1); }
+
+UTEST(c99, EXPECT_NE) { EXPECT_NE(1, 2); }
+
+UTEST(c99, EXPECT_LT) { EXPECT_LT(1, 2); }
+
+UTEST(c99, EXPECT_LE) {
+ EXPECT_LE(1, 1);
+ EXPECT_LE(1, 2);
+}
+
+UTEST(c99, EXPECT_GT) { EXPECT_GT(2, 1); }
+
+UTEST(c99, EXPECT_GE) {
+ EXPECT_GE(1, 1);
+ EXPECT_GE(2, 1);
+}
+
+UTEST(c99, EXPECT_STREQ) { EXPECT_STREQ("foo", "foo"); }
+
+UTEST(c99, EXPECT_STRNE) { EXPECT_STRNE("foo", "bar"); }
+
+UTEST(c99, no_double_eval) {
+ int i = 0;
+ ASSERT_EQ(i++, 0);
+ ASSERT_EQ(i, 1);
+}
+
+struct MyTestF {
+ int foo;
+};
+
+UTEST_F_SETUP(MyTestF) {
+ ASSERT_EQ(0, utest_fixture->foo);
+ utest_fixture->foo = 42;
+}
+
+UTEST_F_TEARDOWN(MyTestF) { ASSERT_EQ(13, utest_fixture->foo); }
+
+UTEST_F(MyTestF, c99) {
+ ASSERT_EQ(42, utest_fixture->foo);
+ utest_fixture->foo = 13;
+}
+
+UTEST_F(MyTestF, c99_2) {
+ ASSERT_EQ(42, utest_fixture->foo);
+ utest_fixture->foo = 13;
+}
+
+struct MyTestI {
+ size_t foo;
+ size_t bar;
+};
+
+UTEST_I_SETUP(MyTestI) {
+ ASSERT_EQ(0u, utest_fixture->foo);
+ ASSERT_EQ(0u, utest_fixture->bar);
+ utest_fixture->foo = 42;
+ utest_fixture->bar = utest_index;
+}
+
+UTEST_I_TEARDOWN(MyTestI) {
+ ASSERT_EQ(13u, utest_fixture->foo);
+ ASSERT_EQ(utest_index, utest_fixture->bar);
+}
+
+UTEST_I(MyTestI, c99, 2) {
+ ASSERT_GT(2u, utest_fixture->bar);
+ utest_fixture->foo = 13;
+}
+
+UTEST_I(MyTestI, c99_2, 128) {
+ ASSERT_GT(128u, utest_fixture->bar);
+ utest_fixture->foo = 13;
+}
+
+UTEST(c99, Float) {
+ float a = 1;
+ float b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Double) {
+ double a = 1;
+ double b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, LongDouble) {
+ long double a = 1;
+ long double b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Char) {
+ signed char a = 1;
+ signed char b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, UChar) {
+ unsigned char a = 1;
+ unsigned char b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Short) {
+ short a = 1;
+ short b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, UShort) {
+ unsigned short a = 1;
+ unsigned short b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Int) {
+ int a = 1;
+ int b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, UInt) {
+ unsigned int a = 1;
+ unsigned int b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Long) {
+ long a = 1;
+ long b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, ULong) {
+ unsigned long a = 1;
+ unsigned long b = 2;
+ EXPECT_NE(a, b);
+ ASSERT_NE(a, b);
+}
+
+UTEST(c99, Ptr) {
+ char foo = 42;
+ EXPECT_NE(&foo, &foo + 1);
+}
+
+UTEST(c99, VoidPtr) {
+ void *foo = 0;
+ EXPECT_NE(foo, (char *)foo + 1);
+}
+
+static const int data[4] = {42, 13, 6, -53};
+
+UTEST(c99, Array) { EXPECT_NE(data, data + 1); }
diff --git a/utest.h b/utest.h
index 7f2bcaa..1970bca 100644
--- a/utest.h
+++ b/utest.h
@@ -101,6 +101,9 @@
#include
#include
#endif
+#else // Other libc implementations
+#include
+#define UTEST_USE_CLOCKGETTIME
#endif
#elif defined(__APPLE__)
@@ -196,6 +199,8 @@
QueryPerformanceFrequency(&frequency);
return UTEST_CAST(utest_int64_t,
(counter.QuadPart * 1000000000) / frequency.QuadPart);
+#elif defined(__linux) && defined(__STRICT_ANSI__)
+ return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC;
#elif defined(__linux)
struct timespec ts;
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
@@ -349,7 +354,11 @@
#endif
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
#define utest_type_printer(val) \
- UTEST_PRINTF(_Generic((val), int \
+ UTEST_PRINTF(_Generic((val), signed char \
+ : "%d", unsigned char \
+ : "%u", short \
+ : "%d", unsigned short \
+ : "%u", int \
: "%d", long \
: "%ld", long long \
: "%lld", unsigned \