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

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
10 11 12 13 14 15
   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
16 17

/****************************************************************************
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 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 ~/
 --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

monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
44 45
char *defaults_extra_file=0;

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

48
#define MAX_DEFAULT_DIRS 5
49
const char *default_directories[MAX_DEFAULT_DIRS + 1];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
50 51

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

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
/*
   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
75
			       const char *dir, const char *config_file);
76 77
static int search_default_file_with_ext(Process_option_func func,
                                        void *func_ctx,
78
					const char *dir, const char *ext,
petr@mysql.com's avatar
petr@mysql.com committed
79
					const char *config_file, int recursion_level);
80
static void init_default_directories();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
81

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

petr@mysql.com's avatar
petr@mysql.com committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/*
  Add/remove option to the option file section.

  SYNOPSYS
    my_correct_file()
    file_location     The location of configuration file to edit
    option            option to look for
    option value      The value of the option we would like to set
    section_name      the name of the section
    remove_option     This is true if we want to remove the option.
                      False otherwise.
  IMPLEMENTATION
    We open the option file first, then read the file line-by-line,
    looking for the section we need. At the same time we put these lines
    into a buffer. Then we look for the option within this section and
    change/remove it. In the end we get a buffer with modified version of the
    file. Then we write it to the file, truncate it if needed and close it.

  RETURN
    0 - ok
    1 - some error has occured. Probably due to the lack of resourses
   -1 - cannot open the file
*/

int my_correct_defaults_file(const char *file_location, const char *option,
                             const char *option_value,
                             const char *section_name, int remove_option)
{
  FILE *cnf_file;
  struct stat file_stat;
  char linebuff[512], *ptr;
  uint optlen;
  uint len;
  char *file_buffer;
  uint position= 0;
  int is_found= FALSE;

  optlen= strlen(option);

  DBUG_ENTER("my_correct_file");

  if (!(cnf_file= my_fopen(file_location, O_RDWR, MYF(0))))
    goto err_fopen;

  /* my_fstat doesn't use the flag parameter */
  if (my_fstat(fileno(cnf_file), &file_stat, MYF(0)))
    goto err;

  /*
    Reserve space to read the contents of the file and some more
    for the option we want ot add.
  */
  file_buffer= (char*) my_malloc(sizeof(char)*
                       (file_stat.st_size + /* current file size */
                        optlen +            /* option name len */
                        2 +                 /* reserve space for newline */
                        1 +                 /* reserve for '=' char */
                        strlen(option_value)),  /* option value len */
                        MYF(MY_WME));

  if (!file_buffer)
    goto malloc_err;
  while (fgets(linebuff, sizeof(linebuff), cnf_file))
  {
    len= strlen(linebuff);

    /* if the section is found traverse it */
    if (is_found)
    {
      /* skip the old value of the option we are changing */
      if (strncmp(linebuff, option, optlen))
      {
        /* copy all other lines */
        strmake(file_buffer + position, linebuff, len);
        position+= len;
      }
    }
    else
    {
      strmake(file_buffer + position, linebuff, len);
      position+= len;
    }


    /* looking for appropriate section */
    for (ptr= linebuff ; my_isspace(&my_charset_latin1,*ptr) ; ptr++)
    {}

    if (*ptr == '[')
    {
      /* copy the line to the buffer */
      if (!strncmp(++ptr, section_name, strlen(section_name)))
      {
        is_found= TRUE;
        /* add option */
        if (!remove_option)
        {
          strmake(file_buffer + position, option, optlen);
          position+= optlen;
          if (*option_value)
          {
            *(file_buffer + position++)= '=';
            strmake(file_buffer + position, option_value,
                    strlen(option_value));
            position+= strlen(option_value);
          }
          /* add a newline */
          strcat(file_buffer + position, NEWLINE);
          position+= strlen(NEWLINE);
        }
      }
      else
        is_found= FALSE; /* mark that this section is of no interest to us */
    }

  }

  if (my_chsize(fileno(cnf_file), position, 0, MYF(MY_WME)) ||
      my_fseek(cnf_file, 0, MY_SEEK_SET, MYF(0)) ||
      my_fwrite(cnf_file, file_buffer, position, MYF(MY_NABP)) ||
      my_fclose(cnf_file, MYF(MY_WME)))
     goto err;

  my_free(file_buffer, MYF(0));

  DBUG_RETURN(0);

err:
  my_free(file_buffer, MYF(0));
malloc_err:
  my_fclose(cnf_file, MYF(0));
  DBUG_RETURN(1); /* out of resources */
err_fopen:
  DBUG_RETURN(-1); /* cannot access the option file */
}

220

221 222 223 224
/*
  Process config files in default directories.

  SYNOPSIS
225
  my_search_option_files()
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
  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

  This function looks for config files in default directories. Then it
  travesrses each of the files and calls func to process each option.

  RETURN
    0  ok
    1  given cinf_file doesn't exist
*/

245
int my_search_option_files(const char *conf_file, int *argc, char ***argv,
246 247 248
                        uint *args_used, Process_option_func func,
                        void *func_ctx)
{
jimw@mysql.com's avatar
jimw@mysql.com committed
249
  const char **dirs, *forced_default_file, *forced_extra_defaults;
250
  int error= 0;
251
  DBUG_ENTER("my_search_option_files");
252 253

  /* Check if we want to force the use a specific default file */
hf@deer.(none)'s avatar
hf@deer.(none) committed
254
  get_defaults_files(*argc, *argv,
jimw@mysql.com's avatar
jimw@mysql.com committed
255 256
                      (char **)&forced_default_file,
                      (char **)&forced_extra_defaults);
hf@deer.(none)'s avatar
hf@deer.(none) committed
257 258
  if (forced_default_file)
    forced_default_file= strchr(forced_default_file,'=')+1;
jimw@mysql.com's avatar
jimw@mysql.com committed
259 260
  if (forced_extra_defaults)
    defaults_extra_file= strchr(forced_extra_defaults,'=')+1;
hf@deer.(none)'s avatar
hf@deer.(none) committed
261

262 263
  (*args_used)+= (forced_default_file ? 1 : 0) +
                 (forced_extra_defaults ? 1 : 0);
264 265 266

  if (forced_default_file)
  {
monty@mysql.com's avatar
monty@mysql.com committed
267
    if ((error= search_default_file_with_ext(func, func_ctx, "", "",
petr@mysql.com's avatar
petr@mysql.com committed
268
                                             forced_default_file, 0)) < 0)
269 270 271 272 273 274 275 276 277 278
      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
279
    if ((error= search_default_file(func, func_ctx, NullS, conf_file)) < 0)
280 281 282 283 284 285 286 287
      goto err;
  }
  else
  {
    for (dirs= default_directories ; *dirs; dirs++)
    {
      if (**dirs)
      {
monty@mysql.com's avatar
monty@mysql.com committed
288
	if (search_default_file(func, func_ctx, *dirs, conf_file) < 0)
289 290 291 292
	  goto err;
      }
      else if (defaults_extra_file)
      {
293 294
        if ((error= search_default_file_with_ext(func, func_ctx, "", "",
                                                defaults_extra_file, 0)) < 0)
295
	  goto err;				/* Fatal error */
296 297 298 299 300 301
        if (error > 0)
        {
          fprintf(stderr, "Could not open required defaults file: %s\n",
                  defaults_extra_file);
          goto err;
        }
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
      }
    }
  }

  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
319 320
    handle_deault_option()
    in_ctx                  Handler context. In this case it is a
321
                            handle_option_ctx structure.
monty@mysql.com's avatar
monty@mysql.com committed
322 323
    group_name              The name of the group the option belongs to.
    option                  The very option to be processed. It is already
324 325
                            prepared to be used in argv (has -- prefix)

326
  DESCRIPTION
monty@mysql.com's avatar
monty@mysql.com committed
327 328 329 330
    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.
331

332 333 334 335 336 337
  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
338
                                 const char *option)
339 340
{
  char *tmp;
monty@mysql.com's avatar
monty@mysql.com committed
341 342 343
  struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;

  if (find_type((char *)group_name, ctx->group, 3))
344
  {
345
    if (!(tmp= alloc_root(ctx->alloc, (uint) strlen(option) + 1)))
346 347 348 349 350 351 352 353 354 355
      return 1;
    if (insert_dynamic(ctx->args, (gptr) &tmp))
      return 1;
    strmov(tmp, option);
  }

  return 0;
}


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
/*
  Gets --defaults-file and --defaults-extra-file options from command line.

  SYNOPSIS
    get_defaults_files()
    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
    defaults and extra_defaults will be set to appropriate items
    of argv array, or to NULL if there are no such options
*/

void get_defaults_files(int argc, char **argv,
                        char **defaults, char **extra_defaults)
{
  *defaults=0;
  *extra_defaults=0;
  if (argc >= 2)
  {
    if (is_prefix(argv[1],"--defaults-file="))
      *defaults= argv[1];
    else if (is_prefix(argv[1],"--defaults-extra-file="))
      *extra_defaults= argv[1];
  }
}


386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
/*
  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
*/


417
int load_defaults(const char *conf_file, const char **groups,
monty@mysql.com's avatar
monty@mysql.com committed
418
                  int *argc, char ***argv)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
419 420 421 422
{
  DYNAMIC_ARRAY args;
  TYPELIB group;
  my_bool found_print_defaults=0;
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
423
  uint args_used=0;
424
  int error= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
425 426
  MEM_ROOT alloc;
  char *ptr,**res;
427
  struct handle_option_ctx ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
428 429
  DBUG_ENTER("load_defaults");

430
  init_default_directories();
431
  init_alloc_root(&alloc,512,0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
432 433 434 435 436 437 438 439 440 441 442
  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
443
    res[i-1]=0;					/* End pointer */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
444 445 446
    (*argc)--;
    *argv=res;
    *(MEM_ROOT*) ptr= alloc;			/* Save alloc root for free */
447
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
448 449 450 451 452
  }

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
454 455 456
  for (; *groups ; groups++)
    group.count++;

457
  if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
458
    goto err;
459 460 461 462

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

464
  error= my_search_option_files(conf_file, argc, argv, &args_used,
465
                      handle_default_option, (void *) &ctx);
466 467 468 469
  /*
    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
470 471 472 473 474 475
  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 */
476
  res[0]= argv[0][0];  /* Name MUST be set, even by embedded library */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
477
  memcpy((gptr) (res+1), args.buffer, args.elements*sizeof(char*));
478
  /* Skip --defaults-file and --defaults-extra-file */
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
479 480
  (*argc)-= args_used;
  (*argv)+= args_used;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
481 482 483 484 485

  /* Check if we wan't to see the new argument list */
  if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults"))
  {
    found_print_defaults=1;
486
    --*argc; ++*argv;				/* skip argument */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
487 488
  }

489 490 491
  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
492 493 494 495 496 497 498 499 500 501 502 503 504 505
  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("");
506
    exit(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
507
  }
508
  DBUG_RETURN(error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
509 510

 err:
511
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
512
  exit(1);
monty@mysql.com's avatar
monty@mysql.com committed
513
  return 0;					/* Keep compiler happy */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
514 515 516 517 518 519 520
}


void free_defaults(char **argv)
{
  MEM_ROOT ptr;
  memcpy_fixed((char*) &ptr,(char *) argv - sizeof(ptr), sizeof(ptr));
521
  free_root(&ptr,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
522 523 524
}


monty@mysql.com's avatar
monty@mysql.com committed
525 526
static int search_default_file(Process_option_func opt_handler,
                               void *handler_ctx,
527
			       const char *dir,
monty@mysql.com's avatar
monty@mysql.com committed
528
			       const char *config_file)
529 530
{
  char **ext;
531 532 533
  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;
534

535
  for (ext= (char**) exts_to_use; *ext; *ext++)
536 537
  {
    int error;
monty@mysql.com's avatar
monty@mysql.com committed
538 539
    if ((error= search_default_file_with_ext(opt_handler, handler_ctx,
                                             dir, *ext,
petr@mysql.com's avatar
petr@mysql.com committed
540
					     config_file, 0)) < 0)
541 542 543 544 545 546
      return error;
  }
  return 0;
}


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 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/*
  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;
}


597
/*
598
  Open a configuration file (if exists) and read given options from it
599

600
  SYNOPSIS
601
    search_default_file_with_ext()
602 603 604 605
    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.
606 607
    dir				directory to read
    ext				Extension for configuration file
608
    config_file                 Name of configuration file
609
    group			groups to read
610 611
    recursion_level             the level of recursion, got while processing
                                "!include" or "!includedir"
612 613 614 615 616

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

monty@mysql.com's avatar
monty@mysql.com committed
619 620 621 622
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
623 624
                                        const char *config_file,
                                        int recursion_level)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
625
{
petr@mysql.com's avatar
petr@mysql.com committed
626
  char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
petr@mysql.com's avatar
petr@mysql.com committed
627
  char *value, option[4096], tmp[FN_REFLEN];
628 629 630
  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
631 632
  FILE *fp;
  uint line=0;
633
  my_bool found_group=0;
634 635 636
  uint i;
  MY_DIR *search_dir;
  FILEINFO *search_file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
637 638 639 640 641

  if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3)
    return 0;					/* Ignore wrong paths */
  if (dir)
  {
642
    end=convert_dirname(name, dir, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
643
    if (dir[0] == FN_HOMELIB)		/* Add . to filenames in home */
644 645
      *end++='.';
    strxmov(end,config_file,ext,NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
646 647 648 649 650
  }
  else
  {
    strmov(name,config_file);
  }
651
  fn_format(name,name,"","",4);
652
#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__)
653 654
  {
    MY_STAT stat_info;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
655
    if (!my_stat(name,&stat_info,MYF(0)))
656
      return 1;
657 658 659 660 661 662 663
    /*
      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
664
    {
665
      fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n",
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
666
              name);
667
      return 0;
serg@serg.mysql.com's avatar
serg@serg.mysql.com committed
668
    }
669 670
  }
#endif
671
  if (!(fp= my_fopen(name, O_RDONLY, MYF(0))))
672
    return 1;					/* Ignore wrong files */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
673

674
  while (fgets(buff, sizeof(buff) - 1, fp))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
675 676 677
  {
    line++;
    /* Ignore comment and empty lines */
678 679 680
    for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++)
    {}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
681 682
    if (*ptr == '#' || *ptr == ';' || !*ptr)
      continue;
683 684

    /* Configuration File Directives */
monty@mysql.com's avatar
monty@mysql.com committed
685
    if ((*ptr == '!'))
686
    {
monty@mysql.com's avatar
monty@mysql.com committed
687 688 689 690 691 692 693 694 695 696 697 698 699 700
      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;
      }

701 702 703 704
      /* skip over `!' and following whitespace */
      for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++)
      {}

monty@mysql.com's avatar
monty@mysql.com committed
705 706 707
      if ((!strncmp(ptr, includedir_keyword,
                    sizeof(includedir_keyword) - 1)) &&
          my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1]))
708
      {
709 710 711 712
	if (!(ptr= get_argument(includedir_keyword,
                                sizeof(includedir_keyword),
                                ptr, name, line)))
	  goto err;
713 714 715 716 717 718 719 720 721

        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
722
          /* check extension */
723 724 725 726 727 728 729 730 731 732 733
          for (tmp_ext= (char**) f_extensions; *tmp_ext; *tmp_ext++)
          {
            if (!strcmp(ext, *tmp_ext))
              break;
          }

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

petr@mysql.com's avatar
petr@mysql.com committed
734
            search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp,
735 736 737 738 739 740
                                         recursion_level + 1);
          }
        }

        my_dirend(search_dir);
      }
monty@mysql.com's avatar
monty@mysql.com committed
741 742
      else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
               my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1]))
743
      {
744 745 746 747
	if (!(ptr= get_argument(include_keyword,
                                sizeof(include_keyword), ptr,
                                name, line)))
	  goto err;
748

petr@mysql.com's avatar
petr@mysql.com committed
749
        search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
750 751 752 753 754 755
                                     recursion_level + 1);
      }

      continue;
    }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
756 757 758 759 760 761 762 763 764 765
    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;
      }
766
      for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;/* Remove end space */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
767
      end[0]=0;
768 769

      strnmov(curr_gr, ptr, min((uint) (end-ptr)+1, 4096));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
770 771 772 773 774 775 776 777 778
      continue;
    }
    if (!found_group)
    {
      fprintf(stderr,
	      "error: Found option without preceding group in config file: %s at line: %d\n",
	      name,line);
      goto err;
    }
779 780
    
   
781 782 783
    end= remove_end_comment(ptr);
    if ((value= strchr(ptr, '=')))
      end= value;				/* Option without argument */
784
    for ( ; my_isspace(&my_charset_latin1,end[-1]) ; end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
785 786
    if (!value)
    {
787 788 789
      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
790 791 792 793 794
    }
    else
    {
      /* Remove pre- and end space */
      char *value_end;
795
      for (value++ ; my_isspace(&my_charset_latin1,*value); value++) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
796
      value_end=strend(value);
797 798 799 800
      /*
	We don't have to test for value_end >= value as we know there is
	an '=' before
      */
801
      for ( ; my_isspace(&my_charset_latin1,value_end[-1]) ; value_end--) ;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
802 803
      if (value_end < value)			/* Empty string */
	value_end=value;
804 805 806 807 808 809 810

      /* remove quotes around argument */
      if ((*value == '\"' || *value == '\'') && *value == value_end[-1])
      {
	value++;
	value_end--;
      }
811
      ptr=strnmov(strmov(option,"--"),ptr,(uint) (end-ptr));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
812
      *ptr++= '=';
813

bk@work.mysql.com's avatar
bk@work.mysql.com committed
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
      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;
834 835 836 837 838 839
	  case '\"':
	    *ptr++= '\"';
	    break;
	  case '\'':
	    *ptr++= '\'';
	    break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
840 841 842 843 844 845 846 847 848 849 850 851 852
	  case '\\':
	    *ptr++= '\\';
	    break;
	  default:				/* Unknown; Keep '\' */
	    *ptr++= '\\';
	    *ptr++= *value;
	    break;
	  }
	}
	else
	  *ptr++= *value;
      }
      *ptr=0;
853 854
      if (opt_handler(handler_ctx, curr_gr, option))
        goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
855 856 857 858 859 860 861
    }
  }
  my_fclose(fp,MYF(0));
  return(0);

 err:
  my_fclose(fp,MYF(0));
862
  return -1;					/* Fatal error */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
863 864 865
}


866 867
static char *remove_end_comment(char *ptr)
{
868 869
  char quote= 0;	/* we are inside quote marks */
  char escape= 0;	/* symbol is protected by escape chagacter */
870 871 872

  for (; *ptr; ptr++)
  {
873
    if ((*ptr == '\'' || *ptr == '\"') && !escape)
874 875 876 877 878 879
    {
      if (!quote)
	quote= *ptr;
      else if (quote == *ptr)
	quote= 0;
    }
monty@mysql.com's avatar
monty@mysql.com committed
880
    /* We are not inside a string */
881
    if (!quote && *ptr == '#')
882 883 884 885
    {
      *ptr= 0;
      return ptr;
    }
886
    escape= (quote && *ptr == '\\' && !escape);
887 888 889 890
  }
  return ptr;
}

monty@mysql.com's avatar
monty@mysql.com committed
891
#include <help_start.h>
892

893
void my_print_default_files(const char *conf_file)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
894
{
895
  const char *empty_list[]= { "", 0 };
896
  my_bool have_ext= fn_ext(conf_file)[0] != 0;
897
  const char **exts_to_use= have_ext ? empty_list : f_extensions;
898
  char name[FN_REFLEN], **ext;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
899
  const char **dirs;
900

901
  init_default_directories();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
902 903 904 905 906 907 908 909
  puts("\nDefault options are read from the following files in the given order:");

  if (dirname_length(conf_file))
    fputs(conf_file,stdout);
  else
  {
    for (dirs=default_directories ; *dirs; dirs++)
    {
910
      for (ext= (char**) exts_to_use; *ext; *ext++)
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
      {
	const char *pos;
	char *end;
	if (**dirs)
	  pos= *dirs;
	else if (defaults_extra_file)
	  pos= 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);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
926 927 928
    }
    puts("");
  }
929 930 931 932 933 934
}

void print_defaults(const char *conf_file, const char **groups)
{
  my_print_default_files(conf_file);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
935 936 937 938 939 940 941 942 943
  fputs("The following groups are read:",stdout);
  for ( ; *groups ; groups++)
  {
    fputc(' ',stdout);
    fputs(*groups,stdout);
  }
  puts("\nThe following options may be given as the first argument:\n\
--print-defaults	Print the program argument list and exit\n\
--no-defaults		Don't read default options from any options file\n\
944 945
--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
946
}
monty@mysql.com's avatar
monty@mysql.com committed
947 948

#include <help_end.h>
949

950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980

/*
  Create the list of default directories.

  On Microsoft Windows, this is:
    1. C:/
    2. GetWindowsDirectory()
    3. GetSystemWindowsDirectory()
    4. getenv(DEFAULT_HOME_ENV)
    5. ""

  On Novell NetWare, this is:
    1. sys:/etc/
    2. getenv(DEFAULT_HOME_ENV)
    3. ""

  On OS/2, this is:
    1. getenv(ETC)
    2. /etc/
    3. getenv(DEFAULT_HOME_ENV)
    4. ""
    5. "~/"

  Everywhere else, this is:
    1. /etc/
    2. getenv(DEFAULT_HOME_ENV)
    3. ""
    4. "~/"

 */

981 982 983 984 985 986
static void init_default_directories()
{
  const char *env, **ptr= default_directories;

#ifdef __WIN__
  *ptr++= "C:/";
987 988 989 990 991 992 993 994

  if (GetWindowsDirectory(system_dir,sizeof(system_dir)))
    *ptr++= &system_dir;
  /* Only add shared system directory if different from default. */
  if (GetSystemWindowsDirectory(shared_system_dir,sizeof(shared_system_dir)) &&
      strcmp(system_dir, shared_system_dir))
    *ptr++= &shared_system_dir;

995 996 997
#elif defined(__NETWARE__)
  *ptr++= "sys:/etc/";
#else
998 999 1000 1001
#if defined(__EMX__) || defined(OS2)
  if ((env= getenv("ETC")))
    *ptr++= env;
#endif
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
  *ptr++= "/etc/";
#endif
  if ((env= getenv(STRINGIFY_ARG(DEFAULT_HOME_ENV))))
    *ptr++= env;
  *ptr++= "";			/* Place for defaults_extra_file */
#if !defined(__WIN__) && !defined(__NETWARE__)
  *ptr++= "~/";;
#endif
  *ptr= 0;			/* end marker */
}