1 /*
2     namedins.c:
3 
4     Copyright (C) 2002, 2005, 2006 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 "csoundCore.h"
25 #include "namedins.h"
26 #include "csound_orc_semantics.h"
27 #include <ctype.h>
28 
29 /* check if the string s is a valid instrument or opcode name */
30 /* return value is zero if the string is not a valid name */
31 
check_instr_name(char * s)32 int check_instr_name(char *s)
33 {
34     char    *c = s;
35 
36     if (UNLIKELY(!*c)) return 0;  /* empty */
37     if (UNLIKELY(!isalpha(*c) &&
38                  *c != '_')) return 0;    /* chk if 1st char is valid */
39     while (*++c)
40       if (UNLIKELY(!isalnum(*c) && *c != '_')) return 0;
41     return 1;   /* no errors */
42 }
43 
44 
named_instr_find_in_engine(CSOUND * csound,char * s,ENGINE_STATE * engineState)45 int32  named_instr_find_in_engine(CSOUND *csound, char *s,
46                                   ENGINE_STATE *engineState) {
47 
48   INSTRNAME     *inm;
49     int ss = (*s=='-'?1:0);
50 
51     if (!engineState->instrumentNames)
52       return 0L;                              /* no named instruments defined */
53     /* now find instrument */
54     inm = cs_hash_table_get(csound, engineState->instrumentNames, s+ss);
55 
56     return (inm == NULL) ? 0L : (ss ? -inm->instno : inm->instno);
57 
58 }
59 /* find the instrument number for the specified name */
60 /* return value is zero if none was found */
61 
named_instr_find(CSOUND * csound,char * s)62 int32 named_instr_find(CSOUND *csound, char *s)
63 {
64   return named_instr_find_in_engine(csound, s, &csound->engineState);
65 }
66 
67 
68 /* convert opcode string argument to instrument number */
69 /* return value is -1 if the instrument cannot be found */
70 /* (in such cases, csoundInitError() is also called) */
strarg2insno(CSOUND * csound,void * p,int is_string)71 int32 strarg2insno(CSOUND *csound, void *p, int is_string)
72 {
73     int32    insno;
74 
75     if (is_string) {
76       if (UNLIKELY((insno = named_instr_find(csound, (char*) p)) <= 0)) {
77         csound->Message(csound, Str("WARNING: instr %s not found\n"), (char*) p);
78         return NOT_AN_INSTRUMENT;
79       }
80     }
81     else {      /* numbered instrument */
82       insno = (int32) *((MYFLT*) p);
83       if (UNLIKELY(insno < 1 || insno > csound->engineState.maxinsno ||
84                    !csound->engineState.instrtxtp[insno])) {
85         csound->Warning(csound, Str("Cannot Find Instrument %d"), (int) insno);
86         return csound->engineState.maxinsno;
87       }
88     }
89     return insno;
90 }
91 
92 /* same as strarg2insno, but runs at perf time, */
93 /* and does not support numbered instruments */
94 /* (used by opcodes like event or schedkwhen) */
strarg2insno_p(CSOUND * csound,char * s)95 int32 strarg2insno_p(CSOUND *csound, char *s)
96 {
97     int32    insno;
98 
99     if (UNLIKELY(!(insno = named_instr_find(csound, s)))) {
100       csound->ErrorMsg(csound, Str("instr %s not found"), s);
101       return NOT_AN_INSTRUMENT;
102     }
103     return insno;
104 }
105 
106 /* convert opcode string argument to instrument number */
107 /* (also allows user defined opcode names); if the integer */
108 /* argument is non-zero, only opcode names are searched */
109 /* return value is -1 if the instrument cannot be found */
110 /* (in such cases, csoundInitError() is also called) */
strarg2opcno(CSOUND * csound,void * p,int is_string,int force_opcode)111 int32 strarg2opcno(CSOUND *csound, void *p, int is_string, int force_opcode)
112 {
113     int32    insno = 0;
114 
115     if (!force_opcode) {        /* try instruments first, if enabled */
116       if (is_string) {
117         insno = named_instr_find(csound, (char*) p);
118       }
119       else {      /* numbered instrument */
120         insno = (int32) *((MYFLT*) p);
121         if (UNLIKELY(insno < 1 || insno > csound->engineState.maxinsno ||
122                      !csound->engineState.instrtxtp[insno])) {
123           csound->InitError(csound, Str("Cannot Find Instrument %d"), (int) insno);
124           return NOT_AN_INSTRUMENT;
125         }
126       }
127     }
128     if (!insno && is_string) {              /* if no instrument was found, */
129       OPCODINFO *inm = csound->opcodeInfo;  /* search for user opcode */
130       while (inm && sCmp(inm->name, (char*) p)) inm = inm->prv;
131       if (inm) insno = (int32) inm->instno;
132     }
133     if (UNLIKELY(insno < 1)) {
134       csound->InitError(csound,
135                         Str("cannot find the specified instrument or opcode"));
136       insno = NOT_AN_INSTRUMENT;
137     }
138     return insno;
139 }
140 
141 /* create file name from opcode argument (string or MYFLT)      */
142 /*   CSOUND *csound:                                            */
143 /*      pointer to Csound instance                              */
144 /*   char *s:                                                   */
145 /*      output buffer, should have enough space; if NULL, the   */
146 /*      required amount of memory is allocated and returned     */
147 /*   void *p:                                                   */
148 /*      opcode argument, is interpreted as char* or MYFLT*,     */
149 /*      depending on the 'is_string' parameter                  */
150 /*   const char *baseName:                                      */
151 /*      name prefix to be used if the 'p' argument is MYFLT,    */
152 /*      and it is neither SSTRCOD, nor a valid index to strset  */
153 /*      space.                                                  */
154 /*      For example, if "soundin." is passed as baseName, file  */
155 /*      names in the format "soundin.%d" will be generated.     */
156 /*      baseName may be an empty string, but should not be NULL */
157 /*   int is_string:                                             */
158 /*      if non-zero, 'p' is interpreted as a char* pointer and  */
159 /*      is used as the file name. Otherwise, it is expected to  */
160 /*      point to a MYFLT value, and the following are tried:    */
161 /*        1. if the value is SSTRCOD, the string argument of    */
162 /*           the current score event is used (string p-field)   */
163 /*        2. if the value, rounded to the nearest integer, is a */
164 /*           valid index to strset space, the strset string is  */
165 /*           used                                               */
166 /*        3. the file name is generated using baseName and the  */
167 /*           value rounded to the nearest integer, as described */
168 /*           above                                              */
169 /*   return value:                                              */
170 /*      pointer to the output string; if 's' is not NULL, it is */
171 /*      always the same as 's', otherwise it is allocated with  */
172 /*      csound->Malloc() and the caller is responsible for      */
173 /*      freeing the allocated memory with csound->Free() or     */
174 /*      csound->Free()                                          */
175 
strarg2name(CSOUND * csound,char * s,void * p,const char * baseName,int is_string)176 char *strarg2name(CSOUND *csound, char *s, void *p, const char *baseName,
177                                   int is_string)
178 {
179     if (is_string) {
180       /* opcode string argument */
181       if (s == NULL)
182         s = csound->Malloc(csound, strlen((char*) p) + 1);
183       strcpy(s, (char*) p);
184     }
185     else if (csound->ISSTRCOD(*((MYFLT*) p))) {
186       /* p-field string, unquote and copy */
187       char  *s2 = get_arg_string(csound, *((MYFLT*)p));
188       int   i = 0;
189       //printf("strarg2name: %g %s\n", *((MYFLT*)p), s2);
190       if (s == NULL)
191         s = csound->Malloc(csound, strlen(s2) + 1);
192       if (*s2 == '"')
193         s2++;
194       while (*s2 != '"' && *s2 != '\0')
195         s[i++] = *(s2++);
196       s[i] = '\0';
197     }
198     else {
199       int   i = (int) ((double) *((MYFLT*) p)
200                        + (*((MYFLT*) p) >= FL(0.0) ? 0.5 : -0.5));
201       if (i >= 0 && i <= (int) csound->strsmax &&
202           csound->strsets != NULL && csound->strsets[i] != NULL) {
203         if (s == NULL)
204           s = csound->Malloc(csound, strlen(csound->strsets[i]) + 1);
205         strcpy(s, csound->strsets[i]);
206       }
207       else {
208         int n;
209         if (s == NULL) {
210           /* allocate +20 characters, assuming sizeof(int) <= 8 */
211           s = csound->Malloc(csound, n = strlen(baseName) + 21);
212           snprintf(s, n, "%s%d", baseName, i);
213         }
214         else sprintf(s, "%s%d", baseName, i); /* dubious */
215       }
216     }
217     return s;
218 }
219 
220 /* ----------------------------------------------------------------------- */
221 /* the following functions are for efficient management of the opcode list */
222 
223 
224 
225 
226 
227 /* -------- IV - Jan 29 2005 -------- */
228 
229 
230 /**
231  * Allocate nbytes bytes of memory that can be accessed later by calling
232  * csoundQueryGlobalVariable() with the specified name; the space is
233  * cleared to zero.
234  * Returns CSOUND_SUCCESS on success, CSOUND_ERROR in case of invalid
235  * parameters (zero nbytes, invalid or already used name), or
236  * CSOUND_MEMORY if there is not enough memory.
237  */
csoundCreateGlobalVariable(CSOUND * csound,const char * name,size_t nbytes)238 PUBLIC int csoundCreateGlobalVariable(CSOUND *csound,
239                                       const char *name, size_t nbytes)
240 {
241     void* p;
242     /* create new empty database if it does not exist yet */
243     if (UNLIKELY(csound->namedGlobals == NULL)) {
244       csound->namedGlobals = cs_hash_table_create(csound);
245       if (UNLIKELY(csound->namedGlobals == NULL))
246         return CSOUND_MEMORY;
247     }
248     /* check for valid parameters */
249     if (UNLIKELY(name == NULL))
250       return CSOUND_ERROR;
251     if (UNLIKELY(name[0] == '\0'))
252       return CSOUND_ERROR;
253     if (UNLIKELY(nbytes < (size_t) 1 || nbytes >= (size_t) 0x7F000000L))
254       return CSOUND_ERROR;
255 
256     if (cs_hash_table_get(csound, csound->namedGlobals, (char*)name) != NULL)
257       return CSOUND_ERROR;
258 
259     p = csound->Calloc(csound, nbytes);
260     if (UNLIKELY(p == NULL))
261       return CSOUND_MEMORY;
262 
263     cs_hash_table_put(csound, csound->namedGlobals, (char*)name, p);
264     return CSOUND_SUCCESS;
265 }
266 
267 /**
268  * Get pointer to space allocated with the name "name".
269  * Returns NULL if the specified name is not defined.
270  */
csoundQueryGlobalVariable(CSOUND * csound,const char * name)271 PUBLIC void *csoundQueryGlobalVariable(CSOUND *csound, const char *name)
272 {
273     /* check if there is an actual database to search */
274     if (csound->namedGlobals == NULL) return NULL;
275 
276     /* check for a valid name */
277     if (UNLIKELY(name == NULL)) return NULL;
278     if (UNLIKELY(name[0] == '\0')) return NULL;
279 
280     return cs_hash_table_get(csound, csound->namedGlobals, (char*) name);
281 }
282 
283 /**
284  * This function is the same as csoundQueryGlobalVariable(), except the
285  * variable is assumed to exist and no error checking is done.
286  * Faster, but may crash or return an invalid pointer if 'name' is
287  * not defined.
288  */
csoundQueryGlobalVariableNoCheck(CSOUND * csound,const char * name)289 PUBLIC void *csoundQueryGlobalVariableNoCheck(CSOUND *csound, const char *name)
290 {
291     return cs_hash_table_get(csound, csound->namedGlobals, (char*) name);
292 }
293 
294 /**
295  * Free memory allocated for "name" and remove "name" from the database.
296  * Return value is CSOUND_SUCCESS on success, or CSOUND_ERROR if the name is
297  * not defined.
298  */
csoundDestroyGlobalVariable(CSOUND * csound,const char * name)299 PUBLIC int csoundDestroyGlobalVariable(CSOUND *csound, const char *name)
300 {
301     void *p = cs_hash_table_get(csound, csound->namedGlobals, (char*)name);
302     if (UNLIKELY(p == NULL))
303       return CSOUND_ERROR;
304 
305     csound->Free(csound, p);
306     cs_hash_table_remove(csound, csound->namedGlobals, (char*) name);
307 
308     return CSOUND_SUCCESS;
309 }
310 
311 /**
312  * Free entire global variable database. This function is for internal use
313  * only (e.g. by RESET routines).
314  */
csoundDeleteAllGlobalVariables(CSOUND * csound)315 void csoundDeleteAllGlobalVariables(CSOUND *csound)
316 {
317     if (csound == NULL || csound->namedGlobals == NULL) return;
318 
319     cs_hash_table_mfree_complete(csound, csound->namedGlobals);
320     csound->namedGlobals = NULL;
321 }
322