nfd2 0.3.1

Please use https://crates.io/crates/rfd instead
Documentation
// 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>
#include <signal.h>

#include "ftg_core.h"

#define release_assert(exp)                                                    \
    {                                                                          \
        if (!(exp)) {                                                          \
            abort();                                                           \
        }                                                                      \
    }

enum PIPE_FILE_DESCRIPTORS { READ_FD = 0, WRITE_FD = 1 };

enum RUN_COMMAND_ERROR { COMMAND_RAN_OK = 0, COMMAND_NOT_FOUND = 1 };

void
sigchldHandler(int p)
{
    FTG_UNUSED(p);
}

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);

    void (*prevHandler)(int);
    prevHandler = signal(SIGCHLD, sigchldHandler);

    pid_t pid;
    switch (pid = fork()) {
    case -1: {
        release_assert(0 && "Fork failed");
        break;
    }

    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;
        ssize_t result = write(errPipe[WRITE_FD], &err, 1);
        release_assert(result != -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;
                ssize_t result = read(errPipe[READ_FD], &errChar, 1);
                release_assert(result != -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");
                break;
            }

            default: {
                if (dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize) {
                    dataReadFromChildSize += dataReadFromChildDefaultSize;
                    dataReadFromChild =
                        (char*)realloc(dataReadFromChild, dataReadFromChildSize);
                }

                memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
                dataReadFromChildUsed += bytesRead;
                break;
            }
            }
        }
    }
    }
    signal(SIGCHLD, prevHandler);
}

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