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   Mark file names in argv[]. File marker is *always* followed by a file name
48   All options after it come from that file.
49   Empty file name ("") means command line.
50 */
51 static char *file_marker= (char*)"----file-marker----";
52 my_bool my_defaults_mark_files= FALSE;
is_file_marker(const char * arg)53 my_bool is_file_marker(const char* arg)
54 {
55   return arg == file_marker;
56 }
57 
58 my_bool my_no_defaults=FALSE, my_print_defaults= FALSE;
59 const char *my_defaults_file=0;
60 const char *my_defaults_group_suffix=0;
61 const char *my_defaults_extra_file=0;
62 
63 /* Which directories are searched for options (and in which order) */
64 
65 #define MAX_DEFAULT_DIRS 7
66 #define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1)  /* Terminate with NULL */
67 static const char **default_directories = NULL;
68 
69 #ifdef __WIN__
70 static const char *f_extensions[]= { ".ini", ".cnf", 0 };
71 #define NEWLINE "\r\n"
72 #else
73 static const char *f_extensions[]= { ".cnf", 0 };
74 #define NEWLINE "\n"
75 #endif
76 
77 struct handle_option_ctx
78 {
79    MEM_ROOT *alloc;
80    DYNAMIC_ARRAY *args;
81    TYPELIB *group;
82 };
83 
84 static int search_default_file(struct handle_option_ctx *,
85 			       const char *, const char *);
86 static int search_default_file_with_ext(struct handle_option_ctx *,
87 					const char *, const char *,
88 					const char *, int);
89 
90 
91 /**
92   Create the list of default directories.
93 
94   @param alloc  MEM_ROOT where the list of directories is stored
95 
96   @details
97   The directories searched, in order, are:
98   - Windows:     GetSystemWindowsDirectory()
99   - Windows:     GetWindowsDirectory()
100   - Windows:     C:/
101   - Windows:     Directory above where the executable is located
102   - Unix:        /etc/ or the value of DEFAULT_SYSCONFDIR, if defined
103   - Unix:        /etc/mysql/ unless DEFAULT_SYSCONFDIR is defined
104   - ALL:         getenv("MYSQL_HOME")
105   - ALL:         --defaults-extra-file=<path> (run-time option)
106   - Unix:        ~/
107 
108   On all systems, if a directory is already in the list, it will be moved
109   to the end of the list.  This avoids reading defaults files multiple times,
110   while ensuring the correct precedence.
111 
112   @retval NULL  Failure (out of memory, probably)
113   @retval other Pointer to NULL-terminated array of default directories
114 */
115 
116 static const char **init_default_directories(MEM_ROOT *alloc);
117 
118 
119 static char *remove_end_comment(char *ptr);
120 
121 
122 /*
123   Process config files in default directories.
124 
125   SYNOPSIS
126   my_search_option_files()
127   conf_file                   Basename for configuration file to search for.
128                               If this is a path, then only this file is read.
129   argc                        Pointer to argc of original program
130   argv                        Pointer to argv of original program
131   func                        Pointer to the function to process options
132   func_ctx                    It's context. Usually it is the structure to
133                               store additional options.
134   DESCRIPTION
135     Process the default options from argc & argv
136     Read through each found config file looks and calls 'func' to process
137     each option.
138 
139   NOTES
140     --defaults-group-suffix is only processed if we are called from
141     load_defaults().
142 
143 
144   RETURN
145     0  ok
146     1  given cinf_file doesn't exist
147     2  out of memory
148     3  Can't get current working directory
149 
150     The global variable 'my_defaults_group_suffix' is updated with value for
151     --defaults_group_suffix
152 */
153 
my_search_option_files(const char * conf_file,struct handle_option_ctx * ctx,const char ** default_directories)154 static int my_search_option_files(const char *conf_file,
155                                   struct handle_option_ctx *ctx,
156                                   const char **default_directories)
157 {
158   const char **dirs;
159   int error= 0;
160   DBUG_ENTER("my_search_option_files");
161 
162   if (my_defaults_group_suffix)
163   {
164     /* Handle --defaults-group-suffix= */
165     uint i;
166     const char **extra_groups;
167     const size_t instance_len= strlen(my_defaults_group_suffix);
168     char *ptr;
169     TYPELIB *group= ctx->group;
170 
171     if (!(extra_groups=
172 	  (const char**)alloc_root(ctx->alloc,
173                                    (2*group->count+1)*sizeof(char*))))
174       DBUG_RETURN(2);
175 
176     for (i= 0; i < group->count; i++)
177     {
178       size_t len;
179       extra_groups[i]= group->type_names[i]; /** copy group */
180 
181       len= strlen(extra_groups[i]);
182       if (!(ptr= alloc_root(ctx->alloc, (uint) (len+instance_len+1))))
183        DBUG_RETURN(2);
184 
185       extra_groups[i+group->count]= ptr;
186 
187       /** Construct new group */
188       memcpy(ptr, extra_groups[i], len);
189       memcpy(ptr+len, my_defaults_group_suffix, instance_len+1);
190     }
191 
192     group->count*= 2;
193     group->type_names= extra_groups;
194     group->type_names[group->count]= 0;
195   }
196 
197   if (my_defaults_file)
198   {
199     if ((error= search_default_file_with_ext(ctx, "", "",
200                                              my_defaults_file, 0)) < 0)
201       goto err;
202     if (error > 0)
203     {
204       fprintf(stderr, "Could not open required defaults file: %s\n",
205               my_defaults_file);
206       goto err;
207     }
208   }
209   else if (dirname_length(conf_file))
210   {
211     if ((error= search_default_file(ctx, NullS, conf_file)) < 0)
212       goto err;
213   }
214   else
215   {
216     for (dirs= default_directories ; *dirs; dirs++)
217     {
218       if (**dirs)
219       {
220 	if (search_default_file(ctx, *dirs, conf_file) < 0)
221 	  goto err;
222       }
223       else if (my_defaults_extra_file)
224       {
225         if ((error= search_default_file_with_ext(ctx, "", "",
226                                                 my_defaults_extra_file, 0)) < 0)
227 	  goto err;				/* Fatal error */
228         if (error > 0)
229         {
230           fprintf(stderr, "Could not open required defaults file: %s\n",
231                   my_defaults_extra_file);
232           goto err;
233         }
234       }
235     }
236   }
237 
238   DBUG_RETURN(0);
239 
240 err:
241   fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
242   DBUG_RETURN(1);
243 }
244 
245 
246 /*
247   adds an option to the array of options
248 
249   SYNOPSIS
250     add_option()
251     in_ctx                  Handler context.
252     option                  The very option to be processed. It is already
253                             prepared to be used in argv (has -- prefix).
254 
255   RETURN
256     0 - ok
257     1 - error occurred
258 */
259 
add_option(struct handle_option_ctx * ctx,const char * option)260 static int add_option(struct handle_option_ctx *ctx, const char *option)
261 {
262   char *tmp= strdup_root(ctx->alloc, option);
263   return !tmp || insert_dynamic(ctx->args, (uchar*) &tmp);
264 }
265 
266 
267 /*
268   Gets options from the command line
269 
270   SYNOPSIS
271     get_defaults_options()
272     argv			Pointer to argv of original program
273 
274   DESCRIPTION
275     Sets my_no_defaults, my_defaults_file, my_defaults_extra_file,
276     my_defaults_group_suffix, my_print_defaults
277 
278   RETURN
279     # Number of arguments used from *argv
280 */
281 
get_defaults_options(char ** argv)282 int get_defaults_options(char **argv)
283 {
284   static char file_buffer[FN_REFLEN];
285   static char extra_file_buffer[FN_REFLEN];
286   char **orig_argv= argv;
287 
288   argv++; /* Skip program name */
289 
290   my_defaults_file= my_defaults_group_suffix= my_defaults_extra_file= 0;
291   my_no_defaults= my_print_defaults= FALSE;
292 
293   if (*argv && !strcmp(*argv, "--no-defaults"))
294   {
295     my_no_defaults= 1;
296     argv++;
297   }
298   else
299     for(; *argv; argv++)
300     {
301       if (!my_defaults_file && is_prefix(*argv, "--defaults-file="))
302         my_defaults_file= *argv + sizeof("--defaults-file=")-1;
303       else
304       if (!my_defaults_extra_file && is_prefix(*argv, "--defaults-extra-file="))
305         my_defaults_extra_file= *argv + sizeof("--defaults-extra-file=")-1;
306       else
307       if (!my_defaults_group_suffix && is_prefix(*argv, "--defaults-group-suffix="))
308         my_defaults_group_suffix= *argv + sizeof("--defaults-group-suffix=")-1;
309       else
310         break;
311     }
312 
313   if (*argv && !strcmp(*argv, "--print-defaults"))
314   {
315     my_print_defaults= 1;
316     my_defaults_mark_files= FALSE;
317     argv++;
318   }
319 
320   if (! my_defaults_group_suffix)
321     my_defaults_group_suffix= getenv("MYSQL_GROUP_SUFFIX");
322 
323   if (my_defaults_extra_file && my_defaults_extra_file != extra_file_buffer)
324   {
325     my_realpath(extra_file_buffer, my_defaults_extra_file, MYF(0));
326     my_defaults_extra_file= extra_file_buffer;
327   }
328 
329   if (my_defaults_file && my_defaults_file != file_buffer)
330   {
331     my_realpath(file_buffer, my_defaults_file, MYF(0));
332     my_defaults_file= file_buffer;
333   }
334 
335   return (int)(argv - orig_argv);
336 }
337 
338 /*
339   Wrapper around my_load_defaults() for interface compatibility.
340 
341   SYNOPSIS
342     load_defaults()
343     conf_file			Basename for configuration file to search for.
344     				If this is a path, then only this file is read.
345     groups			Which [group] entrys to read.
346 				Points to an null terminated array of pointers
347     argc			Pointer to argc of original program
348     argv			Pointer to argv of original program
349 
350   NOTES
351 
352     This function is NOT thread-safe as it uses a global pointer internally.
353     See also notes for my_load_defaults().
354 
355   RETURN
356     0 ok
357     1 The given conf_file didn't exists
358 */
load_defaults(const char * conf_file,const char ** groups,int * argc,char *** argv)359 int load_defaults(const char *conf_file, const char **groups,
360                   int *argc, char ***argv)
361 {
362   return my_load_defaults(conf_file, groups, argc, argv, &default_directories);
363 }
364 
365 /*
366   Read options from configurations files
367 
368   SYNOPSIS
369     my_load_defaults()
370     conf_file			Basename for configuration file to search for.
371     				If this is a path, then only this file is read.
372     groups			Which [group] entrys to read.
373 				Points to an null terminated array of pointers
374     argc			Pointer to argc of original program
375     argv			Pointer to argv of original program
376     default_directories         Pointer to a location where a pointer to the list
377                                 of default directories will be stored
378 
379   IMPLEMENTATION
380 
381    Read options from configuration files and put them BEFORE the arguments
382    that are already in argc and argv.  This way the calling program can
383    easily command line options override options in configuration files
384 
385    NOTES
386     In case of fatal error, the function will print a warning and returns 2
387 
388     To free used memory one should call free_defaults() with the argument
389     that was put in *argv
390 
391    RETURN
392      - If successful, 0 is returned. If 'default_directories' is not NULL,
393      a pointer to the array of default directory paths is stored to a location
394      it points to. That stored value must be passed to my_search_option_files()
395      later.
396 
397      - 1 is returned if the given conf_file didn't exist. In this case, the
398      value pointed to by default_directories is undefined.
399 */
400 
401 
my_load_defaults(const char * conf_file,const char ** groups,int * argc,char *** argv,const char *** default_directories)402 int my_load_defaults(const char *conf_file, const char **groups, int *argc,
403                      char ***argv, const char ***default_directories)
404 {
405   DYNAMIC_ARRAY args;
406   int args_used= 0;
407   int error= 0;
408   MEM_ROOT alloc;
409   char *ptr,**res;
410   const char **dirs;
411   DBUG_ENTER("my_load_defaults");
412 
413   init_alloc_root(key_memory_defaults, &alloc, 512, 0, MYF(0));
414   if ((dirs= init_default_directories(&alloc)) == NULL)
415     goto err;
416 
417   args_used= get_defaults_options(*argv);
418 
419   if (my_init_dynamic_array(key_memory_defaults, &args, sizeof(char*), 128, 64,
420                             MYF(0)))
421     goto err;
422 
423   insert_dynamic(&args, *argv);/* Name MUST be set, even by embedded library */
424 
425   *argc-= args_used;
426   *argv+= args_used;
427 
428   if (!my_no_defaults)
429   {
430     TYPELIB group; // XXX
431     struct handle_option_ctx ctx;
432 
433     group.count=0;
434     group.name= "defaults";
435     group.type_names= groups;
436 
437     for (; *groups ; groups++)
438       group.count++;
439 
440     ctx.alloc= &alloc;
441     ctx.args= &args;
442     ctx.group= &group;
443 
444     if ((error= my_search_option_files(conf_file, &ctx, dirs)))
445     {
446       delete_dynamic(&args);
447       free_root(&alloc,MYF(0));
448       DBUG_RETURN(error);
449     }
450   }
451 
452   if (!(ptr=(char*) alloc_root(&alloc, sizeof(alloc) +
453 			       (args.elements + *argc + 3) * sizeof(char*))))
454     goto err;
455   res= (char**) (ptr+sizeof(alloc));
456 
457   /* found arguments + command line arguments to new array */
458   memcpy(res, args.buffer, args.elements * sizeof(char*));
459 
460   if (my_defaults_mark_files)
461   {
462     res[args.elements++]= file_marker;
463     res[args.elements++]= (char*)"";
464   }
465 
466   if (*argc)
467     memcpy(res + args.elements, *argv, *argc * sizeof(char*));
468 
469   (*argc)+= args.elements;
470   *argv= res;
471   (*argv)[*argc]= 0;
472   *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
473   delete_dynamic(&args);
474   if (my_print_defaults)
475   {
476     int i;
477     printf("%s would have been started with the following arguments:\n",
478 	   **argv);
479     for (i=1 ; i < *argc ; i++)
480       printf("%s ", (*argv)[i]);
481     puts("");
482     DBUG_RETURN(4);
483   }
484 
485   if (default_directories)
486     *default_directories= dirs;
487 
488   DBUG_RETURN(0);
489 
490  err:
491   fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
492   DBUG_RETURN(2);
493 }
494 
495 
free_defaults(char ** argv)496 void free_defaults(char **argv)
497 {
498   MEM_ROOT ptr;
499   memcpy(&ptr, ((char *) argv) - sizeof(ptr), sizeof(ptr));
500   free_root(&ptr,MYF(0));
501 }
502 
503 
search_default_file(struct handle_option_ctx * ctx,const char * dir,const char * config_file)504 static int search_default_file(struct handle_option_ctx *ctx, const char *dir,
505 			       const char *config_file)
506 {
507   char **ext;
508   const char *empty_list[]= { "", 0 };
509   my_bool have_ext= fn_ext(config_file)[0] != 0;
510   const char **exts_to_use= have_ext ? empty_list : f_extensions;
511 
512   for (ext= (char**) exts_to_use; *ext; ext++)
513   {
514     int error;
515     if ((error= search_default_file_with_ext(ctx, dir, *ext, config_file, 0)) < 0)
516       return error;
517   }
518   return 0;
519 }
520 
521 
522 /*
523   Skip over keyword and get argument after keyword
524 
525   SYNOPSIS
526    get_argument()
527    keyword		Include directive keyword
528    kwlen		Length of keyword
529    ptr			Pointer to the keword in the line under process
530    line			line number
531 
532   RETURN
533    0	error
534    #	Returns pointer to the argument after the keyword.
535 */
536 
get_argument(const char * keyword,size_t kwlen,char * ptr,char * name,uint line)537 static char *get_argument(const char *keyword, size_t kwlen,
538                           char *ptr, char *name, uint line)
539 {
540   char *end;
541 
542   /* Skip over "include / includedir keyword" and following whitespace */
543 
544   for (ptr+= kwlen - 1;
545        my_isspace(&my_charset_latin1, ptr[0]);
546        ptr++)
547   {}
548 
549   /*
550     Trim trailing whitespace from directory name
551     The -1 below is for the newline added by fgets()
552     Note that my_isspace() is true for \r and \n
553   */
554   for (end= ptr + strlen(ptr) - 1;
555        my_isspace(&my_charset_latin1, *(end - 1));
556        end--)
557   {}
558   end[0]= 0;                                    /* Cut off end space */
559 
560   /* Print error msg if there is nothing after !include* directive */
561   if (end <= ptr)
562   {
563     fprintf(stderr,
564 	    "error: Wrong '!%s' directive in config file: %s at line %d\n",
565 	    keyword, name, line);
566     return 0;
567   }
568   return ptr;
569 }
570 
571 
572 /*
573   Open a configuration file (if exists) and read given options from it
574 
575   SYNOPSIS
576     search_default_file_with_ext()
577     ctx                         Pointer to the structure to store actual
578                                 parameters of the function.
579     dir				directory to read
580     ext				Extension for configuration file
581     config_file                 Name of configuration file
582     group			groups to read
583     recursion_level             the level of recursion, got while processing
584                                 "!include" or "!includedir"
585 
586   RETURN
587     0   Success
588     -1	Fatal error, abort
589      1	File not found (Warning)
590 */
591 
search_default_file_with_ext(struct handle_option_ctx * ctx,const char * dir,const char * ext,const char * config_file,int recursion_level)592 static int search_default_file_with_ext(struct handle_option_ctx *ctx,
593                                         const char *dir, const char *ext,
594                                         const char *config_file,
595                                         int recursion_level)
596 {
597   char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
598   char *value, option[4096+2], tmp[FN_REFLEN];
599   static const char includedir_keyword[]= "includedir";
600   static const char include_keyword[]= "include";
601   const int max_recursion_level= 10;
602   MYSQL_FILE *fp;
603   uint line=0;
604   enum { NONE, PARSE, SKIP } found_group= NONE;
605   uint i;
606   MY_DIR *search_dir;
607   FILEINFO *search_file;
608 
609   if (safe_strlen(dir) + strlen(config_file) >= FN_REFLEN-3)
610     return 0;					/* Ignore wrong paths */
611   if (dir)
612   {
613     end=convert_dirname(name, dir, NullS);
614     if (dir[0] == FN_HOMELIB)		/* Add . to filenames in home */
615       *end++='.';
616     strxmov(end,config_file,ext,NullS);
617   }
618   else
619   {
620     strmov(name,config_file);
621   }
622   fn_format(name,name,"","",4);
623 #if !defined(__WIN__)
624   {
625     MY_STAT stat_info;
626     if (!my_stat(name,&stat_info,MYF(0)))
627       return 1;
628     /*
629       Ignore world-writable regular files.
630       This is mainly done to protect us to not read a file created by
631       the mysqld server, but the check is still valid in most context.
632     */
633     if ((stat_info.st_mode & S_IWOTH) &&
634 	(stat_info.st_mode & S_IFMT) == S_IFREG)
635     {
636       fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n",
637               name);
638       return 0;
639     }
640   }
641 #endif
642   if (!(fp= mysql_file_fopen(key_file_cnf, name, O_RDONLY, MYF(0))))
643     return 1;					/* Ignore wrong files */
644 
645   if (my_defaults_mark_files)
646     if (insert_dynamic(ctx->args, (uchar*) &file_marker) ||
647         add_option(ctx, name))
648       goto err;
649 
650   while (mysql_file_fgets(buff, sizeof(buff) - 1, fp))
651   {
652     line++;
653     /* Ignore comment and empty lines */
654     for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
655     {}
656 
657     if (*ptr == '#' || *ptr == ';' || !*ptr)
658       continue;
659 
660     /* Configuration File Directives */
661     if (*ptr == '!')
662     {
663       if (recursion_level >= max_recursion_level)
664       {
665         for (end= ptr + strlen(ptr) - 1;
666              my_isspace(&my_charset_latin1, *(end - 1));
667              end--)
668         {}
669         end[0]= 0;
670         fprintf(stderr,
671                 "Warning: skipping '%s' directive as maximum include"
672                 "recursion level was reached in file %s at line %d\n",
673                 ptr, name, line);
674         continue;
675       }
676 
677       /* skip over `!' and following whitespace */
678       for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
679       {}
680 
681       if ((!strncmp(ptr, includedir_keyword,
682                     sizeof(includedir_keyword) - 1)) &&
683           my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
684       {
685 	if (!(ptr= get_argument(includedir_keyword,
686                                 sizeof(includedir_keyword),
687                                 ptr, name, line)))
688 	  goto err;
689 
690         if (!(search_dir= my_dir(ptr, MYF(MY_WME | MY_WANT_SORT))))
691           goto err;
692 
693         for (i= 0; i < (uint) search_dir->number_of_files; i++)
694         {
695           search_file= search_dir->dir_entry + i;
696           ext= fn_ext2(search_file->name);
697 
698           /* check extension */
699           for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++)
700           {
701             if (!strcmp(ext, *tmp_ext))
702               break;
703           }
704 
705           if (*tmp_ext)
706           {
707             fn_format(tmp, search_file->name, ptr, "",
708                       MY_UNPACK_FILENAME | MY_SAFE_PATH);
709 
710             search_default_file_with_ext(ctx, "", "", tmp, recursion_level + 1);
711           }
712         }
713 
714         my_dirend(search_dir);
715       }
716       else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
717                my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
718       {
719 	if (!(ptr= get_argument(include_keyword,
720                                 sizeof(include_keyword), ptr,
721                                 name, line)))
722 	  goto err;
723 
724         search_default_file_with_ext(ctx, "", "", ptr, recursion_level + 1);
725       }
726 
727       continue;
728     }
729 
730     if (*ptr == '[')				/* Group name */
731     {
732       if (!(end=(char *) strchr(++ptr,']')))
733       {
734 	fprintf(stderr,
735 		"error: Wrong group definition in config file: %s at line %d\n",
736 		name,line);
737 	goto err;
738       }
739       /* Remove end space */
740       for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
741       end[0]=0;
742 
743       strmake(curr_gr, ptr, MY_MIN((size_t) (end-ptr), sizeof(curr_gr)-1));
744       found_group= find_type(curr_gr, ctx->group, FIND_TYPE_NO_PREFIX)
745                    ? PARSE : SKIP;
746       continue;
747     }
748     switch (found_group)
749     {
750     case NONE:
751       fprintf(stderr,
752 	      "error: Found option without preceding group in config file: %s at line: %d\n",
753 	      name,line);
754       goto err;
755     case PARSE:
756       break;
757     case SKIP:
758       continue;
759     }
760 
761     end= remove_end_comment(ptr);
762     if ((value= strchr(ptr, '=')))
763       end= value;
764     for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
765     ptr= strmake(strmov(option,"--"), ptr, (size_t) (end-ptr));
766     if (value)
767     {
768       /* Remove pre- and end space */
769       char *value_end;
770       for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
771       value_end=strend(value);
772       /*
773 	We don't have to test for value_end >= value as we know there is
774 	an '=' before
775       */
776       for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
777       if (value_end < value)			/* Empty string */
778 	value_end=value;
779 
780       /* remove quotes around argument */
781       if ((*value == '\"' || *value == '\'') && /* First char is quote */
782           (value + 1 < value_end ) && /* String is longer than 1 */
783           *value == value_end[-1] ) /* First char is equal to last char */
784       {
785 	value++;
786 	value_end--;
787       }
788       *ptr++= '=';
789       for ( ; value != value_end; value++)
790       {
791 	if (*value == '\\' && value != value_end-1)
792 	{
793 	  switch(*++value) {
794 	  case 'n':
795 	    *ptr++='\n';
796 	    break;
797 	  case 't':
798 	    *ptr++= '\t';
799 	    break;
800 	  case 'r':
801 	    *ptr++ = '\r';
802 	    break;
803 	  case 'b':
804 	    *ptr++ = '\b';
805 	    break;
806 	  case 's':
807 	    *ptr++= ' ';			/* space */
808 	    break;
809 	  case '\"':
810 	    *ptr++= '\"';
811 	    break;
812 	  case '\'':
813 	    *ptr++= '\'';
814 	    break;
815 	  case '\\':
816 	    *ptr++= '\\';
817 	    break;
818 	  default:				/* Unknown; Keep '\' */
819 	    *ptr++= '\\';
820 	    *ptr++= *value;
821 	    break;
822 	  }
823 	}
824 	else
825 	  *ptr++= *value;
826       }
827       *ptr=0;
828     }
829 
830     if (add_option(ctx, option))
831       goto err;
832   }
833   mysql_file_fclose(fp, MYF(0));
834   return(0);
835 
836  err:
837   mysql_file_fclose(fp, MYF(0));
838   return -1;					/* Fatal error */
839 }
840 
841 
remove_end_comment(char * ptr)842 static char *remove_end_comment(char *ptr)
843 {
844   char quote= 0;	/* we are inside quote marks */
845   char escape= 0;	/* symbol is protected by escape chagacter */
846 
847   for (; *ptr; ptr++)
848   {
849     if ((*ptr == '\'' || *ptr == '\"') && !escape)
850     {
851       if (!quote)
852 	quote= *ptr;
853       else if (quote == *ptr)
854 	quote= 0;
855     }
856     /* We are not inside a string */
857     if (!quote && *ptr == '#')
858     {
859       *ptr= 0;
860       return ptr;
861     }
862     escape= (quote && *ptr == '\\' && !escape);
863   }
864   return ptr;
865 }
866 
867 
my_print_default_files(const char * conf_file)868 void my_print_default_files(const char *conf_file)
869 {
870   const char *empty_list[]= { "", 0 };
871   my_bool have_ext= fn_ext(conf_file)[0] != 0;
872   const char **exts_to_use= have_ext ? empty_list : f_extensions;
873   char name[FN_REFLEN], **ext;
874 
875   puts("\nDefault options are read from the following files in the given order:");
876   if (my_defaults_file)
877   {
878     puts(my_defaults_file);
879     return;
880   }
881 
882   if (dirname_length(conf_file))
883     fputs(conf_file,stdout);
884   else
885   {
886     const char **dirs;
887     MEM_ROOT alloc;
888     init_alloc_root(key_memory_defaults, &alloc, 512, 0, MYF(0));
889 
890     if ((dirs= init_default_directories(&alloc)) == NULL)
891     {
892       fputs("Internal error initializing default directories list", stdout);
893     }
894     else
895     {
896       for ( ; *dirs; dirs++)
897       {
898         for (ext= (char**) exts_to_use; *ext; ext++)
899         {
900           const char *pos;
901           char *end;
902           if (**dirs)
903             pos= *dirs;
904           else if (my_defaults_extra_file)
905           {
906             pos= my_defaults_extra_file;
907             fputs(pos, stdout);
908             fputs(" ", stdout);
909             continue;
910           }
911           else
912             continue;
913           end= convert_dirname(name, pos, NullS);
914           if (name[0] == FN_HOMELIB)	/* Add . to filenames in home */
915             *end++= '.';
916           strxmov(end, conf_file, *ext, " ", NullS);
917           fputs(name, stdout);
918         }
919       }
920     }
921 
922     free_root(&alloc, MYF(0));
923   }
924   puts("");
925 }
926 
print_defaults(const char * conf_file,const char ** groups)927 void print_defaults(const char *conf_file, const char **groups)
928 {
929   const char **groups_save= groups;
930   my_print_default_files(conf_file);
931 
932   fputs("The following groups are read:",stdout);
933   for ( ; *groups ; groups++)
934   {
935     fputc(' ',stdout);
936     fputs(*groups,stdout);
937   }
938 
939   if (my_defaults_group_suffix)
940   {
941     groups= groups_save;
942     for ( ; *groups ; groups++)
943     {
944       fputc(' ',stdout);
945       fputs(*groups,stdout);
946       fputs(my_defaults_group_suffix,stdout);
947     }
948   }
949   puts("\nThe following options may be given as the first argument:\n\
950 --print-defaults          Print the program argument list and exit.\n\
951 --no-defaults             Don't read default options from any option file.\n\
952 The following specify which files/extra groups are read (specified before remaining options):\n\
953 --defaults-file=#         Only read default options from the given file #.\n\
954 --defaults-extra-file=#   Read this file after the global files are read.\n\
955 --defaults-group-suffix=# Additionally read default groups with # appended as a suffix.");
956 }
957 
958 
add_directory(MEM_ROOT * alloc,const char * dir,const char ** dirs)959 static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs)
960 {
961   char buf[FN_REFLEN];
962   size_t len;
963   char *p;
964   my_bool err __attribute__((unused));
965 
966   len= normalize_dirname(buf, dir);
967   if (!(p= strmake_root(alloc, buf, len)))
968     return 1;  /* Failure */
969   /* Should never fail if DEFAULT_DIRS_SIZE is correct size */
970   err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE);
971   DBUG_ASSERT(err == FALSE);
972 
973   return 0;
974 }
975 
976 #ifdef __WIN__
my_get_module_parent(char * buf,size_t size)977 static const char *my_get_module_parent(char *buf, size_t size)
978 {
979   char *last= NULL;
980   char *end;
981   if (!GetModuleFileName(NULL, buf, (DWORD) size))
982     return NULL;
983   end= strend(buf);
984 
985   /*
986     Look for the second-to-last \ in the filename, but hang on
987     to a pointer after the last \ in case we're in the root of
988     a drive.
989   */
990   for ( ; end > buf; end--)
991   {
992     if (*end == FN_LIBCHAR)
993     {
994       if (last)
995       {
996         /* Keep the last '\' as this works both with D:\ and a directory */
997         end[1]= 0;
998         break;
999       }
1000       last= end;
1001     }
1002   }
1003 
1004   return buf;
1005 }
1006 #endif /* __WIN__ */
1007 
1008 
init_default_directories(MEM_ROOT * alloc)1009 static const char **init_default_directories(MEM_ROOT *alloc)
1010 {
1011   const char **dirs;
1012   char *env;
1013   int errors= 0;
1014   DBUG_ENTER("init_default_directories");
1015 
1016   dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *));
1017   if (dirs == NULL)
1018     DBUG_RETURN(NULL);
1019   bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *));
1020 
1021 #ifdef __WIN__
1022 
1023   {
1024     char fname_buffer[FN_REFLEN];
1025     if (GetSystemWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
1026       errors += add_directory(alloc, fname_buffer, dirs);
1027 
1028     if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
1029       errors += add_directory(alloc, fname_buffer, dirs);
1030 
1031     errors += add_directory(alloc, "C:/", dirs);
1032 
1033     if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL)
1034     {
1035       errors += add_directory(alloc, fname_buffer, dirs);
1036 
1037       strcat_s(fname_buffer, sizeof(fname_buffer), "/data");
1038       errors += add_directory(alloc, fname_buffer, dirs);
1039     }
1040   }
1041 
1042 #else
1043 
1044 #if defined(DEFAULT_SYSCONFDIR)
1045   if (DEFAULT_SYSCONFDIR[0])
1046     errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs);
1047 #else
1048   errors += add_directory(alloc, "/etc/", dirs);
1049   errors += add_directory(alloc, "/etc/mysql/", dirs);
1050 #endif /* DEFAULT_SYSCONFDIR */
1051 
1052 #endif
1053 
1054   if ((env= getenv("MYSQL_HOME")))
1055     errors += add_directory(alloc, env, dirs);
1056 
1057   /* Placeholder for --defaults-extra-file=<path> */
1058   errors += add_directory(alloc, "", dirs);
1059 
1060 #if !defined(__WIN__)
1061   errors += add_directory(alloc, "~/", dirs);
1062 #endif
1063 
1064   DBUG_RETURN(errors > 0 ? NULL : dirs);
1065 }
1066