ACPI AML decompiler w/ CFG recovery and structured pseudocode
29

Configure Feed

Select the types of activity you want to include in your feed.

Expand parser surface validation w argv based cli coverage fuzz corpus discovery and linked list AML seeds

+664 -184
+8
.clang-format
··· 1 + BasedOnStyle: LLVM 2 + IndentWidth: 4 3 + ColumnLimit: 120 4 + BreakBeforeBraces: Attach 5 + AllowShortFunctionsOnASingleLine: Empty 6 + AllowShortIfStatementsOnASingleLine: false 7 + AllowShortLoopsOnASingleLine: false 8 + SortIncludes: false
+11
CMakeLists.txt
··· 14 14 src/rd.c src/p.c src/op.c src/nm.c src/ir.c src/cf.c src/dc.c 15 15 src/da.c src/dg.c src/js.c src/mem.c src/str.c) 16 16 17 + find_program(CLANG_FORMAT_EXE NAMES clang-format) 18 + if(CLANG_FORMAT_EXE) 19 + file(GLOB TOBI_FORMAT_FILES CONFIGURE_DEPENDS 20 + src/*.c src/*.h test/*.c test/*.h fuzz/*.c) 21 + add_custom_target(format 22 + COMMAND ${CLANG_FORMAT_EXE} -i ${TOBI_FORMAT_FILES} 23 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 24 + COMMENT "Format tobi C sources") 25 + endif() 26 + 17 27 add_library(tobilib ${TOBI_COMMON}) 18 28 target_include_directories(tobilib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) 19 29 target_compile_definitions(tobilib PUBLIC _POSIX_C_SOURCE=200809L) 20 30 21 31 add_executable(tobi src/main.c) 22 32 target_link_libraries(tobi PRIVATE tobilib) 33 + target_compile_definitions(tobi PRIVATE TOBI_VERSION="${PROJECT_VERSION}") 23 34 24 35 set(TOBI_WARN -Wall -Wextra -Wpedantic) 25 36 if(TOBI_WERROR)
+23 -9
fuzz/fz.c
··· 1 1 #include "cf.h" 2 2 #include "p.h" 3 3 4 + #include <dirent.h> 4 5 #include <stdint.h> 5 6 #include <stdio.h> 6 7 #include <stdlib.h> 7 8 #include <string.h> 9 + #include <sys/stat.h> 8 10 9 11 #ifndef TOBI_SRC_DIR 10 12 #define TOBI_SRC_DIR "." ··· 47 49 fclose(fp); 48 50 } 49 51 50 - int main(void) { 51 - const char *seed[] = { 52 - TOBI_SRC_DIR "/fuzz/seed/m0.aml", 53 - TOBI_SRC_DIR "/fuzz/seed/m1.aml", 54 - TOBI_SRC_DIR "/fuzz/seed/m2.aml", 55 - TOBI_SRC_DIR "/fuzz/seed/bad0.aml", 56 - }; 57 - for (size_t i = 0; i < sizeof(seed) / sizeof(seed[0]); i++) { 58 - file_one(seed[i]); 52 + static void seed_dir(const char *path) { 53 + DIR *dir = opendir(path); 54 + if (!dir) { 55 + return; 59 56 } 57 + struct dirent *de = NULL; 58 + while ((de = readdir(dir)) != NULL) { 59 + if (de->d_name[0] == '.') { 60 + continue; 61 + } 62 + char file[1024]; 63 + snprintf(file, sizeof(file), "%s/%s", path, de->d_name); 64 + struct stat st; 65 + if (stat(file, &st) == 0 && S_ISREG(st.st_mode)) { 66 + file_one(file); 67 + } 68 + } 69 + closedir(dir); 70 + } 71 + 72 + int main(void) { 73 + seed_dir(TOBI_SRC_DIR "/fuzz/seed"); 60 74 uint32_t x = 0x12345678u; 61 75 for (size_t n = 0; n < 512; n++) { 62 76 uint8_t buf[256];
fuzz/seed/ll.aml

This is a binary file and will not be displayed.

+144 -15
src/cf.c
··· 71 71 return !tobi_diag_has_error(diag); 72 72 } 73 73 74 - static void dot_escape(tobi_sb *sb, const char *s) { 74 + static void dot_escape_limited(tobi_sb *sb, const char *s, size_t wrap, size_t max) { 75 75 if (!s) { 76 76 return; 77 77 } 78 + size_t col = 0; 79 + size_t emitted = 0; 78 80 for (const char *p = s; *p; p++) { 79 - if (*p == '"' || *p == '\\') { 81 + if (max && emitted >= max) { 82 + tobi_sb_add(sb, "\\n..."); 83 + return; 84 + } 85 + unsigned char c = (unsigned char)*p; 86 + if (c == '\n' || c == '\r') { 87 + tobi_sb_add(sb, "\\n"); 88 + col = 0; 89 + continue; 90 + } 91 + if (wrap && col >= wrap) { 92 + tobi_sb_add(sb, "\\n"); 93 + col = 0; 94 + } 95 + if (c == '"' || c == '\\') { 80 96 tobi_sb_ch(sb, '\\'); 81 97 } 82 - if (*p == '\n' || *p == '\r') { 98 + if (c < 0x20) { 83 99 tobi_sb_ch(sb, ' '); 84 100 } else { 85 - tobi_sb_ch(sb, *p); 101 + tobi_sb_ch(sb, (char)c); 86 102 } 103 + col++; 104 + emitted++; 87 105 } 106 + } 107 + 108 + static void dot_escape(tobi_sb *sb, const char *s) { 109 + dot_escape_limited(sb, s, 0, 0); 110 + } 111 + 112 + static void dot_label_text(tobi_sb *sb, const char *s) { 113 + dot_escape_limited(sb, s, 28, 96); 88 114 } 89 115 90 116 typedef struct { ··· 128 154 return emitted; 129 155 } 130 156 131 - static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx) { 132 - size_t id = ctx->stmt_next++; 133 - tobi_sb_printf(sb, " n%zu [label=\"%s", id, tobi_ir_kind_name(n->kind)); 157 + static void dot_ir_label(tobi_sb *sb, const tobi_ir *n) { 158 + tobi_sb_add(sb, tobi_ir_kind_name(n->kind)); 134 159 if (n->name && n->name[0]) { 135 160 tobi_sb_add(sb, "\\n"); 136 - dot_escape(sb, n->name); 161 + dot_label_text(sb, n->name); 162 + } else if (n->path && n->path[0]) { 163 + tobi_sb_add(sb, "\\n"); 164 + dot_label_text(sb, n->path); 137 165 } else if (n->str && n->str[0]) { 138 166 tobi_sb_add(sb, "\\n"); 139 - dot_escape(sb, n->str); 167 + dot_label_text(sb, n->str); 140 168 } 141 169 tobi_sb_printf(sb, "\\noff=0x%zx", n->off); 142 170 if (n->kind == TOBI_IR_UNKNOWN) { 143 171 tobi_sb_printf(sb, "\\nop=0x%x", n->raw_op); 144 172 } 173 + } 174 + 175 + static size_t dot_stmt(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx) { 176 + size_t id = ctx->stmt_next++; 177 + tobi_sb_printf(sb, " n%zu [label=\"", id); 178 + dot_ir_label(sb, n); 145 179 tobi_sb_add(sb, "\"];\n"); 146 180 if (n->kind == TOBI_IR_METHOD && n->child_len > 0) { 147 181 (void)dot_block(sb, n->child[0], id, "entry", ctx, NULL); ··· 167 201 return id; 168 202 } 169 203 170 - char *tobi_cf_dot(const tobi_ir *root) { 204 + static void dot_common_attrs(tobi_sb *sb) { 205 + tobi_sb_add(sb, " graph [rankdir=TB,bgcolor=\"#ffffff\",pad=0.18,nodesep=0.5,ranksep=0.72,"); 206 + tobi_sb_add(sb, "splines=ortho,outputorder=edgesfirst];\n"); 207 + tobi_sb_add(sb, " node [shape=box,style=\"solid\",color=\"#000000\",fontcolor=\"#000000\","); 208 + tobi_sb_add(sb, "fontname=\"Courier\",fontsize=12,margin=\"0.12,0.07\",penwidth=1.1];\n"); 209 + tobi_sb_add(sb, " edge [color=\"#000000\",fontcolor=\"#000000\",arrowsize=0.75,penwidth=1.1];\n"); 210 + } 211 + 212 + static char *dot_cfg(const tobi_ir *root) { 171 213 tobi_sb sb; 172 214 tobi_sb_init(&sb); 173 215 tobi_sb_add(&sb, "digraph tobi_cfg {\n"); 174 - tobi_sb_add(&sb, " graph [rankdir=TB,bgcolor=\"#ffffff\",pad=0.18,nodesep=0.5,ranksep=0.72,"); 175 - tobi_sb_add(&sb, "splines=ortho,outputorder=edgesfirst];\n"); 176 - tobi_sb_add(&sb, " node [shape=box,style=\"solid\",color=\"#000000\",fontcolor=\"#000000\","); 177 - tobi_sb_add(&sb, "fontname=\"Courier\",fontsize=12,margin=\"0.12,0.07\",penwidth=1.1];\n"); 178 - tobi_sb_add(&sb, " edge [color=\"#000000\",fontcolor=\"#000000\",arrowsize=0.75,penwidth=1.1];\n"); 216 + dot_common_attrs(&sb); 179 217 if (root) { 180 218 dot_ctx ctx = {0, 0}; 181 219 size_t root_id = dot_stmt(&sb, root, &ctx); ··· 189 227 tobi_sb_add(&sb, "}\n"); 190 228 return tobi_sb_take(&sb); 191 229 } 230 + 231 + static size_t dot_ast_node(tobi_sb *sb, const tobi_ir *n, dot_ctx *ctx) { 232 + size_t id = ctx->stmt_next++; 233 + tobi_sb_printf(sb, " n%zu [label=\"", id); 234 + dot_ir_label(sb, n); 235 + tobi_sb_add(sb, "\"];\n"); 236 + for (size_t i = 0; i < n->child_len; i++) { 237 + size_t child_id = dot_ast_node(sb, n->child[i], ctx); 238 + tobi_sb_printf(sb, " n%zu -> n%zu;\n", id, child_id); 239 + } 240 + return id; 241 + } 242 + 243 + static char *dot_ast(const tobi_ir *root) { 244 + tobi_sb sb; 245 + tobi_sb_init(&sb); 246 + tobi_sb_add(&sb, "digraph tobi_ast {\n"); 247 + dot_common_attrs(&sb); 248 + if (root) { 249 + dot_ctx ctx = {0, 0}; 250 + (void)dot_ast_node(&sb, root, &ctx); 251 + } 252 + tobi_sb_add(&sb, "}\n"); 253 + return tobi_sb_take(&sb); 254 + } 255 + 256 + static int ns_index(const tobi_ns *ns, const char *path, size_t *idx) { 257 + if (!ns || !path) { 258 + return 0; 259 + } 260 + for (size_t i = 0; i < ns->len; i++) { 261 + if (ns->items[i].path && strcmp(ns->items[i].path, path) == 0) { 262 + *idx = i; 263 + return 1; 264 + } 265 + } 266 + return 0; 267 + } 268 + 269 + static char *dot_namespace(const tobi_ns *ns) { 270 + tobi_sb sb; 271 + tobi_sb_init(&sb); 272 + tobi_sb_add(&sb, "digraph tobi_namespace {\n"); 273 + dot_common_attrs(&sb); 274 + tobi_sb_add(&sb, " n0 [label=\"namespace\\n\\\\\"];\n"); 275 + if (ns) { 276 + for (size_t i = 0; i < ns->len; i++) { 277 + const tobi_ns_ent *e = &ns->items[i]; 278 + tobi_sb_printf(&sb, " n%zu [label=\"%s", i + 1u, tobi_ns_kind_name(e->kind)); 279 + if (e->name && e->name[0]) { 280 + tobi_sb_add(&sb, "\\n"); 281 + dot_label_text(&sb, e->name); 282 + } 283 + if (e->path && e->path[0]) { 284 + tobi_sb_add(&sb, "\\n"); 285 + dot_label_text(&sb, e->path); 286 + } 287 + tobi_sb_printf(&sb, "\\noff=0x%zx\"];\n", e->off); 288 + } 289 + for (size_t i = 0; i < ns->len; i++) { 290 + const tobi_ns_ent *e = &ns->items[i]; 291 + size_t parent = 0; 292 + size_t found = 0; 293 + if (e->owner && ns_index(ns, e->owner, &found)) { 294 + parent = found + 1u; 295 + } 296 + tobi_sb_printf(&sb, " n%zu -> n%zu;\n", parent, i + 1u); 297 + if (e->target && ns_index(ns, e->target, &found)) { 298 + tobi_sb_printf(&sb, " n%zu -> n%zu [style=dashed,arrowhead=vee];\n", i + 1u, found + 1u); 299 + } 300 + } 301 + } 302 + tobi_sb_add(&sb, "}\n"); 303 + return tobi_sb_take(&sb); 304 + } 305 + 306 + char *tobi_cf_dot(const tobi_ir *root) { 307 + return dot_cfg(root); 308 + } 309 + 310 + char *tobi_graph_dot(const tobi_ir *root, const tobi_ns *ns, tobi_graph_kind kind) { 311 + switch (kind) { 312 + case TOBI_GRAPH_AST: 313 + return dot_ast(root); 314 + case TOBI_GRAPH_NAMESPACE: 315 + return dot_namespace(ns); 316 + case TOBI_GRAPH_CFG: 317 + default: 318 + return dot_cfg(root); 319 + } 320 + }
+9
src/cf.h
··· 3 3 4 4 #include "dg.h" 5 5 #include "ir.h" 6 + #include "nm.h" 7 + 8 + typedef enum tobi_graph_kind { 9 + TOBI_GRAPH_CFG, 10 + TOBI_GRAPH_AST, 11 + TOBI_GRAPH_NAMESPACE 12 + } tobi_graph_kind; 6 13 7 14 int tobi_cf_recover(tobi_ir *root, tobi_diag_list *diag); 8 15 9 16 char *tobi_cf_dot(const tobi_ir *root); 17 + 18 + char *tobi_graph_dot(const tobi_ir *root, const tobi_ns *ns, tobi_graph_kind kind); 10 19 11 20 #endif
+1 -1
src/js.c
··· 136 136 tobi_sb_ch(sb, '{'); 137 137 tobi_sb_printf(sb, "\"is_table\":%s,", meta->is_table ? "true" : "false"); 138 138 tobi_sb_add(sb, "\"source\":"); 139 - tobi_json_string(sb, meta->source); 139 + tobi_json_string(sb, meta->source ? meta->source : "raw"); 140 140 tobi_sb_add(sb, ",\"signature\":"); 141 141 tobi_json_string(sb, meta->signature); 142 142 tobi_sb_printf(sb, ",\"table_length\":%zu,\"aml_offset\":%zu,\"aml_length\":%zu,"
+190 -53
src/main.c
··· 11 11 #include <string.h> 12 12 #include <unistd.h> 13 13 14 - #define TOBI_VERSION "0.1.0" 14 + #ifndef TOBI_VERSION 15 + #define TOBI_VERSION "0.0.0" 16 + #endif 15 17 16 18 static void usage(FILE *f) { 17 19 fputs("usage: tobi [opts] <file> [file...]\n" ··· 20 22 " --dis print opcode-level disassembly\n" 21 23 " --json print parsed ir as json\n" 22 24 " --dot print recovered control flow as Graphviz DOT\n" 25 + " --graph K print DOT graph: cfg, ast, namespace\n" 23 26 " --raw print input/table metadata and aml offsets\n" 27 + " -o FILE write primary output to FILE instead of stdout\n" 24 28 " --strict treat malformed or unsupported constructs as hard errors\n" 25 29 " --plain disable coloured diagnostics\n" 26 30 " --help print usage\n" 27 - " --version print version\n", f); 31 + " --version print version\n" 32 + "\n" 33 + "Use '-' as an input path to read AML from stdin.\n", f); 34 + } 35 + 36 + static int append_file(const char ***files, size_t *file_len, size_t *file_cap, const char *path) { 37 + if (*file_len == *file_cap) { 38 + *file_cap = tobi_xgrow_cap(*file_cap, *file_len + 1u, 4u); 39 + *files = tobi_xrealloc((void *)*files, tobi_xmul_size(*file_cap, sizeof((*files)[0]))); 40 + } 41 + (*files)[(*file_len)++] = path; 42 + return 1; 43 + } 44 + 45 + static const char *source_label(const char *path) { 46 + return path && strcmp(path, "-") == 0 ? "<stdin>" : path; 47 + } 48 + 49 + static int read_stream(FILE *fp, const char *label, int close_stream, uint8_t **data, size_t *len) { 50 + size_t cap = 4096; 51 + size_t used = 0; 52 + uint8_t *buf = tobi_xmalloc(cap); 53 + for (;;) { 54 + if (used == cap) { 55 + cap = tobi_xgrow_cap(cap, tobi_xadd_size(used, 1u), 4096u); 56 + buf = tobi_xrealloc(buf, cap); 57 + } 58 + size_t room = cap - used; 59 + size_t got = fread(buf + used, 1, room, fp); 60 + if (got > room) { 61 + fprintf(stderr, "tobi: %s: read exceeded buffer\n", label); 62 + free(buf); 63 + if (close_stream) { 64 + fclose(fp); 65 + } 66 + return 0; 67 + } 68 + used += got; 69 + if (got < room) { 70 + if (ferror(fp)) { 71 + fprintf(stderr, "tobi: %s: read failed: %s\n", label, strerror(errno)); 72 + free(buf); 73 + if (close_stream) { 74 + fclose(fp); 75 + } 76 + return 0; 77 + } 78 + break; 79 + } 80 + } 81 + if (close_stream && fclose(fp) != 0) { 82 + fprintf(stderr, "tobi: %s: close failed: %s\n", label, strerror(errno)); 83 + free(buf); 84 + return 0; 85 + } 86 + *data = buf; 87 + *len = used; 88 + return 1; 28 89 } 29 90 30 91 static int read_file(const char *path, uint8_t **data, size_t *len) { 92 + if (strcmp(path, "-") == 0) { 93 + return read_stream(stdin, "<stdin>", 0, data, len); 94 + } 31 95 FILE *fp = fopen(path, "rb"); 32 96 if (!fp) { 33 97 fprintf(stderr, "tobi: %s: %s\n", path, strerror(errno)); ··· 66 130 return 1; 67 131 } 68 132 133 + static FILE *open_output(const char *path) { 134 + if (!path || strcmp(path, "-") == 0) { 135 + return stdout; 136 + } 137 + FILE *fp = fopen(path, "wb"); 138 + if (!fp) { 139 + fprintf(stderr, "tobi: %s: %s\n", path, strerror(errno)); 140 + return NULL; 141 + } 142 + return fp; 143 + } 144 + 145 + static int close_output(FILE *fp, const char *path) { 146 + if (!fp || fp == stdout) { 147 + return 1; 148 + } 149 + if (fclose(fp) != 0) { 150 + fprintf(stderr, "tobi: %s: close failed: %s\n", path, strerror(errno)); 151 + return 0; 152 + } 153 + return 1; 154 + } 155 + 156 + static int parse_graph_kind(const char *s, tobi_graph_kind *kind) { 157 + if (strcmp(s, "cfg") == 0) { 158 + *kind = TOBI_GRAPH_CFG; 159 + return 1; 160 + } 161 + if (strcmp(s, "ast") == 0) { 162 + *kind = TOBI_GRAPH_AST; 163 + return 1; 164 + } 165 + if (strcmp(s, "namespace") == 0 || strcmp(s, "ns") == 0) { 166 + *kind = TOBI_GRAPH_NAMESPACE; 167 + return 1; 168 + } 169 + return 0; 170 + } 171 + 69 172 static void print_diags(const tobi_diag_list *dl, int plain) { 70 173 int colour = !plain && isatty(fileno(stderr)); 71 174 for (size_t i = 0; i < dl->len; i++) { ··· 86 189 if (!path) { 87 190 return; 88 191 } 89 - (void)snprintf(meta->source, sizeof(meta->source), "%s", path); 192 + free(meta->source); 193 + meta->source = tobi_xstrdup(path); 90 194 } 91 195 92 196 static void apply_source(tobi_parse_result *res, const char *path, size_t input_index) { ··· 101 205 } 102 206 } 103 207 104 - static void print_raw_meta(const char *input, const tobi_parse_result *res) { 105 - printf("input=%s\nsource=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\nrevision=%u\nchecksum_byte=0x%02x\nchecksum_sum=0x%02x\nchecksum_valid=%s\noem_id=%s\noem_table_id=%s\noem_revision=%u\ncreator_id=%s\ncreator_revision=%u\nheader_hash=0x%08x\naml_hash=0x%08x\nnamespace_objects=%zu\n", 106 - input, res->meta.source, res->meta.signature, res->meta.is_table ? "true" : "false", 107 - res->meta.table_len, res->meta.aml_off, res->meta.aml_len, 108 - (unsigned)res->meta.revision, (unsigned)res->meta.checksum_byte, 109 - (unsigned)res->meta.checksum_sum, res->meta.checksum_valid ? "true" : "false", 110 - res->meta.oem_id, res->meta.oem_table_id, res->meta.oem_revision, 111 - res->meta.creator_id, res->meta.creator_revision, res->meta.header_hash, 112 - res->meta.aml_hash, res->ns.len); 208 + static void print_raw_meta(FILE *out, const char *input, const tobi_parse_result *res) { 209 + fprintf(out, "input=%s\nsource=%s\nformat=%s\nis_table=%s\ntable_length=%zu\naml_offset=%zu\naml_length=%zu\nrevision=%u\nchecksum_byte=0x%02x\nchecksum_sum=0x%02x\nchecksum_valid=%s\noem_id=%s\noem_table_id=%s\noem_revision=%u\ncreator_id=%s\ncreator_revision=%u\nheader_hash=0x%08x\naml_hash=0x%08x\nnamespace_objects=%zu\n", 210 + input, res->meta.source ? res->meta.source : "raw", res->meta.signature, 211 + res->meta.is_table ? "true" : "false", res->meta.table_len, 212 + res->meta.aml_off, res->meta.aml_len, (unsigned)res->meta.revision, 213 + (unsigned)res->meta.checksum_byte, (unsigned)res->meta.checksum_sum, 214 + res->meta.checksum_valid ? "true" : "false", res->meta.oem_id, 215 + res->meta.oem_table_id, res->meta.oem_revision, res->meta.creator_id, 216 + res->meta.creator_revision, res->meta.header_hash, res->meta.aml_hash, res->ns.len); 217 + } 218 + 219 + static void free_inputs(uint8_t **data, size_t *lens, size_t file_len, const char **files) { 220 + (void)lens; 221 + for (size_t i = 0; i < file_len; i++) { 222 + free(data[i]); 223 + } 224 + free(lens); 225 + free(data); 226 + free(files); 113 227 } 114 228 115 229 int main(int argc, char **argv) { 116 230 int mode_dis = 0; 117 231 int mode_json = 0; 118 - int mode_dot = 0; 232 + int mode_graph = 0; 119 233 int mode_raw = 0; 120 234 int strict = 0; 121 235 int plain = 0; 236 + int stdin_count = 0; 237 + const char *output_path = NULL; 238 + tobi_graph_kind graph_kind = TOBI_GRAPH_CFG; 122 239 const char **files = NULL; 123 240 size_t file_len = 0; 124 241 size_t file_cap = 0; ··· 136 253 } else if (strcmp(argv[i], "--json") == 0) { 137 254 mode_json = 1; 138 255 } else if (strcmp(argv[i], "--dot") == 0) { 139 - mode_dot = 1; 256 + mode_graph = 1; 257 + graph_kind = TOBI_GRAPH_CFG; 258 + } else if (strcmp(argv[i], "--graph") == 0) { 259 + if (i + 1 >= argc) { 260 + fprintf(stderr, "tobi: --graph requires cfg, ast, or namespace\n"); 261 + free(files); 262 + return 2; 263 + } 264 + if (!parse_graph_kind(argv[++i], &graph_kind)) { 265 + fprintf(stderr, "tobi: unknown graph kind %s\n", argv[i]); 266 + free(files); 267 + return 2; 268 + } 269 + mode_graph = 1; 140 270 } else if (strcmp(argv[i], "--raw") == 0) { 141 271 mode_raw = 1; 272 + } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--output") == 0) { 273 + if (i + 1 >= argc) { 274 + fprintf(stderr, "tobi: %s requires a file\n", argv[i]); 275 + free(files); 276 + return 2; 277 + } 278 + output_path = argv[++i]; 142 279 } else if (strcmp(argv[i], "--strict") == 0) { 143 280 strict = 1; 144 281 } else if (strcmp(argv[i], "--plain") == 0) { 145 282 plain = 1; 283 + } else if (strcmp(argv[i], "-") == 0) { 284 + stdin_count++; 285 + (void)append_file(&files, &file_len, &file_cap, argv[i]); 146 286 } else if (argv[i][0] == '-') { 147 287 fprintf(stderr, "tobi: unknown option %s\n", argv[i]); 148 288 usage(stderr); 149 289 return 2; 150 290 } else { 151 - if (file_len == file_cap) { 152 - file_cap = tobi_xgrow_cap(file_cap, file_len + 1u, 4u); 153 - files = tobi_xrealloc(files, tobi_xmul_size(file_cap, sizeof(files[0]))); 154 - } 155 - files[file_len++] = argv[i]; 291 + (void)append_file(&files, &file_len, &file_cap, argv[i]); 156 292 } 157 293 } 158 294 if (file_len == 0) { ··· 160 296 free(files); 161 297 return 2; 162 298 } 163 - if ((mode_dis + mode_json + mode_dot + mode_raw) > 1) { 299 + if (stdin_count > 1) { 300 + fprintf(stderr, "tobi: stdin can only be used once\n"); 301 + free(files); 302 + return 2; 303 + } 304 + if ((mode_dis + mode_json + mode_graph + mode_raw) > 1) { 164 305 fprintf(stderr, "tobi: choose only one output mode\n"); 165 306 free(files); 166 307 return 2; 167 308 } 309 + FILE *outfp = open_output(output_path); 310 + if (!outfp) { 311 + free(files); 312 + return 1; 313 + } 168 314 uint8_t **data = tobi_xcalloc(file_len, sizeof(data[0])); 169 315 size_t *lens = tobi_xcalloc(file_len, sizeof(lens[0])); 170 316 for (size_t i = 0; i < file_len; i++) { 171 317 if (!read_file(files[i], &data[i], &lens[i])) { 172 - for (size_t j = 0; j < file_len; j++) { 173 - free(data[j]); 174 - } 175 - free(lens); 176 - free(data); 177 - free(files); 318 + (void)close_output(outfp, output_path); 319 + free_inputs(data, lens, file_len, files); 178 320 return 1; 179 321 } 180 322 } 181 323 if (mode_dis || mode_raw) { 182 324 int fail_any = 0; 183 325 for (size_t i = 0; i < file_len; i++) { 326 + const char *label = source_label(files[i]); 184 327 tobi_parse_result one; 185 328 int ok = tobi_parse(data[i], lens[i], strict, &one); 186 - apply_source(&one, files[i], 0); 329 + apply_source(&one, label, 0); 187 330 if (mode_raw) { 188 - print_raw_meta(files[i], &one); 331 + print_raw_meta(outfp, label, &one); 189 332 } else { 190 333 if (file_len > 1) { 191 - printf("== %s ==\n", files[i]); 334 + fprintf(outfp, "== %s ==\n", label); 192 335 } 193 336 char *s = tobi_da_emit(data[i], lens[i], &one.meta); 194 - fputs(s, stdout); 337 + fputs(s, outfp); 195 338 free(s); 196 339 } 197 340 print_diags(&one.diag, plain); ··· 200 343 } 201 344 tobi_parse_result_free(&one); 202 345 } 203 - for (size_t i = 0; i < file_len; i++) { 204 - free(data[i]); 346 + int output_ok = close_output(outfp, output_path); 347 + free_inputs(data, lens, file_len, files); 348 + if (!output_ok) { 349 + return 1; 205 350 } 206 - free(lens); 207 - free(data); 208 - free(files); 209 351 return fail_any ? 1 : 0; 210 352 } 211 353 tobi_parse_result res; 212 354 int ok = 0; 213 355 if (file_len == 1) { 214 356 ok = tobi_parse(data[0], lens[0], strict, &res); 215 - apply_source(&res, files[0], 0); 357 + apply_source(&res, source_label(files[0]), 0); 216 358 } else { 217 359 tobi_parse_input *inputs = tobi_xcalloc(file_len, sizeof(inputs[0])); 218 360 for (size_t i = 0; i < file_len; i++) { 219 361 inputs[i].data = data[i]; 220 362 inputs[i].len = lens[i]; 221 - inputs[i].name = files[i]; 363 + inputs[i].name = source_label(files[i]); 222 364 } 223 365 ok = tobi_parse_multi(inputs, file_len, strict, &res); 224 366 free(inputs); ··· 226 368 if (!res.root) { 227 369 print_diags(&res.diag, plain); 228 370 tobi_parse_result_free(&res); 229 - for (size_t i = 0; i < file_len; i++) { 230 - free(data[i]); 231 - } 232 - free(lens); 233 - free(data); 234 - free(files); 371 + (void)close_output(outfp, output_path); 372 + free_inputs(data, lens, file_len, files); 235 373 return 1; 236 374 } 237 375 (void)tobi_cf_recover(res.root, &res.diag); 238 376 if (mode_json) { 239 377 char *s = tobi_js_emit(&res); 240 - fputs(s, stdout); 378 + fputs(s, outfp); 241 379 free(s); 242 - } else if (mode_dot) { 243 - char *s = tobi_cf_dot(res.root); 244 - fputs(s, stdout); 380 + } else if (mode_graph) { 381 + char *s = tobi_graph_dot(res.root, &res.ns, graph_kind); 382 + fputs(s, outfp); 245 383 free(s); 246 384 } else { 247 385 char *s = tobi_dc_emit(res.root, &res.diag); 248 - fputs(s, stdout); 386 + fputs(s, outfp); 249 387 free(s); 250 388 } 251 389 print_diags(&res.diag, plain); 252 390 int fail = (strict && tobi_diag_has_error(&res.diag)) || !ok; 253 391 tobi_parse_result_free(&res); 254 - for (size_t i = 0; i < file_len; i++) { 255 - free(data[i]); 392 + int output_ok = close_output(outfp, output_path); 393 + free_inputs(data, lens, file_len, files); 394 + if (!output_ok) { 395 + return 1; 256 396 } 257 - free(lens); 258 - free(data); 259 - free(files); 260 397 return fail ? 1 : 0; 261 398 }
+39 -64
src/p.c
··· 71 71 return memcmp(d, "DSDT", 4) == 0 || memcmp(d, "SSDT", 4) == 0; 72 72 } 73 73 74 + static void input_meta_set_source(tobi_input_meta *meta, const char *source) { 75 + free(meta->source); 76 + meta->source = tobi_xstrdup(source && source[0] ? source : "raw"); 77 + } 78 + 79 + static void input_meta_copy(tobi_input_meta *dst, const tobi_input_meta *src) { 80 + *dst = *src; 81 + dst->source = src->source ? tobi_xstrdup(src->source) : NULL; 82 + } 83 + 84 + static void input_meta_free(tobi_input_meta *meta) { 85 + free(meta->source); 86 + meta->source = NULL; 87 + } 88 + 74 89 static void parser_free(parser *p) { 75 90 free(p->scope); 76 91 } ··· 158 173 meta->aml_len = len; 159 174 memcpy(meta->signature, "RAW", 4); 160 175 meta->signature[4] = '\0'; 161 - (void)snprintf(meta->source, sizeof(meta->source), "%s", has_source && source ? source : "raw"); 176 + input_meta_set_source(meta, has_source && source ? source : "raw"); 162 177 if (len >= 4 && is_table_sig(data)) { 163 178 memcpy(meta->signature, data, 4); 164 179 meta->signature[4] = '\0'; ··· 648 663 } 649 664 } 650 665 651 - static tobi_ir *parse_named_ext_expr(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, 652 - uint32_t raw, const char *name, unsigned expr_count, 653 - unsigned name_count) { 666 + static tobi_ir *parse_op_expr(parser *p, tobi_rd *rd, unsigned depth, size_t op_start, 667 + uint32_t raw, const tobi_op_expr *info) { 654 668 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, 0); 655 669 n->raw_op = raw; 656 - tobi_ir_set_name(n, name); 657 - add_expr_children(p, rd, depth, n, expr_count); 658 - for (unsigned i = 0; i < name_count && tobi_rd_left(rd) > 0; i++) { 659 - tobi_ir *ref = ref_from_name(p, rd, op_start, "bad extended opcode namestring"); 670 + tobi_ir_set_name(n, info->ir_name); 671 + add_expr_children(p, rd, depth, n, info->arg_count); 672 + add_expr_children(p, rd, depth, n, info->target_count); 673 + for (unsigned i = 0; i < info->name_count && tobi_rd_left(rd) > 0; i++) { 674 + tobi_ir *ref = ref_from_name(p, rd, op_start, "bad expression namestring"); 660 675 if (ref) { 676 + if (ref->path && ref->path[0] == '\\' && strncmp(info->ir_name, "create_", 7) == 0) { 677 + ns_declare_path(p, TOBI_NS_FIELD, ref->path, ref->off, 0, raw, 0, NULL); 678 + } 661 679 tobi_ir_add(n, ref); 662 680 } 663 681 } ··· 677 695 if (target) { 678 696 tobi_ir_add(n, target); 679 697 } 680 - n->len = tobi_rd_off(rd) - op_start; 681 - return n; 682 - } 683 - 684 - static tobi_ir *parse_ext_load_table(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { 685 - tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, 0); 686 - n->raw_op = 0x5b1fu; 687 - tobi_ir_set_name(n, "load_table"); 688 - add_expr_children(p, rd, depth, n, 6); 689 698 n->len = tobi_rd_off(rd) - op_start; 690 699 return n; 691 700 } ··· 823 832 return parse_ext_pkg_scope(p, rd, depth, op_start, raw, TOBI_IR_SCOPE, "PowerResOp", 2); 824 833 case 0x85: 825 834 return parse_ext_pkg_scope(p, rd, depth, op_start, raw, TOBI_IR_SCOPE, "ThermalZoneOp", 0); 826 - case 0x12: 827 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "cond_ref_of", 1, 1); 828 - case 0x13: 829 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "create_field", 3, 1); 830 - case 0x1f: 831 - return parse_ext_load_table(p, rd, depth, op_start); 832 835 case 0x20: 833 836 return parse_ext_load(p, rd, depth, op_start); 834 - case 0x21: 835 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "stall", 1, 0); 836 - case 0x22: 837 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "sleep", 1, 0); 838 - case 0x23: 839 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "acquire", 2, 0); 840 - case 0x24: 841 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "signal", 1, 0); 842 - case 0x25: 843 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "wait", 2, 0); 844 - case 0x26: 845 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "reset", 1, 0); 846 - case 0x27: 847 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "release", 1, 0); 848 - case 0x28: 849 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "from_bcd", 2, 0); 850 - case 0x29: 851 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "to_bcd", 2, 0); 852 - case 0x2a: 853 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "unload", 1, 0); 854 837 case 0x30: { 855 838 tobi_ir *n = tobi_ir_new(TOBI_IR_INTEGER, op_start, tobi_rd_off(rd) - op_start); 856 839 n->value = 2; ··· 862 845 tobi_ir_set_name(n, "Debug"); 863 846 return n; 864 847 } 865 - case 0x32: 866 - return parse_named_ext_expr(p, rd, depth, op_start, raw, "fatal", 3, 0); 867 848 case 0x33: { 868 849 tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, op_start, tobi_rd_off(rd) - op_start); 869 850 n->raw_op = raw; ··· 915 896 return n; 916 897 } 917 898 default: 918 - return unknown_node(p, op_start, raw, 2, "unsupported extended opcode"); 899 + break; 900 + } 901 + const tobi_op_expr *info = tobi_op_expr_find((uint16_t)raw); 902 + if (info) { 903 + return parse_op_expr(p, rd, depth, op_start, raw, info); 919 904 } 905 + return unknown_node(p, op_start, raw, 2, "unsupported extended opcode"); 920 906 } 921 907 922 908 static tobi_ir *parse_if(parser *p, tobi_rd *rd, unsigned depth, size_t op_start) { ··· 1365 1351 } 1366 1352 const tobi_op_expr *info = tobi_op_expr_find(op); 1367 1353 if (info) { 1368 - tobi_ir *n = tobi_ir_new(TOBI_IR_EXPR, off, 0); 1369 - n->raw_op = op; 1370 - tobi_ir_set_name(n, info->ir_name); 1371 - add_expr_children(p, rd, depth, n, info->arg_count); 1372 - add_expr_children(p, rd, depth, n, info->target_count); 1373 - for (unsigned i = 0; i < info->name_count; i++) { 1374 - tobi_ir *dst = ref_from_name(p, rd, off, "bad expression namestring"); 1375 - if (dst) { 1376 - if (dst->path && dst->path[0] == '\\' && strncmp(info->ir_name, "create_", 7) == 0) { 1377 - ns_declare_path(p, TOBI_NS_FIELD, dst->path, dst->off, 0, op, 0, NULL); 1378 - } 1379 - tobi_ir_add(n, dst); 1380 - } 1381 - } 1382 - n->len = tobi_rd_off(rd) - off; 1383 - return n; 1354 + return parse_op_expr(p, rd, depth, off, op, info); 1384 1355 } 1385 1356 rd->pos--; 1386 1357 uint8_t b = 0; ··· 1906 1877 return 0; 1907 1878 } 1908 1879 out->inputs = tobi_xcalloc(1u, sizeof(out->inputs[0])); 1909 - out->inputs[0] = out->meta; 1880 + input_meta_copy(&out->inputs[0], &out->meta); 1910 1881 out->input_len = 1; 1911 1882 out->root = tobi_ir_new(TOBI_IR_ROOT, out->meta.aml_off, out->meta.aml_len); 1912 1883 tobi_ir_set_scope(out->root, "\\"); ··· 1940 1911 tobi_ns_init(&out->ns); 1941 1912 memcpy(out->meta.signature, "MULT", 4); 1942 1913 out->meta.signature[4] = '\0'; 1943 - memcpy(out->meta.source, "multi", 6); 1914 + input_meta_set_source(&out->meta, "multi"); 1944 1915 out->root = tobi_ir_new(TOBI_IR_ROOT, 0, 0); 1945 1916 tobi_ir_set_scope(out->root, "\\"); 1946 1917 tobi_ir_set_source(out->root, "multi", 0); ··· 2003 1974 void tobi_parse_result_free(tobi_parse_result *res) { 2004 1975 tobi_ir_free(res->root); 2005 1976 res->root = NULL; 1977 + input_meta_free(&res->meta); 1978 + for (size_t i = 0; i < res->input_len; i++) { 1979 + input_meta_free(&res->inputs[i]); 1980 + } 2006 1981 free(res->inputs); 2007 1982 res->inputs = NULL; 2008 1983 res->input_len = 0;
+1 -1
src/p.h
··· 11 11 typedef struct tobi_input_meta { 12 12 int is_table; 13 13 char signature[5]; 14 - char source[128]; 14 + char *source; 15 15 char oem_id[7]; 16 16 char oem_table_id[9]; 17 17 char creator_id[5];
+10
test/t_cf.c
··· 49 49 T_STR(dot, "while"); 50 50 T_STR(dot, "back"); 51 51 free(dot); 52 + dot = tobi_graph_dot(r.root, &r.ns, TOBI_GRAPH_AST); 53 + T_STR(dot, "digraph tobi_ast"); 54 + T_STR(dot, "LL00"); 55 + T_STR(dot, "expr"); 56 + free(dot); 57 + dot = tobi_graph_dot(r.root, &r.ns, TOBI_GRAPH_NAMESPACE); 58 + T_STR(dot, "digraph tobi_namespace"); 59 + T_STR(dot, "LL00"); 60 + T_STR(dot, "method"); 61 + free(dot); 52 62 tobi_parse_result_free(&r); 53 63 54 64 uint8_t amb[] = {0xa0,0x03,0x01,0xa1};
+228 -41
test/t_cli.c
··· 1 1 #include "t.h" 2 2 3 + #include <errno.h> 4 + #include <fcntl.h> 3 5 #include <sys/wait.h> 6 + #include <unistd.h> 4 7 5 8 #ifndef TOBI_BIN 6 9 #define TOBI_BIN "./tobi" ··· 9 12 #define TOBI_SRC_DIR "." 10 13 #endif 11 14 15 + static char *empty_text(void) { 16 + char *buf = malloc(1); 17 + if (!buf) { 18 + abort(); 19 + } 20 + buf[0] = '\0'; 21 + return buf; 22 + } 23 + 12 24 static int write_blob(const char *path, const unsigned char *data, size_t len) { 13 25 FILE *fp = fopen(path, "wb"); 14 26 if (!fp) { ··· 18 30 return fclose(fp) == 0 && ok; 19 31 } 20 32 21 - static char *run_capture(const char *args, int *status) { 22 - char cmd[1024]; 23 - snprintf(cmd, sizeof(cmd), "%s %s 2>/dev/null", TOBI_BIN, args); 24 - FILE *fp = popen(cmd, "r"); 33 + static char *read_text(const char *path) { 34 + FILE *fp = fopen(path, "rb"); 25 35 if (!fp) { 26 - *status = 99; 27 - return NULL; 36 + return empty_text(); 37 + } 38 + if (fseek(fp, 0, SEEK_END) != 0) { 39 + fclose(fp); 40 + return empty_text(); 41 + } 42 + long n = ftell(fp); 43 + if (n < 0) { 44 + fclose(fp); 45 + return empty_text(); 28 46 } 47 + rewind(fp); 48 + char *buf = malloc((size_t)n + 1u); 49 + if (!buf) { 50 + fclose(fp); 51 + abort(); 52 + } 53 + size_t got = fread(buf, 1, (size_t)n, fp); 54 + buf[got] = '\0'; 55 + fclose(fp); 56 + return buf; 57 + } 58 + 59 + static int write_all_fd(int fd, const unsigned char *data, size_t len) { 60 + size_t off = 0; 61 + while (off < len) { 62 + ssize_t n = write(fd, data + off, len - off); 63 + if (n < 0) { 64 + if (errno == EINTR) { 65 + continue; 66 + } 67 + return 0; 68 + } 69 + off += (size_t)n; 70 + } 71 + return 1; 72 + } 73 + 74 + static char *read_all_fd(int fd) { 29 75 size_t cap = 4096; 30 76 size_t len = 0; 31 77 char *buf = malloc(cap); 32 78 if (!buf) { 33 - (void)pclose(fp); 34 - *status = 99; 35 - return NULL; 79 + abort(); 36 80 } 37 - int c = 0; 38 - while ((c = fgetc(fp)) != EOF) { 81 + for (;;) { 39 82 if (len + 1 == cap) { 40 - cap *= 2; 83 + cap *= 2u; 41 84 char *nb = realloc(buf, cap); 42 85 if (!nb) { 43 86 free(buf); 44 - (void)pclose(fp); 45 - *status = 99; 46 - return NULL; 87 + abort(); 47 88 } 48 89 buf = nb; 49 90 } 50 - buf[len++] = (char)c; 91 + ssize_t n = read(fd, buf + len, cap - len - 1u); 92 + if (n < 0) { 93 + if (errno == EINTR) { 94 + continue; 95 + } 96 + free(buf); 97 + return empty_text(); 98 + } 99 + if (n == 0) { 100 + break; 101 + } 102 + len += (size_t)n; 51 103 } 52 104 buf[len] = '\0'; 53 - int rc = pclose(fp); 105 + return buf; 106 + } 107 + 108 + static char *run_capture_argv(const char *const argv[], const unsigned char *input, size_t input_len, int *status) { 109 + int out_pipe[2]; 110 + int in_pipe[2] = {-1, -1}; 111 + if (pipe(out_pipe) != 0) { 112 + *status = 99; 113 + return empty_text(); 114 + } 115 + if (input && pipe(in_pipe) != 0) { 116 + close(out_pipe[0]); 117 + close(out_pipe[1]); 118 + *status = 99; 119 + return empty_text(); 120 + } 121 + pid_t pid = fork(); 122 + if (pid < 0) { 123 + close(out_pipe[0]); 124 + close(out_pipe[1]); 125 + if (input) { 126 + close(in_pipe[0]); 127 + close(in_pipe[1]); 128 + } 129 + *status = 99; 130 + return empty_text(); 131 + } 132 + if (pid == 0) { 133 + if (input) { 134 + dup2(in_pipe[0], STDIN_FILENO); 135 + } else { 136 + int devnull = open("/dev/null", O_RDONLY); 137 + if (devnull >= 0) { 138 + dup2(devnull, STDIN_FILENO); 139 + close(devnull); 140 + } 141 + } 142 + dup2(out_pipe[1], STDOUT_FILENO); 143 + int devnull = open("/dev/null", O_WRONLY); 144 + if (devnull >= 0) { 145 + dup2(devnull, STDERR_FILENO); 146 + close(devnull); 147 + } 148 + close(out_pipe[0]); 149 + close(out_pipe[1]); 150 + if (input) { 151 + close(in_pipe[0]); 152 + close(in_pipe[1]); 153 + } 154 + execv(argv[0], (char *const *)argv); 155 + _exit(127); 156 + } 157 + close(out_pipe[1]); 158 + if (input) { 159 + close(in_pipe[0]); 160 + if (!write_all_fd(in_pipe[1], input, input_len)) { 161 + close(in_pipe[1]); 162 + close(out_pipe[0]); 163 + (void)waitpid(pid, NULL, 0); 164 + *status = 99; 165 + return empty_text(); 166 + } 167 + close(in_pipe[1]); 168 + } 169 + char *buf = read_all_fd(out_pipe[0]); 170 + close(out_pipe[0]); 171 + int rc = 0; 172 + if (waitpid(pid, &rc, 0) < 0) { 173 + free(buf); 174 + *status = 99; 175 + return empty_text(); 176 + } 54 177 *status = WIFEXITED(rc) ? WEXITSTATUS(rc) : 99; 55 178 return buf; 56 179 } ··· 59 182 int st = 0; 60 183 char path[512]; 61 184 snprintf(path, sizeof(path), "%s/sam/m0.aml", TOBI_SRC_DIR); 62 - char args[700]; 63 - snprintf(args, sizeof(args), "%s", path); 64 - char *s = run_capture(args, &st); 185 + const char *argv_default[] = {TOBI_BIN, path, NULL}; 186 + char *s = run_capture_argv(argv_default, NULL, 0, &st); 65 187 T_CHECK(st == 0); 66 188 T_STR(s, "method MTH0"); 67 189 T_STR(s, "return Local0"); 68 190 free(s); 69 - snprintf(args, sizeof(args), "--dis %s", path); 70 - s = run_capture(args, &st); 191 + const char *argv_dis[] = {TOBI_BIN, "--dis", path, NULL}; 192 + s = run_capture_argv(argv_dis, NULL, 0, &st); 71 193 T_CHECK(st == 0); 72 194 T_STR(s, "0x0000"); 73 195 T_STR(s, "MethodOp"); 74 196 free(s); 75 - snprintf(args, sizeof(args), "--json %s", path); 76 - s = run_capture(args, &st); 197 + const char *argv_json[] = {TOBI_BIN, "--json", path, NULL}; 198 + s = run_capture_argv(argv_json, NULL, 0, &st); 77 199 T_CHECK(st == 0); 78 200 T_STR(s, "\"ir\""); 79 201 T_STR(s, "\"kind\":\"method\""); 80 202 T_STR(s, path); 81 203 T_CHECK(strstr(s, "\"source\":\"raw\"") == NULL); 82 204 free(s); 83 - snprintf(args, sizeof(args), "--dot %s", path); 84 - s = run_capture(args, &st); 205 + const char *argv_dot[] = {TOBI_BIN, "--dot", path, NULL}; 206 + s = run_capture_argv(argv_dot, NULL, 0, &st); 85 207 T_CHECK(st == 0); 86 208 T_STR(s, "digraph tobi_cfg"); 87 209 T_STR(s, "method"); 210 + free(s); 211 + const char *argv_ast[] = {TOBI_BIN, "--graph", "ast", path, NULL}; 212 + s = run_capture_argv(argv_ast, NULL, 0, &st); 213 + T_CHECK(st == 0); 214 + T_STR(s, "digraph tobi_ast"); 215 + T_STR(s, "MTH0"); 216 + free(s); 217 + const char *argv_ns[] = {TOBI_BIN, "--graph", "namespace", path, NULL}; 218 + s = run_capture_argv(argv_ns, NULL, 0, &st); 219 + T_CHECK(st == 0); 220 + T_STR(s, "digraph tobi_namespace"); 221 + T_STR(s, "MTH0"); 88 222 free(s); 89 223 { 224 + const char *out_path = "/tmp/tobi_cli_graph.dot"; 225 + const char *argv_out[] = {TOBI_BIN, "--dot", path, "-o", out_path, NULL}; 226 + s = run_capture_argv(argv_out, NULL, 0, &st); 227 + T_CHECK(st == 0); 228 + T_CHECK(s[0] == '\0'); 229 + free(s); 230 + s = read_text(out_path); 231 + T_STR(s, "digraph tobi_cfg"); 232 + free(s); 233 + } 234 + { 235 + unsigned char stdin_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60}; 236 + const char *argv_stdin[] = {TOBI_BIN, "--json", "-", NULL}; 237 + s = run_capture_argv(argv_stdin, stdin_file, sizeof(stdin_file), &st); 238 + T_CHECK(st == 0); 239 + T_STR(s, "\"source\":\"<stdin>\""); 240 + T_STR(s, "MTH0"); 241 + free(s); 242 + } 243 + { 244 + unsigned char user_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60}; 245 + const char *space_path = "/tmp/tobi cli space.aml"; 246 + T_CHECK(write_blob(space_path, user_file, sizeof(user_file))); 247 + const char *argv_space[] = {TOBI_BIN, "--json", space_path, NULL}; 248 + s = run_capture_argv(argv_space, NULL, 0, &st); 249 + T_CHECK(st == 0); 250 + T_STR(s, space_path); 251 + free(s); 252 + } 253 + { 254 + unsigned char user_file[] = {0x14,0x0c,'M','T','H','0',0x01,0x70,0x0a,0x2a,0x60,0xa4,0x60}; 255 + char long_path[240] = "/tmp/tobi_cli_"; 256 + size_t pos = strlen(long_path); 257 + memset(long_path + pos, 'a', 150); 258 + strcpy(long_path + pos + 150, ".aml"); 259 + T_CHECK(write_blob(long_path, user_file, sizeof(user_file))); 260 + const char *argv_long[] = {TOBI_BIN, "--json", long_path, NULL}; 261 + s = run_capture_argv(argv_long, NULL, 0, &st); 262 + T_CHECK(st == 0); 263 + T_STR(s, long_path); 264 + free(s); 265 + } 266 + { 90 267 unsigned char user_file[] = { 91 268 0x14,0x24,'L','L','0','0',0x01, 92 269 0x70,0x00,0x60, ··· 99 276 }; 100 277 const char *user_path = "/tmp/tobi_cli_user_dot.aml"; 101 278 T_CHECK(write_blob(user_path, user_file, sizeof(user_file))); 102 - snprintf(args, sizeof(args), "--dot %s", user_path); 103 - s = run_capture(args, &st); 279 + const char *argv_user_dot[] = {TOBI_BIN, "--dot", user_path, NULL}; 280 + s = run_capture_argv(argv_user_dot, NULL, 0, &st); 104 281 T_CHECK(st == 0); 105 282 T_STR(s, "digraph tobi_cfg"); 106 283 T_STR(s, "LL00"); ··· 108 285 T_STR(s, "back"); 109 286 free(s); 110 287 } 111 - snprintf(args, sizeof(args), "--raw %s", path); 112 - s = run_capture(args, &st); 288 + const char *argv_raw[] = {TOBI_BIN, "--raw", path, NULL}; 289 + s = run_capture_argv(argv_raw, NULL, 0, &st); 113 290 T_CHECK(st == 0); 114 291 T_STR(s, "checksum_sum=0x00"); 115 292 T_STR(s, "checksum_valid=false"); ··· 121 298 const char *cal_path = "/tmp/tobi_cli_cal.aml"; 122 299 T_CHECK(write_blob(use_path, use_file, sizeof(use_file))); 123 300 T_CHECK(write_blob(cal_path, cal_file, sizeof(cal_file))); 124 - snprintf(args, sizeof(args), "%s %s", use_path, cal_path); 125 - s = run_capture(args, &st); 301 + const char *argv_multi[] = {TOBI_BIN, use_path, cal_path, NULL}; 302 + s = run_capture_argv(argv_multi, NULL, 0, &st); 126 303 T_CHECK(st == 0); 127 304 T_STR(s, "method USE0"); 128 305 T_STR(s, "CAL0(0x5)"); 129 306 free(s); 130 307 } 131 - s = run_capture("--help", &st); 308 + const char *argv_help[] = {TOBI_BIN, "--help", NULL}; 309 + s = run_capture_argv(argv_help, NULL, 0, &st); 132 310 T_CHECK(st == 0); 133 311 T_STR(s, "usage: tobi"); 134 312 free(s); 135 - s = run_capture("--version", &st); 313 + const char *argv_version[] = {TOBI_BIN, "--version", NULL}; 314 + s = run_capture_argv(argv_version, NULL, 0, &st); 136 315 T_CHECK(st == 0); 137 - T_STR(s, "tobi"); 316 + T_STR(s, "tobi 0.1.0"); 317 + free(s); 318 + const char *argv_conflict[] = {TOBI_BIN, "--json", "--dis", path, NULL}; 319 + s = run_capture_argv(argv_conflict, NULL, 0, &st); 320 + T_CHECK(st == 2); 321 + free(s); 322 + const char *argv_bad_opt[] = {TOBI_BIN, "--definitely-not-real", NULL}; 323 + s = run_capture_argv(argv_bad_opt, NULL, 0, &st); 324 + T_CHECK(st == 2); 138 325 free(s); 139 - snprintf(path, sizeof(path), "%s/sam/m0.aml", TOBI_SRC_DIR); 140 - snprintf(args, sizeof(args), "--json --dis %s", path); 141 - s = run_capture(args, &st); 326 + const char *argv_bad_graph[] = {TOBI_BIN, "--graph", "nope", path, NULL}; 327 + s = run_capture_argv(argv_bad_graph, NULL, 0, &st); 142 328 T_CHECK(st == 2); 143 329 free(s); 144 - s = run_capture("--definitely-not-real", &st); 330 + const char *argv_missing_graph[] = {TOBI_BIN, "--graph", NULL}; 331 + s = run_capture_argv(argv_missing_graph, NULL, 0, &st); 145 332 T_CHECK(st == 2); 146 333 free(s); 147 334 snprintf(path, sizeof(path), "%s/sam/bad0.aml", TOBI_SRC_DIR); 148 - snprintf(args, sizeof(args), "--strict %s", path); 149 - s = run_capture(args, &st); 335 + const char *argv_strict[] = {TOBI_BIN, "--strict", path, NULL}; 336 + s = run_capture_argv(argv_strict, NULL, 0, &st); 150 337 T_CHECK(st != 0); 151 338 free(s); 152 339 return 0;