default.c 30.8 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2 3 4

   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
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6 7

   This program is distributed in the hope that it will be useful,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
9 10 11 12 13 14
   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
15 16

/****************************************************************************
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 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 automaticly;  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 ~/
monty@mishka.local's avatar
monty@mishka.local committed
32 33
 --defaults-group-suffix  ; Also read groups with concat(group, suffix)
 --print-defaults	  ; Print the modified command line and exit
bk@work.mysql.com's avatar
bk@work.mysql.com committed
34 35 36 37 38
****************************************************************************/

#include "mysys_priv.h"
#include "m_string.h"
#include "m_ctype.h"
39
#include <my_dir.h>
monty@mysql.com's avatar
monty@mysql.com committed
40 41 42
#ifdef __WIN__
#include <winbase.h>
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
43

44 45 46
const char *my_defaults_file=0;
const char *my_defaults_group_suffix=0;
char *my_defaults_extra_file=0;
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
47

bk@work.mysql.com's avatar
bk@work.mysql.com committed
48 49
/* Which directories are searched for options (and in which order) */

50
#define MAX_DEFAULT_DIRS 6
51 52
#define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1)  /* Terminate with NULL */
static const char **default_directories = NULL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
53 54

#ifdef __WIN__
55
static const char *f_extensions[]= { ".ini", ".cnf", 0 };
petr@mysql.com's avatar
petr@mysql.com committed
56
#define NEWLINE "\r\n"
57 58
#else
static const char *f_extensions[]= { ".cnf", 0 };
petr@mysql.com's avatar
petr@mysql.com committed
59
#define NEWLINE "\n"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
60 61
#endif

monty@mishka.local's avatar
monty@mishka.local committed
62 63 64
static int handle_default_option(void *in_ctx, const char *group_name,
                                 const char *option);

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
/*
   This structure defines the context that we pass to callback
   function 'handle_default_option' used in search_default_file
   to process each option. This context is used if search_default_file
   was called from load_defaults.
*/

struct handle_option_ctx
{
   MEM_ROOT *alloc;
   DYNAMIC_ARRAY *args;
   TYPELIB *group;
};

static int search_default_file(Process_option_func func, void *func_ctx,
monty@mysql.com's avatar
monty@mysql.com committed
80
			       const char *dir, const char *config_file);
81 82
static int search_default_file_with_ext(Process_option_func func,
                                        void *func_ctx,
83
					const char *dir, const char *ext,
petr@mysql.com's avatar
petr@mysql.com committed
84
					const char *config_file, int recursion_level);
85 86 87 88 89


/**
  Create the list of default directories.

90 91
  @param alloc  MEM_ROOT where the list of directories is stored

92
  @details
93 94 95 96 97 98 99 100 101 102 103 104 105
  The directories searched, in order, are:
  - Windows:     GetSystemWindowsDirectory()
  - Windows:     GetWindowsDirectory()
  - Windows:     C:/
  - Windows:     Directory above where the executable is located
  - Netware:     sys:/etc/
  - Unix & OS/2: /etc/
  - Unix:        --sysconfdir=<path> (compile-time option)
  - OS/2:        getenv(ETC)
  - ALL:         getenv(DEFAULT_HOME_ENV)
  - ALL:         --defaults-extra-file=<path> (run-time option)
  - Unix:        ~/

106 107 108 109
  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.

110 111
  @retval NULL  Failure (out of memory, probably)
  @retval other Pointer to NULL-terminated array of default directories
112 113
*/

114
static const char **init_default_directories(MEM_ROOT *alloc);
115

bk@work.mysql.com's avatar
bk@work.mysql.com committed
116

117
static char *remove_end_comment(char *ptr);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
118

119

120 121 122 123
/*
  Process config files in default directories.

  SYNOPSIS
124
  my_search_option_files()
125 126 127 128 129 130 131 132 133 134
  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
  args_used                   Pointer to variable for storing the number of
                              arguments used.
  func                        Pointer to the function to process options
  func_ctx                    It's context. Usually it is the structure to
                              store additional options.
  DESCRIPTION
monty@mishka.local's avatar
monty@mishka.local committed
135 136 137 138 139 140 141
    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().
142 143 144 145 146


  RETURN
    0  ok
    1  given cinf_file doesn't exist
monty@mishka.local's avatar
monty@mishka.local committed
147

148
    The global variable 'my_defaults_group_suffix' is updated with value for
monty@mishka.local's avatar
monty@mishka.local committed
149
    --defaults_group_suffix
150 151
*/

152
int my_search_option_files(const char *conf_file, int *argc, char ***argv,
monty@mishka.local's avatar
monty@mishka.local committed
153 154
                           uint *args_used, Process_option_func func,
                           void *func_ctx)
155
{
jimw@mysql.com's avatar
jimw@mysql.com committed
156
  const char **dirs, *forced_default_file, *forced_extra_defaults;
157
  int error= 0;
158
  DBUG_ENTER("my_search_option_files");
159 160

  /* Check if we want to force the use a specific default file */
monty@mishka.local's avatar
monty@mishka.local committed
161 162 163
  *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used,
                                    (char **) &forced_default_file,
                                    (char **) &forced_extra_defaults,
164
                                    (char **) &my_defaults_group_suffix);
hf@deer.(none)'s avatar
hf@deer.(none) committed
165

166 167
  if (! my_defaults_group_suffix)
    my_defaults_group_suffix= getenv(STRINGIFY_ARG(DEFAULT_GROUP_SUFFIX_ENV));
168 169

  if (forced_extra_defaults)
170
    my_defaults_extra_file= (char *) forced_extra_defaults;
monty@mishka.local's avatar
monty@mishka.local committed
171
  
172
  if (forced_default_file)
173
    my_defaults_file= forced_default_file;
174

monty@mishka.local's avatar
monty@mishka.local committed
175 176 177 178
  /*
    We can only handle 'defaults-group-suffix' if we are called from
    load_defaults() as otherwise we can't know the type of 'func_ctx'
  */
179

180
  if (my_defaults_group_suffix && func == handle_default_option)
monty@mishka.local's avatar
monty@mishka.local committed
181 182 183 184
  {
    /* Handle --defaults-group-suffix= */
    uint i;
    const char **extra_groups;
185
    const uint instance_len= strlen(my_defaults_group_suffix); 
monty@mishka.local's avatar
monty@mishka.local committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
    struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx;
    char *ptr;
    TYPELIB *group= ctx->group;
    
    if (!(extra_groups= 
	  (const char**)alloc_root(ctx->alloc,
                                   (2*group->count+1)*sizeof(char*))))
      goto err;
    
    for (i= 0; i < group->count; i++)
    {
      uint len;
      extra_groups[i]= group->type_names[i]; /** copy group */
      
      len= strlen(extra_groups[i]);
      if (!(ptr= alloc_root(ctx->alloc, len+instance_len+1)))
	goto err;
      
      extra_groups[i+group->count]= ptr;
      
      /** Construct new group */
      memcpy(ptr, extra_groups[i], len);
208
      memcpy(ptr+len, my_defaults_group_suffix, instance_len+1);
monty@mishka.local's avatar
monty@mishka.local committed
209 210 211 212 213 214 215
    }
    
    group->count*= 2;
    group->type_names= extra_groups;
    group->type_names[group->count]= 0;
  }
  
216 217
  if (forced_default_file)
  {
monty@mysql.com's avatar
monty@mysql.com committed
218
    if ((error= search_default_file_with_ext(func, func_ctx, "", "",
petr@mysql.com's avatar
petr@mysql.com committed
219
                                             forced_default_file, 0)) < 0)
220 221 222 223 224 225 226 227 228 229
      goto err;
    if (error > 0)
    {
      fprintf(stderr, "Could not open required defaults file: %s\n",
              forced_default_file);
      goto err;
    }
  }
  else if (dirname_length(conf_file))
  {
monty@mysql.com's avatar
monty@mysql.com committed
230
    if ((error= search_default_file(func, func_ctx, NullS, conf_file)) < 0)
231 232 233 234 235 236 237 238
      goto err;
  }
  else
  {
    for (dirs= default_directories ; *dirs; dirs++)
    {
      if (**dirs)
      {
monty@mysql.com's avatar
monty@mysql.com committed
239
	if (search_default_file(func, func_ctx, *dirs, conf_file) < 0)
240 241
	  goto err;
      }
242
      else if (my_defaults_extra_file)
243
      {
244
        if ((error= search_default_file_with_ext(func, func_ctx, "", "",
245
                                                my_defaults_extra_file, 0)) < 0)
246
	  goto err;				/* Fatal error */
247 248 249
        if (error > 0)
        {
          fprintf(stderr, "Could not open required defaults file: %s\n",
250
                  my_defaults_extra_file);
251 252
          goto err;
        }
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
      }
    }
  }

  DBUG_RETURN(error);

err:
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
  exit(1);
  return 0;					/* Keep compiler happy */
}


/*
  The option handler for load_defaults.

  SYNOPSIS
monty@mysql.com's avatar
monty@mysql.com committed
270 271
    handle_deault_option()
    in_ctx                  Handler context. In this case it is a
272
                            handle_option_ctx structure.
monty@mysql.com's avatar
monty@mysql.com committed
273 274
    group_name              The name of the group the option belongs to.
    option                  The very option to be processed. It is already
275 276
                            prepared to be used in argv (has -- prefix)

277
  DESCRIPTION
monty@mysql.com's avatar
monty@mysql.com committed
278 279 280 281
    This handler checks whether a group is one of the listed and adds an option
    to the array if yes. Some other handler can record, for instance, all
    groups and their options, not knowing in advance the names and amount of
    groups.
282

283 284 285 286 287 288
  RETURN
    0 - ok
    1 - error occured
*/

static int handle_default_option(void *in_ctx, const char *group_name,
monty@mysql.com's avatar
monty@mysql.com committed
289
                                 const char *option)
290 291
{
  char *tmp;
monty@mysql.com's avatar
monty@mysql.com committed
292 293 294
  struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;

  if (find_type((char *)group_name, ctx->group, 3))
295
  {
296
    if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1)))
297 298 299 300 301 302 303 304 305 306
      return 1;
    if (insert_dynamic(ctx->args, (gptr) &tmp))
      return 1;
    strmov(tmp, option);
  }

  return 0;
}


307
/*
monty@mishka.local's avatar
monty@mishka.local committed
308
  Gets options from the command line
309 310

  SYNOPSIS
monty@mishka.local's avatar
monty@mishka.local committed
311
    get_defaults_options()
312 313 314 315 316 317
    argc			Pointer to argc of original program
    argv			Pointer to argv of original program
    defaults                    --defaults-file option
    extra_defaults              --defaults-extra-file option

  RETURN
monty@mishka.local's avatar
monty@mishka.local committed
318 319 320
    # Number of arguments used from *argv
      defaults and extra_defaults will be set to option of the appropriate
      items of argv array, or to NULL if there are no such options
321 322
*/

monty@mishka.local's avatar
monty@mishka.local committed
323 324 325 326
int get_defaults_options(int argc, char **argv,
                         char **defaults,
                         char **extra_defaults,
                         char **group_suffix)
327
{
monty@mishka.local's avatar
monty@mishka.local committed
328 329 330 331
  int org_argc= argc, prev_argc= 0;
  *defaults= *extra_defaults= *group_suffix= 0;

  while (argc >= 2 && argc != prev_argc)
332
  {
monty@mishka.local's avatar
monty@mishka.local committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
    /* Skip program name or previously handled argument */
    argv++;
    prev_argc= argc;                            /* To check if we found */
    if (!*defaults && is_prefix(*argv,"--defaults-file="))
    {
      *defaults= *argv + sizeof("--defaults-file=")-1;
       argc--;
       continue;
    }
    if (!*extra_defaults && is_prefix(*argv,"--defaults-extra-file="))
    {
      *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1;
      argc--;
      continue;
    }
    if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix="))
    {
      *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1;
      argc--;
      continue;
    }
354
  }
monty@mishka.local's avatar
monty@mishka.local committed
355
  return org_argc - argc;
356 357 358
}


359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
/*
  Read options from configurations files

  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

  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 do
    exit(1)
 
    To free used memory one should call free_defaults() with the argument
    that was put in *argv

   RETURN
     0	ok
     1	The given conf_file didn't exists
*/


390
int load_defaults(const char *conf_file, const char **groups,
monty@mysql.com's avatar
monty@mysql.com committed
391
                  int *argc, char ***argv)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
392 393 394
{
  DYNAMIC_ARRAY args;
  TYPELIB group;
395 396
  my_bool found_print_defaults= 0;
  uint args_used= 0;
397
  int error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
398 399
  MEM_ROOT alloc;
  char *ptr,**res;
400
  struct handle_option_ctx ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
401 402
  DBUG_ENTER("load_defaults");

403
  init_alloc_root(&alloc,512,0);
404 405
  if ((default_directories= init_default_directories(&alloc)) == NULL)
    goto err;
monty@mishka.local's avatar
monty@mishka.local committed
406 407 408 409
  /*
    Check if the user doesn't want any default option processing
    --no-defaults is always the first option
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
410 411 412 413 414 415 416 417 418 419 420
  if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults"))
  {
    /* remove the --no-defaults argument and return only the other arguments */
    uint i;
    if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+
				 (*argc + 1)*sizeof(char*))))
      goto err;
    res= (char**) (ptr+sizeof(alloc));
    res[0]= **argv;				/* Copy program name */
    for (i=2 ; i < (uint) *argc ; i++)
      res[i-1]=argv[0][i];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
421
    res[i-1]=0;					/* End pointer */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
422 423 424
    (*argc)--;
    *argv=res;
    *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
425
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
426 427 428 429 430
  }

  group.count=0;
  group.name= "defaults";
  group.type_names= groups;
431

bk@work.mysql.com's avatar
bk@work.mysql.com committed
432 433 434
  for (; *groups ; groups++)
    group.count++;

435
  if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
436
    goto err;
437 438 439 440

  ctx.alloc= &alloc;
  ctx.args= &args;
  ctx.group= &group;
petr@mysql.com's avatar
petr@mysql.com committed
441

442
  error= my_search_option_files(conf_file, argc, argv, &args_used,
monty@mishka.local's avatar
monty@mishka.local committed
443
                                handle_default_option, (void *) &ctx);
444 445 446 447
  /*
    Here error contains <> 0 only if we have a fully specified conf_file
    or a forced default file
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
448 449 450 451 452 453
  if (!(ptr=(char*) alloc_root(&alloc,sizeof(alloc)+
			       (args.elements + *argc +1) *sizeof(char*))))
    goto err;
  res= (char**) (ptr+sizeof(alloc));

  /* copy name + found arguments + command line arguments to new array */
454
  res[0]= argv[0][0];  /* Name MUST be set, even by embedded library */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
455
  memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*));
monty@mishka.local's avatar
monty@mishka.local committed
456
  /* Skip --defaults-xxx options */
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
457 458
  (*argc)-= args_used;
  (*argv)+= args_used;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
459

monty@mishka.local's avatar
monty@mishka.local committed
460 461 462 463
  /*
    Check if we wan't to see the new argument list
    This options must always be the last of the default options
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
464 465 466
  if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults"))
  {
    found_print_defaults=1;
467
    --*argc; ++*argv;				/* skip argument */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
468 469
  }

470 471 472
  if (*argc)
    memcpy((gptr) (res+1+args.elements), (char*) ((*argv)+1),
	   (*argc-1)*sizeof(char*));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
473 474 475 476 477 478 479 480 481 482 483 484 485 486
  res[args.elements+ *argc]=0;			/* last null */

  (*argc)+=args.elements;
  *argv= (char**) res;
  *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
  delete_dynamic(&args);
  if (found_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("");
487
    exit(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
488
  }
489
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
490 491

 err:
492
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
493
  exit(1);
monty@mysql.com's avatar
monty@mysql.com committed
494
  return 0;					/* Keep compiler happy */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
495 496 497 498 499 500 501
}


void free_defaults(char **argv)
{
  MEM_ROOT ptr;
  memcpy_fixed((char*) &ptr,(char *) argv - sizeof(ptr), sizeof(ptr));
502
  free_root(&ptr,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
503 504 505
}


monty@mysql.com's avatar
monty@mysql.com committed
506 507
static int search_default_file(Process_option_func opt_handler,
                               void *handler_ctx,
508
			       const char *dir,
monty@mysql.com's avatar
monty@mysql.com committed
509
			       const char *config_file)
510 511
{
  char **ext;
512 513 514
  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;
515

516
  for (ext= (char**) exts_to_use; *ext; ext++)
517 518
  {
    int error;
monty@mysql.com's avatar
monty@mysql.com committed
519 520
    if ((error= search_default_file_with_ext(opt_handler, handler_ctx,
                                             dir, *ext,
petr@mysql.com's avatar
petr@mysql.com committed
521
					     config_file, 0)) < 0)
522 523 524 525 526 527
      return error;
  }
  return 0;
}


528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
/*
  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, uint 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;
}


578
/*
579
  Open a configuration file (if exists) and read given options from it
580

581
  SYNOPSIS
582
    search_default_file_with_ext()
583 584 585 586
    opt_handler                 Option handler function. It is used to process
                                every separate option.
    handler_ctx                 Pointer to the structure to store actual 
                                parameters of the function.
587 588
    dir				directory to read
    ext				Extension for configuration file
589
    config_file                 Name of configuration file
590
    group			groups to read
591 592
    recursion_level             the level of recursion, got while processing
                                "!include" or "!includedir"
593 594 595 596 597

  RETURN
    0   Success
    -1	Fatal error, abort
     1	File not found (Warning)
598 599
*/

monty@mysql.com's avatar
monty@mysql.com committed
600 601 602 603
static int search_default_file_with_ext(Process_option_func opt_handler,
                                        void *handler_ctx,
                                        const char *dir,
                                        const char *ext,
petr@mysql.com's avatar
petr@mysql.com committed
604 605
                                        const char *config_file,
                                        int recursion_level)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
606
{
petr@mysql.com's avatar
petr@mysql.com committed
607
  char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
petr@mysql.com's avatar
petr@mysql.com committed
608
  char *value, option[4096], tmp[FN_REFLEN];
609 610 611
  static const char includedir_keyword[]= "includedir";
  static const char include_keyword[]= "include";
  const int max_recursion_level= 10;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
612 613
  FILE *fp;
  uint line=0;
614
  my_bool found_group=0;
615 616 617
  uint i;
  MY_DIR *search_dir;
  FILEINFO *search_file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
618 619 620 621 622

  if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3)
    return 0;					/* Ignore wrong paths */
  if (dir)
  {
623
    end=convert_dirname(name, dir, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
624
    if (dir[0] == FN_HOMELIB)		/* Add . to filenames in home */
625 626
      *end++='.';
    strxmov(end,config_file,ext,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
627 628 629 630 631
  }
  else
  {
    strmov(name,config_file);
  }
632
  fn_format(name,name,"","",4);
633
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
634 635
  {
    MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
636
    if (!my_stat(name,&stat_info,MYF(0)))
637
      return 1;
638 639 640 641 642 643 644
    /*
      Ignore world-writable regular files.
      This is mainly done to protect us to not read a file created by
      the mysqld server, but the check is still valid in most context. 
    */
    if ((stat_info.st_mode & S_IWOTH) &&
	(stat_info.st_mode & S_IFMT) == S_IFREG)
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
645
    {
646
      fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n",
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
647
              name);
648
      return 0;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
649
    }
650 651
  }
#endif
652
  if (!(fp= my_fopen(name, O_RDONLY, MYF(0))))
653
    return 1;					/* Ignore wrong files */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
654

655
  while (fgets(buff, sizeof(buff) - 1, fp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
656 657 658
  {
    line++;
    /* Ignore comment and empty lines */
659 660 661
    for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
    {}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
662 663
    if (*ptr == '#' || *ptr == ';' || !*ptr)
      continue;
664 665

    /* Configuration File Directives */
monty@mysql.com's avatar
monty@mysql.com committed
666
    if ((*ptr == '!'))
667
    {
monty@mysql.com's avatar
monty@mysql.com committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681
      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;
      }

682 683 684 685
      /* skip over `!' and following whitespace */
      for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
      {}

monty@mysql.com's avatar
monty@mysql.com committed
686 687 688
      if ((!strncmp(ptr, includedir_keyword,
                    sizeof(includedir_keyword) - 1)) &&
          my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
689
      {
690 691 692 693
	if (!(ptr= get_argument(includedir_keyword,
                                sizeof(includedir_keyword),
                                ptr, name, line)))
	  goto err;
694 695 696 697 698 699 700 701 702

        if (!(search_dir= my_dir(ptr, MYF(MY_WME))))
          goto err;

        for (i= 0; i < (uint) search_dir->number_off_files; i++)
        {
          search_file= search_dir->dir_entry + i;
          ext= fn_ext(search_file->name);

petr@mysql.com's avatar
petr@mysql.com committed
703
          /* check extension */
704
          for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++)
705 706 707 708 709 710 711 712 713 714
          {
            if (!strcmp(ext, *tmp_ext))
              break;
          }

          if (*tmp_ext)
          {
            fn_format(tmp, search_file->name, ptr, "",
                      MY_UNPACK_FILENAME | MY_SAFE_PATH);

petr@mysql.com's avatar
petr@mysql.com committed
715
            search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp,
716 717 718 719 720 721
                                         recursion_level + 1);
          }
        }

        my_dirend(search_dir);
      }
monty@mysql.com's avatar
monty@mysql.com committed
722 723
      else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
               my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
724
      {
725 726 727 728
	if (!(ptr= get_argument(include_keyword,
                                sizeof(include_keyword), ptr,
                                name, line)))
	  goto err;
729

petr@mysql.com's avatar
petr@mysql.com committed
730
        search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
731 732 733 734 735 736
                                     recursion_level + 1);
      }

      continue;
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
737 738 739 740 741 742 743 744 745 746
    if (*ptr == '[')				/* Group name */
    {
      found_group=1;
      if (!(end=(char *) strchr(++ptr,']')))
      {
	fprintf(stderr,
		"error: Wrong group definition in config file: %s at line %d\n",
		name,line);
	goto err;
      }
747
      for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;/* Remove end space */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
748
      end[0]=0;
749 750

      strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
751 752 753 754 755 756 757 758 759
      continue;
    }
    if (!found_group)
    {
      fprintf(stderr,
	      "error: Found option without preceding group in config file: %s at line: %d\n",
	      name,line);
      goto err;
    }
760 761
    
   
762 763 764
    end= remove_end_comment(ptr);
    if ((value= strchr(ptr, '=')))
      end= value;				/* Option without argument */
765
    for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
766 767
    if (!value)
    {
768 769 770
      strmake(strmov(option,"--"),ptr,(uint) (end-ptr));
      if (opt_handler(handler_ctx, curr_gr, option))
        goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
771 772 773 774 775
    }
    else
    {
      /* Remove pre- and end space */
      char *value_end;
776
      for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
777
      value_end=strend(value);
778 779 780 781
      /*
	We don't have to test for value_end >= value as we know there is
	an '=' before
      */
782
      for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
783 784
      if (value_end < value)			/* Empty string */
	value_end=value;
785 786

      /* remove quotes around argument */
787 788 789
      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 */
790 791 792 793
      {
	value++;
	value_end--;
      }
794
      ptr=strnmov(strmov(option,"--"),ptr,(uint) (end-ptr));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
795
      *ptr++= '=';
796

bk@work.mysql.com's avatar
bk@work.mysql.com committed
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
      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;
817 818 819 820 821 822
	  case '\"':
	    *ptr++= '\"';
	    break;
	  case '\'':
	    *ptr++= '\'';
	    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
823 824 825 826 827 828 829 830 831 832 833 834 835
	  case '\\':
	    *ptr++= '\\';
	    break;
	  default:				/* Unknown; Keep '\' */
	    *ptr++= '\\';
	    *ptr++= *value;
	    break;
	  }
	}
	else
	  *ptr++= *value;
      }
      *ptr=0;
836 837
      if (opt_handler(handler_ctx, curr_gr, option))
        goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
838 839 840 841 842 843 844
    }
  }
  my_fclose(fp,MYF(0));
  return(0);

 err:
  my_fclose(fp,MYF(0));
845
  return -1;					/* Fatal error */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
846 847 848
}


849 850
static char *remove_end_comment(char *ptr)
{
851 852
  char quote= 0;	/* we are inside quote marks */
  char escape= 0;	/* symbol is protected by escape chagacter */
853 854 855

  for (; *ptr; ptr++)
  {
856
    if ((*ptr == '\'' || *ptr == '\"') && !escape)
857 858 859 860 861 862
    {
      if (!quote)
	quote= *ptr;
      else if (quote == *ptr)
	quote= 0;
    }
monty@mysql.com's avatar
monty@mysql.com committed
863
    /* We are not inside a string */
864
    if (!quote && *ptr == '#')
865 866 867 868
    {
      *ptr= 0;
      return ptr;
    }
869
    escape= (quote && *ptr == '\\' && !escape);
870 871 872 873
  }
  return ptr;
}

monty@mysql.com's avatar
monty@mysql.com committed
874
#include <help_start.h>
875

876
void my_print_default_files(const char *conf_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
877
{
878
  const char *empty_list[]= { "", 0 };
879
  my_bool have_ext= fn_ext(conf_file)[0] != 0;
880
  const char **exts_to_use= have_ext ? empty_list : f_extensions;
881 882
  char name[FN_REFLEN], **ext;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
883 884 885 886 887 888
  puts("\nDefault options are read from the following files in the given order:");

  if (dirname_length(conf_file))
    fputs(conf_file,stdout);
  else
  {
889 890 891 892 893 894 895 896 897
    /*
      If default_directories is already initialized, use it.  Otherwise,
      use a private MEM_ROOT.
    */
    const char **dirs = default_directories;
    MEM_ROOT alloc;
    init_alloc_root(&alloc,512,0);

    if (!dirs && (dirs= init_default_directories(&alloc)) == NULL)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
898
    {
899 900 901 902 903
      fputs("Internal error initializing default directories list", stdout);
    }
    else
    {
      for ( ; *dirs; dirs++)
904
      {
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
        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;
          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);
        }
921
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
922
    }
923 924

    free_root(&alloc, MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
925
  }
926
  puts("");
927 928 929 930
}

void print_defaults(const char *conf_file, const char **groups)
{
931
  const char **groups_save= groups;
932 933
  my_print_default_files(conf_file);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
934 935 936 937 938 939
  fputs("The following groups are read:",stdout);
  for ( ; *groups ; groups++)
  {
    fputc(' ',stdout);
    fputs(*groups,stdout);
  }
940

941
  if (my_defaults_group_suffix)
942 943 944 945 946 947
  {
    groups= groups_save;
    for ( ; *groups ; groups++)
    {
      fputc(' ',stdout);
      fputs(*groups,stdout);
948
      fputs(my_defaults_group_suffix,stdout);
949 950
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
951 952 953
  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 options file\n\
954 955
--defaults-file=#	Only read default options from the given file #\n\
--defaults-extra-file=# Read this file after the global files are read");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
956
}
monty@mysql.com's avatar
monty@mysql.com committed
957 958

#include <help_end.h>
959

960

961 962 963 964 965 966 967 968 969 970 971 972 973 974
static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs)
{
  char buf[FN_REFLEN];
  uint len;
  char *p;
  my_bool err __attribute__((unused));

  /* Normalize directory name */
  len= unpack_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);
975

976 977
  return 0;
}
978 979


980 981 982 983 984 985 986 987 988 989 990 991
#ifdef __WIN__
/*
  This wrapper for GetSystemWindowsDirectory() will dynamically bind to the
  function if it is available, emulate it on NT4 Terminal Server by stripping
  the \SYSTEM32 from the end of the results of GetSystemDirectory(), or just
  return GetSystemDirectory().
 */

typedef UINT (WINAPI *GET_SYSTEM_WINDOWS_DIRECTORY)(LPSTR, UINT);

static uint my_get_system_windows_directory(char *buffer, uint size)
{
992
  uint count;
993 994 995 996 997 998 999 1000
  GET_SYSTEM_WINDOWS_DIRECTORY
    func_ptr= (GET_SYSTEM_WINDOWS_DIRECTORY)
              GetProcAddress(GetModuleHandle("kernel32.dll"),
                                             "GetSystemWindowsDirectoryA");

  if (func_ptr)
    return func_ptr(buffer, size);

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
  /*
    Windows NT 4.0 Terminal Server Edition:  
    To retrieve the shared Windows directory, call GetSystemDirectory and
    trim the "System32" element from the end of the returned path.
  */
  count= GetSystemDirectory(buffer, size);
  if (count > 8 && stricmp(buffer+(count-8), "\\System32") == 0)
  {
    count-= 8;
    buffer[count] = '\0';
1011
  }
1012
  return count;
1013 1014 1015
}


1016
static const char *my_get_module_parent(char *buf, size_t size)
1017
{
1018 1019
  char *last= NULL;
  char *end;
1020 1021
  if (!GetModuleFileName(NULL, buf, size))
    return NULL;
1022
  end= strend(buf);
1023

1024 1025 1026 1027 1028 1029
  /*
    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--)
1030
  {
1031
    if (*end == FN_LIBCHAR)
1032
    {
1033
      if (last)
1034
      {
1035 1036 1037
        /* Keep the last '\' as this works both with D:\ and a directory */
        end[1]= 0;
        break;
1038
      }
1039
      last= end;
1040 1041
    }
  }
1042

1043
  return buf;
1044
}
1045
#endif /* __WIN__ */
1046 1047


1048
static const char **init_default_directories(MEM_ROOT *alloc)
1049
{
1050 1051 1052
  const char **dirs;
  char *env;
  int errors= 0;
1053

1054 1055 1056 1057
  dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *));
  if (dirs == NULL)
    return NULL;
  bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *));
1058

1059
#ifdef __WIN__
1060

1061 1062 1063 1064
  {
    char fname_buffer[FN_REFLEN];
    if (my_get_system_windows_directory(fname_buffer, sizeof(fname_buffer)))
      errors += add_directory(alloc, fname_buffer, dirs);
1065

1066 1067
    if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
      errors += add_directory(alloc, fname_buffer, dirs);
1068

1069
    errors += add_directory(alloc, "C:/", dirs);
1070

1071 1072 1073
    if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL)
      errors += add_directory(alloc, fname_buffer, dirs);
  }
1074

1075
#elif defined(__NETWARE__)
1076

1077
  errors += add_directory(alloc, "sys:/etc/", dirs);
1078

1079
#else
1080

1081
  errors += add_directory(alloc, "/etc/", dirs);
1082

1083 1084 1085 1086
#if defined(__EMX__) || defined(OS2)
  if ((env= getenv("ETC")))
    errors += add_directory(alloc, env, dirs);
#elif defined(DEFAULT_SYSCONFDIR)
1087
  if (DEFAULT_SYSCONFDIR != "")
1088 1089 1090
    errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs);
#endif /* __EMX__ || __OS2__ */

1091
#endif
1092

1093 1094 1095 1096 1097
  if ((env= getenv(STRINGIFY_ARG(DEFAULT_HOME_ENV))))
    errors += add_directory(alloc, env, dirs);

  /* Placeholder for --defaults-extra-file=<path> */
  errors += add_directory(alloc, "", dirs);
1098

1099 1100 1101
#if !defined(__WIN__) && !defined(__NETWARE__) && \
    !defined(__EMX__) && !defined(OS2)
  errors += add_directory(alloc, "~/", dirs);
1102
#endif
1103 1104 1105

  return (errors > 0 ? NULL : dirs);
}