1 /*
2 * XPilot NG, a multiplayer space war game.
3 *
4 * Copyright (C) 1991-2001 by
5 *
6 * Bj�rn Stabell <bjoern@xpilot.org>
7 * Ken Ronny Schouten <ken@xpilot.org>
8 * Bert Gijsbers <bert@xpilot.org>
9 * Dick Balaska <dick@xpilot.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "xpserver.h"
27
28 /*
29 * This module implements an in memory server option database.
30 * Each option is made up by its names (one or two) and its
31 * value. The names are stored together with a pointer to
32 * their value representation in a hash table.
33 */
34
35
36 /* size of the hash table. must be prime. */
37 #define HASH_SIZE 317
38
39
40 /*
41 * Define value representation structure which holds:
42 * a pointer to the string representation of an option value.
43 * a flag which is true if the value was set with override.
44 * an enum which represents the origin of the option value:
45 * where origins can be one of {map, defaultsfile, command line}.
46 * a pointer to an option description structure.
47 * a reference count which will be either zero, one or two.
48 * This structure is automatically deallocated when its reference count
49 * drops to zero.
50 * The option description pointer may be NULL for undefined options.
51 * The string value pointer is usually dynamically allocated, but
52 * in theory (not yet in practice) could also point to a static string
53 * if this option refers to a valString with a static default value.
54 */
55 typedef struct _hash_value hash_value;
56 struct _hash_value {
57 char *value;
58 int override;
59 optOrigin origin;
60 option_desc *desc;
61 int refcount;
62 };
63
64
65 /*
66 * Define option name structure which holds a pointer
67 * to the value structure. More than one (two) different
68 * name structures may point to the same value structure
69 * if the same option has two different names.
70 */
71 typedef struct _hash_node hash_node;
72 struct _hash_node {
73 hash_node *next;
74 char *name;
75 hash_value *value;
76 };
77
78
79 /*
80 * Define memory for hash table along with some statistics.
81 */
82 static int hash_nodes_allocated;
83 static int hash_nodes_freed;
84 static int hash_values_allocated;
85 static int hash_values_freed;
86 static hash_node* Option_hash_array[HASH_SIZE];
87
88
89 /*
90 * Compute a reasonable case-insensitive hash value across a character string.
91 */
Option_hash_string(const char * name)92 static int Option_hash_string(const char *name)
93 {
94 unsigned int hashVal = 0;
95 const unsigned char *string = (const unsigned char *)name;
96 int i;
97
98 for (i = 0; string[i] != '\0'; i++) {
99 unsigned char c = string[i];
100
101 if (isascii(c) && isalpha(c) && islower(c))
102 c = toupper(c);
103
104 hashVal = (((hashVal << 3) + c) ^ i);
105
106 while (hashVal > HASH_SIZE)
107 hashVal = (hashVal % HASH_SIZE) + (hashVal / HASH_SIZE);
108 }
109
110 return (int)(hashVal % HASH_SIZE);
111 }
112
113
114 /*
115 * Free a hash value structure if its
116 * reference count drops to zero.
117 */
Option_free_value(hash_value * val)118 static void Option_free_value(hash_value* val)
119 {
120 if (val->refcount > 0)
121 val->refcount--;
122 if (val->refcount == 0) {
123 if (val->value) {
124 if (!val->desc || val->value != val->desc->defaultValue)
125 free(val->value);
126 val->value = NULL;
127 }
128 free(val);
129 hash_values_freed++;
130 }
131 }
132
133
134 /*
135 * Allocate a new option hash value and fill in its values.
136 * The option value string representation is either derived
137 * from the value parameter, or else from the option description
138 * default value pointer.
139 */
Option_allocate_value(const char * value,option_desc * desc,optOrigin origin)140 static hash_value *Option_allocate_value(
141 const char *value,
142 option_desc *desc,
143 optOrigin origin)
144 {
145 hash_value *tmp = (hash_value *)xp_safe_malloc(sizeof(hash_value));
146
147 tmp->desc = desc;
148 tmp->override = 0;
149 tmp->origin = origin;
150 tmp->refcount = 0;
151 if (value == NULL) {
152 if (desc != NULL && desc->defaultValue != NULL)
153 /* might also simply point to default value instead. */
154 tmp->value = xp_safe_strdup(desc->defaultValue);
155 else
156 tmp->value = NULL;
157 }
158 else
159 tmp->value = xp_safe_strdup(value);
160
161 if (tmp)
162 hash_values_allocated++;
163
164 return tmp;
165 }
166
167
168 /*
169 * Free a hash node and its hash value.
170 */
Option_free_node(hash_node * node)171 static void Option_free_node(hash_node* node)
172 {
173 if (node->value) {
174 Option_free_value(node->value);
175 node->value = NULL;
176 }
177 XFREE(node->name);
178 node->next = NULL;
179 free(node);
180 hash_nodes_freed++;
181 }
182
183
184 /*
185 * Allocate a new node for the hash table and fill in its values.
186 */
Option_allocate_node(const char * name,hash_value * value)187 static hash_node *Option_allocate_node(const char *name, hash_value *value)
188 {
189 hash_node *tmp = (hash_node *)xp_safe_malloc(sizeof(hash_node));
190
191 tmp->next = NULL;
192 tmp->value = value;
193 tmp->name = xp_safe_strdup(name);
194 if (tmp->value)
195 tmp->value->refcount++;
196
197 if (tmp)
198 hash_nodes_allocated++;
199
200 return tmp;
201 }
202
203
204 /*
205 * Add a hash node to the hash table.
206 */
Option_add_node(hash_node * node)207 static void Option_add_node(hash_node *node)
208 {
209 hash_node *np;
210 int ix = Option_hash_string(node->name);
211
212 for (np = Option_hash_array[ix]; np; np = np->next) {
213 if (!strcasecmp(node->name, np->name))
214 fatal("Option_add_node node exists (%s, %s)\n",
215 node->name, np->name);
216 }
217
218 node->next = Option_hash_array[ix];
219 Option_hash_array[ix] = node;
220 }
221
222
223 /*
224 * Return the hash table node of a named option,
225 * or NULL if there is no node for that option name.
226 */
Get_hash_node_by_name(const char * name)227 static hash_node *Get_hash_node_by_name(const char *name)
228 {
229 hash_node *np;
230 int ix = Option_hash_string(name);
231
232 for (np = Option_hash_array[ix]; np; np = np->next) {
233 if (!strcasecmp(name, np->name))
234 return np;
235 }
236
237 return NULL;
238 }
239
240
241 /*
242 * Add an option description to the hash table.
243 */
Option_add_desc(option_desc * desc)244 bool Option_add_desc(option_desc *desc)
245 {
246 hash_value *val = Option_allocate_value(NULL, desc, OPT_INIT);
247 hash_node *node1, *node2;
248
249 if (!val)
250 return false;
251
252 node1 = Option_allocate_node(desc->name, val);
253 if (!node1) {
254 Option_free_value(val);
255 return false;
256 }
257
258 node2 = NULL;
259 if (strcasecmp(desc->name, desc->commandLineOption)) {
260 node2 = Option_allocate_node(desc->commandLineOption, val);
261 if (!node2) {
262 Option_free_node(node1);
263 return false;
264 }
265 }
266
267 Option_add_node(node1);
268 if (node2 != NULL)
269 Option_add_node(node2);
270
271 return true;
272 }
273
274
275 /*
276 * Convert an option origin enumerated constant
277 * to a character representation.
278 */
Origin_name(optOrigin opt_origin)279 static const char* Origin_name(optOrigin opt_origin)
280 {
281 const char *source;
282
283 switch (opt_origin) {
284 case OPT_COMMAND: source = "command line"; break;
285 case OPT_PASSWORD: source = "password file"; break;
286 case OPT_DEFAULTS: source = "defaults file"; break;
287 case OPT_MAP: source = "map file"; break;
288 default: source = "unknown origin"; break;
289 }
290
291 return source;
292 }
293
294
295 /*
296 * Modify the value for a hash node if permissions allow us to do so.
297 */
Option_change_node(hash_node * node,const char * value,int override,optOrigin opt_origin)298 static void Option_change_node(
299 hash_node *node,
300 const char *value,
301 int override,
302 optOrigin opt_origin)
303 {
304 bool set_ok = false;
305
306 if (node->value == NULL) {
307 /* permit if option has no default value. */
308 set_ok = true;
309 }
310 else {
311
312 /* check option description permissions. */
313 if (node->value->desc != NULL) {
314 option_desc *desc = node->value->desc;
315 if ((desc->flags & opt_origin) == 0) {
316 warn("Not allowed to change option '%s' from %s.",
317 node->name, Origin_name(opt_origin));
318 return;
319 }
320 }
321
322 switch (opt_origin) {
323 case OPT_COMMAND:
324 /* command line always overrides */
325 set_ok = true;
326 break;
327
328 case OPT_DEFAULTS:
329 switch (node->value->origin) {
330 case OPT_COMMAND:
331 /* never modify command line arg. */
332 break;
333
334 case OPT_DEFAULTS:
335 /* can't change if previous value has override. */
336 if (!node->value->override)
337 set_ok = true;
338 break;
339
340 case OPT_MAP:
341 /* defaults file override wins over map. */
342 if (override)
343 set_ok = true;
344 break;
345
346 case OPT_PASSWORD:
347 /* never modify if set by options.password file. */
348 break;
349
350 case OPT_INIT:
351 set_ok = true;
352 break;
353
354 default:
355 fatal("unknown node->value origin in set value");
356 }
357 break;
358
359 case OPT_MAP:
360 switch (node->value->origin) {
361 case OPT_COMMAND:
362 /* never modify command line arg. */
363 break;
364
365 case OPT_DEFAULTS:
366 /* can't change if defaults value has override. */
367 if (!node->value->override)
368 set_ok = true;
369 break;
370
371 case OPT_MAP:
372 /* can't change if previous value has override. */
373 if (!node->value->override)
374 set_ok = true;
375 break;
376
377 case OPT_PASSWORD:
378 /* never modify if set by options.password file. */
379 break;
380
381 case OPT_INIT:
382 set_ok = true;
383 break;
384
385 default:
386 fatal("unknown node->value origin in set value");
387 }
388 break;
389
390 case OPT_PASSWORD:
391 switch (node->value->origin) {
392 case OPT_COMMAND:
393 /* never modify command line arg. */
394 break;
395
396 case OPT_DEFAULTS:
397 /* options.password file always wins over defaults. */
398 set_ok = true;
399 break;
400
401 case OPT_MAP:
402 /* options.password file always wins over map. */
403 set_ok = true;
404 break;
405
406 case OPT_PASSWORD:
407 /* can't change if previous value has override. */
408 if (!node->value->override)
409 set_ok = true;
410 break;
411
412 case OPT_INIT:
413 set_ok = true;
414 break;
415
416 default:
417 fatal("unknown node->value origin in set value");
418 }
419 break;
420
421 default:
422 fatal("unknown opt_origin in set value");
423 }
424 }
425
426 if (set_ok) {
427 if (node->value == NULL) {
428 node->value = Option_allocate_value(value, NULL, opt_origin);
429 if (node->value == NULL)
430 fatal("Not enough memory.");
431 else
432 node->value->refcount++;
433 }
434 else {
435 if (node->value->value != NULL) {
436 option_desc *desc = node->value->desc;
437 if (!desc || node->value->value != desc->defaultValue)
438 free(node->value->value);
439 }
440 if (value == NULL)
441 node->value->value = NULL;
442 else
443 node->value->value = xp_safe_strdup(value);
444 }
445 node->value->override = override;
446 node->value->origin = opt_origin;
447 }
448 #ifdef DEVELOPMENT
449 if (set_ok != true) {
450 const char *old_value_origin_name = Origin_name(node->value->origin);
451 const char *new_value_origin_name = Origin_name(opt_origin);
452 warn("Not modifying %s option '%s' from %s\n",
453 old_value_origin_name,
454 node->name,
455 new_value_origin_name);
456 }
457 #endif
458 }
459
460
461 /*
462 * Scan through the hash table of option name-value pairs looking for
463 * an option with the specified name; if found call Option_change_node
464 * to set option to the new value if permissions allow us to do so.
465 */
Option_set_value(const char * name,const char * value,int override,optOrigin opt_origin)466 void Option_set_value(
467 const char *name,
468 const char *value,
469 int override,
470 optOrigin opt_origin)
471 {
472 hash_node *np;
473 hash_value *vp;
474 int ix = Option_hash_string(name);
475
476 /* Warn about obsolete behaviour. */
477 if (opt_origin == OPT_MAP && value) {
478 if ((!strcasecmp(name, "mineLife")
479 || (!strcasecmp(name, "missileLife")))
480 && atoi(value) == 0) {
481 warn("Value of %s is %s in map.", name, value);
482 warn("This is an obsolete way to set the default value.");
483 warn("It will cause the weapon to detonate at once.");
484 warn("To fix, remove the option from the map file.");
485 }
486 }
487
488 for (np = Option_hash_array[ix]; np; np = np->next) {
489 if (!strcasecmp(name, np->name)) {
490 if (opt_origin == OPT_MAP && np->value->origin == OPT_MAP) {
491 warn("The map contains multiple instances of the option %s.",
492 name);
493 warn("The server will use the first instance.");
494 return;
495 }
496 Option_change_node(np, value, override, opt_origin);
497 return;
498 }
499 }
500
501 if (opt_origin == OPT_MAP && np == NULL) {
502 warn("Server does not support option '%s'", name);
503 return;
504 }
505
506 if (!value)
507 return;
508
509 vp = Option_allocate_value(value, NULL, opt_origin);
510 if (!vp)
511 exit(1);
512 vp->override = override;
513
514 np = Option_allocate_node(name, vp);
515 if (!np)
516 exit(1);
517
518 np->next = Option_hash_array[ix];
519 Option_hash_array[ix] = np;
520 }
521
522
523 /*
524 * Return the value of the specified option,
525 * or NULL if there is no value for that option.
526 */
Option_get_value(const char * name,optOrigin * origin_ptr)527 char *Option_get_value(const char *name, optOrigin *origin_ptr)
528 {
529 hash_node *np = Get_hash_node_by_name(name);
530
531 if (np != NULL) {
532 if (origin_ptr != NULL)
533 *origin_ptr = np->value->origin;
534 return np->value->value;
535 }
536
537 return NULL;
538 }
539
540
541 /*
542 * Free all option hash table related dynamically allocated memory.
543 */
Options_hash_free(void)544 static void Options_hash_free(void)
545 {
546 int i;
547 hash_node *np;
548
549 for (i = 0; i < HASH_SIZE; i++) {
550 while ((np = Option_hash_array[i]) != NULL) {
551 Option_hash_array[i] = np->next;
552 Option_free_node(np);
553 }
554 }
555
556 if (hash_nodes_allocated != hash_nodes_freed)
557 warn("hash nodes alloc = %d, hash nodes free = %d, delta = %d\n",
558 hash_nodes_allocated, hash_nodes_freed,
559 hash_nodes_allocated - hash_nodes_freed);
560
561 if (hash_values_allocated != hash_values_freed)
562 warn("hash values alloc = %d, hash values free = %d, delta = %d\n",
563 hash_values_allocated, hash_values_freed,
564 hash_values_allocated - hash_values_freed);
565 }
566
567
568 /*
569 * Print info about our hashing function performance.
570 */
Options_hash_performance(void)571 static void Options_hash_performance(void)
572 {
573 #ifdef DEVELOPMENT
574 int bucket_use_count;
575 int i;
576 hash_node *np;
577 unsigned char histo[HASH_SIZE];
578 char msg[MSG_LEN];
579
580 if (getenv("XPILOTSHASHPERF") == NULL)
581 return;
582
583 memset(histo, 0, sizeof(histo));
584 for (i = 0; i < HASH_SIZE; i++) {
585 bucket_use_count = 0;
586 for (np = Option_hash_array[i]; np; np = np->next)
587 bucket_use_count++;
588 histo[bucket_use_count]++;
589 }
590
591 sprintf(msg, "hash perf histo:");
592 for (i = 0; i < NELEM(histo); i++) {
593 sprintf(msg + strlen(msg), " %d", histo[i]);
594 if (strlen(msg) > 75)
595 break;
596 }
597 printf("%s\n", msg);
598 #endif
599 }
600
601
Convert_string_to_int(const char * value_str,int * int_ptr)602 bool Convert_string_to_int(const char *value_str, int *int_ptr)
603 {
604 char *end_ptr = NULL;
605 long value;
606 bool result;
607
608 /* base 0 has special meaning. */
609 value = strtol(value_str, &end_ptr, 0);
610
611 /* store value regardless of error. */
612 *int_ptr = (int) value;
613
614 /* if at least one digit was found we're satisfied. */
615 if (end_ptr > value_str)
616 result = true;
617 else
618 result = false;
619
620 return result;
621 }
622
623
Convert_string_to_float(const char * value_str,double * float_ptr)624 bool Convert_string_to_float(const char *value_str, double *float_ptr)
625 {
626 char *end_ptr = NULL;
627 double value;
628 bool result;
629
630 value = strtod(value_str, &end_ptr);
631
632 /* store value regardless of error. */
633 *float_ptr = (double) value;
634
635 /* if at least one digit was found we're satisfied. */
636 if (end_ptr > value_str)
637 result = true;
638 else
639 result = false;
640
641 return result;
642 }
643
644
Convert_string_to_bool(const char * value_str,bool * bool_ptr)645 bool Convert_string_to_bool(const char *value_str, bool *bool_ptr)
646 {
647 bool result;
648
649 if (!strcasecmp(value_str, "yes")
650 || !strcasecmp(value_str, "on")
651 || !strcasecmp(value_str, "true")) {
652 *bool_ptr = true;
653 result = true;
654 }
655 else if (!strcasecmp(value_str, "no")
656 || !strcasecmp(value_str, "off")
657 || !strcasecmp(value_str, "false")) {
658 *bool_ptr = false;
659 result = true;
660 }
661 else
662 result = false;
663
664 return result;
665 }
666
667
Convert_list_to_string(list_t list,char ** str)668 void Convert_list_to_string(list_t list, char **str)
669 {
670 list_iter_t iter;
671 size_t size = 0;
672
673 for (iter = List_begin(list);
674 iter != List_end(list);
675 LI_FORWARD(iter))
676 size += 1 + strlen((const char *) LI_DATA(iter));
677
678 *str = (char *)xp_safe_malloc(size);
679 **str = '\0';
680 for (iter = List_begin(list);
681 iter != List_end(list);
682 LI_FORWARD(iter)) {
683 if (iter != List_begin(list))
684 strlcat(*str, ",", size);
685 strlcat(*str, (const char *) LI_DATA(iter), size);
686 }
687 }
688
689
Convert_string_to_list(const char * value,list_t * list_ptr)690 void Convert_string_to_list(const char *value, list_t *list_ptr)
691 {
692 const char *start, *end;
693 char *str;
694
695 /* possibly allocate a new list. */
696 if (NULL == *list_ptr) {
697 *list_ptr = List_new();
698 if (NULL == *list_ptr)
699 fatal("Not enough memory for list");
700 }
701
702 /* make sure list is empty. */
703 List_clear(*list_ptr);
704
705 /* copy comma separated list elements from value to list. */
706 for (start = value; *start; start = end) {
707 /* skip comma separators. */
708 while (*start == ',')
709 start++;
710 /* search for end of list element. */
711 end = start;
712 while (*end && *end != ',')
713 end++;
714 /* copy non-zero results to list. */
715 if (start < end) {
716 size_t size = end - start;
717
718 str = (char *)xp_safe_malloc(size + 1);
719 memcpy(str, start, size);
720 str[size] = '\0';
721 if (NULL == List_push_back(*list_ptr, str))
722 fatal("Not enough memory for list element");
723 }
724 }
725 }
726
727
728 /*
729 * Set the option description variable.
730 */
Option_parse_node(hash_node * np)731 static void Option_parse_node(hash_node *np)
732 {
733 option_desc *desc;
734 const char *value;
735
736 /* Does it have a description? If so, get a pointer to it */
737 if ((desc = np->value->desc) == NULL)
738 return;
739
740 /* get value from command line, defaults file or map file. */
741 value = np->value->value;
742 if (value == NULL) {
743 /* no value has been set, so get the option default value. */
744 value = desc->defaultValue;
745 if (value == NULL) {
746 /* no value at all. (options.mapData or options.serverHost.) */
747 assert(desc->type == valString);
748 return;
749 }
750 }
751
752 if (!desc->variable) {
753 if (desc->type == valVoid)
754 return;
755 else
756 dumpcore("Hashed option %s has no value", np->name);
757 }
758
759 switch (desc->type) {
760
761 case valVoid:
762 break;
763
764 case valInt:
765 {
766 int *ptr = (int *)desc->variable;
767
768 if (Convert_string_to_int(value, ptr) != true) {
769 warn("%s value '%s' not an integral number.",
770 np->name, value);
771 Convert_string_to_int(desc->defaultValue, ptr);
772 }
773 break;
774 }
775
776 case valReal:
777 {
778 double *ptr = (double *)desc->variable;
779
780 if (Convert_string_to_float(value, ptr) != true) {
781 warn("%s value '%s' not a number.",
782 np->name, value);
783 Convert_string_to_float(desc->defaultValue, ptr);
784 }
785 break;
786 }
787
788 case valBool:
789 {
790 bool *ptr = (bool *)desc->variable;
791
792 if (Convert_string_to_bool(value, ptr) != true) {
793 warn("%s value '%s' not a boolean.",
794 np->name, value);
795 Convert_string_to_bool(desc->defaultValue, ptr);
796 }
797 break;
798 }
799
800 case valIPos:
801 {
802 ipos_t *ptr = (ipos_t *)desc->variable;
803 char *s;
804
805 s = strchr(value, ',');
806 if (!s) {
807 error("Invalid coordinate pair for %s - %s\n",
808 desc->name, value);
809 break;
810 }
811 if (Convert_string_to_int(value, &(ptr->x)) != true ||
812 Convert_string_to_int(s + 1, &(ptr->y)) != true) {
813 warn("%s value '%s' not a valid position.",
814 np->name, value);
815 value = desc->defaultValue;
816 s = strchr(value, ',');
817 Convert_string_to_int(value, &(ptr->x));
818 Convert_string_to_int(s + 1, &(ptr->y));
819 }
820 break;
821 }
822
823 case valString:
824 {
825 char **ptr = (char **)desc->variable;
826
827 *ptr = xp_safe_strdup(value);
828 break;
829 }
830
831 case valList:
832 {
833 list_t *list_ptr = (list_t *)desc->variable;
834
835 Convert_string_to_list(value, list_ptr);
836 break;
837 }
838 default:
839 warn("Option_parse_node: unknown option type.");
840 break;
841 }
842 }
843
844
845 /*
846 * Expand any "expand" arguments.
847 */
Options_parse_expand(void)848 static void Options_parse_expand(void)
849 {
850 hash_node *np;
851
852 np = Get_hash_node_by_name("expand");
853 if (np == NULL)
854 dumpcore("Could not find option hash node for option '%s'.",
855 "expand");
856 else
857 Option_parse_node(np);
858
859 if (options.expandList != NULL) {
860 char *name;
861 while ((name = (char *) List_pop_front(options.expandList)) != NULL)
862 expandKeyword(name);
863 List_delete(options.expandList);
864 options.expandList = NULL;
865 }
866 }
867
868
869 /*
870 * Parse the -FPS option.
871 */
Options_parse_FPS(void)872 static void Options_parse_FPS(void)
873 {
874 char *fpsstr;
875 optOrigin value_origin;
876
877 fpsstr = Option_get_value("framesPerSecond", &value_origin);
878 if (fpsstr != NULL) {
879 int frames;
880
881 if (Convert_string_to_int(fpsstr, &frames) != true)
882 warn("Invalid framesPerSecond specification '%s' in %s.",
883 fpsstr, Origin_name(value_origin));
884 else
885 options.framesPerSecond = frames;
886 }
887
888 if (FPS <= 0)
889 fatal("Can't run with %d frames per second, should be positive\n",FPS);
890 }
891
892
893 /*
894 * Go through the hash table looking for name-value pairs that have defaults
895 * assigned to them. Process the defaults and, if possible, set the
896 * associated variables.
897 */
Options_parse(void)898 void Options_parse(void)
899 {
900 int i;
901 hash_node *np;
902 option_desc *option_descs;
903 int option_count;
904
905 option_descs = Get_option_descs(&option_count);
906
907 /*
908 * Expand a possible "-expand" option.
909 */
910 Options_parse_expand();
911
912 /*
913 * kps - this might not be necessary.
914 * This must be done in order that FPS will return the eventual
915 * frames per second for computing valSec and valPerSec.
916 */
917 Options_parse_FPS();
918
919 for (i = 0; i < option_count; i++) {
920 np = Get_hash_node_by_name(option_descs[i].name);
921 if (np == NULL)
922 dumpcore("Could not find option hash node for option '%s'.",
923 option_descs[i].name);
924 else
925 Option_parse_node(np);
926 }
927 }
928
929
930 /*
931 * Free the option database memory.
932 */
Options_free(void)933 void Options_free(void)
934 {
935 Options_hash_performance();
936 Options_hash_free();
937 }
938