Index: build/list-chkfiles.sh ================================================================== --- build/list-chkfiles.sh +++ build/list-chkfiles.sh @@ -5,6 +5,10 @@ if [ "${file}" = "config.h" ]; then continue; fi if [ "${file}" = "win32.h" ]; then continue; fi filelist="${filelist} ${file}" done -grep ' HAVE_.*_H$' ${filelist} | sed 's@.*HAVE_\(.*_H\)@\1@;s@_H$@.h@;s@_@/@g' | dd conv=lcase 2>/dev/null | grep -v '^config.h$' | sort -u +( + grep ' HAVE_.*_H$' ${filelist} | sed 's@.*HAVE_\(.*_H\)@\1@;s@_H$@.h@;s@_@/@g' | dd conv=lcase 2>/dev/null | grep -v '^config.h$' + echo 'time.h' + echo 'sys/time.h' +) | sort -u Index: config.h.in ================================================================== --- config.h.in +++ config.h.in @@ -4,24 +4,21 @@ #undef HAVE_CTYPE_H /* Define to 1 if you have the header file. */ #undef HAVE_DIRENT_H -/* Define to 1 if you have the `getopt' function. */ -#undef HAVE_GETOPT - -/* Define to 1 if you have the header file. */ -#undef HAVE_GETOPT_H +/* Define to 1 if you have the `getpwuid' function. */ +#undef HAVE_GETPWUID /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H -/* Define to 1 if you have the header file. */ -#undef HAVE_LIBINTL_H - /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ Index: configure ================================================================== --- configure +++ configure @@ -3067,13 +3067,11 @@ - - -for ac_header in ctype.h dirent.h libintl.h stdio.h stdlib.h string.h strings.h sys/stat.h sys/types.h unistd.h getopt.h time.h sys/time.h +for ac_header in ctype.h dirent.h pwd.h stdio.h stdlib.h string.h sys/stat.h sys/time.h sys/types.h time.h unistd.h do as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` if eval "test \"\${$as_ac_Header+set}\" = set"; then echo "$as_me:$LINENO: checking for $ac_header" >&5 echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 @@ -15358,12 +15356,11 @@ fi done - -for ac_func in getopt strsep +for ac_func in strsep do as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` echo "$as_me:$LINENO: checking for $ac_func" >&5 echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 if eval "test \"\${$as_ac_var+set}\" = set"; then @@ -15470,10 +15467,112 @@ fi done + +for ac_func in getpwuid +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + for ac_header in windows.h windowsx.h Index: configure.in ================================================================== --- configure.in +++ configure.in @@ -1,7 +1,7 @@ AC_REVISION($Revision $) -AC_INIT(libconfig, 0.0.2) +AC_INIT(libconfig, 0.0.3) AC_CONFIG_HEADER(config.h) dnl Checks for programs. AC_PROG_CC AC_PROG_MAKE_SET @@ -10,11 +10,11 @@ AC_PROG_RANLIB AC_AIX AC_CHECK_TOOL(AR, ar, true) AC_GNU_SOURCE -AC_CHECK_HEADERS(ctype.h dirent.h libintl.h stdio.h stdlib.h string.h strings.h sys/stat.h sys/types.h unistd.h time.h sys/time.h) +AC_CHECK_HEADERS(ctype.h dirent.h pwd.h stdio.h stdlib.h string.h sys/stat.h sys/time.h sys/types.h time.h unistd.h) AC_HEADER_TIME DC_DO_TYPE(uint64_t, unsigned, 8) DC_DO_TYPE(int64_t, signed, 8) DC_DO_TYPE(uint32_t, unsigned, 4) @@ -21,12 +21,13 @@ DC_DO_TYPE(int32_t, signed, 4) DC_DO_TYPE(uint16_t, unsigned, 2) DC_DO_TYPE(int16_t, signed, 2) AC_REPLACE_FUNCS(strsep) +AC_CHECK_FUNCS(getpwuid) dnl Checks for Win32 specific things. DC_DO_WIN32 DC_GET_SHOBJFLAGS AC_OUTPUT(Makefile lc_geterrno.3 lc_process.3 lc_register_var.3 lc_geterrstr.3 lc_register_callback.3) Index: lc_register_callback.3.in ================================================================== --- lc_register_callback.3.in +++ lc_register_callback.3.in @@ -0,0 +1,229 @@ +.TH LC_REGISTER_CALLBACK 3 "25 Oct 04" "@PACKAGE_STRING@" +.SH NAME +lc_register_callback \- Register a function for callback in config processing. + +.SH SYNOPSIS +.B #include +.sp +.BI "int lc_register_callback(const char *" var ", char " opt ", lc_var_type_t " type ", int (*callback)(const char *, const char *, const char *, const char *, lc_flags_t, void *), void *" extra); + +.SH DESCRIPTION +The +.B lc_register_callback +function registers a function to be called when +.IR var +is encounted in a configuration file, command line, or environment variable. +The parameters are as follows: +.TP +.IR "const char *var" +.RS +The +.IR var +parameter indicates the name of the variable to register for a callback when encountered in a configuration file, the environment, or as a long option. The +.IR var +may be prefixed with "*." to indicate that the object can occur in any section or subsection. +.RE + +.TP +.IR "const char opt" +.RS +The +.IR opt +parameter indicates the single charectar short option to use from the command line to invoke the register callback. A value of 0 indicates that no short option is acceptable. +.RE + +.TP +.IR "lc_var_type_t type" +.RS +The +.IR type +parameter indicates the type of values that are acceptable for this callback. Currently only LC_VAR_NONE is acceptable, since callbacks may not have values (though they may have arguments). +.RE + +.TP +.IR "int (*callback)(...)" +.RS +The +.IR callback +parameter indicates the name of the function to invoke when the above parameters are met. The specified function should take 6 parameters, see below for more information. This value may not be NULL. +.RE + +.TP +.IR "void *extra" +.RS +The +.IR extra +parameter is a pointer that can be used to pass data to the callback upon invocation, it will not be mangled or examined by any function. +.RE + +The arguments to the function specified as +.IR callback +are as follows: +.TP +.I "const char *shortvar" +.RS +The +.I shortvar +parameter is the local variable name, provided as a convience. It is the portion of the variable name after the first "dot" (.) in the fully qualified variable name. The "dot" (.) value in the fully qualified variable name indicates a section or subsection that the variable belongs to. +This may be +.B NULL +if the +.IR var +parameter to +.BI lc_register_callback (3) +was +.B NULL +too. +.RE +.TP +.I "const char *var" +.RS +The +.I var +parameter is the fully qualified variable name. It includes in the prefix any sections and subsections that contain this variable. +This may be +.B NULL +if the +.IR var +parameter to +.BI lc_register_callback (3) +was +.B NULL +too. +.RE +.TP +.I "const char *arguments" +.RS +The +.I arguments +parameter provides the arguments passed to the variable, currently only sections may have arguments. +This may be +.B NULL +if there were no arguments specified, or if arguments were not applicable. +.RE +.TP +.I "const char *value" +.RS +The +.I value +parameter provides the value of the variable specified. +This may be +.B NULL +if no value was specified. Values are required if the +.IR type +parameter to +.BI lc_register_callback (3) +was not specified as one of LC_VAR_NONE, LC_VAR_SECTION, LC_VAR_SECTIONSTART, or LC_VAR_SECTIONEND. +.RE +.TP +.I "lc_flags_t flags" +.RS +The flags parameter provides information about the type of command being called. The valid values are: +.IP LC_FLAGS_VAR +To indicate a regular variable in a configuration file. +.IP LC_FLAGS_CMDLINE +To indicate a command line option has been used to invoke this option. +.IP LC_FLAGS_SECTIONSTART +To indicate that this command represents the beginning of a section. +.IP LC_FLAGS_SECTIONEND +To indicate that this command represents the end of a section. +.RE +.TP +.I "void *extra" +.RS +The +.I extra +parameter is just a copy of the +.IR extra +parameter passed to +.BI lc_register_callback (3) +when the callback was registered. +.RE + +The +.IR callback +function should return one of three values: +.TP +LC_CBRET_IGNORESECTION +Returning LC_CBRET_IGNORESECTION from a callback that begins a section causes the entire section to be ignored without generating an error. +.TP +LC_CBRET_OKAY +Returning LC_CBRET_OKAY from a callback indicates that all went well and further processing may continue. +.TP +LC_CBRET_ERROR +Returnning LC_CBRET_ERROR from a callback indicates that the command failed for some reason, the error will be passed back down the chain back to the +.BI lc_process (3) +call that began processing the configuration data. If LC_CBRET_ERROR is returned from a callback that begins a section, the entire section is ignored. + + + "RETURN VALUE" +On success 0 is returned, otherwise -1 is returned. + +.SH EXAMPLE +.nf +#include +#include +#include +#include + +int callback_ifmodule(const char *shortvar, const char *var, + const char *arguments, const char *value, + lc_flags_t flags, void *extra) { + if (flags == LC_FLAGS_SECTIONEND) { + return(LC_CBRET_OKAY); + } + + if (flags != LC_FLAGS_SECTIONSTART) { + fprintf(stderr, "IfModule can only be used as a \\ + section.\\n"); + return(LC_CBRET_ERROR); + } + if (arguments == NULL) { + fprintf(stderr, "You must specify an argument to \\ + IfModule.\\n"); + return(LC_CBRET_ERROR); + } + + printf("IfModule %s\\n", arguments); + + if (strcasecmp(arguments, "MyModule") == 0) { + return(LC_CBRET_IGNORESECTION); + } + + return(LC_CBRET_OKAY); +} + +int main(int argc, char **argv) { + int lc_rc_ret = 0, lc_p_ret; + + lc_rc_ret = lc_register_callback("*.IfModule", 0, LC_VAR_NONE, + callback_ifmodule, NULL); + + if (lc_rc_ret != 0) { + fprintf(stderr, "Error registering callback.\\n"); + return(EXIT_FAILURE); + } + + lc_p_ret = lc_process(argc, argv, "example", LC_CONF_APACHE, + NULL); + if (lc_p_ret != 0) { + fprintf(stderr, "Error processing configuration: \\ + %s\\n", lc_geterrstr()); + return(EXIT_FAILURE); + } + + return(EXIT_SUCCESS); +} +.fi + +.SH ERRORS +.TP +.B ENOMEM +Memory could not be allocated to create the needed internal structures. + +.SH "SEE ALSO" +.BR lc_register_var (3), +.BR lc_geterrno (3), +.BR lc_geterrstr (3), +.BR lc_process (3), +.BR lc_register_var (3) Index: libconfig.c ================================================================== --- libconfig.c +++ libconfig.c @@ -17,10 +17,26 @@ #endif #ifdef HAVE_CTYPE_H #include #endif + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_PWD_H +#include +#endif struct lc_varhandler_st *varhandlers = NULL; lc_err_t lc_errno = LC_ERR_NONE; static int lc_process_var_string(void *data, const char *value) { @@ -417,10 +433,19 @@ } else if (strcasecmp(handler->var, var) != 0) { /* Exact (case-insensitive comparison) failed. */ continue; } } + + if (value == NULL && + handler->type != LC_VAR_NONE && + handler->type != LC_VAR_SECTION && + handler->type != LC_VAR_SECTIONSTART && + handler->type != LC_VAR_SECTIONEND) { + lc_errno = LC_ERR_BADFORMAT; + break; + } return(lc_handle(handler, var, varargs, value, flags)); } return(-1); @@ -476,39 +501,106 @@ varhandlers = newhandler; return(0); } -int lc_process(int argc, char **argv, const char *appname, lc_conf_type_t type, const char *extra) { - int retval = 0, chkretval = 0; +static int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type) { + int chkretval = 0; - /* XXX Handle config files. need to handle this in a loop of config files... */ switch (type) { case LC_CONF_SECTION: - chkretval = lc_process_conf_section(appname, extra); + chkretval = lc_process_conf_section(appname, pathname); break; case LC_CONF_APACHE: - chkretval = lc_process_conf_apache(appname, extra); + chkretval = lc_process_conf_apache(appname, pathname); break; case LC_CONF_COLON: - chkretval = lc_process_conf_colon(appname, extra); + chkretval = lc_process_conf_colon(appname, pathname); break; case LC_CONF_EQUAL: - chkretval = lc_process_conf_equal(appname, extra); + chkretval = lc_process_conf_equal(appname, pathname); break; case LC_CONF_SPACE: - chkretval = lc_process_conf_space(appname, extra); + chkretval = lc_process_conf_space(appname, pathname); break; case LC_CONF_XML: - chkretval = lc_process_conf_xml(appname, extra); + chkretval = lc_process_conf_xml(appname, pathname); break; default: chkretval = -1; lc_errno = LC_ERR_INVDATA; break; } + return(chkretval); +} + +static int lc_process_files(const char *appname, lc_conf_type_t type, const char *extraconfig) { + struct passwd *pwinfo = NULL; + char configfiles[3][13][512] = {{{0}}}; + char *configfile = NULL; + char *homedir = NULL; + int configsetidx = 0, configidx = 0; + int chkretval = 0, retval = 0; + + snprintf(configfiles[0][0], sizeof(**configfiles) - 1, "/etc/%s.cfg", appname); + snprintf(configfiles[0][1], sizeof(**configfiles) - 1, "/etc/%s.conf", appname); + snprintf(configfiles[0][2], sizeof(**configfiles) - 1, "/etc/%s/%s.cfg", appname, appname); + snprintf(configfiles[0][3], sizeof(**configfiles) - 1, "/etc/%s/%s.conf", appname, appname); + snprintf(configfiles[0][4], sizeof(**configfiles) - 1, "/usr/etc/%s.cfg", appname); + snprintf(configfiles[0][5], sizeof(**configfiles) - 1, "/usr/etc/%s.conf", appname); + snprintf(configfiles[0][6], sizeof(**configfiles) - 1, "/usr/etc/%s/%s.cfg", appname, appname); + snprintf(configfiles[0][7], sizeof(**configfiles) - 1, "/usr/etc/%s/%s.conf", appname, appname); + snprintf(configfiles[0][8], sizeof(**configfiles) - 1, "/usr/local/etc/%s.cfg", appname); + snprintf(configfiles[0][9], sizeof(**configfiles) - 1, "/usr/local/etc/%s.conf", appname); + snprintf(configfiles[0][10], sizeof(**configfiles) - 1, "/usr/local/etc/%s/%s.cfg", appname, appname); + snprintf(configfiles[0][11], sizeof(**configfiles) - 1, "/usr/local/etc/%s/%s.conf", appname, appname); + snprintf(configfiles[1][0], sizeof(**configfiles) - 1, "%s", extraconfig); + if (getuid() != 0) { + homedir = getenv("HOME"); + if (homedir == NULL) { + pwinfo = getpwuid(getuid()); + if (pwinfo != NULL) { + homedir = pwinfo->pw_dir; + } + } + if (homedir != NULL) { + if (strcmp(homedir, "/") != 0 && access(homedir, R_OK|W_OK|X_OK) == 0) { + snprintf(configfiles[2][0], sizeof(**configfiles) - 1, "%s/.%src", homedir, appname); + snprintf(configfiles[2][1], sizeof(**configfiles) - 1, "%s/.%s.cfg", homedir, appname); + snprintf(configfiles[2][2], sizeof(**configfiles) - 1, "%s/.%s.conf", homedir, appname); + snprintf(configfiles[2][3], sizeof(**configfiles) - 1, "%s/.%s/%s.cfg", homedir, appname, appname); + snprintf(configfiles[2][4], sizeof(**configfiles) - 1, "%s/.%s/%s.conf", homedir, appname, appname); + snprintf(configfiles[2][5], sizeof(**configfiles) - 1, "%s/.%s/config", homedir, appname); + } + } + } + + for (configsetidx = 0; configsetidx < 3; configsetidx++) { + for (configidx = 0; configidx < 13; configidx++) { + configfile = configfiles[configsetidx][configidx]; + if (configfile[0] == '\0') { + break; + } + if (access(configfile, R_OK) == 0) { + chkretval = lc_process_file(appname, configfile, type); + if (chkretval < 0) { + retval = -1; + } + break; + } + } + } + + return(retval); +} + +int lc_process(int argc, char **argv, const char *appname, lc_conf_type_t type, const char *extra) { + int retval = 0, chkretval = 0; + + /* Handle config files. */ + chkretval = lc_process_files(appname, type, extra); if (chkretval < 0) { retval = -1; } /* Handle environment variables.*/ @@ -560,14 +652,17 @@ retval = "Can't open file."; break; case LC_ERR_CALLBACK: retval = "Error return from application handler."; break; + case LC_ERR_ENOMEM: + retval = "Insuffcient memory."; + break; } lc_errno = LC_ERR_NONE; if (retval != NULL) { return(retval); } return("Unknown error"); } Index: libconfig.h.in ================================================================== --- libconfig.h.in +++ libconfig.h.in @@ -53,10 +53,11 @@ LC_ERR_INVSECTION, LC_ERR_INVDATA, LC_ERR_BADFORMAT, LC_ERR_CANTOPEN, LC_ERR_CALLBACK, + LC_ERR_ENOMEM } lc_err_t; __BLANK_LINE__ int lc_process(int argc, char **argv, const char *appname, lc_conf_type_t type, const char *extra); Index: test-lc.c ================================================================== --- test-lc.c +++ test-lc.c @@ -6,11 +6,11 @@ printf("\n"); exit(EXIT_FAILURE); } int sally_cmd(const char *partarg, const char *arg, const char *argarg, const char *val, lc_flags_t flags, void *extra) { - PRINTERR_D("%s sets value: \"%s\" (flags=%i)", arg, val, flags); + PRINTERR("%s sets value: \"%s\" (flags=%i)", arg, val, flags); return(0); } int cmd_ifmodule(const char *partarg, const char *arg, const char *argarg, const char *val, lc_flags_t flags, void *extra) { if (flags == LC_FLAGS_SECTIONEND) { @@ -23,12 +23,11 @@ if (argarg == NULL) { PRINTERR("You must specify an argument to IfModule."); return(LC_CBRET_ERROR); } - SPOTVAR_S(arg); - SPOTVAR_S(argarg); + PRINTERR("IfModule (%s)", argarg); return(LC_CBRET_IGNORESECTION); } int main(int argc, char **argv) { char *joeval = NULL;