degesch: allow launching an editor for input
Useful for editing multiline text (such as making it single-line). Some refactoring and cleanup.
This commit is contained in:
parent
37e9165548
commit
e101afab38
2
NEWS
2
NEWS
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
* degesch: libedit backend works again
|
* degesch: libedit backend works again
|
||||||
|
|
||||||
|
* degesch: added capability to edit the input line using VISUAL/EDITOR
|
||||||
|
|
||||||
* degesch: correctly respond to stopping and resuming (SIGTSTP)
|
* degesch: correctly respond to stopping and resuming (SIGTSTP)
|
||||||
|
|
||||||
* degesch: fixed decoding of text formatting
|
* degesch: fixed decoding of text formatting
|
||||||
|
|
43
common.c
43
common.c
|
@ -54,6 +54,49 @@ str_vector_find (const struct str_vector *v, const char *s)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This differs from the non-unique version in that we expect the filename
|
||||||
|
/// to be something like a pattern for mkstemp(), so the resulting path can
|
||||||
|
/// reside in a system-wide directory with no risk of a conflict.
|
||||||
|
static char *
|
||||||
|
resolve_relative_runtime_unique_filename (const char *filename)
|
||||||
|
{
|
||||||
|
struct str path;
|
||||||
|
str_init (&path);
|
||||||
|
|
||||||
|
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
|
||||||
|
if (runtime_dir && *runtime_dir == '/')
|
||||||
|
str_append (&path, runtime_dir);
|
||||||
|
else
|
||||||
|
str_append (&path, "/tmp");
|
||||||
|
str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
|
||||||
|
|
||||||
|
// Try to create the file's ancestors;
|
||||||
|
// typically the user will want to immediately create a file in there
|
||||||
|
const char *last_slash = strrchr (path.str, '/');
|
||||||
|
if (last_slash && last_slash != path.str)
|
||||||
|
{
|
||||||
|
char *copy = xstrndup (path.str, last_slash - path.str);
|
||||||
|
(void) mkdir_with_parents (copy, NULL);
|
||||||
|
free (copy);
|
||||||
|
}
|
||||||
|
return str_steal (&path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
xwrite (int fd, const char *data, size_t len, struct error **e)
|
||||||
|
{
|
||||||
|
size_t written = 0;
|
||||||
|
while (written < len)
|
||||||
|
{
|
||||||
|
ssize_t res = write (fd, data + written, len - written);
|
||||||
|
if (res >= 0)
|
||||||
|
written += res;
|
||||||
|
else if (errno != EINTR)
|
||||||
|
FAIL ("%s", strerror (errno));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Logging -----------------------------------------------------------------
|
// --- Logging -----------------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
248
degesch.c
248
degesch.c
|
@ -149,8 +149,6 @@ struct input
|
||||||
int saved_mark; ///< Saved mark
|
int saved_mark; ///< Saved mark
|
||||||
#elif defined HAVE_EDITLINE
|
#elif defined HAVE_EDITLINE
|
||||||
EditLine *editline; ///< The EditLine object
|
EditLine *editline; ///< The EditLine object
|
||||||
char *(*saved_prompt) (EditLine *); ///< Saved prompt function
|
|
||||||
char saved_char; ///< Saved char for the prompt
|
|
||||||
#endif // HAVE_EDITLINE
|
#endif // HAVE_EDITLINE
|
||||||
|
|
||||||
char *prompt; ///< The prompt we use
|
char *prompt; ///< The prompt we use
|
||||||
|
@ -220,14 +218,22 @@ input_set_prompt (struct input *self, char *prompt)
|
||||||
rl_redisplay ();
|
rl_redisplay ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_erase_content (struct input *self)
|
||||||
|
{
|
||||||
|
(void) self;
|
||||||
|
|
||||||
|
rl_replace_line ("", false);
|
||||||
|
rl_redisplay ();
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_erase (struct input *self)
|
input_erase (struct input *self)
|
||||||
{
|
{
|
||||||
(void) self;
|
(void) self;
|
||||||
|
|
||||||
rl_set_prompt ("");
|
rl_set_prompt ("");
|
||||||
rl_replace_line ("", false);
|
input_erase_content (self);
|
||||||
rl_redisplay ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -269,6 +275,13 @@ input_insert (struct input *self, const char *s)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
input_get_content (struct input *self)
|
||||||
|
{
|
||||||
|
(void) self;
|
||||||
|
return rl_copy_text (0, rl_end);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static int app_readline_init (void);
|
static int app_readline_init (void);
|
||||||
|
@ -560,29 +573,39 @@ input_make_empty_prompt (EditLine *editline)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
input_erase (struct input *self)
|
input_erase_content (struct input *self)
|
||||||
{
|
{
|
||||||
const LineInfoW *info = el_wline (self->editline);
|
const LineInfoW *info = el_wline (self->editline);
|
||||||
int len = info->lastchar - info->buffer;
|
int len = info->lastchar - info->buffer;
|
||||||
int point = info->cursor - info->buffer;
|
int point = info->cursor - info->buffer;
|
||||||
el_cursor (self->editline, len - point);
|
el_cursor (self->editline, len - point);
|
||||||
el_wdeletestr (self->editline, len);
|
el_wdeletestr (self->editline, len);
|
||||||
|
|
||||||
// XXX: this doesn't seem to save the escape character
|
|
||||||
el_get (self->editline, EL_PROMPT, &self->saved_prompt, &self->saved_char);
|
|
||||||
el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
|
|
||||||
input_redisplay (self);
|
input_redisplay (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
input_erase (struct input *self)
|
||||||
|
{
|
||||||
|
el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
|
||||||
|
input_erase_content (self);
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
input_insert (struct input *self, const char *s)
|
input_insert (struct input *self, const char *s)
|
||||||
{
|
{
|
||||||
bool success = !el_insertstr (self->editline, s);
|
bool success = !*s || !el_insertstr (self->editline, s);
|
||||||
if (self->prompt_shown > 0)
|
if (self->prompt_shown > 0)
|
||||||
input_redisplay (self);
|
input_redisplay (self);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
input_get_content (struct input *self)
|
||||||
|
{
|
||||||
|
const LineInfo *info = el_line (self->editline);
|
||||||
|
return xstrndup (info->buffer, info->lastchar - info->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -699,8 +722,7 @@ input_show (struct input *self)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
input_restore (self);
|
input_restore (self);
|
||||||
// Would have used "saved_char" but it doesn't seem to work.
|
// XXX: the ignore doesn't quite work, see https://gnats.netbsd.org/47539
|
||||||
// And it doesn't even when it does anyway (it seems to just strip it).
|
|
||||||
el_set (self->editline,
|
el_set (self->editline,
|
||||||
EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
|
EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
|
||||||
input_redisplay (self);
|
input_redisplay (self);
|
||||||
|
@ -1518,6 +1540,8 @@ struct app_context
|
||||||
struct str input_buffer; ///< Buffered pasted content
|
struct str input_buffer; ///< Buffered pasted content
|
||||||
|
|
||||||
bool running_backlog_helper; ///< Running a backlog helper
|
bool running_backlog_helper; ///< Running a backlog helper
|
||||||
|
bool running_editor; ///< Running editor for the input
|
||||||
|
char *editor_filename; ///< The file being edited by user
|
||||||
int terminal_suspended; ///< Terminal suspension level
|
int terminal_suspended; ///< Terminal suspension level
|
||||||
|
|
||||||
struct plugin *plugins; ///< Loaded plugins
|
struct plugin *plugins; ///< Loaded plugins
|
||||||
|
@ -1624,6 +1648,8 @@ app_context_free (struct app_context *self)
|
||||||
|
|
||||||
input_free (&self->input);
|
input_free (&self->input);
|
||||||
str_free (&self->input_buffer);
|
str_free (&self->input_buffer);
|
||||||
|
|
||||||
|
free (self->editor_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void refresh_prompt (struct app_context *ctx);
|
static void refresh_prompt (struct app_context *ctx);
|
||||||
|
@ -10107,47 +10133,52 @@ jump_to_buffer (struct app_context *ctx, int n)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static pid_t
|
||||||
exec_backlog_helper (const char *command, FILE *backlog)
|
spawn_helper_child (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
dup2 (fileno (backlog), STDIN_FILENO);
|
suspend_terminal (ctx);
|
||||||
|
pid_t child = fork ();
|
||||||
// Put the child into a new foreground process group
|
switch (child)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
{
|
||||||
|
int saved_errno = errno;
|
||||||
|
resume_terminal (ctx);
|
||||||
|
errno = saved_errno;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0:
|
||||||
|
// Put the child in a new foreground process group
|
||||||
hard_assert (setpgid (0, 0) != -1);
|
hard_assert (setpgid (0, 0) != -1);
|
||||||
hard_assert (tcsetpgrp (STDOUT_FILENO, getpgid (0)) != -1);
|
hard_assert (tcsetpgrp (STDOUT_FILENO, getpgid (0)) != -1);
|
||||||
|
break;
|
||||||
execl ("/bin/sh", "/bin/sh", "-c", command, NULL);
|
default:
|
||||||
print_error ("%s: %s", "Failed to launch backlog helper", strerror (errno));
|
// Make sure of it in the parent as well before continuing
|
||||||
_exit (EXIT_FAILURE);
|
(void) setpgid (child, child);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
launch_backlog_helper (struct app_context *ctx, FILE *backlog)
|
launch_backlog_helper (struct app_context *ctx, FILE *backlog)
|
||||||
{
|
{
|
||||||
hard_assert (!ctx->running_backlog_helper);
|
hard_assert (!ctx->running_backlog_helper);
|
||||||
suspend_terminal (ctx);
|
switch (spawn_helper_child (ctx))
|
||||||
|
|
||||||
pid_t child = fork ();
|
|
||||||
if (child == 0)
|
|
||||||
exec_backlog_helper (get_config_string
|
|
||||||
(ctx->config.root, "behaviour.backlog_helper"), backlog);
|
|
||||||
|
|
||||||
if (child == -1)
|
|
||||||
{
|
{
|
||||||
int saved_errno = errno;
|
case 0:
|
||||||
resume_terminal (ctx);
|
dup2 (fileno (backlog), 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: #s",
|
log_global_error (ctx, "#s: #s",
|
||||||
"Failed to launch backlog helper", strerror (saved_errno));
|
"Failed to launch backlog helper", strerror (errno));
|
||||||
}
|
break;
|
||||||
else
|
default:
|
||||||
{
|
|
||||||
// Make sure the child has its own process group
|
|
||||||
(void) setpgid (child, child);
|
|
||||||
|
|
||||||
ctx->running_backlog_helper = true;
|
ctx->running_backlog_helper = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose (backlog);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -10168,6 +10199,7 @@ display_backlog (struct app_context *ctx)
|
||||||
rewind (backlog);
|
rewind (backlog);
|
||||||
set_cloexec (fileno (backlog));
|
set_cloexec (fileno (backlog));
|
||||||
launch_backlog_helper (ctx, backlog);
|
launch_backlog_helper (ctx, backlog);
|
||||||
|
fclose (backlog);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -10189,6 +10221,103 @@ display_full_log (struct app_context *ctx)
|
||||||
|
|
||||||
set_cloexec (fileno (full_log));
|
set_cloexec (fileno (full_log));
|
||||||
launch_backlog_helper (ctx, full_log);
|
launch_backlog_helper (ctx, full_log);
|
||||||
|
fclose (full_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
dump_input_to_file (struct app_context *ctx, char *template, struct error **e)
|
||||||
|
{
|
||||||
|
int fd = mkstemp (template);
|
||||||
|
if (fd < 0)
|
||||||
|
FAIL ("%s", strerror (errno));
|
||||||
|
|
||||||
|
char *input = input_get_content (&ctx->input);
|
||||||
|
bool success = xwrite (fd, input, strlen (input), e);
|
||||||
|
free (input);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
(void) unlink (template);
|
||||||
|
|
||||||
|
xclose (fd);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
try_dump_input_to_file (struct app_context *ctx)
|
||||||
|
{
|
||||||
|
char *template = resolve_filename
|
||||||
|
("input.XXXXXX", resolve_relative_runtime_unique_filename);
|
||||||
|
|
||||||
|
struct error *e = NULL;
|
||||||
|
if (dump_input_to_file (ctx, template, &e))
|
||||||
|
return template;
|
||||||
|
|
||||||
|
log_global_error (ctx, "#s: #s",
|
||||||
|
"Failed to create a temporary file for editing", e->message);
|
||||||
|
error_free (e);
|
||||||
|
free (template);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
launch_input_editor (struct app_context *ctx)
|
||||||
|
{
|
||||||
|
char *filename;
|
||||||
|
if (!(filename = try_dump_input_to_file (ctx)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const char *command;
|
||||||
|
if (!(command = getenv ("VISUAL"))
|
||||||
|
&& !(command = getenv ("EDITOR")))
|
||||||
|
command = "vi";
|
||||||
|
|
||||||
|
hard_assert (!ctx->running_editor);
|
||||||
|
switch (spawn_helper_child (ctx))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
execlp (command, command, filename, NULL);
|
||||||
|
print_error ("%s: %s",
|
||||||
|
"Failed to launch editor", strerror (errno));
|
||||||
|
_exit (EXIT_FAILURE);
|
||||||
|
case -1:
|
||||||
|
log_global_error (ctx, "#s: #s",
|
||||||
|
"Failed to launch editor", strerror (errno));
|
||||||
|
free (filename);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ctx->running_editor = true;
|
||||||
|
ctx->editor_filename = filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_edited_input (struct app_context *ctx)
|
||||||
|
{
|
||||||
|
struct str input;
|
||||||
|
str_init (&input);
|
||||||
|
|
||||||
|
struct error *e = NULL;
|
||||||
|
if (!read_file (ctx->editor_filename, &input, &e))
|
||||||
|
{
|
||||||
|
log_global_error (ctx, "#s: #s", "Input editing failed", e->message);
|
||||||
|
error_free (e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
input_erase_content (&ctx->input);
|
||||||
|
|
||||||
|
if (!input_insert (&ctx->input, input.str))
|
||||||
|
log_global_error (ctx, "#s: #s", "Input editing failed",
|
||||||
|
"could not re-insert the modified text");
|
||||||
|
|
||||||
|
if (unlink (ctx->editor_filename))
|
||||||
|
log_global_error (ctx, "Could not unlink `#s': #s",
|
||||||
|
ctx->editor_filename, strerror (errno));
|
||||||
|
|
||||||
|
free (ctx->editor_filename);
|
||||||
|
ctx->editor_filename = NULL;
|
||||||
|
str_free (&input);
|
||||||
|
|
||||||
|
ctx->running_editor = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -10204,6 +10333,7 @@ bind_common_keys (struct app_context *ctx)
|
||||||
|
|
||||||
input_bind_meta (self, 'm', "insert-attribute");
|
input_bind_meta (self, 'm', "insert-attribute");
|
||||||
input_bind_meta (self, 'h', "display-full-log");
|
input_bind_meta (self, 'h', "display-full-log");
|
||||||
|
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");
|
||||||
|
@ -10275,6 +10405,17 @@ on_readline_display_full_log (int count, int key)
|
||||||
return 0;
|
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
|
static int
|
||||||
on_readline_redraw_screen (int count, int key)
|
on_readline_redraw_screen (int count, int key)
|
||||||
{
|
{
|
||||||
|
@ -10380,6 +10521,7 @@ app_readline_init (void)
|
||||||
rl_add_defun ("goto-buffer", on_readline_goto_buffer, -1);
|
rl_add_defun ("goto-buffer", on_readline_goto_buffer, -1);
|
||||||
rl_add_defun ("display-backlog", on_readline_display_backlog, -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 ("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 ("redraw-screen", on_readline_redraw_screen, -1);
|
||||||
rl_add_defun ("insert-attribute", on_readline_insert_attribute, -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 ("start-paste-mode", on_readline_start_paste_mode, -1);
|
||||||
|
@ -10460,6 +10602,16 @@ on_editline_display_full_log (EditLine *editline, int key)
|
||||||
return CC_NORM;
|
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
|
static unsigned char
|
||||||
on_editline_redraw_screen (EditLine *editline, int key)
|
on_editline_redraw_screen (EditLine *editline, int key)
|
||||||
{
|
{
|
||||||
|
@ -10588,6 +10740,7 @@ app_editline_init (struct input *self)
|
||||||
{ "next-buffer", "Next buffer", on_editline_next_buffer },
|
{ "next-buffer", "Next buffer", on_editline_next_buffer },
|
||||||
{ "display-backlog", "Show backlog", on_editline_display_backlog },
|
{ "display-backlog", "Show backlog", on_editline_display_backlog },
|
||||||
{ "display-full-log", "Show full log", on_editline_display_full_log },
|
{ "display-full-log", "Show full log", on_editline_display_full_log },
|
||||||
|
{ "edit-input", "Edit input", on_editline_edit_input },
|
||||||
{ "redraw-screen", "Redraw screen", on_editline_redraw_screen },
|
{ "redraw-screen", "Redraw screen", on_editline_redraw_screen },
|
||||||
{ "insert-attribute", "mIRC formatting", on_editline_insert_attribute },
|
{ "insert-attribute", "mIRC formatting", on_editline_insert_attribute },
|
||||||
{ "start-paste-mode", "Bracketed paste", on_editline_start_paste_mode },
|
{ "start-paste-mode", "Bracketed paste", on_editline_start_paste_mode },
|
||||||
|
@ -10846,19 +10999,22 @@ try_reap_child (struct app_context *ctx)
|
||||||
if (!zombie)
|
if (!zombie)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!ctx->running_backlog_helper)
|
|
||||||
{
|
|
||||||
print_debug ("an unknown child has died");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (WIFSTOPPED (status))
|
if (WIFSTOPPED (status))
|
||||||
{
|
{
|
||||||
// We could also send SIGCONT but what's the point
|
// We could also send SIGCONT but what's the point
|
||||||
|
print_debug ("a child has been stopped, killing its process group");
|
||||||
kill (-zombie, SIGKILL);
|
kill (-zombie, SIGKILL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->running_backlog_helper)
|
||||||
ctx->running_backlog_helper = false;
|
ctx->running_backlog_helper = false;
|
||||||
|
else if (!ctx->running_editor)
|
||||||
|
{
|
||||||
|
print_debug ("an unknown child has died");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
hard_assert (tcsetpgrp (STDOUT_FILENO, getpgid (0)) != -1);
|
hard_assert (tcsetpgrp (STDOUT_FILENO, getpgid (0)) != -1);
|
||||||
resume_terminal (ctx);
|
resume_terminal (ctx);
|
||||||
|
|
||||||
|
@ -10868,6 +11024,8 @@ try_reap_child (struct app_context *ctx)
|
||||||
else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
|
else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
|
||||||
log_global_error (ctx,
|
log_global_error (ctx,
|
||||||
"Child returned status #d", WEXITSTATUS (status));
|
"Child returned status #d", WEXITSTATUS (status));
|
||||||
|
else if (ctx->running_editor)
|
||||||
|
process_edited_input (ctx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue