diff -Nru libeconf-0.4.7+dfsg1/CMakeLists.txt libeconf-0.4.9+dfsg1/CMakeLists.txt --- libeconf-0.4.7+dfsg1/CMakeLists.txt 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/CMakeLists.txt 2022-11-23 12:45:05.000000000 +0000 @@ -3,7 +3,7 @@ # Ensure built-in policies from CMake are used, (e.g. improved policies for macOS) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -project(libeconf VERSION 0.4.7 +project(libeconf VERSION 0.4.9 DESCRIPTION "Enhanced config file parser, which merges config files placed in several locations into one." LANGUAGES C ) diff -Nru libeconf-0.4.7+dfsg1/debian/changelog libeconf-0.4.9+dfsg1/debian/changelog --- libeconf-0.4.7+dfsg1/debian/changelog 2022-10-16 17:24:25.000000000 +0000 +++ libeconf-0.4.9+dfsg1/debian/changelog 2022-11-26 13:22:34.000000000 +0000 @@ -1,3 +1,9 @@ +libeconf (0.4.9+dfsg1-1) unstable; urgency=medium + + * New upstream release. + + -- Andreas Henriksson Sat, 26 Nov 2022 14:22:34 +0100 + libeconf (0.4.7+dfsg1-1) unstable; urgency=medium * New upstream release. diff -Nru libeconf-0.4.7+dfsg1/doc/man/libeconf.3 libeconf-0.4.9+dfsg1/doc/man/libeconf.3 --- libeconf-0.4.7+dfsg1/doc/man/libeconf.3 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/doc/man/libeconf.3 2022-11-23 12:45:05.000000000 +0000 @@ -42,6 +42,15 @@ .br So the user will automatically get improvements of the vendor, with the drawback, that they could be incompatible with the user made changes. +.br +If there is a file with the same name in /usr/_vendor_/_example_._suffix_.d/ and +in /etc/_example_._suffix_.d/*._suffix_., the file in /usr/_vendor_/_example_._suffix_.d/ +will completely ignored. +.br +To disable a configuration file supplied by the vendor, the recommended way is to place +a symlink to /dev/null in the configuration directory in /etc/, with the same filename +as the vendor configuration file. + .SS "Example 1" .sp @@ -94,7 +103,7 @@ .in +1c .ti -1c -.RI "enum \fBeconf_err\fP { \fBECONF_SUCCESS\fP = 0, \fBECONF_ERROR\fP = 1, \fBECONF_NOMEM\fP = 2, \fBECONF_NOFILE\fP = 3, \fBECONF_NOGROUP\fP = 4, \fBECONF_NOKEY\fP = 5, \fBECONF_EMPTYKEY\fP = 6, \fBECONF_WRITEERROR\fP = 7, \fBECONF_PARSE_ERROR\fP = 8, \fBECONF_MISSING_BRACKET\fP = 9, \fBECONF_MISSING_DELIMITER\fP = 10, \fBECONF_EMPTY_SECTION_NAME\fP = 11, \fBECONF_TEXT_AFTER_SECTION\fP = 12, \fBECONF_FILE_LIST_IS_NULL\fP = 13, \fBECONF_WRONG_BOOLEAN_VALUE\fP = 14, \fBECONF_KEY_HAS_NULL_VALUE\fP = 15, \fBECONF_WRONG_OWNER\fP = 16, \fBECONF_WRONG_GROUP\fP = 17, \fBECONF_WRONG_FILE_PERMISSION\fP = 18, \fBECONF_WRONG_DIR_PERMISSION\fP = 19, \fBECONF_ERROR_FILE_IS_SYM_LINK\fP = 20 }" +.RI "enum \fBeconf_err\fP { \fBECONF_SUCCESS\fP = 0, \fBECONF_ERROR\fP = 1, \fBECONF_NOMEM\fP = 2, \fBECONF_NOFILE\fP = 3, \fBECONF_NOGROUP\fP = 4, \fBECONF_NOKEY\fP = 5, \fBECONF_EMPTYKEY\fP = 6, \fBECONF_WRITEERROR\fP = 7, \fBECONF_PARSE_ERROR\fP = 8, \fBECONF_MISSING_BRACKET\fP = 9, \fBECONF_MISSING_DELIMITER\fP = 10, \fBECONF_EMPTY_SECTION_NAME\fP = 11, \fBECONF_TEXT_AFTER_SECTION\fP = 12, \fBECONF_FILE_LIST_IS_NULL\fP = 13, \fBECONF_WRONG_BOOLEAN_VALUE\fP = 14, \fBECONF_KEY_HAS_NULL_VALUE\fP = 15, \fBECONF_WRONG_OWNER\fP = 16, \fBECONF_WRONG_GROUP\fP = 17, \fBECONF_WRONG_FILE_PERMISSION\fP = 18, \fBECONF_WRONG_DIR_PERMISSION\fP = 19, \fBECONF_ERROR_FILE_IS_SYM_LINK\fP = 20, \fBECONF_PARSING_CALLBACK_FAILED\fP = 21 }" .br .RI "libeconf error codes " .in -1c @@ -106,6 +115,10 @@ .br .RI "Process the file of the given file_name and save its contents into key_file object\&. " .ti -1c +.RI "\fBeconf_err\fP \fBeconf_readFileWithCallback\fP (\fBeconf_file\fP **result, const char *file_name, const char *delim, const char *comment, bool (*callback)(const char *filename))" +.br +.RI "Has the same functionality like \fBeconf_readFile\fP. The user can additionally define a callback in order to check the parsed file\&. " +.ti -1c .RI "\fBeconf_err\fP \fBeconf_mergeFiles\fP (\fBeconf_file\fP **merged_file, \fBeconf_file\fP *usr_file, \fBeconf_file\fP *etc_file)" .br .RI "Merge the contents of two key_files objects\&. " @@ -114,10 +127,18 @@ .br .RI "Evaluating key/values of a given configuration by reading and merging all needed/available files in two different directories (normally in /usr/etc and /etc)\&. " .ti -1c +.RI "\fBeconf_err\fP \fBeconf_readDirsWithCallback\fP (\fBeconf_file\fP **key_file, const char *usr_conf_dir, const char *etc_conf_dir, const char *project_name, const char *config_suffix, const char *delim, const char *comment, bool (*callback)(const char *filename))" +.br +.RI "Has the same functionality like \fBeconf_readDirs\fP. The user can additionally define a callback in order e.g. to check all parsed file\&. " +.ti -1c .RI "\fBeconf_err\fP \fBeconf_readDirsHistory\fP (\fBeconf_file\fP ***key_files, size_t *size, const char *usr_conf_dir, const char *etc_conf_dir, const char *project_name, const char *config_suffix, const char *delim, const char *comment)" .br .RI "Evaluating key/values for every given configuration files in two different directories (normally in /usr/etc and /etc)\&. " .ti -1c +.RI "\fBeconf_err\fP \fBeconf_readDirsHistoryWithCallback\fP (\fBeconf_file\fP ***key_files, size_t *size, const char *usr_conf_dir, const char *etc_conf_dir, const char *project_name, const char *config_suffix, const char *delim, const char *comment, bool (*callback)(const char *filename))" +.br +.RI "Has the same functionality like \fBeconf_readDirsHistory\fP. The user can additionally define a callback in order e.g. to check all parsed file\&. " +.ti -1c .RI "\fBeconf_err\fP \fBeconf_newKeyFile\fP (\fBeconf_file\fP **result, char delimiter, char comment)" .br .RI "Create a new econf_file object\&. " @@ -404,13 +425,16 @@ File has wrong group\&. .TP \fB\fIECONF_WRONG_FILE_PERMISSION \fP\fP -File has wrong file permission\&. +File has wrong file permissions\&. .TP \fB\fIECONF_WRONG_DIR_PERMISSION \fP\fP -File has wrong dir permission\&. +File has wrong dir permissions\&. .TP \fB\fIECONF_ERROR_FILE_IS_SYM_LINK \fP\fP File is a sym link which is not permitted\&. +.TP +\fB\fIECONF_PARSING_CALLBACK_FAILED \fP\fP +User defined parsing callback has failed\&. .PP .SH "Function Documentation" @@ -453,35 +477,47 @@ .PP Default behaviour if entries have the same name in one file: The first hit will be returned\&. Further entries will be ignored\&. This can be changed by setting the environment variable ECONF_JOIN_SAME_ENTRIES\&. In that case entries with the same name will be joined to one single entry\&. -.SS "\fBeconf_err\fP econf_readDirsHistory (\fBeconf_file\fP *** key_files, size_t * size, const char * usr_conf_dir, const char * etc_conf_dir, const char * project_name, const char * config_suffix, const char * delim, const char * comment)" +.SS "\fBeconf_err\fP econf_readFileWithCallback (\fBeconf_file\fP ** result, const char * file_name, const char * delim, const char * comment, bool (*callback)(const char *filename))" .PP -Evaluating key/values for every given configuration files in two different directories (normally in /usr/etc and /etc)\&. Returns a list of read configuration files and their values\&. +Process the file of the given file_name and save its contents into key_file object\&. The user defined function will be called in order e.g. to check the correct file permissions\&. .PP \fBParameters:\fP .RS 4 -\fIkey_files\fP list of parsed file(s)\&. Each entry includes all key/value, path, comments,\&.\&.\&. information of the regarding file\&. +\fIresult\fP content of parsed file .br -\fIsize\fP Size of the evaluated key_files list\&. +\fIfile_name\fP absolute path of parsed file .br -\fIusr_conf_dir\fP absolute path of the first directory (normally '/usr/etc') -.br -\fIetc_conf_dir\fP absolute path of the second directory (normally '/etc') +\fIdelim\fP delimiters of key/value e\&.g\&. '\\t =' .br -\fIproject_name\fP basename of the configuration file +\fIcomment\fP array of characters which define the start of a comment .br -\fIconfig_suffix\fP suffix of the configuration file\&. Can also be NULL\&. -.br -\fIdelim\fP delimiters of key/value e\&.g\&. '\\t =' -.br -\fIcomment\fP array of characters which define the start of a comment +\fIcallback\fP function which will be called for the given filename\&. This user defined function has the pathname as paramter and returns true if this file can be parsed\&. If not, the parsing will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned\&. .RE .PP \fBReturns:\fP .RS 4 -econf_err ECONF_SUCCESS or error code +econf_err ECONF_SUCCESS or error code .RE .PP +Usage: +.PP +.nf +#include "libeconf.h" +bool checkFile(const char *filename) { + /* checking code which returns true or false */ + return true; +} + +econf_file *key_file = NULL; +econf_err error; + +error = econf_readFileWithCallback (&key_file, "/etc/test.conf", "=", "#", checkFile); +econf_free (key_file); +.fi +.PP +.PP +Default behaviour if entries have the same name in one file: The first hit will be returned\&. Further entries will be ignored\&. This can be changed by setting the environment variable ECONF_JOIN_SAME_ENTRIES\&. In that case entries with the same name will be joined to one single entry\&. .SS "\fBeconf_err\fP econf_mergeFiles (\fBeconf_file\fP ** merged_file, \fBeconf_file\fP * usr_file, \fBeconf_file\fP * etc_file)" @@ -532,7 +568,7 @@ .br \fIusr_conf_dir\fP absolute path of the first directory (normally '/usr/etc') .br -\fIetc_conf_dir\fP absolute path of the second directory (normally '/etc') +\fIetc_conf_dir\fP absolute path of the second directory (normally '/etc') .br \fIproject_name\fP basename of the configuration file .br @@ -567,6 +603,122 @@ .fi .PP + +.SS "\fBeconf_err\fP econf_readDirsWithCallback (\fBeconf_file\fP ** key_file, const char * usr_conf_dir, const char * etc_conf_dir, const char * project_name, const char * config_suffix, const char * delim, const char * comment, bool (*callback)(const char *filename))" + +.PP +Evaluating the content of a given configuration file by reading all needed/available files in two different directories (normally in /usr/etc and /etc)\&. For each parsed file the user defined function will be called in order e.g. to check the correct file permissions\&. +.PP +\fBParameters:\fP +.RS 4 +\fIkey_file\fP content of parsed file(s) +.br +\fIusr_conf_dir\fP absolute path of the first directory (normally '/usr/etc') +.br +\fIetc_conf_dir\fP absolute path of the second directory (normally '/etc') +.br +\fIproject_name\fP basename of the configuration file +.br +\fIconfig_suffix\fP suffix of the configuration file\&. Can also be NULL\&. +.br +\fIdelim\fP delimiters of key/value e\&.g\&. '\\t =' +.br +\fIcomment\fP array of characters which define the start of a comment +.br +\fIcallback\fP function which will be called for each file\&. This user defined function has the pathname as paramter and returns true if this file can be parsed\&. If not, the parsing of all files will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned\&. +.RE +.PP +\fBReturns:\fP +.RS 4 +econf_err ECONF_SUCCESS or error code +.RE +.PP +Example: Reading content of example\&.conf in /usr/etc and /etc directory\&. +.PP +.nf +#include "libeconf.h" + +bool checkFile(const char *filename) { + /* checking code which returns true or false */ + return true; +} + +econf_file *key_file = NULL; +econf_err error; + +error = econf_readDirsWithCallback (&key_file, + "/usr/etc", + "/etc", + "example", + "conf", + "=", "#", + checkFile); + +econf_free (key_file); +.fi +.PP + +.SS "\fBeconf_err\fP econf_readDirsHistory (\fBeconf_file\fP *** key_files, size_t * size, const char * usr_conf_dir, const char * etc_conf_dir, const char * project_name, const char * config_suffix, const char * delim, const char * comment)" + +.PP +Evaluating key/values for every given configuration files in two different directories (normally in /usr/etc and /etc)\&. Returns a list of read configuration files and their values\&. +.PP +\fBParameters:\fP +.RS 4 +\fIkey_files\fP list of parsed file(s)\&. Each entry includes all key/value, path, comments,\&.\&.\&. information of the regarding file\&. +.br +\fIsize\fP Size of the evaluated key_files list\&. +.br +\fIusr_conf_dir\fP absolute path of the first directory (normally '/usr/etc') +.br +\fIetc_conf_dir\fP absolute path of the second directory (normally '/etc') +.br +\fIproject_name\fP basename of the configuration file +.br +\fIconfig_suffix\fP suffix of the configuration file\&. Can also be NULL\&. +.br +\fIdelim\fP delimiters of key/value e\&.g\&. '\\t =' +.br +\fIcomment\fP array of characters which define the start of a comment +.RE +.PP +\fBReturns:\fP +.RS 4 +econf_err ECONF_SUCCESS or error code +.RE +.PP + +.SS "\fBeconf_err\fP econf_readDirsHistoryWithCallback (\fBeconf_file\fP *** key_files, size_t * size, const char * usr_conf_dir, const char * etc_conf_dir, const char * project_name, const char * config_suffix, const char * delim, const char * comment, bool (*callback)(const char *filename))" + +.PP +Evaluating key/values for every given configuration files in two different directories (normally in /usr/etc and /etc)\&. For each parsed file the user defined function will be called in order e.g. to check the correct file permissions\&. Returns a list of read configuration files and their values\&. +.PP +\fBParameters:\fP +.RS 4 +\fIkey_files\fP list of parsed file(s)\&. Each entry includes all key/value, path, comments,\&.\&.\&. information of the regarding file\&. +.br +\fIsize\fP Size of the evaluated key_files list\&. +.br +\fIusr_conf_dir\fP absolute path of the first directory (normally '/usr/etc') +.br +\fIetc_conf_dir\fP absolute path of the second directory (normally '/etc') +.br +\fIproject_name\fP basename of the configuration file +.br +\fIconfig_suffix\fP suffix of the configuration file\&. Can also be NULL\&. +.br +\fIdelim\fP delimiters of key/value e\&.g\&. '\\t =' +.br +\fIcomment\fP array of characters which define the start of a comment +.br +\fIcallback\fP function which will be called for each file\&. This user defined function has the pathname as paramter and returns true if this file can be parsed\&. If not, the parsing of all files will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned\&. +.RE +.PP +\fBReturns:\fP +.RS 4 +econf_err ECONF_SUCCESS or error code +.RE +.PP .SS "\fBeconf_err\fP econf_newKeyFile (\fBeconf_file\fP ** result, char delimiter, char comment)" diff -Nru libeconf-0.4.7+dfsg1/include/libeconf.h libeconf-0.4.9+dfsg1/include/libeconf.h --- libeconf-0.4.7+dfsg1/include/libeconf.h 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/include/libeconf.h 2022-11-23 12:45:05.000000000 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -74,12 +75,14 @@ ECONF_WRONG_OWNER = 16, /** File has wrong group */ ECONF_WRONG_GROUP = 17, - /** File has wrong file permission */ + /** File has wrong file permissions */ ECONF_WRONG_FILE_PERMISSION = 18, /** File has wrong dir permission */ ECONF_WRONG_DIR_PERMISSION = 19, /** File is a sym link which is not permitted */ - ECONF_ERROR_FILE_IS_SYM_LINK = 20 + ECONF_ERROR_FILE_IS_SYM_LINK = 20, + /** User defined parsing callback has failed **/ + ECONF_PARSING_CALLBACK_FAILED = 21 }; typedef enum econf_err econf_err; @@ -150,6 +153,48 @@ extern econf_err econf_readFile(econf_file **result, const char *file_name, const char *delim, const char *comment); + +/** @brief Process the file of the given file_name and save its contents into key_file object. + * The user defined function will be called in order e.g. to check the correct file permissions. + * + * @param result content of parsed file + * @param file_name absolute path of parsed file + * @param delim delimiters of key/value e.g. "\t =". + * If delim contains space characters AND none space characters, + * multiline values are not parseable. + * @param comment array of characters which define the start of a comment + * @param callback function which will be called for the given filename. This user defined function has + * the pathname as paramter and returns true if this file can be parsed. If not, the + * parsing will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned. + * @return econf_err ECONF_SUCCESS or error code + * + * Usage: + * @code + * #include "libeconf.h" + * + * bool checkFile(const char *filename) { + * - checking code which returns true or false - + * return true; + * } + * + * econf_file *key_file = NULL; + * econf_err error; + * + * error = econf_readFileWithCallback (&key_file, "/etc/test.conf", "=", "#", checkFile); + * + * econf_free (key_file); + * @endcode + * + * Default behaviour if entries have the same name in one file: The + * first hit will be returned. Further entries will be ignored. + * This can be changed by setting the environment variable + * ECONF_JOIN_SAME_ENTRIES. In that case entries with the same name + * will be joined to one single entry. + */ +extern econf_err econf_readFileWithCallback(econf_file **result, const char *file_name, + const char *delim, const char *comment, + bool (*callback)(const char *filename)); + /** @brief Merge the contents of two key_files objects. Entries in etc_file will be * prefered. * Comment and delimiter tag will be taken from usr_file. This can be changed @@ -179,7 +224,7 @@ * */ extern econf_err econf_mergeFiles(econf_file **merged_file, - econf_file *usr_file, econf_file *etc_file); + econf_file *usr_file, econf_file *etc_file); /** @brief Evaluating key/values of a given configuration by reading and merging all * needed/available files in two different directories (normally in /usr/etc and /etc). @@ -214,12 +259,65 @@ * */ extern econf_err econf_readDirs(econf_file **key_file, - const char *usr_conf_dir, - const char *etc_conf_dir, - const char *project_name, - const char *config_suffix, - const char *delim, - const char *comment); + const char *usr_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment); + +/** @brief Evaluating key/values for every given configuration files in two different + * directories (normally in /usr/etc and /etc). For each parsed file the user defined function + * will be called in order e.g. to check the correct file permissions. + * + * @param key_files list of parsed file(s). + * Each entry includes all key/value, path, comments,... information of the regarding file. + * @param size Size of the evaluated key_files list. + * @param usr_conf_dir absolute path of the first directory (normally "/usr/etc") + * @param etc_conf_dir absolute path of the second directory (normally "/etc") + * @param project_name basename of the configuration file + * @param config_suffix suffix of the configuration file. Can also be NULL. + * @param delim delimiters of key/value e.g. "\t =" + * If delim contains space characters AND none space characters, + * multiline values are not parseable. + * @param comment array of characters which define the start of a comment + * @param callback function which will be called for each file. This user defined function has the + * pathname as paramter and returns true if this file can be parsed. If not, the parsing of + * all files will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned. + * @return econf_err ECONF_SUCCESS or error code + * + * Example: Reading content of example.conf in /usr/etc and /etc directory. + * @code + * #include "libeconf.h" + * + * bool checkFile(const char *filename) { + * - checking code which returns true or false - + * return true; + * } + * + * econf_file *key_file = NULL; + * econf_err error; + * + * error = econf_readDirsWithCallback (&key_file, + * "/usr/etc", + * "/etc", + * "example", + * "conf", + * "=", "#", + * checkFile); + * + * econf_free (key_file); + * @endcode + * + */ + extern econf_err econf_readDirsWithCallback(econf_file **key_file, + const char *usr_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename)); /** @brief Evaluating key/values for every given configuration files in two different * directories (normally in /usr/etc and /etc). Returns a list of read configuration @@ -248,6 +346,38 @@ const char *delim, const char *comment); +/** @brief Evaluating key/values for every given configuration files in two different + * directories (normally in /usr/etc and /etc). For each parsed file the user defined function + * will be called in order e.g. to check the correct file permissions. + * Returns a list of read configuration files and their values. + * + * @param key_files list of parsed file(s). + * Each entry includes all key/value, path, comments,... information of the regarding file. + * @param size Size of the evaluated key_files list. + * @param usr_conf_dir absolute path of the first directory (normally "/usr/etc") + * @param etc_conf_dir absolute path of the second directory (normally "/etc") + * @param project_name basename of the configuration file + * @param config_suffix suffix of the configuration file. Can also be NULL. + * @param delim delimiters of key/value e.g. "\t =" + * If delim contains space characters AND none space characters, + * multiline values are not parseable. + * @param comment array of characters which define the start of a comment + * @param callback function which will be called for each file. This user defined function has the + * pathname as paramter and returns true if this file can be parsed. If not, the parsing of + * all files will be aborted and ECONF_PARSING_CALLBACK_FAILED will be returned. + * @return econf_err ECONF_SUCCESS or error code + * + */ +extern econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, + size_t *size, + const char *usr_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename)); + /* The API/ABI of the following three functions (econf_newKeyFile, econf_newIniFile and econf_writeFile) are not stable and will change */ diff -Nru libeconf-0.4.7+dfsg1/lib/econf_error.c libeconf-0.4.9+dfsg1/lib/econf_error.c --- libeconf-0.4.7+dfsg1/lib/econf_error.c 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/lib/econf_error.c 2022-11-23 12:45:05.000000000 +0000 @@ -48,9 +48,10 @@ "Given key has NULL value", /* ECONF_KEY_HAS_NULL_VALUE */ "File has wrong owner", /* ECONF_WRONG_OWNER */ "File has wrong group", /* ECONF_WRONG_GROUP */ - "File has wrong file permission", /* ECONF_WRONG_FILE_PERMISSION */ - "File has wrong dir permission", /* ECONF_WRONG_DIR_PERMISSION */ - "File is a sym link which is not permitted" /* ECONF_ERROR_FILE_IS_SYM_LINK */ + "File has wrong file permissions", /* ECONF_WRONG_FILE_PERMISSION */ + "File has wrong dir permissions", /* ECONF_WRONG_DIR_PERMISSION */ + "File is a sym link which is not permitted", /* ECONF_ERROR_FILE_IS_SYM_LINK */ + "User defined parsing callback has failed" /* ECONF_PARSING_CALLBACK_FAILED */ }; const char * diff -Nru libeconf-0.4.7+dfsg1/lib/getfilecontents.c libeconf-0.4.9+dfsg1/lib/getfilecontents.c --- libeconf-0.4.7+dfsg1/lib/getfilecontents.c 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/lib/getfilecontents.c 2022-11-23 12:45:05.000000000 +0000 @@ -187,8 +187,15 @@ else ef->file_entry[ef->length-1].group = strdup(KEY_FILE_NULL_VALUE); - if (key) - ef->file_entry[ef->length-1].key = strdup(key); + if (key) { + /* remove space at the end of the key */ + const char *p = key + strlen(key); + if (p > key) + p--; + while (p > key && (isspace((unsigned)*p))) + p--; + ef->file_entry[ef->length-1].key = strndup(key, (size_t)(p+1-key)); + } else ef->file_entry[ef->length-1].key = strdup(KEY_FILE_NULL_VALUE); @@ -345,6 +352,20 @@ continue; } + if (delim == NULL || strlen(delim) == 0 || strcmp(delim, "\n") == 0) { + /* No delimiter is defined. Key without a value will be stored. */ + retval = store(ef, current_group, name, data, line, + current_comment_before_key, current_comment_after_value, + false, /* no quote */ + false /* new entry */); + free(current_comment_before_key); + current_comment_before_key = NULL; + free(current_comment_after_value); + current_comment_after_value = NULL; + continue; + } + + /* Valid delimters are defined */ /* go to the end of the name */ data = name; while (*data && !(isspace((unsigned)*data) || @@ -389,6 +410,8 @@ if (!found_delim && /* Entry has already been found */ ef->length > 0 && + /* Value of previous entry is not Null. So delimiter has been found in the previous line */ + ef->file_entry[ef->length-1].value != NULL && /* The Entry must be the next line. Otherwise it is a new one */ ef->file_entry[ef->length-1].line_number+1 == line) { diff -Nru libeconf-0.4.7+dfsg1/lib/keyfile.c libeconf-0.4.9+dfsg1/lib/keyfile.c --- libeconf-0.4.7+dfsg1/lib/keyfile.c 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/lib/keyfile.c 2022-11-23 12:45:05.000000000 +0000 @@ -41,10 +41,13 @@ printf("values:\n"); for(size_t i = 0; i < key_file.length; i++) { - printf(" group: %s ; key: %s ; value: %s\n", + printf(" group: %s ; key: %s ; value: %s ; pre_comment %s ; added_comment: %s\n", key_file.file_entry[i].group, key_file.file_entry[i].key, - key_file.file_entry[i].value); + key_file.file_entry[i].value, + key_file.file_entry[i].comment_before_key, + key_file.file_entry[i].comment_after_value + ); } printf("----------------------------------\n"); } diff -Nru libeconf-0.4.7+dfsg1/lib/libeconf.c libeconf-0.4.9+dfsg1/lib/libeconf.c --- libeconf-0.4.7+dfsg1/lib/libeconf.c 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/lib/libeconf.c 2022-11-23 12:45:05.000000000 +0000 @@ -136,13 +136,14 @@ } // Process the file of the given file_name and save its contents into key_file -econf_err econf_readFile(econf_file **key_file, const char *file_name, - const char *delim, const char *comment) +econf_err econf_readFileWithCallback(econf_file **key_file, const char *file_name, + const char *delim, const char *comment, + bool (*callback)(const char *filename)) { econf_err t_err; struct stat sb; - if (key_file == NULL || file_name == NULL || delim == NULL) + if (key_file == NULL || file_name == NULL || delim == NULL || comment == NULL) return ECONF_ERROR; // Checking file permissions, uid, group,... @@ -166,6 +167,10 @@ if (!(sb_dir.st_mode&file_perms_dir)) return ECONF_WRONG_DIR_PERMISSION; } + + // calling user defined checks + if (callback != NULL && !(*callback)(file_name)) + return ECONF_PARSING_CALLBACK_FAILED; // Get absolute path if not provided char *absolute_path = get_absolute_path(file_name, &t_err); @@ -198,6 +203,12 @@ return ECONF_SUCCESS; } +econf_err econf_readFile(econf_file **key_file, const char *file_name, + const char *delim, const char *comment) +{ + return econf_readFileWithCallback(key_file, file_name, delim, comment, NULL); +} + // Merge the contents of two key files econf_err econf_mergeFiles(econf_file **merged_file, econf_file *usr_file, econf_file *etc_file) { @@ -238,14 +249,15 @@ } -econf_err econf_readDirsHistory(econf_file ***key_files, - size_t *size, - const char *dist_conf_dir, - const char *etc_conf_dir, - const char *project_name, - const char *config_suffix, - const char *delim, - const char *comment) +econf_err econf_readDirsHistoryWithCallback(econf_file ***key_files, + size_t *size, + const char *dist_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename)) { const char *suffix, *default_dirs[3] = {NULL, NULL, NULL}; char *distfile, *etcfile, *cp; @@ -302,7 +314,7 @@ if (etcfile) { - error = econf_readFile(&key_file, etcfile, delim, comment); + error = econf_readFileWithCallback(&key_file, etcfile, delim, comment, callback); if (error && error != ECONF_NOFILE) return error; } @@ -316,7 +328,7 @@ and merge all *.d files. */ if (distfile) { - error = econf_readFile(&key_file, distfile, delim, comment); + error = econf_readFileWithCallback(&key_file, distfile, delim, comment, callback); if (error && error != ECONF_NOFILE) return error; } @@ -388,26 +400,41 @@ return ECONF_SUCCESS; } -econf_err econf_readDirs(econf_file **result, - const char *dist_conf_dir, - const char *etc_conf_dir, - const char *project_name, - const char *config_suffix, - const char *delim, - const char *comment) +econf_err econf_readDirsHistory(econf_file ***key_files, + size_t *size, + const char *dist_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment) { + return econf_readDirsHistoryWithCallback(key_files, size, dist_conf_dir, + etc_conf_dir, project_name, + config_suffix, delim, comment, NULL); +} + +econf_err econf_readDirsWithCallback(econf_file **result, + const char *dist_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename)) { size_t size = 0; econf_file **key_files; econf_err error; - error = econf_readDirsHistory(&key_files, - &size, - dist_conf_dir, - etc_conf_dir, - project_name, - config_suffix, - delim, - comment); + error = econf_readDirsHistoryWithCallback(&key_files, + &size, + dist_conf_dir, + etc_conf_dir, + project_name, + config_suffix, + delim, + comment, + callback); if (error != ECONF_SUCCESS) return error; @@ -418,6 +445,19 @@ return error; } +econf_err econf_readDirs(econf_file **result, + const char *dist_conf_dir, + const char *etc_conf_dir, + const char *project_name, + const char *config_suffix, + const char *delim, + const char *comment) +{ + return econf_readDirsWithCallback(result, dist_conf_dir, etc_conf_dir, + project_name, config_suffix, delim, + comment, NULL); +} + // Write content of a econf_file struct to specified location econf_err econf_writeFile(econf_file *key_file, const char *save_to_dir, const char *file_name) { diff -Nru libeconf-0.4.7+dfsg1/lib/libeconf.map libeconf-0.4.9+dfsg1/lib/libeconf.map --- libeconf-0.4.7+dfsg1/lib/libeconf.map 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/lib/libeconf.map 2022-11-23 12:45:05.000000000 +0000 @@ -62,4 +62,7 @@ econf_delimiter_tag; econf_set_comment_tag; econf_set_delimiter_tag; + econf_readFileWithCallback; + econf_readDirsWithCallback; + econf_readDirsHistoryWithCallback; } LIBECONF_0.3; diff -Nru libeconf-0.4.7+dfsg1/lib/mergefiles.c libeconf-0.4.9+dfsg1/lib/mergefiles.c --- libeconf-0.4.7+dfsg1/lib/mergefiles.c 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/lib/mergefiles.c 2022-11-23 12:45:05.000000000 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include // Insert the content of "etc_file.file_entry" into "fe" if there is no // group specified @@ -232,13 +233,30 @@ while(*key_files) { econf_err error; econf_file *tmp = *merged_files; + econf_file **double_key_files = key_files+1; + char * current_file = basename((*key_files)->path); - error = econf_mergeFiles(merged_files, *merged_files, *key_files); - if (error || *merged_files == NULL) - return error; - (*merged_files)->on_merge_delete = 1; + /* key_files are already sorted. If there is a file with the same name with + a higher priority, the current file will be ignored. + e.g. /usr/etc/shells.d/tcsh will not be merged if /etc/shells.d/tcsh exists. + */ + while (*double_key_files) { + char * compare_file = basename((*double_key_files)->path); + if (strcmp(current_file, ".") != 0 && strcmp(current_file, "..") && + strcmp(current_file, compare_file) == 0) { + break; + } + double_key_files++; + } + + if (*double_key_files == NULL) { + error = econf_mergeFiles(merged_files, *merged_files, *key_files); + if (error || *merged_files == NULL) + return error; + (*merged_files)->on_merge_delete = 1; + if(tmp->on_merge_delete) { econf_free(tmp); } + } - if(tmp->on_merge_delete) { econf_free(tmp); } if((*key_files)->on_merge_delete) { econf_free(*key_files); } key_files++; diff -Nru libeconf-0.4.7+dfsg1/meson.build libeconf-0.4.9+dfsg1/meson.build --- libeconf-0.4.7+dfsg1/meson.build 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/meson.build 2022-11-23 12:45:05.000000000 +0000 @@ -7,7 +7,7 @@ 'b_pie=true', 'warning_level=3',], license : 'MIT', - version : '0.4.7', + version : '0.4.9', ) cc = meson.get_compiler('c') diff -Nru libeconf-0.4.7+dfsg1/NEWS libeconf-0.4.9+dfsg1/NEWS --- libeconf-0.4.7+dfsg1/NEWS 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/NEWS 2022-11-23 12:45:05.000000000 +0000 @@ -1,3 +1,23 @@ +Version 0.4.9 +* new API calls: + ** econf_readFileWithCallback + Has the same functionality like econf_readFile. The user + can additionally define a callback in order to check the parsed file. + ** econf_readDirsWithCallback + Has the same functionality like econf_readDirs. The user + can additionally define a callback in order e.g. to check all parsed file. + ** econf_readDirsHistoryWithCallback + Has the same functionality like econf_readDirsHistory. + The user can additionally define a callback in order e.g. to check + all parsed file. + +Version 0.4.8 +* Parsing files which are containing keys only. + All delimiters are allowed now : "", " =", " ", "=". + But the user should use "" in order to be distinct. +* /usr/etc/shells.d/ will not be parsed if + /etc/shells.d/ is defined too. + Version 0.4.7 * econftool: Adapted compile options regarding LTO (Link Time Optimization). diff -Nru libeconf-0.4.7+dfsg1/README.md libeconf-0.4.9+dfsg1/README.md --- libeconf-0.4.7+dfsg1/README.md 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/README.md 2022-11-23 12:45:05.000000000 +0000 @@ -26,6 +26,13 @@ configuration file and merge all changes from /etc/_example_._suffix_.d/*._suffix_. So the user will automatically get improvements of the vendor, with the drawback, that they could be incompatible with the user made changes. +If there is a file with the same name in /usr/_vendor_/_example_._suffix_.d/ and +in /etc/_example_._suffix_.d/*._suffix_., the file in /usr/_vendor_/_example_._suffix_.d/ +will completely ignored. + +To disable a configuration file supplied by the vendor, the recommended way is to place +a symlink to /dev/null in the configuration directory in /etc/, with the same filename +as the vendor configuration file. **Example 1** diff -Nru libeconf-0.4.7+dfsg1/tests/CMakeLists.txt libeconf-0.4.9+dfsg1/tests/CMakeLists.txt --- libeconf-0.4.7+dfsg1/tests/CMakeLists.txt 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/CMakeLists.txt 2022-11-23 12:45:05.000000000 +0000 @@ -36,6 +36,7 @@ tst-string tst-string-append tst-security + tst-checkfiles tst-delimiter-comment tst-extvalue tst-comments diff -Nru libeconf-0.4.7+dfsg1/tests/meson.build libeconf-0.4.9+dfsg1/tests/meson.build --- libeconf-0.4.7+dfsg1/tests/meson.build 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/meson.build 2022-11-23 12:45:05.000000000 +0000 @@ -45,6 +45,8 @@ test('tst-string-append', tst_string_append_exe) tst_security_exe = executable('tst-security', 'tst-security.c', c_args: test_args, dependencies : libeconf_dep) test('tst-security', tst_security_exe) +tst_checkfiles_exe = executable('tst-checkfiles', 'tst-checkfiles.c', c_args: test_args, dependencies : libeconf_dep) +test('tst-checkfiles', tst_checkfiles_exe) tst_delimiter_comment_exe = executable('tst-delimiter-comment', 'tst-delimiter-comment.c', c_args: test_args, dependencies : libeconf_dep) test('tst-delimiter-comment', tst_delimiter_comment_exe) tst_extvalue_exe = executable('tst-extvalue', 'tst-extvalue.c', c_args: test_args, dependencies : libeconf_dep) @@ -100,6 +102,13 @@ tst_quote1_exe = executable('tst-quote1', 'tst-quote1.c', c_args: test_args, dependencies : libeconf_dep) test('tst-quote1', tst_quote1_exe) + +tst_shells1_exe = executable('tst-shells1', 'tst-shells1.c', c_args: test_args, dependencies : libeconf_dep) +test('tst-shells1', tst_shells1_exe) +tst_shells2_exe = executable('tst-shells2', 'tst-shells2.c', c_args: test_args, dependencies : libeconf_dep) +test('tst-shells2', tst_shells2_exe) + + test('tst_econftool1', find_program('tst-econftool1.sh')) test('tst_econftool_show1', find_program('tst-econftool_show1.sh')) test('tst_econftool_cat', find_program('tst-econftool_cat.sh')) diff -Nru libeconf-0.4.7+dfsg1/tests/tst-checkfiles.c libeconf-0.4.9+dfsg1/tests/tst-checkfiles.c --- libeconf-0.4.7+dfsg1/tests/tst-checkfiles.c 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-checkfiles.c 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,50 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include "libeconf.h" +bool checkLink(const char *filename); + +/* Test case: + * Using user defined callback for checking files which will be parsed. + */ + + bool checkLink(const char *filename) { + struct stat sb; + + if (lstat(filename, &sb) == -1 || (sb.st_mode&S_IFMT) == S_IFLNK) + return false; + + return true; + } + +int +main(void) +{ + econf_file *key_file = NULL; + econf_err error; + + + /* checking not allowed links*/ + if (symlink(TESTSDIR"tst-arguments-string/etc/arguments.conf", TESTSDIR"tst-arguments-string/etc/link.conf") == -1) + { + fprintf (stderr, "ERROR: cannot create sym link: %s\n", TESTSDIR"tst-arguments-string/etc/link.conf"); + return 1; + } + + error = econf_readFileWithCallback (&key_file, TESTSDIR"tst-arguments-string/etc/link.conf", "=", "#", checkLink); + remove(TESTSDIR"tst-arguments-string/etc/link.conf"); + if (error != ECONF_PARSING_CALLBACK_FAILED) + { + fprintf (stderr, "ERROR: expecting: %s\n returned: %s\n", + econf_errString(ECONF_PARSING_CALLBACK_FAILED), + econf_errString(error)); + return 1; + } + + return 0; +} diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells1.c libeconf-0.4.9+dfsg1/tests/tst-shells1.c --- libeconf-0.4.7+dfsg1/tests/tst-shells1.c 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells1.c 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "libeconf.h" +#include "libeconf_ext.h" + +/* Test case: + Open default /etc/shells and try out if we can parse entries +*/ + +int +main(void) +{ + econf_file *key_file = NULL; + char **keys; + size_t key_number; + char *val; + econf_err error; + + if ((error = econf_readFile (&key_file, TESTSDIR"tst-shells1-data/etc/shells", "", "#"))) + { + fprintf (stderr, "ERROR: couldn't read configuration file: %s\n", econf_errString(error)); + return 1; + } + + error = econf_getKeys(key_file, NULL, &key_number, &keys); + if (error) + { + fprintf (stderr, "Error getting all keys: %s\n", econf_errString(error)); + return 1; + } + + if (key_number == 0) + { + fprintf (stderr, "No keys found?\n"); + return 1; + } else { + fprintf (stderr, "%ld keys found\n", key_number); + } + for (size_t i = 0; i < key_number; i++) + { + printf ("%zu: --%s--\n", i, keys[i]); + } + + if ((error = econf_getStringValue (key_file, NULL, "/usr/bin/bash", &val))) + { + fprintf (stderr, "Error reading /usr/bin/bash: %s\n", + econf_errString(error)); + return 1; + } + else if (val != NULL && strlen(val) > 0) + { + fprintf (stderr, "/usr/bin/bash returns wrong value: '%s'\n", val); + return 1; + } + free (val); + + if (!(error = econf_getStringValue (key_file, NULL, "doesnotexist", &val))) + { + fprintf (stderr, "No error looking for \"doesnotexist\"!\n"); + return 1; + } + free (val); + + econf_ext_value *ext_val; + + if ((error = econf_getExtValue(key_file, NULL, "/bin/bash", &ext_val))) + { + fprintf (stderr, "Error ext-reading /bin/bash: %s\n", + econf_errString(error)); + return 1; + } + + const char *comment_before_key = " comment line 2"; + if (ext_val->comment_before_key == NULL || + strcmp(ext_val->comment_before_key, comment_before_key) != 0) + { + fprintf (stderr, "ERROR: %s\nExpected String:\n'%s'\nGot:\n'%s'\n", + "/usr/bin/bash", comment_before_key, ext_val->comment_before_key); + econf_freeExtValue(ext_val); + return false; + } + + econf_freeExtValue(ext_val); + if ((error = econf_getExtValue(key_file, NULL, "/bin/csh", &ext_val))) + { + fprintf (stderr, "Error ext-reading /bin/csh: %s\n", + econf_errString(error)); + return 1; + } + + const char *comment_after_value = " comment for /bin/csh"; + if (ext_val->comment_after_value == NULL || + strcmp(ext_val->comment_after_value, comment_after_value) != 0) + { + fprintf (stderr, "ERROR: %s\nExpected String:\n'%s'\nGot:\n'%s'\n", + "/usr/bin/csh", comment_after_value, ext_val->comment_after_value); + econf_freeExtValue(ext_val); + return false; + } + econf_freeExtValue(ext_val); + + // Rewrite file to disk + econf_writeFile(key_file, TESTSDIR"tst-shells1-data/", "out.ini"); + + // And reading it again + econf_file *key_compare; + error = econf_readFile(&key_compare, + TESTSDIR"tst-shells1-data/out.ini", "", "#"); + if (error || key_compare == NULL) { + fprintf (stderr, "ERROR: couldn't read written configuration file: %s\n", econf_errString(error)); + return 1; + } + remove(TESTSDIR"tst-shells1-data/out.ini"); + + econf_free (keys); + econf_free (key_file); + + return 0; +} diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells1-data/etc/shells libeconf-0.4.9+dfsg1/tests/tst-shells1-data/etc/shells --- libeconf-0.4.7+dfsg1/tests/tst-shells1-data/etc/shells 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells1-data/etc/shells 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,25 @@ +/bin/ash +# comment line 2 +/bin/bash +/bin/csh # comment for /bin/csh +/bin/dash +/bin/false +/bin/ksh +/bin/ksh93 +/bin/mksh +/bin/pdksh +/bin/sh +/bin/tcsh +/bin/true +/bin/zsh +/usr/bin/csh +/usr/bin/dash +/usr/bin/ksh +/usr/bin/ksh93 +/usr/bin/mksh +/usr/bin/passwd +/usr/bin/pdksh +/usr/bin/bash +/usr/bin/tcsh +/usr/bin/zsh +/usr/bin/fish diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells2.c libeconf-0.4.9+dfsg1/tests/tst-shells2.c --- libeconf-0.4.7+dfsg1/tests/tst-shells2.c 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells2.c 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,98 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "libeconf.h" + +/* Test case: + /usr/etc/shells exists + /usr/etc/shells.d/tcsh exists and contains /bin/csh + /etc/shells.d/tcsh exists but does not contain /bin/csh + + /bin/csh should not be a valid shell +*/ + +static int +check_shell(econf_file *key_file, char *shell, int expected) +{ + char *val = NULL; + econf_err error = econf_getStringValue (key_file, "", shell, &val); + + if (error) + { + if (expected) + { + fprintf (stderr, "FAILED: %s: %s\n", shell, econf_errString(error)); + return 1; + } + else + printf("OK: %s not found\n", shell); + } + else + { + if (expected) + printf("OK: %s found\n", shell); + else + { + fprintf (stderr, "FAILED: %s found\n", shell); + return 1; + } + } + + return 0; +} + +int +main(void) +{ + econf_file *key_file = NULL; + int retval = 0; + econf_err error; + char **keys; + size_t key_number; + + error = econf_readDirs (&key_file, + TESTSDIR"tst-shells2-data/usr/etc", + TESTSDIR"tst-shells2-data/etc", + "shells", NULL, "", "#"); + if (error) + { + fprintf (stderr, "ERROR: econf_readDirs: %s\n", + econf_errString(error)); + return 1; + } + + error = econf_getKeys(key_file, NULL, &key_number, &keys); + if (error) + { + fprintf (stderr, "Error getting all keys: %s\n", econf_errString(error)); + return 1; + } + + if (key_number == 0) + { + fprintf (stderr, "No keys found?\n"); + return 1; + } else { + fprintf (stderr, "%ld keys found\n", key_number); + } + for (size_t i = 0; i < key_number; i++) + { + printf ("%zu: --%s--\n", i, keys[i]); + } + + if (check_shell(key_file, "/bin/csh", 0) != 0) + retval = 1; + if (check_shell(key_file, "/bin/tcsh", 1) != 0) + retval = 1; + if (check_shell(key_file, "/bin/foo", 0) != 0) + retval = 1; + + econf_free (keys); + econf_free (key_file); + + return retval; +} Only in /tmp/tmpg5httb0m/G3mDSNpm_P/libeconf-0.4.9+dfsg1/tests/tst-shells2-data/etc/shells.d: foo diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells2-data/etc/shells.d/tcsh libeconf-0.4.9+dfsg1/tests/tst-shells2-data/etc/shells.d/tcsh --- libeconf-0.4.7+dfsg1/tests/tst-shells2-data/etc/shells.d/tcsh 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells2-data/etc/shells.d/tcsh 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,3 @@ +# Allow tcsh, but no csh +/bin/tcsh +/usr/bin/tcsh diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells --- libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,18 @@ +/bin/ash +/bin/dash +/bin/false +/bin/ksh +/bin/ksh93 +/bin/mksh +/bin/pdksh +/bin/sh +/bin/true +/bin/zsh +/usr/bin/dash +/usr/bin/ksh +/usr/bin/ksh93 +/usr/bin/mksh +/usr/bin/passwd +/usr/bin/pdksh +/usr/bin/zsh +/usr/bin/fish diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/bash libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/bash --- libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/bash 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/bash 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,2 @@ +/bin/bash +/usr/bin/bash diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/foo libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/foo --- libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/foo 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/foo 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,2 @@ +/bin/foo +/usr/bin/foo \ No newline at end of file diff -Nru libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/tcsh libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/tcsh --- libeconf-0.4.7+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/tcsh 1970-01-01 00:00:00.000000000 +0000 +++ libeconf-0.4.9+dfsg1/tests/tst-shells2-data/usr/etc/shells.d/tcsh 2022-11-23 12:45:05.000000000 +0000 @@ -0,0 +1,4 @@ +/bin/csh +/bin/tcsh +/usr/bin/csh +/usr/bin/tcsh diff -Nru libeconf-0.4.7+dfsg1/util/econftool.c libeconf-0.4.9+dfsg1/util/econftool.c --- libeconf-0.4.7+dfsg1/util/econftool.c 2022-10-14 09:26:37.000000000 +0000 +++ libeconf-0.4.9+dfsg1/util/econftool.c 2022-11-23 12:45:05.000000000 +0000 @@ -736,7 +736,7 @@ if (getenv("XDG_CONFIG_HOME") == NULL) { /* if no XDG_CONFIG_HOME is specified take ~/.config as default */ - snprintf(xdg_config_dir, sizeof(xdg_config_dir), "%s/%s", home_dir, ".config"); + snprintf(xdg_config_dir, sizeof(xdg_config_dir-8), "%s/.config", home_dir); } else { snprintf(xdg_config_dir, sizeof(xdg_config_dir), "%s", getenv("XDG_CONFIG_HOME")); }