my_getopt.c 34.9 KB
Newer Older
1
/* Copyright (C) 2002-2006 MySQL AB
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.
6 7 8 9 10 11 12 13 14 15 16 17 18

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   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 */

#include <my_global.h>
#include <m_string.h>
#include <stdlib.h>
19
#include <my_sys.h>
20
#include <mysys_err.h>
21
#include <my_getopt.h>
22
#include <errno.h>
23

24 25 26
typedef void (*init_func_p)(const struct my_option *option, uchar* *variable,
                            longlong value);

27 28 29
static void default_reporter(enum loglevel level, const char *format, ...);
my_error_reporter my_getopt_error_reporter= &default_reporter;

30 31 32
static int findopt(char *optpat, uint length,
		   const struct my_option **opt_res,
		   char **ffname);
33 34
my_bool getopt_compare_strings(const char *s,
			       const char *t,
35 36 37 38
			       uint length);
static longlong getopt_ll(char *arg, const struct my_option *optp, int *err);
static ulonglong getopt_ull(char *arg, const struct my_option *optp,
			    int *err);
39
static double getopt_double(char *arg, const struct my_option *optp, int *err);
40 41 42 43 44 45
static void init_variables(const struct my_option *options,
                           init_func_p init_one_value);
static void init_one_value(const struct my_option *option, uchar* *variable,
			   longlong value);
static void fini_one_value(const struct my_option *option, uchar* *variable,
			   longlong value);
46
static int setval(const struct my_option *opts, uchar* *value, char *argument,
47
		  my_bool set_maximum_value);
48
static char *check_struct_option(char *cur_arg, char *key_name);
49

50 51 52 53
/*
  The following three variables belong to same group and the number and
  order of their arguments must correspond to each other.
*/
54
static const char *special_opt_prefix[]=
55
{"skip", "disable", "enable", "maximum", "loose", 0};
56 57
static const uint special_opt_prefix_lengths[]=
{ 4,      7,         6,        7,         5,      0};
58 59
enum enum_special_opt
{ OPT_SKIP, OPT_DISABLE, OPT_ENABLE, OPT_MAXIMUM, OPT_LOOSE};
60

61
char *disabled_my_option= (char*) "0";
62

63 64
/* 
   This is a flag that can be set in client programs. 0 means that
65
   my_getopt will not print error messages, but the client should do
66 67
   it by itself
*/
68

69
my_bool my_getopt_print_errors= 1;
70

antony@ppcg5.local's avatar
antony@ppcg5.local committed
71 72 73 74 75 76 77
/* 
   This is a flag that can be set in client programs. 1 means that
   my_getopt will skip over options it does not know how to handle.
*/

my_bool my_getopt_skip_unknown= 0;

78
static void default_reporter(enum loglevel level,
79
                             const char *format, ...)
rburnett@build.mysql.com's avatar
rburnett@build.mysql.com committed
80 81
{
  va_list args;
serg@serg.mylan's avatar
serg@serg.mylan committed
82
  va_start(args, format);
83 84 85 86
  if (level == WARNING_LEVEL)
    fprintf(stderr, "%s", "Warning: ");
  else if (level == INFORMATION_LEVEL)
    fprintf(stderr, "%s", "Info: ");
serg@serg.mylan's avatar
serg@serg.mylan committed
87 88
  vfprintf(stderr, format, args);
  va_end(args);
89
  fputc('\n', stderr);
90
  fflush(stderr);
rburnett@build.mysql.com's avatar
rburnett@build.mysql.com committed
91
}
92 93 94 95 96 97 98 99 100

/* 
  function: handle_options

  Sort options; put options first, until special end of options (--), or
  until end of argv. Parse options; check that the given option matches with
  one of the options in struct 'my_option', return error in case of ambiguous
  or unknown option. Check that option was given an argument if it requires
  one. Call function 'get_one_option()' once for each option.
101
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
102

103
static uchar** (*getopt_get_addr)(const char *, uint, const struct my_option *);
104

105
void my_getopt_register_get_addr(uchar** (*func_addr)(const char *, uint,
106 107 108 109 110
						    const struct my_option *))
{
  getopt_get_addr= func_addr;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
111
int handle_options(int *argc, char ***argv, 
monty@mysql.com's avatar
monty@mysql.com committed
112
		   const struct my_option *longopts,
113
                   my_get_one_option get_one_option)
114
{
serg@janus.mylan's avatar
serg@janus.mylan committed
115
  uint opt_found, argvpos= 0, length;
monty@mysql.com's avatar
monty@mysql.com committed
116
  my_bool end_of_options= 0, must_be_var, set_maximum_value,
117
          option_is_loose;
118 119
  char **pos, **pos_end, *optend, *prev_found,
       *opt_str, key_name[FN_REFLEN];
120
  const struct my_option *optp;
121
  uchar* *value;
serg@janus.mylan's avatar
serg@janus.mylan committed
122
  int error, i;
123

124
  LINT_INIT(opt_found);
125 126 127
  /* handle_options() assumes arg0 (program name) always exists */
  DBUG_ASSERT(argc && *argc >= 1);
  DBUG_ASSERT(argv && *argv);
128 129
  (*argc)--; /* Skip the program name */
  (*argv)++; /*      --- || ----      */
130
  init_variables(longopts, init_one_value);
131

132
  for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++)
133
  {
antony@ppcg5.local's avatar
antony@ppcg5.local committed
134
    char **first= pos;
135
    char *cur_arg= *pos;
136
    if (cur_arg[0] == '-' && cur_arg[1] && !end_of_options) /* must be opt */
137
    {
138 139
      char *argument=    0;
      must_be_var=       0;
140
      set_maximum_value= 0;
141
      option_is_loose=   0;
142

143
      cur_arg++;		/* skip '-' */
144 145 146
      if (*cur_arg == '-' || *cur_arg == 'O') /* check for long option, */
      {                                       /* --set-variable, or -O  */
	if (*cur_arg == 'O')
147
	{
148 149 150
	  must_be_var= 1;

	  if (!(*++cur_arg))	/* If not -Ovar=# */
151
	  {
152 153 154
	    /* the argument must be in next argv */
	    if (!*++pos)
	    {
155
	      if (my_getopt_print_errors)
156
                my_getopt_error_reporter(ERROR_LEVEL,
157
                                         "%s: Option '-O' requires an argument",
monty@mysql.com's avatar
monty@mysql.com committed
158
                                         my_progname);
159
	      return EXIT_ARGUMENT_REQUIRED;
160 161 162
	    }
	    cur_arg= *pos;
	    (*argc)--;
163
	  }
164
	}
165
	else if (!getopt_compare_strings(cur_arg, "-set-variable", 13))
166 167 168
	{
	  must_be_var= 1;
	  if (cur_arg[13] == '=')
169 170
	  {
	    cur_arg+= 14;
171
	    if (!*cur_arg)
172
	    {
173
	      if (my_getopt_print_errors)
174
                my_getopt_error_reporter(ERROR_LEVEL,
175
                                         "%s: Option '--set-variable' requires an argument",
monty@mysql.com's avatar
monty@mysql.com committed
176
                                         my_progname);
177
	      return EXIT_ARGUMENT_REQUIRED;
178
	    }
179 180 181 182 183 184 185
	  }
	  else if (cur_arg[14]) /* garbage, or another option. break out */
	    must_be_var= 0;
	  else
	  {
	    /* the argument must be in next argv */
	    if (!*++pos)
186
	    {
187
	      if (my_getopt_print_errors)
188
                my_getopt_error_reporter(ERROR_LEVEL,
189
                                         "%s: Option '--set-variable' requires an argument",
monty@mysql.com's avatar
monty@mysql.com committed
190
                                         my_progname);
191
	      return EXIT_ARGUMENT_REQUIRED;
192
	    }
193 194
	    cur_arg= *pos;
	    (*argc)--;
195 196 197 198
	  }
	}
	else if (!must_be_var)
	{
199
	  if (!*++cur_arg)	/* skip the double dash */
200
	  {
201
	    /* '--' means end of options, look no further */
202
	    end_of_options= 1;
203 204 205 206
	    (*argc)--;
	    continue;
	  }
	}
207 208
	opt_str= check_struct_option(cur_arg, key_name);
	optend= strcend(opt_str, '=');
209
	length= (uint) (optend - opt_str);
210 211 212
	if (*optend == '=')
	  optend++;
	else
213
	  optend= 0;
214

215 216 217 218
	/*
	  Find first the right option. Return error in case of an ambiguous,
	  or unknown option
	*/
219
        LINT_INIT(prev_found);
220
	optp= longopts;
221
	if (!(opt_found= findopt(opt_str, length, &optp, &prev_found)))
222 223 224 225 226
	{
	  /*
	    Didn't find any matching option. Let's see if someone called
	    option with a special option prefix
	  */
227
	  if (!must_be_var)
228
	  {
229
	    if (optend)
230
	      must_be_var= 1; /* option is followed by an argument */
231
	    for (i= 0; special_opt_prefix[i]; i++)
232
	    {
233
	      if (!getopt_compare_strings(special_opt_prefix[i], opt_str,
234
					  special_opt_prefix_lengths[i]) &&
235 236
		  (opt_str[special_opt_prefix_lengths[i]] == '-' ||
		   opt_str[special_opt_prefix_lengths[i]] == '_'))
237
	      {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
238 239 240
		/*
		  We were called with a special prefix, we can reuse opt_found
		*/
serg@janus.mylan's avatar
serg@janus.mylan committed
241 242
		opt_str+= special_opt_prefix_lengths[i] + 1;
                length-= special_opt_prefix_lengths[i] + 1;
243
		if (i == OPT_LOOSE)
244
		  option_is_loose= 1;
serg@janus.mylan's avatar
serg@janus.mylan committed
245
		if ((opt_found= findopt(opt_str, length, &optp, &prev_found)))
246 247 248
		{
		  if (opt_found > 1)
		  {
249
		    if (my_getopt_print_errors)
250
                      my_getopt_error_reporter(ERROR_LEVEL,
251
                                               "%s: ambiguous option '--%s-%s' (--%s-%s)",
monty@mysql.com's avatar
monty@mysql.com committed
252
                                               my_progname, special_opt_prefix[i],
253 254
                                               cur_arg, special_opt_prefix[i],
                                               prev_found);
255
		    return EXIT_AMBIGUOUS_OPTION;
256
		  }
257 258 259
		  switch (i) {
		  case OPT_SKIP:
		  case OPT_DISABLE: /* fall through */
260 261 262 263 264 265
		    /*
		      double negation is actually enable again,
		      for example: --skip-option=0 -> option = TRUE
		    */
		    optend= (optend && *optend == '0' && !(*(optend + 1))) ?
		      (char*) "1" : disabled_my_option;
266 267
		    break;
		  case OPT_ENABLE:
268
		    optend= (optend && *optend == '0' && !(*(optend + 1))) ?
serg@janus.mylan's avatar
serg@janus.mylan committed
269
                      disabled_my_option : (char*) "1";
270 271
		    break;
		  case OPT_MAXIMUM:
272 273
		    set_maximum_value= 1;
		    must_be_var= 1;
274
		    break;
275
		  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
276
		  break; /* break from the inner loop, main loop continues */
277
		}
serg@janus.mylan's avatar
serg@janus.mylan committed
278
                i= -1; /* restart the loop */
279 280 281 282 283
	      }
	    }
	  }
	  if (!opt_found)
	  {
antony@ppcg5.local's avatar
antony@ppcg5.local committed
284 285 286 287 288 289 290 291 292 293 294 295 296
            if (my_getopt_skip_unknown)
            {
              /*
                preserve all the components of this unknown option, this may
                occurr when the user provides options like: "-O foo" or
                "--set-variable foo" (note that theres a space in there)
                Generally, these kind of options are to be avoided
              */
              do {
                (*argv)[argvpos++]= *first++;
              } while (first <= pos);
              continue;
            }
297 298
	    if (must_be_var)
	    {
299
	      if (my_getopt_print_errors)
300 301
                my_getopt_error_reporter(option_is_loose ? 
                                           WARNING_LEVEL : ERROR_LEVEL,
302
                                         "%s: unknown variable '%s'",
monty@mysql.com's avatar
monty@mysql.com committed
303
                                         my_progname, cur_arg);
304
	      if (!option_is_loose)
305
		return EXIT_UNKNOWN_VARIABLE;
306 307 308
	    }
	    else
	    {
309
	      if (my_getopt_print_errors)
310 311
                my_getopt_error_reporter(option_is_loose ? 
                                           WARNING_LEVEL : ERROR_LEVEL,
312
                                         "%s: unknown option '--%s'", 
monty@mysql.com's avatar
monty@mysql.com committed
313
                                         my_progname, cur_arg);
314
	      if (!option_is_loose)
315
		return EXIT_UNKNOWN_OPTION;
316 317 318 319 320
	    }
	    if (option_is_loose)
	    {
	      (*argc)--;
	      continue;
321 322 323 324 325 326 327
	    }
	  }
	}
	if (opt_found > 1)
	{
	  if (must_be_var)
	  {
328
	    if (my_getopt_print_errors)
329
              my_getopt_error_reporter(ERROR_LEVEL,
330
                                       "%s: variable prefix '%s' is not unique",
monty@mysql.com's avatar
monty@mysql.com committed
331
                                       my_progname, opt_str);
332
	    return EXIT_VAR_PREFIX_NOT_UNIQUE;
333 334 335
	  }
	  else
	  {
336
	    if (my_getopt_print_errors)
337
              my_getopt_error_reporter(ERROR_LEVEL,
338
                                       "%s: ambiguous option '--%s' (%s, %s)",
monty@mysql.com's avatar
monty@mysql.com committed
339
                                       my_progname, opt_str, prev_found, 
340
                                       optp->name);
341
	    return EXIT_AMBIGUOUS_OPTION;
342 343
	  }
	}
344 345 346 347 348 349 350 351 352 353 354 355 356
	if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED)
	{
	  if (my_getopt_print_errors)
	    fprintf(stderr,
		    "%s: %s: Option '%s' used, but is disabled\n", my_progname,
		    option_is_loose ? "WARNING" : "ERROR", opt_str);
	  if (option_is_loose)
	  {
	    (*argc)--;
	    continue;
	  }
	  return EXIT_OPTION_DISABLED;
	}
357
	if (must_be_var && (optp->var_type & GET_TYPE_MASK) == GET_NO_ARG)
358
	{
359
	  if (my_getopt_print_errors)
360
            my_getopt_error_reporter(ERROR_LEVEL, 
361
                                     "%s: option '%s' cannot take an argument",
monty@mysql.com's avatar
monty@mysql.com committed
362
                                     my_progname, optp->name);
363
	  return EXIT_NO_ARGUMENT_ALLOWED;
364
	}
365
	value= optp->var_type & GET_ASK_ADDR ?
366
	  (*getopt_get_addr)(key_name, (uint) strlen(key_name), optp) : optp->value;
367
  
368
	if (optp->arg_type == NO_ARG)
369
	{
370
	  if (optend && (optp->var_type & GET_TYPE_MASK) != GET_BOOL)
371
	  {
372
	    if (my_getopt_print_errors)
373
              my_getopt_error_reporter(ERROR_LEVEL,
374
                                       "%s: option '--%s' cannot take an argument",
monty@mysql.com's avatar
monty@mysql.com committed
375
                                       my_progname, optp->name);
376
	    return EXIT_NO_ARGUMENT_ALLOWED;
377
	  }
378
	  if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL)
379 380 381 382 383
	  {
	    /*
	      Set bool to 1 if no argument or if the user has used
	      --enable-'option-name'.
	      *optend was set to '0' if one used --disable-option
384
	      */
385
	    (*argc)--;
386 387 388 389 390 391 392 393 394 395
	    if (!optend || *optend == '1' ||
		!my_strcasecmp(&my_charset_latin1, optend, "true"))
	      *((my_bool*) value)= (my_bool) 1;
	    else if (*optend == '0' ||
		     !my_strcasecmp(&my_charset_latin1, optend, "false"))
	      *((my_bool*) value)= (my_bool) 0;
	    else
	    {
	      my_getopt_error_reporter(WARNING_LEVEL,
				       "%s: ignoring option '--%s' due to \
396
invalid value '%s'",
397 398 399
				       my_progname, optp->name, optend);
	      continue;
	    }
400 401 402 403
	    if (get_one_option(optp->id, optp,
                               *((my_bool*) value) ?
                               (char*) "1" : disabled_my_option))
              return EXIT_ARGUMENT_INVALID;
404
	    continue;
405 406 407
	  }
	  argument= optend;
	}
408 409
	else if (optp->arg_type == OPT_ARG &&
		 (optp->var_type & GET_TYPE_MASK) == GET_BOOL)
410 411
	{
	  if (optend == disabled_my_option)
412
	    *((my_bool*) value)= (my_bool) 0;
413 414 415
	  else
	  {
	    if (!optend) /* No argument -> enable option */
416
	      *((my_bool*) value)= (my_bool) 1;
417 418
            else
              argument= optend;
419
	  }
420
	}
421
	else if (optp->arg_type == REQUIRED_ARG && !optend)
422 423
	{
	  /* Check if there are more arguments after this one */
424
	  if (!*++pos)
425
	  {
426
	    if (my_getopt_print_errors)
427
              my_getopt_error_reporter(ERROR_LEVEL,
428
                                       "%s: option '--%s' requires an argument",
monty@mysql.com's avatar
monty@mysql.com committed
429
                                       my_progname, optp->name);
430
	    return EXIT_ARGUMENT_REQUIRED;
431
	  }
432
	  argument= *pos;
433 434
	  (*argc)--;
	}
435 436
	else
	  argument= optend;
437
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
438
      else  /* must be short option */
439
      {
440
	for (optend= cur_arg; *optend; optend++)
441
	{
442
	  opt_found= 0;
443
	  for (optp= longopts; optp->id; optp++)
444 445 446 447
	  {
	    if (optp->id == (int) (uchar) *optend)
	    {
	      /* Option recognized. Find next what to do with it */
448
	      opt_found= 1;
449 450 451 452 453 454 455 456
	      if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED)
	      {
		if (my_getopt_print_errors)
		  fprintf(stderr,
			  "%s: ERROR: Option '-%c' used, but is disabled\n",
			  my_progname, optp->id);
		return EXIT_OPTION_DISABLED;
	      }
457 458
	      if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL &&
		  optp->arg_type == NO_ARG)
459 460
	      {
		*((my_bool*) optp->value)= (my_bool) 1;
461 462
		if (get_one_option(optp->id, optp, argument))
                  return EXIT_UNSPECIFIED_ERROR;
463
		continue;
464 465 466
	      }
	      else if (optp->arg_type == REQUIRED_ARG ||
		       optp->arg_type == OPT_ARG)
467 468 469
	      {
		if (*(optend + 1))
		{
470
		  /* The rest of the option is option argument */
471
		  argument= optend + 1;
472
		  /* This is in effect a jump out of the outer loop */
473
		  optend= (char*) " ";
474
		}
475
		else
476
		{
477 478 479 480
                  if (optp->arg_type == OPT_ARG)
                  {
                    if (optp->var_type == GET_BOOL)
                      *((my_bool*) optp->value)= (my_bool) 1;
481 482
                    if (get_one_option(optp->id, optp, argument))
                      return EXIT_UNSPECIFIED_ERROR;
483 484
                    continue;
                  }
485
		  /* Check if there are more arguments after this one */
486
		  if (!pos[1])
487
		  {
488
                    if (my_getopt_print_errors)
489
                      my_getopt_error_reporter(ERROR_LEVEL,
490
                                               "%s: option '-%c' requires an argument",
monty@mysql.com's avatar
monty@mysql.com committed
491
                                               my_progname, optp->id);
492
                    return EXIT_ARGUMENT_REQUIRED;
493
		  }
494
		  argument= *++pos;
495
		  (*argc)--;
496
		  /* the other loop will break, because *optend + 1 == 0 */
497 498
		}
	      }
499 500
	      if ((error= setval(optp, optp->value, argument,
				 set_maximum_value)))
501
	      {
502
                my_getopt_error_reporter(ERROR_LEVEL,
503
                                         "%s: Error while setting value '%s' to '%s'",
monty@mysql.com's avatar
monty@mysql.com committed
504
                                         my_progname, argument, optp->name);
505 506
		return error;
	      }
507 508
	      if (get_one_option(optp->id, optp, argument))
                return EXIT_UNSPECIFIED_ERROR;
509 510 511
	      break;
	    }
	  }
512 513
	  if (!opt_found)
	  {
514
	    if (my_getopt_print_errors)
515
              my_getopt_error_reporter(ERROR_LEVEL,
516
                                       "%s: unknown option '-%c'", 
monty@mysql.com's avatar
monty@mysql.com committed
517
                                       my_progname, *optend);
518
	    return EXIT_UNKNOWN_OPTION;
519
	  }
520
	}
521 522
	(*argc)--; /* option handled (short), decrease argument count */
	continue;
523
      }
524
      if ((error= setval(optp, value, argument, set_maximum_value)))
525
      {
526
        my_getopt_error_reporter(ERROR_LEVEL,
527
                                 "%s: Error while setting value '%s' to '%s'",
monty@mysql.com's avatar
monty@mysql.com committed
528
                                 my_progname, argument, optp->name);
529
	return error;
530
      }
531 532
      if (get_one_option(optp->id, optp, argument))
        return EXIT_UNSPECIFIED_ERROR;
533

534
      (*argc)--; /* option handled (short or long), decrease argument count */
535
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
536
    else /* non-option found */
537
      (*argv)[argvpos++]= cur_arg;
538
  }
539 540 541 542 543 544 545
  /*
    Destroy the first, already handled option, so that programs that look
    for arguments in 'argv', without checking 'argc', know when to stop.
    Items in argv, before the destroyed one, are all non-option -arguments
    to the program, yet to be (possibly) handled.
  */
  (*argv)[argvpos]= 0;
546 547 548
  return 0;
}

549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565

/*
  function: check_struct_option

  Arguments: Current argument under processing from argv and a variable
  where to store the possible key name.

  Return value: In case option is a struct option, returns a pointer to
  the current argument at the position where the struct option (key_name)
  ends, the next character after the dot. In case argument is not a struct
  option, returns a pointer to the argument.

  key_name will hold the name of the key, or 0 if not found.
*/

static char *check_struct_option(char *cur_arg, char *key_name)
{
566
  char *ptr, *end;
567

568
  ptr= strcend(cur_arg + 1, '.'); /* Skip the first character */
569
  end= strcend(cur_arg, '=');
570 571

  /* 
572
     If the first dot is after an equal sign, then it is part
573
     of a variable value and the option is not a struct option.
574 575 576
     Also, if the last character in the string before the ending
     NULL, or the character right before equal sign is the first
     dot found, the option is not a struct option.
577
  */
578
  if (end - ptr > 1)
579
  {
580
    uint len= (uint) (ptr - cur_arg);
581 582
    set_if_smaller(len, FN_REFLEN-1);
    strmake(key_name, cur_arg, len);
583 584 585 586
    return ++ptr;
  }
  else
  {
587
    key_name[0]= 0;
588 589 590 591
    return cur_arg;
  }
}

592 593 594 595 596 597 598
/*
  function: setval

  Arguments: opts, argument
  Will set the option value to given value
*/

599
static int setval(const struct my_option *opts, uchar* *value, char *argument,
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
600
		  my_bool set_maximum_value)
601 602 603
{
  int err= 0;

604
  if (value && argument)
605
  {
606
    uchar* *result_pos= ((set_maximum_value) ?
607
		       opts->u_max_value : value);
608 609

    if (!result_pos)
610
      return EXIT_NO_PTR_TO_VARIABLE;
611

612
    switch ((opts->var_type & GET_TYPE_MASK)) {
613 614 615
    case GET_BOOL: /* If argument differs from 0, enable option, else disable */
      *((my_bool*) result_pos)= (my_bool) atoi(argument) != 0;
      break;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
616
    case GET_INT:
617
      *((int*) result_pos)= (int) getopt_ll(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
618
      break;
619 620 621
    case GET_UINT:
      *((uint*) result_pos)= (uint) getopt_ull(argument, opts, &err);
      break;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
622
    case GET_LONG:
623
      *((long*) result_pos)= (long) getopt_ll(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
624
      break;
625 626 627
    case GET_ULONG:
      *((long*) result_pos)= (long) getopt_ull(argument, opts, &err);
      break;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
628
    case GET_LL:
629
      *((longlong*) result_pos)= getopt_ll(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
630 631
      break;
    case GET_ULL:
632
      *((ulonglong*) result_pos)= getopt_ull(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
633
      break;
634 635 636
    case GET_DOUBLE:
      *((double*) result_pos)= getopt_double(argument, opts, &err);
      break;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
637
    case GET_STR:
638
      *((char**) result_pos)= argument;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
639 640
      break;
    case GET_STR_ALLOC:
641
      if ((*((char**) result_pos)))
642
	my_free((*(char**) result_pos), MYF(MY_WME | MY_FAE));
643
      if (!(*((char**) result_pos)= my_strdup(argument, MYF(MY_WME))))
644
	return EXIT_OUT_OF_MEMORY;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
645
      break;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
646 647 648 649 650 651 652 653 654
    case GET_ENUM:
      if (((*(int*)result_pos)= find_type(argument, opts->typelib, 2) - 1) < 0)
        return EXIT_ARGUMENT_INVALID;
      break;
    case GET_SET:
      *((ulonglong*)result_pos)= find_typeset(argument, opts->typelib, &err);
      if (err)
        return EXIT_ARGUMENT_INVALID;
      break;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
655 656
    default:    /* dummy default to avoid compiler warnings */
      break;
657
    }
658
    if (err)
659
      return EXIT_UNKNOWN_SUFFIX;
660 661 662
  }
  return 0;
}
663

664

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
/* 
  Find option

  SYNOPSIS
    findopt()
    optpat	Prefix of option to find (with - or _)
    length	Length of optpat
    opt_res	Options
    ffname	Place for pointer to first found name

  IMPLEMENTATION
    Go through all options in the my_option struct. Return number
    of options found that match the pattern and in the argument
    list the option found, if any. In case of ambiguous option, store
    the name in ffname argument

    RETURN
    0    No matching options
    #   Number of matching options
        ffname points to first matching option
685
*/
686

687 688 689
static int findopt(char *optpat, uint length,
		   const struct my_option **opt_res,
		   char **ffname)
690
{
691
  uint count;
692 693
  struct my_option *opt= (struct my_option *) *opt_res;

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
694
  for (count= 0; opt->name; opt++)
695
  {
696
    if (!getopt_compare_strings(opt->name, optpat, length)) /* match found */
697
    {
698
      (*opt_res)= opt;
699
      if (!opt->name[length])		/* Exact match */
700
	return 1;
701 702 703 704 705 706 707 708 709 710 711
      if (!count)
      {
	count= 1;
	*ffname= (char *) opt->name;	/* We only need to know one prev */
      }
      else if (strcmp(*ffname, opt->name))
      {
	/*
	  The above test is to not count same option twice
	  (see mysql.cc, option "help")
	*/
712
	count++;
713
      }
714 715 716 717
    }
  }
  return count;
}
718 719 720 721 722 723 724 725


/* 
  function: compare_strings

  Works like strncmp, other than 1.) considers '-' and '_' the same.
  2.) Returns -1 if strings differ, 0 if they are equal
*/
726

727
my_bool getopt_compare_strings(register const char *s, register const char *t,
728 729 730 731 732 733 734 735 736 737 738
			       uint length)
{
  char const *end= s + length;
  for (;s != end ; s++, t++)
  {
    if ((*s != '-' ? *s : '_') != (*t != '-' ? *t : '_'))
      return 1;
  }
  return 0;
}

739 740
/*
  function: eval_num_suffix
741

742 743
  Transforms a number with a suffix to real number. Suffix can
  be k|K for kilo, m|M for mega or g|G for giga.
744
*/
745

746
static longlong eval_num_suffix(char *argument, int *error, char *option_name)
747 748 749 750
{
  char *endchar;
  longlong num;
  
751
  *error= 0;
752
  errno= 0;
753
  num= strtoll(argument, &endchar, 10);
754 755 756 757 758 759 760
  if (errno == ERANGE)
  {
    my_getopt_error_reporter(ERROR_LEVEL,
                             "Incorrect integer value: '%s'", argument);
    *error= 1;
    return 0;
  }
761 762 763 764 765 766 767 768 769 770
  if (*endchar == 'k' || *endchar == 'K')
    num*= 1024L;
  else if (*endchar == 'm' || *endchar == 'M')
    num*= 1024L * 1024L;
  else if (*endchar == 'g' || *endchar == 'G')
    num*= 1024L * 1024L * 1024L;
  else if (*endchar)
  {
    fprintf(stderr,
	    "Unknown suffix '%c' used for variable '%s' (value '%s')\n",
771 772 773
	    *endchar, option_name, argument);
    *error= 1;
    return 0;
774
  }
775 776 777 778 779 780 781 782 783 784 785 786 787 788
  return num;
}

/* 
  function: getopt_ll

  Evaluates and returns the value that user gave as an argument
  to a variable. Recognizes (case insensitive) K as KILO, M as MEGA
  and G as GIGA bytes. Some values must be in certain blocks, as
  defined in the given my_option struct, this function will check
  that those values are honored.
  In case of an error, set error value in *err.
*/

789
static longlong getopt_ll(char *arg, const struct my_option *optp, int *err)
790
{
791
  longlong num=eval_num_suffix(arg, err, (char*) optp->name);
792
  return getopt_ll_limit_value(num, optp, NULL);
793 794 795 796 797 798 799 800 801
}

/*
  function: getopt_ll_limit_value

  Applies min/max/block_size to a numeric value of an option.
  Returns "fixed" value.
*/

802
longlong getopt_ll_limit_value(longlong num, const struct my_option *optp,
803
                               my_bool *fix)
804 805
{
  longlong old= num;
806
  my_bool adjusted= FALSE;
807
  char buf1[255], buf2[255];
808
  ulonglong block_size= (optp->block_size ? (ulonglong) optp->block_size : 1L);
809 810

  if (num > 0 && ((ulonglong) num > (ulonglong) optp->max_value) &&
811
      optp->max_value) /* if max value is not set -> no upper limit */
812
  {
813
    num= (ulonglong) optp->max_value;
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
    adjusted= TRUE;
  }

  switch ((optp->var_type & GET_TYPE_MASK)) {
  case GET_INT:
    if (num > (longlong) INT_MAX)
    {
      num= ((longlong) INT_MAX);
      adjusted= TRUE;
    }
    break;
  case GET_LONG:
#if SIZEOF_LONG < SIZEOF_LONG_LONG
    if (num > (longlong) LONG_MAX)
    {
      num= ((longlong) LONG_MAX);
      adjusted= TRUE;
    }
#endif
    break;
  default:
    DBUG_ASSERT((optp->var_type & GET_TYPE_MASK) == GET_LL);
    break;
837
  }
838

839
  num= ((num - optp->sub_size) / block_size);
840
  num= (longlong) (num * block_size);
841 842 843 844

  if (num < optp->min_value)
  {
    num= optp->min_value;
845
    adjusted= TRUE;
846 847
  }

848 849 850
  if (fix)
    *fix= adjusted;
  else if (adjusted)
851 852 853 854
    my_getopt_error_reporter(WARNING_LEVEL,
                             "option '%s': signed value %s adjusted to %s",
                             optp->name, llstr(old, buf1), llstr(num, buf2));
  return num;
855 856
}

857 858 859 860 861 862 863
/*
  function: getopt_ull

  This is the same as getopt_ll, but is meant for unsigned long long
  values.
*/

864
static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err)
865
{
866 867
  ulonglong num= eval_num_suffix(arg, err, (char*) optp->name);
  return getopt_ull_limit_value(num, optp, NULL);
868 869 870
}


871
ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp,
872
                                 my_bool *fix)
873
{
874
  my_bool adjusted= FALSE;
875
  ulonglong old= num;
876 877
  char buf1[255], buf2[255];

878
  if ((ulonglong) num > (ulonglong) optp->max_value &&
879
      optp->max_value) /* if max value is not set -> no upper limit */
880
  {
881
    num= (ulonglong) optp->max_value;
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
    adjusted= TRUE;
  }

  switch ((optp->var_type & GET_TYPE_MASK)) {
  case GET_UINT:
    if (num > (ulonglong) UINT_MAX)
    {
      num= ((ulonglong) UINT_MAX);
      adjusted= TRUE;
    }
    break;
  case GET_ULONG:
#if SIZEOF_LONG < SIZEOF_LONG_LONG
    if (num > (ulonglong) ULONG_MAX)
    {
      num= ((ulonglong) ULONG_MAX);
      adjusted= TRUE;
    }
#endif
    break;
  default:
    DBUG_ASSERT((optp->var_type & GET_TYPE_MASK) == GET_ULL);
    break;
  }

907 908 909 910 911
  if (optp->block_size > 1)
  {
    num/= (ulonglong) optp->block_size;
    num*= (ulonglong) optp->block_size;
  }
912

913
  if (num < (ulonglong) optp->min_value)
914
  {
915
    num= (ulonglong) optp->min_value;
916 917 918
    adjusted= TRUE;
  }

919 920 921
  if (fix)
    *fix= adjusted;
  else if (adjusted)
922 923 924 925
    my_getopt_error_reporter(WARNING_LEVEL,
                             "option '%s': unsigned value %s adjusted to %s",
                             optp->name, ullstr(old, buf1), ullstr(num, buf2));

926
  return num;
927
}
928 929


930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
/*
  Get double value withing ranges

  Evaluates and returns the value that user gave as an argument to a variable.

  RETURN
    decimal value of arg

    In case of an error, prints an error message and sets *err to
    EXIT_ARGUMENT_INVALID.  Otherwise err is not touched
*/

static double getopt_double(char *arg, const struct my_option *optp, int *err)
{
  double num;
  int error;
  char *end= arg + 1000;                     /* Big enough as *arg is \0 terminated */
  num= my_strtod(arg, &end, &error);
  if (end[0] != 0 || error)
  {
    fprintf(stderr,
            "%s: ERROR: Invalid decimal value for option '%s'\n",
            my_progname, optp->name);
    *err= EXIT_ARGUMENT_INVALID;
    return 0.0;
  }
  if (optp->max_value && num > (double) optp->max_value)
    num= (double) optp->max_value;
  return max(num, (double) optp->min_value);
}

961 962 963 964 965 966 967 968 969
/*
  Init one value to it's default values

  SYNOPSIS
    init_one_value()
    option		Option to initialize
    value		Pointer to variable
*/

970
static void init_one_value(const struct my_option *option, uchar* *variable,
971 972
			   longlong value)
{
973
  DBUG_ENTER("init_one_value");
974 975 976 977 978 979 980 981
  switch ((option->var_type & GET_TYPE_MASK)) {
  case GET_BOOL:
    *((my_bool*) variable)= (my_bool) value;
    break;
  case GET_INT:
    *((int*) variable)= (int) value;
    break;
  case GET_UINT:
antony@ppcg5.local's avatar
antony@ppcg5.local committed
982
  case GET_ENUM:
983 984 985 986 987 988 989 990 991 992 993 994
    *((uint*) variable)= (uint) value;
    break;
  case GET_LONG:
    *((long*) variable)= (long) value;
    break;
  case GET_ULONG:
    *((ulong*) variable)= (ulong) value;
    break;
  case GET_LL:
    *((longlong*) variable)= (longlong) value;
    break;
  case GET_ULL:
antony@ppcg5.local's avatar
antony@ppcg5.local committed
995
  case GET_SET:
996 997
    *((ulonglong*) variable)=  (ulonglong) value;
    break;
998 999 1000
  case GET_DOUBLE:
    *((double*) variable)=  (double) value;
    break;
1001 1002 1003 1004
  case GET_STR:
    /*
      Do not clear variable value if it has no default value.
      The default value may already be set.
1005 1006
      NOTE: To avoid compiler warnings, we first cast longlong to intptr,
      so that the value has the same size as a pointer.
1007
    */
1008 1009
    if ((char*) (intptr) value)
      *((char**) variable)= (char*) (intptr) value;
1010 1011 1012 1013 1014
    break;
  case GET_STR_ALLOC:
    /*
      Do not clear variable value if it has no default value.
      The default value may already be set.
1015 1016
      NOTE: To avoid compiler warnings, we first cast longlong to intptr,
      so that the value has the same size as a pointer.
1017
    */
1018
    if ((char*) (intptr) value)
1019 1020
    {
      my_free((*(char**) variable), MYF(MY_ALLOW_ZERO_PTR));
1021
      *((char**) variable)= my_strdup((char*) (intptr) value, MYF(MY_WME));
1022 1023
    }
    break;
1024 1025 1026
  default: /* dummy default to avoid compiler warnings */
    break;
  }
1027
  DBUG_VOID_RETURN;
1028 1029 1030
}


1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
/*
  Init one value to it's default values

  SYNOPSIS
    init_one_value()
    option		Option to initialize
    value		Pointer to variable
*/

static void fini_one_value(const struct my_option *option, uchar* *variable,
			   longlong value __attribute__ ((unused)))
{
  DBUG_ENTER("fini_one_value");
  switch ((option->var_type & GET_TYPE_MASK)) {
  case GET_STR_ALLOC:
    my_free((*(char**) variable), MYF(MY_ALLOW_ZERO_PTR));
    *((char**) variable)= NULL;
    break;
  default: /* dummy default to avoid compiler warnings */
    break;
  }
  DBUG_VOID_RETURN;
}


void my_cleanup_options(const struct my_option *options)
{
  init_variables(options, fini_one_value);
}


1062
/* 
1063
  initialize all variables to their default values
1064 1065 1066 1067 1068 1069 1070 1071 1072

  SYNOPSIS
    init_variables()
    options		Array of options

  NOTES
    We will initialize the value that is pointed to by options->value.
    If the value is of type GET_ASK_ADDR, we will also ask for the address
    for a value and initialize.
1073
*/
1074

1075 1076
static void init_variables(const struct my_option *options,
                           init_func_p init_one_value)
1077
{
1078
  DBUG_ENTER("init_variables");
1079
  for (; options->name; options++)
1080
  {
1081
    uchar* *variable;
1082
    DBUG_PRINT("options", ("name: '%s'", options->name));
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
    /*
      We must set u_max_value first as for some variables
      options->u_max_value == options->value and in this case we want to
      set the value to default value.
    */
    if (options->u_max_value)
      init_one_value(options, options->u_max_value, options->max_value);
    if (options->value)
      init_one_value(options, options->value, options->def_value);
    if (options->var_type & GET_ASK_ADDR &&
	(variable= (*getopt_get_addr)("", 0, options)))
      init_one_value(options, variable, options->def_value);
1095
  }
1096
  DBUG_VOID_RETURN;
1097
}
1098 1099 1100 1101 1102 1103 1104 1105


/*
  function: my_print_options

  Print help for all options and variables.
*/

monty@mysql.com's avatar
monty@mysql.com committed
1106 1107
#include <help_start.h>

1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
void my_print_help(const struct my_option *options)
{
  uint col, name_space= 22, comment_space= 57;
  const char *line_end;
  const struct my_option *optp;

  for (optp= options; optp->id; optp++)
  {
    if (optp->id < 256)
    {
1118
      printf("  -%c%s", optp->id, strlen(optp->name) ? ", " : "  ");
1119 1120 1121 1122 1123 1124 1125
      col= 6;
    }
    else
    {
      printf("  ");
      col= 2;
    }
1126
    if (strlen(optp->name))
1127
    {
1128
      printf("--%s", optp->name);
1129
      col+= 2 + (uint) strlen(optp->name);
1130 1131
      if ((optp->var_type & GET_TYPE_MASK) == GET_STR ||
	  (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC)
1132 1133 1134 1135 1136
      {
	printf("%s=name%s ", optp->arg_type == OPT_ARG ? "[" : "",
	       optp->arg_type == OPT_ARG ? "]" : "");
	col+= (optp->arg_type == OPT_ARG) ? 8 : 6;
      }
1137 1138
      else if ((optp->var_type & GET_TYPE_MASK) == GET_NO_ARG ||
	       (optp->var_type & GET_TYPE_MASK) == GET_BOOL)
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
      {
	putchar(' ');
	col++;
      }
      else
      {
	printf("%s=#%s ", optp->arg_type == OPT_ARG ? "[" : "",
	       optp->arg_type == OPT_ARG ? "]" : "");
	col+= (optp->arg_type == OPT_ARG) ? 5 : 3;
      }
      if (col > name_space && optp->comment && *optp->comment)
      {
	putchar('\n');
	col= 0;
      }
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
    }
    for (; col < name_space; col++)
      putchar(' ');
    if (optp->comment && *optp->comment)
    {
      const char *comment= optp->comment, *end= strend(comment);

      while ((uint) (end - comment) > comment_space)
      {
	for (line_end= comment + comment_space; *line_end != ' '; line_end--);
	for (; comment != line_end; comment++)
	  putchar(*comment);
1166
	comment++; /* skip the space, as a newline will take it's place now */
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
	putchar('\n');
	for (col= 0; col < name_space; col++)
	  putchar(' ');
      }
      printf("%s", comment);
    }
    putchar('\n');
  }
}


/*
  function: my_print_options

  Print variables.
*/

void my_print_variables(const struct my_option *options)
{
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1186 1187
  uint name_space= 34, length, nr;
  ulonglong bit, llvalue;
1188 1189 1190
  char buff[255];
  const struct my_option *optp;

jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1191 1192 1193
  printf("\nVariables (--variable-name=value)\n");
  printf("and boolean options {FALSE|TRUE}  Value (after reading options)\n");
  printf("--------------------------------- -----------------------------\n");
1194 1195
  for (optp= options; optp->id; optp++)
  {
1196
    uchar* *value= (optp->var_type & GET_ASK_ADDR ?
1197 1198
		  (*getopt_get_addr)("", 0, optp) : optp->value);
    if (value)
1199
    {
1200 1201
      printf("%s ", optp->name);
      length= (uint) strlen(optp->name)+1;
1202 1203
      for (; length < name_space; length++)
	putchar(' ');
1204
      switch ((optp->var_type & GET_TYPE_MASK)) {
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
      case GET_SET:
        if (!(llvalue= *(ulonglong*) value))
	  printf("%s\n", "(No default value)");
	else
        for (nr= 0, bit= 1; llvalue && nr < optp->typelib->count; nr++, bit<<=1)
	{
	  if (!(bit & llvalue))
	    continue;
	  llvalue&= ~bit;
	  printf( llvalue ? "%s," : "%s\n", get_type(optp->typelib, nr));
	}
	break;
      case GET_ENUM:
        printf("%s\n", get_type(optp->typelib, *(uint*) value));
	break;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1220 1221
      case GET_STR:
      case GET_STR_ALLOC:                    /* fall through */
1222
	printf("%s\n", *((char**) value) ? *((char**) value) :
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1223 1224 1225
	       "(No default value)");
	break;
      case GET_BOOL:
1226
	printf("%s\n", *((my_bool*) value) ? "TRUE" : "FALSE");
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1227 1228
	break;
      case GET_INT:
1229
	printf("%d\n", *((int*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1230 1231
	break;
      case GET_UINT:
1232
	printf("%d\n", *((uint*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1233 1234
	break;
      case GET_LONG:
1235
	printf("%ld\n", *((long*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1236 1237
	break;
      case GET_ULONG:
1238
	printf("%lu\n", *((ulong*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1239 1240
	break;
      case GET_LL:
1241
	printf("%s\n", llstr(*((longlong*) value), buff));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1242 1243
	break;
      case GET_ULL:
1244
	longlong2str(*((ulonglong*) value), buff, 10);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1245 1246
	printf("%s\n", buff);
	break;
1247 1248 1249
      case GET_DOUBLE:
	printf("%g\n", *(double*) value);
	break;
1250 1251
      default:
	printf("(Disabled)\n");
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
1252
	break;
1253
      }
1254 1255 1256
    }
  }
}
monty@mysql.com's avatar
monty@mysql.com committed
1257 1258

#include <help_end.h>