1 /*
2     cfgvar.c:
3 
4     Copyright (C) 2005 Istvan Varga
5 
6     This file is part of Csound.
7 
8     The Csound Library is free software; you can redistribute it
9     and/or modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     Csound 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
19     License along with Csound; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21     02110-1301 USA
22 */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <math.h>
29 
30 #include "csoundCore.h"
31 #include "cfgvar.h"
32 #include "namedins.h"
33 
34 /* the global database */
35 
36 /* list of error messages */
37 
38 static const char *errmsg_list[] = {
39     Str_noop("(no error)"),
40     Str_noop("invalid variable name"),
41     Str_noop("invalid variable type"),
42     Str_noop("invalid flags specified"),
43     Str_noop("NULL pointer argument"),
44     Str_noop("the specified value is too high"),
45     Str_noop("the specified value is too low"),
46     Str_noop("the specified value is not an integer power of two"),
47     Str_noop("invalid boolean value; must be 0 or 1"),
48     Str_noop("memory allocation failed"),
49     Str_noop("string exceeds maximum allowed length"),
50     Str_noop("(unknown error)")
51 };
52 
53 /* check if the specified name, and type and flags values are valid */
54 
check_name(const char * name)55 static int check_name(const char *name)
56 {
57     int i, c;
58 
59     if (UNLIKELY(name == NULL))
60       return CSOUNDCFG_INVALID_NAME;
61     if (UNLIKELY(name[0] == '\0'))
62       return CSOUNDCFG_INVALID_NAME;
63     i = -1;
64     while (name[++i] != '\0') {
65       c = (int) ((unsigned char) name[i]);
66       if (UNLIKELY((c & 0x80) || !(c == '_' || isalpha(c) || isdigit(c))))
67         return CSOUNDCFG_INVALID_NAME;
68     }
69     return CSOUNDCFG_SUCCESS;
70 }
71 
check_type(int type)72 static int check_type(int type)
73 {
74     switch (type) {
75       case CSOUNDCFG_INTEGER:
76       case CSOUNDCFG_BOOLEAN:
77       case CSOUNDCFG_FLOAT:
78       case CSOUNDCFG_DOUBLE:
79       case CSOUNDCFG_MYFLT:
80       case CSOUNDCFG_STRING:
81         break;
82       default:
83         return CSOUNDCFG_INVALID_TYPE;
84     }
85     return CSOUNDCFG_SUCCESS;
86 }
87 
check_flags(int flags)88 static int check_flags(int flags)
89 {
90     if (UNLIKELY((flags & (~(CSOUNDCFG_POWOFTWO))) != 0))
91       return CSOUNDCFG_INVALID_FLAG;
92     return CSOUNDCFG_SUCCESS;
93 }
94 
95 /**
96  * Allocate configuration variable structure with the specified parameters:
97  *   name:    name of the variable (may contain letters, digits, and _)
98  *   p:       pointer to variable
99  *   type:    type of variable, determines how 'p' is interpreted
100  *              CSOUNDCFG_INTEGER:      int*
101  *              CSOUNDCFG_BOOLEAN:      int* (value may be 0 or 1)
102  *              CSOUNDCFG_FLOAT:        float*
103  *              CSOUNDCFG_DOUBLE:       double*
104  *              CSOUNDCFG_MYFLT:        MYFLT*
105  *              CSOUNDCFG_STRING:       char* (should have enough space)
106  *   flags:   bitwise OR of flag values, currently only CSOUNDCFG_POWOFTWO
107  *            is available, which requests CSOUNDCFG_INTEGER values to be
108  *            power of two
109  *   min:     for CSOUNDCFG_INTEGER, CSOUNDCFG_FLOAT, CSOUNDCFG_DOUBLE, and
110  *            CSOUNDCFG_MYFLT, a pointer to a variable of the type selected
111  *            by 'type' that specifies the minimum allowed value.
112  *            If 'min' is NULL, there is no minimum value.
113  *   max:     similar to 'min', except it sets the maximum allowed value.
114  *            For CSOUNDCFG_STRING, it is a pointer to an int variable
115  *            that defines the maximum length of the string (including the
116  *            null character at the end) in bytes. This value is limited
117  *            to the range 8 to 16384, and if max is NULL, it defaults to 256.
118  *   shortDesc: a short description of the variable (may be NULL or an empty
119  *            string if a description is not available)
120  *   longDesc: a long description of the variable (may be NULL or an empty
121  *            string if a description is not available)
122  * A pointer to the newly allocated structure is stored in (*ptr).
123  * Return value is CSOUNDCFG_SUCCESS, or one of the error codes.
124  */
125 
cfg_alloc_structure(CSOUND * csound,csCfgVariable_t ** ptr,const char * name,void * p,int type,int flags,void * min,void * max,const char * shortDesc,const char * longDesc)126 static int cfg_alloc_structure(CSOUND* csound,
127                                csCfgVariable_t **ptr,
128                                const char *name,
129                                void *p, int type, int flags,
130                                void *min, void *max,
131                                const char *shortDesc, const char *longDesc)
132 {
133     int     structBytes = 0, nameBytes, sdBytes, ldBytes, totBytes;
134     void    *pp;
135     unsigned char *sdp = NULL, *ldp = NULL;
136 
137     (*ptr) = (csCfgVariable_t*) NULL;
138     /* check for valid parameters */
139     if (UNLIKELY(p == NULL))
140       return CSOUNDCFG_NULL_POINTER;
141     if (UNLIKELY(check_name(name) != CSOUNDCFG_SUCCESS))
142       return CSOUNDCFG_INVALID_NAME;
143     if (UNLIKELY(check_type(type) != CSOUNDCFG_SUCCESS))
144       return CSOUNDCFG_INVALID_TYPE;
145     if (UNLIKELY(check_flags(flags) != CSOUNDCFG_SUCCESS))
146       return CSOUNDCFG_INVALID_FLAG;
147     /* calculate the number of bytes to allocate */
148     structBytes = ((int) sizeof(csCfgVariable_t) + 15) & (~15);
149     nameBytes = (((int) strlen(name) + 1) + 15) & (~15);
150     if (shortDesc != NULL)
151       sdBytes = (shortDesc[0] == '\0' ? 0 : (int) strlen(shortDesc) + 1);
152     else
153       sdBytes = 0;
154     sdBytes = (sdBytes + 15) & (~15);
155     if (longDesc != NULL)
156       ldBytes = (longDesc[0] == '\0' ? 0 : (int) strlen(longDesc) + 1);
157     else
158       ldBytes = 0;
159     ldBytes = (ldBytes + 15) & (~15);
160     totBytes = structBytes + nameBytes + sdBytes + ldBytes;
161     /* allocate memory */
162     pp = (void*) csound->Calloc(csound, (size_t) totBytes);
163     if (UNLIKELY(pp == NULL))
164       return CSOUNDCFG_MEMORY;
165     //memset(pp, 0, (size_t) totBytes);
166     (*ptr) = (csCfgVariable_t*) pp;
167     /* copy name and descriptions */
168     strcpy(((char*) pp + (int) structBytes), name);
169     if (sdBytes > 0) {
170       sdp = (unsigned char*) pp + (int) (structBytes + nameBytes);
171       strcpy((char*) sdp, shortDesc);
172     }
173     else
174       sdp = NULL;
175     if (ldBytes > 0) {
176       ldp = (unsigned char*) pp + (int) (structBytes + nameBytes + sdBytes);
177       strcpy((char*) ldp, longDesc);
178     }
179     else
180       ldp = NULL;
181     /* set up structure */
182     (*ptr)->h.nxt = (csCfgVariable_t*) NULL;
183     (*ptr)->h.name = (unsigned char*) pp + (int) structBytes;
184     (*ptr)->h.p = p;
185     (*ptr)->h.type = type;
186     (*ptr)->h.flags = flags;
187     (*ptr)->h.shortDesc = sdp;
188     (*ptr)->h.longDesc = ldp;
189     /* depending on type */
190     switch (type) {
191       case CSOUNDCFG_INTEGER:                                   /* integer */
192         (*ptr)->i.min = (min == NULL ? -0x7FFFFFFF : *((int*) min));
193         (*ptr)->i.max = (max == NULL ? 0x7FFFFFFF : *((int*) max));
194         break;
195       case CSOUNDCFG_BOOLEAN:                                   /* boolean */
196         (*ptr)->b.flags &= (~(CSOUNDCFG_POWOFTWO));
197         break;
198       case CSOUNDCFG_FLOAT:                                     /* float */
199         (*ptr)->f.flags &= (~(CSOUNDCFG_POWOFTWO));
200         (*ptr)->f.min = (min == NULL ? -1.0e30f : *((float*) min));
201         (*ptr)->f.max = (max == NULL ? 1.0e30f : *((float*) max));
202         break;
203       case CSOUNDCFG_DOUBLE:                                    /* double */
204         (*ptr)->d.flags &= (~(CSOUNDCFG_POWOFTWO));
205         (*ptr)->d.min = (min == NULL ? -1.0e30 : *((double*) min));
206         (*ptr)->d.max = (max == NULL ? 1.0e30 : *((double*) max));
207         break;
208       case CSOUNDCFG_MYFLT:                                     /* MYFLT */
209         (*ptr)->m.flags &= (~(CSOUNDCFG_POWOFTWO));
210         (*ptr)->m.min = (min == NULL ? (MYFLT) -1.0e30 : *((MYFLT*) min));
211         (*ptr)->m.max = (max == NULL ? (MYFLT) 1.0e30 : *((MYFLT*) max));
212         break;
213       case CSOUNDCFG_STRING:                                    /* string */
214         (*ptr)->s.flags &= (~(CSOUNDCFG_POWOFTWO));
215         if (max != NULL) {
216           (*ptr)->s.maxlen = *((int*) max);
217           if ((*ptr)->s.maxlen < 8) (*ptr)->s.maxlen = 8;
218           if ((*ptr)->s.maxlen > 16384) (*ptr)->s.maxlen = 16384;
219         }
220         else
221           (*ptr)->s.maxlen = 256;   /* default maximum string length */
222         break;
223     }
224     /* done */
225     return CSOUNDCFG_SUCCESS;
226 }
227 
228 /**
229  * This function is similar to csoundCreateGlobalConfigurationVariable(),
230  * except it creates a configuration variable specific to Csound instance
231  * 'csound', and is suitable for calling from the Csound library
232  * (in csoundPreCompile()) or plugins (in csoundModuleCreate()).
233  * The other parameters and return value are the same as in the case of
234  * csoundCreateGlobalConfigurationVariable().
235  */
236 
237 PUBLIC int
csoundCreateConfigurationVariable(CSOUND * csound,const char * name,void * p,int type,int flags,void * min,void * max,const char * shortDesc,const char * longDesc)238   csoundCreateConfigurationVariable(CSOUND *csound, const char *name,
239                                     void *p, int type, int flags,
240                                     void *min, void *max,
241                                     const char *shortDesc,
242                                     const char *longDesc)
243 {
244     csCfgVariable_t *pp;
245     int             retval;
246 
247     /* check if name is already in use */
248     if (UNLIKELY(csoundQueryConfigurationVariable(csound, name) != NULL))
249       return CSOUNDCFG_INVALID_NAME;
250     /* if database is not allocated yet, create an empty one */
251     if (csound->cfgVariableDB == NULL) {
252       csound->cfgVariableDB = cs_hash_table_create(csound);
253       if (UNLIKELY(csound->cfgVariableDB == NULL))
254         return CSOUNDCFG_MEMORY;
255     }
256     /* allocate structure */
257     retval =  cfg_alloc_structure(csound, &pp, name, p, type, flags, min, max,
258                                   shortDesc, longDesc);
259     if (UNLIKELY(retval != CSOUNDCFG_SUCCESS))
260       return retval;
261     /* link into database */
262 
263     cs_hash_table_put(csound, csound->cfgVariableDB, (char*)name, pp);
264 
265     /* report success */
266     return CSOUNDCFG_SUCCESS;
267 }
268 
269 /* set configuration variable to value (with error checking) */
270 
set_cfgvariable_value(csCfgVariable_t * pp,void * value)271 static int set_cfgvariable_value(csCfgVariable_t *pp, void *value)
272 {
273     double  dVal;
274     MYFLT   mVal;
275     float   fVal;
276     int     iVal;
277     /* set value depending on type */
278     if (UNLIKELY(value == NULL))
279       return CSOUNDCFG_NULL_POINTER;
280     switch (pp->h.type) {
281       case CSOUNDCFG_INTEGER:
282         iVal = *((int*) value);
283         if (UNLIKELY(iVal < pp->i.min)) return CSOUNDCFG_TOO_LOW;
284         if (UNLIKELY(iVal > pp->i.max)) return CSOUNDCFG_TOO_HIGH;
285         if (pp->i.flags & CSOUNDCFG_POWOFTWO)
286           if (UNLIKELY(iVal < 1 || (iVal & (iVal - 1)) != 0))
287             return CSOUNDCFG_NOT_POWOFTWO;
288         *(pp->i.p) = iVal;
289         break;
290       case CSOUNDCFG_BOOLEAN:
291         iVal = *((int*) value);
292         if (UNLIKELY(iVal & (~1))) return CSOUNDCFG_INVALID_BOOLEAN;
293         *(pp->b.p) = iVal;
294         break;
295       case CSOUNDCFG_FLOAT:
296         fVal = *((float*) value);
297         if (UNLIKELY(fVal < pp->f.min)) return CSOUNDCFG_TOO_LOW;
298         if (UNLIKELY(fVal > pp->f.max)) return CSOUNDCFG_TOO_HIGH;
299         *(pp->f.p) = fVal;
300         break;
301       case CSOUNDCFG_DOUBLE:
302         dVal = *((double*) value);
303         if (UNLIKELY(dVal < pp->d.min)) return CSOUNDCFG_TOO_LOW;
304         if (UNLIKELY(dVal > pp->d.max)) return CSOUNDCFG_TOO_HIGH;
305         *(pp->d.p) = dVal;
306         break;
307       case CSOUNDCFG_MYFLT:
308         mVal = *((MYFLT*) value);
309         if (UNLIKELY(mVal < pp->m.min)) return CSOUNDCFG_TOO_LOW;
310         if (UNLIKELY(mVal > pp->m.max)) return CSOUNDCFG_TOO_HIGH;
311         *(pp->m.p) = mVal;
312         break;
313       case CSOUNDCFG_STRING:
314         if (UNLIKELY((int) strlen((char*) value) >= (pp->s.maxlen - 1)))
315           return CSOUNDCFG_STRING_LENGTH;
316         strcpy((char*) (pp->s.p), (char*) value);
317         break;
318       default:
319         return CSOUNDCFG_INVALID_TYPE;
320     }
321     return CSOUNDCFG_SUCCESS;
322 }
323 
324 /**
325  * Set the value of a configuration variable of Csound instance 'csound'.
326  * The 'name' and 'value' parameters, and return value are the same as
327  * in the case of csoundSetGlobalConfigurationVariable().
328  */
329 
330 PUBLIC int
csoundSetConfigurationVariable(CSOUND * csound,const char * name,void * value)331   csoundSetConfigurationVariable(CSOUND *csound, const char *name, void *value)
332 {
333     csCfgVariable_t *pp;
334 
335     /* get pointer to variable */
336     pp = csoundQueryConfigurationVariable(csound, name);
337     if (UNLIKELY(pp == NULL))
338       return CSOUNDCFG_INVALID_NAME;    /* not found */
339     return (set_cfgvariable_value(pp, value));
340 }
341 
342 /* parse string value for configuration variable */
343 
parse_cfg_variable(csCfgVariable_t * pp,const char * value)344 static int parse_cfg_variable(csCfgVariable_t *pp, const char *value)
345 {
346     double  dVal;
347     MYFLT   mVal;
348     float   fVal;
349     int     iVal;
350 
351     if (UNLIKELY(value == NULL))
352       return CSOUNDCFG_NULL_POINTER;
353     switch (pp->h.type) {
354       case CSOUNDCFG_INTEGER:
355         iVal = (int) atoi(value);
356         return set_cfgvariable_value(pp, (void*) (&iVal));
357       case CSOUNDCFG_BOOLEAN:
358         if (strcmp(value, "0") == 0   ||
359             strcmp(value, "no") == 0  || strcmp(value, "No") == 0  ||
360             strcmp(value, "NO") == 0  || strcmp(value, "off") == 0 ||
361             strcmp(value, "Off") == 0 || strcmp(value, "OFF") == 0 ||
362             strcmp(value, "false") == 0 || strcmp(value, "False") == 0 ||
363             strcmp(value, "FALSE") == 0)
364           *(pp->b.p) = 0;
365         else if (strcmp(value, "1") == 0   ||
366                  strcmp(value, "yes") == 0 || strcmp(value, "Yes") == 0 ||
367                  strcmp(value, "YES") == 0 || strcmp(value, "on") == 0  ||
368                  strcmp(value, "On") == 0  || strcmp(value, "ON") == 0  ||
369                  strcmp(value, "true") == 0 || strcmp(value, "True") == 0 ||
370                  strcmp(value, "TRUE") == 0)
371           *(pp->b.p) = 1;
372         else
373           return CSOUNDCFG_INVALID_BOOLEAN;
374         return CSOUNDCFG_SUCCESS;
375       case CSOUNDCFG_FLOAT:
376         fVal = (float) atof(value);
377         return set_cfgvariable_value(pp, (void*) (&fVal));
378       case CSOUNDCFG_DOUBLE:
379         dVal = (double) atof(value);
380         return set_cfgvariable_value(pp, (void*) (&dVal));
381       case CSOUNDCFG_MYFLT:
382         mVal = (MYFLT) atof(value);
383         return set_cfgvariable_value(pp, (void*) (&mVal));
384       case CSOUNDCFG_STRING:
385         return set_cfgvariable_value(pp, (void*) value);
386     }
387     return CSOUNDCFG_INVALID_TYPE;
388 }
389 
390 /**
391  * Set the value of a configuration variable of Csound instance 'csound',
392  * by parsing a string.
393  * The 'name' and 'value' parameters, and return value are the same as
394  * in the case of csoundParseGlobalConfigurationVariable().
395  */
396 
397 PUBLIC int
csoundParseConfigurationVariable(CSOUND * csound,const char * name,const char * value)398   csoundParseConfigurationVariable(CSOUND *csound, const char *name,
399                                                     const char *value)
400 {
401     csCfgVariable_t *pp;
402 
403     /* get pointer to variable */
404     pp = csoundQueryConfigurationVariable(csound, name);
405     if (UNLIKELY(pp == NULL))
406       return CSOUNDCFG_INVALID_NAME;    /* not found */
407     return (parse_cfg_variable(pp, value));
408 }
409 
410 /**
411  * Return pointer to the configuration variable of Csound instace 'csound'
412  * with the specified name.
413  * The return value may be NULL if the variable is not found in the database.
414  */
415 
416 PUBLIC csCfgVariable_t
csoundQueryConfigurationVariable(CSOUND * csound,const char * name)417     *csoundQueryConfigurationVariable(CSOUND *csound, const char *name)
418 {
419     if (csound->cfgVariableDB == NULL) {
420         return NULL;
421     }
422     return (csCfgVariable_t*) cs_hash_table_get(csound,
423                                                 csound->cfgVariableDB, (char*)name);
424 }
425 
426 /* compare function for qsort() */
427 
compare_func(const void * p1,const void * p2)428 static int compare_func(const void *p1, const void *p2)
429 {
430     return (int) strcmp((char*) ((*((csCfgVariable_t**) p1))->h.name),
431                         (char*) ((*((csCfgVariable_t**) p2))->h.name));
432 }
433 
434 /* create alphabetically sorted list of all entries in 'db' */
435 
list_db_entries(CSOUND * csound,CS_HASH_TABLE * db)436 static csCfgVariable_t **list_db_entries(CSOUND* csound, CS_HASH_TABLE *db)
437 {
438     csCfgVariable_t **lst;
439     size_t          cnt;
440     CONS_CELL*      values;
441 
442     values = cs_hash_table_values(csound, db);
443     cnt = cs_cons_length(values);
444 
445 
446     /* allocate memory for list */
447     lst = (csCfgVariable_t**) csound->Malloc(csound, sizeof(csCfgVariable_t*)
448                                              * (cnt + (size_t) 1));
449     if (UNLIKELY(lst == NULL))
450       return (csCfgVariable_t**) NULL;  /* not enough memory */
451     /* create list */
452     if (cnt) {
453       cnt = 0;
454         while (values != NULL) {
455             lst[cnt++] = (csCfgVariable_t*)values->value;
456             values = values->next;
457         }
458         qsort((void*) lst, cnt, sizeof(csCfgVariable_t*), compare_func);
459     }
460 
461     lst[cnt] = (csCfgVariable_t*) NULL;
462     /* return pointer to list */
463     return lst;
464 }
465 
466 /**
467  * Create an alphabetically sorted list of all configuration variables
468  * of Csound instance 'csound'.
469  * Returns a pointer to a NULL terminated array of configuration variable
470  * pointers, or NULL on error.
471  * The caller is responsible for freeing the returned list with
472  * csoundDeleteCfgVarList(), however, the variable pointers in the list
473  * should not be freed.
474  */
475 
csoundListConfigurationVariables(CSOUND * csound)476 PUBLIC csCfgVariable_t **csoundListConfigurationVariables(CSOUND *csound)
477 {
478     return (list_db_entries(csound, csound->cfgVariableDB));
479 }
480 
481 /**
482  * Release a configuration variable list previously returned
483  * by csoundListGlobalConfigurationVariables() or
484  * csoundListConfigurationVariables().
485  */
486 
csoundDeleteCfgVarList(CSOUND * csound,csCfgVariable_t ** lst)487 PUBLIC void csoundDeleteCfgVarList(CSOUND* csound, csCfgVariable_t **lst)
488 {
489     if (lst != NULL)
490       csound->Free(csound, lst);
491 }
492 
493 /* remove a configuration variable from 'db' */
494 
remove_entry_from_db(CSOUND * csound,CS_HASH_TABLE * db,const char * name)495 static int remove_entry_from_db(CSOUND* csound, CS_HASH_TABLE *db, const char *name)
496 {
497     csCfgVariable_t *pp = cs_hash_table_get(csound, db, (char*)name);
498 
499     if (UNLIKELY(pp == NULL))
500         return CSOUNDCFG_INVALID_NAME;
501 
502     csound->Free(csound, pp);
503     cs_hash_table_remove(csound, db, (char*)name);
504 
505     return CSOUNDCFG_SUCCESS;
506 }
507 
508 /**
509  * Remove the configuration variable of Csound instance 'csound' with the
510  * specified name from the database. Plugins need not call this, as all
511  * configuration variables are automatically deleted by csoundReset().
512  * Return value is CSOUNDCFG_SUCCESS in case of success, or
513  * CSOUNDCFG_INVALID_NAME if the variable was not found.
514  */
515 
csoundDeleteConfigurationVariable(CSOUND * csound,const char * name)516 PUBLIC int csoundDeleteConfigurationVariable(CSOUND *csound, const char *name)
517 {
518     return remove_entry_from_db(csound, csound->cfgVariableDB, name);
519 }
520 
destroy_entire_db(CSOUND * csound,CS_HASH_TABLE * db)521 static int destroy_entire_db(CSOUND *csound, CS_HASH_TABLE *db)
522 {
523     CONS_CELL *head, *current;
524     if (db == NULL)
525       return CSOUNDCFG_SUCCESS;
526 
527     head = current = cs_hash_table_values(csound, db);
528 
529     while (current != NULL) {
530         if (current->value != NULL) {
531              csound->Free(csound, current->value);
532         }
533         current = current->next;
534     }
535 
536     cs_cons_free(csound, head);
537     cs_hash_table_free(csound, db);
538 
539     return CSOUNDCFG_SUCCESS;
540 }
541 
542 /**
543  * Remove all configuration variables of Csound instance 'csound'
544  * and free database. This function is called by csoundReset().
545  * Return value is CSOUNDCFG_SUCCESS in case of success.
546  */
547 
csoundDeleteAllConfigurationVariables(CSOUND * csound)548 int csoundDeleteAllConfigurationVariables(CSOUND *csound)
549 {
550     int retval;
551     retval = destroy_entire_db(csound, csound->cfgVariableDB);
552     csound->cfgVariableDB = NULL;
553     return retval;
554 }
555 
556 /**
557  * Returns pointer to an error string constant for the specified
558  * CSOUNDCFG error code. The string is not translated.
559  */
560 
csoundCfgErrorCodeToString(int errcode)561 PUBLIC const char *csoundCfgErrorCodeToString(int errcode)
562 {
563     if (errcode > 0 || errcode < CSOUNDCFG_LASTERROR)
564       return errmsg_list[1 - CSOUNDCFG_LASTERROR];      /* unknown */
565     else
566       return errmsg_list[(-errcode)];
567 }
568