#include "config.h" #include "buffers.h" #include "ccdjit.h" #include "common.h" #include "ecex.h" #include "eval.h" #include "util.h" #include "path.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_kill_buffer_force), HOST_SYMBOL(ecex_has_modified_buffers), HOST_SYMBOL(ecex_validate_bindings), HOST_SYMBOL(ecex_config_alloc), HOST_SYMBOL(ecex_config_calloc), HOST_SYMBOL(ecex_config_free), HOST_SYMBOL(ecex_time_seconds), HOST_SYMBOL(ecex_log), HOST_SYMBOL(ecex_log_int), HOST_SYMBOL(ecex_log_double), HOST_SYMBOL(ecex_log_ptr), HOST_SYMBOL(ecex_mem_zero), HOST_SYMBOL(ecex_i32_get), HOST_SYMBOL(ecex_i32_set), HOST_SYMBOL(ecex_prng_next_bounded), HOST_SYMBOL(ecex_random_bounded), HOST_SYMBOL(ecex_tetris_shape_cell), HOST_SYMBOL(ecex_var_get), HOST_SYMBOL(ecex_var_get_or_alloc), HOST_SYMBOL(ecex_var_bind_static), HOST_SYMBOL(ecex_var_free), HOST_SYMBOL(ecex_var_free_owner), HOST_SYMBOL(ecex_var_i32_get), HOST_SYMBOL(ecex_var_i32_set), HOST_SYMBOL(ecex_var_i32), HOST_SYMBOL(ecex_var_i32_set_scalar), HOST_SYMBOL(ecex_object_alloc), HOST_SYMBOL(ecex_object_calloc), HOST_SYMBOL(ecex_object_free), HOST_SYMBOL(ecex_object_valid), HOST_SYMBOL(ecex_object_i32_get), HOST_SYMBOL(ecex_object_i32_set), HOST_SYMBOL(ecex_object_ptr_get), HOST_SYMBOL(ecex_object_ptr_set), HOST_SYMBOL(ecex_text_set), HOST_SYMBOL(ecex_text_set_buffer_title), HOST_SYMBOL(ecex_text_free), HOST_SYMBOL(ecex_text_free_owner), HOST_SYMBOL(ecex_buffer_text_len), HOST_SYMBOL(ecex_buffer_scroll_line), HOST_SYMBOL(ecex_buffer_line_count_i), HOST_SYMBOL(ecex_buffer_line_copy), HOST_SYMBOL(ecex_markdown_draw_line_from_buffer_i), HOST_SYMBOL(ecex_markdown_body_y_i), HOST_SYMBOL(ecex_draw_context_height_i), HOST_SYMBOL(ecex_draw_context_line_height_i), HOST_SYMBOL(ecex_register_file_handler), HOST_SYMBOL(ecex_run_file_handlers), HOST_SYMBOL(ecex_config_register_commands), HOST_SYMBOL(ecex_config_bind_keys), HOST_SYMBOL(ecex_config_bind_mode_keys), HOST_SYMBOL(ecex_config_define_modes), HOST_SYMBOL(ecex_apply_theme), 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_buffer_set_renderer), HOST_SYMBOL(ecex_buffer_clear_renderer), HOST_SYMBOL(ecex_buffer_has_renderer), HOST_SYMBOL(ecex_buffer_renderer_userdata), HOST_SYMBOL(ecex_buffer_set_mouse_handler), HOST_SYMBOL(ecex_buffer_clear_mouse_handler), HOST_SYMBOL(ecex_buffer_has_mouse_handler), HOST_SYMBOL(ecex_buffer_mouse_userdata), HOST_SYMBOL(ecex_buffer_set_animation), HOST_SYMBOL(ecex_buffer_set_animation_ms), HOST_SYMBOL(ecex_buffer_clear_animation), HOST_SYMBOL(ecex_buffer_is_animating), HOST_SYMBOL(ecex_buffer_animation_userdata), HOST_SYMBOL(ecex_tick_animations), HOST_SYMBOL(ecex_buffer_replace_text), HOST_SYMBOL(ecex_buffer_set_modified), HOST_SYMBOL(ecex_draw_set_color), HOST_SYMBOL(ecex_draw_rect), HOST_SYMBOL(ecex_draw_rect_outline), HOST_SYMBOL(ecex_draw_line), HOST_SYMBOL(ecex_draw_text), HOST_SYMBOL(ecex_draw_text_aligned), HOST_SYMBOL(ecex_draw_text_width), HOST_SYMBOL(ecex_draw_color_rgba8), HOST_SYMBOL(ecex_draw_rect_i), HOST_SYMBOL(ecex_draw_rect_outline_i), HOST_SYMBOL(ecex_draw_line_i), HOST_SYMBOL(ecex_draw_text_i), HOST_SYMBOL(ecex_draw_text_id_i), HOST_SYMBOL(ecex_draw_markdown_canvas_i), HOST_SYMBOL(ecex_draw_markdown_text_i), HOST_SYMBOL(ecex_draw_markdown_canvas_auto_i), HOST_SYMBOL(ecex_draw_markdown_line_auto_i), HOST_SYMBOL(ecex_draw_label_i), HOST_SYMBOL(ecex_draw_stat_i), HOST_SYMBOL(ecex_draw_tetris_preview_i), HOST_SYMBOL(ecex_draw_rgba), 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_path_copy), HOST_SYMBOL(ecex_path_expand_user), HOST_SYMBOL(ecex_path_join), HOST_SYMBOL(ecex_path_dirname), HOST_SYMBOL(ecex_path_basename_dup), HOST_SYMBOL(ecex_path_normalize), HOST_SYMBOL(ecex_path_is_dir), HOST_SYMBOL(ecex_path_is_file), HOST_SYMBOL(ecex_path_exists), HOST_SYMBOL(ecex_path_file_size), HOST_SYMBOL(ecex_path_is_image), HOST_SYMBOL(ecex_path_is_previewable_image), HOST_SYMBOL(ecex_path_is_video), HOST_SYMBOL(ecex_path_is_media), HOST_SYMBOL(ecex_path_cwd), HOST_SYMBOL(ecex_media_open), HOST_SYMBOL(ecex_media_toggle_playback), 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(ecex_set_current_line_bg_color), HOST_SYMBOL(ecex_set_search_bg_color), HOST_SYMBOL(ecex_set_line_numbers_enabled), HOST_SYMBOL(ecex_set_current_line_enabled), 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; }