~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/internal/default.cc

  • Committer: Monty Taylor
  • Date: 2011-02-13 17:26:39 UTC
  • mfrom: (2157.2.2 give-in-to-pkg-config)
  • mto: This revision was merged to the branch mainline in revision 2166.
  • Revision ID: mordred@inaugust.com-20110213172639-nhy7i72sfhoq13ms
Merged in pkg-config fixes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000-2003 MySQL AB
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
 
15
 
 
16
/****************************************************************************
 
17
 Add all options from files named "group".cnf from the default_directories
 
18
 before the command line arguments.
 
19
 On Windows defaults will also search in the Windows directory for a file
 
20
 called 'group'.ini
 
21
 As long as the program uses the last argument for conflicting
 
22
 options one only have to add a call to "load_defaults" to enable
 
23
 use of default values.
 
24
 pre- and end 'blank space' are removed from options and values. The
 
25
 following escape sequences are recognized in values:  \b \t \n \r \\
 
26
 
 
27
 The following arguments are handled automaticly;  If used, they must be
 
28
 first argument on the command line!
 
29
 --no-defaults  ; no options are read.
 
30
 --defaults-file=full-path-to-default-file      ; Only this file will be read.
 
31
 --defaults-extra-file=full-path-to-default-file ; Read this file before ~/
 
32
 --defaults-group-suffix  ; Also read groups with concat(group, suffix)
 
33
 --print-defaults         ; Print the modified command line and exit
 
34
****************************************************************************/
 
35
 
 
36
#include "config.h"
 
37
 
 
38
#include "drizzled/internal/my_sys.h"
 
39
#include "drizzled/internal/m_string.h"
 
40
#include "drizzled/charset_info.h"
 
41
#include "drizzled/typelib.h"
 
42
#include <drizzled/configmake.h>
 
43
#include <drizzled/gettext.h>
 
44
 
 
45
#include "drizzled/cached_directory.h"
 
46
 
 
47
#ifdef HAVE_SYS_STAT_H
 
48
# include <sys/stat.h>
 
49
#endif
 
50
 
 
51
#include <cstdio>
 
52
#include <algorithm>
 
53
 
 
54
using namespace std;
 
55
 
 
56
namespace drizzled
 
57
{
 
58
namespace internal
 
59
{
 
60
 
 
61
const char *my_defaults_file=0;
 
62
const char *my_defaults_group_suffix=0;
 
63
char *my_defaults_extra_file=0;
 
64
 
 
65
/* Which directories are searched for options (and in which order) */
 
66
 
 
67
#define MAX_DEFAULT_DIRS 6
 
68
const char *default_directories[MAX_DEFAULT_DIRS + 1];
 
69
 
 
70
static const char *f_extensions[]= { ".cnf", 0 };
 
71
 
 
72
int handle_default_option(void *in_ctx, const char *group_name,
 
73
                          const char *option);
 
74
 
 
75
/*
 
76
   This structure defines the context that we pass to callback
 
77
   function 'handle_default_option' used in search_default_file
 
78
   to process each option. This context is used if search_default_file
 
79
   was called from load_defaults.
 
80
*/
 
81
 
 
82
struct handle_option_ctx
 
83
{
 
84
   memory::Root *alloc;
 
85
   DYNAMIC_ARRAY *args;
 
86
   TYPELIB *group;
 
87
};
 
88
 
 
89
static int search_default_file(Process_option_func func, void *func_ctx,
 
90
                               const char *dir, const char *config_file);
 
91
static int search_default_file_with_ext(Process_option_func func,
 
92
                                        void *func_ctx,
 
93
                                        const char *dir, const char *ext,
 
94
                                        const char *config_file, int recursion_level);
 
95
 
 
96
 
 
97
 
 
98
/**
 
99
  Create the list of default directories.
 
100
 
 
101
  @details
 
102
  On all systems, if a directory is already in the list, it will be moved
 
103
  to the end of the list.  This avoids reading defaults files multiple times,
 
104
  while ensuring the correct precedence.
 
105
 
 
106
  @return void
 
107
*/
 
108
 
 
109
static void init_default_directories(void);
 
110
 
 
111
 
 
112
static char *remove_end_comment(char *ptr);
 
113
 
 
114
 
 
115
/*
 
116
  Process config files in default directories.
 
117
 
 
118
  SYNOPSIS
 
119
  my_search_option_files()
 
120
  conf_file                   Basename for configuration file to search for.
 
121
                              If this is a path, then only this file is read.
 
122
  argc                        Pointer to argc of original program
 
123
  argv                        Pointer to argv of original program
 
124
  args_used                   Pointer to variable for storing the number of
 
125
                              arguments used.
 
126
  func                        Pointer to the function to process options
 
127
  func_ctx                    It's context. Usually it is the structure to
 
128
                              store additional options.
 
129
  DESCRIPTION
 
130
    Process the default options from argc & argv
 
131
    Read through each found config file looks and calls 'func' to process
 
132
    each option.
 
133
 
 
134
  NOTES
 
135
    --defaults-group-suffix is only processed if we are called from
 
136
    load_defaults().
 
137
 
 
138
 
 
139
  RETURN
 
140
    0  ok
 
141
    1  given cinf_file doesn't exist
 
142
 
 
143
    The global variable 'my_defaults_group_suffix' is updated with value for
 
144
    --defaults_group_suffix
 
145
*/
 
146
 
 
147
int my_search_option_files(const char *conf_file, int *argc, char ***argv,
 
148
                           uint32_t *args_used, Process_option_func func,
 
149
                           void *func_ctx)
 
150
{
 
151
  const char **dirs, *forced_default_file, *forced_extra_defaults;
 
152
  int error= 0;
 
153
 
 
154
  /* Check if we want to force the use a specific default file */
 
155
  *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used,
 
156
                                    (char **) &forced_default_file,
 
157
                                    (char **) &forced_extra_defaults,
 
158
                                    (char **) &my_defaults_group_suffix);
 
159
 
 
160
  if (! my_defaults_group_suffix)
 
161
    my_defaults_group_suffix= getenv("DRIZZLE_GROUP_SUFFIX");
 
162
 
 
163
  if (forced_extra_defaults)
 
164
    my_defaults_extra_file= (char *) forced_extra_defaults;
 
165
 
 
166
  if (forced_default_file)
 
167
    my_defaults_file= forced_default_file;
 
168
 
 
169
  /*
 
170
    We can only handle 'defaults-group-suffix' if we are called from
 
171
    load_defaults() as otherwise we can't know the type of 'func_ctx'
 
172
  */
 
173
 
 
174
  if (my_defaults_group_suffix && (func == handle_default_option))
 
175
  {
 
176
    /* Handle --defaults-group-suffix= */
 
177
    uint32_t i;
 
178
    const char **extra_groups;
 
179
    const size_t instance_len= strlen(my_defaults_group_suffix);
 
180
    struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx;
 
181
    char *ptr;
 
182
    TYPELIB *group= ctx->group;
 
183
 
 
184
    if (!(extra_groups=
 
185
          (const char**)ctx->alloc->alloc_root(
 
186
                                   (2*group->count+1)*sizeof(char*))))
 
187
      goto err;
 
188
 
 
189
    for (i= 0; i < group->count; i++)
 
190
    {
 
191
      size_t len;
 
192
      extra_groups[i]= group->type_names[i]; /** copy group */
 
193
 
 
194
      len= strlen(extra_groups[i]);
 
195
      if (!(ptr= (char *)ctx->alloc->alloc_root( len+instance_len+1)))
 
196
        goto err;
 
197
 
 
198
      extra_groups[i+group->count]= ptr;
 
199
 
 
200
      /** Construct new group */
 
201
      memcpy(ptr, extra_groups[i], len);
 
202
      memcpy(ptr+len, my_defaults_group_suffix, instance_len+1);
 
203
    }
 
204
 
 
205
    group->count*= 2;
 
206
    group->type_names= extra_groups;
 
207
    group->type_names[group->count]= 0;
 
208
  }
 
209
 
 
210
  if (forced_default_file)
 
211
  {
 
212
    if ((error= search_default_file_with_ext(func, func_ctx, "", "",
 
213
                                             forced_default_file, 0)) < 0)
 
214
      goto err;
 
215
    if (error > 0)
 
216
    {
 
217
      fprintf(stderr, "Could not open required defaults file: %s\n",
 
218
              forced_default_file);
 
219
      goto err;
 
220
    }
 
221
  }
 
222
  else if (dirname_length(conf_file))
 
223
  {
 
224
    if ((error= search_default_file(func, func_ctx, NULL, conf_file)) < 0)
 
225
      goto err;
 
226
  }
 
227
  else
 
228
  {
 
229
    for (dirs= default_directories ; *dirs; dirs++)
 
230
    {
 
231
      if (**dirs)
 
232
      {
 
233
        if (search_default_file(func, func_ctx, *dirs, conf_file) < 0)
 
234
          goto err;
 
235
      }
 
236
      else if (my_defaults_extra_file)
 
237
      {
 
238
        if ((error= search_default_file_with_ext(func, func_ctx, "", "",
 
239
                                                my_defaults_extra_file, 0)) < 0)
 
240
          goto err;                             /* Fatal error */
 
241
        if (error > 0)
 
242
        {
 
243
          fprintf(stderr, "Could not open required defaults file: %s\n",
 
244
                  my_defaults_extra_file);
 
245
          goto err;
 
246
        }
 
247
      }
 
248
    }
 
249
  }
 
250
 
 
251
  return(error);
 
252
 
 
253
err:
 
254
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
 
255
  exit(1);
 
256
}
 
257
 
 
258
 
 
259
/*
 
260
  The option handler for load_defaults.
 
261
 
 
262
  SYNOPSIS
 
263
    handle_deault_option()
 
264
    in_ctx                  Handler context. In this case it is a
 
265
                            handle_option_ctx structure.
 
266
    group_name              The name of the group the option belongs to.
 
267
    option                  The very option to be processed. It is already
 
268
                            prepared to be used in argv (has -- prefix). If it
 
269
                            is NULL, we are handling a new group (section).
 
270
 
 
271
  DESCRIPTION
 
272
    This handler checks whether a group is one of the listed and adds an option
 
273
    to the array if yes. Some other handler can record, for instance, all
 
274
    groups and their options, not knowing in advance the names and amount of
 
275
    groups.
 
276
 
 
277
  RETURN
 
278
    0 - ok
 
279
    1 - error occured
 
280
*/
 
281
 
 
282
int handle_default_option(void *in_ctx, const char *group_name,
 
283
                          const char *option)
 
284
{
 
285
  char *tmp;
 
286
  struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx;
 
287
 
 
288
  if (!option)
 
289
    return 0;
 
290
 
 
291
  if (ctx->group->find_type(const_cast<char*>(group_name), 3))
 
292
  {
 
293
    if (!(tmp= (char *)ctx->alloc->alloc_root(strlen(option) + 1)))
 
294
      return 1;
 
295
    if (insert_dynamic(ctx->args, (unsigned char*) &tmp))
 
296
      return 1;
 
297
    strcpy(tmp, option);
 
298
  }
 
299
 
 
300
  return 0;
 
301
}
 
302
 
 
303
 
 
304
/*
 
305
  Gets options from the command line
 
306
 
 
307
  SYNOPSIS
 
308
    get_defaults_options()
 
309
    argc                        Pointer to argc of original program
 
310
    argv                        Pointer to argv of original program
 
311
    defaults                    --defaults-file option
 
312
    extra_defaults              --defaults-extra-file option
 
313
 
 
314
  RETURN
 
315
    # Number of arguments used from *argv
 
316
      defaults and extra_defaults will be set to option of the appropriate
 
317
      items of argv array, or to NULL if there are no such options
 
318
*/
 
319
 
 
320
int get_defaults_options(int argc, char **argv,
 
321
                         char **defaults,
 
322
                         char **extra_defaults,
 
323
                         char **group_suffix)
 
324
{
 
325
  int org_argc= argc, prev_argc= 0;
 
326
  *defaults= *extra_defaults= *group_suffix= 0;
 
327
 
 
328
  const std::string DEFAULTS_FILE("--defaults-file=");
 
329
  const std::string DEFAULTS_EXTRA_FILE("--defaults-extra-file=");
 
330
  const std::string DEFAULTS_GROUP_SUFFIX("--defaults-group-suffix=");
 
331
 
 
332
  while (argc >= 2 && argc != prev_argc)
 
333
  {
 
334
    /* Skip program name or previously handled argument */
 
335
    argv++;
 
336
    prev_argc= argc;                            /* To check if we found */
 
337
    if (!*defaults && (strncmp(*argv,
 
338
                               DEFAULTS_FILE.c_str(),
 
339
                               DEFAULTS_FILE.size()) == 0))
 
340
    {
 
341
      *defaults= *argv + DEFAULTS_FILE.size();
 
342
       argc--;
 
343
       continue;
 
344
    }
 
345
    if (!*extra_defaults && (strncmp(*argv, 
 
346
                                     DEFAULTS_EXTRA_FILE.c_str(),
 
347
                                     DEFAULTS_EXTRA_FILE.size()) == 0))
 
348
    {
 
349
      *extra_defaults= *argv + DEFAULTS_EXTRA_FILE.size();
 
350
      argc--;
 
351
      continue;
 
352
    }
 
353
    if (!*group_suffix && (strncmp(*argv, 
 
354
                                   DEFAULTS_GROUP_SUFFIX.c_str(),
 
355
                                   DEFAULTS_GROUP_SUFFIX.size()) == 0))
 
356
 
 
357
    {
 
358
      *group_suffix= *argv + DEFAULTS_GROUP_SUFFIX.size();
 
359
      argc--;
 
360
      continue;
 
361
    }
 
362
  }
 
363
  return org_argc - argc;
 
364
}
 
365
 
 
366
 
 
367
/*
 
368
  Read options from configurations files
 
369
 
 
370
  SYNOPSIS
 
371
    load_defaults()
 
372
    conf_file                   Basename for configuration file to search for.
 
373
                                If this is a path, then only this file is read.
 
374
    groups                      Which [group] entrys to read.
 
375
                                Points to an null terminated array of pointers
 
376
    argc                        Pointer to argc of original program
 
377
    argv                        Pointer to argv of original program
 
378
 
 
379
  IMPLEMENTATION
 
380
 
 
381
   Read options from configuration files and put them BEFORE the arguments
 
382
   that are already in argc and argv.  This way the calling program can
 
383
   easily command line options override options in configuration files
 
384
 
 
385
   NOTES
 
386
    In case of fatal error, the function will print a warning and do
 
387
    exit(1)
 
388
 
 
389
    To free used memory one should call free_defaults() with the argument
 
390
    that was put in *argv
 
391
 
 
392
   RETURN
 
393
     0  ok
 
394
     1  The given conf_file didn't exists
 
395
*/
 
396
 
 
397
 
 
398
int load_defaults(const char *conf_file, const char **groups,
 
399
                  int *argc, char ***argv)
 
400
{
 
401
  DYNAMIC_ARRAY args;
 
402
  TYPELIB group;
 
403
  uint32_t args_used= 0;
 
404
  int error= 0;
 
405
  memory::Root alloc(512);
 
406
  char *ptr,**res;
 
407
  struct handle_option_ctx ctx;
 
408
 
 
409
  init_default_directories();
 
410
  /*
 
411
    Check if the user doesn't want any default option processing
 
412
    --no-defaults is always the first option
 
413
  */
 
414
  if (*argc >= 2 && !strcmp(argv[0][1],"--no-defaults"))
 
415
  {
 
416
    /* remove the --no-defaults argument and return only the other arguments */
 
417
    uint32_t i;
 
418
    if (!(ptr=(char*) alloc.alloc_root(sizeof(alloc)+ (*argc + 1)*sizeof(char*))))
 
419
      goto err;
 
420
    res= (char**) (ptr+sizeof(alloc));
 
421
    memset(res,0,(*argc + 1));
 
422
    res[0]= **argv;                             /* Copy program name */
 
423
    for (i=2 ; i < (uint32_t) *argc ; i++)
 
424
      res[i-1]=argv[0][i];
 
425
    res[i-1]=0;                                 /* End pointer */
 
426
    (*argc)--;
 
427
    *argv=res;
 
428
    *(memory::Root*) ptr= alloc;                        /* Save alloc root for free */
 
429
    return(0);
 
430
  }
 
431
 
 
432
  group.count=0;
 
433
  group.name= "defaults";
 
434
  group.type_names= groups;
 
435
 
 
436
  for (; *groups ; groups++)
 
437
    group.count++;
 
438
 
 
439
  if (my_init_dynamic_array(&args, sizeof(char*),*argc, 32))
 
440
    goto err;
 
441
 
 
442
  ctx.alloc= &alloc;
 
443
  ctx.args= &args;
 
444
  ctx.group= &group;
 
445
 
 
446
  error= my_search_option_files(conf_file, argc, argv, &args_used,
 
447
                                handle_default_option, (void *) &ctx);
 
448
  /*
 
449
    Here error contains <> 0 only if we have a fully specified conf_file
 
450
    or a forced default file
 
451
  */
 
452
  if (!(ptr=(char*) alloc.alloc_root(sizeof(alloc)+ (args.elements + *argc +1) *sizeof(char*))))
 
453
    goto err;
 
454
  res= (char**) (ptr+sizeof(alloc));
 
455
 
 
456
  /* copy name + found arguments + command line arguments to new array */
 
457
  res[0]= argv[0][0];  /* Name MUST be set, even by embedded library */
 
458
  memcpy(res+1, args.buffer, args.elements*sizeof(char*));
 
459
  /* Skip --defaults-xxx options */
 
460
  (*argc)-= args_used;
 
461
  (*argv)+= args_used;
 
462
 
 
463
  /*
 
464
    Check if we wan't to see the new argument list
 
465
    This options must always be the last of the default options
 
466
  */
 
467
  if (*argc)
 
468
    memcpy(res+1+args.elements, *argv + 1, (*argc-1)*sizeof(char*));
 
469
  res[args.elements+ *argc]=0;                  /* last null */
 
470
 
 
471
  (*argc)+=int(args.elements);
 
472
  *argv= static_cast<char**>(res);
 
473
  *(memory::Root*) ptr= alloc;                  /* Save alloc root for free */
 
474
  delete_dynamic(&args);
 
475
 
 
476
  return(error);
 
477
 
 
478
 err:
 
479
  fprintf(stderr,"Fatal error in defaults handling. Program aborted\n");
 
480
  exit(1);
 
481
}
 
482
 
 
483
 
 
484
void free_defaults(char **argv)
 
485
{
 
486
  memory::Root ptr;
 
487
  memcpy(&ptr, (char*) argv - sizeof(ptr), sizeof(ptr));
 
488
  ptr.free_root(MYF(0));
 
489
}
 
490
 
 
491
 
 
492
static int search_default_file(Process_option_func opt_handler,
 
493
                               void *handler_ctx,
 
494
                               const char *dir,
 
495
                               const char *config_file)
 
496
{
 
497
  char **ext;
 
498
  const char *empty_list[]= { "", 0 };
 
499
  bool have_ext= fn_ext(config_file)[0] != 0;
 
500
  const char **exts_to_use= have_ext ? empty_list : f_extensions;
 
501
 
 
502
  for (ext= (char**) exts_to_use; *ext; ext++)
 
503
  {
 
504
    int error;
 
505
    if ((error= search_default_file_with_ext(opt_handler, handler_ctx,
 
506
                                             dir, *ext,
 
507
                                             config_file, 0)) < 0)
 
508
      return error;
 
509
  }
 
510
  return 0;
 
511
}
 
512
 
 
513
 
 
514
/*
 
515
  Skip over keyword and get argument after keyword
 
516
 
 
517
  SYNOPSIS
 
518
   get_argument()
 
519
   keyword              Include directive keyword
 
520
   kwlen                Length of keyword
 
521
   ptr                  Pointer to the keword in the line under process
 
522
   line                 line number
 
523
 
 
524
  RETURN
 
525
   0    error
 
526
   #    Returns pointer to the argument after the keyword.
 
527
*/
 
528
 
 
529
static char *get_argument(const char *keyword, size_t kwlen,
 
530
                          char *ptr, char *name, uint32_t line)
 
531
{
 
532
  char *end;
 
533
 
 
534
  /* Skip over "include / includedir keyword" and following whitespace */
 
535
 
 
536
  for (ptr+= kwlen - 1;
 
537
       my_isspace(&my_charset_utf8_general_ci, ptr[0]);
 
538
       ptr++)
 
539
  {}
 
540
 
 
541
  /*
 
542
    Trim trailing whitespace from directory name
 
543
    The -1 below is for the newline added by fgets()
 
544
    Note that my_isspace() is true for \r and \n
 
545
  */
 
546
  for (end= ptr + strlen(ptr) - 1;
 
547
       my_isspace(&my_charset_utf8_general_ci, *(end - 1));
 
548
       end--)
 
549
  {}
 
550
  end[0]= 0;                                    /* Cut off end space */
 
551
 
 
552
  /* Print error msg if there is nothing after !include* directive */
 
553
  if (end <= ptr)
 
554
  {
 
555
    fprintf(stderr,
 
556
            "error: Wrong '!%s' directive in config file: %s at line %d\n",
 
557
            keyword, name, line);
 
558
    return 0;
 
559
  }
 
560
  return ptr;
 
561
}
 
562
 
 
563
 
 
564
/*
 
565
  Open a configuration file (if exists) and read given options from it
 
566
 
 
567
  SYNOPSIS
 
568
    search_default_file_with_ext()
 
569
    opt_handler                 Option handler function. It is used to process
 
570
                                every separate option.
 
571
    handler_ctx                 Pointer to the structure to store actual
 
572
                                parameters of the function.
 
573
    dir                         directory to read
 
574
    ext                         Extension for configuration file
 
575
    config_file                 Name of configuration file
 
576
    group                       groups to read
 
577
    recursion_level             the level of recursion, got while processing
 
578
                                "!include" or "!includedir"
 
579
 
 
580
  RETURN
 
581
    0   Success
 
582
    -1  Fatal error, abort
 
583
     1  File not found (Warning)
 
584
*/
 
585
 
 
586
static int search_default_file_with_ext(Process_option_func opt_handler,
 
587
                                        void *handler_ctx,
 
588
                                        const char *dir,
 
589
                                        const char *ext,
 
590
                                        const char *config_file,
 
591
                                        int recursion_level)
 
592
{
 
593
  char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext;
 
594
  char *value, option[4096], tmp[FN_REFLEN];
 
595
  static const char includedir_keyword[]= "includedir";
 
596
  static const char include_keyword[]= "include";
 
597
  const int max_recursion_level= 10;
 
598
  FILE *fp;
 
599
  uint32_t line=0;
 
600
  bool found_group=0;
 
601
 
 
602
  if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3)
 
603
    return 0;                                   /* Ignore wrong paths */
 
604
  if (dir)
 
605
  {
 
606
    end=convert_dirname(name, dir, NULL);
 
607
    if (dir[0] == FN_HOMELIB)           /* Add . to filenames in home */
 
608
      *end++='.';
 
609
    sprintf(end,"%s%s",config_file,ext);
 
610
  }
 
611
  else
 
612
  {
 
613
    strcpy(name,config_file);
 
614
  }
 
615
  fn_format(name,name,"","",4);
 
616
  {
 
617
    struct stat stat_info;
 
618
    if (stat(name,&stat_info))
 
619
      return 1;
 
620
    /*
 
621
      Ignore world-writable regular files.
 
622
      This is mainly done to protect us to not read a file created by
 
623
      the mysqld server, but the check is still valid in most context.
 
624
    */
 
625
    if ((stat_info.st_mode & S_IWOTH) &&
 
626
        (stat_info.st_mode & S_IFMT) == S_IFREG)
 
627
    {
 
628
      fprintf(stderr, "Warning: World-writable config file '%s' is ignored\n",
 
629
              name);
 
630
      return 0;
 
631
    }
 
632
  }
 
633
  if (!(fp= fopen(name, "r")))
 
634
    return 1;                                   /* Ignore wrong files */
 
635
 
 
636
  memset(buff,0,sizeof(buff));
 
637
  while (fgets(buff, sizeof(buff) - 1, fp))
 
638
  {
 
639
    line++;
 
640
    /* Ignore comment and empty lines */
 
641
    for (ptr= buff; my_isspace(&my_charset_utf8_general_ci, *ptr); ptr++)
 
642
    {}
 
643
 
 
644
    if (*ptr == '#' || *ptr == ';' || !*ptr)
 
645
      continue;
 
646
 
 
647
    /* Configuration File Directives */
 
648
    if ((*ptr == '!'))
 
649
    {
 
650
      if (recursion_level >= max_recursion_level)
 
651
      {
 
652
        for (end= ptr + strlen(ptr) - 1;
 
653
             my_isspace(&my_charset_utf8_general_ci, *(end - 1));
 
654
             end--)
 
655
        {}
 
656
        end[0]= 0;
 
657
        fprintf(stderr,
 
658
                "Warning: skipping '%s' directive as maximum include"
 
659
                "recursion level was reached in file %s at line %d\n",
 
660
                ptr, name, line);
 
661
        continue;
 
662
      }
 
663
 
 
664
      /* skip over `!' and following whitespace */
 
665
      for (++ptr; my_isspace(&my_charset_utf8_general_ci, ptr[0]); ptr++)
 
666
      {}
 
667
 
 
668
      if ((!strncmp(ptr, includedir_keyword,
 
669
                    sizeof(includedir_keyword) - 1)) &&
 
670
          my_isspace(&my_charset_utf8_general_ci, ptr[sizeof(includedir_keyword) - 1]))
 
671
      {
 
672
        if (!(ptr= get_argument(includedir_keyword,
 
673
                                sizeof(includedir_keyword),
 
674
                                ptr, name, line)))
 
675
          goto err;
 
676
 
 
677
        CachedDirectory dir_cache(ptr);
 
678
 
 
679
        if (dir_cache.fail())
 
680
        {
 
681
          /**
 
682
           * @todo
 
683
           * Since clients still use this code, we use fprintf here.
 
684
           * This fprintf needs to be turned into errmsg_printf
 
685
           * as soon as the client programs no longer use mysys
 
686
           * and can use the pluggable error message system.
 
687
           */
 
688
          fprintf(stderr, _("error: could not open directory: %s\n"), ptr);
 
689
          goto err;
 
690
        }
 
691
 
 
692
        CachedDirectory::Entries files= dir_cache.getEntries();
 
693
        CachedDirectory::Entries::iterator file_iter= files.begin();
 
694
 
 
695
        while (file_iter != files.end())
 
696
        {
 
697
          CachedDirectory::Entry *entry= *file_iter;
 
698
          ext= fn_ext(entry->filename.c_str());
 
699
 
 
700
          /* check extension */
 
701
          for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++)
 
702
          {
 
703
            if (!strcmp(ext, *tmp_ext))
 
704
            {
 
705
              fn_format(tmp, entry->filename.c_str(), ptr, "",
 
706
                        MY_UNPACK_FILENAME | MY_SAFE_PATH);
 
707
 
 
708
              search_default_file_with_ext(opt_handler, handler_ctx, "", "",
 
709
                                           tmp, recursion_level + 1);
 
710
            }
 
711
          }
 
712
 
 
713
          ++file_iter;
 
714
        }
 
715
      }
 
716
      else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) &&
 
717
               my_isspace(&my_charset_utf8_general_ci, ptr[sizeof(include_keyword)-1]))
 
718
      {
 
719
        if (!(ptr= get_argument(include_keyword,
 
720
                                sizeof(include_keyword), ptr,
 
721
                                name, line)))
 
722
          goto err;
 
723
 
 
724
        search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr,
 
725
                                     recursion_level + 1);
 
726
      }
 
727
 
 
728
      continue;
 
729
    }
 
730
 
 
731
    if (*ptr == '[')                            /* Group name */
 
732
    {
 
733
      found_group=1;
 
734
      if (!(end=(char *) strchr(++ptr,']')))
 
735
      {
 
736
        fprintf(stderr,
 
737
                "error: Wrong group definition in config file: %s at line %d\n",
 
738
                name,line);
 
739
        goto err;
 
740
      }
 
741
      /* Remove end space */
 
742
      for ( ; my_isspace(&my_charset_utf8_general_ci,end[-1]) ; end--) ;
 
743
      end[0]=0;
 
744
 
 
745
      strncpy(curr_gr, ptr, min((size_t) (end-ptr)+1, sizeof(curr_gr)-1));
 
746
      curr_gr[min((size_t)(end-ptr)+1, sizeof(curr_gr)-1)] = '\0';
 
747
 
 
748
      /* signal that a new group is found */
 
749
      opt_handler(handler_ctx, curr_gr, NULL);
 
750
 
 
751
      continue;
 
752
    }
 
753
    if (!found_group)
 
754
    {
 
755
      fprintf(stderr,
 
756
              "error: Found option without preceding group in config file: %s at line: %d\n",
 
757
              name,line);
 
758
      goto err;
 
759
    }
 
760
 
 
761
 
 
762
    end= remove_end_comment(ptr);
 
763
    if ((value= strchr(ptr, '=')))
 
764
      end= value;                               /* Option without argument */
 
765
    for ( ; my_isspace(&my_charset_utf8_general_ci,end[-1]) || end[-1]== '\n'; end--) ;
 
766
    if (!value)
 
767
    {
 
768
      strncpy(strcpy(option,"--")+2,ptr,strlen(ptr)+1);
 
769
      if (opt_handler(handler_ctx, curr_gr, option))
 
770
        goto err;
 
771
    }
 
772
    else
 
773
    {
 
774
      /* Remove pre- and end space */
 
775
      char *value_end;
 
776
      for (value++ ; my_isspace(&my_charset_utf8_general_ci,*value); value++) ;
 
777
      value_end= strchr(value, '\0');
 
778
      /*
 
779
       We don't have to test for value_end >= value as we know there is
 
780
       an '=' before
 
781
      */
 
782
      for ( ; my_isspace(&my_charset_utf8_general_ci,value_end[-1]) ; value_end--) ;
 
783
      if (value_end < value)                    /* Empty string */
 
784
        value_end=value;
 
785
 
 
786
      /* remove quotes around argument */
 
787
      if ((*value == '\"' || *value == '\'') && /* First char is quote */
 
788
          (value + 1 < value_end ) && /* String is longer than 1 */
 
789
          *value == value_end[-1] ) /* First char is equal to last char */
 
790
      {
 
791
        value++;
 
792
        value_end--;
 
793
      }
 
794
      
 
795
      memset(option,0,2+(size_t)(end-ptr)+1);
 
796
      ptr= strncpy(strcpy(option,"--")+2,ptr,(size_t) (end-ptr));
 
797
      ptr[end-ptr]= '\0';
 
798
      ptr+= strlen(ptr);
 
799
      *ptr++= '=';
 
800
 
 
801
      for ( ; value != value_end; value++)
 
802
      {
 
803
        if (*value == '\\' && value != value_end-1)
 
804
        {
 
805
          switch(*++value) {
 
806
          case 'n':
 
807
            *ptr++='\n';
 
808
            break;
 
809
          case 't':
 
810
            *ptr++= '\t';
 
811
            break;
 
812
          case 'r':
 
813
            *ptr++ = '\r';
 
814
            break;
 
815
          case 'b':
 
816
            *ptr++ = '\b';
 
817
            break;
 
818
          case 's':
 
819
            *ptr++= ' ';                        /* space */
 
820
            break;
 
821
          case '\"':
 
822
            *ptr++= '\"';
 
823
            break;
 
824
          case '\'':
 
825
            *ptr++= '\'';
 
826
            break;
 
827
          case '\\':
 
828
            *ptr++= '\\';
 
829
            break;
 
830
          default:                              /* Unknown; Keep '\' */
 
831
            *ptr++= '\\';
 
832
            *ptr++= *value;
 
833
            break;
 
834
          }
 
835
        }
 
836
        else
 
837
          *ptr++= *value;
 
838
      }
 
839
      *ptr=0;
 
840
      if (opt_handler(handler_ctx, curr_gr, option))
 
841
        goto err;
 
842
    }
 
843
  }
 
844
  fclose(fp);
 
845
  return(0);
 
846
 
 
847
 err:
 
848
  fclose(fp);
 
849
 
 
850
  return -1;                                    /* Fatal error */
 
851
}
 
852
 
 
853
 
 
854
static char *remove_end_comment(char *ptr)
 
855
{
 
856
  char quote= 0;        /* we are inside quote marks */
 
857
  char escape= 0;       /* symbol is protected by escape chagacter */
 
858
 
 
859
  for (; *ptr; ptr++)
 
860
  {
 
861
    if ((*ptr == '\'' || *ptr == '\"') && !escape)
 
862
    {
 
863
      if (!quote)
 
864
        quote= *ptr;
 
865
      else if (quote == *ptr)
 
866
        quote= 0;
 
867
    }
 
868
    /* We are not inside a string */
 
869
    if (!quote && *ptr == '#')
 
870
    {
 
871
      *ptr= 0;
 
872
      return ptr;
 
873
    }
 
874
    escape= (quote && *ptr == '\\' && !escape);
 
875
  }
 
876
  return ptr;
 
877
}
 
878
 
 
879
void my_print_default_files(const char *conf_file)
 
880
{
 
881
  const char *empty_list[]= { "", 0 };
 
882
  bool have_ext= fn_ext(conf_file)[0] != 0;
 
883
  const char **exts_to_use= have_ext ? empty_list : f_extensions;
 
884
  char name[FN_REFLEN], **ext;
 
885
  const char **dirs;
 
886
 
 
887
  init_default_directories();
 
888
  puts("\nDefault options are read from the following files in the given order:");
 
889
 
 
890
  if (dirname_length(conf_file))
 
891
    fputs(conf_file,stdout);
 
892
  else
 
893
  {
 
894
    for (dirs=default_directories ; *dirs; dirs++)
 
895
    {
 
896
      for (ext= (char**) exts_to_use; *ext; ext++)
 
897
      {
 
898
        const char *pos;
 
899
        char *end;
 
900
        if (**dirs)
 
901
          pos= *dirs;
 
902
        else if (my_defaults_extra_file)
 
903
          pos= my_defaults_extra_file;
 
904
        else
 
905
          continue;
 
906
        end= convert_dirname(name, pos, NULL);
 
907
        if (name[0] == FN_HOMELIB)      /* Add . to filenames in home */
 
908
          *end++='.';
 
909
  sprintf(end,"%s%s ",conf_file, *ext);
 
910
        fputs(name,stdout);
 
911
      }
 
912
    }
 
913
  }
 
914
  puts("");
 
915
}
 
916
 
 
917
void print_defaults(const char *conf_file, const char **groups)
 
918
{
 
919
  const char **groups_save= groups;
 
920
  my_print_default_files(conf_file);
 
921
 
 
922
  fputs("The following groups are read:",stdout);
 
923
  for ( ; *groups ; groups++)
 
924
  {
 
925
    fputc(' ',stdout);
 
926
    fputs(*groups,stdout);
 
927
  }
 
928
 
 
929
  if (my_defaults_group_suffix)
 
930
  {
 
931
    groups= groups_save;
 
932
    for ( ; *groups ; groups++)
 
933
    {
 
934
      fputc(' ',stdout);
 
935
      fputs(*groups,stdout);
 
936
      fputs(my_defaults_group_suffix,stdout);
 
937
    }
 
938
  }
 
939
  puts("\nThe following options may be given as the first argument:\n\
 
940
  --no-defaults         Don't read default options from any options file\n\
 
941
  --defaults-file=#     Only read default options from the given file #\n\
 
942
  --defaults-extra-file=# Read this file after the global files are read");
 
943
}
 
944
 
 
945
/*
 
946
  This extra complexity is to avoid declaring 'rc' if it won't be
 
947
  used.
 
948
*/
 
949
static void add_directory(const char* dir)
 
950
{
 
951
  array_append_string_unique(dir, default_directories, array_elements(default_directories));
 
952
}
 
953
 
 
954
static void add_common_directories()
 
955
{
 
956
  const char *env= getenv("DRIZZLE_HOME"); 
 
957
  if (env) 
 
958
    add_directory(env); 
 
959
  // Placeholder for --defaults-extra-file=<path>
 
960
  add_directory(""); 
 
961
}
 
962
 
 
963
/**
 
964
  Initialize default directories for Unix
 
965
 
 
966
  @details
 
967
    1. /etc/
 
968
    2. /etc/drizzle/
 
969
    3. --sysconfdir=<path> (compile-time option)
 
970
    4. getenv("DRIZZLE_HOME")
 
971
    5. --defaults-extra-file=<path> (run-time option)
 
972
    6. "~/"
 
973
*/
 
974
 
 
975
static void init_default_directories(void)
 
976
{
 
977
  memset(default_directories, 0, sizeof(default_directories));
 
978
  add_directory("/etc/");
 
979
  add_directory("/etc/drizzle/");
 
980
  add_directory(SYSCONFDIR);
 
981
  add_common_directories();
 
982
  add_directory("~/");
 
983
}
 
984
 
 
985
} /* namespace internal */
 
986
} /* namespace drizzled */