_______________________________
/_______________________________/|
|o ____ _ _ ____ ____ o|$|
| / ___)( \/ )/ ___)(___ \ |$| ______
| \___ \ ) / \___ \ / __/ |$| $$$$$/
| (____/(__/ (____/(____) |$| $$$$/
|o____________________________o|/ &//
From: https://patorjk.com with Graceful font
System2 is a cross-platform c library that allows you to call shell commands and other executables (subprocess), just like system but with the ability to
provide input to stdin and capture the output from stdout and stderr.
- Header only (source version available as well)
- Written in C99, and is ready to be used in C++ as well
- Cross-platform (POSIX and Windows)
- Command interaction with stdin, stdout, and stderr
- Blocking (sync) and non-blocking (async) version
- Termintating commands early
- Custom Environment Variables Support
- No dependencies (only standard C and system libraries). No longer need a heavy framework like boost or poco just to capture output from running a command.
- UTF-8 support*
- CMake integration
* See Remarks for UTF-8 support
Check main.c for more examples.
#include "System2.h"
#include <stdio.h>
int main(int, char**)
{
//Initialize command info
System2CommandInfo commandInfo;
{
memset(&commandInfo, 0, sizeof(System2CommandInfo));
commandInfo.RedirectInput = true;
commandInfo.RedirectOutput = true;
}
//Run the command in shell (subprocess is also available, see main.c)
{
#if defined(__unix__) || defined(__APPLE__)
System2Run("read testVar && echo testVar is \\\"$testVar\\\"", &commandInfo);
#elif
System2Run("set /p testVar= && echo testVar is \"!testVar!\"", &commandInfo);
#endif
}
//Send input to the command
{
char input[] = "test content\n";
System2WriteToInput(&commandInfo, input, sizeof(input));
}
//Wait for command to finish and get return code
int returnCode = -1;
System2GetCommandReturnValueSync(&commandInfo, &returnCode);
//Capture output and print it
{
char outputBuffer[1024];
uint32_t bytesRead = 0;
System2ReadFromOutput(&commandInfo, outputBuffer, 1023, &bytesRead);
outputBuffer[bytesRead] = 0;
printf("%s\n", outputBuffer);
printf("%s: %d\n", "Command has executed with return value", returnCode);
}
System2CleanupCommand(&commandInfo);
return 0;
//Output: testVar is "test content"
//Output: Command has executed with return value: 0
}This library has header only version, just include "System2.h" and you are good to go.
However, this will leak system library headers to your codebase.
In that case, you can use the source version of the library.
-
Define
SYSTEM2_DECLARATION_ONLY 1before includingSystem2.h -
Then, either:
- Add
System2.cto you codebase - Or include
System2.hin a single c file and defineSYSTEM2_IMPLEMENTATION_ONLY 1before it - Or link your project with
System2target in CMake`
- Add
- Posix spawn version is also available by defining
SYSTEM2_POSIX_SPAWN 1before including, see #3 for more details
typedef struct
{
bool RedirectInput; //Redirect input with pipe?
bool RedirectOutput; //Redirect output with pipe?
const char* RunDirectory; //The directory to run the command in? NULL for current working directory
const char** EnvVarsNames; //Array of environment variables names to add/set/unset. Will be ignored if NULL
const char** EnvVarsValues; //Array of environment variables values to add/set/unset. Will be ignored if NULL.
//If the value itself is NULL, it will unset the environment variable
int EnvVarsCount; //How many environment variables, if `EnvVarsNames` is not NULL
#if defined(_WIN32)
bool DisableEscapes; //Disable automatic escaping?
#endif
//Internal fields...
} System2CommandInfo;
/*
Runs the command in system shell just like the `system()` funcion with the given settings
passed with `inOutCommandInfo`.
`System2CleanupCommand()` should be called when you are done with it.
This uses
`sh -c command` for POSIX and
`cmd /s /v /c command` for Windows
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_PIPE_CREATE_FAILED
- SYSTEM2_RESULT_CREATE_CHILD_PROCESS_FAILED
- SYSTEM2_RESULT_PIPE_FD_CLOSE_FAILED
- SYSTEM2_RESULT_COMMAND_CONSTRUCT_FAILED
- SYSTEM2_RESULT_POSIX_SPAWN_FILE_ACTION_DESTROY_FAILED
- SYSTEM2_RESULT_POSIX_SPAWN_FILE_ACTION_DUP2_FAILED
- SYSTEM2_RESULT_POSIX_SPAWN_RUN_DIRECTORY_NOT_SUPPORTED
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2Run( const char* command,
System2CommandInfo* inOutCommandInfo);
/*
Runs the executable (which can search in PATH env variable) with the given arguments and settings
passed with inOutCommandInfo. Arguments are passed to the executable directly.
Passing `NULL` to `args` denotes no arguments.
`System2CleanupCommand()` should be called when you are done with it.
On Windows, automatic escaping can be removed by setting the `DisableEscape` in `inOutCommandInfo`
NOTE: Unlike posix exec* function calls, you don't need to pass the path of executable to `args`.
This is handled internally.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_PIPE_CREATE_FAILED
- SYSTEM2_RESULT_CREATE_CHILD_PROCESS_FAILED
- SYSTEM2_RESULT_PIPE_FD_CLOSE_FAILED
- SYSTEM2_RESULT_COMMAND_CONSTRUCT_FAILED
- SYSTEM2_RESULT_POSIX_SPAWN_FILE_ACTION_DESTROY_FAILED
- SYSTEM2_RESULT_POSIX_SPAWN_FILE_ACTION_DUP2_FAILED
- SYSTEM2_RESULT_POSIX_SPAWN_RUN_DIRECTORY_NOT_SUPPORTED
- SYSTEM2_RESULT_INVALID_ARGUMENT
- SYSTEM2_RESULT_WINDOWS_UNICODE_FAILED
- SYSTEM2_RESULT_MALLOC_FAILED
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2RunSubprocess(const char* executable,
const char* const* args,
int argsCount,
System2CommandInfo* inOutCommandInfo);
/*
Reads the output (stdout and stderr) from the command.
Output string is **NOT** null terminated.
If SYSTEM2_RESULT_READ_NOT_FINISHED is returned,
this function can be called again until SYSTEM2_RESULT_SUCCESS to retrieve the rest of the output.
outBytesRead determines how many bytes have been read for **this** function call
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_READ_NOT_FINISHED
- SYSTEM2_RESULT_READ_FAILED
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2ReadFromOutput( const System2CommandInfo* info,
char* outputBuffer,
uint32_t outputBufferSize,
uint32_t* outBytesRead);
/*
Write the input (stdin) to the command.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_WRITE_FAILED
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2WriteToInput( const System2CommandInfo* info,
const char* inputBuffer,
const uint32_t inputBufferSize);
//TODO: Might want to add this to have this ability to close input pipe manually
//SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2CloseInput(System2CommandInfo* info);
/*
Cleanup any open handles associated with the command.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_PIPE_FD_CLOSE_FAILED
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2CleanupCommand(const System2CommandInfo* info);
/*
Gets the return code if the command has finished.
Otherwise, this will return SYSTEM2_RESULT_COMMAND_NOT_FINISHED immediately.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_COMMAND_NOT_FINISHED
- SYSTEM2_RESULT_COMMAND_TERMINATED
- SYSTEM2_RESULT_PIPE_FD_CLOSE_FAILED
- SYSTEM2_RESULT_COMMAND_WAIT_ASYNC_FAILED
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX
SYSTEM2_RESULT System2GetCommandReturnValueAsync(const System2CommandInfo* info, int* outReturnCode);
/*
Wait for the command to finish and gets the return code
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_COMMAND_TERMINATED
- SYSTEM2_RESULT_PIPE_FD_CLOSE_FAILED
- SYSTEM2_RESULT_COMMAND_WAIT_SYNC_FAILED
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2GetCommandReturnValueSync(const System2CommandInfo* info,
int* outReturnCode);
/*
Kills (cannot be caught) a spawned command.
NOTE: On Posix, this will cause `System2GetCommandReturnValue*` to return
`SYSTEM2_RESULT_COMMAND_TERMINATED`.
While on Windows, `SYSTEM2_RESULT_SUCCESS` will be returned instead.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_INVALID_ARGUMENT
- SYSTEM2_RESULT_KILL_FAILED
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2Kill(const System2CommandInfo* info);
/*
Terminates a spawned command.
NOTE: This has no guarantee that the command is terminated even if the returned value is
`SYSTEM2_RESULT_SUCCESS`. You should always check the status of the command with
`System2GetCommandReturnValue*`.
NOTE: On Posix, this will cause `System2GetCommandReturnValue*` to return
`SYSTEM2_RESULT_COMMAND_TERMINATED`.
While on Windows, `SYSTEM2_RESULT_SUCCESS` will be returned instead.
NOTE: This will fail with `SYSTEM2_RESULT_WINDOWS_TERM_NO_WINDOW` on Windows if the spawned command
has no window handle. In which case, you will need to kill it instead.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_INVALID_ARGUMENT
- SYSTEM2_RESULT_TERM_FAILED
- SYSTEM2_RESULT_WINDOWS_TERM_NO_WINDOW
- SYSTEM2_RESULT_MALLOC_FAILED
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2Term(const System2CommandInfo* info);
/*
Returns the count of environment variables, along with a resource handle which can be used to
access the environment variable values with `System2GetEnvironmentVariables()`.
The resource handle should be freed with `System2EnvironmentVariableFree()` when done.
NOTE: If you need to get a particular environment variable without iteration, use `getenv()` from the
standard library.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_INVALID_ARGUMENT
- SYSTEM2_RESULT_MALLOC_FAILED
*/
SYSTEM2_FUNC_PREFIX
SYSTEM2_RESULT System2GetEnvironmentVariablesCount(int* outCount, void** outResource);
/*
Returns the environment variable name and value for a given index. The behavior is undefined if
trying to index an environment variable outside of bound.
The content of the returned environment name and value should be copied to a local buffer
immediately as changes to the environment variable might invalidate them.
NOTE: If you need to get a particular environment variable without iteration, use `getenv()` from the
standard library.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2GetEnvironmentVariable( const void* resource,
const char** outName,
int* outNameLength,
const char** outValue,
int* outValueLength,
int index);
/*
Free the resource handle created by `System2GetEnvironmentVariablesCount()` and set it to NULL.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_INVALID_ARGUMENT
*/
SYSTEM2_FUNC_PREFIX SYSTEM2_RESULT System2EnvironmentVariableFree(void** resource);
/*
Sets/unsets an environment variable where it is unset if `envValue` is `NULL`.
`envName` must be valid for the platform otherwise this function will fail.
If the environment variable with `envName` does not exists when trying to unset,
this function MIGHT fail depending on the platform.
To make sure the environement variable is correctly set, you should get the environment variable.
Could return the following results:
- SYSTEM2_RESULT_SUCCESS
- SYSTEM2_RESULT_INVALID_ARGUMENT
- SYSTEM2_RESULT_WINDOWS_UNICODE_FAILED
- SYSTEM2_RESULT_MALLOC_FAILED
- SYSTEM2_RESULT_WINDOWS_SET_ENV_FAILED
*/
SYSTEM2_FUNC_PREFIX
SYSTEM2_RESULT System2SetEnvironmentVariable(const char* envName, const char* envValue);- For Linux or MacOS,
System2Run()andSystem2RunSubprocess()will inherit the parent process memory (due to howfork()works). Meaning it is possible to over commit memory and therefore causes out of memory error.- A temporary fix is there by using
posix_spawn()instead offork()by#define SYSTEM2_POSIX_SPAWN 1before#include "System2.h" - See Issue
- A temporary fix is there by using
- For POSIX, UTF-8 support should work if it is available on the system. This is however not tested.
- For Windows, UTF-8 support works for the command input (in theory XP and above but tested on Windows 10).
However, the output part is NOT in UTF-8. The closest thing you can get for the output is UTF-16 as far as I know.
Here's what needed to get output in UTF-16:
- Instead of
System2Run("<your command>", &commandInfo), doSystem2Run("cmd /u /s /v /c \"<your command>\"", &commandInfo)This will output a UTF-16 string from cmd stdout/stderr - Read output as usual from
System2ReadFromOutputbut interpret the output as wchar_t string instead.- You can then use
WideCharToMultiByteto convert the output to UTF-8 if needed
- You can then use
- If you want to output the UTF-16 output to console, you need to use
_setmodebefore callingwprintf/printf- See this for
_setmodeexample
- See this for
- Instead of