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);
unknown's avatar
unknown committed
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, ...)
unknown's avatar
unknown committed
64 65
{
  va_list args;
unknown's avatar
unknown committed
66 67 68
  va_start(args, format);
  vfprintf(stderr, format, args);
  va_end(args);
unknown's avatar
unknown 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
*/
unknown's avatar
unknown 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;
}

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

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

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

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

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

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

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

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

540
static int setval(const struct my_option *opts, gptr *value, char *argument,
unknown's avatar
unknown committed
541
		  my_bool set_maximum_value)
542 543 544
{
  int err= 0;

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

    if (!result_pos)
551
      return EXIT_NO_PTR_TO_VARIABLE;
552

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

589 590 591 592 593 594 595 596 597 598
/* 
  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
599
*/
600

unknown's avatar
unknown committed
601 602 603
static int findopt(char *optpat, uint length,
		   const struct my_option **opt_res,
		   char **ffname)
604
{
605
  uint count;
606 607
  struct my_option *opt= (struct my_option *) *opt_res;

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


/* 
  function: compare_strings

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

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

644 645
/*
  function: eval_num_suffix
646

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

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

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

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

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

unknown's avatar
unknown committed
707
static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err)
708 709 710 711
{
  ulonglong num;

  num= eval_num_suffix(arg, err, (char*) optp->name);  
unknown's avatar
unknown committed
712 713 714 715 716 717
  return getopt_ull_limit_value(num, optp);
}


ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp)
{
718
  if ((ulonglong) num > (ulonglong) optp->max_value &&
unknown's avatar
unknown committed
719
      optp->max_value) /* if max value is not set -> no upper limit */
720
    num= (ulonglong) optp->max_value;
unknown's avatar
unknown committed
721 722 723 724 725
  if (optp->block_size > 1)
  {
    num/= (ulonglong) optp->block_size;
    num*= (ulonglong) optp->block_size;
  }
726 727
  if (num < (ulonglong) optp->min_value)
    num= (ulonglong) optp->min_value;
unknown's avatar
unknown committed
728
  return num;
729
}
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
/*
  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;
  }
}


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

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

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


/*
  function: my_print_options

  Print help for all options and variables.
*/

unknown's avatar
unknown committed
812 813
#include <help_start.h>

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

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

#include <help_end.h>