diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-16 17:02:20 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-16 17:02:20 -0400 |
| commit | 9703ca45f0472dfed440f373c29206b0b2c8e4db (patch) | |
| tree | 0ea5f11d0bd59f2b7d36623a6e510c460317feac /stdlib | |
| parent | 32c139e1f468864ce23bc091d9db309b1206c872 (diff) | |
Update Shell API to have byte-based mode and by_line() just like files
Diffstat (limited to 'stdlib')
| -rw-r--r-- | stdlib/shell.c | 95 | ||||
| -rw-r--r-- | stdlib/shell.h | 9 |
2 files changed, 88 insertions, 16 deletions
diff --git a/stdlib/shell.c b/stdlib/shell.c index 36b6a9ad..977fa512 100644 --- a/stdlib/shell.c +++ b/stdlib/shell.c @@ -1,4 +1,5 @@ // A lang for Shell Command Language +#include <errno.h> #include <stdbool.h> #include <stdint.h> @@ -32,29 +33,95 @@ public Shell_t Shell$escape_text(Text_t text) 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) +public OptionalArray_t Shell$run_bytes(Shell_t command) { const char *cmd_str = Text$as_c_string(command); FILE *prog = popen(cmd_str, "r"); + if (!prog) + return NULL_ARRAY; - const int chunk_size = 256; - char *buf = GC_MALLOC_ATOMIC(chunk_size); - Text_t output = Text(""); + size_t capacity = 256, len = 0; + char *content = GC_MALLOC_ATOMIC(capacity); + char chunk[256]; 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); + just_read = fread(chunk, 1, sizeof(chunk), prog); + if (just_read == 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + break; } - } while (just_read > 0); - if (status) - *status = WEXITSTATUS(pclose(prog)); - else - pclose(prog); + if (len + (size_t)just_read >= capacity) + content = GC_REALLOC(content, (capacity *= 2)); - return Text$trim(output, Pattern("{1 nl}"), false, true); + memcpy(&content[len], chunk, (size_t)just_read); + len += (size_t)just_read; + } while (just_read == sizeof(chunk)); + + int status = pclose(prog); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return NULL_ARRAY; + + return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; +} + +public OptionalText_t Shell$run(Shell_t command) +{ + Array_t bytes = Shell$run_bytes(command); + if (bytes.length < 0) + return NULL_TEXT; + + if (bytes.length > 0 && *(char*)(bytes.data + (bytes.length-1)*bytes.stride) == '\n') { + --bytes.length; + if (bytes.length > 0 && *(char*)(bytes.data + (bytes.length-1)*bytes.stride) == '\r') + --bytes.length; + } + return Text$from_bytes(bytes); +} + +static void _line_reader_cleanup(FILE **f) +{ + if (f && *f) { + pclose(*f); + *f = NULL; + } +} + +static Text_t _next_line(FILE **f) +{ + if (!f || !*f) return NULL_TEXT; + + char *line = NULL; + size_t size = 0; + ssize_t len = getline(&line, &size, *f); + if (len <= 0) { + _line_reader_cleanup(f); + return NULL_TEXT; + } + + while (len > 0 && (line[len-1] == '\r' || line[len-1] == '\n')) + --len; + + if (u8_check((uint8_t*)line, (size_t)len) != NULL) + fail("Invalid UTF8!"); + + Text_t line_text = Text$format("%.*s", len, line); + free(line); + return line_text; +} + +public OptionalClosure_t Shell$by_line(Shell_t command) +{ + const char *cmd_str = Text$as_c_string(command); + FILE *prog = popen(cmd_str, "r"); + if (!prog) + return NULL_CLOSURE; + + FILE **wrapper = GC_MALLOC(sizeof(FILE*)); + *wrapper = prog; + GC_register_finalizer(wrapper, (void*)_line_reader_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn=(void*)_next_line, .userdata=wrapper}; } public const TypeInfo Shell$info = { diff --git a/stdlib/shell.h b/stdlib/shell.h index 48c59abc..16d0b10d 100644 --- a/stdlib/shell.h +++ b/stdlib/shell.h @@ -5,15 +5,20 @@ #include <stdbool.h> #include <stdint.h> -#include "types.h" +#include "arrays.h" #include "datatypes.h" +#include "optionals.h" +#include "text.h" +#include "types.h" #define Shell_t Text_t #define Shell(text) ((Shell_t)Text(text)) #define Shells(...) ((Shell_t)Texts(__VA_ARGS__)) -Text_t Shell$run(Shell_t command, int32_t *status); +OptionalClosure_t Shell$by_line(Shell_t command); Shell_t Shell$escape_text(Text_t text); +OptionalArray_t Shell$run_bytes(Shell_t command); +OptionalText_t Shell$run(Shell_t command); #define Shell$hash Text$hash #define Shell$compare Text$compare |
