1 /*
2  * MOC - music on console
3  * Copyright (C) 2004 - 2006 Damian Pietras <daper@daper.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  */
11 
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15 
16 #include <assert.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <sys/types.h>
26 #include <regex.h>
27 
28 #include "common.h"
29 #include "files.h"
30 #include "log.h"
31 #include "options.h"
32 #include "lists.h"
33 
34 #define OPTIONS_MAX	181
35 #define OPTION_NAME_MAX	32
36 
37 typedef int options_t_check (int, ...);
38 
39 union option_value
40 {
41 	char *str;
42 	int num;
43 	bool boolean;
44 	lists_t_strs *list;
45 };
46 
47 struct option
48 {
49 	char name[OPTION_NAME_MAX];
50 	enum option_type type;
51 	union option_value value;
52 	int ignore_in_config;
53 	int set_in_config;
54 	unsigned int hash;
55 	options_t_check *check;
56 	int count;
57 	void *constraints;
58 };
59 
60 static struct option options[OPTIONS_MAX];
61 static int options_num = 0;
62 
63 
64 /* Returns the str's hash using djb2 algorithm. */
hash(const char * str)65 static unsigned int hash (const char * str)
66 {
67 	unsigned int hash = 5381;
68 
69 	while (*str)
70 		hash = ((hash << 5) + hash) + tolower(*(str++));
71 	return hash;
72 }
73 
74 /* Return an index to an option in the options hashtable.
75  * If there is no such option return -1. */
find_option(const char * name,enum option_type type)76 static int find_option (const char *name, enum option_type type)
77 {
78 	unsigned int h=hash(name),i,init_pos=h%OPTIONS_MAX;
79 
80 	for(i=init_pos;i<OPTIONS_MAX;i++)
81 		if(options[i].type==OPTION_FREE)
82 			return -1;
83 		else if(h == options[i].hash && type & options[i].type)
84 			if(!strcasecmp(name, options[i].name))
85 		    	return i;
86 
87 	for(i=0;i<init_pos;i++)
88 		if(options[i].type==OPTION_FREE)
89 			return -1;
90 		else if(h == options[i].hash && type & options[i].type)
91 			if(!strcasecmp(name, options[i].name))
92 		    	return i;
93 
94 	return -1;
95 }
96 
97 /* Return an index of a free slot in the options hashtable.
98  * If there is no such slot return -1. */
find_free(unsigned int h)99 static int find_free (unsigned int h)
100 {
101 	unsigned int i;
102 
103 	assert (options_num < OPTIONS_MAX);
104 	h%=OPTIONS_MAX;
105 
106 	for(i=h;i<OPTIONS_MAX;i++)
107 		if(options[i].type==OPTION_FREE)
108 			return i;
109 
110 	for(i=0;i<h;i++)
111 		if(options[i].type==OPTION_FREE)
112 			return i;
113 
114 	return -1;
115 }
116 
117 /* Check that a value falls within the specified range(s). */
check_range(int opt,...)118 static int check_range (int opt, ...)
119 {
120 	int rc, ix, int_val;
121 	char *str_val;
122 	va_list va;
123 
124 	assert (opt != -1);
125 	assert (options[opt].count % 2 == 0);
126 	assert (options[opt].type & (OPTION_INT | OPTION_STR | OPTION_LIST));
127 
128 	rc = 0;
129 	va_start (va, opt);
130 	switch (options[opt].type) {
131 
132 		case OPTION_INT:
133 			int_val = va_arg (va, int);
134 			for (ix = 0; ix < options[opt].count; ix += 2) {
135 				if (int_val >= ((int *) options[opt].constraints)[ix] &&
136 		    		int_val <= ((int *) options[opt].constraints)[ix + 1]) {
137 					rc = 1;
138 					break;
139 				}
140 			}
141 			break;
142 
143 		case OPTION_STR:
144 		case OPTION_LIST:
145 			str_val = va_arg (va, char *);
146 			for (ix = 0; ix < options[opt].count; ix += 2) {
147 				if (strcasecmp (str_val, (((char **) options[opt].constraints)[ix])) >= 0 &&
148 				    strcasecmp (str_val, (((char **) options[opt].constraints)[ix + 1])) <= 0) {
149 					rc = 1;
150 					break;
151 				}
152 			}
153 			break;
154 
155 		case OPTION_BOOL:
156 		case OPTION_SYMB:
157 		case OPTION_ANY:
158 		case OPTION_FREE:
159 			break;
160 	}
161 	va_end (va);
162 
163 	return rc;
164 }
165 
166 /* Check that a value is one of the specified values. */
check_discrete(int opt,...)167 static int check_discrete (int opt, ...)
168 {
169 	int rc, ix, int_val;
170 	char *str_val;
171 	va_list va;
172 
173 	assert (opt != -1);
174 	assert (options[opt].type & (OPTION_INT | OPTION_SYMB | OPTION_LIST));
175 
176 	rc = 0;
177 	va_start (va, opt);
178 	switch (options[opt].type) {
179 
180 		case OPTION_INT:
181 			int_val = va_arg (va, int);
182 			for (ix = 0; ix < options[opt].count; ix += 1) {
183 				if (int_val == ((int *) options[opt].constraints)[ix]) {
184 					rc = 1;
185 					break;
186 				}
187 			}
188 			break;
189 
190 		case OPTION_SYMB:
191 		case OPTION_LIST:
192 			str_val = va_arg (va, char *);
193 			for (ix = 0; ix < options[opt].count; ix += 1) {
194 				if (!strcasecmp(str_val, (((char **) options[opt].constraints)[ix]))) {
195 					rc = 1;
196 					break;
197 				}
198 			}
199 			break;
200 
201 		case OPTION_BOOL:
202 		case OPTION_STR:
203 		case OPTION_ANY:
204 		case OPTION_FREE:
205 			break;
206 	}
207 	va_end (va);
208 
209 	return rc;
210 }
211 
212 /* Check that a string length falls within the specified range(s). */
check_length(int opt,...)213 static int check_length (int opt, ...)
214 {
215 	int rc, ix, str_len;
216 	va_list va;
217 
218 	assert (opt != -1);
219 	assert (options[opt].count % 2 == 0);
220 	assert (options[opt].type & (OPTION_STR | OPTION_LIST));
221 
222 	rc = 0;
223 	va_start (va, opt);
224 	str_len = strlen (va_arg (va, char *));
225 	for (ix = 0; ix < options[opt].count; ix += 2) {
226 		if (str_len >= ((int *) options[opt].constraints)[ix] &&
227     		str_len <= ((int *) options[opt].constraints)[ix + 1]) {
228 			rc = 1;
229 			break;
230 		}
231 	}
232 	va_end (va);
233 
234 	return rc;
235 }
236 
237 /* Check that a string has a function-like syntax. */
check_function(int opt,...)238 static int check_function (int opt, ...)
239 {
240 	int rc;
241 	const char *str;
242 	const char regex[] = "^[a-z0-9/-]+\\([^,) ]*(,[^,) ]*)*\\)$";
243 	static regex_t *preg = NULL;
244 	va_list va;
245 
246 	assert (opt != -1);
247 	assert (options[opt].count == 0);
248 	assert (options[opt].type & (OPTION_STR | OPTION_LIST));
249 
250 	if (preg == NULL) {
251 		preg = (regex_t *)xmalloc (sizeof (regex_t));
252 		rc = regcomp (preg, regex, REG_EXTENDED | REG_ICASE | REG_NOSUB);
253 		assert (rc == 0);
254 	}
255 
256 	va_start (va, opt);
257 	str = va_arg (va, const char *);
258 	rc = regexec (preg, str, 0, NULL, 0);
259 	va_end (va);
260 
261 	return (rc == 0) ? 1 : 0;
262 }
263 
264 /* Always pass a value as valid. */
check_true(int opt ATTR_UNUSED,...)265 static int check_true (int opt ATTR_UNUSED, ...)
266 {
267 	return 1;
268 }
269 
270 /* Initializes a position on the options table. This is intended to be used at
271  * initialization to make a table of valid options and its default values. */
init_option(const char * name,enum option_type type)272 static int init_option (const char *name, enum option_type type)
273 {
274 	unsigned int h=hash(name);
275 	int pos=find_free(h);
276 
277 	assert (strlen(name) < OPTION_NAME_MAX);
278 	assert (is_valid_symbol (name));
279 	assert(pos>=0);
280 
281 	strcpy (options[pos].name, name);
282 	options[pos].hash=h;
283 	options[pos].type = type;
284 	options[pos].ignore_in_config = 0;
285 	options[pos].set_in_config = 0;
286 	options[pos].check = check_true;
287 	options[pos].count = 0;
288 	options[pos].constraints = NULL;
289 
290 	options_num++;
291 	return pos;
292 }
293 
294 /* Add an integer option to the options table. This is intended to be used at
295  * initialization to make a table of valid options and their default values. */
add_int(const char * name,const int value,options_t_check * check,const int count,...)296 static void add_int (const char *name, const int value, options_t_check *check, const int count, ...)
297 {
298 	int ix, pos;
299 	va_list va;
300 
301 	pos = init_option (name, OPTION_INT);
302 	options[pos].value.num = value;
303 	options[pos].check = check;
304 	options[pos].count = count;
305 	if (count > 0) {
306 		options[pos].constraints = xcalloc (count, sizeof (int));
307 		va_start (va, count);
308 		for (ix = 0; ix < count; ix += 1)
309 			((int *) options[pos].constraints)[ix] = va_arg (va, int);
310 		va_end (va);
311 	}
312 }
313 
314 /* Add a boolean option to the options table. This is intended to be used at
315  * initialization to make a table of valid options and their default values. */
add_bool(const char * name,const bool value)316 static void add_bool (const char *name, const bool value)
317 {
318 	int pos;
319 
320 	pos = init_option (name, OPTION_BOOL);
321 	options[pos].value.boolean = value;
322 }
323 
324 /* Add a string option to the options table. This is intended to be used at
325  * initialization to make a table of valid options and their default values. */
add_str(const char * name,const char * value,options_t_check * check,const int count,...)326 static void add_str (const char *name, const char *value, options_t_check *check, const int count, ...)
327 {
328 	int ix, pos;
329 	va_list va;
330 
331 	pos = init_option (name, OPTION_STR);
332 	options[pos].value.str = xstrdup (value);
333 	options[pos].check = check;
334 	options[pos].count = count;
335 	if (count > 0) {
336 		va_start (va, count);
337 		if (check == check_length) {
338 			options[pos].constraints = xcalloc (count, sizeof (int));
339 			for (ix = 0; ix < count; ix += 1)
340 				((int *) options[pos].constraints)[ix] = va_arg (va, int);
341 		} else {
342 			options[pos].constraints = xcalloc (count, sizeof (char *));
343 			for (ix = 0; ix < count; ix += 1)
344 				((char **) options[pos].constraints)[ix] = xstrdup (va_arg (va, char *));
345 		}
346 		va_end (va);
347 	}
348 }
349 
350 /* Add a symbol option to the options table. This is intended to be used at
351  * initialization to make a table of valid options and their default values. */
add_symb(const char * name,const char * value,const int count,...)352 static void add_symb (const char *name, const char *value, const int count, ...)
353 {
354 	int ix, pos;
355 	va_list va;
356 
357 	assert (name != NULL);
358 	assert (value != NULL);
359 	assert (count > 0);
360 
361 	pos = init_option (name, OPTION_SYMB);
362 	options[pos].value.str = NULL;
363 	options[pos].check = check_discrete;
364 	options[pos].count = count;
365 	va_start (va, count);
366 	options[pos].constraints = xcalloc (count, sizeof (char *));
367 	for (ix = 0; ix < count; ix += 1) {
368 		char *val = va_arg (va, char *);
369 		if (!is_valid_symbol (val))
370 			fatal ("Invalid symbol in '%s' constraint list!", name);
371 		((char **) options[pos].constraints)[ix] = xstrdup (val);
372 		if (!strcasecmp (val, value))
373 			options[pos].value.str = ((char **) options[pos].constraints)[ix];
374 	}
375 	if (!options[pos].value.str)
376 		fatal ("Invalid default value symbol in '%s'!", name);
377 	va_end (va);
378 }
379 
380 /* Add a list option to the options table. This is intended to be used at
381  * initialization to make a table of valid options and their default values. */
add_list(const char * name,const char * value,options_t_check * check,const int count,...)382 static void add_list (const char *name, const char *value, options_t_check *check, const int count, ...)
383 {
384 	int ix, pos;
385 	va_list va;
386 
387 	pos = init_option (name, OPTION_LIST);
388 	options[pos].value.list = lists_strs_new (8);
389 	if (value)
390 		lists_strs_split (options[pos].value.list, value, ":");
391 	options[pos].check = check;
392 	options[pos].count = count;
393 	if (count > 0) {
394 		va_start (va, count);
395 		if (check == check_length) {
396 			options[pos].constraints = xcalloc (count, sizeof (int));
397 			for (ix = 0; ix < count; ix += 1)
398 				((int *) options[pos].constraints)[ix] = va_arg (va, int);
399 		} else {
400 			options[pos].constraints = xcalloc (count, sizeof (char *));
401 			for (ix = 0; ix < count; ix += 1)
402 				((char **) options[pos].constraints)[ix] = xstrdup (va_arg (va, char *));
403 		}
404 		va_end (va);
405 	}
406 }
407 
408 /* Set an integer option to the value. */
options_set_int(const char * name,const int value)409 void options_set_int (const char *name, const int value)
410 {
411 	int i = find_option (name, OPTION_INT | OPTION_BOOL);
412 
413 	if (i == -1)
414 		fatal ("Tried to set wrong option '%s'!", name);
415 	if (options[i].type == OPTION_INT)
416 		options[i].value.num = value;
417 	else
418 		options[i].value.boolean = value ? true : false;
419 }
420 
421 /* Set a boolean option to the value. */
options_set_bool(const char * name,const bool value)422 void options_set_bool (const char *name, const bool value)
423 {
424 	int i = find_option (name, OPTION_BOOL);
425 
426 	if (i == -1)
427 		fatal ("Tried to set wrong option '%s'!", name);
428 	options[i].value.boolean = value;
429 }
430 
431 /* Set a symbol option to the value. */
options_set_symb(const char * name,const char * value)432 void options_set_symb (const char *name, const char *value)
433 {
434 	int opt, ix;
435 
436 	opt = find_option (name, OPTION_SYMB);
437 	if (opt == -1)
438 		fatal ("Tried to set wrong option '%s'!", name);
439 
440 	options[opt].value.str = NULL;
441 	for (ix = 0; ix < options[opt].count; ix += 1) {
442 		if (!strcasecmp(value, (((char **) options[opt].constraints)[ix])))
443 			options[opt].value.str = ((char **) options[opt].constraints)[ix];
444 	}
445 	if (!options[opt].value.str)
446 		fatal ("Tried to set '%s' to unknown symbol '%s'!", name, value);
447 }
448 
449 /* Set a string option to the value. The string is duplicated. */
options_set_str(const char * name,const char * value)450 void options_set_str (const char *name, const char *value)
451 {
452 	int opt = find_option (name, OPTION_STR | OPTION_SYMB);
453 
454 	if (opt == -1)
455 		fatal ("Tried to set wrong option '%s'!", name);
456 
457 	if (options[opt].type == OPTION_SYMB) {
458 		options_set_symb (name, value);
459 	} else {
460 		if (options[opt].value.str)
461 			free (options[opt].value.str);
462 		options[opt].value.str = xstrdup (value);
463 	}
464 }
465 
466 /* Set list option values to the colon separated value. */
options_set_list(const char * name,const char * value,bool append)467 void options_set_list (const char *name, const char *value, bool append)
468 {
469 	int opt;
470 
471 	opt = find_option (name, OPTION_LIST);
472 	if (opt == -1)
473 		fatal ("Tried to set wrong option '%s'!", name);
474 
475 	if (!append && !lists_strs_empty (options[opt].value.list))
476 		lists_strs_clear (options[opt].value.list);
477 	lists_strs_split (options[opt].value.list, value, ":");
478 }
479 
480 /* Given a type, a name and a value, set that option's value.
481  * Return false on error. */
options_set_pair(const char * name,const char * value,bool append)482 bool options_set_pair (const char *name, const char *value, bool append)
483 {
484 	int num;
485 	char *end;
486 	bool val;
487 
488 	switch (options_get_type (name)) {
489 
490 		case OPTION_INT:
491 			num = strtol (value, &end, 10);
492 			if (*end)
493 				return false;
494 			if (!options_check_int (name, num))
495 				return false;
496 			options_set_int (name, num);
497 			break;
498 
499 		case OPTION_BOOL:
500 			if (!strcasecmp (value, "yes"))
501 				val = true;
502 			else if (!strcasecmp (value, "no"))
503 				val = false;
504 			else
505 				return false;
506 			options_set_bool (name, val);
507 			break;
508 
509 		case OPTION_STR:
510 			if (!options_check_str (name, value))
511 				return false;
512 			options_set_str (name, value);
513 			break;
514 
515 		case OPTION_SYMB:
516 			if (!options_check_symb (name, value))
517 				return false;
518 			options_set_symb (name, value);
519 			break;
520 
521 		case OPTION_LIST:
522 			if (!options_check_list (name, value))
523 				return false;
524 			options_set_list (name, value, append);
525 			break;
526 
527 		case OPTION_FREE:
528 		case OPTION_ANY:
529 			return false;
530 	}
531 
532 	return true;
533 }
534 
options_ignore_config(const char * name)535 void options_ignore_config (const char *name)
536 {
537 	int opt = find_option (name, OPTION_ANY);
538 
539 	if (opt == -1)
540 		fatal ("Tried to set wrong option '%s'!", name);
541 
542 	options[opt].ignore_in_config = 1;
543 }
544 
545 #define CHECK_DISCRETE(c)   check_discrete, (c)
546 #define CHECK_RANGE(c)      check_range, (2 * (c))
547 #define CHECK_LENGTH(c)     check_length, (2 * (c))
548 #define CHECK_SYMBOL(c)     (c)
549 #define CHECK_FUNCTION      check_function, 0
550 #define CHECK_NONE          check_true, 0
551 
552 /* Make a table of options and its default values. */
options_init()553 void options_init ()
554 {
555 	memset (options, 0, sizeof(options));
556 
557 	add_bool ("ReadTags", true);
558 	add_str  ("MusicDir", NULL, CHECK_NONE);
559 	add_bool ("StartInMusicDir", false);
560 	add_symb ("Sort", "FileName", CHECK_SYMBOL(1), "FileName");
561 	add_bool ("ShowStreamErrors", false);
562 	add_bool ("MP3IgnoreCRCErrors", true);
563 	add_bool ("Repeat", false);
564 	add_bool ("Shuffle", false);
565 	add_bool ("AutoNext", true);
566 	add_str  ("FormatString",
567 	          "%(n:%n :)%(a:%a - :)%(t:%t:)%(A: \\(%A\\):)", CHECK_NONE);
568 	add_int  ("InputBuffer", 512, CHECK_RANGE(1), 32, INT_MAX);
569 	add_int  ("OutputBuffer", 512, CHECK_RANGE(1), 128, INT_MAX);
570 	add_int  ("Prebuffering", 64, CHECK_RANGE(1), 0, INT_MAX);
571 	add_str  ("HTTPProxy", NULL, CHECK_NONE);
572 
573 #ifdef OPENBSD
574 	add_list ("SoundDriver", "SNDIO:JACK:OSS",
575 	          CHECK_DISCRETE(5), "SNDIO", "Jack", "ALSA", "OSS", "null");
576 #else
577 	add_list ("SoundDriver", "Jack:ALSA:OSS",
578 	          CHECK_DISCRETE(5), "SNDIO", "Jack", "ALSA", "OSS", "null");
579 #endif
580 
581 	add_str  ("JackClientName", "moc", CHECK_NONE);
582 	add_bool ("JackStartServer", false);
583 	add_str  ("JackOutLeft", "system:playback_1", CHECK_NONE);
584 	add_str  ("JackOutRight", "system:playback_2", CHECK_NONE);
585 
586 	add_str  ("OSSDevice", "/dev/dsp", CHECK_NONE);
587 	add_str  ("OSSMixerDevice", "/dev/mixer", CHECK_NONE);
588 	add_symb ("OSSMixerChannel1", "pcm",
589 	          CHECK_SYMBOL(3), "pcm", "master", "speaker");
590 	add_symb ("OSSMixerChannel2", "master",
591 	          CHECK_SYMBOL(3), "pcm", "master", "speaker");
592 
593 	add_str  ("ALSADevice", "default", CHECK_NONE);
594 	add_str  ("ALSAMixer1", "PCM", CHECK_NONE);
595 	add_str  ("ALSAMixer2", "Master", CHECK_NONE);
596 	add_bool ("ALSAStutterDefeat", false);
597 
598 	add_bool ("Softmixer_SaveState", true);
599 	add_bool ("Equalizer_SaveState", true);
600 
601 	add_bool ("ShowHiddenFiles", false);
602 	add_bool ("HideFileExtension", false);
603 	add_bool ("ShowFormat", true);
604 	add_symb ("ShowTime", "IfAvailable",
605 	                 CHECK_SYMBOL(3), "yes", "no", "IfAvailable");
606 	add_bool ("ShowTimePercent", false);
607 
608 	add_list ("ScreenTerms", "screen:screen-w:vt100", CHECK_NONE);
609 
610 	add_list ("XTerms", "xterm:"
611 	                    "xterm-colour:xterm-color:"
612 	                    "xterm-256colour:xterm-256color:"
613 	                    "rxvt:rxvt-unicode:"
614 	                    "rxvt-unicode-256colour:rxvt-unicode-256color:"
615 	                    "eterm", CHECK_NONE);
616 
617 	add_str  ("Theme", NULL, CHECK_NONE);
618 	add_str  ("XTermTheme", NULL, CHECK_NONE);
619 	add_str  ("ForceTheme", NULL, CHECK_NONE); /* Used when -T is set */
620 	add_bool ("AutoLoadLyrics", true);
621 	add_str  ("MOCDir", "~/.moc", CHECK_NONE);
622 	add_bool ("UseMMap", false);
623 	add_bool ("UseMimeMagic", false);
624 	add_str  ("ID3v1TagsEncoding", "WINDOWS-1250", CHECK_NONE);
625 	add_bool ("UseRCC", true);
626 	add_bool ("UseRCCForFilesystem", true);
627 	add_bool ("EnforceTagsEncoding", false);
628 	add_bool ("FileNamesIconv", false);
629 	add_bool ("NonUTFXterm", false);
630 	add_bool ("Precache", true);
631 	add_bool ("SavePlaylist", true);
632 	add_bool ("SyncPlaylist", true);
633 	add_str  ("Keymap", NULL, CHECK_NONE);
634 	add_bool ("ASCIILines", false);
635 
636 	add_str  ("FastDir1", NULL, CHECK_NONE);
637 	add_str  ("FastDir2", NULL, CHECK_NONE);
638 	add_str  ("FastDir3", NULL, CHECK_NONE);
639 	add_str  ("FastDir4", NULL, CHECK_NONE);
640 	add_str  ("FastDir5", NULL, CHECK_NONE);
641 	add_str  ("FastDir6", NULL, CHECK_NONE);
642 	add_str  ("FastDir7", NULL, CHECK_NONE);
643 	add_str  ("FastDir8", NULL, CHECK_NONE);
644 	add_str  ("FastDir9", NULL, CHECK_NONE);
645 	add_str  ("FastDir10", NULL, CHECK_NONE);
646 
647 	add_int  ("SeekTime", 1, CHECK_RANGE(1), 1, INT_MAX);
648 	add_int  ("SilentSeekTime", 5, CHECK_RANGE(1), 1, INT_MAX);
649 
650 	add_list ("PreferredDecoders",
651 	                 "aac(aac,ffmpeg):m4a(ffmpeg):"
652 	                 "mpc(musepack,*,ffmpeg):mpc8(musepack,*,ffmpeg):"
653 	                 "sid(sidplay2):mus(sidplay2):"
654 	                 "wav(sndfile,*,ffmpeg):"
655 	                 "wv(wavpack,*,ffmpeg):"
656 	                 "audio/aac(aac):audio/aacp(aac):audio/m4a(ffmpeg):"
657 	                 "audio/wav(sndfile,*):"
658 	                 "ogg(vorbis,ffmpeg):oga(vorbis,ffmpeg):ogv(ffmpeg):"
659 	                 "opus(ffmpeg):"
660 	                 "spx(speex):"
661 	                 "application/ogg(vorbis):audio/ogg(vorbis)",
662 	                 CHECK_FUNCTION);
663 
664 	add_symb ("ResampleMethod", "Linear",
665 	                 CHECK_SYMBOL(5), "SincBestQuality", "SincMediumQuality",
666 	                                  "SincFastest", "ZeroOrderHold", "Linear");
667 	add_int  ("ForceSampleRate", 0, CHECK_RANGE(1), 0, 500000);
668 	add_bool ("Allow24bitOutput", false);
669 	add_bool ("UseRealtimePriority", false);
670 	add_int  ("TagsCacheSize", 256, CHECK_RANGE(1), 0, INT_MAX);
671 	add_bool ("PlaylistNumbering", true);
672 
673 	add_list ("Layout1", "directory(0,0,50%,100%):playlist(50%,0,FILL,100%)",
674 	                     CHECK_FUNCTION);
675 	add_list ("Layout2", "directory(0,0,100%,100%):playlist(0,0,100%,100%)",
676 	                     CHECK_FUNCTION);
677 	add_list ("Layout3", NULL, CHECK_FUNCTION);
678 
679 	add_bool ("FollowPlayedFile", true);
680 	add_bool ("CanStartInPlaylist", true);
681 	add_str  ("ExecCommand1", NULL, CHECK_NONE);
682 	add_str  ("ExecCommand2", NULL, CHECK_NONE);
683 	add_str  ("ExecCommand3", NULL, CHECK_NONE);
684 	add_str  ("ExecCommand4", NULL, CHECK_NONE);
685 	add_str  ("ExecCommand5", NULL, CHECK_NONE);
686 	add_str  ("ExecCommand6", NULL, CHECK_NONE);
687 	add_str  ("ExecCommand7", NULL, CHECK_NONE);
688 	add_str  ("ExecCommand8", NULL, CHECK_NONE);
689 	add_str  ("ExecCommand9", NULL, CHECK_NONE);
690 	add_str  ("ExecCommand10", NULL, CHECK_NONE);
691 
692 	add_bool ("UseCursorSelection", false);
693 	add_bool ("SetXtermTitle", true);
694 	add_bool ("SetScreenTitle", true);
695 	add_bool ("PlaylistFullPaths", true);
696 
697 	add_str  ("BlockDecorators", "`\"'", CHECK_LENGTH(1), 3, 3);
698 	add_int  ("MessageLingerTime", 3, CHECK_RANGE(1), 0, INT_MAX);
699 	add_bool ("PrefixQueuedMessages", true);
700 	add_str  ("ErrorMessagesQueued", "!", CHECK_NONE);
701 
702 	add_bool ("ModPlug_Oversampling", true);
703 	add_bool ("ModPlug_NoiseReduction", true);
704 	add_bool ("ModPlug_Reverb", false);
705 	add_bool ("ModPlug_MegaBass", false);
706 	add_bool ("ModPlug_Surround", false);
707 	add_symb ("ModPlug_ResamplingMode", "FIR",
708 	                 CHECK_SYMBOL(4), "FIR", "SPLINE", "LINEAR", "NEAREST");
709 	add_int  ("ModPlug_Channels", 2, CHECK_DISCRETE(2), 1, 2);
710 	add_int  ("ModPlug_Bits", 16, CHECK_DISCRETE(3), 8, 16, 32);
711 	add_int  ("ModPlug_Frequency", 44100,
712 	                 CHECK_DISCRETE(4), 11025, 22050, 44100, 48000);
713 	add_int  ("ModPlug_ReverbDepth", 0, CHECK_RANGE(1), 0, 100);
714 	add_int  ("ModPlug_ReverbDelay", 0, CHECK_RANGE(1), 0, INT_MAX);
715 	add_int  ("ModPlug_BassAmount", 0, CHECK_RANGE(1), 0, 100);
716 	add_int  ("ModPlug_BassRange", 10, CHECK_RANGE(1), 10, 100);
717 	add_int  ("ModPlug_SurroundDepth", 0, CHECK_RANGE(1), 0, 100);
718 	add_int  ("ModPlug_SurroundDelay", 0, CHECK_RANGE(1), 0, INT_MAX);
719 	add_int  ("ModPlug_LoopCount", 0, CHECK_RANGE(1), -1, INT_MAX);
720 
721 	add_int  ("TiMidity_Rate", 44100, CHECK_RANGE(1), 8000, 48000);
722 		// not sure about the limits... I like 44100
723 	add_int  ("TiMidity_Bits", 16, CHECK_DISCRETE(2), 8, 16);
724 	add_int  ("TiMidity_Channels", 2, CHECK_DISCRETE(2), 1, 2);
725 	add_int  ("TiMidity_Volume", 100, CHECK_RANGE(1), 0, 800);
726 	add_str  ("TiMidity_Config", NULL, CHECK_NONE);
727 
728 	add_int  ("SidPlay2_DefaultSongLength", 180,
729 	                 CHECK_RANGE(1), 0, INT_MAX);
730 	add_int  ("SidPlay2_MinimumSongLength", 0,
731 	                 CHECK_RANGE(1), 0, INT_MAX);
732 	add_str  ("SidPlay2_Database", NULL, CHECK_NONE);
733 	add_int  ("SidPlay2_Frequency", 44100, CHECK_RANGE(1), 4000, 48000);
734 	add_int  ("SidPlay2_Bits", 16, CHECK_DISCRETE(2), 8, 16);
735 	add_int  ("SidPlay2_Optimisation", 0, CHECK_RANGE(1), 0, 2);
736 	add_symb ("SidPlay2_PlayMode", "M",
737 	                 CHECK_SYMBOL(4), "M", "S", "L", "R");
738 	add_bool ("SidPlay2_StartAtStart", true);
739 	add_bool ("SidPlay2_PlaySubTunes", true);
740 
741 	add_str  ("OnSongChange", NULL, CHECK_NONE);
742 	add_bool ("RepeatSongChange", false);
743 	add_str  ("OnStop", NULL, CHECK_NONE);
744 
745 	add_bool ("QueueNextSongReturn", false);
746 }
747 
748 /* Return 1 if a parameter to an integer option is valid. */
options_check_int(const char * name,const int val)749 int options_check_int (const char *name, const int val)
750 {
751 	int opt;
752 
753 	opt = find_option (name, OPTION_INT);
754 	if (opt == -1)
755 		return 0;
756 	return options[opt].check (opt, val);
757 }
758 
759 /* Return 1 if a parameter to a boolean option is valid.  This may seem
760  * pointless but it provides a consistant interface, ensures the existence
761  * of the option and checks the value where true booleans are emulated with
762  * other types. */
options_check_bool(const char * name,const bool val)763 int options_check_bool (const char *name, const bool val)
764 {
765 	int opt, result = 0;
766 
767 	opt = find_option (name, OPTION_BOOL);
768 	if (opt == -1)
769 		return 0;
770 	if (val == true || val == false)
771 		result = 1;
772 	return result;
773 }
774 
775 /* Return 1 if a parameter to a string option is valid. */
options_check_str(const char * name,const char * val)776 int options_check_str (const char *name, const char *val)
777 {
778 	int opt;
779 
780 	opt = find_option (name, OPTION_STR | OPTION_SYMB);
781 	if (opt == -1)
782 		return 0;
783 	return options[opt].check (opt, val);
784 }
785 
786 /* Return 1 if a parameter to a symbol option is valid. */
options_check_symb(const char * name,const char * val)787 int options_check_symb (const char *name, const char *val)
788 {
789 	int opt;
790 
791 	opt = find_option (name, OPTION_SYMB);
792 	if (opt == -1)
793 		return 0;
794 	return check_discrete (opt, val);
795 }
796 
797 /* Return 1 if a parameter to a list option is valid. */
options_check_list(const char * name,const char * val)798 int options_check_list (const char *name, const char *val)
799 {
800 	int opt, size, ix, result;
801 	lists_t_strs *list;
802 
803 	assert (name);
804 	assert (val);
805 
806 	opt = find_option (name, OPTION_LIST);
807 	if (opt == -1)
808 		return 0;
809 
810 	list = lists_strs_new (8);
811 	size = lists_strs_split (list, val, ":");
812 	result = 1;
813 	for (ix = 0; ix < size; ix += 1) {
814 		if (!options[opt].check (opt, lists_strs_at (list, ix))) {
815 			result = 0;
816 			break;
817 		}
818 	}
819 
820 	lists_strs_free (list);
821 
822 	return result;
823 }
824 
825 /* Return 1 if the named option was defaulted. */
options_was_defaulted(const char * name)826 int options_was_defaulted (const char *name)
827 {
828 	int opt, result = 0;
829 
830 	assert (name);
831 
832 	opt = find_option (name, OPTION_ANY);
833 	if (opt == -1)
834 		return 0;
835 
836 	if (!options[opt].set_in_config && !options[opt].ignore_in_config)
837 		result = 1;
838 
839 	return result;
840 }
841 
is_deprecated_option(const char * name)842 static int is_deprecated_option (const char *name)
843 {
844 	if (!strcmp(name, "TagsIconv"))
845 		return 1;
846 
847 	return 0;
848 }
849 
850 /* Find and substitute variables enclosed by '${...}'.  Variables are
851  * substituted first from the environment then, if not found, from
852  * the configuration options.  Strings of the form '$${' are reduced to
853  * '${' and not substituted.  The result is returned as a new string. */
substitute_variable(const char * name_in,const char * value_in)854 static char *substitute_variable (const char *name_in, const char *value_in)
855 {
856 	size_t len;
857 	char *dollar, *result, *ptr, *name, *value, *dflt, *end;
858 	static const char accept[] = "abcdefghijklmnopqrstuvwxyz"
859 	                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
860 	                             "0123456789_";
861 	lists_t_strs *strs;
862 
863 	result = xstrdup (value_in);
864 	ptr = result;
865 	strs = lists_strs_new (5);
866 	dollar = strstr (result, "${");
867 	while (dollar) {
868 
869 		/* Escape "$${". */
870 		if (dollar > ptr && dollar[-1] == '$') {
871 			dollar[-1] = 0x00;
872 			lists_strs_append (strs, ptr);
873 			ptr = dollar;
874 			dollar = strstr (&dollar[2], "${");
875 			continue;
876 		}
877 
878 		/* Copy up to this point verbatim. */
879 		dollar[0] = 0x00;
880 		lists_strs_append (strs, ptr);
881 
882 		/* Find where the substitution variable name ends. */
883 		name = &dollar[2];
884 		len = strspn (name, accept);
885 		if (len == 0)
886 			fatal ("Error in config file option '%s':\n"
887 			       "             substitution variable name is missing!",
888 			       name_in);
889 
890 		/* Find default substitution or closing brace. */
891 		dflt = NULL;
892 		if (name[len] == '}') {
893 			end = &name[len];
894 			end[0] = 0x00;
895 		}
896 		else if (strncmp (&name[len], ":-", 2) == 0) {
897 			name[len] = 0x00;
898 			dflt = &name[len + 2];
899 			end = strchr (dflt, '}');
900 			if (end == NULL)
901 				fatal ("Error in config file option '%s': "
902 				       "unterminated '${%s:-'!",
903 				       name_in, name);
904 			end[0] = 0x00;
905 		}
906 		else if (name[len] == 0x00) {
907 			fatal ("Error in config file option '%s': "
908 			       "unterminated '${'!",
909 			       name_in);
910 		}
911 		else {
912 			fatal ("Error in config file option '%s':\n"
913 			       "             expecting  ':-' or '}' found '%c'!",
914 			       name_in, name[len]);
915 		}
916 
917 		/* Fetch environment variable or configuration option value. */
918 		value = xstrdup (getenv (name));
919 		if (value == NULL && find_option (name, OPTION_ANY) != -1) {
920 			char buf[16];
921 			lists_t_strs *list;
922 
923 			switch (options_get_type (name)) {
924 			case OPTION_INT:
925 				snprintf (buf, sizeof (buf), "%d", options_get_int (name));
926 				value = xstrdup (buf);
927 				break;
928 			case OPTION_BOOL:
929 				value = xstrdup (options_get_bool (name) ? "yes" : "no");
930 				break;
931 			case OPTION_STR:
932 				value = xstrdup (options_get_str (name));
933 				break;
934 			case OPTION_SYMB:
935 				value = xstrdup (options_get_symb (name));
936 				break;
937 			case OPTION_LIST:
938 				list = options_get_list (name);
939 				if (!lists_strs_empty (list)) {
940 					value = lists_strs_fmt (list, "%s:");
941 					value[strlen (value) - 1] = 0x00;
942 				}
943 				break;
944 			case OPTION_FREE:
945 			case OPTION_ANY:
946 				break;
947 			}
948 		}
949 		if (value && value[0])
950 			lists_strs_append (strs, value);
951 		else if (dflt)
952 			lists_strs_append (strs, dflt);
953 		else
954 			fatal ("Error in config file option '%s':\n"
955 			       "             substitution variable '%s' not set or null!",
956 		           name_in, &dollar[2]);
957 		free (value);
958 
959 		/* Go look for another substitution. */
960 		ptr = &end[1];
961 		dollar = strstr (ptr, "${");
962 	}
963 
964 	/* If anything changed copy segments to result. */
965 	if (!lists_strs_empty (strs)) {
966 		lists_strs_append (strs, ptr);
967 		free (result);
968 		result = lists_strs_cat (strs);
969 	}
970 	lists_strs_free (strs);
971 
972 	return result;
973 }
974 
975 /* Rewrite comma-separated SoundDriver value as a list value. */
rewrite_sounddriver_as_list(const char * str)976 static char *rewrite_sounddriver_as_list (const char *str)
977 {
978 	char *result, *ptr;
979 
980 	ptr = result = xmalloc (strlen (str) + 1);
981 
982 	do {
983 		if (*str == ',')
984 			*ptr++ = ':';
985 		else if (*ptr != ' ')
986 			*ptr++ = *str;
987 	} while (*str++);
988 
989 	return result;
990 }
991 
992 /* Rewrite Layout string value as a function-valued list. */
rewrite_layout_as_list(const char * str)993 static char *rewrite_layout_as_list (const char *str)
994 {
995 	int len, size, ix;
996 	char *result, *work;
997 	lists_t_strs *layouts;
998 
999 	layouts = lists_strs_new (4);
1000 	work = xstrdup (str);
1001 	size = lists_strs_split (layouts, str, " ");
1002 	free (work);
1003 
1004 	for (ix = 0; ix < size; ix += 1) {
1005 		char *ptr;
1006 
1007 		ptr = strchr (lists_strs_at (layouts, ix), ':');
1008 		if (!ptr)
1009 			fatal ("Malformed layout option: %s", str);
1010 		*ptr = '(';
1011 	}
1012 
1013 	result = lists_strs_fmt (layouts, "%s):");
1014 	len = strlen (result);
1015 	result[len - 1] = 0x00;
1016 	lists_strs_free (layouts);
1017 
1018 	return result;
1019 }
1020 
1021 /* Set an option read from the configuration file. Return false on error. */
set_option(const char * name,const char * value_in,bool append)1022 static bool set_option (const char *name, const char *value_in, bool append)
1023 {
1024 	int i;
1025 	char *value, *value_s;
1026 	const char *name_s;
1027 
1028 	if (is_deprecated_option (name)) {
1029 		fprintf (stderr, "\n\tOption '%s' was ignored;"
1030 		                 "\n\tplease remove it from your configuration file.\n", name);
1031 		sleep (5);
1032 		return true;
1033 	}
1034 
1035 	name_s = name;
1036 
1037 	/* Handle a change of option name for OSSMixerChannel. */
1038 	if (!strcasecmp (name, "OSSMixerChannel"))
1039 		name_s = "OSSMixerChannel1";
1040 
1041 	/* Handle a change of option name for ALSAMixer. */
1042 	if (!strcasecmp (name, "ALSAMixer"))
1043 		name_s = "ALSAMixer1";
1044 
1045 	/* Warn if configuration file needs updating. */
1046 	if (name != name_s) {
1047 		fprintf (stderr, "\n\tThe name of option '%s' has changed to '%s';"
1048 		                 "\n\tplease update your configuration file accordingly.\n\n",
1049 		                 name, name_s);
1050 		sleep (5);
1051 	}
1052 
1053 	i = find_option (name_s, OPTION_ANY);
1054 	if (i == -1) {
1055 		fprintf (stderr, "Wrong option name: '%s'.", name_s);
1056 		return false;
1057 	}
1058 
1059 	if (options[i].ignore_in_config)
1060 		return true;
1061 
1062 	if (append && options[i].type != OPTION_LIST) {
1063 		fprintf (stderr,
1064 		         "Only list valued options can be appended to ('%s').",
1065 		         name_s);
1066 		return false;
1067 	}
1068 
1069 	if (!append && options[i].set_in_config) {
1070 		fprintf (stderr, "Tried to set an option that has been already "
1071 		                 "set in the config file ('%s').", name_s);
1072 		return false;
1073 	}
1074 
1075 	options[i].set_in_config = 1;
1076 
1077 	/* Substitute environmental variables. */
1078 	value_s = substitute_variable (name_s, value_in);
1079 	value = NULL;
1080 
1081 	/* Handle a change of option type for SidPlay2_StartAtStart. */
1082 	if (!strcasecmp (options[i].name, "SidPlay2_StartAtStart")) {
1083 		if (!strcmp (value_s, "0"))
1084 			value = xstrdup ("no");
1085 		else if (!strcmp (value_s, "1"))
1086 			value = xstrdup ("yes");
1087 	}
1088 
1089 	/* Handle a change of option type for SidPlay2_PlaySubTunes. */
1090 	if (!strcasecmp (options[i].name, "SidPlay2_PlaySubTunes")) {
1091 		if (!strcmp (value_s, "0"))
1092 			value = xstrdup ("no");
1093 		else if (!strcmp (value_s, "1"))
1094 			value = xstrdup ("yes");
1095 	}
1096 
1097 	/* Handle a change of option type for QueueNextSongReturn. */
1098 	if (!strcasecmp (options[i].name, "QueueNextSongReturn")) {
1099 		if (!strcmp (value_s, "0"))
1100 			value = xstrdup ("no");
1101 		else if (!strcmp (value_s, "1"))
1102 			value = xstrdup ("yes");
1103 	}
1104 
1105 	/* Handle a change of option type for SoundDriver. */
1106 	if (!strcasecmp (options[i].name, "SoundDriver")) {
1107 		if (strchr (value_s, ','))
1108 			value = rewrite_sounddriver_as_list (value_s);
1109 	}
1110 
1111 	/* Handle a change of option type for Layouts. */
1112 	if (!strncasecmp (options[i].name, "Layout", 6)) {
1113 		if (value_s[0] && !strchr (value_s, '('))
1114 			value = rewrite_layout_as_list (value_s);
1115 	}
1116 
1117 	/* Warn if configuration file needs updating. */
1118 	if (value) {
1119 		free (value_s);
1120 		fprintf (stderr, "\n\tThe valid values of '%s' have changed;"
1121 		                 "\n\tplease read the comments for this option in"
1122 		                 "\n\tthe supplied config.example file and update"
1123 		                 "\n\tyour own configuration file accordingly.\n\n",
1124 		                 name_s);
1125 		sleep (5);
1126 	}
1127 	else
1128 		value = value_s;
1129 
1130 	if (!options_set_pair (name_s, value, append))
1131 		return false;
1132 
1133 	free (value);
1134 	return true;
1135 }
1136 
1137 /* Check if values of options make sense. This only checks options that can't
1138  * be checked without parsing the whole file. */
sanity_check()1139 static void sanity_check ()
1140 {
1141 	if (options_get_int ("Prebuffering") > options_get_int ("InputBuffer"))
1142 		fatal ("Prebuffering is set to a value greater than InputBuffer!");
1143 }
1144 
1145 /* Parse the configuration file. */
options_parse(const char * config_file)1146 void options_parse (const char *config_file)
1147 {
1148 	int ch;
1149 	int comm = 0; /* comment? */
1150 	int eq = 0; /* equal character appeared? */
1151 	int quote = 0; /* are we in quotes? */
1152 	int esc = 0;
1153 	bool plus = false; /* plus character appeared? */
1154 	bool append = false; /* += (list append) appeared */
1155 	bool sp = false; /* first post-name space detected */
1156 	char opt_name[30];
1157 	char opt_value[512];
1158 	int line = 1;
1159 	int name_pos = 0;
1160 	int value_pos = 0;
1161 	FILE *file;
1162 
1163 	if (!is_secure (config_file))
1164 		fatal ("Configuration file is not secure: %s", config_file);
1165 
1166 	if (!(file = fopen(config_file, "r"))) {
1167 		logit ("Can't open config file: %s", strerror (errno));
1168 		return;
1169 	}
1170 
1171 	while ((ch = getc(file)) != EOF) {
1172 
1173 		/* Skip comment */
1174 		if (comm && ch != '\n')
1175 			continue;
1176 
1177 		/* Check for "+=" (list append) */
1178 		if (ch != '=' && plus)
1179 			fatal ("Error in config file: stray '+' on line %d!", line);
1180 
1181 		/* Interpret parameter */
1182 		if (ch == '\n') {
1183 			comm = 0;
1184 
1185 			opt_name[name_pos] = 0;
1186 			opt_value[value_pos] = 0;
1187 
1188 			if (name_pos) {
1189 				if (value_pos == 0 && strncasecmp (opt_name, "Layout", 6))
1190 					fatal ("Error in config file: "
1191 					       "missing option value on line %d!", line);
1192 				if (!set_option (opt_name, opt_value, append))
1193 					fatal ("Error in config file on line %d!", line);
1194 			}
1195 
1196 			name_pos = 0;
1197 			value_pos = 0;
1198 			eq = 0;
1199 			quote = 0;
1200 			esc = 0;
1201 			append = false;
1202 			sp = false;
1203 
1204 			line++;
1205 		}
1206 
1207 		/* Turn on comment */
1208 		else if (ch == '#' && !quote)
1209 			comm = 1;
1210 
1211 		/* Turn on quote */
1212 		else if (!quote && !esc && (ch == '"'))
1213 			quote = 1;
1214 
1215 		/* Turn off quote */
1216 		else if (!esc && quote && ch == '"')
1217 			quote = 0;
1218 
1219 		else if (!esc && !eq && ch == '+')
1220 			plus = true;
1221 
1222 		else if (ch == '=' && !quote) {
1223 			if (eq)
1224 				fatal ("Error in config file: stray '=' on line %d!", line);
1225 			if (name_pos == 0)
1226 				fatal ("Error in config file: "
1227 				       "missing option name on line %d!", line);
1228 			append = plus;
1229 			plus = false;
1230 			eq = 1;
1231 		}
1232 
1233 		/* Turn on escape */
1234 		else if (ch == '\\' && !esc)
1235 			esc = 1;
1236 
1237 		/* Embedded blank detection */
1238 		else if (!eq && name_pos && isblank(ch))
1239 			sp = true;
1240 		else if (!eq && sp && !isblank(ch))
1241 			fatal ("Error in config file: "
1242 			       "embedded blank in option name on line %d!", line);
1243 
1244 		/* Add char to parameter value */
1245 		else if ((!isblank(ch) || quote) && eq) {
1246 			if (esc && ch != '"') {
1247 				if (sizeof(opt_value) == value_pos)
1248 					fatal ("Error in config file: "
1249 					       "option value on line %d is too long!", line);
1250 				opt_value[value_pos++] = '\\';
1251 			}
1252 
1253 			if (sizeof(opt_value) == value_pos)
1254 				fatal ("Error in config file: "
1255 				       "option value on line %d is too long!", line);
1256 			opt_value[value_pos++] = ch;
1257 			esc = 0;
1258 		}
1259 
1260 		/* Add char to parameter name */
1261 		else if (!isblank(ch) || quote) {
1262 			if (sizeof(opt_name) == name_pos)
1263 				fatal ("Error in config file: "
1264 				       "option name on line %d is too long!", line);
1265 			opt_name[name_pos++] = ch;
1266 			esc = 0;
1267 		}
1268 	}
1269 
1270 	if (name_pos || value_pos)
1271 		fatal ("Parse error at the end of the config file (need end of "
1272 				"line?)!");
1273 
1274 	sanity_check ();
1275 
1276 	fclose (file);
1277 }
1278 
options_free()1279 void options_free ()
1280 {
1281 	int i, ix;
1282 
1283 	for (i = 0; i < options_num; i++) {
1284 		if (options[i].type == OPTION_STR && options[i].value.str) {
1285 			free (options[i].value.str);
1286 			options[i].value.str = NULL;
1287 		}
1288 		else if (options[i].type == OPTION_LIST) {
1289 			lists_strs_free (options[i].value.list);
1290 			options[i].value.list = NULL;
1291 			for (ix = 0; ix < options[i].count; ix += 1)
1292 				free (((char **) options[i].constraints)[ix]);
1293 		}
1294 		else if (options[i].type == OPTION_SYMB)
1295 			options[i].value.str = NULL;
1296 		if (options[i].type & (OPTION_STR | OPTION_SYMB)) {
1297 			if (options[i].check != check_length) {
1298 				for (ix = 0; ix < options[i].count; ix += 1)
1299 					free (((char **) options[i].constraints)[ix]);
1300 			}
1301 		}
1302 		options[i].check = check_true;
1303 		options[i].count = 0;
1304 		if (options[i].constraints)
1305 			free (options[i].constraints);
1306 		options[i].constraints = NULL;
1307 	}
1308 }
1309 
options_get_int(const char * name)1310 int options_get_int (const char *name)
1311 {
1312 	int i = find_option (name, OPTION_INT | OPTION_BOOL);
1313 
1314 	if (i == -1)
1315 		fatal ("Tried to get wrong option '%s'!", name);
1316 
1317 	if (options[i].type == OPTION_BOOL)
1318 		return options[i].value.boolean ? 1 : 0;
1319 	return options[i].value.num;
1320 }
1321 
options_get_bool(const char * name)1322 bool options_get_bool (const char *name)
1323 {
1324 	int i = find_option (name, OPTION_BOOL);
1325 
1326 	if (i == -1)
1327 		fatal ("Tried to get wrong option '%s'!", name);
1328 
1329 	return options[i].value.boolean;
1330 }
1331 
options_get_str(const char * name)1332 char *options_get_str (const char *name)
1333 {
1334 	int i = find_option (name, OPTION_STR | OPTION_SYMB);
1335 
1336 	if (i == -1)
1337 		fatal ("Tried to get wrong option '%s'!", name);
1338 
1339 	return options[i].value.str;
1340 }
1341 
options_get_symb(const char * name)1342 char *options_get_symb (const char *name)
1343 {
1344 	int i = find_option (name, OPTION_SYMB);
1345 
1346 	if (i == -1)
1347 		fatal ("Tried to get wrong option '%s'!", name);
1348 
1349 	return options[i].value.str;
1350 }
1351 
options_get_list(const char * name)1352 struct lists_s_strs *options_get_list (const char *name)
1353 {
1354 	int i = find_option (name, OPTION_LIST);
1355 
1356 	if (i == -1)
1357 		fatal ("Tried to get wrong option '%s'!", name);
1358 
1359 	return options[i].value.list;
1360 }
1361 
options_get_type(const char * name)1362 enum option_type options_get_type (const char *name)
1363 {
1364 	int i = find_option (name, OPTION_ANY);
1365 
1366 	if (i == -1)
1367 		return OPTION_FREE;
1368 
1369 	return options[i].type;
1370 }
1371