aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/stdlib/mapmacro.h29
-rw-r--r--src/stdlib/print.h29
-rw-r--r--src/stdlib/simpleparse.c128
-rw-r--r--src/stdlib/simpleparse.h51
-rw-r--r--src/stdlib/stacktrace.c144
-rw-r--r--src/stdlib/stacktrace.h2
-rw-r--r--src/stdlib/stdlib.h4
-rw-r--r--src/stdlib/text.c1
-rw-r--r--src/tomo.c2
9 files changed, 293 insertions, 97 deletions
diff --git a/src/stdlib/mapmacro.h b/src/stdlib/mapmacro.h
new file mode 100644
index 00000000..5ed7a4b7
--- /dev/null
+++ b/src/stdlib/mapmacro.h
@@ -0,0 +1,29 @@
+#pragma once
+
+// This file defines a MAP_LIST(fn, ...) function that applies a function to
+// every one of the varargs.
+// For example: baz(MAP_LIST(foo, 1, "x")) -> baz(foo(1), foo("x"))
+
+#define EVAL0(...) __VA_ARGS__
+#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
+#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
+#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
+#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
+#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
+
+#define MAP_END(...)
+#define MAP_OUT
+#define MAP_COMMA ,
+
+#define MAP_GET_END2() 0, MAP_END
+#define MAP_GET_END1(...) MAP_GET_END2
+#define MAP_GET_END(...) MAP_GET_END1
+#define MAP_NEXT0(test, next, ...) next MAP_OUT
+
+#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
+#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
+
+#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
+#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
+
+#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
diff --git a/src/stdlib/print.h b/src/stdlib/print.h
index 0b911ed6..60c9c139 100644
--- a/src/stdlib/print.h
+++ b/src/stdlib/print.h
@@ -24,35 +24,12 @@
#include <unistd.h>
#include "datatypes.h"
+#include "mapmacro.h"
#ifndef PRINT_COLOR
#define PRINT_COLOR 0
#endif
-#define EVAL0(...) __VA_ARGS__
-#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
-#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
-#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
-#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
-#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
-
-#define MAP_END(...)
-#define MAP_OUT
-#define MAP_COMMA ,
-
-#define MAP_GET_END2() 0, MAP_END
-#define MAP_GET_END1(...) MAP_GET_END2
-#define MAP_GET_END(...) MAP_GET_END1
-#define MAP_NEXT0(test, next, ...) next MAP_OUT
-
-#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
-#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
-
-#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
-#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
-
-#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
// GCC lets you define macro-like functions which are always inlined and never
// compiled using this combination of flags. See: https://gcc.gnu.org/onlinedocs/gcc/Inline.html
#ifndef PRINT_FN
@@ -108,10 +85,10 @@ PRINT_FN _print_float(FILE *f, float x) { return fprintf(f, hl("%g"), (double)x)
PRINT_FN _print_double(FILE *f, double x) { return fprintf(f, hl("%g"), x); }
PRINT_FN _print_pointer(FILE *f, void *p) { return fprintf(f, hl("%p"), p); }
PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? hl("yes") : hl("no"), f); }
-PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s, f); }
+PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s ? s : "(null)", f); }
int _print_char(FILE *f, char c);
int _print_quoted(FILE *f, quoted_t quoted);
-PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { return fwrite(slice.str, 1, slice.length, f); }
+PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { return slice.str ? fwrite(slice.str, 1, slice.length, f) : (size_t)fputs("(null)", f); }
PRINT_FN _print_hex(FILE *f, hex_format_t hex) {
return fprintf(f, hex.no_prefix ? (hex.uppercase ? hl("%0*"FMT64"X") : hl("%0*"FMT64"x")) : (hex.uppercase ? hl("0x%0*"FMT64"X") : hl("%#0*"FMT64"x")), hex.digits, hex.n);
}
diff --git a/src/stdlib/simpleparse.c b/src/stdlib/simpleparse.c
new file mode 100644
index 00000000..1ee64036
--- /dev/null
+++ b/src/stdlib/simpleparse.c
@@ -0,0 +1,128 @@
+// A simple text parsing primitive:
+// const char *line = "foo.txt:15";
+// const char *filename; int line;
+// if (strparse(line, &filename, ":", &line)) { success...}
+// or:
+// FILE *f = ...;
+// if (fparse(f, &filename, ":", &line)) { success... }
+
+#include <ctype.h>
+#include <gc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "simpleparse.h"
+#include "util.h"
+
+static bool _match_word(const char **str, const char *target) {
+ size_t len = strlen(target);
+ if (strncasecmp(*str, target, len) == 0 && !isalnum((*str)[len]) && (*str)[len] != '_') {
+ *str += len;
+ return true;
+ }
+ return false;
+}
+
+public const char *simpleparse(const char *str, int n, parse_type_e types[n], void *destinations[n])
+{
+ for (int i = 0; i < n; i++) {
+ switch (types[i]) {
+ case PARSE_SOME_OF: {
+ if (destinations[i]) str += strspn(str, (char*)destinations[i]);
+ break;
+ }
+ case PARSE_LITERAL: {
+ const char *target = (const char*)destinations[i];
+ if (target) {
+ if (strncmp(str, target, strlen(target)) != 0)
+ return str;
+ str += strlen(target);
+ }
+ break;
+ }
+ case PARSE_STRING: {
+ size_t len;
+ static const char matching_pair[256] = {[(int)'(']=')', [(int)'{']='}', [(int)'[']=']',
+ [(int)'"']='"', [(int)'\'']='\'', [(int)'`']='`', [(int)'<']='>'};
+ if (i > 0 && i + 1 < n && types[i-1] == PARSE_LITERAL && types[i+1] == PARSE_LITERAL
+ && destinations[i-1] && destinations[i+1]
+ && strlen((char*)destinations[i-1]) == 1 && strlen((char*)destinations[i+1]) == 1
+ && *(char*)destinations[i+1] == matching_pair[(int)*(char*)destinations[i-1]]) {
+ len = 0;
+ char special_characters[4] = {'\\', *(char*)destinations[i-1], *(char*)destinations[i+1], 0};
+ for (int depth = 1; depth > 0; ) {
+ len += strcspn(str + len, special_characters);
+ if (str[len] == '\0') {
+ return str;
+ } else if (str[len] == '\\'
+ && (special_characters[1] == '"' || special_characters[1] == '\'' || special_characters[1] == '`')) {
+ if (str[len+1] == '\0') return str;
+ len += 2;
+ } else if (str[len] == special_characters[2]) { // Check for closing quotes before opening quotes
+ depth -= 1;
+ if (depth > 0) len += 1;
+ } else if (str[len] == special_characters[1]) {
+ depth += 1;
+ if (depth > 999999) return str;
+ len += 1;
+ }
+ }
+ } else if (i + 1 < n && types[i+1] == PARSE_LITERAL) {
+ const char *terminator = (const char*)destinations[i+1];
+ if (terminator) {
+ const char *end = strstr(str, terminator);
+ if (!end) return str;
+ len = (size_t)((ptrdiff_t)end - (ptrdiff_t)str);
+ } else {
+ len = strlen(str);
+ }
+ } else if (i + 1 < n && types[i+1] == PARSE_SOME_OF) {
+ len = destinations[i+1] ? strcspn(str, (char*)destinations[i+1]) : strlen(str);;
+ } else {
+ len = strlen(str);
+ }
+ if (destinations[i]) {
+ char *matched = GC_MALLOC_ATOMIC(len+1);
+ memcpy(matched, str, len);
+ matched[len] = '\0';
+ *(const char**)destinations[i] = matched;
+ }
+ str += len;
+ break;
+ }
+ case PARSE_DOUBLE: {
+ char *end = NULL;
+ double val = strtod(str, &end);
+ if (end == str) return str;
+ if (destinations[i]) *(double*)destinations[i] = val;
+ str = end;
+ break;
+ }
+ case PARSE_LONG: {
+ char *end = NULL;
+ long val = strtol(str, &end, 10);
+ if (end == str) return str;
+ if (destinations[i]) *(long*)destinations[i] = val;
+ str = end;
+ break;
+ }
+ case PARSE_BOOL: {
+ if (_match_word(&str, "true") || _match_word(&str, "yes") || _match_word(&str, "on") || _match_word(&str, "1")) {
+ if (destinations[i]) *(bool*)destinations[i] = true;
+ } else if (_match_word(&str, "false") || _match_word(&str, "no") || _match_word(&str, "off") || _match_word(&str, "0")) {
+ if (destinations[i]) *(bool*)destinations[i] = false;
+ } else {
+ return str;
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+ return NULL;
+}
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/simpleparse.h b/src/stdlib/simpleparse.h
new file mode 100644
index 00000000..1448f74e
--- /dev/null
+++ b/src/stdlib/simpleparse.h
@@ -0,0 +1,51 @@
+#pragma once
+
+// This file defines some functions to make it easy to parse simply formatted
+// strings **correctly** without memory bugs.
+//
+// strparse(input_str, format...) - parse a string
+//
+// Examples:
+//
+// const char *filename; long line_num;
+// const char *err = NULL;
+// if ((err=strparse("foo.c:12", &filename, ":", &line_num)))
+// errx(1, "Failed to parse file:line at: ", err);
+//
+// const char *item1, *item2;
+// if ((err=strparse("one, two", &item1, ",", PARSE_WHITESPACE, &item2)))
+// errx(1, "Failed to parse items at: ", err);
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mapmacro.h"
+
+typedef struct { char c; } some_char_t;
+#define PARSE_SOME_OF(chars) ((some_char_t*)chars)
+#define PARSE_WHITESPACE PARSE_SOME_OF(" \t\r\n\v")
+typedef enum {PARSE_LITERAL, PARSE_LONG, PARSE_DOUBLE, PARSE_BOOL, PARSE_STRING, PARSE_SOME_OF} parse_type_e;
+
+typedef struct { parse_type_e type; void *dest; } parse_element_t;
+
+#define _parse_type(dest) _Generic((dest), \
+ some_char_t*: PARSE_SOME_OF, \
+ const char*: PARSE_LITERAL, \
+ const char**: PARSE_STRING, \
+ char**: PARSE_STRING, \
+ double*: PARSE_DOUBLE, \
+ long*: PARSE_LONG, \
+ bool*: PARSE_BOOL)
+
+#define as_void_star(x) ((void*)x)
+#define strparse(str, ...) simpleparse(str, sizeof((const void*[]){__VA_ARGS__})/sizeof(void*), (parse_type_e[]){MAP_LIST(_parse_type, __VA_ARGS__)}, (void*[]){MAP_LIST(as_void_star, __VA_ARGS__)})
+#define fparse(file, ...) ({ char *_file_contents = NULL; size_t _file_len; \
+ (void)getdelim(&_file_contents, &_file_len, '\0', file); \
+ const char *_parse_err = strparse(_file_contents, __VA_ARGS__); \
+ free(_file_contents); \
+ _parse_err; })
+
+const char *simpleparse(const char *str, int n, parse_type_e types[n], void *destinations[n]);
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c
index bd867c7b..aa5dd119 100644
--- a/src/stdlib/stacktrace.c
+++ b/src/stdlib/stacktrace.c
@@ -1,19 +1,20 @@
-#include <backtrace.h>
+#include <dlfcn.h>
#include <err.h>
+#include <execinfo.h>
#include <gc.h>
-#include <signal.h>
#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include "print.h"
+#include "simpleparse.h"
#include "util.h"
extern bool USE_COLOR;
-static struct backtrace_state *bt_state = NULL;
-
static void fprint_context(FILE *out, const char *filename, int lineno, int context_before, int context_after)
{
FILE *f = fopen(filename, "r");
@@ -44,31 +45,49 @@ static void fprint_context(FILE *out, const char *filename, int lineno, int cont
fclose(f);
}
-typedef struct stack_info_s {
- const char *function, *filename;
- int lineno;
- struct stack_info_s *next;
-} stack_info_t;
-
-// Simple callback to print each frame
-static int print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
+static void _print_stack_frame(FILE *out, const char *cwd, const char *install_dir, const char *function, const char *filename, int lineno)
{
- (void)pc;
- stack_info_t *info = GC_MALLOC(sizeof(stack_info_t));
- info->next = *(stack_info_t**)data;
- info->function = function;
- info->filename = filename;
- info->lineno = lineno;
- *(stack_info_t**)data = info;
- return 0;
+ if (function == NULL) {
+ fprint(out, USE_COLOR ? "\033[2m...unknown function...\033[m" : "...unknown function...");
+ return;
+ }
+
+ function = String(string_slice(function, strcspn(function, "+")));
+ if (function[0] == '\0') function = "???";
+
+ char *function_display = GC_MALLOC_ATOMIC(strlen(function));
+ memcpy(function_display, function, strlen(function)+1);
+ function_display += strcspn(function_display, "$");
+ function_display += strspn(function_display, "$");
+ function_display += strcspn(function_display, "$");
+ function_display += strspn(function_display, "$");
+ for (char *p = function_display; *p; p++) {
+ if (*p == '$') *p = '.';
+ }
+
+ if (filename) {
+ if (strncmp(filename, cwd, strlen(cwd)) == 0)
+ filename += strlen(cwd);
+
+ fprintf(out, USE_COLOR ? "\033[1mIn \033[33m%s()\033[37m" : "In %s()", function_display);
+ if (filename) {
+ if (install_dir[0] && strncmp(filename, install_dir, strlen(install_dir)) == 0)
+ fprintf(out, USE_COLOR ? " in library \033[35m%s:%d" : " in library %s:%d",
+ filename + strlen(install_dir), lineno);
+ else
+ fprintf(out, USE_COLOR ? " in \033[35m%s:%d" : " in %s:%d", filename, lineno);
+ }
+ fprintf(out, USE_COLOR ? "\033[m\n" : "\n");
+ if (filename)
+ fprint_context(out, filename, lineno, 3, 1);
+ } else {
+ fprint(out, "LINE: ", function);
+ }
}
+__attribute__ ((noinline))
public void print_stacktrace(FILE *out, int offset)
{
- stack_info_t *backwards = NULL;
-
- backtrace_full(bt_state, offset, print_callback, NULL, &backwards);
-
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) == NULL)
errx(1, "Path too large!");
@@ -78,49 +97,42 @@ public void print_stacktrace(FILE *out, int offset)
cwd[cwd_len++] = '/';
cwd[cwd_len] = '\0';
- // Skip C entrypoint stuff:
- while (backwards && backwards->function == NULL)
- backwards = backwards->next;
-
- if (backwards && strstr(backwards->function, "$parse_and_run"))
- backwards = backwards->next;
-
- for (stack_info_t *frame = backwards; frame; frame = frame->next) {
- while (frame && frame->function == NULL) {
- fprintf(out, USE_COLOR ? "\033[2m... unknown function ...\033[m\n" : "... unknown function ...\n");
- if (frame->next)
- fprintf(out, "\n");
- frame = frame->next;
- }
- if (frame == NULL) break;
-
- char *function_display = GC_MALLOC_ATOMIC(strlen(frame->function));
- memcpy(function_display, frame->function, strlen(frame->function)+1);
- function_display += strcspn(function_display, "$");
- function_display += strspn(function_display, "$");
- function_display += strcspn(function_display, "$");
- function_display += strspn(function_display, "$");
- for (char *p = function_display; *p; p++) {
- if (*p == '$') *p = '.';
+ const char *install_dir = String(getenv("HOME"), "/.local/share/tomo/installed/");
+
+ static void *stack[1024];
+ int64_t size = (int64_t)backtrace(stack, sizeof(stack)/sizeof(stack[0]));
+ char **strings = backtrace_symbols(stack, size);
+ bool start_printing = false;
+ for (int64_t i = size-1; i > offset; i--) {
+ Dl_info info;
+ void *call_address = stack[i]-1;
+ if (dladdr(call_address, &info) && info.dli_fname) {
+ const char *file = info.dli_fname;
+ uintptr_t frame_offset = (uintptr_t)call_address - (uintptr_t)info.dli_fbase;
+ FILE *fp = popen(String("addr2line -f -e '", file, "' ", (void*)frame_offset, " 2>/dev/null"), "r");
+ if (fp) {
+ const char *function = NULL, *filename = NULL;
+ long line_num = 0;
+ if (fparse(fp, &function, "\n", &filename, ":", &line_num) == NULL) {
+ if (strstr(function, "$main$parse_and_run")) {
+ start_printing = true;
+ continue;
+ }
+ if (start_printing)
+ _print_stack_frame(out, cwd, install_dir, function, filename, line_num);
+ } else {
+ if (start_printing)
+ _print_stack_frame(out, cwd, install_dir, NULL, NULL, line_num);
+ }
+ pclose(fp);
+ }
+ } else {
+ if (start_printing)
+ _print_stack_frame(out, cwd, install_dir, NULL, NULL, 0);
}
-
- if (strncmp(frame->filename, cwd, cwd_len) == 0)
- frame->filename += cwd_len;
-
- fprintf(out, USE_COLOR ? "\033[1mIn \033[33m%s()\033[37m" : "In %s()", function_display);
- if (frame->filename)
- fprintf(out, USE_COLOR ? " in \033[35m%s:%d" : " in %s:%d", frame->filename, frame->lineno);
- fprintf(out, USE_COLOR ? "\033[m\n" : "\n");
- if (frame->filename)
- fprint_context(out, frame->filename, frame->lineno, 3, 1);
- if (frame->next)
- fprintf(out, "\n");
+ if (start_printing && i - 1 > offset) fputs("\n", out);
}
+ free(strings);
}
-public void initialize_stacktrace(const char *program)
-{
- bt_state = backtrace_create_state(program, 1, NULL, NULL);
- if (!bt_state)
- errx(1, "Failed to create stacktrace state");
-}
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/stacktrace.h b/src/stdlib/stacktrace.h
index e3e04814..828bbe98 100644
--- a/src/stdlib/stacktrace.h
+++ b/src/stdlib/stacktrace.h
@@ -1,5 +1,5 @@
#pragma once
#include <stdio.h>
-void initialize_stacktrace(const char *program);
+__attribute__ ((noinline))
void print_stacktrace(FILE *out, int offset);
diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h
index 51983bc8..ae2079e3 100644
--- a/src/stdlib/stdlib.h
+++ b/src/stdlib/stdlib.h
@@ -32,7 +32,7 @@ void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spe
fflush(stdout); \
if (USE_COLOR) fputs("\x1b[31;7m ==================== ERROR ==================== \033[m\n\n", stderr); \
else fputs("==================== ERROR ====================\n\n", stderr); \
- print_stacktrace(stderr, 2); \
+ print_stacktrace(stderr, 1); \
if (USE_COLOR) fputs("\n\x1b[31;1m", stderr); \
else fputs("\n", stderr); \
fprint_inline(stderr, "Error: ", __VA_ARGS__); \
@@ -56,7 +56,7 @@ void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, int spe
fputs("\n", stderr); \
} \
if (USE_COLOR) fputs("\x1b[m", stderr); \
- print_stacktrace(stderr, 2); \
+ print_stacktrace(stderr, 1); \
fflush(stderr); \
raise(SIGABRT); \
_exit(1); \
diff --git a/src/stdlib/text.c b/src/stdlib/text.c
index 3346bf4b..21d1a3db 100644
--- a/src/stdlib/text.c
+++ b/src/stdlib/text.c
@@ -68,6 +68,7 @@
#include <unistring/version.h>
#include <uniwidth.h>
+#include "datatypes.h"
#include "lists.h"
#include "integers.h"
#include "tables.h"
diff --git a/src/tomo.c b/src/tomo.c
index c78af1bf..d26ba73d 100644
--- a/src/tomo.c
+++ b/src/tomo.c
@@ -759,9 +759,7 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t
paths_str(object_files), " -x c - -o ", exe_path);
CORD program = CORD_all(
"extern int ", main_binding->code, "$parse_and_run(int argc, char *argv[]);\n"
- "extern void initialize_stacktrace(const char *program);\n"
"int main(int argc, char *argv[]) {\n"
- "\tinitialize_stacktrace(argv[0]);\n"
"\treturn ", main_binding->code, "$parse_and_run(argc, argv);\n"
"}\n"
);