diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-13 20:18:08 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-13 20:18:08 -0400 |
| commit | c455e7b67d2e55e6ed03e3449203d4e307f5a7dd (patch) | |
| tree | 27d9d4c77193f7aa1fe3a3c6fe5631d0ccfd59e2 /stdlib/shell.c | |
| parent | 816aa29b799132acb8c71d4968df6c4619fb2b1d (diff) | |
Rename builtins/ -> stdlib/
Diffstat (limited to 'stdlib/shell.c')
| -rw-r--r-- | stdlib/shell.c | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/stdlib/shell.c b/stdlib/shell.c new file mode 100644 index 00000000..36b6a9ad --- /dev/null +++ b/stdlib/shell.c @@ -0,0 +1,67 @@ +// A lang for Shell Command Language +#include <stdbool.h> +#include <stdint.h> + +#include "arrays.h" +#include "integers.h" +#include "patterns.h" +#include "shell.h" +#include "text.h" +#include "types.h" +#include "util.h" + +public Shell_t Shell$escape_text(Text_t text) +{ + // TODO: optimize for ASCII and short strings + Array_t shell_graphemes = {.atomic=1}; +#define add_char(c) Array$insert(&shell_graphemes, (uint32_t[1]){c}, I_small(0), sizeof(uint32_t)) + add_char('\''); + const char *text_utf8 = Text$as_c_string(text); + for (const char *p = text_utf8; *p; p++) { + if (*p == '\'') { + add_char('\''); + add_char('"'); + add_char('\''); + add_char('"'); + add_char('\''); + } else + add_char((uint8_t)*p); + } + add_char('\''); +#undef add_char + return (Text_t){.length=shell_graphemes.length, .tag=TEXT_GRAPHEMES, .graphemes=shell_graphemes.data}; +} + +public Text_t Shell$run(Shell_t command, int32_t *status) +{ + const char *cmd_str = Text$as_c_string(command); + FILE *prog = popen(cmd_str, "r"); + + const int chunk_size = 256; + char *buf = GC_MALLOC_ATOMIC(chunk_size); + Text_t output = Text(""); + size_t just_read; + do { + just_read = fread(buf, sizeof(char), chunk_size, prog); + if (just_read > 0) { + output = Texts(output, Text$from_strn(buf, just_read)); + buf = GC_MALLOC_ATOMIC(chunk_size); + } + } while (just_read > 0); + + if (status) + *status = WEXITSTATUS(pclose(prog)); + else + pclose(prog); + + return Text$trim(output, Pattern("{1 nl}"), false, true); +} + +public const TypeInfo Shell$info = { + .size=sizeof(Shell_t), + .align=__alignof__(Shell_t), + .tag=TextInfo, + .TextInfo={.lang="Shell"}, +}; + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 |
