1 | /* Handles parsing the Options provided to the user. |
2 | Copyright (C) 1989-1998, 2000, 2002-2004, 2006-2009, 2011, 2016 Free Software Foundation, Inc. |
3 | Written by Douglas C. Schmidt <schmidt@ics.uci.edu> |
4 | and Bruno Haible <bruno@clisp.org>. |
5 | |
6 | This file is part of GNU GPERF. |
7 | |
8 | This program is free software: you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation; either version 3 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
20 | |
21 | /* Specification. */ |
22 | #include "options.h" |
23 | |
24 | #include <stdio.h> |
25 | #include <stdlib.h> /* declares atoi(), abs(), exit() */ |
26 | #include <string.h> /* declares strcmp() */ |
27 | #include <ctype.h> /* declares isdigit() */ |
28 | #include <limits.h> /* defines CHAR_MAX */ |
29 | #include "getopt.h" |
30 | #include "version.h" |
31 | |
32 | /* Global option coordinator for the entire program. */ |
33 | Options option; |
34 | |
35 | /* Records the program name. */ |
36 | const char *program_name; |
37 | |
38 | /* Size to jump on a collision. */ |
39 | static const int DEFAULT_JUMP_VALUE = 5; |
40 | |
41 | /* Default name for generated lookup function. */ |
42 | static const char *const DEFAULT_FUNCTION_NAME = "in_word_set" ; |
43 | |
44 | /* Default name for the key component. */ |
45 | static const char *const DEFAULT_SLOT_NAME = "name" ; |
46 | |
47 | /* Default struct initializer suffix. */ |
48 | static const char *const DEFAULT_INITIALIZER_SUFFIX = "" ; |
49 | |
50 | /* Default name for the generated class. */ |
51 | static const char *const DEFAULT_CLASS_NAME = "Perfect_Hash" ; |
52 | |
53 | /* Default name for generated hash function. */ |
54 | static const char *const DEFAULT_HASH_NAME = "hash" ; |
55 | |
56 | /* Default name for generated hash table array. */ |
57 | static const char *const DEFAULT_WORDLIST_NAME = "wordlist" ; |
58 | |
59 | /* Default name for generated length table array. */ |
60 | static const char *const DEFAULT_LENGTHTABLE_NAME = "lengthtable" ; |
61 | |
62 | /* Default name for string pool. */ |
63 | static const char *const DEFAULT_STRINGPOOL_NAME = "stringpool" ; |
64 | |
65 | /* Default prefix for constants. */ |
66 | static const char *const DEFAULT_CONSTANTS_PREFIX = "" ; |
67 | |
68 | /* Default delimiters that separate keywords from their attributes. */ |
69 | static const char *const DEFAULT_DELIMITERS = "," ; |
70 | |
71 | /* Prints program usage to given stream. */ |
72 | |
73 | void |
74 | Options::short_usage (FILE * stream) |
75 | { |
76 | fprintf (stream, |
77 | "Try '%s --help' for more information.\n" , program_name); |
78 | } |
79 | |
80 | void |
81 | Options::long_usage (FILE * stream) |
82 | { |
83 | fprintf (stream, |
84 | "GNU 'gperf' generates perfect hash functions.\n" ); |
85 | fprintf (stream, "\n" ); |
86 | fprintf (stream, |
87 | "Usage: %s [OPTION]... [INPUT-FILE]\n" , |
88 | program_name); |
89 | fprintf (stream, "\n" ); |
90 | fprintf (stream, |
91 | "If a long option shows an argument as mandatory, then it is mandatory\n" |
92 | "for the equivalent short option also.\n" ); |
93 | fprintf (stream, "\n" ); |
94 | fprintf (stream, |
95 | "Output file location:\n" ); |
96 | fprintf (stream, |
97 | " --output-file=FILE Write output to specified file.\n" ); |
98 | fprintf (stream, |
99 | "The results are written to standard output if no output file is specified\n" |
100 | "or if it is -.\n" ); |
101 | fprintf (stream, "\n" ); |
102 | fprintf (stream, |
103 | "Input file interpretation:\n" ); |
104 | fprintf (stream, |
105 | " -e, --delimiters=DELIMITER-LIST\n" |
106 | " Allow user to provide a string containing delimiters\n" |
107 | " used to separate keywords from their attributes.\n" |
108 | " Default is \",\".\n" ); |
109 | fprintf (stream, |
110 | " -t, --struct-type Allows the user to include a structured type\n" |
111 | " declaration for generated code. Any text before %%%%\n" |
112 | " is considered part of the type declaration. Key\n" |
113 | " words and additional fields may follow this, one\n" |
114 | " group of fields per line.\n" ); |
115 | fprintf (stream, |
116 | " --ignore-case Consider upper and lower case ASCII characters as\n" |
117 | " equivalent. Note that locale dependent case mappings\n" |
118 | " are ignored.\n" ); |
119 | fprintf (stream, "\n" ); |
120 | fprintf (stream, |
121 | "Language for the output code:\n" ); |
122 | fprintf (stream, |
123 | " -L, --language=LANGUAGE-NAME\n" |
124 | " Generates code in the specified language. Languages\n" |
125 | " handled are currently C++, ANSI-C, C, and KR-C. The\n" |
126 | " default is ANSI-C.\n" ); |
127 | fprintf (stream, "\n" ); |
128 | fprintf (stream, |
129 | "Details in the output code:\n" ); |
130 | fprintf (stream, |
131 | " -K, --slot-name=NAME Select name of the keyword component in the keyword\n" |
132 | " structure.\n" ); |
133 | fprintf (stream, |
134 | " -F, --initializer-suffix=INITIALIZERS\n" |
135 | " Initializers for additional components in the keyword\n" |
136 | " structure.\n" ); |
137 | fprintf (stream, |
138 | " -H, --hash-function-name=NAME\n" |
139 | " Specify name of generated hash function. Default is\n" |
140 | " 'hash'.\n" ); |
141 | fprintf (stream, |
142 | " -N, --lookup-function-name=NAME\n" |
143 | " Specify name of generated lookup function. Default\n" |
144 | " name is 'in_word_set'.\n" ); |
145 | fprintf (stream, |
146 | " -Z, --class-name=NAME Specify name of generated C++ class. Default name is\n" |
147 | " 'Perfect_Hash'.\n" ); |
148 | fprintf (stream, |
149 | " -7, --seven-bit Assume 7-bit characters.\n" ); |
150 | fprintf (stream, |
151 | " -l, --compare-lengths Compare key lengths before trying a string\n" |
152 | " comparison. This is necessary if the keywords\n" |
153 | " contain NUL bytes. It also helps cut down on the\n" |
154 | " number of string comparisons made during the lookup.\n" ); |
155 | fprintf (stream, |
156 | " -c, --compare-strncmp Generate comparison code using strncmp rather than\n" |
157 | " strcmp.\n" ); |
158 | fprintf (stream, |
159 | " -C, --readonly-tables Make the contents of generated lookup tables\n" |
160 | " constant, i.e., readonly.\n" ); |
161 | fprintf (stream, |
162 | " -E, --enum Define constant values using an enum local to the\n" |
163 | " lookup function rather than with defines.\n" ); |
164 | fprintf (stream, |
165 | " -I, --includes Include the necessary system include file <string.h>\n" |
166 | " at the beginning of the code.\n" ); |
167 | fprintf (stream, |
168 | " -G, --global-table Generate the static table of keywords as a static\n" |
169 | " global variable, rather than hiding it inside of the\n" |
170 | " lookup function (which is the default behavior).\n" ); |
171 | fprintf (stream, |
172 | " -P, --pic Optimize the generated table for inclusion in shared\n" |
173 | " libraries. This reduces the startup time of programs\n" |
174 | " using a shared library containing the generated code.\n" ); |
175 | fprintf (stream, |
176 | " -Q, --string-pool-name=NAME\n" |
177 | " Specify name of string pool generated by option --pic.\n" |
178 | " Default name is 'stringpool'.\n" ); |
179 | fprintf (stream, |
180 | " --null-strings Use NULL strings instead of empty strings for empty\n" |
181 | " keyword table entries.\n" ); |
182 | fprintf (stream, |
183 | " --constants-prefix=PREFIX\n" |
184 | " Specify prefix for the constants like TOTAL_KEYWORDS.\n" ); |
185 | fprintf (stream, |
186 | " -W, --word-array-name=NAME\n" |
187 | " Specify name of word list array. Default name is\n" |
188 | " 'wordlist'.\n" ); |
189 | fprintf (stream, |
190 | " --length-table-name=NAME\n" |
191 | " Specify name of length table array. Default name is\n" |
192 | " 'lengthtable'.\n" ); |
193 | fprintf (stream, |
194 | " -S, --switch=COUNT Causes the generated C code to use a switch\n" |
195 | " statement scheme, rather than an array lookup table.\n" |
196 | " This can lead to a reduction in both time and space\n" |
197 | " requirements for some keyfiles. The COUNT argument\n" |
198 | " determines how many switch statements are generated.\n" |
199 | " A value of 1 generates 1 switch containing all the\n" |
200 | " elements, a value of 2 generates 2 tables with 1/2\n" |
201 | " the elements in each table, etc. If COUNT is very\n" |
202 | " large, say 1000000, the generated C code does a\n" |
203 | " binary search.\n" ); |
204 | fprintf (stream, |
205 | " -T, --omit-struct-type\n" |
206 | " Prevents the transfer of the type declaration to the\n" |
207 | " output file. Use this option if the type is already\n" |
208 | " defined elsewhere.\n" ); |
209 | fprintf (stream, "\n" ); |
210 | fprintf (stream, |
211 | "Algorithm employed by gperf:\n" ); |
212 | fprintf (stream, |
213 | " -k, --key-positions=KEYS\n" |
214 | " Select the key positions used in the hash function.\n" |
215 | " The allowable choices range between 1-%d, inclusive.\n" |
216 | " The positions are separated by commas, ranges may be\n" |
217 | " used, and key positions may occur in any order.\n" |
218 | " Also, the meta-character '*' causes the generated\n" |
219 | " hash function to consider ALL key positions, and $\n" |
220 | " indicates the \"final character\" of a key, e.g.,\n" |
221 | " $,1,2,4,6-10.\n" , |
222 | Positions::MAX_KEY_POS); |
223 | fprintf (stream, |
224 | " -D, --duplicates Handle keywords that hash to duplicate values. This\n" |
225 | " is useful for certain highly redundant keyword sets.\n" ); |
226 | fprintf (stream, |
227 | " -m, --multiple-iterations=ITERATIONS\n" |
228 | " Perform multiple choices of the -i and -j values,\n" |
229 | " and choose the best results. This increases the\n" |
230 | " running time by a factor of ITERATIONS but does a\n" |
231 | " good job minimizing the generated table size.\n" ); |
232 | fprintf (stream, |
233 | " -i, --initial-asso=N Provide an initial value for the associate values\n" |
234 | " array. Default is 0. Setting this value larger helps\n" |
235 | " inflate the size of the final table.\n" ); |
236 | fprintf (stream, |
237 | " -j, --jump=JUMP-VALUE Affects the \"jump value\", i.e., how far to advance\n" |
238 | " the associated character value upon collisions. Must\n" |
239 | " be an odd number, default is %d.\n" , |
240 | DEFAULT_JUMP_VALUE); |
241 | fprintf (stream, |
242 | " -n, --no-strlen Do not include the length of the keyword when\n" |
243 | " computing the hash function.\n" ); |
244 | fprintf (stream, |
245 | " -r, --random Utilizes randomness to initialize the associated\n" |
246 | " values table.\n" ); |
247 | fprintf (stream, |
248 | " -s, --size-multiple=N Affects the size of the generated hash table. The\n" |
249 | " numeric argument N indicates \"how many times larger\n" |
250 | " or smaller\" the associated value range should be,\n" |
251 | " in relationship to the number of keys, e.g. a value\n" |
252 | " of 3 means \"allow the maximum associated value to\n" |
253 | " be about 3 times larger than the number of input\n" |
254 | " keys\". Conversely, a value of 1/3 means \"make the\n" |
255 | " maximum associated value about 3 times smaller than\n" |
256 | " the number of input keys\". A larger table should\n" |
257 | " decrease the time required for an unsuccessful\n" |
258 | " search, at the expense of extra table space. Default\n" |
259 | " value is 1.\n" ); |
260 | fprintf (stream, "\n" ); |
261 | fprintf (stream, |
262 | "Informative output:\n" |
263 | " -h, --help Print this message.\n" |
264 | " -v, --version Print the gperf version number.\n" |
265 | " -d, --debug Enables the debugging option (produces verbose\n" |
266 | " output to the standard error).\n" ); |
267 | fprintf (stream, "\n" ); |
268 | fprintf (stream, |
269 | "Report bugs to <bug-gperf@gnu.org>.\n" ); |
270 | } |
271 | |
272 | /* Prints the given options. */ |
273 | |
274 | void |
275 | Options::print_options () const |
276 | { |
277 | printf ("/* Command-line: " ); |
278 | |
279 | for (int i = 0; i < _argument_count; i++) |
280 | { |
281 | const char *arg = _argument_vector[i]; |
282 | |
283 | /* Escape arg if it contains shell metacharacters. */ |
284 | if (*arg == '-') |
285 | { |
286 | putchar (*arg); |
287 | arg++; |
288 | if ((*arg >= 'A' && *arg <= 'Z') || (*arg >= 'a' && *arg <= 'z')) |
289 | { |
290 | putchar (*arg); |
291 | arg++; |
292 | } |
293 | else if (*arg == '-') |
294 | { |
295 | do |
296 | { |
297 | putchar (*arg); |
298 | arg++; |
299 | } |
300 | while ((*arg >= 'A' && *arg <= 'Z') || (*arg >= 'a' && *arg <= 'z') || *arg == '-'); |
301 | if (*arg == '=') |
302 | { |
303 | putchar (*arg); |
304 | arg++; |
305 | } |
306 | } |
307 | } |
308 | if (strpbrk (arg, "\t\n !\"#$&'()*;<>?[\\]`{|}~" ) != NULL) |
309 | { |
310 | if (strchr (arg, '\'') != NULL) |
311 | { |
312 | putchar ('"'); |
313 | for (; *arg; arg++) |
314 | { |
315 | if (*arg == '\"' || *arg == '\\' || *arg == '$' || *arg == '`') |
316 | putchar ('\\'); |
317 | putchar (*arg); |
318 | } |
319 | putchar ('"'); |
320 | } |
321 | else |
322 | { |
323 | putchar ('\''); |
324 | for (; *arg; arg++) |
325 | { |
326 | if (*arg == '\\') |
327 | putchar ('\\'); |
328 | putchar (*arg); |
329 | } |
330 | putchar ('\''); |
331 | } |
332 | } |
333 | else |
334 | printf ("%s" , arg); |
335 | |
336 | printf (" " ); |
337 | } |
338 | |
339 | printf (" */" ); |
340 | } |
341 | |
342 | /* ------------------------------------------------------------------------- */ |
343 | |
344 | /* Parses a string denoting key positions. */ |
345 | |
346 | class PositionStringParser |
347 | { |
348 | public: |
349 | /* Initializes a key position string parser for string STR. */ |
350 | PositionStringParser (const char *str, |
351 | int low_bound, int high_bound, |
352 | int end_word_marker, int error_value, int end_marker); |
353 | /* Returns the next key position from the given string. */ |
354 | int nextPosition (); |
355 | private: |
356 | /* A pointer to the string provided by the user. */ |
357 | const char * _str; |
358 | /* Smallest possible value, inclusive. */ |
359 | int const _low_bound; |
360 | /* Greatest possible value, inclusive. */ |
361 | int const _high_bound; |
362 | /* A value marking the abstract "end of word" ( usually '$'). */ |
363 | int const _end_word_marker; |
364 | /* Error value returned when input is syntactically erroneous. */ |
365 | int const _error_value; |
366 | /* Value returned after last key is processed. */ |
367 | int const _end_marker; |
368 | /* Intermediate state for producing a range of positions. */ |
369 | bool _in_range; /* True while producing a range of positions. */ |
370 | int _range_upper_bound; /* Upper bound (inclusive) of the range. */ |
371 | int _range_curr_value; /* Last value returned. */ |
372 | }; |
373 | |
374 | /* Initializes a key position strng parser for string STR. */ |
375 | PositionStringParser::PositionStringParser (const char *str, |
376 | int low_bound, int high_bound, |
377 | int end_word_marker, int error_value, int end_marker) |
378 | : _str (str), |
379 | _low_bound (low_bound), |
380 | _high_bound (high_bound), |
381 | _end_word_marker (end_word_marker), |
382 | _error_value (error_value), |
383 | _end_marker (end_marker), |
384 | _in_range (false) |
385 | { |
386 | } |
387 | |
388 | /* Returns the next key position from the given string. */ |
389 | int |
390 | PositionStringParser::nextPosition () |
391 | { |
392 | if (_in_range) |
393 | { |
394 | /* We are inside a range. Return the next value from the range. */ |
395 | if (++_range_curr_value >= _range_upper_bound) |
396 | _in_range = false; |
397 | return _range_curr_value; |
398 | } |
399 | else |
400 | { |
401 | /* Continue parsing the given string. */ |
402 | while (*_str) |
403 | switch (*_str) |
404 | { |
405 | case ',': |
406 | /* Skip the comma. */ |
407 | _str++; |
408 | break; |
409 | case '$': |
410 | /* Valid key position. */ |
411 | _str++; |
412 | return _end_word_marker; |
413 | case '0': case '1': case '2': case '3': case '4': |
414 | case '5': case '6': case '7': case '8': case '9': |
415 | /* Valid key position. */ |
416 | { |
417 | int curr_value; |
418 | for (curr_value = 0; isdigit (static_cast<unsigned char>(*_str)); _str++) |
419 | curr_value = curr_value * 10 + (*_str - '0'); |
420 | |
421 | if (*_str == '-') |
422 | { |
423 | _str++; |
424 | /* Starting a range of key positions. */ |
425 | _in_range = true; |
426 | |
427 | for (_range_upper_bound = 0; |
428 | isdigit (static_cast<unsigned char>(*_str)); |
429 | _str++) |
430 | _range_upper_bound = _range_upper_bound * 10 + (*_str - '0'); |
431 | |
432 | /* Verify range's upper bound. */ |
433 | if (!(_range_upper_bound > curr_value && _range_upper_bound <= _high_bound)) |
434 | return _error_value; |
435 | _range_curr_value = curr_value; |
436 | } |
437 | |
438 | /* Verify range's lower bound. */ |
439 | if (!(curr_value >= _low_bound && curr_value <= _high_bound)) |
440 | return _error_value; |
441 | return curr_value; |
442 | } |
443 | default: |
444 | /* Invalid syntax. */ |
445 | return _error_value; |
446 | } |
447 | |
448 | return _end_marker; |
449 | } |
450 | } |
451 | |
452 | /* ------------------------------------------------------------------------- */ |
453 | |
454 | /* Sets the default Options. */ |
455 | |
456 | Options::Options () |
457 | : _option_word (ANSIC), |
458 | _input_file_name (NULL), |
459 | _output_file_name (NULL), |
460 | _language (NULL), |
461 | _jump (DEFAULT_JUMP_VALUE), |
462 | _initial_asso_value (0), |
463 | _asso_iterations (0), |
464 | _total_switches (1), |
465 | _size_multiple (1), |
466 | _function_name (DEFAULT_FUNCTION_NAME), |
467 | _slot_name (DEFAULT_SLOT_NAME), |
468 | _initializer_suffix (DEFAULT_INITIALIZER_SUFFIX), |
469 | _class_name (DEFAULT_CLASS_NAME), |
470 | _hash_name (DEFAULT_HASH_NAME), |
471 | _wordlist_name (DEFAULT_WORDLIST_NAME), |
472 | _lengthtable_name (DEFAULT_LENGTHTABLE_NAME), |
473 | _stringpool_name (DEFAULT_STRINGPOOL_NAME), |
474 | _constants_prefix (DEFAULT_CONSTANTS_PREFIX), |
475 | _delimiters (DEFAULT_DELIMITERS), |
476 | _key_positions () |
477 | { |
478 | } |
479 | |
480 | /* Dumps option status when debugging is enabled. */ |
481 | |
482 | Options::~Options () |
483 | { |
484 | if (_option_word & DEBUG) |
485 | { |
486 | fprintf (stderr, "\ndumping Options:" |
487 | "\nTYPE is........: %s" |
488 | "\nUPPERLOWER is..: %s" |
489 | "\nKRC is.........: %s" |
490 | "\nC is...........: %s" |
491 | "\nANSIC is.......: %s" |
492 | "\nCPLUSPLUS is...: %s" |
493 | "\nSEVENBIT is....: %s" |
494 | "\nLENTABLE is....: %s" |
495 | "\nCOMP is........: %s" |
496 | "\nCONST is.......: %s" |
497 | "\nENUM is........: %s" |
498 | "\nINCLUDE is.....: %s" |
499 | "\nGLOBAL is......: %s" |
500 | "\nNULLSTRINGS is.: %s" |
501 | "\nSHAREDLIB is...: %s" |
502 | "\nSWITCH is......: %s" |
503 | "\nNOTYPE is......: %s" |
504 | "\nDUP is.........: %s" |
505 | "\nNOLENGTH is....: %s" |
506 | "\nRANDOM is......: %s" |
507 | "\nDEBUG is.......: %s" |
508 | "\nlookup function name = %s" |
509 | "\nhash function name = %s" |
510 | "\nword list name = %s" |
511 | "\nlength table name = %s" |
512 | "\nstring pool name = %s" |
513 | "\nslot name = %s" |
514 | "\ninitializer suffix = %s" |
515 | "\nasso_values iterations = %d" |
516 | "\njump value = %d" |
517 | "\nhash table size multiplier = %g" |
518 | "\ninitial associated value = %d" |
519 | "\ndelimiters = %s" |
520 | "\nnumber of switch statements = %d\n" , |
521 | _option_word & TYPE ? "enabled" : "disabled" , |
522 | _option_word & UPPERLOWER ? "enabled" : "disabled" , |
523 | _option_word & KRC ? "enabled" : "disabled" , |
524 | _option_word & C ? "enabled" : "disabled" , |
525 | _option_word & ANSIC ? "enabled" : "disabled" , |
526 | _option_word & CPLUSPLUS ? "enabled" : "disabled" , |
527 | _option_word & SEVENBIT ? "enabled" : "disabled" , |
528 | _option_word & LENTABLE ? "enabled" : "disabled" , |
529 | _option_word & COMP ? "enabled" : "disabled" , |
530 | _option_word & CONST ? "enabled" : "disabled" , |
531 | _option_word & ENUM ? "enabled" : "disabled" , |
532 | _option_word & INCLUDE ? "enabled" : "disabled" , |
533 | _option_word & GLOBAL ? "enabled" : "disabled" , |
534 | _option_word & NULLSTRINGS ? "enabled" : "disabled" , |
535 | _option_word & SHAREDLIB ? "enabled" : "disabled" , |
536 | _option_word & SWITCH ? "enabled" : "disabled" , |
537 | _option_word & NOTYPE ? "enabled" : "disabled" , |
538 | _option_word & DUP ? "enabled" : "disabled" , |
539 | _option_word & NOLENGTH ? "enabled" : "disabled" , |
540 | _option_word & RANDOM ? "enabled" : "disabled" , |
541 | _option_word & DEBUG ? "enabled" : "disabled" , |
542 | _function_name, _hash_name, _wordlist_name, _lengthtable_name, |
543 | _stringpool_name, _slot_name, _initializer_suffix, |
544 | _asso_iterations, _jump, _size_multiple, _initial_asso_value, |
545 | _delimiters, _total_switches); |
546 | if (_key_positions.is_useall()) |
547 | fprintf (stderr, "all characters are used in the hash function\n" ); |
548 | else |
549 | { |
550 | fprintf (stderr, "maximum keysig size = %d\nkey positions are: \n" , |
551 | _key_positions.get_size()); |
552 | |
553 | PositionIterator iter = _key_positions.iterator(); |
554 | for (int pos; (pos = iter.next()) != PositionIterator::EOS; ) |
555 | if (pos == Positions::LASTCHAR) |
556 | fprintf (stderr, "$\n" ); |
557 | else |
558 | fprintf (stderr, "%d\n" , pos + 1); |
559 | } |
560 | |
561 | fprintf (stderr, "finished dumping Options\n" ); |
562 | } |
563 | } |
564 | |
565 | |
566 | /* Sets the output language, if not already set. */ |
567 | void |
568 | Options::set_language (const char *language) |
569 | { |
570 | if (_language == NULL) |
571 | { |
572 | _language = language; |
573 | _option_word &= ~(KRC | C | ANSIC | CPLUSPLUS); |
574 | if (!strcmp (language, "KR-C" )) |
575 | _option_word |= KRC; |
576 | else if (!strcmp (language, "C" )) |
577 | _option_word |= C; |
578 | else if (!strcmp (language, "ANSI-C" )) |
579 | _option_word |= ANSIC; |
580 | else if (!strcmp (language, "C++" )) |
581 | _option_word |= CPLUSPLUS; |
582 | else |
583 | { |
584 | fprintf (stderr, |
585 | "unsupported language option %s, defaulting to ANSI-C\n" , |
586 | language); |
587 | _option_word |= ANSIC; |
588 | } |
589 | } |
590 | } |
591 | |
592 | /* Sets the total number of switch statements, if not already set. */ |
593 | void |
594 | Options::set_total_switches (int total_switches) |
595 | { |
596 | if (!(_option_word & SWITCH)) |
597 | { |
598 | _option_word |= SWITCH; |
599 | _total_switches = total_switches; |
600 | } |
601 | } |
602 | |
603 | /* Sets the generated function name, if not already set. */ |
604 | void |
605 | Options::set_function_name (const char *name) |
606 | { |
607 | if (_function_name == DEFAULT_FUNCTION_NAME) |
608 | _function_name = name; |
609 | } |
610 | |
611 | /* Sets the keyword key name, if not already set. */ |
612 | void |
613 | Options::set_slot_name (const char *name) |
614 | { |
615 | if (_slot_name == DEFAULT_SLOT_NAME) |
616 | _slot_name = name; |
617 | } |
618 | |
619 | /* Sets the struct initializer suffix, if not already set. */ |
620 | void |
621 | Options::set_initializer_suffix (const char *initializers) |
622 | { |
623 | if (_initializer_suffix == DEFAULT_INITIALIZER_SUFFIX) |
624 | _initializer_suffix = initializers; |
625 | } |
626 | |
627 | /* Sets the generated class name, if not already set. */ |
628 | void |
629 | Options::set_class_name (const char *name) |
630 | { |
631 | if (_class_name == DEFAULT_CLASS_NAME) |
632 | _class_name = name; |
633 | } |
634 | |
635 | /* Sets the hash function name, if not already set. */ |
636 | void |
637 | Options::set_hash_name (const char *name) |
638 | { |
639 | if (_hash_name == DEFAULT_HASH_NAME) |
640 | _hash_name = name; |
641 | } |
642 | |
643 | /* Sets the hash table array name, if not already set. */ |
644 | void |
645 | Options::set_wordlist_name (const char *name) |
646 | { |
647 | if (_wordlist_name == DEFAULT_WORDLIST_NAME) |
648 | _wordlist_name = name; |
649 | } |
650 | |
651 | /* Sets the length table array name, if not already set. */ |
652 | void |
653 | Options::set_lengthtable_name (const char *name) |
654 | { |
655 | if (_lengthtable_name == DEFAULT_LENGTHTABLE_NAME) |
656 | _lengthtable_name = name; |
657 | } |
658 | |
659 | /* Sets the prefix for the constants, if not already set. */ |
660 | void |
661 | Options::set_constants_prefix (const char *prefix) |
662 | { |
663 | if (_constants_prefix == DEFAULT_CONSTANTS_PREFIX) |
664 | _constants_prefix = prefix; |
665 | } |
666 | |
667 | /* Sets the string pool name, if not already set. */ |
668 | void |
669 | Options::set_stringpool_name (const char *name) |
670 | { |
671 | if (_stringpool_name == DEFAULT_STRINGPOOL_NAME) |
672 | _stringpool_name = name; |
673 | } |
674 | |
675 | /* Sets the delimiters string, if not already set. */ |
676 | void |
677 | Options::set_delimiters (const char *delimiters) |
678 | { |
679 | if (_delimiters == DEFAULT_DELIMITERS) |
680 | _delimiters = delimiters; |
681 | } |
682 | |
683 | |
684 | /* Parses the command line Options and sets appropriate flags in option_word. */ |
685 | |
686 | static const struct option long_options[] = |
687 | { |
688 | { "output-file" , required_argument, NULL, CHAR_MAX + 1 }, |
689 | { "ignore-case" , no_argument, NULL, CHAR_MAX + 2 }, |
690 | { "delimiters" , required_argument, NULL, 'e' }, |
691 | { "struct-type" , no_argument, NULL, 't' }, |
692 | { "language" , required_argument, NULL, 'L' }, |
693 | { "slot-name" , required_argument, NULL, 'K' }, |
694 | { "initializer-suffix" , required_argument, NULL, 'F' }, |
695 | { "hash-fn-name" , required_argument, NULL, 'H' }, /* backward compatibility */ |
696 | { "hash-function-name" , required_argument, NULL, 'H' }, |
697 | { "lookup-fn-name" , required_argument, NULL, 'N' }, /* backward compatibility */ |
698 | { "lookup-function-name" , required_argument, NULL, 'N' }, |
699 | { "class-name" , required_argument, NULL, 'Z' }, |
700 | { "seven-bit" , no_argument, NULL, '7' }, |
701 | { "compare-strncmp" , no_argument, NULL, 'c' }, |
702 | { "readonly-tables" , no_argument, NULL, 'C' }, |
703 | { "enum" , no_argument, NULL, 'E' }, |
704 | { "includes" , no_argument, NULL, 'I' }, |
705 | { "global-table" , no_argument, NULL, 'G' }, |
706 | { "constants-prefix" , required_argument, NULL, CHAR_MAX + 5 }, |
707 | { "word-array-name" , required_argument, NULL, 'W' }, |
708 | { "length-table-name" , required_argument, NULL, CHAR_MAX + 4 }, |
709 | { "switch" , required_argument, NULL, 'S' }, |
710 | { "omit-struct-type" , no_argument, NULL, 'T' }, |
711 | { "key-positions" , required_argument, NULL, 'k' }, |
712 | { "compare-strlen" , no_argument, NULL, 'l' }, /* backward compatibility */ |
713 | { "compare-lengths" , no_argument, NULL, 'l' }, |
714 | { "duplicates" , no_argument, NULL, 'D' }, |
715 | { "fast" , required_argument, NULL, 'f' }, |
716 | { "initial-asso" , required_argument, NULL, 'i' }, |
717 | { "jump" , required_argument, NULL, 'j' }, |
718 | { "multiple-iterations" , required_argument, NULL, 'm' }, |
719 | { "no-strlen" , no_argument, NULL, 'n' }, |
720 | { "occurrence-sort" , no_argument, NULL, 'o' }, |
721 | { "optimized-collision-resolution" , no_argument, NULL, 'O' }, |
722 | { "pic" , no_argument, NULL, 'P' }, |
723 | { "string-pool-name" , required_argument, NULL, 'Q' }, |
724 | { "null-strings" , no_argument, NULL, CHAR_MAX + 3 }, |
725 | { "random" , no_argument, NULL, 'r' }, |
726 | { "size-multiple" , required_argument, NULL, 's' }, |
727 | { "help" , no_argument, NULL, 'h' }, |
728 | { "version" , no_argument, NULL, 'v' }, |
729 | { "debug" , no_argument, NULL, 'd' }, |
730 | { NULL, no_argument, NULL, 0 } |
731 | }; |
732 | |
733 | void |
734 | Options::parse_options (int argc, char *argv[]) |
735 | { |
736 | int option_char; |
737 | |
738 | program_name = argv[0]; |
739 | _argument_count = argc; |
740 | _argument_vector = argv; |
741 | |
742 | while ((option_char = |
743 | getopt_long (_argument_count, _argument_vector, |
744 | "acCdDe:Ef:F:gGhH:i:Ij:k:K:lL:m:nN:oOpPQ:rs:S:tTvW:Z:7" , |
745 | long_options, NULL)) |
746 | != -1) |
747 | { |
748 | switch (option_char) |
749 | { |
750 | case 'a': /* Generated code uses the ANSI prototype format. */ |
751 | break; /* This is now the default. */ |
752 | case 'c': /* Generate strncmp rather than strcmp. */ |
753 | { |
754 | _option_word |= COMP; |
755 | break; |
756 | } |
757 | case 'C': /* Make the generated tables readonly (const). */ |
758 | { |
759 | _option_word |= CONST; |
760 | break; |
761 | } |
762 | case 'd': /* Enable debugging option. */ |
763 | { |
764 | _option_word |= DEBUG; |
765 | fprintf (stderr, "Starting program %s, version %s, with debugging on.\n" , |
766 | program_name, version_string); |
767 | break; |
768 | } |
769 | case 'D': /* Enable duplicate option. */ |
770 | { |
771 | _option_word |= DUP; |
772 | break; |
773 | } |
774 | case 'e': /* Specify keyword/attribute separator */ |
775 | { |
776 | _delimiters = /*getopt*/optarg; |
777 | break; |
778 | } |
779 | case 'E': |
780 | { |
781 | _option_word |= ENUM; |
782 | break; |
783 | } |
784 | case 'f': /* Generate the hash table "fast". */ |
785 | break; /* Not needed any more. */ |
786 | case 'F': |
787 | { |
788 | _initializer_suffix = /*getopt*/optarg; |
789 | break; |
790 | } |
791 | case 'g': /* Use the 'inline' keyword for generated sub-routines, ifdef __GNUC__. */ |
792 | break; /* This is now the default. */ |
793 | case 'G': /* Make the keyword table a global variable. */ |
794 | { |
795 | _option_word |= GLOBAL; |
796 | break; |
797 | } |
798 | case 'h': /* Displays a list of helpful Options to the user. */ |
799 | { |
800 | long_usage (stdout); |
801 | exit (0); |
802 | } |
803 | case 'H': /* Sets the name for the hash function. */ |
804 | { |
805 | _hash_name = /*getopt*/optarg; |
806 | break; |
807 | } |
808 | case 'i': /* Sets the initial value for the associated values array. */ |
809 | { |
810 | if ((_initial_asso_value = atoi (/*getopt*/optarg)) < 0) |
811 | fprintf (stderr, "Initial value %d should be non-zero, ignoring and continuing.\n" , _initial_asso_value); |
812 | if (option[RANDOM]) |
813 | fprintf (stderr, "warning, -r option superceeds -i, ignoring -i option and continuing\n" ); |
814 | break; |
815 | } |
816 | case 'I': /* Enable #include statements. */ |
817 | { |
818 | _option_word |= INCLUDE; |
819 | break; |
820 | } |
821 | case 'j': /* Sets the jump value, must be odd for later algorithms. */ |
822 | { |
823 | if ((_jump = atoi (/*getopt*/optarg)) < 0) |
824 | { |
825 | fprintf (stderr, "Jump value %d must be a positive number.\n" , _jump); |
826 | short_usage (stderr); |
827 | exit (1); |
828 | } |
829 | else if (_jump && ((_jump % 2) == 0)) |
830 | fprintf (stderr, "Jump value %d should be odd, adding 1 and continuing...\n" , _jump++); |
831 | break; |
832 | } |
833 | case 'k': /* Sets key positions used for hash function. */ |
834 | { |
835 | _option_word |= POSITIONS; |
836 | const int BAD_VALUE = -3; |
837 | const int EOS = PositionIterator::EOS; |
838 | int value; |
839 | PositionStringParser sparser (/*getopt*/optarg, 1, Positions::MAX_KEY_POS, Positions::LASTCHAR, BAD_VALUE, EOS); |
840 | |
841 | if (/*getopt*/optarg [0] == '*') /* Use all the characters for hashing!!!! */ |
842 | _key_positions.set_useall(true); |
843 | else |
844 | { |
845 | _key_positions.set_useall(false); |
846 | int *key_positions = _key_positions.pointer(); |
847 | int *key_pos; |
848 | |
849 | for (key_pos = key_positions; (value = sparser.nextPosition()) != EOS; key_pos++) |
850 | { |
851 | if (value == BAD_VALUE) |
852 | { |
853 | fprintf (stderr, "Invalid position value or range, use 1,2,3-%d,'$' or '*'.\n" , |
854 | Positions::MAX_KEY_POS); |
855 | short_usage (stderr); |
856 | exit (1); |
857 | } |
858 | if (key_pos - key_positions == Positions::MAX_SIZE) |
859 | { |
860 | /* More than Positions::MAX_SIZE key positions. |
861 | Since all key positions are in the range |
862 | 0..Positions::MAX_KEY_POS-1 or == Positions::LASTCHAR, |
863 | there must be duplicates. */ |
864 | fprintf (stderr, "Duplicate key positions selected\n" ); |
865 | short_usage (stderr); |
866 | exit (1); |
867 | } |
868 | if (value != Positions::LASTCHAR) |
869 | /* We use 0-based indices in the class Positions. */ |
870 | value = value - 1; |
871 | *key_pos = value; |
872 | } |
873 | |
874 | unsigned int total_keysig_size = key_pos - key_positions; |
875 | if (total_keysig_size == 0) |
876 | { |
877 | fprintf (stderr, "No key positions selected.\n" ); |
878 | short_usage (stderr); |
879 | exit (1); |
880 | } |
881 | _key_positions.set_size (total_keysig_size); |
882 | |
883 | /* Sorts the key positions *IN REVERSE ORDER!!* |
884 | This makes further routines more efficient. Especially |
885 | when generating code. */ |
886 | if (! _key_positions.sort()) |
887 | { |
888 | fprintf (stderr, "Duplicate key positions selected\n" ); |
889 | short_usage (stderr); |
890 | exit (1); |
891 | } |
892 | } |
893 | break; |
894 | } |
895 | case 'K': /* Make this the keyname for the keyword component field. */ |
896 | { |
897 | _slot_name = /*getopt*/optarg; |
898 | break; |
899 | } |
900 | case 'l': /* Create length table to avoid extra string compares. */ |
901 | { |
902 | _option_word |= LENTABLE; |
903 | break; |
904 | } |
905 | case 'L': /* Deal with different generated languages. */ |
906 | { |
907 | _language = NULL; |
908 | set_language (/*getopt*/optarg); |
909 | break; |
910 | } |
911 | case 'm': /* Multiple iterations for finding good asso_values. */ |
912 | { |
913 | if ((_asso_iterations = atoi (/*getopt*/optarg)) < 0) |
914 | { |
915 | fprintf (stderr, "asso_iterations value must not be negative, assuming 0\n" ); |
916 | _asso_iterations = 0; |
917 | } |
918 | break; |
919 | } |
920 | case 'n': /* Don't include the length when computing hash function. */ |
921 | { |
922 | _option_word |= NOLENGTH; |
923 | break; |
924 | } |
925 | case 'N': /* Make generated lookup function name be optarg. */ |
926 | { |
927 | _function_name = /*getopt*/optarg; |
928 | break; |
929 | } |
930 | case 'o': /* Order input by frequency of key set occurrence. */ |
931 | break; /* Not needed any more. */ |
932 | case 'O': /* Optimized choice during collision resolution. */ |
933 | break; /* Not needed any more. */ |
934 | case 'p': /* Generated lookup function a pointer instead of int. */ |
935 | break; /* This is now the default. */ |
936 | case 'P': /* Optimize for position-independent code. */ |
937 | { |
938 | _option_word |= SHAREDLIB; |
939 | break; |
940 | } |
941 | case 'Q': /* Sets the name for the string pool. */ |
942 | { |
943 | _stringpool_name = /*getopt*/optarg; |
944 | break; |
945 | } |
946 | case 'r': /* Utilize randomness to initialize the associated values table. */ |
947 | { |
948 | _option_word |= RANDOM; |
949 | if (_initial_asso_value != 0) |
950 | fprintf (stderr, "warning, -r option supersedes -i, disabling -i option and continuing\n" ); |
951 | break; |
952 | } |
953 | case 's': /* Range of associated values, determines size of final table. */ |
954 | { |
955 | float numerator; |
956 | float denominator = 1; |
957 | bool invalid = false; |
958 | char *endptr; |
959 | |
960 | numerator = strtod (/*getopt*/optarg, &endptr); |
961 | if (endptr == /*getopt*/optarg) |
962 | invalid = true; |
963 | else if (*endptr != '\0') |
964 | { |
965 | if (*endptr == '/') |
966 | { |
967 | char *denomptr = endptr + 1; |
968 | denominator = strtod (denomptr, &endptr); |
969 | if (endptr == denomptr || *endptr != '\0') |
970 | invalid = true; |
971 | } |
972 | else |
973 | invalid = true; |
974 | } |
975 | if (invalid) |
976 | { |
977 | fprintf (stderr, "Invalid value for option -s.\n" ); |
978 | short_usage (stderr); |
979 | exit (1); |
980 | } |
981 | _size_multiple = numerator / denominator; |
982 | /* Backward compatibility: -3 means 1/3. */ |
983 | if (_size_multiple < 0) |
984 | _size_multiple = 1 / (-_size_multiple); |
985 | /* Catch stupid users. */ |
986 | if (_size_multiple == 0) |
987 | _size_multiple = 1; |
988 | /* Warnings. */ |
989 | if (_size_multiple > 50) |
990 | fprintf (stderr, "Size multiple %g is excessive, did you really mean this?! (try '%s --help' for help)\n" , _size_multiple, program_name); |
991 | else if (_size_multiple < 0.01f) |
992 | fprintf (stderr, "Size multiple %g is extremely small, did you really mean this?! (try '%s --help' for help)\n" , _size_multiple, program_name); |
993 | break; |
994 | } |
995 | case 'S': /* Generate switch statement output, rather than lookup table. */ |
996 | { |
997 | _option_word |= SWITCH; |
998 | _total_switches = atoi (/*getopt*/optarg); |
999 | if (_total_switches <= 0) |
1000 | { |
1001 | fprintf (stderr, "number of switches %s must be a positive number\n" , /*getopt*/optarg); |
1002 | short_usage (stderr); |
1003 | exit (1); |
1004 | } |
1005 | break; |
1006 | } |
1007 | case 't': /* Enable the TYPE mode, allowing arbitrary user structures. */ |
1008 | { |
1009 | _option_word |= TYPE; |
1010 | break; |
1011 | } |
1012 | case 'T': /* Don't print structure definition. */ |
1013 | { |
1014 | _option_word |= NOTYPE; |
1015 | break; |
1016 | } |
1017 | case 'v': /* Print out the version and quit. */ |
1018 | fprintf (stdout, "GNU gperf %s\n" , version_string); |
1019 | fprintf (stdout, "Copyright (C) %s Free Software Foundation, Inc.\n\ |
1020 | License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\ |
1021 | This is free software: you are free to change and redistribute it.\n\ |
1022 | There is NO WARRANTY, to the extent permitted by law.\n\ |
1023 | " , |
1024 | "1989-2017" ); |
1025 | fprintf (stdout, "Written by %s and %s.\n" , |
1026 | "Douglas C. Schmidt" , "Bruno Haible" ); |
1027 | exit (0); |
1028 | case 'W': /* Sets the name for the hash table array. */ |
1029 | { |
1030 | _wordlist_name = /*getopt*/optarg; |
1031 | break; |
1032 | } |
1033 | case 'Z': /* Set the class name. */ |
1034 | { |
1035 | _class_name = /*getopt*/optarg; |
1036 | break; |
1037 | } |
1038 | case '7': /* Assume 7-bit characters. */ |
1039 | { |
1040 | _option_word |= SEVENBIT; |
1041 | break; |
1042 | } |
1043 | case CHAR_MAX + 1: /* Set the output file name. */ |
1044 | { |
1045 | _output_file_name = /*getopt*/optarg; |
1046 | break; |
1047 | } |
1048 | case CHAR_MAX + 2: /* Case insignificant. */ |
1049 | { |
1050 | _option_word |= UPPERLOWER; |
1051 | break; |
1052 | } |
1053 | case CHAR_MAX + 3: /* Use NULL instead of "". */ |
1054 | { |
1055 | _option_word |= NULLSTRINGS; |
1056 | break; |
1057 | } |
1058 | case CHAR_MAX + 4: /* Sets the name for the length table array. */ |
1059 | { |
1060 | _lengthtable_name = /*getopt*/optarg; |
1061 | break; |
1062 | } |
1063 | case CHAR_MAX + 5: /* Sets the prefix for the constants. */ |
1064 | { |
1065 | _constants_prefix = /*getopt*/optarg; |
1066 | break; |
1067 | } |
1068 | default: |
1069 | short_usage (stderr); |
1070 | exit (1); |
1071 | } |
1072 | |
1073 | } |
1074 | |
1075 | if (/*getopt*/optind < argc) |
1076 | _input_file_name = argv[/*getopt*/optind++]; |
1077 | |
1078 | if (/*getopt*/optind < argc) |
1079 | { |
1080 | fprintf (stderr, "Extra trailing arguments to %s.\n" , program_name); |
1081 | short_usage (stderr); |
1082 | exit (1); |
1083 | } |
1084 | } |
1085 | |
1086 | /* ------------------------------------------------------------------------- */ |
1087 | |
1088 | #ifndef __OPTIMIZE__ |
1089 | |
1090 | #define INLINE /* not inline */ |
1091 | #include "options.icc" |
1092 | #undef INLINE |
1093 | |
1094 | #endif /* not defined __OPTIMIZE__ */ |
1095 | |