default.c 32.2 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
  The directories searched, in order, are:
  - Windows:     GetSystemWindowsDirectory()
  - Windows:     GetWindowsDirectory()
  - Windows:     C:/
  - Windows:     Directory above where the executable is located
  - Netware:     sys:/etc/
99 100
  - Unix:        /etc/
  - Unix:        /etc/mysql/
101 102 103 104 105
  - Unix:        --sysconfdir=<path> (compile-time option)
  - 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
147
    2  out of memory
monty@mishka.local's avatar
monty@mishka.local committed
148

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

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

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

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

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

monty@mishka.local's avatar
monty@mishka.local committed
176 177 178 179
  /*
    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'
  */
180

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

258
  DBUG_RETURN(0);
259 260 261

err:
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
262
  DBUG_RETURN(1);
263 264 265 266 267 268 269
}


/*
  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). If it
                            is NULL, we are handling a new group (section).
277

278
  DESCRIPTION
monty@mysql.com's avatar
monty@mysql.com committed
279 280 281 282
    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.
283

284 285 286 287 288 289
  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
290
                                 const char *option)
291 292
{
  char *tmp;
monty@mysql.com's avatar
monty@mysql.com committed
293 294
  struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;

295 296 297
  if (!option)
    return 0;

monty@mysql.com's avatar
monty@mysql.com committed
298
  if (find_type((char *)group_name, ctx->group, 3))
299
  {
300
    if (!(tmp= alloc_root(ctx->alloc, strlen(option) + 1)))
301
      return 1;
302
    if (insert_dynamic(ctx->args, (uchar*) &tmp))
303 304 305 306 307 308 309 310
      return 1;
    strmov(tmp, option);
  }

  return 0;
}


311
/*
monty@mishka.local's avatar
monty@mishka.local committed
312
  Gets options from the command line
313 314

  SYNOPSIS
monty@mishka.local's avatar
monty@mishka.local committed
315
    get_defaults_options()
316 317 318 319 320 321
    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
322 323 324
    # 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
325 326
*/

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

  while (argc >= 2 && argc != prev_argc)
336
  {
monty@mishka.local's avatar
monty@mishka.local committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
    /* 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;
    }
358
  }
monty@mishka.local's avatar
monty@mishka.local committed
359
  return org_argc - argc;
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
/*
  Wrapper around my_load_defaults() for interface compatibility.

  SYNOPSIS
    load_defaults()
    conf_file			Basename for configuration file to search for.
    				If this is a path, then only this file is read.
    groups			Which [group] entrys to read.
				Points to an null terminated array of pointers
    argc			Pointer to argc of original program
    argv			Pointer to argv of original program

  NOTES

    This function is NOT thread-safe as it uses a global pointer internally.
    See also notes for my_load_defaults().

  RETURN
    0 ok
    1 The given conf_file didn't exists
*/
int load_defaults(const char *conf_file, const char **groups,
                  int *argc, char ***argv)
{
  return my_load_defaults(conf_file, groups, argc, argv, &default_directories);
}
388

389 390 391 392
/*
  Read options from configurations files

  SYNOPSIS
393
    my_load_defaults()
394 395 396 397 398 399
    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
400 401
    default_directories         Pointer to a location where a pointer to the list
                                of default directories will be stored
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416

  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
417 418 419 420 421 422 423
     - If successful, 0 is returned. If 'default_directories' is not NULL,
     a pointer to the array of default directory paths is stored to a location
     it points to. That stored value must be passed to my_search_option_files()
     later.
     
     - 1 is returned if the given conf_file didn't exist. In this case, the
     value pointed to by default_directories is undefined.
424 425 426
*/


427 428
int my_load_defaults(const char *conf_file, const char **groups,
                  int *argc, char ***argv, const char ***default_directories)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
429 430 431
{
  DYNAMIC_ARRAY args;
  TYPELIB group;
432 433
  my_bool found_print_defaults= 0;
  uint args_used= 0;
434
  int error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
435 436
  MEM_ROOT alloc;
  char *ptr,**res;
437
  struct handle_option_ctx ctx;
438
  const char **dirs;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
439 440
  DBUG_ENTER("load_defaults");

441
  init_alloc_root(&alloc,512,0);
442
  if ((dirs= init_default_directories(&alloc)) == NULL)
443
    goto err;
monty@mishka.local's avatar
monty@mishka.local committed
444 445 446 447
  /*
    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
448 449 450 451 452 453 454 455 456 457 458
  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
459
    res[i-1]=0;					/* End pointer */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
460 461 462
    (*argc)--;
    *argv=res;
    *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
463 464
    if (default_directories)
      *default_directories= dirs;
465
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
466 467 468 469 470
  }

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
472 473 474
  for (; *groups ; groups++)
    group.count++;

475
  if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
476
    goto err;
477 478 479 480

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

482
  error= my_search_option_files(conf_file, argc, argv, &args_used,
483 484
                                handle_default_option, (void *) &ctx,
                                dirs);
485 486 487 488
  /*
    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
489 490 491 492 493 494
  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 */
495
  res[0]= argv[0][0];  /* Name MUST be set, even by embedded library */
496
  memcpy((uchar*) (res+1), args.buffer, args.elements*sizeof(char*));
monty@mishka.local's avatar
monty@mishka.local committed
497
  /* Skip --defaults-xxx options */
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
498 499
  (*argc)-= args_used;
  (*argv)+= args_used;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
500

monty@mishka.local's avatar
monty@mishka.local committed
501 502 503 504
  /*
    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
505 506 507
  if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults"))
  {
    found_print_defaults=1;
508
    --*argc; ++*argv;				/* skip argument */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
509 510
  }

511
  if (*argc)
512
    memcpy((uchar*) (res+1+args.elements), (char*) ((*argv)+1),
513
	   (*argc-1)*sizeof(char*));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
514 515 516 517 518 519 520 521 522 523 524 525 526 527
  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("");
528
    exit(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
529
  }
530 531 532 533

  if (error == 0 && default_directories)
    *default_directories= dirs;

534
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
535 536

 err:
537
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
538
  exit(1);
monty@mysql.com's avatar
monty@mysql.com committed
539
  return 0;					/* Keep compiler happy */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
540 541 542 543 544 545 546
}


void free_defaults(char **argv)
{
  MEM_ROOT ptr;
  memcpy_fixed((char*) &ptr,(char *) argv - sizeof(ptr), sizeof(ptr));
547
  free_root(&ptr,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
548 549 550
}


monty@mysql.com's avatar
monty@mysql.com committed
551 552
static int search_default_file(Process_option_func opt_handler,
                               void *handler_ctx,
553
			       const char *dir,
monty@mysql.com's avatar
monty@mysql.com committed
554
			       const char *config_file)
555 556
{
  char **ext;
557 558 559
  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;
560

561
  for (ext= (char**) exts_to_use; *ext; ext++)
562 563
  {
    int error;
monty@mysql.com's avatar
monty@mysql.com committed
564 565
    if ((error= search_default_file_with_ext(opt_handler, handler_ctx,
                                             dir, *ext,
petr@mysql.com's avatar
petr@mysql.com committed
566
					     config_file, 0)) < 0)
567 568 569 570 571 572
      return error;
  }
  return 0;
}


573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
/*
  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.
*/

588
static char *get_argument(const char *keyword, size_t kwlen,
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
                          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;
}


623
/*
624
  Open a configuration file (if exists) and read given options from it
625

626
  SYNOPSIS
627
    search_default_file_with_ext()
628 629 630 631
    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.
632 633
    dir				directory to read
    ext				Extension for configuration file
634
    config_file                 Name of configuration file
635
    group			groups to read
636 637
    recursion_level             the level of recursion, got while processing
                                "!include" or "!includedir"
638 639 640 641 642

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

monty@mysql.com's avatar
monty@mysql.com committed
645 646 647 648
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
649 650
                                        const char *config_file,
                                        int recursion_level)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
651
{
petr@mysql.com's avatar
petr@mysql.com committed
652
  char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
petr@mysql.com's avatar
petr@mysql.com committed
653
  char *value, option[4096], tmp[FN_REFLEN];
654 655 656
  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
657 658
  FILE *fp;
  uint line=0;
659
  my_bool found_group=0;
660 661 662
  uint i;
  MY_DIR *search_dir;
  FILEINFO *search_file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
663 664 665 666 667

  if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3)
    return 0;					/* Ignore wrong paths */
  if (dir)
  {
668
    end=convert_dirname(name, dir, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
669
    if (dir[0] == FN_HOMELIB)		/* Add . to filenames in home */
670 671
      *end++='.';
    strxmov(end,config_file,ext,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
672 673 674 675 676
  }
  else
  {
    strmov(name,config_file);
  }
677
  fn_format(name,name,"","",4);
678
#if !defined(__WIN__) && !defined(__NETWARE__)
679 680
  {
    MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
681
    if (!my_stat(name,&stat_info,MYF(0)))
682
      return 1;
683 684 685 686 687 688 689
    /*
      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
690
    {
691
      fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n",
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
692
              name);
693
      return 0;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
694
    }
695 696
  }
#endif
697
  if (!(fp= my_fopen(name, O_RDONLY, MYF(0))))
698
    return 1;					/* Ignore wrong files */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
699

700
  while (fgets(buff, sizeof(buff) - 1, fp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
701 702 703
  {
    line++;
    /* Ignore comment and empty lines */
704 705 706
    for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
    {}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
707 708
    if (*ptr == '#' || *ptr == ';' || !*ptr)
      continue;
709 710

    /* Configuration File Directives */
monty@mysql.com's avatar
monty@mysql.com committed
711
    if ((*ptr == '!'))
712
    {
monty@mysql.com's avatar
monty@mysql.com committed
713 714 715 716 717 718 719 720 721 722 723 724 725 726
      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;
      }

727 728 729 730
      /* skip over `!' and following whitespace */
      for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
      {}

monty@mysql.com's avatar
monty@mysql.com committed
731 732 733
      if ((!strncmp(ptr, includedir_keyword,
                    sizeof(includedir_keyword) - 1)) &&
          my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
734
      {
735 736 737 738
	if (!(ptr= get_argument(includedir_keyword,
                                sizeof(includedir_keyword),
                                ptr, name, line)))
	  goto err;
739 740 741 742 743 744 745 746 747

        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
748
          /* check extension */
749
          for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++)
750 751 752 753 754 755 756 757 758 759
          {
            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
760
            search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp,
761 762 763 764 765 766
                                         recursion_level + 1);
          }
        }

        my_dirend(search_dir);
      }
monty@mysql.com's avatar
monty@mysql.com committed
767 768
      else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
               my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
769
      {
770 771 772 773
	if (!(ptr= get_argument(include_keyword,
                                sizeof(include_keyword), ptr,
                                name, line)))
	  goto err;
774

petr@mysql.com's avatar
petr@mysql.com committed
775
        search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
776 777 778 779 780 781
                                     recursion_level + 1);
      }

      continue;
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
782 783 784 785 786 787 788 789 790 791
    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;
      }
792 793
      /* Remove end space */
      for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
794
      end[0]=0;
795

796
      strmake(curr_gr, ptr, min((size_t) (end-ptr)+1, sizeof(curr_gr)-1));
797 798 799 800

      /* signal that a new group is found */
      opt_handler(handler_ctx, curr_gr, NULL);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
801 802 803 804 805 806 807 808 809
      continue;
    }
    if (!found_group)
    {
      fprintf(stderr,
	      "error: Found option without preceding group in config file: %s at line: %d\n",
	      name,line);
      goto err;
    }
810 811
    
   
812 813 814
    end= remove_end_comment(ptr);
    if ((value= strchr(ptr, '=')))
      end= value;				/* Option without argument */
815
    for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
816 817
    if (!value)
    {
818
      strmake(strmov(option,"--"),ptr, (size_t) (end-ptr));
819 820
      if (opt_handler(handler_ctx, curr_gr, option))
        goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
821 822 823 824 825
    }
    else
    {
      /* Remove pre- and end space */
      char *value_end;
826
      for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
827
      value_end=strend(value);
828 829 830 831
      /*
	We don't have to test for value_end >= value as we know there is
	an '=' before
      */
832
      for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
833 834
      if (value_end < value)			/* Empty string */
	value_end=value;
835 836

      /* remove quotes around argument */
837 838 839
      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 */
840 841 842 843
      {
	value++;
	value_end--;
      }
844
      ptr=strnmov(strmov(option,"--"),ptr,(size_t) (end-ptr));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
845
      *ptr++= '=';
846

bk@work.mysql.com's avatar
bk@work.mysql.com committed
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
      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;
867 868 869 870 871 872
	  case '\"':
	    *ptr++= '\"';
	    break;
	  case '\'':
	    *ptr++= '\'';
	    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
873 874 875 876 877 878 879 880 881 882 883 884 885
	  case '\\':
	    *ptr++= '\\';
	    break;
	  default:				/* Unknown; Keep '\' */
	    *ptr++= '\\';
	    *ptr++= *value;
	    break;
	  }
	}
	else
	  *ptr++= *value;
      }
      *ptr=0;
886 887
      if (opt_handler(handler_ctx, curr_gr, option))
        goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
888 889 890 891 892 893 894
    }
  }
  my_fclose(fp,MYF(0));
  return(0);

 err:
  my_fclose(fp,MYF(0));
895
  return -1;					/* Fatal error */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
896 897 898
}


899 900
static char *remove_end_comment(char *ptr)
{
901 902
  char quote= 0;	/* we are inside quote marks */
  char escape= 0;	/* symbol is protected by escape chagacter */
903 904 905

  for (; *ptr; ptr++)
  {
906
    if ((*ptr == '\'' || *ptr == '\"') && !escape)
907 908 909 910 911 912
    {
      if (!quote)
	quote= *ptr;
      else if (quote == *ptr)
	quote= 0;
    }
monty@mysql.com's avatar
monty@mysql.com committed
913
    /* We are not inside a string */
914
    if (!quote && *ptr == '#')
915 916 917 918
    {
      *ptr= 0;
      return ptr;
    }
919
    escape= (quote && *ptr == '\\' && !escape);
920 921 922 923
  }
  return ptr;
}

monty@mysql.com's avatar
monty@mysql.com committed
924
#include <help_start.h>
925

926
void my_print_default_files(const char *conf_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
927
{
928
  const char *empty_list[]= { "", 0 };
929
  my_bool have_ext= fn_ext(conf_file)[0] != 0;
930
  const char **exts_to_use= have_ext ? empty_list : f_extensions;
931 932
  char name[FN_REFLEN], **ext;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
933 934 935 936 937 938
  puts("\nDefault options are read from the following files in the given order:");

  if (dirname_length(conf_file))
    fputs(conf_file,stdout);
  else
  {
939
    const char **dirs;
940 941 942
    MEM_ROOT alloc;
    init_alloc_root(&alloc,512,0);

943
    if ((dirs= init_default_directories(&alloc)) == NULL)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
944
    {
945 946 947
      fputs("Internal error initializing default directories list", stdout);
    }
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
948
    {
949
      for ( ; *dirs; dirs++)
950
      {
951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
        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);
        }
967
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
968
    }
969 970

    free_root(&alloc, MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
971
  }
972
  puts("");
973 974 975 976
}

void print_defaults(const char *conf_file, const char **groups)
{
977
  const char **groups_save= groups;
978 979
  my_print_default_files(conf_file);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
980 981 982 983 984 985
  fputs("The following groups are read:",stdout);
  for ( ; *groups ; groups++)
  {
    fputc(' ',stdout);
    fputs(*groups,stdout);
  }
986

987
  if (my_defaults_group_suffix)
988 989 990 991 992 993
  {
    groups= groups_save;
    for ( ; *groups ; groups++)
    {
      fputc(' ',stdout);
      fputs(*groups,stdout);
994
      fputs(my_defaults_group_suffix,stdout);
995 996
    }
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
997 998 999
  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\
1000 1001
--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
1002
}
monty@mysql.com's avatar
monty@mysql.com committed
1003 1004

#include <help_end.h>
1005

1006

1007 1008 1009 1010 1011 1012 1013
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));

1014
  len= normalize_dirname(buf, dir);
1015 1016 1017 1018 1019
  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);
1020

1021 1022
  return 0;
}
1023 1024


1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
#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);

1035
static size_t my_get_system_windows_directory(char *buffer, size_t size)
1036
{
1037
  size_t count;
1038 1039 1040 1041 1042 1043 1044 1045
  GET_SYSTEM_WINDOWS_DIRECTORY
    func_ptr= (GET_SYSTEM_WINDOWS_DIRECTORY)
              GetProcAddress(GetModuleHandle("kernel32.dll"),
                                             "GetSystemWindowsDirectoryA");

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

1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
  /*
    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';
1056
  }
1057
  return count;
1058 1059 1060
}


1061
static const char *my_get_module_parent(char *buf, size_t size)
1062
{
1063 1064
  char *last= NULL;
  char *end;
1065
  if (!GetModuleFileName(NULL, buf, (DWORD) size))
1066
    return NULL;
1067
  end= strend(buf);
1068

1069 1070 1071 1072 1073 1074
  /*
    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--)
1075
  {
1076
    if (*end == FN_LIBCHAR)
1077
    {
1078
      if (last)
1079
      {
1080 1081 1082
        /* Keep the last '\' as this works both with D:\ and a directory */
        end[1]= 0;
        break;
1083
      }
1084
      last= end;
1085 1086
    }
  }
1087

1088
  return buf;
1089
}
1090
#endif /* __WIN__ */
1091 1092


1093
static const char **init_default_directories(MEM_ROOT *alloc)
1094
{
1095 1096 1097
  const char **dirs;
  char *env;
  int errors= 0;
1098

1099 1100 1101 1102
  dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *));
  if (dirs == NULL)
    return NULL;
  bzero((char *) dirs, DEFAULT_DIRS_SIZE * sizeof(char *));
1103

1104
#ifdef __WIN__
1105

1106 1107 1108 1109
  {
    char fname_buffer[FN_REFLEN];
    if (my_get_system_windows_directory(fname_buffer, sizeof(fname_buffer)))
      errors += add_directory(alloc, fname_buffer, dirs);
1110

1111 1112
    if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer)))
      errors += add_directory(alloc, fname_buffer, dirs);
1113

1114
    errors += add_directory(alloc, "C:/", dirs);
1115

1116 1117 1118
    if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL)
      errors += add_directory(alloc, fname_buffer, dirs);
  }
1119

1120
#elif defined(__NETWARE__)
1121

1122
  errors += add_directory(alloc, "sys:/etc/", dirs);
1123

1124
#else
1125

1126
  errors += add_directory(alloc, "/etc/", dirs);
1127
  errors += add_directory(alloc, "/etc/mysql/", dirs);
1128

1129
#if defined(DEFAULT_SYSCONFDIR)
1130
  if (DEFAULT_SYSCONFDIR != "")
1131
    errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs);
1132
#endif /* DEFAULT_SYSCONFDIR */
1133

1134
#endif
1135

1136 1137
  if ((env= getenv(STRINGIFY_ARG(DEFAULT_HOME_ENV))))
    errors += add_directory(alloc, env, dirs);
1138

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

1142
#if !defined(__WIN__) && !defined(__NETWARE__)
1143
  errors += add_directory(alloc, "~/", dirs);
1144
#endif
1145 1146 1147

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