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