aboutsummaryrefslogtreecommitdiff
path: root/stdlib/shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib/shell.c')
-rw-r--r--stdlib/shell.c67
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