#include "config.h" #include "buffers.h" #include "ccdjit.h" #include "common.h" #include "ecex.h" #include "eval.h" #include "util.h" #include #include #include typedef int (*ecex_config_init_fn)(ecex_t *ed); typedef struct host_symbol { const char *name; void *addr; } host_symbol_t; void ecex_print_ccdjit_error(ccdjit_context *ctx) { const ccdjit_diagnostic *diag = ccdjit_context_last_error(ctx); if (!diag) { fprintf(stderr, "ccdjit: unknown error\n"); return; } fprintf(stderr, "ccdjit error [%s] %s:%d:%d: %s\n", diag->phase ? diag->phase : "?", diag->filename ? diag->filename : "", diag->line, diag->column, diag->message ? diag->message : "(no message)"); if (diag->source_line) fprintf(stderr, "%s\n", diag->source_line); if (diag->caret_line) fprintf(stderr, "%s\n", diag->caret_line); } #define HOST_SYMBOL(fn) { #fn, (void *)(fn) } static const host_symbol_t host_symbols[] = { HOST_SYMBOL(ecex_current_buffer), HOST_SYMBOL(ecex_create_buffer), HOST_SYMBOL(ecex_find_buffer), HOST_SYMBOL(ecex_switch_buffer), HOST_SYMBOL(ecex_next_buffer), HOST_SYMBOL(ecex_previous_buffer), HOST_SYMBOL(ecex_current_window), HOST_SYMBOL(ecex_window_count), HOST_SYMBOL(ecex_split_window_vertically), HOST_SYMBOL(ecex_split_window_horizontally), HOST_SYMBOL(ecex_other_window), HOST_SYMBOL(ecex_previous_window), HOST_SYMBOL(ecex_delete_window), HOST_SYMBOL(ecex_delete_other_windows), HOST_SYMBOL(ecex_kill_buffer), HOST_SYMBOL(ecex_register_command), HOST_SYMBOL(ecex_execute_command), HOST_SYMBOL(ecex_bind_key), HOST_SYMBOL(ecex_lookup_key), HOST_SYMBOL(ecex_define_major_mode), HOST_SYMBOL(ecex_major_mode_by_name), HOST_SYMBOL(ecex_major_mode_name), HOST_SYMBOL(ecex_buffer_set_major_mode), HOST_SYMBOL(ecex_buffer_set_major_mode_by_name), HOST_SYMBOL(ecex_buffer_major_mode_name), HOST_SYMBOL(ecex_bind_mode_key), HOST_SYMBOL(ecex_lookup_key_for_buffer), HOST_SYMBOL(ecex_key_sequence_has_prefix_for_buffer), HOST_SYMBOL(ecex_auto_set_major_mode), HOST_SYMBOL(ecex_list_commands), HOST_SYMBOL(ecex_list_buffers), HOST_SYMBOL(ecex_create_interactive_buffer), HOST_SYMBOL(ecex_interactive_append_line), HOST_SYMBOL(ecex_interactive_activate_current_line), HOST_SYMBOL(ecex_find_file), HOST_SYMBOL(ecex_save_current_buffer), HOST_SYMBOL(ecex_write_current_buffer), HOST_SYMBOL(ecex_request_prompt), HOST_SYMBOL(ecex_clear_prompt_request), HOST_SYMBOL(ecex_complete_command), HOST_SYMBOL(ecex_eval_source), HOST_SYMBOL(ecex_eval_current_buffer), HOST_SYMBOL(ecex_eval_current_line), HOST_SYMBOL(ecex_eval_current_region), HOST_SYMBOL(ecex_eval_file), HOST_SYMBOL(ecex_eval_rerun_last), HOST_SYMBOL(ecex_set_font), HOST_SYMBOL(ecex_get_font_size), HOST_SYMBOL(ecex_set_font_size), HOST_SYMBOL(ecex_adjust_font_size), HOST_SYMBOL(ecex_set_bg_color), HOST_SYMBOL(ecex_set_fg_color), HOST_SYMBOL(ecex_set_status_bg_color), HOST_SYMBOL(ecex_set_status_fg_color), HOST_SYMBOL(ecex_set_status_border_color), HOST_SYMBOL(ecex_set_cursor_color), HOST_SYMBOL(ecex_set_region_bg_color), HOST_SYMBOL(ecex_set_minibuffer_bg_color), HOST_SYMBOL(ecex_set_minibuffer_fg_color), HOST_SYMBOL(ecex_set_completion_fg_color), HOST_SYMBOL(ecex_set_completion_enabled), HOST_SYMBOL(ecex_set_interactive_highlight_bg_color), HOST_SYMBOL(ecex_set_interactive_highlight_fg_color), HOST_SYMBOL(buffer_clear), HOST_SYMBOL(buffer_set_text), HOST_SYMBOL(buffer_insert), HOST_SYMBOL(buffer_insert_at), HOST_SYMBOL(buffer_append), HOST_SYMBOL(buffer_prepend), HOST_SYMBOL(buffer_insert_char), HOST_SYMBOL(buffer_insert_char_at), HOST_SYMBOL(buffer_delete_range), HOST_SYMBOL(buffer_delete_selection), HOST_SYMBOL(buffer_replace_selection), HOST_SYMBOL(buffer_backspace), HOST_SYMBOL(buffer_delete_forward), HOST_SYMBOL(buffer_kill_line), HOST_SYMBOL(buffer_set_point), HOST_SYMBOL(buffer_move_left), HOST_SYMBOL(buffer_move_right), HOST_SYMBOL(buffer_move_up), HOST_SYMBOL(buffer_move_down), HOST_SYMBOL(buffer_move_word_left), HOST_SYMBOL(buffer_move_word_right), HOST_SYMBOL(buffer_move_beginning_of_line), HOST_SYMBOL(buffer_move_end_of_line), HOST_SYMBOL(buffer_move_beginning_of_buffer), HOST_SYMBOL(buffer_move_end_of_buffer), HOST_SYMBOL(buffer_set_mark), HOST_SYMBOL(buffer_clear_mark), HOST_SYMBOL(buffer_has_selection), HOST_SYMBOL(buffer_selection_range), HOST_SYMBOL(buffer_line_start_at), HOST_SYMBOL(buffer_line_end_at), HOST_SYMBOL(buffer_current_line_start), HOST_SYMBOL(buffer_current_line_end), HOST_SYMBOL(buffer_current_column), HOST_SYMBOL(buffer_current_line_number), HOST_SYMBOL(buffer_line_count), HOST_SYMBOL(buffer_substring), HOST_SYMBOL(buffer_current_line_copy), HOST_SYMBOL(buffer_load_file), HOST_SYMBOL(buffer_save), HOST_SYMBOL(buffer_save_as), HOST_SYMBOL(buffer_set_interactive), HOST_SYMBOL(buffer_is_interactive), HOST_SYMBOL(buffer_clear_interactive_actions), HOST_SYMBOL(buffer_add_interactive_action), HOST_SYMBOL(buffer_interactive_action_at_line), }; #undef HOST_SYMBOL int ecex_register_host_symbols(ccdjit_context *ctx) { for (size_t i = 0; i < ECEX_ARRAY_LEN(host_symbols); i++) { if (ccdjit_context_register_symbol(ctx, host_symbols[i].name, host_symbols[i].addr) != 0) { fprintf(stderr, "ecex: failed to register symbol: %s\n", host_symbols[i].name); ecex_print_ccdjit_error(ctx); return ECEX_ERR; } } return ECEX_OK; } static int add_include_path(ccdjit_context *ctx, const char *path) { if (ccdjit_context_add_include_path(ctx, path) == 0) return ECEX_OK; fprintf(stderr, "ecex: failed to add include path: %s\n", path); ecex_print_ccdjit_error(ctx); return ECEX_ERR; } int ecex_add_ccdjit_include_paths(ccdjit_context *ctx) { if (add_include_path(ctx, "include") != ECEX_OK) return ECEX_ERR; if (add_include_path(ctx, "../include") != ECEX_OK) return ECEX_ERR; const char *env_include = getenv("ECEX_INCLUDE"); if (env_include && env_include[0]) { if (add_include_path(ctx, env_include) != ECEX_OK) return ECEX_ERR; } return ECEX_OK; } static char *make_config_source_with_forced_main(const char *path) { size_t source_size = 0; char *source = ecex_read_entire_file(path, &source_size); if (!source) return NULL; const char *forced_main = "\n" "\n" "int main(int argc, char **argv) {\n" " (void)argc;\n" " (void)argv;\n" " return ecex_config_init((ecex_t *)0);\n" "}\n"; size_t forced_main_len = strlen(forced_main); char *combined = malloc(source_size + forced_main_len + 1); if (!combined) { free(source); return NULL; } memcpy(combined, source, source_size); memcpy(combined + source_size, forced_main, forced_main_len + 1); free(source); return combined; } int ecex_load_c_config(ecex_t *ed, const char *path) { if (!ed || !path) return ECEX_ERR; ccdjit_context *ctx = ccdjit_context_new(NULL); if (!ctx) { fprintf(stderr, "ecex: failed to create ccdjit context\n"); return ECEX_ERR; } if (ecex_add_ccdjit_include_paths(ctx) != ECEX_OK || ecex_register_host_symbols(ctx) != ECEX_OK) { ccdjit_context_free(ctx); return ECEX_ERR; } char *source = make_config_source_with_forced_main(path); if (!source) { fprintf(stderr, "ecex: failed to read or prepare config: %s\n", path); ccdjit_context_free(ctx); return ECEX_ERR; } ccdjit_module *module = NULL; if (ccdjit_compile_string(ctx, source, path, &module) != 0) { fprintf(stderr, "ecex: failed to compile config: %s\n", path); ecex_print_ccdjit_error(ctx); free(source); ccdjit_context_free(ctx); return ECEX_ERR; } free(source); ecex_config_init_fn init = (ecex_config_init_fn)ccdjit_module_symbol(module, "ecex_config_init"); if (!init) { fprintf(stderr, "ecex: config missing function: ecex_config_init\n"); ecex_print_ccdjit_error(ctx); ccdjit_module_free(module); ccdjit_context_free(ctx); return ECEX_ERR; } int result = init(ed); if (result != 0) { fprintf(stderr, "ecex: config init failed with code: %d\n", result); ccdjit_module_free(module); ccdjit_context_free(ctx); return ECEX_ERR; } if (ecex_keep_jit_module(ed, module) != ECEX_OK) { fprintf(stderr, "ecex: failed to retain config module\n"); ccdjit_module_free(module); ccdjit_context_free(ctx); return ECEX_ERR; } if (ecex_set_config_path(ed, path) != ECEX_OK) { fprintf(stderr, "ecex: failed to remember config path: %s\n", path); ccdjit_context_free(ctx); return ECEX_ERR; } ccdjit_context_free(ctx); fprintf(stderr, "ecex: loaded config: %s\n", path); return ECEX_OK; }