config: assert item type matching
[vlc.git] / src / config / core.c
1 /*****************************************************************************
2  * core.c management of the modules configuration
3  *****************************************************************************
4  * Copyright (C) 2001-2007 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc_common.h>
29 #include <vlc_keys.h>
30 #include <vlc_modules.h>
31 #include <vlc_plugin.h>
32
33 #include "vlc_configuration.h"
34
35 #include <errno.h>
36 #include <assert.h>
37
38 #include "configuration.h"
39 #include "modules/modules.h"
40
41 vlc_rwlock_t config_lock = VLC_STATIC_RWLOCK;
42 bool config_dirty = false;
43
44 static inline char *strdupnull (const char *src)
45 {
46     return src ? strdup (src) : NULL;
47 }
48
49 /*****************************************************************************
50  * config_GetType: get the type of a variable (bool, int, float, string)
51  *****************************************************************************
52  * This function is used to get the type of a variable from its name.
53  *****************************************************************************/
54 int config_GetType(const char *psz_name)
55 {
56     module_config_t *p_config = config_FindConfig(psz_name);
57
58     /* sanity checks */
59     if( !p_config )
60     {
61         return 0;
62     }
63
64     switch( CONFIG_CLASS(p_config->i_type) )
65     {
66         case CONFIG_ITEM_FLOAT:
67             return VLC_VAR_FLOAT;
68         case CONFIG_ITEM_INTEGER:
69             return VLC_VAR_INTEGER;
70         case CONFIG_ITEM_BOOL:
71             return VLC_VAR_BOOL;
72         case CONFIG_ITEM_STRING:
73             return VLC_VAR_STRING;
74         default:
75             return 0;
76     }
77 }
78
79 bool config_IsSafe( const char *name )
80 {
81     module_config_t *p_config = config_FindConfig( name );
82     return p_config != NULL && p_config->b_safe;
83 }
84
85 #undef config_GetInt
86 /*****************************************************************************
87  * config_GetInt: get the value of an int variable
88  *****************************************************************************
89  * This function is used to get the value of variables which are internally
90  * represented by an integer (CONFIG_ITEM_INTEGER and
91  * CONFIG_ITEM_BOOL).
92  *****************************************************************************/
93 int64_t config_GetInt( vlc_object_t *p_this, const char *psz_name )
94 {
95     module_config_t *p_config = config_FindConfig( psz_name );
96
97     /* sanity checks */
98     if( !p_config )
99     {
100         msg_Err( p_this, "option %s does not exist", psz_name );
101         return -1;
102     }
103
104     assert(IsConfigIntegerType(p_config->i_type));
105
106     int64_t val;
107
108     vlc_rwlock_rdlock (&config_lock);
109     val = p_config->value.i;
110     vlc_rwlock_unlock (&config_lock);
111     return val;
112 }
113
114 #undef config_GetFloat
115 /*****************************************************************************
116  * config_GetFloat: get the value of a float variable
117  *****************************************************************************
118  * This function is used to get the value of variables which are internally
119  * represented by a float (CONFIG_ITEM_FLOAT).
120  *****************************************************************************/
121 float config_GetFloat( vlc_object_t *p_this, const char *psz_name )
122 {
123     module_config_t *p_config;
124
125     p_config = config_FindConfig( psz_name );
126
127     /* sanity checks */
128     if( !p_config )
129     {
130         msg_Err( p_this, "option %s does not exist", psz_name );
131         return -1;
132     }
133
134     assert(IsConfigFloatType(p_config->i_type));
135
136     float val;
137
138     vlc_rwlock_rdlock (&config_lock);
139     val = p_config->value.f;
140     vlc_rwlock_unlock (&config_lock);
141     return val;
142 }
143
144 #undef config_GetPsz
145 /*****************************************************************************
146  * config_GetPsz: get the string value of a string variable
147  *****************************************************************************
148  * This function is used to get the value of variables which are internally
149  * represented by a string (CONFIG_ITEM_STRING, CONFIG_ITEM_*FILE,
150  * CONFIG_ITEM_DIRECTORY, CONFIG_ITEM_PASSWORD, and CONFIG_ITEM_MODULE).
151  *
152  * Important note: remember to free() the returned char* because it's a
153  *   duplicate of the actual value. It isn't safe to return a pointer to the
154  *   actual value as it can be modified at any time.
155  *****************************************************************************/
156 char * config_GetPsz( vlc_object_t *p_this, const char *psz_name )
157 {
158     module_config_t *p_config;
159
160     p_config = config_FindConfig( psz_name );
161
162     /* sanity checks */
163     if( !p_config )
164     {
165         msg_Err( p_this, "option %s does not exist", psz_name );
166         return NULL;
167     }
168
169     assert(IsConfigStringType (p_config->i_type));
170
171     /* return a copy of the string */
172     vlc_rwlock_rdlock (&config_lock);
173     char *psz_value = strdupnull (p_config->value.psz);
174     vlc_rwlock_unlock (&config_lock);
175
176     return psz_value;
177 }
178
179 #undef config_PutPsz
180 /*****************************************************************************
181  * config_PutPsz: set the string value of a string variable
182  *****************************************************************************
183  * This function is used to set the value of variables which are internally
184  * represented by a string (CONFIG_ITEM_STRING, CONFIG_ITEM_*FILE,
185  * CONFIG_ITEM_DIRECTORY, CONFIG_ITEM_PASSWORD, and CONFIG_ITEM_MODULE).
186  *****************************************************************************/
187 void config_PutPsz( vlc_object_t *p_this,
188                       const char *psz_name, const char *psz_value )
189 {
190     module_config_t *p_config = config_FindConfig( psz_name );
191
192
193     /* sanity checks */
194     if( !p_config )
195     {
196         msg_Warn( p_this, "option %s does not exist", psz_name );
197         return;
198     }
199
200     assert(IsConfigStringType(p_config->i_type));
201
202     char *str, *oldstr;
203     if ((psz_value != NULL) && *psz_value)
204         str = strdup (psz_value);
205     else
206         str = NULL;
207
208     vlc_rwlock_wrlock (&config_lock);
209     oldstr = (char *)p_config->value.psz;
210     p_config->value.psz = str;
211     config_dirty = true;
212     vlc_rwlock_unlock (&config_lock);
213
214     free (oldstr);
215 }
216
217 #undef config_PutInt
218 /*****************************************************************************
219  * config_PutInt: set the integer value of an int variable
220  *****************************************************************************
221  * This function is used to set the value of variables which are internally
222  * represented by an integer (CONFIG_ITEM_INTEGER and
223  * CONFIG_ITEM_BOOL).
224  *****************************************************************************/
225 void config_PutInt( vlc_object_t *p_this, const char *psz_name,
226                     int64_t i_value )
227 {
228     module_config_t *p_config = config_FindConfig( psz_name );
229
230     /* sanity checks */
231     if( !p_config )
232     {
233         msg_Warn( p_this, "option %s does not exist", psz_name );
234         return;
235     }
236
237     assert(IsConfigIntegerType(p_config->i_type));
238
239     if (i_value < p_config->min.i)
240         i_value = p_config->min.i;
241     if (i_value > p_config->max.i)
242         i_value = p_config->max.i;
243
244     vlc_rwlock_wrlock (&config_lock);
245     p_config->value.i = i_value;
246     config_dirty = true;
247     vlc_rwlock_unlock (&config_lock);
248 }
249
250 #undef config_PutFloat
251 /*****************************************************************************
252  * config_PutFloat: set the value of a float variable
253  *****************************************************************************
254  * This function is used to set the value of variables which are internally
255  * represented by a float (CONFIG_ITEM_FLOAT).
256  *****************************************************************************/
257 void config_PutFloat( vlc_object_t *p_this,
258                       const char *psz_name, float f_value )
259 {
260     module_config_t *p_config = config_FindConfig( psz_name );
261
262     /* sanity checks */
263     if( !p_config )
264     {
265         msg_Warn( p_this, "option %s does not exist", psz_name );
266         return;
267     }
268
269     assert(IsConfigFloatType(p_config->i_type));
270
271     /* if f_min == f_max == 0, then do not use them */
272     if ((p_config->min.f == 0.f) && (p_config->max.f == 0.f))
273         ;
274     else if (f_value < p_config->min.f)
275         f_value = p_config->min.f;
276     else if (f_value > p_config->max.f)
277         f_value = p_config->max.f;
278
279     vlc_rwlock_wrlock (&config_lock);
280     p_config->value.f = f_value;
281     config_dirty = true;
282     vlc_rwlock_unlock (&config_lock);
283 }
284
285 /**
286  * Determines a list of suggested values for an integer configuration item.
287  * \param values pointer to a table of integer values [OUT]
288  * \param texts pointer to a table of descriptions strings [OUT]
289  * \return number of choices, or -1 on error
290  * \note the caller is responsible for calling free() on all descriptions and
291  * on both tables. In case of error, both pointers are set to NULL.
292  */
293 ssize_t config_GetIntChoices (vlc_object_t *obj, const char *name,
294                              int64_t **restrict values, char ***restrict texts)
295 {
296     *values = NULL;
297     *texts = NULL;
298
299     module_config_t *cfg = config_FindConfig(name);
300     if (cfg == NULL)
301     {
302         msg_Warn (obj, "option %s does not exist", name);
303         errno = ENOENT;
304         return -1;
305     }
306
307     size_t count = cfg->list_count;
308     if (count == 0)
309     {
310         if (module_Map(obj, cfg->owner))
311         {
312             errno = EIO;
313             return -1;
314         }
315
316         if (cfg->list.i_cb == NULL)
317             return 0;
318         return cfg->list.i_cb(obj, name, values, texts);
319     }
320
321     int64_t *vals = malloc (sizeof (*vals) * count);
322     char **txts = malloc (sizeof (*txts) * count);
323     if (vals == NULL || txts == NULL)
324     {
325         errno = ENOMEM;
326         goto error;
327     }
328
329     for (size_t i = 0; i < count; i++)
330     {
331         vals[i] = cfg->list.i[i];
332         /* FIXME: use module_gettext() instead */
333         txts[i] = strdup ((cfg->list_text[i] != NULL)
334                                        ? vlc_gettext (cfg->list_text[i]) : "");
335         if (unlikely(txts[i] == NULL))
336         {
337             for (int j = i - 1; j >= 0; --j)
338                 free(txts[j]);
339             errno = ENOMEM;
340             goto error;
341         }
342     }
343
344     *values = vals;
345     *texts = txts;
346     return count;
347 error:
348
349     free(vals);
350     free(txts);
351     return -1;
352 }
353
354
355 static ssize_t config_ListModules (const char *cap, char ***restrict values,
356                                    char ***restrict texts)
357 {
358     module_t **list;
359     ssize_t n = module_list_cap (&list, cap);
360     if (n <= 0)
361     {
362         *values = *texts = NULL;
363         module_list_free (list);
364         return n;
365     }
366
367     char **vals = xmalloc ((n + 2) * sizeof (*vals));
368     char **txts = xmalloc ((n + 2) * sizeof (*txts));
369
370     vals[0] = xstrdup ("any");
371     txts[0] = xstrdup (_("Automatic"));
372
373     for (ssize_t i = 0; i < n; i++)
374     {
375         vals[i + 1] = xstrdup (module_get_object (list[i]));
376         txts[i + 1] = xstrdup (module_gettext (list[i],
377                                module_get_name (list[i], true)));
378     }
379
380     vals[n + 1] = xstrdup ("none");
381     txts[n + 1] = xstrdup (_("Disable"));
382
383     *values = vals;
384     *texts = txts;
385     module_list_free (list);
386     return n + 2;
387 }
388
389 /**
390  * Determines a list of suggested values for a string configuration item.
391  * \param values pointer to a table of value strings [OUT]
392  * \param texts pointer to a table of descriptions strings [OUT]
393  * \return number of choices, or -1 on error
394  * \note the caller is responsible for calling free() on all values, on all
395  * descriptions and on both tables.
396  * In case of error, both pointers are set to NULL.
397  */
398 ssize_t config_GetPszChoices (vlc_object_t *obj, const char *name,
399                               char ***restrict values, char ***restrict texts)
400 {
401     *values = *texts = NULL;
402
403     module_config_t *cfg = config_FindConfig(name);
404     if (cfg == NULL)
405     {
406         errno = ENOENT;
407         return -1;
408     }
409
410     switch (cfg->i_type)
411     {
412         case CONFIG_ITEM_MODULE:
413             return config_ListModules (cfg->psz_type, values, texts);
414         default:
415             if (!IsConfigStringType (cfg->i_type))
416             {
417                 errno = EINVAL;
418                 return -1;
419             }
420             break;
421     }
422
423     size_t count = cfg->list_count;
424     if (count == 0)
425     {
426         if (module_Map(obj, cfg->owner))
427         {
428             errno = EIO;
429             return -1;
430         }
431
432         if (cfg->list.psz_cb == NULL)
433             return 0;
434         return cfg->list.psz_cb(obj, name, values, texts);
435     }
436
437     char **vals = xmalloc (sizeof (*vals) * count);
438     char **txts = xmalloc (sizeof (*txts) * count);
439
440     for (size_t i = 0; i < count; i++)
441     {
442         vals[i] = xstrdup ((cfg->list.psz[i] != NULL) ? cfg->list.psz[i] : "");
443         /* FIXME: use module_gettext() instead */
444         txts[i] = xstrdup ((cfg->list_text[i] != NULL)
445                                        ? vlc_gettext (cfg->list_text[i]) : "");
446     }
447
448     *values = vals;
449     *texts = txts;
450     return count;
451 }
452
453 static int confcmp (const void *a, const void *b)
454 {
455     const module_config_t *const *ca = a, *const *cb = b;
456
457     return strcmp ((*ca)->psz_name, (*cb)->psz_name);
458 }
459
460 static int confnamecmp (const void *key, const void *elem)
461 {
462     const module_config_t *const *conf = elem;
463
464     return strcmp (key, (*conf)->psz_name);
465 }
466
467 static struct
468 {
469     module_config_t **list;
470     size_t count;
471 } config = { NULL, 0 };
472
473 /**
474  * Index the configuration items by name for faster lookups.
475  */
476 int config_SortConfig (void)
477 {
478     vlc_plugin_t *p;
479     size_t nconf = 0;
480
481     for (p = vlc_plugins; p != NULL; p = p->next)
482          nconf += p->conf.size;
483
484     module_config_t **clist = malloc (sizeof (*clist) * nconf);
485     if (unlikely(clist == NULL))
486         return VLC_ENOMEM;
487
488     nconf = 0;
489     for (p = vlc_plugins; p != NULL; p = p->next)
490     {
491         module_config_t *item, *end;
492
493         for (item = p->conf.items, end = item + p->conf.size;
494              item < end;
495              item++)
496         {
497             if (!CONFIG_ITEM(item->i_type))
498                 continue; /* ignore hints */
499             clist[nconf++] = item;
500         }
501     }
502
503     qsort (clist, nconf, sizeof (*clist), confcmp);
504
505     config.list = clist;
506     config.count = nconf;
507     return VLC_SUCCESS;
508 }
509
510 void config_UnsortConfig (void)
511 {
512     module_config_t **clist;
513
514     clist = config.list;
515     config.list = NULL;
516     config.count = 0;
517
518     free (clist);
519 }
520
521 /*****************************************************************************
522  * config_FindConfig: find the config structure associated with an option.
523  *****************************************************************************/
524 module_config_t *config_FindConfig(const char *name)
525 {
526     if (unlikely(name == NULL))
527         return NULL;
528
529     module_config_t *const *p;
530     p = bsearch (name, config.list, config.count, sizeof (*p), confnamecmp);
531     return p ? *p : NULL;
532 }
533
534 /**
535  * Destroys an array of configuration items.
536  * \param config start of array of items
537  * \param confsize number of items in the array
538  */
539 void config_Free (module_config_t *tab, size_t confsize)
540 {
541     for (size_t j = 0; j < confsize; j++)
542     {
543         module_config_t *p_item = &tab[j];
544
545         if (IsConfigStringType (p_item->i_type))
546         {
547             free (p_item->value.psz);
548             if (p_item->list_count)
549                 free (p_item->list.psz);
550         }
551
552         free (p_item->list_text);
553     }
554
555     free (tab);
556 }
557
558 #undef config_ResetAll
559 /*****************************************************************************
560  * config_ResetAll: reset the configuration data for all the modules.
561  *****************************************************************************/
562 void config_ResetAll( vlc_object_t *p_this )
563 {
564     vlc_rwlock_wrlock (&config_lock);
565     for (vlc_plugin_t *p = vlc_plugins; p != NULL; p = p->next)
566     {
567         for (size_t i = 0; i < p->conf.size; i++ )
568         {
569             module_config_t *p_config = p->conf.items + i;
570
571             if (IsConfigIntegerType (p_config->i_type))
572                 p_config->value.i = p_config->orig.i;
573             else
574             if (IsConfigFloatType (p_config->i_type))
575                 p_config->value.f = p_config->orig.f;
576             else
577             if (IsConfigStringType (p_config->i_type))
578             {
579                 free ((char *)p_config->value.psz);
580                 p_config->value.psz =
581                         strdupnull (p_config->orig.psz);
582             }
583         }
584     }
585     vlc_rwlock_unlock (&config_lock);
586
587     VLC_UNUSED(p_this);
588 }