diff options
Diffstat (limited to 'mysys/my_default.c')
-rw-r--r-- | mysys/my_default.c | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/mysys/my_default.c b/mysys/my_default.c new file mode 100644 index 00000000..65a876a9 --- /dev/null +++ b/mysys/my_default.c @@ -0,0 +1,1089 @@ +/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2011, 2018, MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/**************************************************************************** + Add all options from files named "group".cnf from the default_directories + before the command line arguments. + On Windows defaults will also search in the Windows directory for a file + called 'group'.ini + As long as the program uses the last argument for conflicting + options one only have to add a call to "load_defaults" to enable + use of default values. + pre- and end 'blank space' are removed from options and values. The + following escape sequences are recognized in values: \b \t \n \r \\ + + The following arguments are handled automatically; If used, they must be + first argument on the command line! + --no-defaults ; no options are read. + --defaults-file=full-path-to-default-file ; Only this file will be read. + --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ + --defaults-group-suffix ; Also read groups with concat(group, suffix) + --print-defaults ; Print the modified command line and exit +****************************************************************************/ + +#include "mysys_priv.h" +#include <my_default.h> +#include <m_string.h> +#include <m_ctype.h> +#include <my_dir.h> +#ifdef _WIN32 +#include <winbase.h> +#endif + +/* + Mark file names in argv[]. File marker is *always* followed by a file name + All options after it come from that file. + Empty file name ("") means command line. +*/ +static char *file_marker= (char*)"----file-marker----"; +my_bool my_defaults_mark_files= FALSE; +my_bool is_file_marker(const char* arg) +{ + return arg == file_marker; +} + +my_bool my_no_defaults=FALSE, my_print_defaults= FALSE; +const char *my_defaults_file=0; +const char *my_defaults_group_suffix=0; +const char *my_defaults_extra_file=0; + +/* Which directories are searched for options (and in which order) */ + +#define MAX_DEFAULT_DIRS 7 +#define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1) /* Terminate with NULL */ +static const char **default_directories = NULL; + +#ifdef _WIN32 +static const char *f_extensions[]= { ".ini", ".cnf", 0 }; +#define NEWLINE "\r\n" +#else +static const char *f_extensions[]= { ".cnf", 0 }; +#define NEWLINE "\n" +#endif + +struct handle_option_ctx +{ + MEM_ROOT *alloc; + DYNAMIC_ARRAY *args; + TYPELIB *group; +}; + +static int search_default_file(struct handle_option_ctx *, + const char *, const char *); +static int search_default_file_with_ext(struct handle_option_ctx *, + const char *, const char *, + const char *, int); + + +/** + Create the list of default directories. + + @param alloc MEM_ROOT where the list of directories is stored + + @details + The directories searched, in order, are: + - Windows: GetSystemWindowsDirectory() + - Windows: GetWindowsDirectory() + - Windows: C:/ + - Windows: Directory above where the executable is located + - Unix: /etc/ or the value of DEFAULT_SYSCONFDIR, if defined + - Unix: /etc/mysql/ unless DEFAULT_SYSCONFDIR is defined + - ALL: getenv("MYSQL_HOME") + - ALL: --defaults-extra-file=<path> (run-time option) + - Unix: ~/ + + On all systems, if a directory is already in the list, it will be moved + to the end of the list. This avoids reading defaults files multiple times, + while ensuring the correct precedence. + + @retval NULL Failure (out of memory, probably) + @retval other Pointer to NULL-terminated array of default directories +*/ + +static const char **init_default_directories(MEM_ROOT *alloc); + + +static char *remove_end_comment(char *ptr); + + +/* + Process config files in default directories. + + SYNOPSIS + my_search_option_files() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + argc Pointer to argc of original program + argv Pointer to argv of original program + func Pointer to the function to process options + func_ctx It's context. Usually it is the structure to + store additional options. + DESCRIPTION + Process the default options from argc & argv + Read through each found config file looks and calls 'func' to process + each option. + + NOTES + --defaults-group-suffix is only processed if we are called from + load_defaults(). + + + RETURN + 0 ok + 1 given cinf_file doesn't exist + 2 out of memory + 3 Can't get current working directory + + The global variable 'my_defaults_group_suffix' is updated with value for + --defaults_group_suffix +*/ + +static int my_search_option_files(const char *conf_file, + struct handle_option_ctx *ctx, + const char **default_directories) +{ + const char **dirs; + int error= 0; + DBUG_ENTER("my_search_option_files"); + + if (my_defaults_group_suffix) + { + /* Handle --defaults-group-suffix= */ + uint i; + const char **extra_groups; + const size_t instance_len= strlen(my_defaults_group_suffix); + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= + (const char**)alloc_root(ctx->alloc, + (2*group->count+1)*sizeof(char*)))) + DBUG_RETURN(2); + + for (i= 0; i < group->count; i++) + { + size_t len; + extra_groups[i]= group->type_names[i]; /** copy group */ + + len= strlen(extra_groups[i]); + if (!(ptr= alloc_root(ctx->alloc, (uint) (len+instance_len+1)))) + DBUG_RETURN(2); + + extra_groups[i+group->count]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, my_defaults_group_suffix, instance_len+1); + } + + group->count*= 2; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + + if (my_defaults_file) + { + if ((error= search_default_file_with_ext(ctx, "", "", + my_defaults_file, 0)) < 0) + goto err; + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + my_defaults_file); + goto err; + } + } + else if (dirname_length(conf_file)) + { + if ((error= search_default_file(ctx, NullS, conf_file)) < 0) + goto err; + } + else + { + for (dirs= default_directories ; *dirs; dirs++) + { + if (**dirs) + { + if (search_default_file(ctx, *dirs, conf_file) < 0) + goto err; + } + else if (my_defaults_extra_file) + { + if ((error= search_default_file_with_ext(ctx, "", "", + my_defaults_extra_file, 0)) < 0) + goto err; /* Fatal error */ + if (error > 0) + { + fprintf(stderr, "Could not open required defaults file: %s\n", + my_defaults_extra_file); + goto err; + } + } + } + } + + DBUG_RETURN(0); + +err: + fprintf(stderr,"Fatal error in defaults handling. Program aborted\n"); + DBUG_RETURN(1); +} + + +/* + adds an option to the array of options + + SYNOPSIS + add_option() + in_ctx Handler context. + option The very option to be processed. It is already + prepared to be used in argv (has -- prefix). + + RETURN + 0 - ok + 1 - error occurred +*/ + +static int add_option(struct handle_option_ctx *ctx, const char *option) +{ + char *tmp= strdup_root(ctx->alloc, option); + return !tmp || insert_dynamic(ctx->args, (uchar*) &tmp); +} + + +/* + Gets options from the command line + + SYNOPSIS + get_defaults_options() + argv Pointer to argv of original program + + DESCRIPTION + Sets my_no_defaults, my_defaults_file, my_defaults_extra_file, + my_defaults_group_suffix, my_print_defaults + + RETURN + # Number of arguments used from *argv +*/ + +int get_defaults_options(char **argv) +{ + static char file_buffer[FN_REFLEN]; + static char extra_file_buffer[FN_REFLEN]; + char **orig_argv= argv; + + argv++; /* Skip program name */ + + my_defaults_file= my_defaults_group_suffix= my_defaults_extra_file= 0; + my_no_defaults= my_print_defaults= FALSE; + + if (*argv && !strcmp(*argv, "--no-defaults")) + { + my_no_defaults= 1; + argv++; + } + else + for(; *argv; argv++) + { + if (!my_defaults_file && is_prefix(*argv, "--defaults-file=")) + my_defaults_file= *argv + sizeof("--defaults-file=")-1; + else + if (!my_defaults_extra_file && is_prefix(*argv, "--defaults-extra-file=")) + my_defaults_extra_file= *argv + sizeof("--defaults-extra-file=")-1; + else + if (!my_defaults_group_suffix && is_prefix(*argv, "--defaults-group-suffix=")) + my_defaults_group_suffix= *argv + sizeof("--defaults-group-suffix=")-1; + else + break; + } + + if (*argv && !strcmp(*argv, "--print-defaults")) + { + my_print_defaults= 1; + my_defaults_mark_files= FALSE; + argv++; + } + + if (! my_defaults_group_suffix) + my_defaults_group_suffix= getenv("MYSQL_GROUP_SUFFIX"); + + if (my_defaults_extra_file && my_defaults_extra_file != extra_file_buffer) + { + my_realpath(extra_file_buffer, my_defaults_extra_file, MYF(0)); + my_defaults_extra_file= extra_file_buffer; + } + + if (my_defaults_file && my_defaults_file != file_buffer) + { + my_realpath(file_buffer, my_defaults_file, MYF(0)); + my_defaults_file= file_buffer; + } + + return (int)(argv - orig_argv); +} + +/* + Wrapper around my_load_defaults() for interface compatibility. + + SYNOPSIS + load_defaults() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + groups Which [group] entrys to read. + Points to an null terminated array of pointers + argc Pointer to argc of original program + argv Pointer to argv of original program + + NOTES + + This function is NOT thread-safe as it uses a global pointer internally. + See also notes for my_load_defaults(). + + RETURN + 0 ok + 1 The given conf_file didn't exists +*/ +int load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv) +{ + return my_load_defaults(conf_file, groups, argc, argv, &default_directories); +} + +/* + Read options from configurations files + + SYNOPSIS + my_load_defaults() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + groups Which [group] entrys to read. + Points to an null terminated array of pointers + argc Pointer to argc of original program + argv Pointer to argv of original program + default_directories Pointer to a location where a pointer to the list + of default directories will be stored + + IMPLEMENTATION + + Read options from configuration files and put them BEFORE the arguments + that are already in argc and argv. This way the calling program can + easily command line options override options in configuration files + + NOTES + In case of fatal error, the function will print a warning and returns 2 + + To free used memory one should call free_defaults() with the argument + that was put in *argv + + RETURN + - If successful, 0 is returned. If 'default_directories' is not NULL, + a pointer to the array of default directory paths is stored to a location + it points to. That stored value must be passed to my_search_option_files() + later. + + - 1 is returned if the given conf_file didn't exist. In this case, the + value pointed to by default_directories is undefined. +*/ + + +int my_load_defaults(const char *conf_file, const char **groups, int *argc, + char ***argv, const char ***default_directories) +{ + DYNAMIC_ARRAY args; + int args_used= 0; + int error= 0; + MEM_ROOT alloc; + char *ptr,**res; + const char **dirs; + DBUG_ENTER("my_load_defaults"); + + init_alloc_root(key_memory_defaults, &alloc, 512, 0, MYF(0)); + if ((dirs= init_default_directories(&alloc)) == NULL) + goto err; + + args_used= get_defaults_options(*argv); + + if (my_init_dynamic_array(key_memory_defaults, &args, sizeof(char*), 128, 64, + MYF(0))) + goto err; + + insert_dynamic(&args, *argv);/* Name MUST be set, even by embedded library */ + + *argc-= args_used; + *argv+= args_used; + + if (!my_no_defaults) + { + TYPELIB group; // XXX + struct handle_option_ctx ctx; + + group.count=0; + group.name= "defaults"; + group.type_names= groups; + + for (; *groups ; groups++) + group.count++; + + ctx.alloc= &alloc; + ctx.args= &args; + ctx.group= &group; + + if ((error= my_search_option_files(conf_file, &ctx, dirs))) + { + delete_dynamic(&args); + free_root(&alloc,MYF(0)); + DBUG_RETURN(error); + } + } + + if (!(ptr=(char*) alloc_root(&alloc, sizeof(alloc) + + (args.elements + *argc + 3) * sizeof(char*)))) + goto err; + res= (char**) (ptr+sizeof(alloc)); + + /* found arguments + command line arguments to new array */ + memcpy(res, args.buffer, args.elements * sizeof(char*)); + + if (my_defaults_mark_files) + { + res[args.elements++]= file_marker; + res[args.elements++]= (char*)""; + } + + if (*argc) + memcpy(res + args.elements, *argv, *argc * sizeof(char*)); + + (*argc)+= (int)args.elements; + *argv= res; + (*argv)[*argc]= 0; + *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ + delete_dynamic(&args); + if (my_print_defaults) + { + int i; + printf("%s would have been started with the following arguments:\n", + **argv); + for (i=1 ; i < *argc ; i++) + printf("%s ", (*argv)[i]); + puts(""); + DBUG_RETURN(4); + } + + if (default_directories) + *default_directories= dirs; + + DBUG_RETURN(0); + + err: + fprintf(stderr,"Fatal error in defaults handling. Program aborted\n"); + DBUG_RETURN(2); +} + + +void free_defaults(char **argv) +{ + MEM_ROOT ptr; + memcpy(&ptr, ((char *) argv) - sizeof(ptr), sizeof(ptr)); + free_root(&ptr,MYF(0)); +} + + +static int search_default_file(struct handle_option_ctx *ctx, const char *dir, + const char *config_file) +{ + char **ext; + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(config_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; + + for (ext= (char**) exts_to_use; *ext; ext++) + { + int error; + if ((error= search_default_file_with_ext(ctx, dir, *ext, config_file, 0)) < 0) + return error; + } + return 0; +} + + +/* + Skip over keyword and get argument after keyword + + SYNOPSIS + get_argument() + keyword Include directive keyword + kwlen Length of keyword + ptr Pointer to the keword in the line under process + line line number + + RETURN + 0 error + # Returns pointer to the argument after the keyword. +*/ + +static char *get_argument(const char *keyword, size_t kwlen, + char *ptr, char *name, uint line) +{ + char *end; + + /* Skip over "include / includedir keyword" and following whitespace */ + + for (ptr+= kwlen - 1; + my_isspace(&my_charset_latin1, ptr[0]); + ptr++) + {} + + /* + Trim trailing whitespace from directory name + The -1 below is for the newline added by fgets() + Note that my_isspace() is true for \r and \n + */ + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; /* Cut off end space */ + + /* Print error msg if there is nothing after !include* directive */ + if (end <= ptr) + { + fprintf(stderr, + "error: Wrong '!%s' directive in config file: %s at line %d\n", + keyword, name, line); + return 0; + } + return ptr; +} + + +/* + Open a configuration file (if exists) and read given options from it + + SYNOPSIS + search_default_file_with_ext() + ctx Pointer to the structure to store actual + parameters of the function. + dir directory to read + ext Extension for configuration file + config_file Name of configuration file + group groups to read + recursion_level the level of recursion, got while processing + "!include" or "!includedir" + + RETURN + 0 Success + -1 Fatal error, abort + 1 File not found (Warning) +*/ + +static int search_default_file_with_ext(struct handle_option_ctx *ctx, + const char *dir, const char *ext, + const char *config_file, + int recursion_level) +{ + char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext; + char *value, option[4096+2], tmp[FN_REFLEN]; + static const char includedir_keyword[]= "includedir"; + static const char include_keyword[]= "include"; + const int max_recursion_level= 10; + MYSQL_FILE *fp; + uint line=0; + enum { NONE, PARSE, SKIP } found_group= NONE; + size_t i; + MY_DIR *search_dir; + FILEINFO *search_file; + + if (safe_strlen(dir) + strlen(config_file) >= FN_REFLEN-3) + return 0; /* Ignore wrong paths */ + if (dir) + { + end=convert_dirname(name, dir, NullS); + if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ + *end++='.'; + strxmov(end,config_file,ext,NullS); + } + else + { + strmov(name,config_file); + } + fn_format(name,name,"","",4); +#if !defined(_WIN32) + { + MY_STAT stat_info; + if (!my_stat(name,&stat_info,MYF(0))) + return 1; + /* + Ignore world-writable regular files (exceptions apply). + This is mainly done to protect us to not read a file that may be + modified by anyone. + + Also check access so that read only mounted (EROFS) + or immutable files (EPERM) that are suitable protections. + + The main case we are allowing is a container readonly volume mount + from a filesystem that doesn't have unix permissions. This will + have a 0777 permission and access will set errno = EROFS. + + Note if a ROFS has a file with permissions 04n6, access sets errno + EACCESS, rather the ROFS, so in this case we'll error, even though + the ROFS is protecting the file. + + An ideal, race free, implementation would do fstat / fstatvfs / ioctl + for permission, read only filesystem, and immutability resprectively. + */ + if ((stat_info.st_mode & S_IWOTH) && + (stat_info.st_mode & S_IFMT) == S_IFREG && + (access(name, W_OK) == 0 || (errno != EROFS && errno != EPERM))) + { + fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n", + name); + return 0; + } + } +#endif + if (!(fp= mysql_file_fopen(key_file_cnf, name, O_RDONLY, MYF(0)))) + return 1; /* Ignore wrong files */ + + if (my_defaults_mark_files) + if (insert_dynamic(ctx->args, (uchar*) &file_marker) || + add_option(ctx, name)) + goto err; + + while (mysql_file_fgets(buff, sizeof(buff) - 1, fp)) + { + line++; + /* Ignore comment and empty lines */ + for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++) + {} + + if (*ptr == '#' || *ptr == ';' || !*ptr) + continue; + + /* Configuration File Directives */ + if (*ptr == '!') + { + if (recursion_level >= max_recursion_level) + { + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; + fprintf(stderr, + "Warning: skipping '%s' directive as maximum include" + "recursion level was reached in file %s at line %d\n", + ptr, name, line); + continue; + } + + /* skip over `!' and following whitespace */ + for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++) + {} + + if ((!strncmp(ptr, includedir_keyword, + sizeof(includedir_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) + { + if (!(ptr= get_argument(includedir_keyword, + sizeof(includedir_keyword), + ptr, name, line))) + goto err; + + if (!(search_dir= my_dir(ptr, MYF(MY_WME | MY_WANT_SORT)))) + goto err; + + for (i= 0; i < search_dir->number_of_files; i++) + { + search_file= search_dir->dir_entry + i; + ext= fn_ext2(search_file->name); + + /* check extension */ + for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++) + { + if (!strcmp(ext, *tmp_ext)) + break; + } + + if (*tmp_ext) + { + fn_format(tmp, search_file->name, ptr, "", + MY_UNPACK_FILENAME | MY_SAFE_PATH); + + search_default_file_with_ext(ctx, "", "", tmp, recursion_level + 1); + } + } + + my_dirend(search_dir); + } + else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) + { + if (!(ptr= get_argument(include_keyword, + sizeof(include_keyword), ptr, + name, line))) + goto err; + + search_default_file_with_ext(ctx, "", "", ptr, recursion_level + 1); + } + + continue; + } + + if (*ptr == '[') /* Group name */ + { + if (!(end=(char *) strchr(++ptr,']'))) + { + fprintf(stderr, + "error: Wrong group definition in config file: %s at line %d\n", + name,line); + goto err; + } + /* Remove end space */ + for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; + end[0]=0; + + strmake(curr_gr, ptr, MY_MIN((size_t) (end-ptr), sizeof(curr_gr)-1)); + found_group= find_type(curr_gr, ctx->group, FIND_TYPE_NO_PREFIX) + ? PARSE : SKIP; + continue; + } + switch (found_group) + { + case NONE: + fprintf(stderr, + "error: Found option without preceding group in config file: %s at line: %d\n", + name,line); + goto err; + case PARSE: + break; + case SKIP: + continue; + } + + end= remove_end_comment(ptr); + if ((value= strchr(ptr, '='))) + end= value; + for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ; + ptr= strmake(strmov(option,"--"), ptr, (size_t) (end-ptr)); + if (value) + { + /* Remove pre- and end space */ + char *value_end; + for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ; + value_end=strend(value); + /* + We don't have to test for value_end >= value as we know there is + an '=' before + */ + for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ; + if (value_end < value) /* Empty string */ + value_end=value; + + /* remove quotes around argument */ + if ((*value == '\"' || *value == '\'') && /* First char is quote */ + (value + 1 < value_end ) && /* String is longer than 1 */ + *value == value_end[-1] ) /* First char is equal to last char */ + { + value++; + value_end--; + } + *ptr++= '='; + for ( ; value != value_end; value++) + { + if (*value == '\\' && value != value_end-1) + { + switch(*++value) { + case 'n': + *ptr++='\n'; + break; + case 't': + *ptr++= '\t'; + break; + case 'r': + *ptr++ = '\r'; + break; + case 'b': + *ptr++ = '\b'; + break; + case 's': + *ptr++= ' '; /* space */ + break; + case '\"': + *ptr++= '\"'; + break; + case '\'': + *ptr++= '\''; + break; + case '\\': + *ptr++= '\\'; + break; + default: /* Unknown; Keep '\' */ + *ptr++= '\\'; + *ptr++= *value; + break; + } + } + else + *ptr++= *value; + } + *ptr=0; + } + + if (add_option(ctx, option)) + goto err; + } + mysql_file_fclose(fp, MYF(0)); + return(0); + + err: + mysql_file_fclose(fp, MYF(0)); + return -1; /* Fatal error */ +} + + +static char *remove_end_comment(char *ptr) +{ + char quote= 0; /* we are inside quote marks */ + char escape= 0; /* symbol is protected by escape chagacter */ + + for (; *ptr; ptr++) + { + if ((*ptr == '\'' || *ptr == '\"') && !escape) + { + if (!quote) + quote= *ptr; + else if (quote == *ptr) + quote= 0; + } + /* We are not inside a string */ + if (!quote && *ptr == '#') + { + *ptr= 0; + return ptr; + } + escape= (quote && *ptr == '\\' && !escape); + } + return ptr; +} + + +void my_print_default_files(const char *conf_file) +{ + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(conf_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; + char name[FN_REFLEN], **ext; + + puts("\nDefault options are read from the following files in the given order:"); + if (my_defaults_file) + { + puts(my_defaults_file); + return; + } + + if (dirname_length(conf_file)) + fputs(conf_file,stdout); + else + { + const char **dirs; + MEM_ROOT alloc; + init_alloc_root(key_memory_defaults, &alloc, 512, 0, MYF(0)); + + if ((dirs= init_default_directories(&alloc)) == NULL) + { + fputs("Internal error initializing default directories list", stdout); + } + else + { + for ( ; *dirs; dirs++) + { + for (ext= (char**) exts_to_use; *ext; ext++) + { + const char *pos; + char *end; + if (**dirs) + pos= *dirs; + else if (my_defaults_extra_file) + { + pos= my_defaults_extra_file; + fputs(pos, stdout); + fputs(" ", stdout); + continue; + } + else + continue; + end= convert_dirname(name, pos, NullS); + if (name[0] == FN_HOMELIB) /* Add . to filenames in home */ + *end++= '.'; + strxmov(end, conf_file, *ext, " ", NullS); + fputs(name, stdout); + } + } + } + + free_root(&alloc, MYF(0)); + } + puts(""); +} + +void print_defaults(const char *conf_file, const char **groups) +{ + const char **groups_save= groups; + my_print_default_files(conf_file); + + fputs("The following groups are read:",stdout); + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + } + + if (my_defaults_group_suffix) + { + groups= groups_save; + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + fputs(my_defaults_group_suffix,stdout); + } + } + puts("\nThe following options may be given as the first argument:\n\ +--print-defaults Print the program argument list and exit.\n\ +--no-defaults Don't read default options from any option file.\n\ +The following specify which files/extra groups are read (specified before remaining options):\n\ +--defaults-file=# Only read default options from the given file #.\n\ +--defaults-extra-file=# Read this file after the global files are read.\n\ +--defaults-group-suffix=# Additionally read default groups with # appended as a suffix."); +} + + +static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs) +{ + char buf[FN_REFLEN]; + size_t len; + char *p; + my_bool err __attribute__((unused)); + + len= normalize_dirname(buf, dir); + if (!(p= strmake_root(alloc, buf, len))) + return 1; /* Failure */ + /* Should never fail if DEFAULT_DIRS_SIZE is correct size */ + err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE); + DBUG_ASSERT(err == FALSE); + + return 0; +} + +#ifdef _WIN32 +static const char *my_get_module_parent(char *buf, size_t size) +{ + char *last= NULL; + char *end; + if (!GetModuleFileName(NULL, buf, (DWORD) size)) + return NULL; + end= strend(buf); + + /* + Look for the second-to-last \ in the filename, but hang on + to a pointer after the last \ in case we're in the root of + a drive. + */ + for ( ; end > buf; end--) + { + if (*end == FN_LIBCHAR) + { + if (last) + { + /* Keep the last '\' as this works both with D:\ and a directory */ + end[1]= 0; + break; + } + last= end; + } + } + + return buf; +} +#endif /* _WIN32 */ + + +static const char **init_default_directories(MEM_ROOT *alloc) +{ + const char **dirs; + char *env; + int errors= 0; + DBUG_ENTER("init_default_directories"); + + dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *)); + if (dirs == NULL) + DBUG_RETURN(NULL); + bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *)); + +#ifdef _WIN32 + + { + char fname_buffer[FN_REFLEN]; + if (GetSystemWindowsDirectory(fname_buffer, sizeof(fname_buffer))) + errors += add_directory(alloc, fname_buffer, dirs); + + if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer))) + errors += add_directory(alloc, fname_buffer, dirs); + + errors += add_directory(alloc, "C:/", dirs); + + if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL) + { + errors += add_directory(alloc, fname_buffer, dirs); + + strcat_s(fname_buffer, sizeof(fname_buffer), "/data"); + errors += add_directory(alloc, fname_buffer, dirs); + } + } + +#else + +#if defined(DEFAULT_SYSCONFDIR) + if (DEFAULT_SYSCONFDIR[0]) + errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs); +#else + errors += add_directory(alloc, "/etc/", dirs); + errors += add_directory(alloc, "/etc/mysql/", dirs); +#endif /* DEFAULT_SYSCONFDIR */ + +#endif + + /* + If value of $MARIADB_HOME environment variable name is NULL, check + for $MYSQL_HOME + */ + if ((env= getenv("MARIADB_HOME"))) + errors += add_directory(alloc, env, dirs); + else + { + if ((env= getenv("MYSQL_HOME"))) + errors += add_directory(alloc, env, dirs); + } + + /* Placeholder for --defaults-extra-file=<path> */ + errors += add_directory(alloc, "", dirs); + +#if !defined(_WIN32) + errors += add_directory(alloc, "~/", dirs); +#endif + + DBUG_RETURN(errors > 0 ? NULL : dirs); +} |