#include "config.h" #include "buffers.h" #include "ccdjit.h" #include "common.h" #include "ecex.h" #include "eval.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_logf), HOST_SYMBOL(ecex_log_group_begin), HOST_SYMBOL(ecex_log_group_end), HOST_SYMBOL(ecex_log_int), HOST_SYMBOL(ecex_log_double), HOST_SYMBOL(ecex_log_ptr), HOST_SYMBOL(ecex_log_flush), 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_plugin_register), HOST_SYMBOL(ecex_plugin_require), HOST_SYMBOL(ecex_plugin_find), HOST_SYMBOL(ecex_plugin_id), HOST_SYMBOL(ecex_plugin_slot_alloc), HOST_SYMBOL(ecex_plugin_slot_get), HOST_SYMBOL(ecex_plugin_slot_free), HOST_SYMBOL(ecex_plugin_slot_set_export_flags), HOST_SYMBOL(ecex_plugin_slot_read_exported), HOST_SYMBOL(ecex_plugin_slot_i32_get), HOST_SYMBOL(ecex_plugin_slot_i32_set), HOST_SYMBOL(ecex_plugin_slot_i32_get_2d), HOST_SYMBOL(ecex_plugin_slot_i32_set_2d), HOST_SYMBOL(ecex_plugin_slot_i32_get_scalar), HOST_SYMBOL(ecex_plugin_slot_i32_set_scalar), HOST_SYMBOL(ecex_plugin_object_alloc), HOST_SYMBOL(ecex_plugin_object_calloc), HOST_SYMBOL(ecex_plugin_object_free), HOST_SYMBOL(ecex_plugin_object_valid), HOST_SYMBOL(ecex_plugin_object_i32_get), HOST_SYMBOL(ecex_plugin_object_i32_set), HOST_SYMBOL(ecex_plugin_object_ptr_get), HOST_SYMBOL(ecex_plugin_object_ptr_set), HOST_SYMBOL(ecex_plugin_text_set), HOST_SYMBOL(ecex_plugin_text_set_from_buffer_title), HOST_SYMBOL(ecex_plugin_text_free), HOST_SYMBOL(ecex_plugin_text_free_all), HOST_SYMBOL(ecex_plugin_text_get_drawable), HOST_SYMBOL(ecex_buffer_text_len), HOST_SYMBOL(ecex_buffer_scroll_line_index), HOST_SYMBOL(ecex_buffer_line_count_int), HOST_SYMBOL(ecex_buffer_line_copy_text), HOST_SYMBOL(ecex_markdown_draw_buffer_line_i), HOST_SYMBOL(ecex_markdown_body_y_px), HOST_SYMBOL(ecex_draw_context_height_px), HOST_SYMBOL(ecex_draw_context_line_height_px), HOST_SYMBOL(ecex_plugin_file_handler_register), HOST_SYMBOL(ecex_plugin_file_handlers_run), 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_add_command_hook), HOST_SYMBOL(ecex_remove_command_hook), HOST_SYMBOL(ecex_add_prefix_hook), HOST_SYMBOL(ecex_remove_prefix_hook), HOST_SYMBOL(ecex_notify_prefix_hooks), HOST_SYMBOL(ecex_add_buffer_hook), HOST_SYMBOL(ecex_remove_buffer_hook), HOST_SYMBOL(ecex_notify_buffer_hooks), HOST_SYMBOL(ecex_message), HOST_SYMBOL(ecex_dependency_available), HOST_SYMBOL(ecex_plugin_require_dependency), HOST_SYMBOL(ecex_add_completion_provider), HOST_SYMBOL(ecex_add_word_completion_provider), HOST_SYMBOL(ecex_define_word_completion_provider), HOST_SYMBOL(ecex_completion_provider_add_word), HOST_SYMBOL(ecex_completion_provider_add_words), HOST_SYMBOL(ecex_completion_provider_set_detail), HOST_SYMBOL(ecex_add_clangd_completion_provider), HOST_SYMBOL(ecex_remove_completion_provider), HOST_SYMBOL(ecex_buffer_identifier_prefix), HOST_SYMBOL(ecex_complete_at_point), 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_describe_key_prefix), 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_i), 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_plugin_text_i), HOST_SYMBOL(ecex_draw_plugin_text_rect_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_compile), HOST_SYMBOL(ecex_grep), HOST_SYMBOL(ecex_rerun_compile), HOST_SYMBOL(ecex_rerun_grep), HOST_SYMBOL(ecex_next_interactive_action), HOST_SYMBOL(ecex_previous_interactive_action), HOST_SYMBOL(ecex_indent_line_to), HOST_SYMBOL(ecex_comment_region), HOST_SYMBOL(ecex_uncomment_region), 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 int ccdjit_type_is_int(ccdjit_type_info type) { return type.has_type && type.base == CCDJIT_TYPE_INT && type.pointer_level == 0; } static int ccdjit_type_is_pointer(ccdjit_type_info type) { return type.has_type && type.pointer_level == 1; } static ecex_config_init_fn ecex_config_init_symbol(ccdjit_context *ctx, ccdjit_module *module, const char *path) { void *symbol = ccdjit_module_symbol(module, "ecex_config_init"); if (!symbol) return NULL; ccdjit_symbol_info info; memset(&info, 0, sizeof(info)); if (ccdjit_module_symbol_info(module, "ecex_config_init", &info) == 0 && info.has_signature) { ccdjit_type_info param; memset(¶m, 0, sizeof(param)); if (info.kind != CCDJIT_SYMBOL_FUNCTION || !ccdjit_type_is_int(info.return_type) || info.parameter_count != 1 || info.is_variadic || ccdjit_module_function_parameter_type(module, "ecex_config_init", 0, ¶m) != 0 || !ccdjit_type_is_pointer(param)) { fprintf(stderr, "ecex: invalid config signature in %s; expected int ecex_config_init(ecex_t *ed)\n", path ? path : ""); return NULL; } } else if (ctx) { const ccdjit_diagnostic *diag = ccdjit_context_last_error(ctx); if (diag && diag->code != CCDJIT_DIAG_NONE) { ecex_print_ccdjit_error(ctx); } } return (ecex_config_init_fn)symbol; } 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; } ccdjit_module *module = NULL; if (ccdjit_compile_file_module(ctx, path, &module) != 0) { fprintf(stderr, "ecex: failed to compile config: %s\n", path); ecex_print_ccdjit_error(ctx); ccdjit_context_free(ctx); return ECEX_ERR; } ecex_config_init_fn init = ecex_config_init_symbol(ctx, module, path); 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); /* Config init may have registered hooks/renderers before returning an * error. Keep the module alive so rollback can call any JIT-owned * destructors safely. */ if (ecex_keep_jit_module(ed, module) != ECEX_OK) { fprintf(stderr, "ecex: failed to retain failed config module; leaving it mapped for cleanup safety\n"); } 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; }