diff options
-rw-r--r-- | ChangeLog.adoc | 1 | ||||
-rw-r--r-- | doc/en/weechat_plugin_api.en.adoc | 41 | ||||
-rw-r--r-- | doc/fr/weechat_plugin_api.fr.adoc | 41 | ||||
-rw-r--r-- | doc/it/weechat_plugin_api.it.adoc | 42 | ||||
-rw-r--r-- | doc/ja/weechat_plugin_api.ja.adoc | 42 | ||||
-rw-r--r-- | doc/sr/weechat_plugin_api.sr.adoc | 42 | ||||
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/Makefile.am | 2 | ||||
-rw-r--r-- | src/core/wee-dir.c | 906 | ||||
-rw-r--r-- | src/core/wee-dir.h | 8 | ||||
-rw-r--r-- | src/gui/curses/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/gui/curses/headless/Makefile.am | 2 | ||||
-rw-r--r-- | src/gui/curses/normal/Makefile.am | 2 | ||||
-rw-r--r-- | src/plugins/plugin.c | 1 | ||||
-rw-r--r-- | src/plugins/weechat-plugin.h | 8 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 2 |
17 files changed, 824 insertions, 323 deletions
diff --git a/ChangeLog.adoc b/ChangeLog.adoc index a2a2110e7..f6a133c34 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -29,6 +29,7 @@ New features:: * api: add function crypto_hash_file * api: add support of priority in function hook_line (issue #1821) * api: add function string_parse_size + * api: add function file_compress * buflist: add variable `${hotlist_priority_number}` (integer version of `${hotlist_priority}`) * irc: display SETNAME command in channels and private buffers, add options irc.color.message_setname and irc.look.smart_filter_setname (issue #1805) * irc: add option irc.look.display_pv_nick_change diff --git a/doc/en/weechat_plugin_api.en.adoc b/doc/en/weechat_plugin_api.en.adoc index b6093fd52..e3c416e0b 100644 --- a/doc/en/weechat_plugin_api.en.adoc +++ b/doc/en/weechat_plugin_api.en.adoc @@ -4189,6 +4189,47 @@ if (weechat_file_copy ("/tmp/test.txt", "/path/to/test2.txt")) [NOTE] This function is not available in scripting API. +==== file_compress + +_WeeChat ≥ 3.7._ + +Compress a file with gzip or zstd. + +Prototype: + +[source,c] +---- +int weechat_file_compress (const char *from, const char *to, + const char *compressor, int compression_level); +---- + +Arguments: + +* _from_: source file +* _to_: destination file +* _compressor_: the compressor to use, one of: +** _gzip_: gzip compression +** _zstd_: zstandard compression +* _compression_level_: compression level, between 1 (fast, low compression) to + 100 (slow, best compression) + +Return value: + +* 1 if OK, 0 if error + +C example: + +[source,c] +---- +if (weechat_file_compress ("/tmp/test.txt", "/tmp/test.txt.zst", "zstd", 50)) +{ + /* OK */ +} +---- + +[NOTE] +This function is not available in scripting API. + [[util]] === Util diff --git a/doc/fr/weechat_plugin_api.fr.adoc b/doc/fr/weechat_plugin_api.fr.adoc index f6d6af359..09b4fca84 100644 --- a/doc/fr/weechat_plugin_api.fr.adoc +++ b/doc/fr/weechat_plugin_api.fr.adoc @@ -4258,6 +4258,47 @@ if (weechat_file_copy ("/tmp/test.txt", "/path/to/test2.txt")) [NOTE] Cette fonction n'est pas disponible dans l'API script. +==== file_compress + +_WeeChat ≥ 3.7._ + +Compresser un fichier avec gzip ou zstd. + +Prototype : + +[source,c] +---- +int weechat_file_compress (const char *from, const char *to, + const char *compressor, int compression_level); +---- + +Paramètres : + +* _from_ : fichier source +* _to_ : fichier cible +* _compressor_ : le compresseur à utiliser, un parmi : +** _gzip_ : compression gzip +** _zstd_ : compression zstandard +* _compression_level_ : niveau de compression, entre 1 (rapide, peu de + compression) à 100 (lent, meilleure compression) + +Valeur de retour : + +* 1 si OK, 0 si erreur + +Exemple en C : + +[source,c] +---- +if (weechat_file_compress ("/tmp/test.txt", "/tmp/test.txt.zst", "zstd", 50)) +{ + /* OK */ +} +---- + +[NOTE] +Cette fonction n'est pas disponible dans l'API script. + [[util]] === Util diff --git a/doc/it/weechat_plugin_api.it.adoc b/doc/it/weechat_plugin_api.it.adoc index ddd9c2d4b..f7b75669b 100644 --- a/doc/it/weechat_plugin_api.it.adoc +++ b/doc/it/weechat_plugin_api.it.adoc @@ -4342,6 +4342,48 @@ if (weechat_file_copy ("/tmp/test.txt", "/path/to/test2.txt")) [NOTE] Questa funzione non è disponibile nelle API per lo scripting. +// TRANSLATION MISSING +==== file_compress + +_WeeChat ≥ 3.7._ + +Compress a file with gzip or zstd. + +Prototipo: + +[source,c] +---- +int weechat_file_compress (const char *from, const char *to, + const char *compressor, int compression_level); +---- + +Argomenti: + +* _from_: source file +* _to_: destination file +* _compressor_: the compressor to use, one of: +** _gzip_: gzip compression +** _zstd_: zstandard compression +* _compression_level_: compression level, between 1 (fast, low compression) to + 100 (slow, best compression) + +Valore restituito: + +* 1 if OK, 0 if error + +Esempio in C: + +[source,c] +---- +if (weechat_file_compress ("/tmp/test.txt", "/tmp/test.txt.zst", "zstd", 50)) +{ + /* OK */ +} +---- + +[NOTE] +Questa funzione non è disponibile nelle API per lo scripting. + [[util]] === Utilità diff --git a/doc/ja/weechat_plugin_api.ja.adoc b/doc/ja/weechat_plugin_api.ja.adoc index 755974b45..bd9b6ea63 100644 --- a/doc/ja/weechat_plugin_api.ja.adoc +++ b/doc/ja/weechat_plugin_api.ja.adoc @@ -4258,6 +4258,48 @@ if (weechat_file_copy ("/tmp/test.txt", "/path/to/test2.txt")) [NOTE] スクリプト API ではこの関数を利用できません。 +// TRANSLATION MISSING +==== file_compress + +_WeeChat ≥ 3.7._ + +Compress a file with gzip or zstd. + +プロトタイプ: + +[source,c] +---- +int weechat_file_compress (const char *from, const char *to, + const char *compressor, int compression_level); +---- + +引数: + +* _from_: source file +* _to_: destination file +* _compressor_: the compressor to use, one of: +** _gzip_: gzip compression +** _zstd_: zstandard compression +* _compression_level_: compression level, between 1 (fast, low compression) to + 100 (slow, best compression) + +戻り値: + +* 1 if OK, 0 if error + +C 言語での使用例: + +[source,c] +---- +if (weechat_file_compress ("/tmp/test.txt", "/tmp/test.txt.zst", "zstd", 50)) +{ + /* OK */ +} +---- + +[NOTE] +スクリプト API ではこの関数を利用できません。 + [[util]] === ユーティリティ diff --git a/doc/sr/weechat_plugin_api.sr.adoc b/doc/sr/weechat_plugin_api.sr.adoc index a1e761a5f..4661e22e3 100644 --- a/doc/sr/weechat_plugin_api.sr.adoc +++ b/doc/sr/weechat_plugin_api.sr.adoc @@ -4053,6 +4053,48 @@ if (weechat_file_copy ("/tmp/test.txt", "/path/to/test2.txt")) Неке корисне функције. +// TRANSLATION MISSING +==== file_compress + +_WeeChat ≥ 3.7._ + +Compress a file with gzip or zstd. + +Прототип: + +[source,c] +---- +int weechat_file_compress (const char *from, const char *to, + const char *compressor, int compression_level); +---- + +Аргументи: + +* _from_: изворишни фајл +* _to_: одредишни фајл +* _compressor_: the compressor to use, one of: +** _gzip_: gzip compression +** _zstd_: zstandard compression +* _compression_level_: compression level, between 1 (fast, low compression) to + 100 (slow, best compression) + +Повратна вредност: + +* 1 ако је копирање OK, 0 у случају грешке + +C пример: + +[source,c] +---- +if (weechat_file_compress ("/tmp/test.txt", "/tmp/test.txt.zst", "zstd", 50)) +{ + /* OK */ +} +---- + +[NOTE] +Ова функција није доступна у API скриптовања. + ==== util_timeval_cmp Пореди две „timeval” структуре. diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ca11b685c..78099ecd7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -88,6 +88,8 @@ include_directories(${GNUTLS_INCLUDE_PATH}) include_directories(${CURL_INCLUDE_DIRS}) +include_directories(${ZSTD_INCLUDE_DIRS}) + include_directories(${CMAKE_BINARY_DIR}) add_library(weechat_core STATIC ${LIB_CORE_SRC}) target_link_libraries(weechat_core coverage_config) diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 6e977a74a..eed6204d2 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -17,7 +17,7 @@ # along with WeeChat. If not, see <https://www.gnu.org/licenses/>. # -AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(GCRYPT_CFLAGS) $(GNUTLS_CFLAGS) $(CURL_CFLAGS) +AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(GCRYPT_CFLAGS) $(GNUTLS_CFLAGS) $(CURL_CFLAGS) $(ZLIB_CFLAGS) $(ZSTD_CFLAGS) noinst_LIBRARIES = lib_weechat_core.a diff --git a/src/core/wee-dir.c b/src/core/wee-dir.c index 7a0b702e0..32a607f34 100644 --- a/src/core/wee-dir.c +++ b/src/core/wee-dir.c @@ -44,6 +44,8 @@ #include <unistd.h> #include <dirent.h> #include <ftw.h> +#include <zlib.h> +#include <zstd.h> #include "weechat.h" #include "wee-config.h" @@ -294,324 +296,6 @@ dir_rmtree (const char *directory) } /* - * Finds files in a directory and executes a function on each file. - */ - -void -dir_exec_on_files (const char *directory, int recurse_subdirs, - int hidden_files, - void (*callback)(void *data, const char *filename), - void *callback_data) -{ - char complete_filename[PATH_MAX]; - DIR *dir; - struct dirent *entry; - struct stat statbuf; - - if (!directory || !callback) - return; - - dir = opendir (directory); - if (dir) - { - while ((entry = readdir (dir))) - { - if (hidden_files || (entry->d_name[0] != '.')) - { - snprintf (complete_filename, sizeof (complete_filename), - "%s/%s", directory, entry->d_name); - lstat (complete_filename, &statbuf); - if (S_ISDIR(statbuf.st_mode)) - { - if (recurse_subdirs - && (strcmp (entry->d_name, ".") != 0) - && (strcmp (entry->d_name, "..") != 0)) - { - dir_exec_on_files (complete_filename, 1, hidden_files, - callback, callback_data); - } - } - else - { - (*callback) (callback_data, complete_filename); - } - } - } - closedir (dir); - } -} - -/* - * Searches for the full name of a WeeChat library with name and extension - * (searches first in WeeChat user's dir, then WeeChat global lib directory). - * - * Returns name of library found, NULL if not found. - * - * Note: result must be freed after use (if not NULL). - */ - -char * -dir_search_full_lib_name_ext (const char *filename, const char *extension, - const char *plugins_dir) -{ - char *name_with_ext, *final_name, *extra_libdir; - int length; - struct stat st; - - length = strlen (filename) + strlen (extension) + 1; - name_with_ext = malloc (length); - if (!name_with_ext) - return NULL; - snprintf (name_with_ext, length, - "%s%s", - filename, - (strchr (filename, '.')) ? "" : extension); - - /* try libdir from environment variable WEECHAT_EXTRA_LIBDIR */ - extra_libdir = getenv (WEECHAT_EXTRA_LIBDIR); - if (extra_libdir && extra_libdir[0]) - { - length = strlen (extra_libdir) + strlen (name_with_ext) + - strlen (plugins_dir) + 16; - final_name = malloc (length); - if (!final_name) - { - free (name_with_ext); - return NULL; - } - snprintf (final_name, length, - "%s%s%s%s%s", - extra_libdir, - DIR_SEPARATOR, - plugins_dir, - DIR_SEPARATOR, - name_with_ext); - if ((stat (final_name, &st) == 0) && (st.st_size > 0)) - { - free (name_with_ext); - return final_name; - } - free (final_name); - } - - /* try WeeChat user's dir */ - length = strlen (weechat_data_dir) + strlen (name_with_ext) + - strlen (plugins_dir) + 16; - final_name = malloc (length); - if (!final_name) - { - free (name_with_ext); - return NULL; - } - snprintf (final_name, length, - "%s%s%s%s%s", - weechat_data_dir, - DIR_SEPARATOR, - plugins_dir, - DIR_SEPARATOR, - name_with_ext); - if ((stat (final_name, &st) == 0) && (st.st_size > 0)) - { - free (name_with_ext); - return final_name; - } - free (final_name); - - /* try WeeChat global lib dir */ - length = strlen (WEECHAT_LIBDIR) + strlen (name_with_ext) + - strlen (plugins_dir) + 16; - final_name = malloc (length); - if (!final_name) - { - free (name_with_ext); - return NULL; - } - snprintf (final_name, length, - "%s%s%s%s%s", - WEECHAT_LIBDIR, - DIR_SEPARATOR, - plugins_dir, - DIR_SEPARATOR, - name_with_ext); - if ((stat (final_name, &st) == 0) && (st.st_size > 0)) - { - free (name_with_ext); - return final_name; - } - free (final_name); - - free (name_with_ext); - - return NULL; -} - -/* - * Searches for the full name of a WeeChat library with name. - * - * All extensions listed in option "weechat.plugin.extension" are tested. - * - * Note: result must be freed after use (if not NULL). - */ - -char * -dir_search_full_lib_name (const char *filename, const char *plugins_dir) -{ - char *filename2, *full_name; - int i; - - /* expand home in filename */ - filename2 = string_expand_home (filename); - if (!filename2) - return NULL; - - /* if full path, return it */ - if (strchr (filename2, '/') || strchr (filename2, '\\')) - return filename2; - - if (config_plugin_extensions) - { - for (i = 0; i < config_num_plugin_extensions; i++) - { - full_name = dir_search_full_lib_name_ext ( - filename2, - config_plugin_extensions[i], - plugins_dir); - if (full_name) - { - free (filename2); - return full_name; - } - } - } - else - { - full_name = dir_search_full_lib_name_ext (filename2, "", plugins_dir); - if (full_name) - { - free (filename2); - return full_name; - } - } - - free (filename2); - - return strdup (filename); -} - -/* - * Reads content of a file. - * - * Returns an allocated buffer with the content of file, NULL if error. - * - * Note: result must be freed after use. - */ - -char * -dir_file_get_content (const char *filename) -{ - char *buffer, *buffer2; - FILE *f; - size_t count, fp; - - if (!filename) - return NULL; - - buffer = NULL; - fp = 0; - - f = fopen (filename, "r"); - if (!f) - goto error; - - while (!feof (f)) - { - if (fp > SIZE_MAX - (1024 * sizeof (char))) - goto error; - buffer2 = (char *) realloc (buffer, (fp + (1024 * sizeof (char)))); - if (!buffer2) - goto error; - buffer = buffer2; - count = fread (&buffer[fp], sizeof (char), 1024, f); - if (count <= 0) - goto error; - fp += count; - } - if (fp > SIZE_MAX - sizeof (char)) - goto error; - buffer2 = (char *) realloc (buffer, fp + sizeof (char)); - if (!buffer2) - goto error; - buffer = buffer2; - buffer[fp] = '\0'; - fclose (f); - - return buffer; - -error: - if (buffer) - free (buffer); - if (f) - fclose (f); - return NULL; -} - -/* - * Copies a file to another location. - * - * Returns: - * 1: OK - * 0: error - */ - -int -dir_file_copy (const char *from, const char *to) -{ - FILE *src, *dst; - char *buffer; - int rc; - size_t count; - - rc = 0; - buffer = NULL; - src = NULL; - dst = NULL; - - if (!from || !from[0] || !to || !to[0]) - goto end; - - buffer = malloc (65536); - if (!buffer) - goto end; - - src = fopen (from, "rb"); - if (!src) - goto end; - dst = fopen (to, "wb"); - if (!dst) - goto end; - - while (!feof (src)) - { - count = fread (buffer, 1, 65536, src); - if (count <= 0) - goto end; - if (fwrite (buffer, 1, count, dst) <= 0) - goto end; - } - - rc = 1; - -end: - if (buffer) - free (buffer); - if (src) - fclose (src); - if (dst) - fclose (dst); - return rc; -} - -/* * Uses one or four different paths for WeeChat home directories. * * If 4 paths are given, they must be separated by colons and given in this @@ -1038,3 +722,589 @@ dir_get_string_home_dirs () return string_rebuild_split_string ((const char **)dirs, ":", 0, -1); } + +/* + * Finds files in a directory and executes a function on each file. + */ + +void +dir_exec_on_files (const char *directory, int recurse_subdirs, + int hidden_files, + void (*callback)(void *data, const char *filename), + void *callback_data) +{ + char complete_filename[PATH_MAX]; + DIR *dir; + struct dirent *entry; + struct stat statbuf; + + if (!directory || !callback) + return; + + dir = opendir (directory); + if (dir) + { + while ((entry = readdir (dir))) + { + if (hidden_files || (entry->d_name[0] != '.')) + { + snprintf (complete_filename, sizeof (complete_filename), + "%s/%s", directory, entry->d_name); + lstat (complete_filename, &statbuf); + if (S_ISDIR(statbuf.st_mode)) + { + if (recurse_subdirs + && (strcmp (entry->d_name, ".") != 0) + && (strcmp (entry->d_name, "..") != 0)) + { + dir_exec_on_files (complete_filename, 1, hidden_files, + callback, callback_data); + } + } + else + { + (*callback) (callback_data, complete_filename); + } + } + } + closedir (dir); + } +} + +/* + * Searches for the full name of a WeeChat library with name and extension + * (searches first in WeeChat user's dir, then WeeChat global lib directory). + * + * Returns name of library found, NULL if not found. + * + * Note: result must be freed after use (if not NULL). + */ + +char * +dir_search_full_lib_name_ext (const char *filename, const char *extension, + const char *plugins_dir) +{ + char *name_with_ext, *final_name, *extra_libdir; + int length; + struct stat st; + + length = strlen (filename) + strlen (extension) + 1; + name_with_ext = malloc (length); + if (!name_with_ext) + return NULL; + snprintf (name_with_ext, length, + "%s%s", + filename, + (strchr (filename, '.')) ? "" : extension); + + /* try libdir from environment variable WEECHAT_EXTRA_LIBDIR */ + extra_libdir = getenv (WEECHAT_EXTRA_LIBDIR); + if (extra_libdir && extra_libdir[0]) + { + length = strlen (extra_libdir) + strlen (name_with_ext) + + strlen (plugins_dir) + 16; + final_name = malloc (length); + if (!final_name) + { + free (name_with_ext); + return NULL; + } + snprintf (final_name, length, + "%s%s%s%s%s", + extra_libdir, + DIR_SEPARATOR, + plugins_dir, + DIR_SEPARATOR, + name_with_ext); + if ((stat (final_name, &st) == 0) && (st.st_size > 0)) + { + free (name_with_ext); + return final_name; + } + free (final_name); + } + + /* try WeeChat user's dir */ + length = strlen (weechat_data_dir) + strlen (name_with_ext) + + strlen (plugins_dir) + 16; + final_name = malloc (length); + if (!final_name) + { + free (name_with_ext); + return NULL; + } + snprintf (final_name, length, + "%s%s%s%s%s", + weechat_data_dir, + DIR_SEPARATOR, + plugins_dir, + DIR_SEPARATOR, + name_with_ext); + if ((stat (final_name, &st) == 0) && (st.st_size > 0)) + { + free (name_with_ext); + return final_name; + } + free (final_name); + + /* try WeeChat global lib dir */ + length = strlen (WEECHAT_LIBDIR) + strlen (name_with_ext) + + strlen (plugins_dir) + 16; + final_name = malloc (length); + if (!final_name) + { + free (name_with_ext); + return NULL; + } + snprintf (final_name, length, + "%s%s%s%s%s", + WEECHAT_LIBDIR, + DIR_SEPARATOR, + plugins_dir, + DIR_SEPARATOR, + name_with_ext); + if ((stat (final_name, &st) == 0) && (st.st_size > 0)) + { + free (name_with_ext); + return final_name; + } + free (final_name); + + free (name_with_ext); + + return NULL; +} + +/* + * Searches for the full name of a WeeChat library with name. + * + * All extensions listed in option "weechat.plugin.extension" are tested. + * + * Note: result must be freed after use (if not NULL). + */ + +char * +dir_search_full_lib_name (const char *filename, const char *plugins_dir) +{ + char *filename2, *full_name; + int i; + + /* expand home in filename */ + filename2 = string_expand_home (filename); + if (!filename2) + return NULL; + + /* if full path, return it */ + if (strchr (filename2, '/') || strchr (filename2, '\\')) + return filename2; + + if (config_plugin_extensions) + { + for (i = 0; i < config_num_plugin_extensions; i++) + { + full_name = dir_search_full_lib_name_ext ( + filename2, + config_plugin_extensions[i], + plugins_dir); + if (full_name) + { + free (filename2); + return full_name; + } + } + } + else + { + full_name = dir_search_full_lib_name_ext (filename2, "", plugins_dir); + if (full_name) + { + free (filename2); + return full_name; + } + } + + free (filename2); + + return strdup (filename); +} + +/* + * Reads content of a file. + * + * Returns an allocated buffer with the content of file, NULL if error. + * + * Note: result must be freed after use. + */ + +char * +dir_file_get_content (const char *filename) +{ + char *buffer, *buffer2; + FILE *f; + size_t count, fp; + + if (!filename) + return NULL; + + buffer = NULL; + fp = 0; + + f = fopen (filename, "r"); + if (!f) + goto error; + + while (!feof (f)) + { + if (fp > SIZE_MAX - (1024 * sizeof (char))) + goto error; + buffer2 = (char *) realloc (buffer, (fp + (1024 * sizeof (char)))); + if (!buffer2) + goto error; + buffer = buffer2; + count = fread (&buffer[fp], sizeof (char), 1024, f); + if (count <= 0) + goto error; + fp += count; + } + if (fp > SIZE_MAX - sizeof (char)) + goto error; + buffer2 = (char *) realloc (buffer, fp + sizeof (char)); + if (!buffer2) + goto error; + buffer = buffer2; + buffer[fp] = '\0'; + fclose (f); + + return buffer; + +error: + if (buffer) + free (buffer); + if (f) + fclose (f); + return NULL; +} + +/* + * Copies a file to another location. + * + * Returns: + * 1: OK + * 0: error + */ + +int +dir_file_copy (const char *from, const char *to) +{ + FILE *src, *dst; + char *buffer; + int rc; + size_t count; + + rc = 0; + buffer = NULL; + src = NULL; + dst = NULL; + + if (!from || !from[0] || !to || !to[0]) + goto end; + + buffer = malloc (65536); + if (!buffer) + goto end; + + src = fopen (from, "rb"); + if (!src) + goto end; + dst = fopen (to, "wb"); + if (!dst) + goto end; + + while (!feof (src)) + { + count = fread (buffer, 1, 65536, src); + if (count <= 0) + goto end; + if (fwrite (buffer, 1, count, dst) <= 0) + goto end; + } + + rc = 1; + +end: + if (buffer) + free (buffer); + if (src) + fclose (src); + if (dst) + fclose (dst); + return rc; +} + +/* + * Compresses a file with gzip. + * + * The output file must not exist. + * + * Returns: + * 1: OK + * 0: error + */ + +int +dir_file_compress_gzip (const char *from, const char *to, + int compression_level) +{ + FILE *source, *dest; + z_stream strm; + unsigned char *buffer_in, *buffer_out; + int rc, ret, flush; + size_t buffer_size, have; + + source = NULL; + dest = NULL; + buffer_size = 256 * 1024; + buffer_in = NULL; + buffer_out = NULL; + rc = 0; + + if (!from || !to || (compression_level < 1) || (compression_level > 9)) + goto end; + + if (access (to, F_OK) == 0) + goto end; + + buffer_in = malloc (buffer_size); + if (!buffer_in) + goto end; + buffer_out = malloc (buffer_size); + if (!buffer_out) + goto end; + + source = fopen (from, "rb"); + if (!source) + goto end; + dest = fopen (to, "wb"); + if (!dest) + goto end; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + ret = deflateInit2 ( + &strm, + compression_level, + Z_DEFLATED, /* method */ + 15 + 16, /* + 16 = gzip instead of zlib */ + 8, /* memLevel */ + Z_DEFAULT_STRATEGY); /* strategy */ + if (ret != Z_OK) + goto end; + + do + { + strm.avail_in = fread (buffer_in, 1, buffer_size, source); + if (ferror (source)) + goto error; + + flush = feof (source) ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = buffer_in; + + do + { + strm.avail_out = buffer_size; + strm.next_out = buffer_out; + ret = deflate (&strm, flush); + if (ret == Z_STREAM_ERROR) + goto error; + have = buffer_size - strm.avail_out; + if (fwrite (buffer_out, 1, have, dest) != have || ferror (dest)) + goto error; + } while (strm.avail_out == 0); + if (strm.avail_in != 0) + goto error; + } while (flush != Z_FINISH); + + if (ret != Z_STREAM_END) + goto error; + + (void) deflateEnd (&strm); + + rc = 1; + goto end; + +error: + (void) deflateEnd (&strm); + unlink (to); + +end: + if (buffer_in) + free (buffer_in); + if (buffer_out) + free (buffer_out); + if (source) + fclose (source); + if (dest) + fclose (dest); + + return rc; +} + +/* + * Compresses a file with zstandard. + * + * The output file must not exist. + * + * Returns: + * 1: OK + * 0: error + */ + +int +dir_file_compress_zstd (const char *from, const char *to, + int compression_level) +{ + FILE *source, *dest; + void *buffer_in, *buffer_out; + size_t buffer_in_size, buffer_out_size, num_read, remaining; + int rc, finished, last_chunk; + ZSTD_CCtx *cctx; + ZSTD_EndDirective mode; + ZSTD_inBuffer input; + ZSTD_outBuffer output; + + source = NULL; + dest = NULL; + buffer_in = NULL; + buffer_out = NULL; + cctx = NULL; + rc = 0; + + if (!from || !to || (compression_level < 1) || (compression_level > 19)) + goto end; + + if (access (to, F_OK) == 0) + goto end; + + buffer_in_size = ZSTD_CStreamInSize (); + buffer_in = malloc (buffer_in_size); + if (!buffer_in) + goto end; + buffer_out_size = ZSTD_CStreamOutSize (); + buffer_out = malloc (buffer_out_size); + if (!buffer_out) + goto end; + + source = fopen (from, "rb"); + if (!source) + goto end; + dest = fopen (to, "wb"); + if (!dest) + goto end; + + cctx = ZSTD_createCCtx (); + if (!cctx) + goto end; + + ZSTD_CCtx_setParameter (cctx, ZSTD_c_compressionLevel, compression_level); + + while (1) + { + num_read = fread (buffer_in, 1, buffer_in_size, source); + if (ferror (source)) + goto error; + last_chunk = (num_read < buffer_in_size); + mode = (last_chunk) ? ZSTD_e_end : ZSTD_e_continue; + input.src = buffer_in; + input.size = num_read; + input.pos = 0; + finished = 0; + while (!finished) + { + output.dst = buffer_out; + output.size = buffer_out_size; + output.pos = 0; + remaining = ZSTD_compressStream2(cctx, &output , &input, mode); + if (ZSTD_isError (remaining)) + goto error; + fwrite (buffer_out, 1, output.pos, dest); + finished = (last_chunk) ? (remaining == 0) : (input.pos == input.size); + }; + if (input.pos != input.size) + goto error; + if (last_chunk) + break; + } + + rc = 1; + goto end; + +error: + if (cctx) + { + ZSTD_freeCCtx (cctx); + cctx = NULL; + } + unlink (to); + +end: + if (cctx) + ZSTD_freeCCtx (cctx); + if (buffer_in) + free (buffer_in); + if (buffer_out) + free (buffer_out); + if (source) + fclose (source); + if (dest) + fclose (dest); + + return rc; +} + +/* + * Compresses a file with gzip or zstandard. + * + * The output file must not exist. + * + * Supported values for parameter "compressor": + * - "gzip": gzip compression (via zlib) + * - "zstd": zstandard compression + * + * Parameter "compression_level" is the compression level as percentage: + * from 1 (fast, low compression) to 100 (slow, best compression). + * + * Returns: + * 1: OK + * 0: error + */ + +int +dir_file_compress (const char *filename_input, + const char *filename_output, + const char *compressor, + int compression_level) +{ + int level; + + if (!compressor || (compression_level < 1) || (compression_level > 100)) + return 0; + + if (strcmp (compressor, "gzip") == 0) + { + /* convert percent to zlib compression level (1-9) */ + level = (((compression_level - 1) * 9) / 100) + 1; + return dir_file_compress_gzip (filename_input, filename_output, level); + } + else if (strcmp (compressor, "zstd") == 0) + { + /* convert percent to zstd compression level (1-19) */ + level = (((compression_level - 1) * 19) / 100) + 1; + return dir_file_compress_zstd (filename_input, filename_output, level); + } + else + { + return 0; + } +} diff --git a/src/core/wee-dir.h b/src/core/wee-dir.h index 861a68182..b02d31e28 100644 --- a/src/core/wee-dir.h +++ b/src/core/wee-dir.h @@ -25,6 +25,9 @@ extern int dir_mkdir_home (const char *directory, int mode); extern int dir_mkdir (const char *directory, int mode); extern int dir_mkdir_parents (const char *directory, int mode); extern int dir_rmtree (const char *directory); +extern void dir_create_home_dirs (); +extern void dir_remove_home_dirs (); +extern char *dir_get_string_home_dirs (); extern void dir_exec_on_files (const char *directory, int recurse_subdirs, int hidden_files, void (*callback)(void *data, @@ -34,8 +37,7 @@ extern char *dir_search_full_lib_name (const char *filename, const char *sys_directory); extern char *dir_file_get_content (const char *filename); extern int dir_file_copy (const char *from, const char *to); -extern void dir_create_home_dirs (); -extern void dir_remove_home_dirs (); -extern char *dir_get_string_home_dirs (); +extern int dir_file_compress (const char *from, const char *to, + const char *compressor, int compression_level); #endif /* WEECHAT_DIR_H */ diff --git a/src/gui/curses/CMakeLists.txt b/src/gui/curses/CMakeLists.txt index 71bb93ab2..27057d53b 100644 --- a/src/gui/curses/CMakeLists.txt +++ b/src/gui/curses/CMakeLists.txt @@ -47,6 +47,9 @@ list(APPEND EXTRA_LIBS "m") list(APPEND EXTRA_LIBS ${CURL_LIBRARIES}) +list(APPEND EXTRA_LIBS ${ZLIB_LIBRARY}) +list(APPEND EXTRA_LIBS ${LIBZSTD_LDFLAGS}) + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # link with resolv lib on macOS list(APPEND EXTRA_LIBS "resolv") diff --git a/src/gui/curses/headless/Makefile.am b/src/gui/curses/headless/Makefile.am index 5851d8583..18757c94d 100644 --- a/src/gui/curses/headless/Makefile.am +++ b/src/gui/curses/headless/Makefile.am @@ -48,6 +48,8 @@ weechat_headless_LDADD = ./../../../core/lib_weechat_core.a \ $(GCRYPT_LFLAGS) \ $(GNUTLS_LFLAGS) \ $(CURL_LFLAGS) \ + $(ZLIB_LFLAGS) \ + $(ZSTD_LFLAGS) \ -lm weechat_headless_SOURCES = main.c diff --git a/src/gui/curses/normal/Makefile.am b/src/gui/curses/normal/Makefile.am index 6936a41a4..df2fb2b58 100644 --- a/src/gui/curses/normal/Makefile.am +++ b/src/gui/curses/normal/Makefile.am @@ -45,6 +45,8 @@ weechat_LDADD = ./../../../core/lib_weechat_core.a \ $(GCRYPT_LFLAGS) \ $(GNUTLS_LFLAGS) \ $(CURL_LFLAGS) \ + $(ZLIB_LFLAGS) \ + $(ZSTD_LFLAGS) \ -lm weechat_SOURCES = main.c diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index 3e2471dd2..741876d12 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -676,6 +676,7 @@ plugin_load (const char *filename, int init_plugin, int argc, char **argv) new_plugin->exec_on_files = &dir_exec_on_files; new_plugin->file_get_content = &dir_file_get_content; new_plugin->file_copy = &dir_file_copy; + new_plugin->file_compress = &dir_file_compress; new_plugin->util_timeval_cmp = &util_timeval_cmp; new_plugin->util_timeval_diff = &util_timeval_diff; diff --git a/src/plugins/weechat-plugin.h b/src/plugins/weechat-plugin.h index 1bfa9a9dc..a0f6184ad 100644 --- a/src/plugins/weechat-plugin.h +++ b/src/plugins/weechat-plugin.h @@ -68,7 +68,7 @@ struct timeval; * please change the date with current one; for a second change at same * date, increment the 01, otherwise please keep 01. */ -#define WEECHAT_PLUGIN_API_VERSION "20220925-01" +#define WEECHAT_PLUGIN_API_VERSION "20220926-01" /* macros for defining plugin infos */ #define WEECHAT_PLUGIN_NAME(__name) \ @@ -402,6 +402,8 @@ struct t_weechat_plugin void *callback_data); char *(*file_get_content) (const char *filename); int (*file_copy) (const char *from, const char *to); + int (*file_compress) (const char *from, const char *to, + const char *compressor, int compression_level); /* util */ int (*util_timeval_cmp) (struct timeval *tv1, struct timeval *tv2); @@ -1415,6 +1417,10 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin); (weechat_plugin->file_get_content)(__filename) #define weechat_file_copy(__from, __to) \ (weechat_plugin->file_copy)(__from, __to) +#define weechat_file_compress(__from, __to, __compressor, \ + __compression_level) \ + (weechat_plugin->file_compress)(__from, __to, __compressor, \ + __compression_level) /* util */ #define weechat_util_timeval_cmp(__time1, __time2) \ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 765bdec2b..8d1f37f89 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -137,6 +137,8 @@ target_link_libraries(tests weechat_core ${EXTRA_LIBS} ${CURL_LIBRARIES} + ${ZLIB_LIBRARY} + ${LIBZSTD_LDFLAGS} ${CPPUTEST_LIBRARIES} -rdynamic ) diff --git a/tests/Makefile.am b/tests/Makefile.am index 190e7c79f..56c7440fd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -66,6 +66,8 @@ tests_LDADD = ./../src/core/lib_weechat_core.a \ $(GCRYPT_LFLAGS) \ $(GNUTLS_LFLAGS) \ $(CURL_LFLAGS) \ + $(ZLIB_LFLAGS) \ + $(ZSTD_LFLAGS) \ $(CPPUTEST_LFLAGS) \ -lm tests_LDFLAGS = -rdynamic |