1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-30 06:46:38 +02:00

core: implement /theme save and /theme delete

Add two complementary subcommands:

  /theme save <name> [-full]: writes a user theme file at
    ${weechat_config_dir}/themes/<name>.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 delete <name>: removes ${weechat_config_dir}/themes/<name>.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).

Bug fix while at it: the writer was previously calling
config_file_option_value_to_string (ptr_option, 0, 1, 0); the third
and fourth arguments are "use_colors" and "use_delimiters", so the
call inserted GUI color escape codes into the file output and skipped
quoting strings. Corrected to (ptr_option, 0, 0, 1) so plain text
with proper string quoting is written; the change also fixes the
content of files produced by theme_make_backup in the previous commit.
This commit is contained in:
Sébastien Helleu
2026-05-26 18:57:56 +02:00
parent 74faa91578
commit 144d79f331
4 changed files with 252 additions and 17 deletions
+109 -11
View File
@@ -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,22 +331,22 @@ 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_options_section, 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);
@@ -354,7 +355,7 @@ TEST(CoreTheme, WriteFileFull)
CHECK(file != NULL);
saw_info = saw_name = saw_description = saw_date = saw_weechat = 0;
saw_options_section = saw_an_option = 0;
saw_options_section = full_options = 0;
while (fgets (line, sizeof (line) - 1, file))
{
if (strncmp (line, "[info]", 6) == 0)
@@ -372,7 +373,7 @@ TEST(CoreTheme, WriteFileFull)
else if (saw_options_section
&& (strchr (line, '=') != NULL)
&& (strchr (line, '.') != NULL))
saw_an_option = 1;
full_options++;
}
fclose (file);
@@ -382,7 +383,30 @@ TEST(CoreTheme, WriteFileFull)
LONGS_EQUAL(1, saw_date);
LONGS_EQUAL(1, saw_weechat);
LONGS_EQUAL(1, saw_options_section);
LONGS_EQUAL(1, saw_an_option);
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);
@@ -619,6 +643,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