diff --git a/degesch.c b/degesch.c index 26a1cbd..0119b13 100644 --- a/degesch.c +++ b/degesch.c @@ -1610,6 +1610,7 @@ register_config_modules (struct app_context *ctx) struct config *config = &ctx->config; // The servers are loaded later when we can create buffers for them config_register_module (config, "servers", NULL, NULL); + config_register_module (config, "aliases", NULL, NULL); config_register_module (config, "behaviour", load_config_behaviour, ctx); config_register_module (config, "attributes", load_config_attributes, ctx); } @@ -7625,8 +7626,9 @@ init_partial_matching_user_command_map (struct str_map *partial) } } -static void -process_user_command (struct app_context *ctx, char *input) +static bool +process_user_command + (struct app_context *ctx, const char *command_name, char *input) { static bool initialized = false; static struct str_map partial; @@ -7636,9 +7638,8 @@ process_user_command (struct app_context *ctx, char *input) initialized = true; } - char *command_name = cut_word (&input); if (try_handle_buffer_goto (ctx, command_name)) - return; + return true; struct handler_args args = { @@ -7647,10 +7648,11 @@ process_user_command (struct app_context *ctx, char *input) .arguments = input, }; - struct command_handler *handler = str_map_find (&partial, command_name); - if (!handler) - log_global_error (ctx, "#s: #s", "No such command", command_name); - else if ((handler->flags & HANDLER_SERVER) + struct command_handler *handler; + if (!(handler = str_map_find (&map, command_name))) + return false; + + if ((handler->flags & HANDLER_SERVER) && args.buffer->type == BUFFER_GLOBAL) log_global_error (ctx, "/#s: #s", command_name, "can't do this from a global buffer"); @@ -7671,6 +7673,64 @@ process_user_command (struct app_context *ctx, char *input) else if (!handler->handler (&args)) log_global_error (ctx, "#s: /#s #s", "Usage", handler->name, handler->usage); + return true; +} + +static char * +expand_alias_definition (const struct str *definition, const char *arguments) +{ + struct str_vector v; + str_vector_init (&v); + split_str_ignore_empty (arguments, ' ', &v); + + struct str expanded; + str_init (&expanded); + + // TODO: eventually also support argument ranges + bool escape = false; + for (const char *p = definition->str; *p; p++) + { + if (!escape) + { + if (*p == '$' && p[1]) + escape = true; + else + str_append_c (&expanded, *p); + continue; + } + + int as_number = *p - '0'; + if (as_number > 0 && as_number <= 9 + && (size_t) as_number <= v.len) + str_append (&expanded, v.vector[as_number - 1]); + else if (*p == '*') + str_append (&expanded, arguments); + else if (*p == '$') + str_append_c (&expanded, '$'); + else + str_append_printf (&expanded, "$%c", *p); + escape = false; + } + str_vector_free (&v); + return str_steal (&expanded); +} + +static char * +expand_alias (struct app_context *ctx, const char *alias_name, char *input) +{ + struct str_map *aliases = + &config_item_get (ctx->config.root, "aliases", NULL)->value.object; + + struct config_item_ *entry = str_map_find (aliases, alias_name); + if (!entry) + return NULL; + + if (config_item_type_is_string (entry->type)) + return expand_alias_definition (&entry->value.string, input); + + log_global_error (ctx, "Error executing `/%s': " + "alias definition is not a string", alias_name); + return NULL; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -7707,21 +7767,37 @@ send_message_to_current_buffer (struct app_context *ctx, char *message) } } +static void +process_input_utf8 (struct app_context *ctx, char *input, int alias_level) +{ + if (*input != '/' || *++input == '/') + { + send_message_to_current_buffer (ctx, input); + return; + } + + char *name = cut_word (&input); + if (process_user_command (ctx, name, input)) + return; + + char *expanded = expand_alias (ctx, name, input); + if (!expanded) + log_global_error (ctx, "#s: /#s", "No such command or alias", name); + else if (alias_level != 0) + log_global_error (ctx, "#s: /#s", "Aliases can't nest", name); + else + process_input_utf8 (ctx, expanded, alias_level++); + free (expanded); +} + static void process_input (struct app_context *ctx, char *user_input) { char *input; - size_t len; - - if (!(input = iconv_xstrdup (ctx->term_to_utf8, user_input, -1, &len))) + if (!(input = iconv_xstrdup (ctx->term_to_utf8, user_input, -1, NULL))) print_error ("character conversion failed for `%s'", "user input"); - else if (input[0] != '/') - send_message_to_current_buffer (ctx, input); - else if (input[1] == '/') - send_message_to_current_buffer (ctx, input + 1); else - process_user_command (ctx, input + 1); - + process_input_utf8 (ctx, input, 0); free (input); }