123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h
- // simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now)
- //
- // do this:
- // #define SIMPLE_EXEC_IMPLEMENTATION
- // before you include this file in *one* C or C++ file to create the implementation.
- // i.e. it should look like this:
- // #define SIMPLE_EXEC_IMPLEMENTATION
- // #include "simple_exec.h"
- #ifndef SIMPLE_EXEC_H
- #define SIMPLE_EXEC_H
- int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...);
- int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs);
- #endif // SIMPLE_EXEC_H
- #ifdef SIMPLE_EXEC_IMPLEMENTATION
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <assert.h>
- #include <sys/wait.h>
- #include <stdarg.h>
- #include <fcntl.h>
- #define release_assert(x) do { int __release_assert_tmp__ = (x); assert(__release_assert_tmp__); } while(0)
- enum PIPE_FILE_DESCRIPTORS
- {
- READ_FD = 0,
- WRITE_FD = 1
- };
- enum RUN_COMMAND_ERROR
- {
- COMMAND_RAN_OK = 0,
- COMMAND_NOT_FOUND = 1
- };
- int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs)
- {
- // adapted from: https://stackoverflow.com/a/479103
- int bufferSize = 256;
- char buffer[bufferSize + 1];
- int dataReadFromChildDefaultSize = bufferSize * 5;
- int dataReadFromChildSize = dataReadFromChildDefaultSize;
- int dataReadFromChildUsed = 0;
- char* dataReadFromChild = (char*)malloc(dataReadFromChildSize);
- int parentToChild[2];
- release_assert(pipe(parentToChild) == 0);
- int childToParent[2];
- release_assert(pipe(childToParent) == 0);
- int errPipe[2];
- release_assert(pipe(errPipe) == 0);
- pid_t pid;
- switch( pid = fork() )
- {
- case -1:
- {
- release_assert(0 && "Fork failed");
- }
- case 0: // child
- {
- release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
- release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
-
- if(includeStdErr)
- {
- release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
- }
- else
- {
- int devNull = open("/dev/null", O_WRONLY);
- release_assert(dup2(devNull, STDERR_FILENO) != -1);
- }
- // unused
- release_assert(close(parentToChild[WRITE_FD]) == 0);
- release_assert(close(childToParent[READ_FD ]) == 0);
- release_assert(close(errPipe[READ_FD]) == 0);
-
- const char* command = allArgs[0];
- execvp(command, allArgs);
- char err = 1;
- write(errPipe[WRITE_FD], &err, 1);
-
- close(errPipe[WRITE_FD]);
- close(parentToChild[READ_FD]);
- close(childToParent[WRITE_FD]);
- exit(0);
- }
- default: // parent
- {
- // unused
- release_assert(close(parentToChild[READ_FD]) == 0);
- release_assert(close(childToParent[WRITE_FD]) == 0);
- release_assert(close(errPipe[WRITE_FD]) == 0);
- while(1)
- {
- ssize_t bytesRead = 0;
- switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
- {
- case 0: // End-of-File, or non-blocking read.
- {
- int status = 0;
- release_assert(waitpid(pid, &status, 0) == pid);
- // done with these now
- release_assert(close(parentToChild[WRITE_FD]) == 0);
- release_assert(close(childToParent[READ_FD]) == 0);
- char errChar = 0;
- read(errPipe[READ_FD], &errChar, 1);
- close(errPipe[READ_FD]);
- if(errChar)
- {
- free(dataReadFromChild);
- return COMMAND_NOT_FOUND;
- }
-
- // free any un-needed memory with realloc + add a null terminator for convenience
- dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
- dataReadFromChild[dataReadFromChildUsed] = '\0';
-
- if(stdOut != NULL)
- *stdOut = dataReadFromChild;
- else
- free(dataReadFromChild);
- if(stdOutByteCount != NULL)
- *stdOutByteCount = dataReadFromChildUsed;
- if(returnCode != NULL)
- *returnCode = WEXITSTATUS(status);
- return COMMAND_RAN_OK;
- }
- case -1:
- {
- release_assert(0 && "read() failed");
- }
- default:
- {
- if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
- {
- dataReadFromChildSize += dataReadFromChildDefaultSize;
- dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
- }
- memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
- dataReadFromChildUsed += bytesRead;
- break;
- }
- }
- }
- }
- }
- }
- int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
- {
- va_list vl;
- va_start(vl, command);
-
- char* currArg = NULL;
-
- int allArgsInitialSize = 16;
- int allArgsSize = allArgsInitialSize;
- char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
- allArgs[0] = command;
-
- int i = 1;
- do
- {
- currArg = va_arg(vl, char*);
- allArgs[i] = currArg;
- i++;
- if(i >= allArgsSize)
- {
- allArgsSize += allArgsInitialSize;
- allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
- }
- } while(currArg != NULL);
- va_end(vl);
- int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
- free(allArgs);
- return retval;
- }
- #endif //SIMPLE_EXEC_IMPLEMENTATION
|