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