#include "options.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "getopt.h"
#include "version.h"
Options option;
const char *program_name;
static const int DEFAULT_JUMP_VALUE = 5;
static const char *const DEFAULT_FUNCTION_NAME = "in_word_set";
static const char *const DEFAULT_SLOT_NAME = "name";
static const char *const DEFAULT_INITIALIZER_SUFFIX = "";
static const char *const DEFAULT_CLASS_NAME = "Perfect_Hash";
static const char *const DEFAULT_HASH_NAME = "hash";
static const char *const DEFAULT_WORDLIST_NAME = "wordlist";
static const char *const DEFAULT_LENGTHTABLE_NAME = "lengthtable";
static const char *const DEFAULT_STRINGPOOL_NAME = "stringpool";
static const char *const DEFAULT_DELIMITERS = ",";
void
Options::short_usage (FILE * stream)
{
fprintf (stream,
"Try '%s --help' for more information.\n", program_name);
}
void
Options::long_usage (FILE * stream)
{
fprintf (stream,
"GNU 'gperf' generates perfect hash functions.\n");
fprintf (stream, "\n");
fprintf (stream,
"Usage: %s [OPTION]... [INPUT-FILE]\n",
program_name);
fprintf (stream, "\n");
fprintf (stream,
"If a long option shows an argument as mandatory, then it is mandatory\n"
"for the equivalent short option also.\n");
fprintf (stream, "\n");
fprintf (stream,
"Output file location:\n");
fprintf (stream,
" --output-file=FILE Write output to specified file.\n");
fprintf (stream,
"The results are written to standard output if no output file is specified\n"
"or if it is -.\n");
fprintf (stream, "\n");
fprintf (stream,
"Input file interpretation:\n");
fprintf (stream,
" -e, --delimiters=DELIMITER-LIST\n"
" Allow user to provide a string containing delimiters\n"
" used to separate keywords from their attributes.\n"
" Default is \",\".\n");
fprintf (stream,
" -t, --struct-type Allows the user to include a structured type\n"
" declaration for generated code. Any text before %%%%\n"
" is considered part of the type declaration. Key\n"
" words and additional fields may follow this, one\n"
" group of fields per line.\n");
fprintf (stream,
" --ignore-case Consider upper and lower case ASCII characters as\n"
" equivalent. Note that locale dependent case mappings\n"
" are ignored.\n");
fprintf (stream, "\n");
fprintf (stream,
"Language for the output code:\n");
fprintf (stream,
" -L, --language=LANGUAGE-NAME\n"
" Generates code in the specified language. Languages\n"
" handled are currently C++, ANSI-C, C, and KR-C. The\n"
" default is C.\n");
fprintf (stream, "\n");
fprintf (stream,
"Details in the output code:\n");
fprintf (stream,
" -K, --slot-name=NAME Select name of the keyword component in the keyword\n"
" structure.\n");
fprintf (stream,
" -F, --initializer-suffix=INITIALIZERS\n"
" Initializers for additional components in the keyword\n"
" structure.\n");
fprintf (stream,
" -H, --hash-function-name=NAME\n"
" Specify name of generated hash function. Default is\n"
" 'hash'.\n");
fprintf (stream,
" -N, --lookup-function-name=NAME\n"
" Specify name of generated lookup function. Default\n"
" name is 'in_word_set'.\n");
fprintf (stream,
" -Z, --class-name=NAME Specify name of generated C++ class. Default name is\n"
" 'Perfect_Hash'.\n");
fprintf (stream,
" -7, --seven-bit Assume 7-bit characters.\n");
fprintf (stream,
" -l, --compare-lengths Compare key lengths before trying a string\n"
" comparison. This is necessary if the keywords\n"
" contain NUL bytes. It also helps cut down on the\n"
" number of string comparisons made during the lookup.\n");
fprintf (stream,
" -c, --compare-strncmp Generate comparison code using strncmp rather than\n"
" strcmp.\n");
fprintf (stream,
" -C, --readonly-tables Make the contents of generated lookup tables\n"
" constant, i.e., readonly.\n");
fprintf (stream,
" -E, --enum Define constant values using an enum local to the\n"
" lookup function rather than with defines.\n");
fprintf (stream,
" -I, --includes Include the necessary system include file <string.h>\n"
" at the beginning of the code.\n");
fprintf (stream,
" -G, --global-table Generate the static table of keywords as a static\n"
" global variable, rather than hiding it inside of the\n"
" lookup function (which is the default behavior).\n");
fprintf (stream,
" -P, --pic Optimize the generated table for inclusion in shared\n"
" libraries. This reduces the startup time of programs\n"
" using a shared library containing the generated code.\n");
fprintf (stream,
" -Q, --string-pool-name=NAME\n"
" Specify name of string pool generated by option --pic.\n"
" Default name is 'stringpool'.\n");
fprintf (stream,
" --null-strings Use NULL strings instead of empty strings for empty\n"
" keyword table entries.\n");
fprintf (stream,
" -W, --word-array-name=NAME\n"
" Specify name of word list array. Default name is\n"
" 'wordlist'.\n");
fprintf (stream,
" --length-table-name=NAME\n"
" Specify name of length table array. Default name is\n"
" 'lengthtable'.\n");
fprintf (stream,
" -S, --switch=COUNT Causes the generated C code to use a switch\n"
" statement scheme, rather than an array lookup table.\n"
" This can lead to a reduction in both time and space\n"
" requirements for some keyfiles. The COUNT argument\n"
" determines how many switch statements are generated.\n"
" A value of 1 generates 1 switch containing all the\n"
" elements, a value of 2 generates 2 tables with 1/2\n"
" the elements in each table, etc. If COUNT is very\n"
" large, say 1000000, the generated C code does a\n"
" binary search.\n");
fprintf (stream,
" -T, --omit-struct-type\n"
" Prevents the transfer of the type declaration to the\n"
" output file. Use this option if the type is already\n"
" defined elsewhere.\n");
fprintf (stream, "\n");
fprintf (stream,
"Algorithm employed by gperf:\n");
fprintf (stream,
" -k, --key-positions=KEYS\n"
" Select the key positions used in the hash function.\n"
" The allowable choices range between 1-%d, inclusive.\n"
" The positions are separated by commas, ranges may be\n"
" used, and key positions may occur in any order.\n"
" Also, the meta-character '*' causes the generated\n"
" hash function to consider ALL key positions, and $\n"
" indicates the \"final character\" of a key, e.g.,\n"
" $,1,2,4,6-10.\n",
Positions::MAX_KEY_POS);
fprintf (stream,
" -D, --duplicates Handle keywords that hash to duplicate values. This\n"
" is useful for certain highly redundant keyword sets.\n");
fprintf (stream,
" -m, --multiple-iterations=ITERATIONS\n"
" Perform multiple choices of the -i and -j values,\n"
" and choose the best results. This increases the\n"
" running time by a factor of ITERATIONS but does a\n"
" good job minimizing the generated table size.\n");
fprintf (stream,
" -i, --initial-asso=N Provide an initial value for the associate values\n"
" array. Default is 0. Setting this value larger helps\n"
" inflate the size of the final table.\n");
fprintf (stream,
" -j, --jump=JUMP-VALUE Affects the \"jump value\", i.e., how far to advance\n"
" the associated character value upon collisions. Must\n"
" be an odd number, default is %d.\n",
DEFAULT_JUMP_VALUE);
fprintf (stream,
" -n, --no-strlen Do not include the length of the keyword when\n"
" computing the hash function.\n");
fprintf (stream,
" -r, --random Utilizes randomness to initialize the associated\n"
" values table.\n");
fprintf (stream,
" -s, --size-multiple=N Affects the size of the generated hash table. The\n"
" numeric argument N indicates \"how many times larger\n"
" or smaller\" the associated value range should be,\n"
" in relationship to the number of keys, e.g. a value\n"
" of 3 means \"allow the maximum associated value to\n"
" be about 3 times larger than the number of input\n"
" keys\". Conversely, a value of 1/3 means \"make the\n"
" maximum associated value about 3 times smaller than\n"
" the number of input keys\". A larger table should\n"
" decrease the time required for an unsuccessful\n"
" search, at the expense of extra table space. Default\n"
" value is 1.\n");
fprintf (stream, "\n");
fprintf (stream,
"Informative output:\n"
" -h, --help Print this message.\n"
" -v, --version Print the gperf version number.\n"
" -d, --debug Enables the debugging option (produces verbose\n"
" output to the standard error).\n");
fprintf (stream, "\n");
fprintf (stream,
"Report bugs to <bug-gnu-gperf@gnu.org>.\n");
}
void
Options::print_options () const
{
printf ("/* Command-line: ");
for (int i = 0; i < _argument_count; i++)
{
const char *arg = _argument_vector[i];
if (*arg == '-')
{
putchar (*arg);
arg++;
if (*arg >= 'A' && *arg <= 'Z' || *arg >= 'a' && *arg <= 'z')
{
putchar (*arg);
arg++;
}
else if (*arg == '-')
{
do
{
putchar (*arg);
arg++;
}
while (*arg >= 'A' && *arg <= 'Z' || *arg >= 'a' && *arg <= 'z' || *arg == '-');
if (*arg == '=')
{
putchar (*arg);
arg++;
}
}
}
if (strpbrk (arg, "\t\n !\"#$&'()*;<>?[\\]`{|}~") != NULL)
{
if (strchr (arg, '\'') != NULL)
{
putchar ('"');
for (; *arg; arg++)
{
if (*arg == '\"' || *arg == '\\' || *arg == '$' || *arg == '`')
putchar ('\\');
putchar (*arg);
}
putchar ('"');
}
else
{
putchar ('\'');
for (; *arg; arg++)
{
if (*arg == '\\')
putchar ('\\');
putchar (*arg);
}
putchar ('\'');
}
}
else
printf ("%s", arg);
printf (" ");
}
printf (" */");
}
class PositionStringParser
{
public:
PositionStringParser (const char *str,
int low_bound, int high_bound,
int end_word_marker, int error_value, int end_marker);
int nextPosition ();
private:
const char * _str;
int const _low_bound;
int const _high_bound;
int const _end_word_marker;
int const _error_value;
int const _end_marker;
bool _in_range;
int _range_upper_bound;
int _range_curr_value;
};
PositionStringParser::PositionStringParser (const char *str,
int low_bound, int high_bound,
int end_word_marker, int error_value, int end_marker)
: _str (str),
_low_bound (low_bound),
_high_bound (high_bound),
_end_word_marker (end_word_marker),
_error_value (error_value),
_end_marker (end_marker),
_in_range (false)
{
}
int
PositionStringParser::nextPosition ()
{
if (_in_range)
{
if (++_range_curr_value >= _range_upper_bound)
_in_range = false;
return _range_curr_value;
}
else
{
while (*_str)
switch (*_str)
{
case ',':
_str++;
break;
case '$':
_str++;
return _end_word_marker;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
int curr_value;
for (curr_value = 0; isdigit (static_cast<unsigned char>(*_str)); _str++)
curr_value = curr_value * 10 + (*_str - '0');
if (*_str == '-')
{
_str++;
_in_range = true;
for (_range_upper_bound = 0;
isdigit (static_cast<unsigned char>(*_str));
_str++)
_range_upper_bound = _range_upper_bound * 10 + (*_str - '0');
if (!(_range_upper_bound > curr_value && _range_upper_bound <= _high_bound))
return _error_value;
_range_curr_value = curr_value;
}
if (!(curr_value >= _low_bound && curr_value <= _high_bound))
return _error_value;
return curr_value;
}
default:
return _error_value;
}
return _end_marker;
}
}
Options::Options ()
: _option_word (C),
_input_file_name (NULL),
_output_file_name (NULL),
_language (NULL),
_jump (DEFAULT_JUMP_VALUE),
_initial_asso_value (0),
_asso_iterations (0),
_total_switches (1),
_size_multiple (1),
_function_name (DEFAULT_FUNCTION_NAME),
_slot_name (DEFAULT_SLOT_NAME),
_initializer_suffix (DEFAULT_INITIALIZER_SUFFIX),
_class_name (DEFAULT_CLASS_NAME),
_hash_name (DEFAULT_HASH_NAME),
_wordlist_name (DEFAULT_WORDLIST_NAME),
_lengthtable_name (DEFAULT_LENGTHTABLE_NAME),
_stringpool_name (DEFAULT_STRINGPOOL_NAME),
_delimiters (DEFAULT_DELIMITERS),
_key_positions ()
{
}
Options::~Options ()
{
if (_option_word & DEBUG)
{
fprintf (stderr, "\ndumping Options:"
"\nTYPE is........: %s"
"\nUPPERLOWER is..: %s"
"\nKRC is.........: %s"
"\nC is...........: %s"
"\nANSIC is.......: %s"
"\nCPLUSPLUS is...: %s"
"\nSEVENBIT is....: %s"
"\nLENTABLE is....: %s"
"\nCOMP is........: %s"
"\nCONST is.......: %s"
"\nENUM is........: %s"
"\nINCLUDE is.....: %s"
"\nGLOBAL is......: %s"
"\nNULLSTRINGS is.: %s"
"\nSHAREDLIB is...: %s"
"\nSWITCH is......: %s"
"\nNOTYPE is......: %s"
"\nDUP is.........: %s"
"\nNOLENGTH is....: %s"
"\nRANDOM is......: %s"
"\nDEBUG is.......: %s"
"\nlookup function name = %s"
"\nhash function name = %s"
"\nword list name = %s"
"\nlength table name = %s"
"\nstring pool name = %s"
"\nslot name = %s"
"\ninitializer suffix = %s"
"\nasso_values iterations = %d"
"\njump value = %d"
"\nhash table size multiplier = %g"
"\ninitial associated value = %d"
"\ndelimiters = %s"
"\nnumber of switch statements = %d\n",
_option_word & TYPE ? "enabled" : "disabled",
_option_word & UPPERLOWER ? "enabled" : "disabled",
_option_word & KRC ? "enabled" : "disabled",
_option_word & C ? "enabled" : "disabled",
_option_word & ANSIC ? "enabled" : "disabled",
_option_word & CPLUSPLUS ? "enabled" : "disabled",
_option_word & SEVENBIT ? "enabled" : "disabled",
_option_word & LENTABLE ? "enabled" : "disabled",
_option_word & COMP ? "enabled" : "disabled",
_option_word & CONST ? "enabled" : "disabled",
_option_word & ENUM ? "enabled" : "disabled",
_option_word & INCLUDE ? "enabled" : "disabled",
_option_word & GLOBAL ? "enabled" : "disabled",
_option_word & NULLSTRINGS ? "enabled" : "disabled",
_option_word & SHAREDLIB ? "enabled" : "disabled",
_option_word & SWITCH ? "enabled" : "disabled",
_option_word & NOTYPE ? "enabled" : "disabled",
_option_word & DUP ? "enabled" : "disabled",
_option_word & NOLENGTH ? "enabled" : "disabled",
_option_word & RANDOM ? "enabled" : "disabled",
_option_word & DEBUG ? "enabled" : "disabled",
_function_name, _hash_name, _wordlist_name, _lengthtable_name,
_stringpool_name, _slot_name, _initializer_suffix,
_asso_iterations, _jump, _size_multiple, _initial_asso_value,
_delimiters, _total_switches);
if (_key_positions.is_useall())
fprintf (stderr, "all characters are used in the hash function\n");
else
{
fprintf (stderr, "maximum keysig size = %d\nkey positions are: \n",
_key_positions.get_size());
PositionIterator iter = _key_positions.iterator();
for (int pos; (pos = iter.next()) != PositionIterator::EOS; )
if (pos == Positions::LASTCHAR)
fprintf (stderr, "$\n");
else
fprintf (stderr, "%d\n", pos + 1);
}
fprintf (stderr, "finished dumping Options\n");
}
}
void
Options::set_language (const char *language)
{
if (_language == NULL)
{
_language = language;
_option_word &= ~(KRC | C | ANSIC | CPLUSPLUS);
if (!strcmp (language, "KR-C"))
_option_word |= KRC;
else if (!strcmp (language, "C"))
_option_word |= C;
else if (!strcmp (language, "ANSI-C"))
_option_word |= ANSIC;
else if (!strcmp (language, "C++"))
_option_word |= CPLUSPLUS;
else
{
fprintf (stderr, "unsupported language option %s, defaulting to C\n",
language);
_option_word |= C;
}
}
}
void
Options::set_total_switches (int total_switches)
{
if (!(_option_word & SWITCH))
{
_option_word |= SWITCH;
_total_switches = total_switches;
}
}
void
Options::set_function_name (const char *name)
{
if (_function_name == DEFAULT_FUNCTION_NAME)
_function_name = name;
}
void
Options::set_slot_name (const char *name)
{
if (_slot_name == DEFAULT_SLOT_NAME)
_slot_name = name;
}
void
Options::set_initializer_suffix (const char *initializers)
{
if (_initializer_suffix == DEFAULT_INITIALIZER_SUFFIX)
_initializer_suffix = initializers;
}
void
Options::set_class_name (const char *name)
{
if (_class_name == DEFAULT_CLASS_NAME)
_class_name = name;
}
void
Options::set_hash_name (const char *name)
{
if (_hash_name == DEFAULT_HASH_NAME)
_hash_name = name;
}
void
Options::set_wordlist_name (const char *name)
{
if (_wordlist_name == DEFAULT_WORDLIST_NAME)
_wordlist_name = name;
}
void
Options::set_lengthtable_name (const char *name)
{
if (_lengthtable_name == DEFAULT_LENGTHTABLE_NAME)
_lengthtable_name = name;
}
void
Options::set_stringpool_name (const char *name)
{
if (_stringpool_name == DEFAULT_STRINGPOOL_NAME)
_stringpool_name = name;
}
void
Options::set_delimiters (const char *delimiters)
{
if (_delimiters == DEFAULT_DELIMITERS)
_delimiters = delimiters;
}
static const struct option long_options[] =
{
{ "output-file", required_argument, NULL, CHAR_MAX + 1 },
{ "ignore-case", no_argument, NULL, CHAR_MAX + 2 },
{ "delimiters", required_argument, NULL, 'e' },
{ "struct-type", no_argument, NULL, 't' },
{ "language", required_argument, NULL, 'L' },
{ "slot-name", required_argument, NULL, 'K' },
{ "initializer-suffix", required_argument, NULL, 'F' },
{ "hash-fn-name", required_argument, NULL, 'H' },
{ "hash-function-name", required_argument, NULL, 'H' },
{ "lookup-fn-name", required_argument, NULL, 'N' },
{ "lookup-function-name", required_argument, NULL, 'N' },
{ "class-name", required_argument, NULL, 'Z' },
{ "seven-bit", no_argument, NULL, '7' },
{ "compare-strncmp", no_argument, NULL, 'c' },
{ "readonly-tables", no_argument, NULL, 'C' },
{ "enum", no_argument, NULL, 'E' },
{ "includes", no_argument, NULL, 'I' },
{ "global-table", no_argument, NULL, 'G' },
{ "word-array-name", required_argument, NULL, 'W' },
{ "length-table-name", required_argument, NULL, CHAR_MAX + 4 },
{ "switch", required_argument, NULL, 'S' },
{ "omit-struct-type", no_argument, NULL, 'T' },
{ "key-positions", required_argument, NULL, 'k' },
{ "compare-strlen", no_argument, NULL, 'l' },
{ "compare-lengths", no_argument, NULL, 'l' },
{ "duplicates", no_argument, NULL, 'D' },
{ "fast", required_argument, NULL, 'f' },
{ "initial-asso", required_argument, NULL, 'i' },
{ "jump", required_argument, NULL, 'j' },
{ "multiple-iterations", required_argument, NULL, 'm' },
{ "no-strlen", no_argument, NULL, 'n' },
{ "occurrence-sort", no_argument, NULL, 'o' },
{ "optimized-collision-resolution", no_argument, NULL, 'O' },
{ "pic", no_argument, NULL, 'P' },
{ "string-pool-name", required_argument, NULL, 'Q' },
{ "null-strings", no_argument, NULL, CHAR_MAX + 3 },
{ "random", no_argument, NULL, 'r' },
{ "size-multiple", required_argument, NULL, 's' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
{ "debug", no_argument, NULL, 'd' },
{ NULL, no_argument, NULL, 0 }
};
void
Options::parse_options (int argc, char *argv[])
{
int option_char;
program_name = argv[0];
_argument_count = argc;
_argument_vector = argv;
while ((option_char =
getopt_long (_argument_count, _argument_vector,
"acCdDe:Ef:F:gGhH:i:Ij:k:K:lL:m:nN:oOpPQ:rs:S:tTvW:Z:7",
long_options, NULL))
!= -1)
{
switch (option_char)
{
case 'a':
break;
case 'c':
{
_option_word |= COMP;
break;
}
case 'C':
{
_option_word |= CONST;
break;
}
case 'd':
{
_option_word |= DEBUG;
fprintf (stderr, "Starting program %s, version %s, with debugging on.\n",
program_name, version_string);
break;
}
case 'D':
{
_option_word |= DUP;
break;
}
case 'e':
{
_delimiters = optarg;
break;
}
case 'E':
{
_option_word |= ENUM;
break;
}
case 'f':
break;
case 'F':
{
_initializer_suffix = optarg;
break;
}
case 'g':
break;
case 'G':
{
_option_word |= GLOBAL;
break;
}
case 'h':
{
long_usage (stdout);
exit (0);
}
case 'H':
{
_hash_name = optarg;
break;
}
case 'i':
{
if ((_initial_asso_value = atoi (optarg)) < 0)
fprintf (stderr, "Initial value %d should be non-zero, ignoring and continuing.\n", _initial_asso_value);
if (option[RANDOM])
fprintf (stderr, "warning, -r option superceeds -i, ignoring -i option and continuing\n");
break;
}
case 'I':
{
_option_word |= INCLUDE;
break;
}
case 'j':
{
if ((_jump = atoi (optarg)) < 0)
{
fprintf (stderr, "Jump value %d must be a positive number.\n", _jump);
short_usage (stderr);
exit (1);
}
else if (_jump && ((_jump % 2) == 0))
fprintf (stderr, "Jump value %d should be odd, adding 1 and continuing...\n", _jump++);
break;
}
case 'k':
{
_option_word |= POSITIONS;
const int BAD_VALUE = -3;
const int EOS = PositionIterator::EOS;
int value;
PositionStringParser sparser (optarg, 1, Positions::MAX_KEY_POS, Positions::LASTCHAR, BAD_VALUE, EOS);
if (optarg [0] == '*')
_key_positions.set_useall(true);
else
{
_key_positions.set_useall(false);
int *key_positions = _key_positions.pointer();
int *key_pos;
for (key_pos = key_positions; (value = sparser.nextPosition()) != EOS; key_pos++)
{
if (value == BAD_VALUE)
{
fprintf (stderr, "Invalid position value or range, use 1,2,3-%d,'$' or '*'.\n",
Positions::MAX_KEY_POS);
short_usage (stderr);
exit (1);
}
if (key_pos - key_positions == Positions::MAX_SIZE)
{
fprintf (stderr, "Duplicate key positions selected\n");
short_usage (stderr);
exit (1);
}
if (value != Positions::LASTCHAR)
value = value - 1;
*key_pos = value;
}
unsigned int total_keysig_size = key_pos - key_positions;
if (total_keysig_size == 0)
{
fprintf (stderr, "No key positions selected.\n");
short_usage (stderr);
exit (1);
}
_key_positions.set_size (total_keysig_size);
if (! _key_positions.sort())
{
fprintf (stderr, "Duplicate key positions selected\n");
short_usage (stderr);
exit (1);
}
}
break;
}
case 'K':
{
_slot_name = optarg;
break;
}
case 'l':
{
_option_word |= LENTABLE;
break;
}
case 'L':
{
_language = NULL;
set_language (optarg);
break;
}
case 'm':
{
if ((_asso_iterations = atoi (optarg)) < 0)
{
fprintf (stderr, "asso_iterations value must not be negative, assuming 0\n");
_asso_iterations = 0;
}
break;
}
case 'n':
{
_option_word |= NOLENGTH;
break;
}
case 'N':
{
_function_name = optarg;
break;
}
case 'o':
break;
case 'O':
break;
case 'p':
break;
case 'P':
{
_option_word |= SHAREDLIB;
break;
}
case 'Q':
{
_stringpool_name = optarg;
break;
}
case 'r':
{
_option_word |= RANDOM;
if (_initial_asso_value != 0)
fprintf (stderr, "warning, -r option supersedes -i, disabling -i option and continuing\n");
break;
}
case 's':
{
float numerator;
float denominator = 1;
bool invalid = false;
char *endptr;
numerator = strtod (optarg, &endptr);
if (endptr == optarg)
invalid = true;
else if (*endptr != '\0')
{
if (*endptr == '/')
{
char *denomptr = endptr + 1;
denominator = strtod (denomptr, &endptr);
if (endptr == denomptr || *endptr != '\0')
invalid = true;
}
else
invalid = true;
}
if (invalid)
{
fprintf (stderr, "Invalid value for option -s.\n");
short_usage (stderr);
exit (1);
}
_size_multiple = numerator / denominator;
if (_size_multiple < 0)
_size_multiple = 1 / (-_size_multiple);
if (_size_multiple == 0)
_size_multiple = 1;
if (_size_multiple > 50)
fprintf (stderr, "Size multiple %g is excessive, did you really mean this?! (try '%s --help' for help)\n", _size_multiple, program_name);
else if (_size_multiple < 0.01f)
fprintf (stderr, "Size multiple %g is extremely small, did you really mean this?! (try '%s --help' for help)\n", _size_multiple, program_name);
break;
}
case 'S':
{
_option_word |= SWITCH;
_total_switches = atoi (optarg);
if (_total_switches <= 0)
{
fprintf (stderr, "number of switches %s must be a positive number\n", optarg);
short_usage (stderr);
exit (1);
}
break;
}
case 't':
{
_option_word |= TYPE;
break;
}
case 'T':
{
_option_word |= NOTYPE;
break;
}
case 'v':
fprintf (stdout, "GNU gperf %s\n", version_string);
fprintf (stdout, "Copyright (C) %s Free Software Foundation, Inc.\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
",
"1989-1998, 2000-2004, 2006-2007");
fprintf (stdout, "Written by %s and %s.\n",
"Douglas C. Schmidt", "Bruno Haible");
exit (0);
case 'W':
{
_wordlist_name = optarg;
break;
}
case 'Z':
{
_class_name = optarg;
break;
}
case '7':
{
_option_word |= SEVENBIT;
break;
}
case CHAR_MAX + 1:
{
_output_file_name = optarg;
break;
}
case CHAR_MAX + 2:
{
_option_word |= UPPERLOWER;
break;
}
case CHAR_MAX + 3:
{
_option_word |= NULLSTRINGS;
break;
}
case CHAR_MAX + 4:
{
_lengthtable_name = optarg;
break;
}
default:
short_usage (stderr);
exit (1);
}
}
if (optind < argc)
_input_file_name = argv[optind++];
if (optind < argc)
{
fprintf (stderr, "Extra trailing arguments to %s.\n", program_name);
short_usage (stderr);
exit (1);
}
}
#ifndef __OPTIMIZE__
#define INLINE
#include "options.icc"
#undef INLINE
#endif