fix(editor): follow live terminal size in external editor
All checks were successful
CI Code / Check spelling (pull_request) Successful in 14s
CI Code / Check coding style (pull_request) Successful in 22s
CI Code / Linux (debian) (pull_request) Successful in 4m24s
CI Code / Code Coverage (pull_request) Successful in 3m8s
CI Code / Linux (ubuntu) (pull_request) Successful in 8m41s
CI Code / Linux (arch) (pull_request) Successful in 13m2s
CI Code / Check spelling (push) Successful in 15s
CI Code / Check coding style (push) Successful in 23s
CI Code / Code Coverage (push) Successful in 3m13s
Publish Docker image / Push Docker image to Docker Hub (push) Successful in 3m35s
CI Code / Linux (debian) (push) Successful in 5m0s
CI Code / Linux (ubuntu) (push) Successful in 5m5s
CI Code / Linux (arch) (push) Successful in 6m30s

The compose editor is spawned via fork+execvp and inherits profanity's
LINES/COLUMNS, which hold the size captured at startup and are never
refreshed on resize. A curses editor (nano, vim, ...) honors them over
the real window, so it renders at the launch-time size after a resize.

Drop LINES/COLUMNS from the child's environment before forking so its
curses falls back to ioctl(TIOCGWINSZ). Assembling the env in the parent
lets the child only reassign environ instead of calling unsetenv()
between fork and exec, keeping it clear of unsetenv()'s allocator work in
the multithreaded fork->exec window. profanity itself is unaffected.
This commit is contained in:
2026-07-04 14:08:08 +03:00
committed by Jabber Developer
parent 2be16df905
commit 964f73d0ad

View File

@@ -25,6 +25,8 @@
#include "ui/ui.h"
#include "xmpp/xmpp.h"
extern char** environ;
typedef struct EditorContext
{
gchar* filename;
@@ -140,6 +142,22 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
GSource* sigchld_warmup = g_child_watch_source_new(getpid());
g_source_unref(sigchld_warmup);
// Build the editor's env without LINES/COLUMNS pre-fork, so the child only
// reassigns environ instead of calling unsetenv() between fork and exec.
// The editor's (n)curses then reads the live window via ioctl(TIOCGWINSZ).
gsize env_len = 0;
while (environ[env_len]) {
env_len++;
}
gchar** editor_env = g_new0(gchar*, env_len + 1);
gsize env_kept = 0;
for (gsize i = 0; i < env_len; i++) {
if (g_str_has_prefix(environ[i], "LINES=") || g_str_has_prefix(environ[i], "COLUMNS=")) {
continue;
}
editor_env[env_kept++] = environ[i];
}
pid_t pid = fork();
if (pid == -1) {
log_error("[Editor] Failed to fork: %s", strerror(errno));
@@ -148,12 +166,14 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
ui_resize();
cons_show_error("Failed to start editor: %s", strerror(errno));
g_strfreev(editor_argv);
g_free(ctx->filename);
g_free(editor_env);
g_free(ctx);
return TRUE;
} else if (pid == 0) {
// Child process: Inherits TTY from parent
environ = editor_env; // live TIOCGWINSZ size, not the inherited LINES/COLUMNS
// SIGTSTP=SIG_DFL lets vim's :stop / Ctrl-Z work; profanity catches
// the STOPPED state via editor_check_stopped() and drops to the shell.
signal(SIGINT, SIG_DFL);
@@ -170,6 +190,7 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
editor_pid = pid;
g_child_watch_add((GPid)pid, _editor_exit_cb, ctx);
g_strfreev(editor_argv);
g_free(editor_env); // array only; strings are borrowed from environ
return FALSE;
}