tomo/builtins/shell.c

68 lines
1.8 KiB
C

// A lang for Shell Command Language
#include <stdbool.h>
#include <stdint.h>
#include "array.h"
#include "functions.h"
#include "integers.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