Download
332 lines 10.6 KiBCModified 2022-09-27 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
#include <assert.h>
#include <bp/match.h>
#include <bp/pattern.h>
#include <bp/printmatch.h>
#include <ctype.h>
#include <err.h>
#include <gc.h>
#include <gc/cord.h>
#include <intern.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <time.h>

#include "types.h"

static const int64_t INT_NIL = 0x7FFFFFFFFFFFFFFF;

#define RETURN_FMT(fmt, ...) do { char *ret = NULL; int status = asprintf(&ret, fmt, __VA_ARGS__); if (status < 0) err(1, "string formatting failed"); const char *tmp = intern_str(ret); free(ret); return tmp; } while(0)
#define CLAMP(x, lo, hi) MIN(hi, MAX(x,lo))

const char *bl_string(const char *s) { return intern_str(s); }
const char *bl_tostring_int(int64_t i) { RETURN_FMT("%ld", i); }
const char *bl_tostring_int32(int32_t i) { RETURN_FMT("%d", i); }
const char *bl_tostring_int16(int16_t i) { RETURN_FMT("%d", i); }
const char *bl_tostring_int8(int8_t i) { RETURN_FMT("%d", i); }
const char *bl_tostring_hex(int64_t i) { RETURN_FMT("0x%lX", i); }
const char *bl_tostring_char(int64_t c) { RETURN_FMT("%c", (char)c); }
const char *bl_tostring_float(double f) { RETURN_FMT("%g", f); }
const char *bl_tostring_float32(float f) { RETURN_FMT("%g", f); }
const char *bl_tostring_percent(double f) { RETURN_FMT("%g%%", f*100.0); }
const char *bl_tostring_bool(int64_t b) { return intern_str(b ? "yes" : "no"); }
const char *bl_tostring_nil(void) { return intern_str("nil"); }
const char *bl_tostring_range(range_t *r) {
    if (r->first < 99999999 && r->last > 99999999)
        return intern_str("..");
    else if (r->first < 99999999)
        RETURN_FMT("..%ld", r->last);
    else if (r->last > 99999999 && r->next != r->first+1)
        RETURN_FMT("%ld,%ld..", r->first, r->next);
    else if (r->last > 99999999)
        RETURN_FMT("%ld..", r->first);
    else if (r->last > r->first ? (r->next == r->first+1) : (r->next == r->first-1))
        RETURN_FMT("%ld..%ld", r->first, r->last);
    else
        RETURN_FMT("%ld,%ld..%ld", r->first, r->next, r->last);
}
const char *bl_tostring_time(const char *fmt, int64_t seconds)
{
    size_t strftime(char *restrict s, size_t max,
                    const char *restrict format,
                    const struct tm *restrict tm);
    time_t t = seconds;
    struct tm tm;
    localtime_r(&t, &tm);
    char buf[128];
    strftime(buf, 127, fmt, &tm);
    return intern_str(buf);
}

// const char *bl_string_join(int64_t count, char **strings, char *sep) {
//     if (!strings) return NULL;
//     CORD buf = CORD_EMPTY;
//     size_t seplen = sep ? strlen(sep) : 0;
//     for (int64_t i = 0; i < count; i++) {
//         char *str = strings[i];
//         if (!str) str = "(nil)";
//         buf = CORD_cat(buf, str);
//         if (sep && i < count - 1)
//             buf = CORD_cat_char_star(buf, sep, seplen);
//     }
//     return bl_string(CORD_to_const_char_star(buf));
// }

// const char *bl_string_list_join(list_t *list, char *sep) {
//     return bl_string_join(list->len, (char**)list->items, sep);
// }

const char *bl_string_append_int(char *s, int64_t i) { RETURN_FMT("%s%ld", s, i); }
const char *bl_string_append_float(char *s, double f) { RETURN_FMT("%s%g", s, f); }
const char *bl_string_append_char(char *s, int64_t c) { RETURN_FMT("%s%c", s, (char)c); }
const char *bl_string_append_bool(char *s, int64_t b) { RETURN_FMT("%s%s", s, b ? "yes" : "no"); }
const char *bl_string_append_range(char *s, range_t *r) {
    if (r->first < 99999999 && r->last > 99999999)
        RETURN_FMT("%s..", s);
    else if (r->first < 99999999)
        RETURN_FMT("%s..%ld", s, r->last);
    else if (r->last > 99999999 && r->next != r->first+1)
        RETURN_FMT("%s%ld,%ld..", s, r->first, r->next);
    else if (r->last > 99999999)
        RETURN_FMT("%s%ld..", s, r->first);
    else if (r->last > r->first ? (r->next == r->first+1) : (r->next == r->first-1))
        RETURN_FMT("%s%ld..%ld", s, r->first, r->last);
    else
        RETURN_FMT("%s%ld,%ld..%ld", s, r->first, r->next, r->last);
}
const char *bl_string_append_string(char *a, char *b) { RETURN_FMT("%s%s", a, b); }

const char *bl_string_slice(const char *s, range_t *r) {
    int64_t step = r->next - r->first;
    if (step == 0) return intern_str("");

    int64_t len = (int64_t)strlen(s);
    int64_t first = CLAMP(r->first-1, 0, len-1), last = CLAMP(r->last-1, 0, len-1);
    int64_t slice_len = 0;
    for (int64_t i = first; step > 0 ? i <= last : i >= last; i += step)
        ++slice_len;
    char *buf = calloc(slice_len+1, 1);
    assert(buf);
    for (int64_t i = first, b_i = 0; step > 0 ? i <= last : i >= last; i += step)
        buf[b_i++] = s[i];
    const char *ret = intern_str(buf);
    free(buf);
    return ret;
}

bool bl_string_starts_with(const char *s, const char *prefix) {
    while (*prefix) {
        if (*prefix++ != *s++)
            return false;
    }
    return true;
}

bool bl_string_ends_with(const char *s, const char *suffix) {
    size_t s_len = strlen(s);
    size_t suffix_len = strlen(suffix);
    if (suffix_len > s_len) return false;
    return memcmp(s+(s_len-suffix_len), suffix, suffix_len) == 0;
}

const char *bl_string_upper(const char *s) {
    char *s2 = strdup(s);
    for (char *p = s2; *p; p++)
        *p = toupper(*p);
    const char *ret = intern_str(s2);
    free(s2);
    return ret;
}

const char *bl_string_lower(const char *s) {
    char *s2 = strdup(s);
    for (char *p = s2; *p; p++)
        *p = tolower(*p);
    const char *ret = intern_str(s2);
    free(s2);
    return ret;
}

const char *bl_string_capitalized(const char *s) {
    if (!isalpha(s[0]) || isupper(s[0]))
        return s;
    char *s2 = strdup(s);
    s2[0] = toupper(s2[0]);
    const char *ret = intern_str(s2);
    free(s2);
    return ret;
}

const char *bl_string_titlecased(const char *s) {
    char *s2 = strdup(s);
    bool should_capitalize = true;
    for (char *p = s2; *p; p++) {
        if (isalpha(*p)) {
            if (should_capitalize) {
                *p = toupper(*p);
                should_capitalize = false;
            } else {
                *p = tolower(*p);
            }
        } else {
            should_capitalize = true;
        }
    }
    const char *ret = intern_str(s2);
    free(s2);
    return ret;
}

int64_t bl_string_nth_char(const char *s, int64_t n) {
    --n;
    if (n < 0) return INT_NIL;
    int64_t len = (int64_t)strlen(s);
    if (n > len) return INT_NIL;
    return (int64_t)s[n];
}

const char *bl_string_repeat(const char *s, int64_t count) {
    if (count <= 0) return intern_str("");
    size_t len = strlen(s);
    char *buf = calloc(len*count + 1, 1);
    assert(buf);
    char *p = buf;
    for (int64_t i = 0; i < count; i++) {
        memcpy(p, s, len);
        p += len;
    }
    const char *ret = intern_str(buf);
    free(buf);
    return ret;
}

const char *bl_string_strip(const char *s, const char *strip_chars, int8_t strip_left, int8_t strip_right) {
    if (strip_chars == NULL) strip_chars = " \n\r\t";
    const char *start = s;
    if (strip_left)
        start += strspn(s, strip_chars);
    size_t len = strlen(start);
    if (strip_right) {
        while (strspn(start + len - 1, strip_chars))
            --len;
    }
    const char *buf = strndupa(start, len);
    return intern_str(buf);
}

const char *bl_string_replace(char *text, char *pat_text, char *rep_text, int64_t limit) {
    maybe_pat_t maybe_pat = bp_stringpattern(pat_text, pat_text + strlen(pat_text));
    if (!maybe_pat.success) {
        return text;
    }
    pat_t *pat = maybe_pat.value.pat;

    maybe_pat_t maybe_replacement = bp_replacement(pat, rep_text, rep_text + strlen(rep_text));
    if (!maybe_replacement.success) {
        return text;
    }

    char *buf = NULL;
    size_t size = 0;
    FILE *out = open_memstream(&buf, &size);
    const char *prev = text;
    pat_t *rep_pat = maybe_replacement.value.pat;
    size_t textlen = strlen(text);
    if (limit == INT_NIL) limit = textlen + 1;
    if (limit > 0) {
        for (match_t *m = NULL; next_match(&m, text, &text[textlen], rep_pat, NULL, NULL, false); ) {
            fwrite(prev, sizeof(char), (size_t)(m->start - prev), out);
            fprint_match(out, text, m, NULL);
            prev = m->end;
            if (--limit == 0) {
                stop_matching(&m);
                break;
            }
        }
    }
    fwrite(prev, sizeof(char), (size_t)(&text[textlen] - prev) + 1, out);
    fflush(out);
    const char *replaced = buf ? intern_str(buf) : intern_str("");
    fclose(out);
    return replaced;
}

// const char *bl_string_match(char *text, char *pat_text) {
//     maybe_pat_t maybe_pat = bp_pattern(pat_text, pat_text + strlen(pat_text));
//     if (!maybe_pat.success) {
//         return intern_str("");
//     }

//     char *buf = NULL;
//     size_t size = 0;
//     FILE *out = open_memstream(&buf, &size);
//     size_t textlen = strlen(text);
//     pat_t *pat = maybe_pat.value.pat;
//     for (match_t *m = NULL; next_match(&m, text, &text[textlen], pat, NULL, NULL, false); ) {
//         fprint_match(out, text, m, NULL);
//         stop_matching(&m);
//         break;
//     }
//     fflush(out);
//     const char *match = buf ? intern_str(buf) : intern_str("");
//     fclose(out);
//     return match;
// }

bool bl_string_matches(char *text, char *pat_text) {
    maybe_pat_t maybe_pat = bp_stringpattern(pat_text, pat_text + strlen(pat_text));
    if (!maybe_pat.success) {
        return intern_str("");
    }

    size_t textlen = strlen(text);
    pat_t *pat = maybe_pat.value.pat;
    match_t *m = NULL;
    if (next_match(&m, text, &text[textlen], pat, NULL, NULL, false)) {
        stop_matching(&m);
        return true;
    } else {
        return false;
    }
}

const char *bl_ask(char *prompt) {
    printf("%s", prompt);
    char *line = NULL;
    size_t capacity = 0;
    ssize_t len = getline(&line, &capacity, stdin);
    if (len < 0 || !line)
        return NULL;
    if (len > 1 && line[len-1] == '\n')
        line[--len] = '\0';
    const char *ret = intern_str(line);
    free(line);
    return ret;
}

const char *bl_system(char *cmd) {
    FILE *f = popen(cmd, "r");
    char buffer[256];
    size_t chread;
    /* String to store entire command contents in */
    size_t comalloc = 256;
    size_t comlen = 0;
    char *comout = malloc(comalloc);
 
    /* Use fread so binary data is dealt with correctly */
    while ((chread = fread(buffer, 1, sizeof(buffer), f)) > 0) {
        if (comlen + chread >= comalloc) {
            comalloc *= 2;
            comout = realloc(comout, comalloc);
        }
        memmove(comout + comlen, buffer, chread);
        comlen += chread;
    }
 
    comout[strlen(comout)-1] = '\0';
    const char *ret = bl_string(comout);
    free(comout);
    pclose(f);
    return ret;
}