فهرست منبع

Port existing pipe functionality

rexim 4 سال پیش
والد
کامیت
0bf65e3fd9
2فایلهای تغییر یافته به همراه262 افزوده شده و 7 حذف شده
  1. 17 2
      nobuild2.c
  2. 245 5
      nobuild2.h

+ 17 - 2
nobuild2.c

@@ -42,10 +42,25 @@ void run_examples(void)
     });
     });
 }
 }
 
 
+
+void print_chain(const Chain *chain)
+{
+    INFO("input: %s", chain->input_filepath);
+    INFO("output: %s", chain->output_filepath);
+    FOREACH_ARRAY(Cmd_Line, cmd_line, chain->cmd_lines, {
+        INFO("cmd: %s", cmd_line_show(*cmd_line));
+    });
+}
+
 int main(void)
 int main(void)
 {
 {
-    build_tools();
-    run_examples();
+    CHAIN(IN(__FILE__),
+          CHAIN_CMD(PATH("tools", "rot13")),
+          CHAIN_CMD(PATH("tools", "hex")),
+          OUT("output.txt"));
+
+    // build_tools();
+    // run_examples();
 
 
     return 0;
     return 0;
 }
 }

+ 245 - 5
nobuild2.h

@@ -8,14 +8,17 @@
 #    include <sys/stat.h>
 #    include <sys/stat.h>
 #    include <unistd.h>
 #    include <unistd.h>
 #    include <dirent.h>
 #    include <dirent.h>
+#    include <fcntl.h>
 #    define PATH_SEP "/"
 #    define PATH_SEP "/"
      typedef pid_t Pid;
      typedef pid_t Pid;
+     typedef int Fd;
 #else
 #else
 #    define WIN32_MEAN_AND_LEAN
 #    define WIN32_MEAN_AND_LEAN
 #    include "windows.h"
 #    include "windows.h"
 #    include <process.h>
 #    include <process.h>
 #    define PATH_SEP "\\"
 #    define PATH_SEP "\\"
      typedef HANDLE Pid;
      typedef HANDLE Pid;
+     typedef HANDLE Fd;
 // minirent.h HEADER BEGIN ////////////////////////////////////////
 // minirent.h HEADER BEGIN ////////////////////////////////////////
     // Copyright 2021 Alexey Kutepov <[email protected]>
     // Copyright 2021 Alexey Kutepov <[email protected]>
     //
     //
@@ -79,12 +82,21 @@
 #include <string.h>
 #include <string.h>
 #include <errno.h>
 #include <errno.h>
 
 
+#define FOREACH_ARRAY(type, elem, array, body)  \
+    for (size_t elem_##index = 0;                           \
+         elem_##index < array.count;                        \
+         ++elem_##index)                                    \
+    {                                                       \
+        type *elem = &array.elems[elem_##index];            \
+        body;                                               \
+    }
+
 typedef const char * Cstr;
 typedef const char * Cstr;
 
 
 int cstr_ends_with(Cstr cstr, Cstr postfix);
 int cstr_ends_with(Cstr cstr, Cstr postfix);
-Cstr cstr_no_ext(Cstr path);
-
 #define ENDS_WITH(cstr, postfix) cstr_ends_with(cstr, postfix)
 #define ENDS_WITH(cstr, postfix) cstr_ends_with(cstr, postfix)
+
+Cstr cstr_no_ext(Cstr path);
 #define NOEXT(path) cstr_no_ext(path)
 #define NOEXT(path) cstr_no_ext(path)
 
 
 typedef struct {
 typedef struct {
@@ -108,9 +120,14 @@ typedef struct {
 
 
 void pid_wait(Pid pid);
 void pid_wait(Pid pid);
 Cstr cmd_line_show(Cmd_Line cmd_line);
 Cstr cmd_line_show(Cmd_Line cmd_line);
-Pid cmd_line_run_async(Cmd_Line cmd_line);
+Pid cmd_line_run_async(Cmd_Line cmd_line, Fd *fdin, Fd *fdout);
 void cmd_line_run_sync(Cmd_Line cmd_line);
 void cmd_line_run_sync(Cmd_Line cmd_line);
 
 
+typedef struct {
+    Cmd_Line *elems;
+    size_t count;
+} Cmd_Line_Array;
+
 #define CMD(...)                                    \
 #define CMD(...)                                    \
     do {                                            \
     do {                                            \
         Cmd_Line cmd_line = CMD_LINE(__VA_ARGS__);  \
         Cmd_Line cmd_line = CMD_LINE(__VA_ARGS__);  \
@@ -118,6 +135,54 @@ void cmd_line_run_sync(Cmd_Line cmd_line);
         cmd_line_run_sync(cmd_line);                \
         cmd_line_run_sync(cmd_line);                \
     } while (0)
     } while (0)
 
 
+typedef enum {
+    CHAIN_TOKEN_END = 0,
+    CHAIN_TOKEN_IN,
+    CHAIN_TOKEN_OUT,
+    CHAIN_TOKEN_CMD
+} Chain_Token_Type;
+
+// A single token for the CHAIN(...) DSL syntax
+typedef struct {
+    Chain_Token_Type type;
+    Cstr_Array args;
+} Chain_Token;
+
+#define IN(path) \
+    (Chain_Token) { \
+        .type = CHAIN_TOKEN_IN, \
+        .args = cstr_array_make(path, NULL) \
+    }
+
+#define OUT(path) \
+    (Chain_Token) { \
+        .type = CHAIN_TOKEN_OUT, \
+        .args = cstr_array_make(path, NULL) \
+    }
+
+#define CHAIN_CMD(...) \
+    (Chain_Token) { \
+        .type = CHAIN_TOKEN_CMD, \
+        .args = cstr_array_make(__VA_ARGS__, NULL) \
+    }
+
+typedef struct {
+    Cstr input_filepath;
+    Cmd_Line_Array cmd_lines;
+    Cstr output_filepath;
+} Chain;
+
+Chain chain_build_from_tokens(Chain_Token first, ...);
+void chain_run_sync(Chain chain);
+void chain_echo(Chain chain);
+
+#define CHAIN(...)                                                      \
+    do {                                                                \
+        Chain chain = chain_build_from_tokens(__VA_ARGS__, (Chain_Token) {0}); \
+        chain_echo(chain);                                              \
+        chain_run_sync(chain);                                          \
+    } while(0)
+
 int path_is_dir(Cstr path);
 int path_is_dir(Cstr path);
 #define IS_DIR(path) path_is_dir(path)
 #define IS_DIR(path) path_is_dir(path)
 
 
@@ -397,7 +462,7 @@ Cstr cmd_line_show(Cmd_Line cmd_line)
     return cstr_array_join(" ", cmd_line.line);
     return cstr_array_join(" ", cmd_line.line);
 }
 }
 
 
-Pid cmd_line_run_async(Cmd_Line cmd_line)
+Pid cmd_line_run_async(Cmd_Line cmd_line, Fd *fdin, Fd *fdout)
 {
 {
 #ifdef _WIN32
 #ifdef _WIN32
     PANIC("TODO: cmd_line_run_sync is not implemented for WinAPI");
     PANIC("TODO: cmd_line_run_sync is not implemented for WinAPI");
@@ -412,6 +477,18 @@ Pid cmd_line_run_async(Cmd_Line cmd_line)
     if (cpid == 0) {
     if (cpid == 0) {
         Cstr_Array args = cstr_array_append(cmd_line.line, NULL);
         Cstr_Array args = cstr_array_append(cmd_line.line, NULL);
 
 
+        if (fdin) {
+            if (dup2(*fdin, STDIN_FILENO) < 0) {
+                PANIC("Could not setup stdin for child process: %s", strerror(errno));
+            }
+        }
+
+        if (fdout) {
+            if (dup2(*fdout, STDOUT_FILENO) < 0) {
+                PANIC("Could not setup stdout for child process: %s", strerror(errno));
+            }
+        }
+
         if (execvp(args.elems[0], (char * const*) args.elems) < 0) {
         if (execvp(args.elems[0], (char * const*) args.elems) < 0) {
             PANIC("Could not exec child process: %s: %s",
             PANIC("Could not exec child process: %s: %s",
                   cmd_line_show(cmd_line), strerror(errno));
                   cmd_line_show(cmd_line), strerror(errno));
@@ -425,7 +502,7 @@ Pid cmd_line_run_async(Cmd_Line cmd_line)
 void cmd_line_run_sync(Cmd_Line cmd_line)
 void cmd_line_run_sync(Cmd_Line cmd_line)
 {
 {
 #ifndef _WIN32
 #ifndef _WIN32
-    pid_wait(cmd_line_run_async(cmd_line));
+    pid_wait(cmd_line_run_async(cmd_line, NULL, NULL));
 #else
 #else
     Cstr_Array args = cstr_array_append(cmd_line.line, NULL);
     Cstr_Array args = cstr_array_append(cmd_line.line, NULL);
     intptr_t status = _spawnvp(_P_WAIT, args.elems[0], (char * const*) args.elems);
     intptr_t status = _spawnvp(_P_WAIT, args.elems[0], (char * const*) args.elems);
@@ -439,6 +516,169 @@ void cmd_line_run_sync(Cmd_Line cmd_line)
 #endif  // _WIN32
 #endif  // _WIN32
 }
 }
 
 
+static void chain_set_input_output_files_or_count_cmds(Chain *chain, Chain_Token token)
+{
+    switch (token.type) {
+    case CHAIN_TOKEN_CMD: {
+        chain->cmd_lines.count += 1;
+    } break;
+
+    case CHAIN_TOKEN_IN: {
+        if (chain->input_filepath) {
+            PANIC("Input file path was already set");
+        }
+
+        chain->input_filepath = token.args.elems[0];
+    } break;
+
+    case CHAIN_TOKEN_OUT: {
+        if (chain->output_filepath) {
+            PANIC("Output file path was already set");
+        }
+
+        chain->output_filepath = token.args.elems[0];
+    } break;
+
+    case CHAIN_TOKEN_END:
+    default: {
+        assert(0 && "unreachable");
+        exit(1);
+    }
+    }
+}
+
+static void chain_push_cmd_line(Chain *chain, Chain_Token token)
+{
+    if (token.type == CHAIN_TOKEN_CMD) {
+        chain->cmd_lines.elems[chain->cmd_lines.count++] = (Cmd_Line) {
+            .line = token.args
+        };
+    }
+}
+
+Chain chain_build_from_tokens(Chain_Token first, ...)
+{
+    Chain result = {0};
+
+    chain_set_input_output_files_or_count_cmds(&result, first);
+    va_list args;
+    va_start(args, first);
+    Chain_Token next = va_arg(args, Chain_Token);
+    while (next.type != CHAIN_TOKEN_END) {
+        chain_set_input_output_files_or_count_cmds(&result, next);
+        next = va_arg(args, Chain_Token);
+    }
+    va_end(args);
+
+    result.cmd_lines.elems = malloc(sizeof(result.cmd_lines.elems[0]) * result.cmd_lines.count);
+    if (result.cmd_lines.elems == NULL) {
+        PANIC("could not allocate memory: %s", strerror(errno));
+    }
+    result.cmd_lines.count = 0;
+
+    chain_push_cmd_line(&result, first);
+
+    va_start(args, first);
+    next = va_arg(args, Chain_Token);
+    while (next.type != CHAIN_TOKEN_END) {
+        chain_push_cmd_line(&result, next);
+        next = va_arg(args, Chain_Token);
+    }
+    va_end(args);
+
+    return result;
+}
+
+void chain_run_sync(Chain chain)
+{
+#ifdef _WIN32
+    PANIC("chain_run_sync is not implemented for WinAPI");
+#else
+    if (chain.cmd_lines.count == 0) {
+        return;
+    }
+
+    Pid *cpids = malloc(sizeof(pid_t) * chain.cmd_lines.count);
+
+    Fd pipefd[2] = {0};
+    Fd fdin = 0;
+    Fd *fdprev = NULL;
+
+    if (chain.input_filepath) {
+        fdin = open(chain.input_filepath, O_RDONLY);
+        if (fdin < 0) {
+            PANIC("could not open file %s: %s", chain.input_filepath, strerror(errno));
+        }
+        fdprev = &fdin;
+    }
+
+    for (size_t i = 0; i < chain.cmd_lines.count - 1; ++i) {
+        if (pipe(pipefd) < 0) {
+            PANIC("could not create pipe for a child process: %s", strerror(errno));
+        }
+
+        cpids[i] = cmd_line_run_async(
+            chain.cmd_lines.elems[i],
+            fdprev,
+            &pipefd[1]);
+
+        if (fdprev) close(*fdprev);
+        close(pipefd[1]);
+        fdprev = &fdin;
+        fdin = pipefd[0];
+    }
+
+    {
+        Fd fdout = 0;
+        Fd *fdnext = NULL;
+
+        if (chain.output_filepath) {
+            fdout = open(chain.output_filepath,
+                         O_WRONLY | O_CREAT | O_TRUNC,
+                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+            if (fdout < 0) {
+                PANIC("could not open file %s: %s",
+                      chain.output_filepath,
+                      strerror(errno));
+            }
+            fdnext = &fdout;
+        }
+
+        const size_t last = chain.cmd_lines.count - 1;
+        cpids[last] =
+            cmd_line_run_async(
+                chain.cmd_lines.elems[last],
+                fdprev,
+                fdnext);
+
+        if (fdprev) close(*fdprev);
+        if (fdnext) close(*fdnext);
+    }
+
+    for (size_t i = 0; i < chain.cmd_lines.count; ++i) {
+        pid_wait(cpids[i]);
+    }
+#endif // _WIN32
+}
+
+void chain_echo(Chain chain)
+{
+    printf("[INFO]");
+    if (chain.input_filepath) {
+        printf(" %s", chain.input_filepath);
+    }
+
+    FOREACH_ARRAY(Cmd_Line, cmd_line, chain.cmd_lines, {
+        printf(" |> %s", cmd_line_show(*cmd_line));
+    });
+
+    if (chain.output_filepath) {
+        printf(" |> %s", chain.output_filepath);
+    }
+
+    printf("\n");
+}
+
 int path_is_dir(Cstr path)
 int path_is_dir(Cstr path)
 {
 {
 #ifdef _WIN32
 #ifdef _WIN32