degesch: use libffi to unify input callbacks

And fuck you both, Readline and Editline.
This commit is contained in:
Přemysl Eric Janouch 2016-03-06 17:59:45 +01:00
parent 3304b718aa
commit 584d2f0295
3 changed files with 323 additions and 365 deletions

View File

@ -39,7 +39,7 @@ include (AddThreads)
find_package (Curses) find_package (Curses)
find_package (PkgConfig REQUIRED) find_package (PkgConfig REQUIRED)
pkg_check_modules (libssl REQUIRED libssl libcrypto) pkg_check_modules (dependencies REQUIRED libssl libcrypto libffi)
pkg_check_modules (ncursesw ncursesw) pkg_check_modules (ncursesw ncursesw)
if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD") if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
@ -50,9 +50,9 @@ if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1) add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD") endif ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
list (APPEND project_libraries ${libssl_LIBRARIES}) list (APPEND project_libraries ${dependencies_LIBRARIES})
include_directories (${libssl_INCLUDE_DIRS}) include_directories (${dependencies_INCLUDE_DIRS})
link_directories (${libssl_LIBRARY_DIRS}) link_directories (${dependencies_LIBRARY_DIRS})
# FIXME: other Lua versions may be acceptable, don't know yet # FIXME: other Lua versions may be acceptable, don't know yet
pkg_search_module (lua lua53 lua5.3 lua-5.3 lua>=5.3) pkg_search_module (lua lua53 lua5.3 lua-5.3 lua>=5.3)

View File

@ -10,7 +10,7 @@ All of them have these potentially interesting properties:
- full IPv6 support - full IPv6 support
- TLS support, including client certificates - TLS support, including client certificates
- minimal dependencies - lean on dependencies (with the exception of 'degesch')
- compact and arguably easy to hack on - compact and arguably easy to hack on
- permissive license - permissive license
@ -66,9 +66,9 @@ support (even though socksify can add that easily to any program).
Building Building
-------- --------
Build dependencies: CMake, pkg-config, help2man, awk, sh, liberty (included) + Build dependencies: CMake, pkg-config, help2man, awk, sh, liberty (included) +
Runtime dependencies: openssl, curses (degesch), Runtime dependencies: openssl +
readline >= 6.0 or libedit >= 2013-07-12 (degesch), Additionally for degesch: curses, libffi, lua >= 5.3 (optional),
lua >= 5.3 (degesch, optional) readline >= 6.0 or libedit >= 2013-07-12
$ git clone --recursive https://github.com/pjanouch/uirc3.git $ git clone --recursive https://github.com/pjanouch/uirc3.git
$ mkdir uirc3/build $ mkdir uirc3/build

670
degesch.c
View File

@ -68,6 +68,8 @@ enum
#undef lines #undef lines
#undef columns #undef columns
#include <ffi.h>
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
#include <readline/readline.h> #include <readline/readline.h>
#include <readline/history.h> #include <readline/history.h>
@ -134,6 +136,22 @@ input_buffer_destroy (struct input_buffer *self)
free (self); free (self);
} }
typedef bool (*input_fn) (int count, int key, void *user_data);
struct input_fn_data
{
ffi_closure closure; ///< Closure
LIST_HEADER (struct input_fn_data)
input_fn callback; ///< Real callback
void *user_data; ///< Real callback user data
#ifdef HAVE_EDITLINE
wchar_t *name; ///< Function name
wchar_t *help; ///< Function help
#endif // HAVE_EDITLINE
};
struct input struct input
{ {
bool active; ///< Are we a thing? bool active; ///< Are we a thing?
@ -145,6 +163,7 @@ struct input
#elif defined HAVE_EDITLINE #elif defined HAVE_EDITLINE
EditLine *editline; ///< The EditLine object EditLine *editline; ///< The EditLine object
#endif // HAVE_EDITLINE #endif // HAVE_EDITLINE
struct input_fn_data *fns; ///< Functions
char *prompt; ///< The prompt we use char *prompt; ///< The prompt we use
int prompt_shown; ///< Whether the prompt is shown now int prompt_shown; ///< Whether the prompt is shown now
@ -164,6 +183,14 @@ input_free (struct input *self)
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
free (self->saved_line); free (self->saved_line);
#endif // HAVE_READLINE #endif // HAVE_READLINE
LIST_FOR_EACH (struct input_fn_data, iter, self->fns)
{
#ifdef HAVE_EDITLINE
free (iter->name);
free (iter->help);
#endif // HAVE_EDITLINE
ffi_closure_free (iter);
}
free (self->prompt); free (self->prompt);
} }
@ -279,6 +306,45 @@ input_get_content (struct input *self)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
{
(void) cif;
struct input_fn_data *data = user_data;
if (!data->callback
(*(int *) args[0], UNMETA (*(int *) args[1]), data->user_data))
rl_ding ();
*(int *) ret = 0;
}
static void
input_add_fn (struct input *self,
const char *name, const char *help, input_fn callback, void *user_data)
{
(void) help;
void *bound_fn = NULL;
struct input_fn_data *data = ffi_closure_alloc (sizeof *data, &bound_fn);
hard_assert (data);
static ffi_cif cif;
static ffi_type *args[2] = { &ffi_type_sint, &ffi_type_sint };
hard_assert (ffi_prep_cif
(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint, args) == FFI_OK);
data->prev = data->next = NULL;
data->callback = callback;
data->user_data = user_data;
hard_assert (ffi_prep_closure_loc (&data->closure,
&cif, input_closure_forwarder, data, bound_fn) == FFI_OK);
rl_add_defun (name, (rl_command_func_t *) bound_fn, -1);
LIST_PREPEND (self->fns, data);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static int app_readline_init (void); static int app_readline_init (void);
static void on_readline_input (char *line); static void on_readline_input (char *line);
static char **app_readline_completion (const char *text, int start, int end); static char **app_readline_completion (const char *text, int start, int end);
@ -603,6 +669,52 @@ input_get_content (struct input *self)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
{
(void) cif;
struct input_fn_data *data = user_data;
*(unsigned char *) ret = data->callback
(1, *(int *) args[1], data->user_data) ? CC_NORM : CC_ERROR;
}
static wchar_t *
ascii_to_wide (const char *ascii)
{
size_t len = strlen (ascii) + 1;
wchar_t *wide = xcalloc (sizeof *wide, len);
while (len--)
hard_assert ((wide[len] = (unsigned char) ascii[len]) < 0x80);
return wide;
}
static void
input_add_fn (struct input *self,
const char *name, const char *help, input_fn callback, void *user_data)
{
void *bound_fn = NULL;
struct input_fn_data *data = ffi_closure_alloc (sizeof *data, &bound_fn);
hard_assert (data);
static ffi_cif cif;
static ffi_type *args[2] = { &ffi_type_pointer, &ffi_type_sint };
hard_assert (ffi_prep_cif
(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_uchar, args) == FFI_OK);
data->user_data = user_data;
data->callback = callback;
data->name = ascii_to_wide (name);
data->help = ascii_to_wide (help);
hard_assert (ffi_prep_closure_loc (&data->closure,
&cif, input_closure_forwarder, data, bound_fn) == FFI_OK);
el_wset (self->editline, EL_ADDFN, data->name, data->help, bound_fn);
LIST_PREPEND (self->fns, data);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void static void
input_start (struct input *self, const char *program_name) input_start (struct input *self, const char *program_name)
{ {
@ -11161,40 +11273,6 @@ resume_terminal (struct app_context *ctx)
input_show (&ctx->input); input_show (&ctx->input);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
redraw_screen (struct app_context *ctx)
{
input_hide (&ctx->input);
// If by some circumstance we had the wrong idea
input_on_terminal_resized (&ctx->input);
update_screen_size ();
buffer_print_backlog (ctx, ctx->current_buffer);
input_show (&ctx->input);
}
static bool
jump_to_buffer (struct app_context *ctx, int n)
{
if (n < 0 || n > 9)
return false;
// There's no buffer zero
if (n == 0)
n = 10;
if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
// Fast switching between two buffers
buffer_activate (ctx, ctx->last_buffer);
else if (!buffer_goto (ctx, n))
return false;
return true;
}
static pid_t static pid_t
spawn_helper_child (struct app_context *ctx) spawn_helper_child (struct app_context *ctx)
{ {
@ -11222,69 +11300,20 @@ spawn_helper_child (struct app_context *ctx)
} }
static void static void
launch_backlog_helper (struct app_context *ctx, int backlog_fd) redraw_screen (struct app_context *ctx)
{ {
hard_assert (!ctx->running_backlog_helper); input_hide (&ctx->input);
switch (spawn_helper_child (ctx))
{ // If by some circumstance we had the wrong idea
case 0: input_on_terminal_resized (&ctx->input);
dup2 (backlog_fd, STDIN_FILENO); update_screen_size ();
execl ("/bin/sh", "/bin/sh", "-c", get_config_string
(ctx->config.root, "behaviour.backlog_helper"), NULL); buffer_print_backlog (ctx, ctx->current_buffer);
print_error ("%s: %s",
"Failed to launch backlog helper", strerror (errno)); input_show (&ctx->input);
_exit (EXIT_FAILURE);
case -1:
log_global_error (ctx, "#s: #l",
"Failed to launch backlog helper", strerror (errno));
break;
default:
ctx->running_backlog_helper = true;
}
} }
static void // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
display_backlog (struct app_context *ctx)
{
FILE *backlog = tmpfile ();
if (!backlog)
{
log_global_error (ctx, "#s: #l",
"Failed to create a temporary file", strerror (errno));
return;
}
for (struct buffer_line *line = ctx->current_buffer->lines;
line; line = line->next)
buffer_line_write_to_backlog (ctx, line, backlog);
rewind (backlog);
set_cloexec (fileno (backlog));
launch_backlog_helper (ctx, fileno (backlog));
fclose (backlog);
}
static void
display_full_log (struct app_context *ctx)
{
char *path = buffer_get_log_path (ctx->current_buffer);
FILE *full_log = fopen (path, "rb");
free (path);
if (!full_log)
{
log_global_error (ctx, "Failed to open log file for #s: #l",
ctx->current_buffer->name, strerror (errno));
return;
}
if (ctx->current_buffer->log_file)
fflush (ctx->current_buffer->log_file);
set_cloexec (fileno (full_log));
launch_backlog_helper (ctx, fileno (full_log));
fclose (full_log);
}
static bool static bool
dump_input_to_file (struct app_context *ctx, char *template, struct error **e) dump_input_to_file (struct app_context *ctx, char *template, struct error **e)
@ -11324,12 +11353,16 @@ try_dump_input_to_file (struct app_context *ctx)
return NULL; return NULL;
} }
static void static bool
launch_input_editor (struct app_context *ctx) on_edit_input (int count, int key, void *user_data)
{ {
(void) count;
(void) key;
struct app_context *ctx = user_data;
char *filename; char *filename;
if (!(filename = try_dump_input_to_file (ctx))) if (!(filename = try_dump_input_to_file (ctx)))
return; return false;
const char *command; const char *command;
if (!(command = getenv ("VISUAL")) if (!(command = getenv ("VISUAL"))
@ -11353,6 +11386,7 @@ launch_input_editor (struct app_context *ctx)
ctx->running_editor = true; ctx->running_editor = true;
ctx->editor_filename = filename; ctx->editor_filename = filename;
} }
return true;
} }
static void static void
@ -11390,10 +11424,185 @@ input_editor_cleanup (struct app_context *ctx)
ctx->running_editor = false; ctx->running_editor = false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
launch_backlog_helper (struct app_context *ctx, int backlog_fd)
{
hard_assert (!ctx->running_backlog_helper);
switch (spawn_helper_child (ctx))
{
case 0:
dup2 (backlog_fd, STDIN_FILENO);
execl ("/bin/sh", "/bin/sh", "-c", get_config_string
(ctx->config.root, "behaviour.backlog_helper"), NULL);
print_error ("%s: %s",
"Failed to launch backlog helper", strerror (errno));
_exit (EXIT_FAILURE);
case -1:
log_global_error (ctx, "#s: #l",
"Failed to launch backlog helper", strerror (errno));
break;
default:
ctx->running_backlog_helper = true;
}
}
static bool
on_display_backlog (int count, int key, void *user_data)
{
(void) count;
(void) key;
struct app_context *ctx = user_data;
FILE *backlog = tmpfile ();
if (!backlog)
{
log_global_error (ctx, "#s: #l",
"Failed to create a temporary file", strerror (errno));
return false;
}
for (struct buffer_line *line = ctx->current_buffer->lines;
line; line = line->next)
buffer_line_write_to_backlog (ctx, line, backlog);
rewind (backlog);
set_cloexec (fileno (backlog));
launch_backlog_helper (ctx, fileno (backlog));
fclose (backlog);
return true;
}
static bool
on_display_full_log (int count, int key, void *user_data)
{
(void) count;
(void) key;
struct app_context *ctx = user_data;
char *path = buffer_get_log_path (ctx->current_buffer);
FILE *full_log = fopen (path, "rb");
free (path);
if (!full_log)
{
log_global_error (ctx, "Failed to open log file for #s: #l",
ctx->current_buffer->name, strerror (errno));
return false;
}
if (ctx->current_buffer->log_file)
fflush (ctx->current_buffer->log_file);
set_cloexec (fileno (full_log));
launch_backlog_helper (ctx, fileno (full_log));
fclose (full_log);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static bool
on_goto_buffer (int count, int key, void *user_data)
{
(void) count;
struct app_context *ctx = user_data;
int n = key - '0';
if (n < 0 || n > 9)
return false;
// There's no buffer zero
if (n == 0)
n = 10;
if (!ctx->last_buffer || buffer_get_index (ctx, ctx->current_buffer) != n)
return buffer_goto (ctx, n);
// Fast switching between two buffers
buffer_activate (ctx, ctx->last_buffer);
return true;
}
static bool
on_previous_buffer (int count, int key, void *user_data)
{
(void) key;
buffer_activate (user_data, buffer_previous (user_data, count));
return true;
}
static bool
on_next_buffer (int count, int key, void *user_data)
{
(void) key;
buffer_activate (user_data, buffer_next (user_data, count));
return true;
}
static bool
on_switch_buffer (int count, int key, void *user_data)
{
(void) count;
(void) key;
struct app_context *ctx = user_data;
if (!ctx->last_buffer)
return false;
buffer_activate (ctx, ctx->last_buffer);
return true;
}
static bool
on_redraw_screen (int count, int key, void *user_data)
{
(void) count;
(void) key;
redraw_screen (user_data);
return true;
}
static bool
on_insert_attribute (int count, int key, void *user_data)
{
(void) count;
(void) key;
struct app_context *ctx = user_data;
ctx->awaiting_mirc_escape = true;
return true;
}
static bool
on_start_paste_mode (int count, int key, void *user_data)
{
(void) count;
(void) key;
struct app_context *ctx = user_data;
ctx->in_bracketed_paste = true;
return true;
}
static void static void
bind_common_keys (struct app_context *ctx) bind_common_keys (struct app_context *ctx)
{ {
struct input *self = &ctx->input; struct input *self = &ctx->input;
#define XX(...) input_add_fn (self, __VA_ARGS__, ctx);
XX ("previous-buffer", "Previous buffer", on_previous_buffer)
XX ("next-buffer", "Next buffer", on_next_buffer)
XX ("goto-buffer", "Go to buffer", on_goto_buffer)
XX ("switch-buffer", "Switch buffer", on_switch_buffer)
XX ("display-backlog", "Show backlog", on_display_backlog)
XX ("display-full-log", "Show full log", on_display_full_log)
XX ("edit-input", "Edit input", on_edit_input)
XX ("redraw-screen", "Redraw screen", on_redraw_screen)
XX ("insert-attribute", "mIRC formatting", on_insert_attribute)
XX ("start-paste-mode", "Bracketed paste", on_start_paste_mode)
#undef XX
input_bind_control (self, 'p', "previous-buffer"); input_bind_control (self, 'p', "previous-buffer");
input_bind_control (self, 'n', "next-buffer"); input_bind_control (self, 'n', "next-buffer");
@ -11406,12 +11615,9 @@ bind_common_keys (struct app_context *ctx)
input_bind_meta (self, 'h', "display-full-log"); input_bind_meta (self, 'h', "display-full-log");
input_bind_meta (self, 'e', "edit-input"); input_bind_meta (self, 'e', "edit-input");
if (key_f5) if (key_f5) input_bind (self, key_f5, "previous-buffer");
input_bind (self, key_f5, "previous-buffer"); if (key_f6) input_bind (self, key_f6, "next-buffer");
if (key_f6) if (key_ppage) input_bind (self, key_ppage, "display-backlog");
input_bind (self, key_f6, "next-buffer");
if (key_ppage)
input_bind (self, key_ppage, "display-backlog");
if (clear_screen) if (clear_screen)
input_bind_control (self, 'l', "redraw-screen"); input_bind_control (self, 'l', "redraw-screen");
@ -11423,129 +11629,17 @@ bind_common_keys (struct app_context *ctx)
#ifdef HAVE_READLINE #ifdef HAVE_READLINE
static int
on_readline_goto_buffer (int count, int key)
{
(void) count;
struct app_context *ctx = g_ctx;
if (!jump_to_buffer (ctx, UNMETA (key) - '0'))
input_ding (&ctx->input);
return 0;
}
static int
on_readline_previous_buffer (int count, int key)
{
(void) key;
struct app_context *ctx = g_ctx;
buffer_activate (ctx, buffer_previous (ctx, count));
return 0;
}
static int
on_readline_next_buffer (int count, int key)
{
(void) key;
struct app_context *ctx = g_ctx;
buffer_activate (ctx, buffer_next (ctx, count));
return 0;
}
static int
on_readline_switch_buffer (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
if (ctx->last_buffer)
buffer_activate (ctx, ctx->last_buffer);
else
input_ding (&ctx->input);
return 0;
}
static int
on_readline_display_backlog (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
display_backlog (ctx);
return 0;
}
static int
on_readline_display_full_log (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
display_full_log (ctx);
return 0;
}
static int
on_readline_edit_input (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
launch_input_editor (ctx);
return 0;
}
static int
on_readline_redraw_screen (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
redraw_screen (ctx);
return 0;
}
static int
on_readline_insert_attribute (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
ctx->awaiting_mirc_escape = true;
return 0;
}
static int
on_readline_start_paste_mode (int count, int key)
{
(void) count;
(void) key;
struct app_context *ctx = g_ctx;
ctx->in_bracketed_paste = true;
return 0;
}
static int static int
on_readline_return (int count, int key) on_readline_return (int count, int key)
{ {
(void) count; (void) count;
(void) key; (void) key;
struct app_context *ctx = g_ctx;
// Let readline pass the line to our input handler // Let readline pass the line to our input handler
rl_done = 1; rl_done = 1;
// Hide the line, don't redisplay it // Hide the line, don't redisplay it
struct app_context *ctx = g_ctx;
input_hide (&ctx->input); input_hide (&ctx->input);
input_restore (&ctx->input); input_restore (&ctx->input);
return 0; return 0;
@ -11602,18 +11696,7 @@ app_readline_init (void)
// our dear user could potentionally rig things up in a way that might // our dear user could potentionally rig things up in a way that might
// result in some funny unspecified behaviour // result in some funny unspecified behaviour
rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
rl_add_defun ("goto-buffer", on_readline_goto_buffer, -1);
rl_add_defun ("switch-buffer", on_readline_switch_buffer, -1);
rl_add_defun ("display-backlog", on_readline_display_backlog, -1);
rl_add_defun ("display-full-log", on_readline_display_full_log, -1);
rl_add_defun ("edit-input", on_readline_edit_input, -1);
rl_add_defun ("redraw-screen", on_readline_redraw_screen, -1);
rl_add_defun ("insert-attribute", on_readline_insert_attribute, -1);
rl_add_defun ("start-paste-mode", on_readline_start_paste_mode, -1);
rl_add_defun ("send-line", on_readline_return, -1); rl_add_defun ("send-line", on_readline_return, -1);
bind_common_keys (ctx); bind_common_keys (ctx);
// Move native history commands // Move native history commands
@ -11636,113 +11719,6 @@ app_readline_init (void)
#ifdef HAVE_EDITLINE #ifdef HAVE_EDITLINE
static unsigned char
on_editline_goto_buffer (EditLine *editline, int key)
{
(void) editline;
struct app_context *ctx = g_ctx;
if (!jump_to_buffer (ctx, key - '0'))
return CC_ERROR;
return CC_NORM;
}
static unsigned char
on_editline_previous_buffer (EditLine *editline, int key)
{
(void) editline;
(void) key;
struct app_context *ctx = g_ctx;
buffer_activate (ctx, buffer_previous (ctx, 1));
return CC_NORM;
}
static unsigned char
on_editline_next_buffer (EditLine *editline, int key)
{
(void) editline;
(void) key;
struct app_context *ctx = g_ctx;
buffer_activate (ctx, buffer_next (ctx, 1));
return CC_NORM;
}
static unsigned char
on_editline_switch_buffer (EditLine *editline, int key)
{
(void) editline;
(void) key;
struct app_context *ctx = g_ctx;
if (ctx->last_buffer)
buffer_activate (ctx, ctx->last_buffer);
else
input_ding (&ctx->input);
return CC_NORM;
}
static unsigned char
on_editline_display_backlog (EditLine *editline, int key)
{
(void) editline;
(void) key;
display_backlog (g_ctx);
return CC_NORM;
}
static unsigned char
on_editline_display_full_log (EditLine *editline, int key)
{
(void) editline;
(void) key;
display_full_log (g_ctx);
return CC_NORM;
}
static unsigned char
on_editline_edit_input (EditLine *editline, int key)
{
(void) editline;
(void) key;
launch_input_editor (g_ctx);
return CC_NORM;
}
static unsigned char
on_editline_redraw_screen (EditLine *editline, int key)
{
(void) editline;
(void) key;
redraw_screen (g_ctx);
return CC_NORM;
}
static unsigned char
on_editline_insert_attribute (EditLine *editline, int key)
{
(void) editline;
(void) key;
g_ctx->awaiting_mirc_escape = true;
return CC_NORM;
}
static unsigned char
on_editline_start_paste_mode (EditLine *editline, int key)
{
(void) editline;
(void) key;
g_ctx->in_bracketed_paste = true;
return CC_NORM;
}
static unsigned char static unsigned char
on_editline_complete (EditLine *editline, int key) on_editline_complete (EditLine *editline, int key)
{ {
@ -11831,29 +11807,11 @@ on_editline_return (EditLine *editline, int key)
static void static void
app_editline_init (struct input *self) app_editline_init (struct input *self)
{ {
#define XX(name, help, fn) { name, help, on_editline_ ## fn },
// el_set() leaks memory in 20150325 and other versions, we need wchar_t // el_set() leaks memory in 20150325 and other versions, we need wchar_t
static const struct { const wchar_t *name; const wchar_t *help; el_wset (self->editline, EL_ADDFN,
unsigned char (*func) (EditLine *, int); } x[] = L"send-line", L"Send line", on_editline_return);
{ el_wset (self->editline, EL_ADDFN,
XX( L"goto-buffer", L"Go to buffer", goto_buffer ) L"complete", L"Complete word", on_editline_complete);
XX( L"previous-buffer", L"Previous buffer", previous_buffer )
XX( L"next-buffer", L"Next buffer", next_buffer )
XX( L"switch-buffer", L"Switch buffer", switch_buffer )
XX( L"display-backlog", L"Show backlog", display_backlog )
XX( L"display-full-log", L"Show full log", display_full_log )
XX( L"edit-input", L"Edit input", edit_input )
XX( L"redraw-screen", L"Redraw screen", redraw_screen )
XX( L"insert-attribute", L"mIRC formatting", insert_attribute )
XX( L"start-paste-mode", L"Bracketed paste", start_paste_mode )
XX( L"send-line", L"Send line", return )
XX( L"complete", L"Complete word", complete )
};
for (size_t i = 0; i < N_ELEMENTS (x); i++)
el_wset (self->editline, EL_ADDFN, x[i].name, x[i].help, x[i].func);
#undef XX
bind_common_keys (g_ctx); bind_common_keys (g_ctx);