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