1 /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
2    Copyright (c) 2011, 2018, MariaDB Corporation
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA */
16 
17 /****************************************************************************
18  Add all options from files named "group".cnf from the default_directories
19  before the command line arguments.
20  On Windows defaults will also search in the Windows directory for a file
21  called 'group'.ini
22  As long as the program uses the last argument for conflicting
23  options one only have to add a call to "load_defaults" to enable
24  use of default values.
25  pre- and end 'blank space' are removed from options and values. The
26  following escape sequences are recognized in values:  \b \t \n \r \\
27 
28  The following arguments are handled automatically;  If used, they must be
29  first argument on the command line!
30  --no-defaults	; no options are read.
31  --defaults-file=full-path-to-default-file	; Only this file will be read.
32  --defaults-extra-file=full-path-to-default-file ; Read this file before ~/
33  --defaults-group-suffix  ; Also read groups with concat(group, suffix)
34  --print-defaults	  ; Print the modified command line and exit
35 ****************************************************************************/
36 
37 #include "mysys_priv.h"
38 #include <my_default.h>
39 #include <m_string.h>
40 #include <m_ctype.h>
41 #include <my_dir.h>
42 #ifdef __WIN__
43 #include <winbase.h>
44 #endif
45 
46 /**
47    arguments separator
48 
49    load_defaults() loads arguments from config file and put them
50    before the arguments from command line, this separator is used to
51    separate the arguments loaded from config file and arguments user
52    provided on command line.
53 
54    Options with value loaded from config file are always in the form
55    '--option=value', while for command line options, the value can be
56    given as the next argument. Thus we used a separator so that
57    handle_options() can distinguish them.
58 
59    Note: any other places that does not need to distinguish them
60    should skip the separator.
61 
62    The content of arguments separator does not matter, one should only
63    check the pointer, use "----args-separator----" here to ease debug
64    if someone misused it.
65 
66    The args separator will only be added when
67    my_getopt_use_args_seprator is set to TRUE before calling
68    load_defaults();
69 
70    See BUG#25192
71 */
72 static const char *args_separator= "----args-separator----";
73 inline static void set_args_separator(char** arg)
74 {
75   DBUG_ASSERT(my_getopt_use_args_separator);
76   *arg= (char*)args_separator;
77 }
78 my_bool my_getopt_use_args_separator= FALSE;
79 my_bool my_getopt_is_args_separator(const char* arg)
80 {
81   return (arg == args_separator);
82 }
my_cpu_init(void)83 const char *my_defaults_file=0;
84 const char *my_defaults_group_suffix=0;
85 const char *my_defaults_extra_file=0;
86 
87 static char my_defaults_file_buffer[FN_REFLEN];
88 static char my_defaults_extra_file_buffer[FN_REFLEN];
89 
90 static my_bool defaults_already_read= FALSE;
91 
92 /* Which directories are searched for options (and in which order) */
93 
94 #define MAX_DEFAULT_DIRS 7
95 #define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1)  /* Terminate with NULL */
96 static const char **default_directories = NULL;
97 
98 #ifdef __WIN__
99 static const char *f_extensions[]= { ".ini", ".cnf", 0 };
100 #define NEWLINE "\r\n"
101 #else
102 static const char *f_extensions[]= { ".cnf", 0 };
103 #define NEWLINE "\n"
104 #endif
105 
106 static int handle_default_option(void *, const char *, const char *);
107 
108 /*
109    This structure defines the context that we pass to callback
110    function 'handle_default_option' used in search_default_file
111    to process each option. This context is used if search_default_file
112    was called from load_defaults.
113 */
114 
115 struct handle_option_ctx
116 {
117    MEM_ROOT *alloc;
118    DYNAMIC_ARRAY *args;
119    TYPELIB *group;
120 };
121 
122 static int search_default_file(Process_option_func func, void *func_ctx,
123 			       const char *dir, const char *config_file);
124 static int search_default_file_with_ext(Process_option_func func,
125                                         void *func_ctx,
126 					const char *dir, const char *ext,
127 					const char *config_file, int recursion_level);
128 
129 
130 /**
131   Create the list of default directories.
132 
133   @param alloc  MEM_ROOT where the list of directories is stored
134 
135   @details
136   The directories searched, in order, are:
137   - Windows:     GetSystemWindowsDirectory()
138   - Windows:     GetWindowsDirectory()
139   - Windows:     C:/
140   - Windows:     Directory above where the executable is located
141   - Unix:        /etc/ or the value of DEFAULT_SYSCONFDIR, if defined
142   - Unix:        /etc/mysql/ unless DEFAULT_SYSCONFDIR is defined
143   - ALL:         getenv("MYSQL_HOME")
144   - ALL:         --defaults-extra-file=<path> (run-time option)
145   - Unix:        ~/
146 
147   On all systems, if a directory is already in the list, it will be moved
148   to the end of the list.  This avoids reading defaults files multiple times,
149   while ensuring the correct precedence.
150 
151   @retval NULL  Failure (out of memory, probably)
152   @retval other Pointer to NULL-terminated array of default directories
153 */
154 
155 static const char **init_default_directories(MEM_ROOT *alloc);
156 
157 
158 static char *remove_end_comment(char *ptr);
159 
160 
161 /*
162   Expand a file name so that the current working directory is added if
163   the name is relative.
164 
165   RETURNS
166    0   All OK
167    2   Out of memory or path to long
168    3   Not able to get working directory
169  */
170 
171 static int
172 fn_expand(const char *filename, char *result_buf)
173 {
174   char dir[FN_REFLEN];
175   const int flags= MY_UNPACK_FILENAME | MY_SAFE_PATH | MY_RELATIVE_PATH;
176   DBUG_ENTER("fn_expand");
177   DBUG_PRINT("enter", ("filename: %s, result_buf: %p",
178                        filename, result_buf));
179   if (my_getwd(dir, sizeof(dir), MYF(0)))
180     DBUG_RETURN(3);
181   DBUG_PRINT("debug", ("dir: %s", dir));
182   if (fn_format(result_buf, filename, dir, "", flags) == NULL)
183     DBUG_RETURN(2);
184   DBUG_PRINT("return", ("result: %s", result_buf));
185   DBUG_RETURN(0);
186 }
187 
188 /*
189   Process config files in default directories.
190 
191   SYNOPSIS
192   my_search_option_files()
193   conf_file                   Basename for configuration file to search for.
194                               If this is a path, then only this file is read.
195   argc                        Pointer to argc of original program
196   argv                        Pointer to argv of original program
197   args_used                   Pointer to variable for storing the number of
198                               arguments used.
199   func                        Pointer to the function to process options
200   func_ctx                    It's context. Usually it is the structure to
201                               store additional options.
202   DESCRIPTION
203     Process the default options from argc & argv
204     Read through each found config file looks and calls 'func' to process
205     each option.
206 
207   NOTES
208     --defaults-group-suffix is only processed if we are called from
209     load_defaults().
210 
211 
212   RETURN
213     0  ok
214     1  given cinf_file doesn't exist
215     2  out of memory
216     3  Can't get current working directory
217 
218     The global variable 'my_defaults_group_suffix' is updated with value for
219     --defaults_group_suffix
220 */
221 
222 int my_search_option_files(const char *conf_file, int *argc, char ***argv,
223                            uint *args_used, Process_option_func func,
224                            void *func_ctx, const char **default_directories)
225 {
226   const char **dirs, *forced_default_file, *forced_extra_defaults;
227   int error= 0;
228   DBUG_ENTER("my_search_option_files");
229 
230   /* Check if we want to force the use a specific default file */
231   *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used,
232                                     (char **) &forced_default_file,
233                                     (char **) &forced_extra_defaults,
234                                     (char **) &my_defaults_group_suffix);
235 
236   if (! my_defaults_group_suffix)
237     my_defaults_group_suffix= getenv("MYSQL_GROUP_SUFFIX");
238 
239   if (forced_extra_defaults && !defaults_already_read)
240   {
241     int error= fn_expand(forced_extra_defaults, my_defaults_extra_file_buffer);
242     if (error)
243       DBUG_RETURN(error);
244     my_defaults_extra_file= my_defaults_extra_file_buffer;
245   }
246 
247   if (forced_default_file && !defaults_already_read)
248   {
249     int error= fn_expand(forced_default_file, my_defaults_file_buffer);
250     if (error)
251       DBUG_RETURN(error);
252     my_defaults_file= my_defaults_file_buffer;
253   }
254 
255   defaults_already_read= TRUE;
256 
257   /*
258     We can only handle 'defaults-group-suffix' if we are called from
259     load_defaults() as otherwise we can't know the type of 'func_ctx'
260   */
261 
262   if (my_defaults_group_suffix && func == handle_default_option)
263   {
264     /* Handle --defaults-group-suffix= */
265     uint i;
266     const char **extra_groups;
267     const size_t instance_len= strlen(my_defaults_group_suffix);
268     struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx;
269     char *ptr;
270     TYPELIB *group= ctx->group;
271 
272     if (!(extra_groups=
273 	  (const char**)alloc_root(ctx->alloc,
274                                    (2*group->count+1)*sizeof(char*))))
275       DBUG_RETURN(2);
276 
277     for (i= 0; i < group->count; i++)
278     {
279       size_t len;
280       extra_groups[i]= group->type_names[i]; /** copy group */
281 
282       len= strlen(extra_groups[i]);
283       if (!(ptr= alloc_root(ctx->alloc, (uint) (len+instance_len+1))))
284        DBUG_RETURN(2);
285 
286       extra_groups[i+group->count]= ptr;
287 
288       /** Construct new group */
289       memcpy(ptr, extra_groups[i], len);
290       memcpy(ptr+len, my_defaults_group_suffix, instance_len+1);
291     }
292 
293     group->count*= 2;
294     group->type_names= extra_groups;
295     group->type_names[group->count]= 0;
296   }
297 
298   if (my_defaults_file)
299   {
300     if ((error= search_default_file_with_ext(func, func_ctx, "", "",
301                                              my_defaults_file, 0)) < 0)
302       goto err;
303     if (error > 0)
304     {
305       fprintf(stderr, "Could not open required defaults file: %s\n",
306               my_defaults_file);
307       goto err;
308     }
309   }
310   else if (dirname_length(conf_file))
311   {
312     if ((error= search_default_file(func, func_ctx, NullS, conf_file)) < 0)
313       goto err;
314   }
315   else
316   {
317     for (dirs= default_directories ; *dirs; dirs++)
318     {
319       if (**dirs)
320       {
321 	if (search_default_file(func, func_ctx, *dirs, conf_file) < 0)
322 	  goto err;
323       }
324       else if (my_defaults_extra_file)
325       {
326         if ((error= search_default_file_with_ext(func, func_ctx, "", "",
327                                                 my_defaults_extra_file, 0)) < 0)
328 	  goto err;				/* Fatal error */
329         if (error > 0)
330         {
331           fprintf(stderr, "Could not open required defaults file: %s\n",
332                   my_defaults_extra_file);
333           goto err;
334         }
335       }
336     }
337   }
338 
339   DBUG_RETURN(0);
340 
341 err:
342   fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
343   DBUG_RETURN(1);
344 }
345 
346 
347 /*
348   The option handler for load_defaults.
349 
350   SYNOPSIS
351     handle_deault_option()
352     in_ctx                  Handler context. In this case it is a
353                             handle_option_ctx structure.
354     group_name              The name of the group the option belongs to.
355     option                  The very option to be processed. It is already
356                             prepared to be used in argv (has -- prefix). If it
357                             is NULL, we are handling a new group (section).
358 
359   DESCRIPTION
360     This handler checks whether a group is one of the listed and adds an option
361     to the array if yes. Some other handler can record, for instance, all
362     groups and their options, not knowing in advance the names and amount of
363     groups.
364 
365   RETURN
366     0 - ok
367     1 - error occurred
368 */
369 
370 static int handle_default_option(void *in_ctx, const char *group_name,
371                                  const char *option)
372 {
373   char *tmp;
374   struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;
375 
376   if (!option)
377     return 0;
378 
379   if (find_type((char *)group_name, ctx->group, FIND_TYPE_NO_PREFIX))
380   {
381     if (!(tmp= alloc_root(ctx->alloc, strlen(option) + 1)))
382       return 1;
383     if (insert_dynamic(ctx->args, (uchar*) &tmp))
384       return 1;
385     strmov(tmp, option);
386   }
387 
388   return 0;
389 }
390 
391 
392 /*
393   Gets options from the command line
394 
395   SYNOPSIS
396     get_defaults_options()
397     argc			Pointer to argc of original program
398     argv			Pointer to argv of original program
399     defaults                    --defaults-file option
400     extra_defaults              --defaults-extra-file option
401 
402   RETURN
403     # Number of arguments used from *argv
404       defaults and extra_defaults will be set to option of the appropriate
405       items of argv array, or to NULL if there are no such options
406 */
407 
408 int get_defaults_options(int argc, char **argv,
409                          char **defaults,
410                          char **extra_defaults,
411                          char **group_suffix)
412 {
413   int org_argc= argc;
414   *defaults= *extra_defaults= *group_suffix= 0;
415 
416   while (argc >= 2)
417   {
418     /* Skip program name or previously handled argument */
419     argv++;
420     if (!*defaults && is_prefix(*argv,"--defaults-file="))
421     {
422       *defaults= *argv + sizeof("--defaults-file=")-1;
423        argc--;
424        continue;
425     }
426     if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file="))
427     {
428       *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1;
429       argc--;
430       continue;
431     }
432     if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix="))
433     {
434       *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1;
435       argc--;
436       continue;
437     }
438     break;
439   }
440   return org_argc - argc;
441 }
442 
443 /*
444   Wrapper around my_load_defaults() for interface compatibility.
445 
446   SYNOPSIS
447     load_defaults()
448     conf_file			Basename for configuration file to search for.
449     				If this is a path, then only this file is read.
450     groups			Which [group] entrys to read.
451 				Points to an null terminated array of pointers
452     argc			Pointer to argc of original program
453     argv			Pointer to argv of original program
454 
455   NOTES
456 
457     This function is NOT thread-safe as it uses a global pointer internally.
458     See also notes for my_load_defaults().
459 
460   RETURN
461     0 ok
462     1 The given conf_file didn't exists
463 */
464 int load_defaults(const char *conf_file, const char **groups,
465                   int *argc, char ***argv)
466 {
467   return my_load_defaults(conf_file, groups, argc, argv, &default_directories);
468 }
469 
470 /*
471   Read options from configurations files
472 
473   SYNOPSIS
474     my_load_defaults()
475     conf_file			Basename for configuration file to search for.
476     				If this is a path, then only this file is read.
477     groups			Which [group] entrys to read.
478 				Points to an null terminated array of pointers
479     argc			Pointer to argc of original program
480     argv			Pointer to argv of original program
481     default_directories         Pointer to a location where a pointer to the list
482                                 of default directories will be stored
483 
484   IMPLEMENTATION
485 
486    Read options from configuration files and put them BEFORE the arguments
487    that are already in argc and argv.  This way the calling program can
488    easily command line options override options in configuration files
489 
490    NOTES
491     In case of fatal error, the function will print a warning and returns 2
492 
493     To free used memory one should call free_defaults() with the argument
494     that was put in *argv
495 
496    RETURN
497      - If successful, 0 is returned. If 'default_directories' is not NULL,
498      a pointer to the array of default directory paths is stored to a location
499      it points to. That stored value must be passed to my_search_option_files()
500      later.
501 
502      - 1 is returned if the given conf_file didn't exist. In this case, the
503      value pointed to by default_directories is undefined.
504 */
505 
506 
507 int my_load_defaults(const char *conf_file, const char **groups,
508                   int *argc, char ***argv, const char ***default_directories)
509 {
510   DYNAMIC_ARRAY args;
511   TYPELIB group;
512   my_bool found_print_defaults= 0;
513   uint args_used= 0;
514   int error= 0;
515   MEM_ROOT alloc;
516   char *ptr,**res;
517   struct handle_option_ctx ctx;
518   const char **dirs;
519   uint args_sep= my_getopt_use_args_separator ? 1 : 0;
520   DBUG_ENTER("load_defaults");
521 
522   init_alloc_root(&alloc, "my_load_defaults", 512, 0, MYF(0));
523   if ((dirs= init_default_directories(&alloc)) == NULL)
524     goto err;
525   /*
526     Check if the user doesn't want any default option processing
527     --no-defaults is always the first option
528   */
529   if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults"))
530   {
531     /* remove the --no-defaults argument and return only the other arguments */
532     uint i, j;
533     if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+
534 				 (*argc + 1)*sizeof(char*))))
535       goto err;
536     res= (char**) (ptr+sizeof(alloc));
537     res[0]= **argv;				/* Copy program name */
538     j= 1;                 /* Start from 1 for the reset result args */
539     if (my_getopt_use_args_separator)
540     {
541       /* set arguments separator */
542       set_args_separator(&res[1]);
543       j++;
544     }
545     for (i=2 ; i < (uint) *argc ; i++, j++)
546       res[j]=argv[0][i];
547     res[j]=0;					/* End pointer */
548     /*
549       Update the argc, if have not added args separator, then we have
550       to decrease argc because we have removed the "--no-defaults".
551     */
552     if (!my_getopt_use_args_separator)
553       (*argc)--;
554     *argv=res;
555     *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
556     if (default_directories)
557       *default_directories= dirs;
558     DBUG_RETURN(0);
559   }
560 
561   group.count=0;
562   group.name= "defaults";
563   group.type_names= groups;
564 
565   for (; *groups ; groups++)
566     group.count++;
567 
568   if (my_init_dynamic_array(&args, sizeof(char*), 128, 64, MYF(0)))
569     goto err;
570 
571   ctx.alloc= &alloc;
572   ctx.args= &args;
573   ctx.group= &group;
574 
575   if ((error= my_search_option_files(conf_file, argc, argv, &args_used,
576                                      handle_default_option, (void *) &ctx,
577                                      dirs)))
578   {
579     delete_dynamic(&args);
580     free_root(&alloc,MYF(0));
581     DBUG_RETURN(error);
582   }
583   /*
584     Here error contains <> 0 only if we have a fully specified conf_file
585     or a forced default file
586   */
587   if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+
588 			       (args.elements + *argc + 1 + args_sep) *sizeof(char*))))
589     goto err;
590   res= (char**) (ptr+sizeof(alloc));
591 
592   /* copy name + found arguments + command line arguments to new array */
593   res[0]= argv[0][0];  /* Name MUST be set, even by embedded library */
594   memcpy((uchar*) (res+1), args.buffer, args.elements*sizeof(char*));
595   /* Skip --defaults-xxx options */
596   (*argc)-= args_used;
597   (*argv)+= args_used;
598 
599   /*
600     Check if we want to see the new argument list
601     This options must always be the last of the default options
602   */
603   if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults"))
604   {
605     found_print_defaults=1;
606     --*argc; ++*argv;				/* skip argument */
607   }
608 
609   if (my_getopt_use_args_separator)
610   {
611     /* set arguments separator for arguments from config file and
612        command line */
613     set_args_separator(&res[args.elements+1]);
614   }
615 
616   if (*argc)
617     memcpy((uchar*) (res+1+args.elements+args_sep), (char*) ((*argv)+1),
618 	   (*argc-1)*sizeof(char*));
619   res[args.elements+ *argc+args_sep]=0;                /* last null */
620 
621   (*argc)+=args.elements+args_sep;
622   *argv= (char**) res;
623   *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
624   delete_dynamic(&args);
625   if (found_print_defaults)
626   {
627     int i;
628     printf("%s would have been started with the following arguments:\n",
629 	   **argv);
630     for (i=1 ; i < *argc ; i++)
631       if (!my_getopt_is_args_separator((*argv)[i])) /* skip arguments separator */
632         printf("%s ", (*argv)[i]);
633     puts("");
634     DBUG_RETURN(4);
635   }
636 
637   if (default_directories)
638     *default_directories= dirs;
639 
640   DBUG_RETURN(0);
641 
642  err:
643   fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
644   DBUG_RETURN(2);
645 }
646 
647 
648 void free_defaults(char **argv)
649 {
650   MEM_ROOT ptr;
651   memcpy(&ptr, ((char *) argv) - sizeof(ptr), sizeof(ptr));
652   free_root(&ptr,MYF(0));
653 }
654 
655 
656 static int search_default_file(Process_option_func opt_handler,
657                                void *handler_ctx,
658 			       const char *dir,
659 			       const char *config_file)
660 {
661   char **ext;
662   const char *empty_list[]= { "", 0 };
663   my_bool have_ext= fn_ext(config_file)[0] != 0;
664   const char **exts_to_use= have_ext ? empty_list : f_extensions;
665 
666   for (ext= (char**) exts_to_use; *ext; ext++)
667   {
668     int error;
669     if ((error= search_default_file_with_ext(opt_handler, handler_ctx,
670                                              dir, *ext,
671 					     config_file, 0)) < 0)
672       return error;
673   }
674   return 0;
675 }
676 
677 
678 /*
679   Skip over keyword and get argument after keyword
680 
681   SYNOPSIS
682    get_argument()
683    keyword		Include directive keyword
684    kwlen		Length of keyword
685    ptr			Pointer to the keword in the line under process
686    line			line number
687 
688   RETURN
689    0	error
690    #	Returns pointer to the argument after the keyword.
691 */
692 
693 static char *get_argument(const char *keyword, size_t kwlen,
694                           char *ptr, char *name, uint line)
695 {
696   char *end;
697 
698   /* Skip over "include / includedir keyword" and following whitespace */
699 
700   for (ptr+= kwlen - 1;
701        my_isspace(&my_charset_latin1, ptr[0]);
702        ptr++)
703   {}
704 
705   /*
706     Trim trailing whitespace from directory name
707     The -1 below is for the newline added by fgets()
708     Note that my_isspace() is true for \r and \n
709   */
710   for (end= ptr + strlen(ptr) - 1;
711        my_isspace(&my_charset_latin1, *(end - 1));
712        end--)
713   {}
714   end[0]= 0;                                    /* Cut off end space */
715 
716   /* Print error msg if there is nothing after !include* directive */
717   if (end <= ptr)
718   {
719     fprintf(stderr,
720 	    "error: Wrong '!%s' directive in config file: %s at line %d\n",
721 	    keyword, name, line);
722     return 0;
723   }
724   return ptr;
725 }
726 
727 
728 /*
729   Open a configuration file (if exists) and read given options from it
730 
731   SYNOPSIS
732     search_default_file_with_ext()
733     opt_handler                 Option handler function. It is used to process
734                                 every separate option.
735     handler_ctx                 Pointer to the structure to store actual
736                                 parameters of the function.
737     dir				directory to read
738     ext				Extension for configuration file
739     config_file                 Name of configuration file
740     group			groups to read
741     recursion_level             the level of recursion, got while processing
742                                 "!include" or "!includedir"
743 
744   RETURN
745     0   Success
746     -1	Fatal error, abort
747      1	File not found (Warning)
748 */
749 
750 static int search_default_file_with_ext(Process_option_func opt_handler,
751                                         void *handler_ctx,
752                                         const char *dir,
753                                         const char *ext,
754                                         const char *config_file,
755                                         int recursion_level)
756 {
757   char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
758   char *value, option[4096+2], tmp[FN_REFLEN];
759   static const char includedir_keyword[]= "includedir";
760   static const char include_keyword[]= "include";
761   const int max_recursion_level= 10;
762   MYSQL_FILE *fp;
763   uint line=0;
764   my_bool found_group=0;
765   uint i;
766   MY_DIR *search_dir;
767   FILEINFO *search_file;
768 
769   if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3)
770     return 0;					/* Ignore wrong paths */
771   if (dir)
772   {
773     end=convert_dirname(name, dir, NullS);
774     if (dir[0] == FN_HOMELIB)		/* Add . to filenames in home */
775       *end++='.';
776     strxmov(end,config_file,ext,NullS);
777   }
778   else
779   {
780     strmov(name,config_file);
781   }
782   fn_format(name,name,"","",4);
783 #if !defined(__WIN__)
784   {
785     MY_STAT stat_info;
786     if (!my_stat(name,&stat_info,MYF(0)))
787       return 1;
788     /*
789       Ignore world-writable regular files.
790       This is mainly done to protect us to not read a file created by
791       the mysqld server, but the check is still valid in most context.
792     */
793     if ((stat_info.st_mode & S_IWOTH) &&
794 	(stat_info.st_mode & S_IFMT) == S_IFREG)
795     {
796       fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n",
797               name);
798       return 0;
799     }
800   }
801 #endif
802   if (!(fp= mysql_file_fopen(key_file_cnf, name, O_RDONLY, MYF(0))))
803     return 1;					/* Ignore wrong files */
804 
805   while (mysql_file_fgets(buff, sizeof(buff) - 1, fp))
806   {
807     line++;
808     /* Ignore comment and empty lines */
809     for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
810     {}
811 
812     if (*ptr == '#' || *ptr == ';' || !*ptr)
813       continue;
814 
815     /* Configuration File Directives */
816     if (*ptr == '!')
817     {
818       if (recursion_level >= max_recursion_level)
819       {
820         for (end= ptr + strlen(ptr) - 1;
821              my_isspace(&my_charset_latin1, *(end - 1));
822              end--)
823         {}
824         end[0]= 0;
825         fprintf(stderr,
826                 "Warning: skipping '%s' directive as maximum include"
827                 "recursion level was reached in file %s at line %d\n",
828                 ptr, name, line);
829         continue;
830       }
831 
832       /* skip over `!' and following whitespace */
833       for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
834       {}
835 
836       if ((!strncmp(ptr, includedir_keyword,
837                     sizeof(includedir_keyword) - 1)) &&
838           my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
839       {
840 	if (!(ptr= get_argument(includedir_keyword,
841                                 sizeof(includedir_keyword),
842                                 ptr, name, line)))
843 	  goto err;
844 
845         if (!(search_dir= my_dir(ptr, MYF(MY_WME | MY_WANT_SORT))))
846           goto err;
847 
848         for (i= 0; i < (uint) search_dir->number_of_files; i++)
849         {
850           search_file= search_dir->dir_entry + i;
851           ext= fn_ext2(search_file->name);
852 
853           /* check extension */
854           for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++)
855           {
856             if (!strcmp(ext, *tmp_ext))
857               break;
858           }
859 
860           if (*tmp_ext)
861           {
862             fn_format(tmp, search_file->name, ptr, "",
863                       MY_UNPACK_FILENAME | MY_SAFE_PATH);
864 
865             search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp,
866                                          recursion_level + 1);
867           }
868         }
869 
870         my_dirend(search_dir);
871       }
872       else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
873                my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
874       {
875 	if (!(ptr= get_argument(include_keyword,
876                                 sizeof(include_keyword), ptr,
877                                 name, line)))
878 	  goto err;
879 
880         search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
881                                      recursion_level + 1);
882       }
883 
884       continue;
885     }
886 
887     if (*ptr == '[')				/* Group name */
888     {
889       found_group=1;
890       if (!(end=(char *) strchr(++ptr,']')))
891       {
892 	fprintf(stderr,
893 		"error: Wrong group definition in config file: %s at line %d\n",
894 		name,line);
895 	goto err;
896       }
897       /* Remove end space */
898       for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
899       end[0]=0;
900 
901       strmake(curr_gr, ptr, MY_MIN((size_t) (end-ptr)+1, sizeof(curr_gr)-1));
902 
903       /* signal that a new group is found */
904       opt_handler(handler_ctx, curr_gr, NULL);
905 
906       continue;
907     }
908     if (!found_group)
909     {
910       fprintf(stderr,
911 	      "error: Found option without preceding group in config file: %s at line: %d\n",
912 	      name,line);
913       goto err;
914     }
915 
916 
917     end= remove_end_comment(ptr);
918     if ((value= strchr(ptr, '=')))
919       end= value;
920     for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
921     if (!value)
922     {
923       strmake(strmov(option,"--"),ptr, (size_t) (end-ptr));
924       if (opt_handler(handler_ctx, curr_gr, option))
925         goto err;
926     }
927     else
928     {
929       /* Remove pre- and end space */
930       char *value_end;
931       for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
932       value_end=strend(value);
933       /*
934 	We don't have to test for value_end >= value as we know there is
935 	an '=' before
936       */
937       for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
938       if (value_end < value)			/* Empty string */
939 	value_end=value;
940 
941       /* remove quotes around argument */
942       if ((*value == '\"' || *value == '\'') && /* First char is quote */
943           (value + 1 < value_end ) && /* String is longer than 1 */
944           *value == value_end[-1] ) /* First char is equal to last char */
945       {
946 	value++;
947 	value_end--;
948       }
949       ptr=strnmov(strmov(option,"--"),ptr,(size_t) (end-ptr));
950       *ptr++= '=';
951 
952       for ( ; value != value_end; value++)
953       {
954 	if (*value == '\\' && value != value_end-1)
955 	{
956 	  switch(*++value) {
957 	  case 'n':
958 	    *ptr++='\n';
959 	    break;
960 	  case 't':
961 	    *ptr++= '\t';
962 	    break;
963 	  case 'r':
964 	    *ptr++ = '\r';
965 	    break;
966 	  case 'b':
967 	    *ptr++ = '\b';
968 	    break;
969 	  case 's':
970 	    *ptr++= ' ';			/* space */
971 	    break;
972 	  case '\"':
973 	    *ptr++= '\"';
974 	    break;
975 	  case '\'':
976 	    *ptr++= '\'';
977 	    break;
978 	  case '\\':
979 	    *ptr++= '\\';
980 	    break;
981 	  default:				/* Unknown; Keep '\' */
982 	    *ptr++= '\\';
983 	    *ptr++= *value;
984 	    break;
985 	  }
986 	}
987 	else
988 	  *ptr++= *value;
989       }
990       *ptr=0;
991       if (opt_handler(handler_ctx, curr_gr, option))
992         goto err;
993     }
994   }
995   mysql_file_fclose(fp, MYF(0));
996   return(0);
997 
998  err:
999   mysql_file_fclose(fp, MYF(0));
1000   return -1;					/* Fatal error */
1001 }
1002 
1003 
1004 static char *remove_end_comment(char *ptr)
1005 {
1006   char quote= 0;	/* we are inside quote marks */
1007   char escape= 0;	/* symbol is protected by escape chagacter */
1008 
1009   for (; *ptr; ptr++)
1010   {
1011     if ((*ptr == '\'' || *ptr == '\"') && !escape)
1012     {
1013       if (!quote)
1014 	quote= *ptr;
1015       else if (quote == *ptr)
1016 	quote= 0;
1017     }
1018     /* We are not inside a string */
1019     if (!quote && *ptr == '#')
1020     {
1021       *ptr= 0;
1022       return ptr;
1023     }
1024     escape= (quote && *ptr == '\\' && !escape);
1025   }
1026   return ptr;
1027 }
1028 
1029 
1030 void my_print_default_files(const char *conf_file)
1031 {
1032   const char *empty_list[]= { "", 0 };
1033   my_bool have_ext= fn_ext(conf_file)[0] != 0;
1034   const char **exts_to_use= have_ext ? empty_list : f_extensions;
1035   char name[FN_REFLEN], **ext;
1036 
1037   puts("\nDefault options are read from the following files in the given order:");
1038   if (my_defaults_file)
1039   {
1040     puts(my_defaults_file);
1041     return;
1042   }
1043 
1044   if (dirname_length(conf_file))
1045     fputs(conf_file,stdout);
1046   else
1047   {
1048     const char **dirs;
1049     MEM_ROOT alloc;
1050     init_alloc_root(&alloc, "my_print_defaults", 512, 0, MYF(0));
1051 
1052     if ((dirs= init_default_directories(&alloc)) == NULL)
1053     {
1054       fputs("Internal error initializing default directories list", stdout);
1055     }
1056     else
1057     {
1058       for ( ; *dirs; dirs++)
1059       {
1060         for (ext= (char**) exts_to_use; *ext; ext++)
1061         {
1062           const char *pos;
1063           char *end;
1064           if (**dirs)
1065             pos= *dirs;
1066           else if (my_defaults_extra_file)
1067           {
1068             pos= my_defaults_extra_file;
1069             fputs(pos, stdout);
1070             fputs(" ", stdout);
1071             continue;
1072           }
1073           else
1074             continue;
1075           end= convert_dirname(name, pos, NullS);
1076           if (name[0] == FN_HOMELIB)	/* Add . to filenames in home */
1077             *end++= '.';
1078           strxmov(end, conf_file, *ext, " ", NullS);
1079           fputs(name, stdout);
1080         }
1081       }
1082     }
1083 
1084     free_root(&alloc, MYF(0));
1085   }
1086   puts("");
1087 }
1088 
1089 void print_defaults(const char *conf_file, const char **groups)
1090 {
1091   const char **groups_save= groups;
1092   my_print_default_files(conf_file);
1093 
1094   fputs("The following groups are read:",stdout);
1095   for ( ; *groups ; groups++)
1096   {
1097     fputc(' ',stdout);
1098     fputs(*groups,stdout);
1099   }
1100 
1101   if (my_defaults_group_suffix)
1102   {
1103     groups= groups_save;
1104     for ( ; *groups ; groups++)
1105     {
1106       fputc(' ',stdout);
1107       fputs(*groups,stdout);
1108       fputs(my_defaults_group_suffix,stdout);
1109     }
1110   }
1111   puts("\nThe following options may be given as the first argument:\n\
1112 --print-defaults          Print the program argument list and exit.\n\
1113 --no-defaults             Don't read default options from any option file.\n\
1114 The following specify which files/extra groups are read (specified before remaining options):\n\
1115 --defaults-file=#         Only read default options from the given file #.\n\
1116 --defaults-extra-file=#   Read this file after the global files are read.\n\
1117 --defaults-group-suffix=# Additionally read default groups with # appended as a suffix.");
1118 }
1119 
1120 
1121 static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs)
1122 {
1123   char buf[FN_REFLEN];
1124   size_t len;
1125   char *p;
1126   my_bool err __attribute__((unused));
1127 
1128   len= normalize_dirname(buf, dir);
1129   if (!(p= strmake_root(alloc, buf, len)))
1130     return 1;  /* Failure */
1131   /* Should never fail if DEFAULT_DIRS_SIZE is correct size */
1132   err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE);
1133   DBUG_ASSERT(err == FALSE);
1134 
1135   return 0;
1136 }
1137 
1138 #ifdef __WIN__
1139 static const char *my_get_module_parent(char *buf, size_t size)
1140 {
1141   char *last= NULL;
1142   char *end;
1143   if (!GetModuleFileName(NULL, buf, (DWORD) size))
1144     return NULL;
1145   end= strend(buf);
1146 
1147   /*
1148     Look for the second-to-last \ in the filename, but hang on
1149     to a pointer after the last \ in case we're in the root of
1150     a drive.
1151   */
1152   for ( ; end > buf; end--)
1153   {
1154     if (*end == FN_LIBCHAR)
1155     {
1156       if (last)
1157       {
1158         /* Keep the last '\' as this works both with D:\ and a directory */
1159         end[1]= 0;
1160         break;
1161       }
1162       last= end;
1163     }
1164   }
1165 
1166   return buf;
1167 }
1168 #endif /* __WIN__ */
1169 
1170 
1171 static const char **init_default_directories(MEM_ROOT *alloc)
1172 {
1173   const char **dirs;
1174   char *env;
1175   int errors= 0;
1176   DBUG_ENTER("init_default_directories");
1177 
1178   dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *));
1179   if (dirs == NULL)
1180     DBUG_RETURN(NULL);
1181   bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *));
1182 
1183 #ifdef __WIN__
1184 
1185   {
1186     char fname_buffer[FN_REFLEN];
1187     if (GetSystemWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
1188       errors += add_directory(alloc, fname_buffer, dirs);
1189 
1190     if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
1191       errors += add_directory(alloc, fname_buffer, dirs);
1192 
1193     errors += add_directory(alloc, "C:/", dirs);
1194 
1195     if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL)
1196     {
1197       errors += add_directory(alloc, fname_buffer, dirs);
1198 
1199       strcat_s(fname_buffer, sizeof(fname_buffer), "/data");
1200       errors += add_directory(alloc, fname_buffer, dirs);
1201     }
1202   }
1203 
1204 #else
1205 
1206 #if defined(DEFAULT_SYSCONFDIR)
1207   if (DEFAULT_SYSCONFDIR[0])
1208     errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs);
1209 #else
1210   errors += add_directory(alloc, "/etc/", dirs);
1211   errors += add_directory(alloc, "/etc/mysql/", dirs);
1212 #endif /* DEFAULT_SYSCONFDIR */
1213 
1214 #endif
1215 
1216   if ((env= getenv("MYSQL_HOME")))
1217     errors += add_directory(alloc, env, dirs);
1218 
1219   /* Placeholder for --defaults-extra-file=<path> */
1220   errors += add_directory(alloc, "", dirs);
1221 
1222 #if !defined(__WIN__)
1223   errors += add_directory(alloc, "~/", dirs);
1224 #endif
1225 
1226   DBUG_RETURN(errors > 0 ? NULL : dirs);
1227 }
1228