From 9e7903c60105786de159d83d46e1d70f79a52864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 4 Jul 2026 17:02:12 +0200 Subject: [PATCH] core: implement /theme save and /theme del Add two complementary subcommands: /theme save [-full]: writes a user theme file at ${weechat_config_dir}/themes/.theme containing the current themable options. By default only options whose value differs from their default (config_file_option_has_changed) are written, which keeps the file small and focused. Pass "-full" to write every themable option (matches the format used by automatic backups). Name validation: refuses any name matching a built-in theme (those are reserved for in-memory registrations) and any name starting with "backup-" (reserved for /theme apply backups). Both checks print an error and abort without writing. /theme del : removes ${weechat_config_dir}/themes/.theme via unlink. Refuses to delete a name registered as a built-in theme (a built-in has no file on disk to delete, even if the user has a shadowing file of the same name they cannot remove it this way; they can rename or delete it manually). The full-snapshot writer used by /theme apply backups is refactored into theme_write_file (name, description, diff_only). It is reused by theme_make_backup (diff_only=0) and theme_save (diff_only inverted from the user's -full flag). --- po/cs.po | 49 +++++++++++- po/de.po | 52 ++++++++++++- po/es.po | 51 +++++++++++- po/fr.po | 60 +++++++++++++- po/hu.po | 46 ++++++++++- po/it.po | 49 +++++++++++- po/ja.po | 49 +++++++++++- po/pl.po | 52 ++++++++++++- po/pt.po | 49 +++++++++++- po/pt_BR.po | 48 +++++++++++- po/ru.po | 46 ++++++++++- po/sr.po | 52 ++++++++++++- po/tr.po | 52 ++++++++++++- po/weechat.pot | 44 ++++++++++- src/core/core-command.c | 28 +++++++ src/core/core-theme.c | 116 ++++++++++++++++++++++++++-- src/core/core-theme.h | 2 + tests/unit/core/test-core-theme.cpp | 116 ++++++++++++++++++++++++++-- 18 files changed, 919 insertions(+), 42 deletions(-) diff --git a/po/cs.po b/po/cs.po index 011cc37cc..46af22de8 100644 --- a/po/cs.po +++ b/po/cs.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-05-30 14:01+0200\n" "Last-Translator: Ondřej Súkup \n" "Language-Team: Czech \n" @@ -3709,7 +3709,9 @@ msgid "manage color themes" msgstr "seznam položek polí" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3729,6 +3731,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "získat/nastavit téma kanálu" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6340,6 +6354,37 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: nemůžu parsovat soubor \"%s\"" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Volba \"%s%s%s\":" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sChyba: nemohu vytvořit soubor \"%s\"" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: nemůžu parsovat soubor \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sChyba aktualizace WeeChat se souborem \"%s\":" diff --git a/po/de.po b/po/de.po index a29a91da2..ded6da285 100644 --- a/po/de.po +++ b/po/de.po @@ -26,7 +26,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:28+0200\n" "Last-Translator: Nils Görs \n" "Language-Team: German \n" @@ -4101,7 +4101,9 @@ msgid "manage color themes" msgstr "Verwalten von benutzerdefinierten Bar-Items" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -4128,6 +4130,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "raw[del]: delete a server" +msgid "raw[del]: delete a user theme file" +msgstr "raw[del]: entfernt einen Server" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -7182,6 +7196,40 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%sDer Buffer-Name \"%s\" ist für WeeChat reserviert" + +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%sDer Buffer-Name \"%s\" ist für WeeChat reserviert" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: Datei \"%s\" Analyse nicht möglich" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Einstellung \"%s%s%s\":" + +#, fuzzy, c-format +#| msgid "%sCannot create file \"%s\"" +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sDie Datei \"%s\" kann nicht erstellt werden" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: Datei \"%s\" Analyse nicht möglich" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "" diff --git a/po/es.po b/po/es.po index cdebb47e3..ce51e0fbb 100644 --- a/po/es.po +++ b/po/es.po @@ -24,7 +24,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:46+0200\n" "Last-Translator: Santiago Forero \n" "Language-Team: Spanish \n" @@ -3801,7 +3801,9 @@ msgid "manage color themes" msgstr "lista de elementos de barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3821,6 +3823,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "ver/establecer el tema del canal" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6498,6 +6512,39 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%sEl nombre del buffer \"%s\" está reservado por WeeChat" + +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%sEl nombre del buffer \"%s\" está reservado por WeeChat" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: no es posible analizar el archivo \"%s\"" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Opción \"%s%s%s\":" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sError: no es posible crear el archivo \"%s\"" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: no es posible analizar el archivo \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sError al actualizar WeeChat con el archivo \"%s\":" diff --git a/po/fr.po b/po/fr.po index c0e8485e1..59e4707f5 100644 --- a/po/fr.po +++ b/po/fr.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" -"PO-Revision-Date: 2026-07-04 16:05+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" +"PO-Revision-Date: 2026-07-04 17:00+0200\n" "Last-Translator: Sébastien Helleu \n" "Language-Team: French \n" "Language: fr\n" @@ -4026,8 +4026,12 @@ msgid "manage color themes" msgstr "gestion des thèmes de couleurs" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " -msgstr "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " +msgstr "" +"[list [-backups]] || apply || save [-full] || del || info " +"" msgid "" "raw[list]: list registered themes and any *.theme files in the WeeChat " @@ -4050,6 +4054,21 @@ msgstr "" "thème à la valeur du thème) ; si un fichier nommé .theme existe dans le " "répertoire \"themes\", il masque tout thème intégré du même nom" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" +"raw[save] : sauvegarder les options de thème dans un fichier .theme " +"dans le répertoire \"themes\" ; par défaut seulement les options dont la " +"valeur diffère de leur valeur par défaut sont écrites, utilisez \"-full\" " +"pour écrire toutes les options ; le nom ne doit pas correspondre à un thème " +"intégré ou démarrer par \"backup-\"" + +msgid "raw[del]: delete a user theme file" +msgstr "raw[del] : supprimer un fichier de thème utilisateur" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -7059,6 +7078,34 @@ msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" "État précédent sauvé sous le thème \"%s\" ; pour restaurer : /theme apply %s" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%sLe nom \"%s\" est réservé pour les sauvegardes automatiques" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%sLe nom \"%s\" est réservé pour un thème intégré" + +#, c-format +msgid "%sFailed to save theme \"%s\"" +msgstr "%sÉchec de sauvegarde du thème \"%s\"" + +#, c-format +msgid "Theme saved: %s" +msgstr "Thème sauvegardé : %s" + +#, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sImpossible de supprimer le thème intégré \"%s\"" + +#, c-format +msgid "%sFailed to delete theme \"%s\"" +msgstr "%sImpossible de supprimer le thème \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "Thème supprimé : %s" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sErreur de mise à jour de WeeChat avec le fichier \"%s\" :" @@ -18933,6 +18980,11 @@ msgstr "" "%s%s : impossible d'accepter de continuer le fichier \"%s\" (port : %d, " "position de départ : %llu) : xfer non trouvé ou non prêt pour le transfert" +#~ msgid "" +#~ "raw[delete]: delete a user theme file (refuses to delete built-in themes, " +#~ "which have no file)" +#~ msgstr "raw[delete] : supprimer un fichier thème de l'utilisateur" + #~ msgid "No theme registered" #~ msgstr "Aucun thème enregistré" diff --git a/po/hu.po b/po/hu.po index 96ebe2fc1..39fe9d94c 100644 --- a/po/hu.po +++ b/po/hu.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-03-08 08:59+0100\n" "Last-Translator: Andras Voroskoi \n" "Language-Team: Hungarian \n" @@ -3554,7 +3554,9 @@ msgid "manage color themes" msgstr "Aliaszok listája:\n" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3573,6 +3575,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "szoba témájának olvasása/módosítása" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6006,6 +6020,34 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, fuzzy, c-format +msgid "%sFailed to save theme \"%s\"" +msgstr "Nem sikerült a(z) \"%s\" naplófájlt írni\n" + +#, fuzzy, c-format +msgid "Theme saved: %s" +msgstr "Felhasználók a %s%s%s szobában: %s[" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%s nem sikerült a \"%s\" fájlt létrehozni\n" + +#, fuzzy, c-format +msgid "%sFailed to delete theme \"%s\"" +msgstr "Nem sikerült a(z) \"%s\" naplófájlt írni\n" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, fuzzy, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "WeeChat frissítése...\n" diff --git a/po/it.po b/po/it.po index a0a30e43a..bc2c20d52 100644 --- a/po/it.po +++ b/po/it.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-05-30 14:02+0200\n" "Last-Translator: Esteban I. Ruiz Moreno \n" "Language-Team: Italian \n" @@ -3732,7 +3732,9 @@ msgid "manage color themes" msgstr "elenco degli elementi barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3752,6 +3754,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "legge/modifica argomento del canale" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6486,6 +6500,37 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: impossibile analizzare il file \"%s\"" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Opzione \"%s%s%s\":" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sErrore: impossibile creare il file \"%s\"" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: impossibile analizzare il file \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sErrore durante l'aggiornamento di WeeChat con il file \"%s\":" diff --git a/po/ja.po b/po/ja.po index e4e30972c..d509c5f9e 100644 --- a/po/ja.po +++ b/po/ja.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-05-30 14:02+0200\n" "Last-Translator: AYANOKOUZI, Ryuunosuke \n" "Language-Team: Japanese \n" @@ -3817,7 +3817,9 @@ msgid "manage color themes" msgstr "バー要素のリスト" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3837,6 +3839,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "チャンネルトピックの取得/設定" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6662,6 +6676,37 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, fuzzy, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%sエラー: \"%s\" は WeeChat の予約名です" + +#, fuzzy, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%sエラー: \"%s\" は WeeChat の予約名です" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: ファイル \"%s\" を解析できません" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "オプション \"%s%s%s\":" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sエラー: ファイル \"%s\" の作成に失敗" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: ファイル \"%s\" を解析できません" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sファイル \"%s\" で WeeChat のアップグレード中にエラー:" diff --git a/po/pl.po b/po/pl.po index 359e53d9a..93aa99c74 100644 --- a/po/pl.po +++ b/po/pl.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:55+0200\n" "Last-Translator: Krzysztof Korościk \n" "Language-Team: Polish \n" @@ -3927,7 +3927,9 @@ msgid "manage color themes" msgstr "zarządza niestandardowymi elementami pasków" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3953,6 +3955,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "raw[del]: delete a server" +msgid "raw[del]: delete a user theme file" +msgstr "raw[del]: usuń serwer" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6843,6 +6857,40 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%sNazwa bufora \"%s\" jest zarezerwowana dla WeeChat" + +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%sNazwa bufora \"%s\" jest zarezerwowana dla WeeChat" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: nie można przetworzyć pliku \"%s\"" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Opcja \"%s%s%s\":" + +#, fuzzy, c-format +#| msgid "%sCannot create file \"%s\"" +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sNie można utworzyć pliku \"%s\"" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: nie można przetworzyć pliku \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sBłąd przy uaktualnianiu WeeChat z użyciem pliku \"%s\":" diff --git a/po/pt.po b/po/pt.po index b6f6897c5..1308129bf 100644 --- a/po/pt.po +++ b/po/pt.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:48+0200\n" "Last-Translator: Vasco Almeida \n" "Language-Team: Portuguese \n" @@ -3826,7 +3826,9 @@ msgid "manage color themes" msgstr "lista de itens da barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3846,6 +3848,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "obter/definir o tópico do canal" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6718,6 +6732,37 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: não foi possível analisar o ficheiro \"%s\"" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Opção \"%s%s%s\":" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sErro: não é possível criar o ficheiro \"%s\"" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: não foi possível analisar o ficheiro \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sErro ao atualizar o WeeChat com o ficheiro \"%s\":" diff --git a/po/pt_BR.po b/po/pt_BR.po index 9daf94863..f97100d63 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -46,7 +46,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:49+0200\n" "Last-Translator: Érico Nogueira \n" "Language-Team: Portuguese (Brazil) \n" @@ -3746,7 +3746,9 @@ msgid "manage color themes" msgstr "lista de itens da barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3766,6 +3768,17 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +msgid "raw[del]: delete a user theme file" +msgstr "lista de atalhos" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6385,6 +6398,37 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: não foi possível interpretar arquivo \"%s\"" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Opção \"%s%s%s\":" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sErro: não foi possível criar arquivo \"%s\"" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: não foi possível interpretar arquivo \"%s\"" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "" diff --git a/po/ru.po b/po/ru.po index bbc7d9fc7..c9d44fb62 100644 --- a/po/ru.po +++ b/po/ru.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-03-08 08:59+0100\n" "Last-Translator: Aleksey V Zapparov AKA ixti \n" "Language-Team: Russian \n" @@ -3578,7 +3578,9 @@ msgid "manage color themes" msgstr "Список сокращений:\n" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3597,6 +3599,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "получить/установить тему канала" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6043,6 +6057,34 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, fuzzy, c-format +msgid "%sFailed to save theme \"%s\"" +msgstr "Не могу записать лог-файл \"%s\"\n" + +#, fuzzy, c-format +msgid "Theme saved: %s" +msgstr "Ники %s%s%s: %s[" + +#, fuzzy, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%s не могу создать файл \"%s\"\n" + +#, fuzzy, c-format +msgid "%sFailed to delete theme \"%s\"" +msgstr "Не могу записать лог-файл \"%s\"\n" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, fuzzy, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "Обновляю WeeChat...\n" diff --git a/po/sr.po b/po/sr.po index 51bd12f96..248a80303 100644 --- a/po/sr.po +++ b/po/sr.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:52+0200\n" "Last-Translator: Ivan Pešić \n" "Language-Team: Serbian \n" @@ -3904,7 +3904,9 @@ msgid "manage color themes" msgstr "управљање прилагођеним ставкама траке" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3930,6 +3932,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "raw[del]: delete a server" +msgid "raw[del]: delete a user theme file" +msgstr "raw[del]: брисање сервера" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6825,6 +6839,40 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%sИме бафера „%s” је резервисано за програм WeeChat" + +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%sИме бафера „%s” је резервисано за програм WeeChat" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: није успело парсирање фајла „%s”" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "Опција „%s%s%s”:" + +#, fuzzy, c-format +#| msgid "%sCannot create file \"%s\"" +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%sНе може да се креира фајл „%s”" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: није успело парсирање фајла „%s”" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sГрешка приликом ажурирања програма WeeChat фајлом „%s”:" diff --git a/po/tr.po b/po/tr.po index 0ff9f8462..9f1a9d7f6 100644 --- a/po/tr.po +++ b/po/tr.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2026-06-28 08:53+0200\n" "Last-Translator: Emir SARI \n" "Language-Team: Turkish \n" @@ -3763,7 +3763,9 @@ msgid "manage color themes" msgstr "özel çubuk ögelerini yönet" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" #, fuzzy @@ -3785,6 +3787,18 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +#, fuzzy +#| msgid "get/set channel topic" +msgid "raw[del]: delete a user theme file" +msgstr "kanal konusunu al/ayarla" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -6622,6 +6636,40 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "%s\"%s\" arabellek adı WeeChat için ayrılmış" + +#, fuzzy, c-format +#| msgid "%sBuffer name \"%s\" is reserved for WeeChat" +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "%s\"%s\" arabellek adı WeeChat için ayrılmış" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to save theme \"%s\"" +msgstr "%s%s: \"%s\" dosyası ayrıştırılamıyor" + +#, fuzzy, c-format +#| msgid "Option \"%s%s%s\":" +msgid "Theme saved: %s" +msgstr "\"%s%s%s\" seçeneği:" + +#, fuzzy, c-format +#| msgid "%sCannot create file \"%s\"" +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "%s\"%s\" dosyası oluşturulamıyor" + +#, fuzzy, c-format +#| msgid "%s%s: unable to parse file \"%s\"" +msgid "%sFailed to delete theme \"%s\"" +msgstr "%s%s: \"%s\" dosyası ayrıştırılamıyor" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sWeeChat'i \"%s\" dosyası ile yükseltirken hata:" diff --git a/po/weechat.pot b/po/weechat.pot index 69ee2c12b..6326c414d 100644 --- a/po/weechat.pot +++ b/po/weechat.pot @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 16:03+0200\n" +"POT-Creation-Date: 2026-07-04 17:00+0200\n" "PO-Revision-Date: 2014-08-16 10:27+0200\n" "Last-Translator: Sébastien Helleu \n" "Language-Team: weechat-dev \n" @@ -3346,7 +3346,9 @@ msgid "manage color themes" msgstr "" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list [-backups]] || apply || info " +msgid "" +"[list [-backups]] || apply || save [-full] || del || " +"info " msgstr "" msgid "" @@ -3364,6 +3366,16 @@ msgid "" "shadows any built-in theme of the same name" msgstr "" +msgid "" +"raw[save]: save current themable options to a file .theme in directory " +"\"themes\"; by default only options whose value differs from their default " +"are written, use \"-full\" to write every themable option; the name must not " +"match a built-in theme or start with \"backup-\"" +msgstr "" + +msgid "raw[del]: delete a user theme file" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -5603,6 +5615,34 @@ msgstr "" msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" msgstr "" +#, c-format +msgid "%sName \"%s\" is reserved for automatic backups" +msgstr "" + +#, c-format +msgid "%sName \"%s\" is reserved for a built-in theme" +msgstr "" + +#, c-format +msgid "%sFailed to save theme \"%s\"" +msgstr "" + +#, c-format +msgid "Theme saved: %s" +msgstr "" + +#, c-format +msgid "%sCannot delete built-in theme \"%s\"" +msgstr "" + +#, c-format +msgid "%sFailed to delete theme \"%s\"" +msgstr "" + +#, c-format +msgid "Theme deleted: %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "" diff --git a/src/core/core-command.c b/src/core/core-command.c index fb660f5b8..99c13ad6d 100644 --- a/src/core/core-command.c +++ b/src/core/core-command.c @@ -7339,6 +7339,23 @@ COMMAND_CALLBACK(theme) return theme_apply (argv[2]); } + /* "/theme save [-full]": write a user theme file */ + if (string_strcmp (argv[1], "save") == 0) + { + COMMAND_MIN_ARGS(3, "save"); + return theme_save (argv[2], + ((argc >= 4) + && (string_strcmp (argv[3], "-full") == 0)) + ? 1 : 0); + } + + /* "/theme del ": remove a user theme file */ + if (string_strcmp (argv[1], "del") == 0) + { + COMMAND_MIN_ARGS(3, "del"); + return theme_delete (argv[2]); + } + /* "/theme info ": show details about a theme */ if (string_strcmp (argv[1], "info") == 0) { @@ -10063,6 +10080,8 @@ command_init (void) /* TRANSLATORS: only text between angle brackets (eg: "") may be translated */ N_("[list [-backups]]" " || apply " + " || save [-full]" + " || del " " || info "), CMD_ARGS_DESC( N_("raw[list]: list registered themes and any *.theme files in " @@ -10073,6 +10092,13 @@ command_init (void) "value from the theme); if a file named .theme " "exists in directory \"themes\" it shadows any built-in " "theme of the same name"), + N_("raw[save]: save current themable options to a file " + ".theme in directory \"themes\"; by default only " + "options whose value differs from their default are " + "written, use \"-full\" to write every themable option; " + "the name must not match a built-in theme or start with " + "\"backup-\""), + N_("raw[del]: delete a user theme file"), N_("raw[info]: display details on a theme (name, description, " "creation date, WeeChat version, number of option overrides)"), N_("name: name of a theme"), @@ -10089,6 +10115,8 @@ command_init (void) "This is controlled by the option weechat.look.theme_backup.")), "list -backups" " || apply" + " || save -full" + " || del" " || info", &command_theme, NULL, NULL); hook_command ( diff --git a/src/core/core-theme.c b/src/core/core-theme.c index fb3bd3d48..e856278e8 100644 --- a/src/core/core-theme.c +++ b/src/core/core-theme.c @@ -312,12 +312,16 @@ theme_make_backup_name (void) } /* - * Write a full snapshot of every themable option to a .theme file at + * Write a snapshot of themable options to a .theme file at * "/themes/.theme". * * The themes directory is created if missing. The file contains an * [info] section (name, description, date, weechat version) followed by - * an [options] section listing every themable option's current value. + * an [options] section. + * + * If "diff_only" is non-zero, only options whose value differs from + * their default (config_file_option_has_changed) are written. If zero, + * every themable option is written (full snapshot). * * Return: * 1: success @@ -325,7 +329,7 @@ theme_make_backup_name (void) */ int -theme_write_file_full (const char *name, const char *description) +theme_write_file (const char *name, const char *description, int diff_only) { char *path, *dir, *value, *now; FILE *file; @@ -374,6 +378,8 @@ theme_write_file_full (const char *name, const char *description) { if (!ptr_option->themable) continue; + if (diff_only && !config_file_option_has_changed (ptr_option)) + continue; value = config_file_option_value_to_string ( ptr_option, 0, 0, 1); fprintf (file, "%s.%s.%s = %s\n", @@ -405,9 +411,10 @@ theme_make_backup (void) name = theme_make_backup_name (); if (!name) return NULL; - if (!theme_write_file_full ( + if (!theme_write_file ( name, - _("Automatic backup written before /theme apply"))) + _("Automatic backup written before /theme apply"), + 0)) /* full snapshot: backups must round-trip exactly */ { free (name); return NULL; @@ -785,6 +792,105 @@ theme_apply (const char *name) return WEECHAT_RC_OK; } +/* + * Save the current themable options to a user theme file. + * + * Refuse names that match a built-in theme (registered via API) or + * that start with "backup-" (reserved for automatic backups). If + * "full" is non-zero, every themable option is written; otherwise + * only options whose value differs from their default are written. + * + * Return WEECHAT_RC_OK on success, WEECHAT_RC_ERROR on validation or + * I/O failure. + */ + +int +theme_save (const char *name, int full) +{ + if (!name || !name[0]) + return WEECHAT_RC_ERROR; + + if (strncmp (name, "backup-", 7) == 0) + { + gui_chat_printf ( + NULL, + _("%sName \"%s\" is reserved for automatic backups"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + name); + return WEECHAT_RC_ERROR; + } + + if (theme_search (name)) + { + gui_chat_printf ( + NULL, + _("%sName \"%s\" is reserved for a built-in theme"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + name); + return WEECHAT_RC_ERROR; + } + + if (!theme_write_file (name, NULL, (full) ? 0 : 1)) + { + gui_chat_printf (NULL, + _("%sFailed to save theme \"%s\""), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + name); + return WEECHAT_RC_ERROR; + } + + gui_chat_printf (NULL, + _("Theme saved: %s"), + name); + return WEECHAT_RC_OK; +} + +/* + * Delete a user theme file. + * + * Refuse names registered as built-in themes (they have no file). + * Return WEECHAT_RC_OK on success, WEECHAT_RC_ERROR otherwise. + */ + +int +theme_delete (const char *name) +{ + char *path; + + if (!name || !name[0]) + return WEECHAT_RC_ERROR; + + if (theme_search (name)) + { + gui_chat_printf ( + NULL, + _("%sCannot delete built-in theme \"%s\""), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + name); + return WEECHAT_RC_ERROR; + } + + path = theme_user_file_path (name); + if (!path) + return WEECHAT_RC_ERROR; + + if (unlink (path) != 0) + { + gui_chat_printf (NULL, + _("%sFailed to delete theme \"%s\""), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + name); + free (path); + return WEECHAT_RC_ERROR; + } + + gui_chat_printf (NULL, + _("Theme deleted: %s"), + name); + free (path); + return WEECHAT_RC_OK; +} + /* * Initialize the theme subsystem. * diff --git a/src/core/core-theme.h b/src/core/core-theme.h index 93a3d4dbd..b666062f2 100644 --- a/src/core/core-theme.h +++ b/src/core/core-theme.h @@ -45,6 +45,8 @@ extern struct t_theme *theme_register (const char *name, struct t_hashtable *overrides); extern struct t_arraylist *theme_list (void); extern int theme_apply (const char *name); +extern int theme_save (const char *name, int full); +extern int theme_delete (const char *name); extern char *theme_make_backup (void); extern char *theme_user_file_path (const char *name); extern struct t_theme *theme_file_parse (const char *path); diff --git a/tests/unit/core/test-core-theme.cpp b/tests/unit/core/test-core-theme.cpp index 651642543..c0e5fc7af 100644 --- a/tests/unit/core/test-core-theme.cpp +++ b/tests/unit/core/test-core-theme.cpp @@ -47,7 +47,8 @@ extern struct t_theme *theme_alloc (const char *name); extern void theme_free (struct t_theme *theme); extern char *theme_user_file_path (const char *name); extern char *theme_make_backup_name (void); -extern int theme_write_file_full (const char *name, const char *description); +extern int theme_write_file (const char *name, const char *description, + int diff_only); extern char *theme_file_strip_quotes (char *value); extern struct t_theme *theme_file_parse (const char *path); } @@ -330,23 +331,24 @@ TEST(CoreTheme, MakeBackupName) /* * Test functions: - * theme_write_file_full + * theme_write_file */ -TEST(CoreTheme, WriteFileFull) +TEST(CoreTheme, WriteFile) { char *path, line[8192]; FILE *file; int saw_info, saw_name, saw_description, saw_date, saw_weechat; int saw_options_section, saw_an_option; int saw_color_code, saw_string_option, string_option_quoted; + int full_options, diff_options; /* refuse empty/NULL */ - LONGS_EQUAL(0, theme_write_file_full (NULL, NULL)); - LONGS_EQUAL(0, theme_write_file_full ("", NULL)); + LONGS_EQUAL(0, theme_write_file (NULL, NULL, 0)); + LONGS_EQUAL(0, theme_write_file ("", NULL, 0)); - /* write a valid file */ - LONGS_EQUAL(1, theme_write_file_full ("test_wrt", "a description")); + /* full snapshot: every themable option is written */ + LONGS_EQUAL(1, theme_write_file ("test_wrt", "a description", 0)); path = theme_user_file_path ("test_wrt"); CHECK(path != NULL); @@ -357,6 +359,7 @@ TEST(CoreTheme, WriteFileFull) saw_info = saw_name = saw_description = saw_date = saw_weechat = 0; saw_options_section = saw_an_option = 0; saw_color_code = saw_string_option = string_option_quoted = 0; + full_options = 0; while (fgets (line, sizeof (line) - 1, file)) { if (strncmp (line, "[info]", 6) == 0) @@ -376,6 +379,7 @@ TEST(CoreTheme, WriteFileFull) && (strchr (line, '.') != NULL)) { saw_an_option = 1; + full_options++; /* * values must be stored verbatim: no WeeChat color code * (byte 0x19) may leak into the file @@ -404,6 +408,30 @@ TEST(CoreTheme, WriteFileFull) /* the string option was found and its value is quoted */ LONGS_EQUAL(1, saw_string_option); LONGS_EQUAL(1, string_option_quoted); + CHECK(full_options > 10); /* core has many themable options */ + + unlink (path); + + /* diff-only snapshot in a freshly initialized config writes very + few (typically zero) [options] entries — never more than the + full snapshot */ + LONGS_EQUAL(1, theme_write_file ("test_wrt", NULL, 1)); + + file = fopen (path, "r"); + CHECK(file != NULL); + diff_options = 0; + saw_options_section = 0; + while (fgets (line, sizeof (line) - 1, file)) + { + if (strncmp (line, "[options]", 9) == 0) + saw_options_section = 1; + else if (saw_options_section + && (strchr (line, '=') != NULL) + && (strchr (line, '.') != NULL)) + diff_options++; + } + fclose (file); + CHECK(diff_options < full_options); unlink (path); free (path); @@ -640,6 +668,80 @@ TEST(CoreTheme, FileParse) unlink (path); } +/* + * Test functions: + * theme_save + */ + +TEST(CoreTheme, Save) +{ + char *path; + struct stat st; + + /* NULL / empty => error, no file */ + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save (NULL, 0)); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("", 0)); + + /* reserved "backup-" prefix => error */ + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("backup-anything", 0)); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("backup-anything", 1)); + + /* name colliding with a built-in is refused */ + theme_register ("dark", NULL); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("dark", 0)); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("dark", 1)); + + /* happy path: sparse save => file exists */ + LONGS_EQUAL(WEECHAT_RC_OK, theme_save ("save_test", 0)); + path = theme_user_file_path ("save_test"); + CHECK(path != NULL); + LONGS_EQUAL(0, stat (path, &st)); + unlink (path); + free (path); + + /* happy path: full snapshot => file exists, bigger than sparse */ + LONGS_EQUAL(WEECHAT_RC_OK, theme_save ("save_test", 1)); + path = theme_user_file_path ("save_test"); + CHECK(path != NULL); + LONGS_EQUAL(0, stat (path, &st)); + CHECK(st.st_size > 0); + unlink (path); + free (path); +} + +/* + * Test functions: + * theme_delete + */ + +TEST(CoreTheme, Delete) +{ + char *path; + struct stat st; + + /* NULL / empty => error */ + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_delete (NULL)); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_delete ("")); + + /* refuses to delete a built-in (no file to delete) */ + theme_register ("dark", NULL); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_delete ("dark")); + + /* missing file => error */ + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_delete ("does_not_exist")); + + /* happy path: write a file via theme_save (also ensures the themes + directory exists), delete it, confirm it is gone */ + LONGS_EQUAL(WEECHAT_RC_OK, theme_save ("del_test", 0)); + path = theme_user_file_path ("del_test"); + CHECK(path != NULL); + LONGS_EQUAL(0, stat (path, &st)); + + LONGS_EQUAL(WEECHAT_RC_OK, theme_delete ("del_test")); + LONGS_EQUAL(-1, stat (path, &st)); + free (path); +} + /* * Test functions: * theme_init