aboutsummaryrefslogtreecommitdiff
path: root/stdlib/shell.c
blob: 36b6a9ad23377ea437db5df839a60a690d2ad851 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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