merge: sync upstream profanity-im/profanity

Sync with upstream profanity-im/profanity.

Major upstream changes incorporated:

Memory management
  - Replace malloc+memset with g_new0 throughout codebase
  - Adopt auto_gchar / auto_gcharv / auto_gerror / auto_jid cleanup macros
  - Replace free() with g_free() for GAlloc'd memory

Editor rewrite
  - Remove pthread-based async editor; use GChildWatch callback API
  - New launch_editor(initial_content, callback, user_data) interface
  - Proper signal handling (SIGINT, SIGTSTP, SIGPIPE reset in child)
  - ui_suspend()/ui_resume() integration for TTY management

OMEMO improvements
  - Dual backend support: libsignal-protocol-c and libomemo-c
  - Proper pre-key removal after use (XEP-0384 compliance)
  - Automatic pre-key regeneration when store drops below threshold
  - New functions: omemo_is_device_active(), omemo_is_jid_trusted()
  - omemo_get_jid_untrusted_fingerprints() for better error messages
  - Fingerprint notifications on new device identity discovery
  - Deterministic pre-key ID generation tracking max_pre_key_id
  - omemo_trust_changed() UI updates on trust state changes

JID validation
  - RFC 6122-compliant validation in jid_is_valid()
  - Character-level checks (RFC 6122 forbidden chars: & ' / : < > @)
  - Length limits: 1023 per component, 3071 total
  - New jid_is_valid_user_jid() for user vs. service JID distinction

Database
  - Schema migration v3: UNIQUE constraint on archive_id for deduplication
  - Triggers for corrected message tracking (replaces_db_id / replaced_by_db_id)
  - db_history_result_t return type, _truncate_datetime_suffix()

UI / console
  - win_warn_needed() / win_warn_sent() warning deduplication hash table
  - PAD_MIN_HEIGHT dynamic pad sizing with PAD_THRESHOLD auto-cleanup
  - Spellcheck integration in input field with Unicode word detection
  - cons_spellcheck_setting() for /settings ui output
  - /[command]? shortcut for command help

Account config
  - Account name sanitization for GKeyFile special chars ([ ] = # \n \r)
  - Replace popen() with g_spawn_sync() for eval_password
  - TLS policy: add "direct" option alongside legacy

Connection
  - Port validation with g_assert (0–65535)
  - SHA-256 certificate fingerprint support (XMPP_CERT_PUBKEY_FINGERPRINT_SHA256)
  - "direct" TLS policy alias for legacy SSL

Common utilities
  - str_xml_sanitize() for XML 1.0 illegal character removal
  - string_matches_one_of() with formatted error messages
  - valid_tls_policy_option() helper
  - prof_date_time_format_iso8601() utility
  - Improved strip_arg_quotes() with backslash unescaping
  - prof_occurrences() uses g_slist_prepend + reverse for performance

PGP / OX
  - Proper GPGME resource cleanup with goto-cleanup pattern
  - g_string_free(xmppuri) leak fix in _ox_key_lookup

CSV export
  - Use GString + g_file_set_contents instead of raw write() syscalls

Tests
  - Restructured into subdirectories: command/, config/, xmpp/, ui/, omemo/, otr/, pgp/
  - New test_cmd_ac.c for autocompleter unit tests
  - Updated stubs for new UI suspend/resume functions

License headers
  - Migrate to SPDX-3.0 identifiers (GPL-3.0-or-later WITH OpenSSL-exception)

────────────────────────────────────────────────────
cproof-specific preservations:

  - XEP-0308 LMC: replace_id ?: id logic in message/stanza/omemo
  - Force encryption: cmd_force_encryption, test_forced_encryption
  - CWE-134: format string protection (cons_show("%s", ...))
  - y_start_pos-based paging in window.c
  - db_history_result_t return type, _truncate_datetime_suffix()

Merge-time fixes:

  - common.c: format-security (-Werror) — cons_show(errmsg) → cons_show("%s", errmsg)
  - database.c: null-deref guard — !msg->timestamp → msg && !msg->timestamp
  - console.c: implicit size_t → int cast — (int)(maxlen + 1)
  - tlscerts.c: %d for size_t — %zu

Build system:
  - Kept autotools (Makefile.am, configure.ac); upstream uses Meson
  - Restored deleted files: bootstrap.sh, autogen.sh, ax_valgrind_check.m4, configure-debug
  - Updated Makefile.am test paths for subdirectory structure
  - Added test_cmd_ac, test_forced_encryption to test sources

Functional tests:
  - Use cproof version; upstream requires stbbr_for_xmlns from updated stabber
  - Not yet available in devs/stabber fork

Closes #64

Merge author: jabber.developer2
Commits authors:
 Michael Vetter <jubalh@iodoru.org>
& Steffen Jaeckel <s@jaeckel.eu>
This commit is contained in:
2026-05-26 17:48:14 +00:00
parent 3b673150b4
commit 72f4f186da
303 changed files with 10658 additions and 9857 deletions

View File

@@ -3,41 +3,18 @@
* vim: expandtab:ts=4:sts=4:sw=4
*
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
*
* This file is part of Profanity.
*
* Profanity is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Profanity is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give permission to
* link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all of the
* code used other than OpenSSL. If you modify file(s) with this exception, you
* may extend this exception to your version of the file(s), but you are not
* obligated to do so. If you do not wish to do so, delete this exception
* statement from your version. If you delete this exception statement from all
* source files in the program, then also delete it here.
* Copyright (C) 2019 - 2026 Michael Vetter <jubalh@iodoru.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later WITH OpenSSL-exception
*/
#include "config.h"
#include "database.h"
#include "ui/win_types.h"
#include "ui/window_list.h"
#ifdef HAVE_OMEMO
#include "omemo/omemo.h"
#endif
#include <stdlib.h>
#include <string.h>
@@ -65,6 +42,31 @@
#include "xmpp/roster_list.h"
#include "ai/ai_client.h"
static const int PAD_MIN_HEIGHT = 100;
static const int PAD_THRESHOLD = 3000;
static gboolean _in_redraw = FALSE;
static void
_win_ensure_pad_capacity(ProfWin* window, WINDOW* win, int lines_needed)
{
if (!win) {
return;
}
int cur_height = getmaxy(win);
int cur_width = getmaxx(win);
if (lines_needed >= cur_height - 1) {
// If we are getting too large, trigger a redraw to clean up old lines
// but only if we are not already in a redraw process.
if (window && cur_height >= PAD_THRESHOLD && !_in_redraw) {
win_redraw(window);
} else {
// resize to required lines + some buffer for next messages
int new_height = lines_needed + 100;
wresize(win, new_height, cur_width);
}
}
}
static const char* LOADING_MESSAGE = "Loading older messages…";
static const char* CONS_WIN_TITLE = "CProof. Type /help for help information.";
static const char* XML_WIN_TITLE = "XML Console";
@@ -75,7 +77,7 @@ static void
_win_printf(ProfWin* window, const char* show_char, int pad_indent, GDateTime* timestamp, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message_id, const char* const message, ...);
static void _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDateTime* time,
int flags, theme_item_t theme_item, const char* const from, const char* const message, DeliveryReceipt* receipt);
static void _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent);
static void _win_print_wrapped(WINDOW* win, const char* const message, int indent, int pad_indent);
// Helper: clamp a subwindow width to a sane range [1, cols-1] if possible
static int
@@ -111,9 +113,9 @@ _win_create_simple_layout(void)
{
int cols = getmaxx(stdscr);
ProfLayoutSimple* layout = malloc(sizeof(ProfLayoutSimple));
ProfLayoutSimple* layout = g_new0(ProfLayoutSimple, 1);
layout->base.type = LAYOUT_SIMPLE;
layout->base.win = newpad(PAD_SIZE, cols);
layout->base.win = newpad(PAD_MIN_HEIGHT, cols);
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
layout->base.buffer = buffer_create();
layout->base.y_pos = 0;
@@ -129,9 +131,9 @@ _win_create_split_layout(void)
{
int cols = getmaxx(stdscr);
ProfLayoutSplit* layout = malloc(sizeof(ProfLayoutSplit));
ProfLayoutSplit* layout = g_new0(ProfLayoutSplit, 1);
layout->base.type = LAYOUT_SPLIT;
layout->base.win = newpad(PAD_SIZE, cols);
layout->base.win = newpad(PAD_MIN_HEIGHT, cols);
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
layout->base.buffer = buffer_create();
layout->base.y_pos = 0;
@@ -148,7 +150,7 @@ _win_create_split_layout(void)
ProfWin*
win_create_console(void)
{
ProfConsoleWin* new_win = malloc(sizeof(ProfConsoleWin));
ProfConsoleWin* new_win = g_new0(ProfConsoleWin, 1);
new_win->window.type = WIN_CONSOLE;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_split_layout();
@@ -159,8 +161,7 @@ win_create_console(void)
ProfWin*
win_create_chat(const char* const barejid)
{
assert(barejid != NULL);
ProfChatWin* new_win = malloc(sizeof(ProfChatWin));
ProfChatWin* new_win = g_new0(ProfChatWin, 1);
new_win->window.type = WIN_CHAT;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_simple_layout();
@@ -172,6 +173,9 @@ win_create_chat(const char* const barejid)
new_win->pgp_recv = FALSE;
new_win->pgp_send = FALSE;
new_win->is_omemo = FALSE;
#ifdef HAVE_OMEMO
new_win->omemo_trusted = omemo_is_jid_trusted(barejid);
#endif
new_win->is_ox = FALSE;
new_win->history_shown = FALSE;
new_win->unread = 0;
@@ -191,23 +195,22 @@ win_create_chat(const char* const barejid)
ProfWin*
win_create_muc(const char* const roomjid)
{
assert(roomjid != NULL);
ProfMucWin* new_win = malloc(sizeof(ProfMucWin));
ProfMucWin* new_win = g_new0(ProfMucWin, 1);
int cols = getmaxx(stdscr);
new_win->window.type = WIN_MUC;
new_win->window.scroll_state = WIN_SCROLL_INNER;
ProfLayoutSplit* layout = malloc(sizeof(ProfLayoutSplit));
ProfLayoutSplit* layout = g_new0(ProfLayoutSplit, 1);
layout->base.type = LAYOUT_SPLIT;
if (prefs_get_boolean(PREF_OCCUPANTS)) {
int subwin_cols = win_occpuants_cols();
layout->base.win = newpad(PAD_SIZE, cols - subwin_cols);
layout->base.win = newpad(PAD_MIN_HEIGHT, cols - subwin_cols);
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
layout->subwin = newpad(PAD_SIZE, subwin_cols);
layout->subwin = newpad(PAD_MIN_HEIGHT, subwin_cols);
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
} else {
layout->base.win = newpad(PAD_SIZE, (cols));
layout->base.win = newpad(PAD_MIN_HEIGHT, (cols));
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
layout->subwin = NULL;
}
@@ -238,6 +241,9 @@ win_create_muc(const char* const roomjid)
new_win->enctext = NULL;
new_win->message_char = NULL;
new_win->is_omemo = FALSE;
#ifdef HAVE_OMEMO
new_win->omemo_trusted = omemo_is_jid_trusted(roomjid);
#endif
new_win->last_message = NULL;
new_win->last_msg_id = NULL;
new_win->has_attention = FALSE;
@@ -250,9 +256,7 @@ win_create_muc(const char* const roomjid)
ProfWin*
win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback submit, ProfConfWinCallback cancel, const void* userdata)
{
assert(roomjid != NULL);
assert(form != NULL);
ProfConfWin* new_win = malloc(sizeof(ProfConfWin));
ProfConfWin* new_win = g_new0(ProfConfWin, 1);
new_win->window.type = WIN_CONFIG;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_simple_layout();
@@ -270,8 +274,7 @@ win_create_config(const char* const roomjid, DataForm* form, ProfConfWinCallback
ProfWin*
win_create_private(const char* const fulljid)
{
assert(fulljid != NULL);
ProfPrivateWin* new_win = malloc(sizeof(ProfPrivateWin));
ProfPrivateWin* new_win = g_new0(ProfPrivateWin, 1);
new_win->window.type = WIN_PRIVATE;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_simple_layout();
@@ -288,7 +291,7 @@ win_create_private(const char* const fulljid)
ProfWin*
win_create_xmlconsole(void)
{
ProfXMLWin* new_win = malloc(sizeof(ProfXMLWin));
ProfXMLWin* new_win = g_new0(ProfXMLWin, 1);
new_win->window.type = WIN_XML;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_simple_layout();
@@ -301,9 +304,7 @@ win_create_xmlconsole(void)
ProfWin*
win_create_plugin(const char* const plugin_name, const char* const tag)
{
assert(plugin_name != NULL);
assert(tag != NULL);
ProfPluginWin* new_win = malloc(sizeof(ProfPluginWin));
ProfPluginWin* new_win = g_new0(ProfPluginWin, 1);
new_win->window.type = WIN_PLUGIN;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_simple_layout();
@@ -319,8 +320,7 @@ win_create_plugin(const char* const plugin_name, const char* const tag)
ProfWin*
win_create_vcard(vCard* vcard)
{
assert(vcard != NULL);
ProfVcardWin* new_win = malloc(sizeof(ProfVcardWin));
ProfVcardWin* new_win = g_new0(ProfVcardWin, 1);
new_win->window.type = WIN_VCARD;
new_win->window.scroll_state = WIN_SCROLL_INNER;
new_win->window.layout = _win_create_simple_layout();
@@ -586,11 +586,11 @@ win_hide_subwin(ProfWin* window)
layout->subwin = NULL;
layout->sub_y_pos = 0;
int cols = getmaxx(stdscr);
wresize(layout->base.win, PAD_SIZE, cols);
wresize(layout->base.win, PAD_MIN_HEIGHT, cols);
win_redraw(window);
} else {
int cols = getmaxx(stdscr);
wresize(window->layout->win, PAD_SIZE, cols);
wresize(window->layout->win, PAD_MIN_HEIGHT, cols);
win_redraw(window);
}
}
@@ -612,27 +612,14 @@ win_show_subwin(ProfWin* window)
}
ProfLayoutSplit* layout = (ProfLayoutSplit*)window->layout;
// If a subwindow already exists (e.g. repeated call), destroy it to avoid leaks
/* Free a previous pad if win_show_subwin was called twice in a row
* without an intervening win_hide_subwin. */
if (layout->subwin) {
delwin(layout->subwin);
layout->subwin = NULL;
}
// Ensure minimum width to avoid creating a zero-width pad
if (subwin_cols <= 0) {
subwin_cols = 1;
}
layout->subwin = newpad(PAD_SIZE, subwin_cols);
if (layout->subwin == NULL) {
// Failed to allocate subwindow; keep base window resized to full width
log_error("Failed to create subwindow pad (cols=%d)", subwin_cols);
wresize(layout->base.win, PAD_SIZE, cols);
win_redraw(window);
return;
}
layout->subwin = newpad(PAD_MIN_HEIGHT, subwin_cols);
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
wresize(layout->base.win, PAD_MIN_HEIGHT, cols - subwin_cols);
win_redraw(window);
}
@@ -652,6 +639,10 @@ win_free(ProfWin* window)
}
free(window->layout);
if (window->warned_jids) {
g_hash_table_destroy(window->warned_jids);
}
switch (window->type) {
case WIN_CHAT:
{
@@ -715,6 +706,44 @@ win_free(ProfWin* window)
free(window);
}
gboolean
win_warn_needed(ProfWin* window, const char* const type, const char* const jid)
{
if (!window || !type || !jid) {
return TRUE;
}
if (!window->warned_jids) {
return TRUE;
}
auto_gchar gchar* key = g_strdup_printf("%s:%s", type, jid);
return !g_hash_table_contains(window->warned_jids, key);
}
void
win_warn_sent(ProfWin* window, const char* const type, const char* const jid)
{
if (!window || !type || !jid) {
return;
}
if (!window->warned_jids) {
window->warned_jids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
}
gchar* key = g_strdup_printf("%s:%s", type, jid);
g_hash_table_insert(window->warned_jids, key, GINT_TO_POINTER(1));
}
void
win_clear_warned_jids(ProfWin* window)
{
if (window && window->warned_jids) {
g_hash_table_remove_all(window->warned_jids);
}
}
void
win_page_up(ProfWin* window, int scroll_size)
{
@@ -811,7 +840,7 @@ win_page_down(ProfWin* window, int scroll_size)
GDateTime* now = g_date_time_new_now_local();
ProfBuffEntry* last_entry = buffer_get_entry(window->layout->buffer, bf_size - 1);
auto_gchar gchar* start = g_date_time_format_iso8601(last_entry->time);
auto_gchar gchar* end_date = g_date_time_format_iso8601(now);
auto_gchar gchar* end_date = prof_date_time_format_iso8601(NULL);
db_history_result_t db_response = chatwin_db_history((ProfChatWin*)window, start, end_date, FALSE);
if (db_response == DB_RESPONSE_EMPTY)
*scroll_state = WIN_SCROLL_REACHED_BOTTOM;
@@ -934,9 +963,9 @@ win_resize(ProfWin* window)
subwin_cols = win_occpuants_cols();
}
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
wresize(layout->base.win, PAD_SIZE, cols - subwin_cols);
wresize(layout->base.win, PAD_MIN_HEIGHT, cols - subwin_cols);
wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
wresize(layout->subwin, PAD_SIZE, subwin_cols);
wresize(layout->subwin, PAD_MIN_HEIGHT, subwin_cols);
if (window->type == WIN_CONSOLE) {
rosterwin_roster();
} else if (window->type == WIN_MUC) {
@@ -946,11 +975,11 @@ win_resize(ProfWin* window)
}
} else {
wbkgd(layout->base.win, theme_attrs(THEME_TEXT));
wresize(layout->base.win, PAD_SIZE, cols);
wresize(layout->base.win, PAD_MIN_HEIGHT, cols);
}
} else {
wbkgd(window->layout->win, theme_attrs(THEME_TEXT));
wresize(window->layout->win, PAD_SIZE, cols);
wresize(window->layout->win, PAD_MIN_HEIGHT, cols);
}
win_redraw(window);
@@ -1078,11 +1107,11 @@ win_show_contact(ProfWin* window, PContact contact)
GTimeSpan span = g_date_time_difference(now, last_activity);
g_date_time_unref(now);
int hours = span / G_TIME_SPAN_HOUR;
int hours = (int)(span / G_TIME_SPAN_HOUR);
span = span - hours * G_TIME_SPAN_HOUR;
int minutes = span / G_TIME_SPAN_MINUTE;
int minutes = (int)(span / G_TIME_SPAN_MINUTE);
span = span - minutes * G_TIME_SPAN_MINUTE;
int seconds = span / G_TIME_SPAN_SECOND;
int seconds = (int)(span / G_TIME_SPAN_SECOND);
if (hours > 0) {
win_append(window, presence_colour, ", idle %dh%dm%ds", hours, minutes, seconds);
@@ -1203,11 +1232,11 @@ win_show_info(ProfWin* window, PContact contact)
GDateTime* now = g_date_time_new_now_local();
GTimeSpan span = g_date_time_difference(now, last_activity);
int hours = span / G_TIME_SPAN_HOUR;
int hours = (int)(span / G_TIME_SPAN_HOUR);
span = span - hours * G_TIME_SPAN_HOUR;
int minutes = span / G_TIME_SPAN_MINUTE;
int minutes = (int)(span / G_TIME_SPAN_MINUTE);
span = span - minutes * G_TIME_SPAN_MINUTE;
int seconds = span / G_TIME_SPAN_SECOND;
int seconds = (int)(span / G_TIME_SPAN_SECOND);
if (hours > 0) {
win_println(window, THEME_DEFAULT, "-", "Last activity: %dh%dm%ds", hours, minutes, seconds);
@@ -1743,7 +1772,7 @@ win_appendln_highlight(ProfWin* window, theme_item_t theme_item, const char* con
void
win_print_http_transfer(ProfWin* window, const char* const message, char* id)
{
win_print_outgoing_with_receipt(window, "!", NULL, message, id, NULL);
win_print_status_with_id(window, message, id, THEME_DEFAULT, 0);
}
void
@@ -1751,7 +1780,7 @@ win_print_outgoing_with_receipt(ProfWin* window, const char* show_char, const ch
{
GDateTime* time = g_date_time_new_now_local();
DeliveryReceipt* receipt = malloc(sizeof(struct delivery_receipt_t));
DeliveryReceipt* receipt = g_new0(struct delivery_receipt_t, 1);
receipt->received = FALSE;
const char* myjid = connection_get_fulljid();
@@ -1792,6 +1821,33 @@ win_update_entry_message(ProfWin* window, const char* const id, const char* cons
}
}
void
win_update_entry(ProfWin* window, const char* const id, const char* const message, theme_item_t theme_item, int flags)
{
if (window->type == WIN_CONSOLE)
return;
ProfBuffEntry* entry = buffer_get_entry_by_id(window->layout->buffer, id);
if (entry) {
if (message) {
free(entry->message);
entry->message = strdup(message);
}
entry->theme_item = theme_item;
entry->flags |= flags;
win_redraw(window);
}
}
void
win_print_status_with_id(ProfWin* window, const char* const message, char* id, theme_item_t theme_item, int flags)
{
GDateTime* time = g_date_time_new_now_local();
int y_start_pos = getcury(window->layout->win);
_win_print_internal(window, "!", 0, time, flags, theme_item, NULL, message, NULL);
buffer_append(window->layout->buffer, "!", 0, time, flags, theme_item, NULL, NULL, message, NULL, id, y_start_pos, getcury(window->layout->win));
g_date_time_unref(time);
}
void
win_remove_entry_message(ProfWin* window, const char* const id)
{
@@ -1851,7 +1907,7 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
gboolean me_message = FALSE;
int offset = 0;
int colour = theme_attrs(THEME_ME);
size_t indent = 0;
int indent = 0;
auto_gchar gchar* time_pref = NULL;
switch (window->type) {
@@ -1884,7 +1940,7 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
assert(date_fmt != NULL);
if (strlen(date_fmt) != 0) {
indent = 3 + strlen(date_fmt);
indent = 3 + (int)strlen(date_fmt);
}
if ((flags & NO_DATE) == 0) {
@@ -1945,6 +2001,8 @@ _win_print_internal(ProfWin* window, const char* show_char, int pad_indent, GDat
}
}
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
if (prefs_get_boolean(PREF_WRAP)) {
_win_print_wrapped(window->layout->win, message + offset, indent, pad_indent);
} else {
@@ -1978,11 +2036,11 @@ _win_indent(WINDOW* win, int size)
}
static void
_win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pad_indent)
_win_print_wrapped(WINDOW* win, const char* const message, int indent, int pad_indent)
{
int starty = getcury(win);
int wordi = 0;
auto_char char* word = malloc(strlen(message) + 1);
auto_gchar gchar* word = g_malloc(strlen(message) + 1);
gchar* curr_ch = g_utf8_offset_to_pointer(message, 0);
@@ -2005,11 +2063,12 @@ _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pa
int wordlen = 0;
while (*curr_ch != ' ' && *curr_ch != '\n' && *curr_ch != '\0') {
size_t ch_len = mbrlen(curr_ch, MB_CUR_MAX, NULL);
if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1)) {
// 0 / invalid step: advance one byte to avoid spinning.
if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1) || ch_len == 0) {
curr_ch++;
continue;
}
int offset = 0;
size_t offset = 0;
while (offset < ch_len) {
word[wordi++] = curr_ch[offset++];
}
@@ -2094,6 +2153,7 @@ _win_print_wrapped(WINDOW* win, const char* const message, size_t indent, int pa
void
win_print_trackbar(ProfWin* window)
{
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
int cols = getmaxx(window->layout->win);
wbkgdset(window->layout->win, theme_attrs(THEME_TRACKBAR));
@@ -2109,12 +2169,20 @@ win_print_trackbar(ProfWin* window)
void
win_redraw(ProfWin* window)
{
int size = buffer_size(window->layout->buffer);
unsigned int size = buffer_size(window->layout->buffer);
_in_redraw = TRUE;
// shrink pad back to minimum size and erase it
int cols = getmaxx(window->layout->win);
wresize(window->layout->win, PAD_MIN_HEIGHT, cols);
werase(window->layout->win);
for (int i = 0; i < size; i++) {
for (unsigned int i = 0; i < size; i++) {
ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i);
// check if we need more space before printing
_win_ensure_pad_capacity(window, window->layout->win, getcury(window->layout->win));
e->y_start_pos = getcury(window->layout->win);
if (e->display_from == NULL && e->message && e->message[0] == '-') {
// just an indicator to print the trackbar/separator not the actual message
@@ -2129,6 +2197,8 @@ win_redraw(ProfWin* window)
e->_lines = e->y_end_pos - e->y_start_pos;
window->layout->buffer->lines += e->_lines;
}
_in_redraw = FALSE;
}
void
@@ -2263,6 +2333,7 @@ win_toggle_attention(ProfWin* window)
void
win_sub_print(WINDOW* win, char* msg, gboolean newline, gboolean wrap, int indent)
{
_win_ensure_pad_capacity(NULL, win, getcury(win));
int maxx = getmaxx(win);
int curx = getcurx(win);
int cury = getcury(win);
@@ -2351,11 +2422,11 @@ win_handle_command_exec_result_note(ProfWin* window, const char* const type, con
void
win_insert_last_read_position_marker(ProfWin* window, char* id)
{
int size = buffer_size(window->layout->buffer);
unsigned int size = buffer_size(window->layout->buffer);
// TODO: this is somewhat costly. We should improve this later.
// check if we already have a separator present
for (int i = 0; i < size; i++) {
for (unsigned int i = 0; i < size; i++) {
ProfBuffEntry* e = buffer_get_entry(window->layout->buffer, i);
// if yes, don't print a new one