my_getopt.c 26.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB

   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,
   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>
20
#include <my_sys.h>
21
#include <mysys_err.h>
22
#include <my_getopt.h>
23

24 25 26
static void default_reporter(enum loglevel level, const char *format, ...);
my_error_reporter my_getopt_error_reporter= &default_reporter;

27 28 29
static int findopt(char *optpat, uint length,
		   const struct my_option **opt_res,
		   char **ffname);
30 31
my_bool getopt_compare_strings(const char *s,
			       const char *t,
32 33 34 35
			       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);
36
static void init_variables(const struct my_option *options);
37
static int setval(const struct my_option *opts, gptr *value, char *argument,
38
		  my_bool set_maximum_value);
39
static char *check_struct_option(char *cur_arg, char *key_name);
40

41 42 43 44
/*
  The following three variables belong to same group and the number and
  order of their arguments must correspond to each other.
*/
45
static const char *special_opt_prefix[]=
46
{"skip", "disable", "enable", "maximum", "loose", 0};
47 48
static const uint special_opt_prefix_lengths[]=
{ 4,      7,         6,        7,         5,      0};
49 50
enum enum_special_opt
{ OPT_SKIP, OPT_DISABLE, OPT_ENABLE, OPT_MAXIMUM, OPT_LOOSE};
51

52
char *disabled_my_option= (char*) "0";
53

54 55
/* 
   This is a flag that can be set in client programs. 0 means that
56
   my_getopt will not print error messages, but the client should do
57 58
   it by itself
*/
59

60
my_bool my_getopt_print_errors= 1;
61

62 63
static void default_reporter(enum loglevel level __attribute__((unused)),
                             const char *format, ...)
rburnett@build.mysql.com's avatar
rburnett@build.mysql.com committed
64 65
{
  va_list args;
serg@serg.mylan's avatar
serg@serg.mylan committed
66 67 68
  va_start(args, format);
  vfprintf(stderr, format, args);
  va_end(args);
rburnett@build.mysql.com's avatar
rburnett@build.mysql.com committed
69
}
70 71 72 73 74 75 76 77 78

/* 
  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.
79
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
80

81
static gptr* (*getopt_get_addr)(const char *, uint, const struct my_option *);
82

83
void my_getopt_register_get_addr(gptr* (*func_addr)(const char *, uint,
84 85 86 87 88
						    const struct my_option *))
{
  getopt_get_addr= func_addr;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
89
int handle_options(int *argc, char ***argv, 
monty@mysql.com's avatar
monty@mysql.com committed
90
		   const struct my_option *longopts,
91
                   my_get_one_option get_one_option)
92
{
93
  uint opt_found, argvpos= 0, length, i;
monty@mysql.com's avatar
monty@mysql.com committed
94
  my_bool end_of_options= 0, must_be_var, set_maximum_value,
95
          option_is_loose;
96 97
  char **pos, **pos_end, *optend, *prev_found,
       *opt_str, key_name[FN_REFLEN];
98
  const struct my_option *optp;
99
  gptr *value;
100
  int error;
101

102
  LINT_INIT(opt_found);
103 104 105
  (*argc)--; /* Skip the program name */
  (*argv)++; /*      --- || ----      */
  init_variables(longopts);
106

107
  for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++)
108 109
  {
    char *cur_arg= *pos;
110
    if (cur_arg[0] == '-' && cur_arg[1] && !end_of_options) /* must be opt */
111
    {
112 113
      char *argument=    0;
      must_be_var=       0;
114
      set_maximum_value= 0;
115
      option_is_loose=   0;
116

117
      cur_arg++;		/* skip '-' */
118 119 120
      if (*cur_arg == '-' || *cur_arg == 'O') /* check for long option, */
      {                                       /* --set-variable, or -O  */
	if (*cur_arg == 'O')
121
	{
122 123 124
	  must_be_var= 1;

	  if (!(*++cur_arg))	/* If not -Ovar=# */
125
	  {
126 127 128
	    /* the argument must be in next argv */
	    if (!*++pos)
	    {
129
	      if (my_getopt_print_errors)
130 131
                my_getopt_error_reporter(ERROR_LEVEL,
                                         "%s: Option '-O' requires an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
132
                                         my_progname);
133
	      return EXIT_ARGUMENT_REQUIRED;
134 135 136
	    }
	    cur_arg= *pos;
	    (*argc)--;
137
	  }
138
	}
139
	else if (!getopt_compare_strings(cur_arg, "-set-variable", 13))
140 141 142
	{
	  must_be_var= 1;
	  if (cur_arg[13] == '=')
143 144
	  {
	    cur_arg+= 14;
145
	    if (!*cur_arg)
146
	    {
147
	      if (my_getopt_print_errors)
148 149
                my_getopt_error_reporter(ERROR_LEVEL,
                                         "%s: Option '--set-variable' requires an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
150
                                         my_progname);
151
	      return EXIT_ARGUMENT_REQUIRED;
152
	    }
153 154 155 156 157 158 159
	  }
	  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)
160
	    {
161
	      if (my_getopt_print_errors)
162 163
                my_getopt_error_reporter(ERROR_LEVEL,
                                         "%s: Option '--set-variable' requires an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
164
                                         my_progname);
165
	      return EXIT_ARGUMENT_REQUIRED;
166
	    }
167 168
	    cur_arg= *pos;
	    (*argc)--;
169 170 171 172
	  }
	}
	else if (!must_be_var)
	{
173
	  if (!*++cur_arg)	/* skip the double dash */
174
	  {
175
	    /* '--' means end of options, look no further */
176
	    end_of_options= 1;
177 178 179 180
	    (*argc)--;
	    continue;
	  }
	}
181 182 183
	opt_str= check_struct_option(cur_arg, key_name);
	optend= strcend(opt_str, '=');
	length= optend - opt_str;
184 185 186
	if (*optend == '=')
	  optend++;
	else
187
	  optend= 0;
188

189 190 191 192
	/*
	  Find first the right option. Return error in case of an ambiguous,
	  or unknown option
	*/
193
	optp= longopts;
194
	if (!(opt_found= findopt(opt_str, length, &optp, &prev_found)))
195 196 197 198 199
	{
	  /*
	    Didn't find any matching option. Let's see if someone called
	    option with a special option prefix
	  */
200
	  if (!must_be_var)
201
	  {
202
	    if (optend)
203
	      must_be_var= 1; /* option is followed by an argument */
204
	    for (i= 0; special_opt_prefix[i]; i++)
205
	    {
206
	      if (!getopt_compare_strings(special_opt_prefix[i], opt_str,
207
					  special_opt_prefix_lengths[i]) &&
208 209
		  (opt_str[special_opt_prefix_lengths[i]] == '-' ||
		   opt_str[special_opt_prefix_lengths[i]] == '_'))
210
	      {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
211 212 213
		/*
		  We were called with a special prefix, we can reuse opt_found
		*/
214
		opt_str+= (special_opt_prefix_lengths[i] + 1);
215
		if (i == OPT_LOOSE)
216
		  option_is_loose= 1;
217
		if ((opt_found= findopt(opt_str, length -
218
					(special_opt_prefix_lengths[i] + 1),
219
					&optp, &prev_found)))
220 221 222
		{
		  if (opt_found > 1)
		  {
223
		    if (my_getopt_print_errors)
224 225
                      my_getopt_error_reporter(ERROR_LEVEL,
                                               "%s: ambiguous option '--%s-%s' (--%s-%s)\n",
monty@mysql.com's avatar
monty@mysql.com committed
226
                                               my_progname, special_opt_prefix[i],
227 228
                                               cur_arg, special_opt_prefix[i],
                                               prev_found);
229
		    return EXIT_AMBIGUOUS_OPTION;
230
		  }
231 232 233
		  switch (i) {
		  case OPT_SKIP:
		  case OPT_DISABLE: /* fall through */
234 235 236 237 238 239
		    /*
		      double negation is actually enable again,
		      for example: --skip-option=0 -> option = TRUE
		    */
		    optend= (optend && *optend == '0' && !(*(optend + 1))) ?
		      (char*) "1" : disabled_my_option;
240 241
		    break;
		  case OPT_ENABLE:
242 243
		    optend= (optend && *optend == '0' && !(*(optend + 1))) ?
 		      disabled_my_option : (char*) "1";
244 245
		    break;
		  case OPT_MAXIMUM:
246 247
		    set_maximum_value= 1;
		    must_be_var= 1;
248
		    break;
249
		  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
250
		  break; /* break from the inner loop, main loop continues */
251 252 253 254 255 256 257 258
		}
	      }
	    }
	  }
	  if (!opt_found)
	  {
	    if (must_be_var)
	    {
259
	      if (my_getopt_print_errors)
260 261 262
                my_getopt_error_reporter(option_is_loose ? 
                                           WARNING_LEVEL : ERROR_LEVEL,
                                         "%s: unknown variable '%s'\n",
monty@mysql.com's avatar
monty@mysql.com committed
263
                                         my_progname, cur_arg);
264
	      if (!option_is_loose)
265
		return EXIT_UNKNOWN_VARIABLE;
266 267 268
	    }
	    else
	    {
269
	      if (my_getopt_print_errors)
270 271 272
                my_getopt_error_reporter(option_is_loose ? 
                                           WARNING_LEVEL : ERROR_LEVEL,
                                         "%s: unknown option '--%s'\n", 
monty@mysql.com's avatar
monty@mysql.com committed
273
                                         my_progname, cur_arg);
274
	      if (!option_is_loose)
275
		return EXIT_UNKNOWN_OPTION;
276 277 278 279 280
	    }
	    if (option_is_loose)
	    {
	      (*argc)--;
	      continue;
281 282 283 284 285 286 287
	    }
	  }
	}
	if (opt_found > 1)
	{
	  if (must_be_var)
	  {
288
	    if (my_getopt_print_errors)
289 290
              my_getopt_error_reporter(ERROR_LEVEL,
                                       "%s: variable prefix '%s' is not unique\n",
monty@mysql.com's avatar
monty@mysql.com committed
291
                                       my_progname, opt_str);
292
	    return EXIT_VAR_PREFIX_NOT_UNIQUE;
293 294 295
	  }
	  else
	  {
296
	    if (my_getopt_print_errors)
297 298
              my_getopt_error_reporter(ERROR_LEVEL,
                                       "%s: ambiguous option '--%s' (%s, %s)\n",
monty@mysql.com's avatar
monty@mysql.com committed
299
                                       my_progname, opt_str, prev_found, 
300
                                       optp->name);
301
	    return EXIT_AMBIGUOUS_OPTION;
302 303
	  }
	}
304 305 306 307 308 309 310 311 312 313 314 315 316
	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;
	}
317
	if (must_be_var && (optp->var_type & GET_TYPE_MASK) == GET_NO_ARG)
318
	{
319
	  if (my_getopt_print_errors)
320 321
            my_getopt_error_reporter(ERROR_LEVEL, 
                                     "%s: option '%s' cannot take an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
322
                                     my_progname, optp->name);
323
	  return EXIT_NO_ARGUMENT_ALLOWED;
324
	}
325 326 327
	value= optp->var_type & GET_ASK_ADDR ?
	  (*getopt_get_addr)(key_name, strlen(key_name), optp) : optp->value;
  
328
	if (optp->arg_type == NO_ARG)
329
	{
330
	  if (optend && (optp->var_type & GET_TYPE_MASK) != GET_BOOL)
331
	  {
332
	    if (my_getopt_print_errors)
333 334
              my_getopt_error_reporter(ERROR_LEVEL,
                                       "%s: option '--%s' cannot take an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
335
                                       my_progname, optp->name);
336
	    return EXIT_NO_ARGUMENT_ALLOWED;
337
	  }
338
	  if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL)
339 340 341 342 343
	  {
	    /*
	      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
344
	      */
345
	    my_bool tmp= (my_bool) (!optend || *optend == '1');
monty@mysql.com's avatar
monty@mysql.com committed
346
	    *((my_bool*) value)= tmp;
347 348 349
	    (*argc)--;
	    get_one_option(optp->id, optp,
			   tmp ? (char*) "1" : disabled_my_option);
350
	    continue;
351 352 353
	  }
	  argument= optend;
	}
354 355
	else if (optp->arg_type == OPT_ARG &&
		 (optp->var_type & GET_TYPE_MASK) == GET_BOOL)
356 357
	{
	  if (optend == disabled_my_option)
358
	    *((my_bool*) value)= (my_bool) 0;
359 360 361
	  else
	  {
	    if (!optend) /* No argument -> enable option */
362
	      *((my_bool*) value)= (my_bool) 1;
363 364
            else
              argument= optend;
365
	  }
366
	}
367
	else if (optp->arg_type == REQUIRED_ARG && !optend)
368 369
	{
	  /* Check if there are more arguments after this one */
370
	  if (!*++pos)
371
	  {
372
	    if (my_getopt_print_errors)
373 374
              my_getopt_error_reporter(ERROR_LEVEL,
                                       "%s: option '--%s' requires an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
375
                                       my_progname, optp->name);
376
	    return EXIT_ARGUMENT_REQUIRED;
377
	  }
378
	  argument= *pos;
379 380
	  (*argc)--;
	}
381 382
	else
	  argument= optend;
383
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
384
      else  /* must be short option */
385
      {
386
	for (optend= cur_arg; *optend; optend++)
387
	{
388
	  opt_found= 0;
389
	  for (optp= longopts; optp->id; optp++)
390 391 392 393
	  {
	    if (optp->id == (int) (uchar) *optend)
	    {
	      /* Option recognized. Find next what to do with it */
394
	      opt_found= 1;
395 396 397 398 399 400 401 402
	      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;
	      }
403 404
	      if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL &&
		  optp->arg_type == NO_ARG)
405 406
	      {
		*((my_bool*) optp->value)= (my_bool) 1;
407 408
		get_one_option(optp->id, optp, argument);
		continue;
409 410 411
	      }
	      else if (optp->arg_type == REQUIRED_ARG ||
		       optp->arg_type == OPT_ARG)
412 413 414
	      {
		if (*(optend + 1))
		{
415
		  /* The rest of the option is option argument */
416
		  argument= optend + 1;
417
		  /* This is in effect a jump out of the outer loop */
418
		  optend= (char*) " ";
419
		}
420
		else
421
		{
422 423 424 425 426 427 428
                  if (optp->arg_type == OPT_ARG)
                  {
                    if (optp->var_type == GET_BOOL)
                      *((my_bool*) optp->value)= (my_bool) 1;
                    get_one_option(optp->id, optp, argument);
                    continue;
                  }
429
		  /* Check if there are more arguments after this one */
430
		  if (!pos[1])
431
		  {
432
                    if (my_getopt_print_errors)
433 434
                      my_getopt_error_reporter(ERROR_LEVEL,
                                               "%s: option '-%c' requires an argument\n",
monty@mysql.com's avatar
monty@mysql.com committed
435
                                               my_progname, optp->id);
436
                    return EXIT_ARGUMENT_REQUIRED;
437
		  }
438
		  argument= *++pos;
439
		  (*argc)--;
440
		  /* the other loop will break, because *optend + 1 == 0 */
441 442
		}
	      }
443 444
	      if ((error= setval(optp, optp->value, argument,
				 set_maximum_value)))
445
	      {
446 447
                my_getopt_error_reporter(ERROR_LEVEL,
                                         "%s: Error while setting value '%s' to '%s'\n",
monty@mysql.com's avatar
monty@mysql.com committed
448
                                         my_progname, argument, optp->name);
449 450
		return error;
	      }
451
	      get_one_option(optp->id, optp, argument);
452 453 454
	      break;
	    }
	  }
455 456
	  if (!opt_found)
	  {
457
	    if (my_getopt_print_errors)
458 459
              my_getopt_error_reporter(ERROR_LEVEL,
                                       "%s: unknown option '-%c'\n", 
monty@mysql.com's avatar
monty@mysql.com committed
460
                                       my_progname, *optend);
461
	    return EXIT_UNKNOWN_OPTION;
462
	  }
463
	}
464 465
	(*argc)--; /* option handled (short), decrease argument count */
	continue;
466
      }
467
      if ((error= setval(optp, value, argument, set_maximum_value)))
468
      {
469 470
        my_getopt_error_reporter(ERROR_LEVEL,
                                 "%s: Error while setting value '%s' to '%s'\n",
monty@mysql.com's avatar
monty@mysql.com committed
471
                                 my_progname, argument, optp->name);
472
	return error;
473
      }
474
      get_one_option(optp->id, optp, argument);
475

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
476
      (*argc)--; /* option handled (shortorlong), decrease argument count */
477
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
478
    else /* non-option found */
479
      (*argv)[argvpos++]= cur_arg;
480
  }
481 482 483 484 485 486 487
  /*
    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;
488 489 490
  return 0;
}

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507

/*
  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)
{
508
  char *ptr, *end;
509

510
  ptr= strcend(cur_arg + 1, '.'); /* Skip the first character */
511
  end= strcend(cur_arg, '=');
512 513

  /* 
514
     If the first dot is after an equal sign, then it is part
515
     of a variable value and the option is not a struct option.
516 517 518
     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.
519
  */
520
  if (end - ptr > 1)
521 522
  {
    uint len= ptr - cur_arg;
523 524
    set_if_smaller(len, FN_REFLEN-1);
    strmake(key_name, cur_arg, len);
525 526 527 528
    return ++ptr;
  }
  else
  {
529
    key_name[0]= 0;
530 531 532 533
    return cur_arg;
  }
}

534 535 536 537 538 539 540
/*
  function: setval

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

541
static int setval(const struct my_option *opts, gptr *value, char *argument,
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
542
		  my_bool set_maximum_value)
543 544 545
{
  int err= 0;

546
  if (value && argument)
547
  {
548
    gptr *result_pos= ((set_maximum_value) ?
549
		       opts->u_max_value : value);
550 551

    if (!result_pos)
552
      return EXIT_NO_PTR_TO_VARIABLE;
553

554
    switch ((opts->var_type & GET_TYPE_MASK)) {
555 556 557
    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
558 559
    case GET_INT:
    case GET_UINT:           /* fall through */
560
      *((int*) result_pos)= (int) getopt_ll(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
561 562 563
      break;
    case GET_LONG:
    case GET_ULONG:          /* fall through */
564
      *((long*) result_pos)= (long) getopt_ll(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
565 566
      break;
    case GET_LL:
567
      *((longlong*) result_pos)= getopt_ll(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
568 569
      break;
    case GET_ULL:
570
      *((ulonglong*) result_pos)= getopt_ull(argument, opts, &err);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
571 572
      break;
    case GET_STR:
573
      *((char**) result_pos)= argument;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
574 575
      break;
    case GET_STR_ALLOC:
576
      if ((*((char**) result_pos)))
577
	my_free((*(char**) result_pos), MYF(MY_WME | MY_FAE));
578
      if (!(*((char**) result_pos)= my_strdup(argument, MYF(MY_WME))))
579
	return EXIT_OUT_OF_MEMORY;
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
580 581 582
      break;
    default:    /* dummy default to avoid compiler warnings */
      break;
583
    }
584
    if (err)
585
      return EXIT_UNKNOWN_SUFFIX;
586 587 588
  }
  return 0;
}
589

590 591 592 593 594 595 596 597 598 599
/* 
  function: findopt

  Arguments: opt_pattern, length of opt_pattern, opt_struct, first found
  name (ffname)

  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
600
*/
601

602 603 604
static int findopt(char *optpat, uint length,
		   const struct my_option **opt_res,
		   char **ffname)
605
{
606
  uint count;
607 608
  struct my_option *opt= (struct my_option *) *opt_res;

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
609
  for (count= 0; opt->name; opt++)
610
  {
611
    if (!getopt_compare_strings(opt->name, optpat, length)) /* match found */
612
    {
613
      (*opt_res)= opt;
614
      if (!count)
615 616
	*ffname= (char *) opt->name;	/* We only need to know one prev */
      if (!opt->name[length])		/* Exact match */
617
	return 1;
618 619
      if (!count || strcmp(*ffname, opt->name)) /* Don't count synonyms */
	count++;
620 621 622 623
    }
  }
  return count;
}
624 625 626 627 628 629 630 631


/* 
  function: compare_strings

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

633
my_bool getopt_compare_strings(register const char *s, register const char *t,
634 635 636 637 638 639 640 641 642 643 644
			       uint length)
{
  char const *end= s + length;
  for (;s != end ; s++, t++)
  {
    if ((*s != '-' ? *s : '_') != (*t != '-' ? *t : '_'))
      return 1;
  }
  return 0;
}

645 646
/*
  function: eval_num_suffix
647

648 649
  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.
650
*/
651

652
static longlong eval_num_suffix (char *argument, int *error, char *option_name)
653 654 655 656
{
  char *endchar;
  longlong num;
  
657 658
  *error= 0;
  num= strtoll(argument, &endchar, 10);
659 660 661 662 663 664 665 666 667 668
  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",
669 670 671
	    *endchar, option_name, argument);
    *error= 1;
    return 0;
672
  }
673 674 675 676 677 678 679 680 681 682 683 684 685 686
  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.
*/

687
static longlong getopt_ll(char *arg, const struct my_option *optp, int *err)
688 689
{
  longlong num;
690
  ulonglong block_size= (optp->block_size ? (ulonglong) optp->block_size : 1L);
691 692
  
  num= eval_num_suffix(arg, err, (char*) optp->name);
693 694
  if (num > 0 && (ulonglong) num > (ulonglong) (ulong) optp->max_value &&
      optp->max_value) /* if max value is not set -> no upper limit */
695
    num= (longlong) (ulong) optp->max_value;
696 697 698
  num= ((num - (longlong) optp->sub_size) / block_size);
  num= (longlong) (num * block_size);
  return max(num, optp->min_value);
699 700
}

701 702 703 704 705 706 707
/*
  function: getopt_ull

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

708
static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err)
709 710 711 712
{
  ulonglong num;

  num= eval_num_suffix(arg, err, (char*) optp->name);  
713 714 715 716 717 718
  return getopt_ull_limit_value(num, optp);
}


ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp)
{
719
  if ((ulonglong) num > (ulonglong) optp->max_value &&
720
      optp->max_value) /* if max value is not set -> no upper limit */
721
    num= (ulonglong) optp->max_value;
722 723 724 725 726
  if (optp->block_size > 1)
  {
    num/= (ulonglong) optp->block_size;
    num*= (ulonglong) optp->block_size;
  }
727 728
  if (num < (ulonglong) optp->min_value)
    num= (ulonglong) optp->min_value;
729
  return num;
730
}
731 732


733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
/*
  Init one value to it's default values

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

static void init_one_value(const struct my_option *option, gptr *variable,
			   longlong value)
{
  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:
    *((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:
    *((ulonglong*) variable)=  (ulonglong) value;
    break;
  default: /* dummy default to avoid compiler warnings */
    break;
  }
}


/* 
774
  initialize all variables to their default values
775 776 777 778 779 780 781 782 783

  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.
784
*/
785

786 787
static void init_variables(const struct my_option *options)
{
788
  for (; options->name; options++)
789
  {
790 791 792 793 794 795 796 797 798 799 800 801 802
    gptr *variable;
    /*
      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);
803 804
  }
}
805 806 807 808 809 810 811 812


/*
  function: my_print_options

  Print help for all options and variables.
*/

monty@mysql.com's avatar
monty@mysql.com committed
813 814
#include <help_start.h>

815 816 817 818 819 820 821 822 823 824
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)
    {
825
      printf("  -%c%s", optp->id, strlen(optp->name) ? ", " : "  ");
826 827 828 829 830 831 832
      col= 6;
    }
    else
    {
      printf("  ");
      col= 2;
    }
833
    if (strlen(optp->name))
834
    {
835 836
      printf("--%s", optp->name);
      col+= 2 + strlen(optp->name);
837 838
      if ((optp->var_type & GET_TYPE_MASK) == GET_STR ||
	  (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC)
839 840 841 842 843
      {
	printf("%s=name%s ", optp->arg_type == OPT_ARG ? "[" : "",
	       optp->arg_type == OPT_ARG ? "]" : "");
	col+= (optp->arg_type == OPT_ARG) ? 8 : 6;
      }
844 845
      else if ((optp->var_type & GET_TYPE_MASK) == GET_NO_ARG ||
	       (optp->var_type & GET_TYPE_MASK) == GET_BOOL)
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
      {
	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;
      }
861 862 863 864 865 866 867 868 869 870 871 872
    }
    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);
873
	comment++; /* skip the space, as a newline will take it's place now */
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
	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)
{
  uint name_space= 34, length;
  char buff[255];
  const struct my_option *optp;

jani@hynda.(none)'s avatar
jani@hynda.(none) committed
897 898 899
  printf("\nVariables (--variable-name=value)\n");
  printf("and boolean options {FALSE|TRUE}  Value (after reading options)\n");
  printf("--------------------------------- -----------------------------\n");
900 901
  for (optp= options; optp->id; optp++)
  {
902 903 904
    gptr *value= (optp->var_type & GET_ASK_ADDR ?
		  (*getopt_get_addr)("", 0, optp) : optp->value);
    if (value)
905 906 907 908 909
    {
      printf("%s", optp->name);
      length= strlen(optp->name);
      for (; length < name_space; length++)
	putchar(' ');
910
      switch ((optp->var_type & GET_TYPE_MASK)) {
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
911 912
      case GET_STR:
      case GET_STR_ALLOC:                    /* fall through */
913
	printf("%s\n", *((char**) value) ? *((char**) value) :
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
914 915 916
	       "(No default value)");
	break;
      case GET_BOOL:
917
	printf("%s\n", *((my_bool*) value) ? "TRUE" : "FALSE");
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
918 919
	break;
      case GET_INT:
920
	printf("%d\n", *((int*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
921 922
	break;
      case GET_UINT:
923
	printf("%d\n", *((uint*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
924 925
	break;
      case GET_LONG:
926
	printf("%lu\n", *((long*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
927 928
	break;
      case GET_ULONG:
929
	printf("%lu\n", *((ulong*) value));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
930 931
	break;
      case GET_LL:
932
	printf("%s\n", llstr(*((longlong*) value), buff));
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
933 934
	break;
      case GET_ULL:
935
	longlong2str(*((ulonglong*) value), buff, 10);
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
936 937
	printf("%s\n", buff);
	break;
938 939
      default:
	printf("(Disabled)\n");
jani@hynda.(none)'s avatar
jani@hynda.(none) committed
940
	break;
941
      }
942 943 944
    }
  }
}
monty@mysql.com's avatar
monty@mysql.com committed
945 946

#include <help_end.h>