1 /*
2     envvar.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 "csoundCore.h"
25 #include "soundio.h"
26 #include "envvar.h"
27 #include <ctype.h>
28 #include <math.h>
29 
30 #if defined(MSVC)
31 #include <fcntl.h>
32 #endif
33 
34 #if defined(WIN32) && !defined(__CYGWIN__)
35 #  include <direct.h>
36 #  define getcwd(x,y) _getcwd(x,y)
37 #endif
38 
39 
40 #include "namedins.h"
41 
42 /* list of environment variables used by Csound */
43 
44 static const char *envVar_list[] = {
45     "CSNOSTOP",
46     "CSOUND6RC",
47     "CSSTRNGS",
48     "CS_LANG",
49     "HOME",
50     "INCDIR",
51     "OPCODE6DIR",
52     "OPCODE6DIR64",
53     "RAWWAVE_PATH",
54     "SADIR",
55     "SFDIR",
56     "SFOUTYP",
57     "SNAPDIR",
58     "SSDIR",
59     "MFDIR",
60     NULL
61 };
62 
63 typedef struct CSFILE_ {
64     struct CSFILE_  *nxt;
65     struct CSFILE_  *prv;
66     int             type;
67     int             fd;
68     FILE            *f;
69     SNDFILE         *sf;
70     void            *cb;
71     int             async_flag;
72     int             items;
73     int             pos;
74     MYFLT           *buf;
75     int             bufsize;
76     char            fullName[1];
77 } CSFILE;
78 
79 #if defined(MSVC)
80 #define RD_OPTS  _O_RDONLY | _O_BINARY
81 #define WR_OPTS  _O_TRUNC | _O_CREAT | _O_WRONLY | _O_BINARY,_S_IWRITE
82 #elif defined(WIN32)
83 #define RD_OPTS  O_RDONLY | O_BINARY
84 #define WR_OPTS  O_TRUNC | O_CREAT | O_WRONLY | O_BINARY, 0644
85 #elif defined DOSGCC
86 #define RD_OPTS  O_RDONLY | O_BINARY, 0
87 #define WR_OPTS  O_TRUNC | O_CREAT | O_WRONLY | O_BINARY, 0644
88 #else
89 #ifndef O_BINARY
90 # define O_BINARY (0)
91 #endif
92 #define RD_OPTS  O_RDONLY | O_BINARY, 0
93 #define WR_OPTS  O_TRUNC | O_CREAT | O_WRONLY | O_BINARY, 0644
94 #endif
95 
96 typedef struct searchPathCacheEntry_s {
97     char    *name;
98     struct searchPathCacheEntry_s   *nxt;
99     char    *lst[1];
100 } searchPathCacheEntry_t;
101 
102 typedef struct nameChain_s {
103     struct nameChain_s  *nxt;
104     char    s[1];
105 } nameChain_t;
106 
107 /* Space for 16 global environment variables, */
108 /* 32 bytes for name and 480 bytes for value. */
109 /* Only written by csoundSetGlobalEnv().      */
110 
111 static char globalEnvVars[8192] = { (char) 0 };
112 
113 #define globalEnvVarName(x)   ((char*) &(globalEnvVars[(int) (x) << 9]))
114 #define globalEnvVarValue(x)  ((char*) &(globalEnvVars[((int) (x) << 9) + 32]))
115 
is_valid_envvar_name(const char * name)116 static int is_valid_envvar_name(const char *name)
117 {
118     char *s;
119 
120     if (UNLIKELY(name == NULL || name[0] == '\0'))
121       return 0;
122     s = (char*) &(name[0]);
123     if (UNLIKELY(!(isalpha(*s) || *s == '_')))
124       return 0;
125     while (*(++s) != '\0') {
126       if (UNLIKELY(!(isalpha(*s) || isdigit(*s) || *s == '_')))
127         return 0;
128     }
129     return 1;
130 }
131 
132 /**
133  * Get pointer to value of environment variable 'name'.
134  * Return value is NULL if the variable is not set.
135  */
136 
csoundGetEnv(CSOUND * csound,const char * name)137 PUBLIC const char *csoundGetEnv(CSOUND *csound, const char *name)
138 {
139     if (csound == NULL) {
140       int i;
141       if (name == NULL || name[0] == '\0')
142         return (const char*) NULL;
143       for (i = 0; i < 16; i++) {
144         if (strcmp(globalEnvVarName(i), name) == 0)
145           return (const char*) globalEnvVarValue(i);
146       }
147       return (const char*) getenv(name);
148     }
149 
150     if (csound->envVarDB == NULL) return NULL;
151 
152     return (const char*) cs_hash_table_get(csound, csound->envVarDB, (char*)name);
153 }
154 
155 /**
156  * Set the global value of environment variable 'name' to 'value',
157  * or delete variable if 'value' is NULL.
158  * It is not safe to call this function while any Csound instances
159  * are active.
160  * Returns zero on success.
161  */
162 
csoundSetGlobalEnv(const char * name,const char * value)163 PUBLIC int csoundSetGlobalEnv(const char *name, const char *value)
164 {
165     int   i;
166 
167     if (UNLIKELY(name == NULL || name[0] == '\0' || (int) strlen(name) >= 32))
168       return -1;                        /* invalid name             */
169     for (i = 0; i < 16; i++) {
170       if ((value != NULL && globalEnvVarName(i)[0] == '\0') ||
171           strcmp(name, globalEnvVarName(i)) == 0)
172         break;
173     }
174     if (UNLIKELY(i >= 16))              /* not found / no free slot */
175       return -1;
176     if (value == NULL) {
177       globalEnvVarName(i)[0] = '\0';    /* delete existing variable */
178       return 0;
179     }
180     if (UNLIKELY(strlen(value) >= 480))
181       return -1;                        /* string value is too long */
182     strcpy(globalEnvVarName(i), name);
183     strcpy(globalEnvVarValue(i), value);
184     return 0;
185 }
186 
187 /**
188  * Set environment variable 'name' to 'value'.
189  * Returns CSOUND_SUCCESS on success, and CSOUND_ERROR or CSOUND_MEMORY
190  * if the environment variable could not be set for some reason.
191  */
192 
csoundSetEnv(CSOUND * csound,const char * name,const char * value)193 int csoundSetEnv(CSOUND *csound, const char *name, const char *value)
194 {
195     searchPathCacheEntry_t  *ep, *nxt;
196     char                    *oldValue;
197 
198     /* check for valid parameters */
199     if (UNLIKELY(csound == NULL || !is_valid_envvar_name(name)))
200       return CSOUND_ERROR;
201 
202     /* invalidate search path cache */
203     ep = (searchPathCacheEntry_t*) csound->searchPathCache;
204     while (ep != NULL) {
205       nxt = ep->nxt;
206       csound->Free(csound, ep);
207       ep = nxt;
208     }
209     csound->searchPathCache = NULL;
210 
211 
212     oldValue = cs_hash_table_get(csound, csound->envVarDB, (char*)name);
213     if (oldValue != NULL) {
214       csound->Free(csound, oldValue);
215     }
216 
217     cs_hash_table_put(csound, csound->envVarDB,
218                       (char*)name, cs_strdup(csound, (char*)value));
219 
220     /* print debugging info if requested */
221     if (UNLIKELY(csound->oparms->odebug)) {
222       csoundMessage(csound, Str("Environment variable '%s' has been set to "),
223                               name);
224       if (value == NULL)
225         csoundMessage(csound, "NULL\n");
226       else
227         csoundMessage(csound, "'%s'\n", value);
228     }
229     /* report success */
230     return CSOUND_SUCCESS;
231 }
232 
233 /**
234  * Append 'value' to environment variable 'name', using ENVSEP as
235  * separator character.
236  * Returns CSOUND_SUCCESS on success, and CSOUND_ERROR or CSOUND_MEMORY
237  * if the environment variable could not be set for some reason.
238  */
239 
csoundAppendEnv(CSOUND * csound,const char * name,const char * value)240 int csoundAppendEnv(CSOUND *csound, const char *name, const char *value)
241 {
242     const char  *oldval;
243     char        *newval;
244     int         retval;
245 
246     /* check for valid parameters */
247     if (UNLIKELY(csound == NULL || !is_valid_envvar_name(name)))
248       return CSOUND_ERROR;
249     /* get original value of variable */
250     oldval = csoundGetEnv(csound, name);
251     if (oldval == NULL)
252       return csoundSetEnv(csound, name, value);
253     if (value == NULL || value[0] == '\0')
254       return CSOUND_SUCCESS;
255     /* allocate new value (+ 2 bytes for ENVSEP and null character) */
256     newval = (char*) csound->Malloc(csound, (size_t) strlen(oldval)
257                              + (size_t) strlen(value) + (size_t) 2);
258     /* append to old value */
259     strcpy(newval, oldval);     /* These are safe as space calculated above */
260     //    printf("%d: newval = %s\n", __LINE__, newval);
261     // should be a better way
262     newval[strlen(oldval)]= ENVSEP;
263     newval[strlen(oldval)+1]= '\0';
264     //    printf("%d: newval = %s\n", __LINE__, newval);
265     strcat(newval, value);
266     //    printf("%d: newval = %s\n", __LINE__, newval);
267     /* set variable */
268     retval = csoundSetEnv(csound, name, newval);
269     csound->Free(csound, newval);
270     /* return with error code */
271     return retval;
272 }
273 
274 /**
275  * Prepend 'value' to environment variable 'name', using ENVSEP as
276  * separator character.
277  * Returns CSOUND_SUCCESS on success, and CSOUND_ERROR or CSOUND_MEMORY
278  * if the environment variable could not be set for some reason.
279  */
280 
csoundPrependEnv(CSOUND * csound,const char * name,const char * value)281 int csoundPrependEnv(CSOUND *csound, const char *name, const char *value)
282 {
283     const char  *oldval;
284     char        *newval;
285     int         retval;
286 
287     /* check for valid parameters */
288     if (UNLIKELY(csound == NULL || !is_valid_envvar_name(name)))
289       return CSOUND_ERROR;
290     /* get original value of variable */
291     oldval = csoundGetEnv(csound, name);
292     if (oldval == NULL)
293       return csoundSetEnv(csound, name, value);
294     if (value == NULL || value[0] == '\0')
295       return CSOUND_SUCCESS;
296     /* allocate new value (+ 2 bytes for ';' and null character) */
297     newval = (char*) csound->Malloc(csound, (size_t) strlen(oldval)
298                                      + (size_t) strlen(value) + (size_t) 2);
299     /* prepend to old value */
300     strcpy(newval, value);
301     //    printf("%d: newval = %s\n", __LINE__,  newval);
302     newval[strlen(value)]= ENVSEP;
303     newval[strlen(value)+1]= '\0';
304     //    printf("%d: newval = %s\n", __LINE__,  newval);
305     strcat(newval, oldval);
306     //    printf("%d: newval = %s\n", __LINE__,  newval);
307     /* set variable */
308     retval = csoundSetEnv(csound, name, newval);
309     csound->Free(csound, newval);
310     /* return with error code */
311     return retval;
312 }
313 
314 /**
315  * Initialise environment variable database, and copy system
316  * environment variables.
317  * Returns CSOUND_SUCCESS on success, and CSOUND_ERROR or
318  * CSOUND_MEMORY in case of an error.
319  */
320 
csoundInitEnv(CSOUND * csound)321 int csoundInitEnv(CSOUND *csound)
322 {
323     int i, retval;
324     /* check if already initialised */
325     if (csound->envVarDB != NULL)
326       return CSOUND_SUCCESS;
327     /* allocate table */
328     csound->envVarDB = cs_hash_table_create(csound);
329     /* copy standard Csound environment variables */
330     for (i = 0; envVar_list[i] != NULL; i++) {
331       const char  *name = envVar_list[i];
332       const char  *value = getenv(name);
333       if (value != NULL) {
334         retval = csoundSetEnv(csound, name, value);
335         if (retval != CSOUND_SUCCESS)
336           return retval;
337       }
338     }
339     /* copy any global defaults set with csoundSetGlobalEnv() */
340     for (i = 0; i < 16; i++) {
341       if (globalEnvVarName(i)[0] != '\0') {
342         retval = csoundSetEnv(csound, globalEnvVarName(i),
343                                       globalEnvVarValue(i));
344         if (retval != CSOUND_SUCCESS)
345           return retval;
346       }
347     }
348     /* done */
349     return CSOUND_SUCCESS;
350 }
351 
352 /**
353  * Parse 's' as an assignment to environment variable, in the format
354  * "NAME=VALUE" for replacing the previous value, or "NAME+=VALUE"
355  * for appending.
356  * Returns CSOUND_SUCCESS on success, and CSOUND_ERROR or
357  * CSOUND_MEMORY in case of an error.
358  */
359 
csoundParseEnv(CSOUND * csound,const char * s)360 int csoundParseEnv(CSOUND *csound, const char *s)
361 {
362     char  *name, *value, msg[256];
363     int   append_mode, retval;
364 
365     /* copy string constant */
366     name = (char*) csound->Malloc(csound, (size_t) strlen(s) + (size_t) 1);
367     strcpy(name, s);
368     /* check assignment format */
369     value = strchr(name, '=');
370     append_mode = 0;
371     if (UNLIKELY(value == NULL || value == name)) {
372       strNcpy(msg, Str(" *** invalid format for --env\n"), 256);
373       retval = CSOUND_ERROR;
374       goto err_return;
375     }
376     *(value++) = '\0';
377     if (*(value - 2) == '+') {
378       append_mode = 1;
379       *(value - 2) = '\0';
380     }
381     if (UNLIKELY(!is_valid_envvar_name(name))) {
382       strNcpy(msg, Str(" *** invalid environment variable name\n"), 256);
383       retval = CSOUND_ERROR;
384       goto err_return;
385     }
386     /* set variable */
387     if (!append_mode)
388       retval = csoundSetEnv(csound, name, value);
389     else
390       retval = csoundAppendEnv(csound, name, value);
391     if (UNLIKELY(retval == CSOUND_MEMORY))
392       strNcpy(msg, Str(" *** memory allocation failure\n"), 256);
393     else
394       strNcpy(msg, Str(" *** error setting environment variable\n"), 256);
395 
396  err_return:
397     if (UNLIKELY(retval != CSOUND_SUCCESS))
398       csoundMessage(csound, "%s", msg);
399     csound->Free(csound, name);
400     return retval;
401 }
402 
csoundGetSearchPathFromEnv(CSOUND * csound,const char * envList)403 char **csoundGetSearchPathFromEnv(CSOUND *csound, const char *envList)
404 {
405     searchPathCacheEntry_t  *p;
406     nameChain_t             *env_lst = NULL, *path_lst = NULL, *tmp, *prv, *nxt;
407     char                    *s;
408     int                     i, j, k, len, pathCnt = 0, totLen = 0;
409 
410     /* check if the specified environment variable list was already parsed */
411     p = (searchPathCacheEntry_t*) csound->searchPathCache;
412     while (p != NULL) {
413       if (sCmp(p->name, envList) == 0)
414         return (&(p->lst[0]));
415       p = p->nxt;
416     }
417     /* not found, need to create new entry */
418     len = (int) strlen(envList);
419     /* split environment variable list to tokens */
420     for (i = j = 0; i <= len; i++) {
421       if (envList[i] == ';' || envList[i] == ':' || envList[i] == '\0') {
422         if (i > j) {
423           tmp = (nameChain_t*)csound->Malloc(csound, sizeof(nameChain_t) + (i-j));
424           for (k = 0; j < i; j++, k++)
425             tmp->s[k] = envList[j];
426           tmp->s[k] = '\0';
427           tmp->nxt = NULL;
428           if (env_lst != NULL) {
429             /* search for duplicate entry */
430             prv = nxt = env_lst;
431             do {
432               if (sCmp(env_lst->s, tmp->s) == 0)
433                 break;
434               prv = nxt;
435             } while ((nxt = prv->nxt) != NULL);
436             if (nxt == NULL)
437               prv->nxt = tmp;
438             else
439               csound->Free(csound, tmp);       /* and remove if there is any */
440           }
441           else
442             env_lst = tmp;
443         }
444         j = i + 1;
445       }
446     }
447     /* expand environment variables to path list */
448     while (env_lst != NULL) {
449       nxt = env_lst->nxt;
450       s = (char*) csoundGetEnv(csound, env_lst->s);
451       csound->Free(csound, env_lst);
452       env_lst = nxt;
453       if (s != NULL && s[0] != '\0')
454         len = (int) strlen(s);
455       else
456         len = -1;
457       // **** THIS CODE DOES NOT CHECK FOR WINDOWS STYLE C:\foo ****
458       for (i = j = 0; i <= len; i++) {
459         if (i==0 && isalpha(s[i]) && s[i+1]==':') i++;
460         else if (s[i] == ';' || s[i] == ':' || s[i] == '\0') {
461           if (i > j) {
462             tmp = (nameChain_t*) csound->Malloc(csound, sizeof(nameChain_t)
463                                                  + (i - j) + 1);
464             /* copy with converting pathname delimiters */
465             /* FIXME: should call csoundConvertPathname instead */
466             for (k = 0; j < i; j++, k++)
467               tmp->s[k] = (s[j] == '/' || s[j] == '\\' ? DIRSEP : s[j]);
468             while (tmp->s[--k] == DIRSEP);
469             tmp->s[++k] = DIRSEP;
470             tmp->s[++k] = '\0';
471             tmp->nxt = path_lst;
472             path_lst = tmp;
473             /* search for duplicate entry */
474             for (prv = tmp; (tmp = tmp->nxt) != NULL; prv = tmp)
475               if (sCmp(path_lst->s, tmp->s) == 0)
476                 break;
477             if (tmp != NULL) {
478               /* and remove if there is any */
479               prv->nxt = tmp->nxt;
480               csound->Free(csound, tmp);
481             }
482             else {
483               /* calculate storage requirement */
484               pathCnt++; totLen += (k + 1);
485             }
486           }
487           j = i + 1;
488           if (i+2<=len && s[i+2]==':' && isalpha(s[i+1])) i+=2;
489         }
490       }
491     }
492     totLen += ((int) strlen(envList) + 1);
493     /* create path cache entry */
494     p = (searchPathCacheEntry_t*) csound->Malloc(csound,
495                                                  sizeof(searchPathCacheEntry_t)
496                                                  + sizeof(char*) * pathCnt
497                                                  + sizeof(char) * totLen);
498     s = (char*) &(p->lst[pathCnt + 1]);
499     p->name = s;
500     strcpy(p->name, envList);
501     s += ((int) strlen(envList) + 1);
502     p->nxt = (searchPathCacheEntry_t*) csound->searchPathCache;
503     if (UNLIKELY(csound->oparms->odebug))
504       csound->DebugMsg(csound, Str("Creating search path cache for '%s':"),
505                                p->name);
506     for (i = 0; (i < pathCnt) && (path_lst != NULL); i++) {
507       p->lst[i] = s;
508       strcpy(s, path_lst->s);
509       s += ((int) strlen(path_lst->s) + 1);
510       nxt = path_lst->nxt;
511       csound->Free(csound, path_lst);
512       path_lst = nxt;
513       if (UNLIKELY(csound->oparms->odebug))
514         csound->DebugMsg(csound, "%5d: \"%s\"", (i + 1), p->lst[i]);
515     }
516     p->lst[i] = NULL;
517     /* link into database */
518     csound->searchPathCache = (void*) p;
519     /* return with pathname list */
520     return (&(p->lst[0]));
521 }
522 
523 /** Check if file name is valid, and copy with converting pathname delimiters */
csoundConvertPathname(CSOUND * csound,const char * filename)524 char *csoundConvertPathname(CSOUND *csound, const char *filename)
525 {
526     char  *name;
527     int   i = 0;
528 
529 /* FIXMEs:  need to convert ':' from Mac pathnames (but be careful of not
530    messing up Windows drive names!); need to be careful of
531    relative paths containing "./", "../", or multiple colons "::"; need to
532    collapse multiple slashes "//" or "\\\\" ??  */
533     if (filename == NULL || filename[0] == '\0')
534       return NULL;
535     name = (char*) csound->Malloc(csound, (size_t) strlen(filename) + (size_t) 1);
536     do {
537       if (filename[i] != '/' && filename[i] != '\\')
538         name[i] = filename[i];
539       else
540         name[i] = DIRSEP;
541     } while (filename[i++] != '\0');
542     if (name[i - 2] == DIRSEP
543 #ifdef WIN32
544         || (isalpha(name[0]) && name[1] == ':' && name[2] == '\0')
545 #endif
546         ) {
547       csound->Free(csound, name);
548       return NULL;
549     }
550     return name;
551 }
552 
553 /**  Check if name is a full pathname for the platform we are running on. */
csoundIsNameFullpath(const char * name)554 int csoundIsNameFullpath(const char *name)
555 {
556 #ifdef WIN32
557     if (isalpha(name[0]) && name[1] == ':') return 1;
558 #endif
559     if (name[0] == DIRSEP) /* ||
560         (name[0] == '.' && (name[1] == DIRSEP ||
561                             (name[1] == '.' && name[2] == DIRSEP)))) */
562       return 1;
563     return 0;
564 }
565 
566 /** Check if name is a relative pathname for this platform.  Bare
567  *  filenames with no path information are not counted.
568  */
csoundIsNameRelativePath(const char * name)569 int csoundIsNameRelativePath(const char *name)
570 {
571     if (name[0] != DIRSEP && strchr(name, DIRSEP) != NULL)
572       return 1;
573     return 0;
574 }
575 
576 /** Check if name is a "leaf" (bare) filename for this platform. */
csoundIsNameJustFilename(const char * name)577 int csoundIsNameJustFilename(const char *name)
578 {
579     if (strchr(name, DIRSEP) != NULL) return 0;
580 #ifdef WIN32
581     if (name[2] == ':') return 0;
582 #endif
583     return 1;
584 }
585 
586 /** Properly concatenates the full or relative pathname in path1 with
587  *  the relative pathname or filename in path2 according to the rules
588  *  for the platform we are running on.  path1 is assumed to be
589  *  a directory whether it ends with DIRSEP or not.  Relative paths must
590  *  conform to the conventions for the current platform (begin with ':'
591  *  on MacOS 9 and not begin with DIRSEP on others).
592  */
csoundConcatenatePaths(CSOUND * csound,const char * path1,const char * path2)593 char* csoundConcatenatePaths(CSOUND* csound, const char *path1,
594                              const char *path2)
595 {
596     char *result;
597     const char *start2;
598     char separator[2];
599     int  len1 = strlen(path1);
600     int  len2 = strlen(path2);
601 
602     /* cannot join two full pathnames -- so just return path2 ? */
603     if (csoundIsNameFullpath(path2)) {
604         result = (char*) csound->Malloc(csound, (size_t)len2+1);
605         strcpy(result, path2);
606         return result;
607     }
608 
609     start2 = path2;
610     /* ignore "./" at the beginning */
611     if (path2[0] == '.' && path2[1] == DIRSEP) start2 = path2 + 2;
612 
613     result = (char*) csound->Malloc(csound, (size_t)len1+(size_t)len2+2);
614     strcpy(result, path1);
615     /* check for final DIRSEP in path1 */
616     if (path1[len1-1] != DIRSEP) {
617         separator[0] = DIRSEP; separator[1] = '\0';
618         strcat(result, separator);
619     }
620     strcat(result, start2);
621 
622     return result;
623 }
624 
625 /** Converts a pathname to native format and returns just the part of
626  *  the path that specifies the directory.  Does not return the final
627  *  DIRSEP.  Returns an empty string if no path components occur before
628  *  the filename.  Returns NULL if unable to carry out the operation
629  *  for some reason.
630  */
csoundSplitDirectoryFromPath(CSOUND * csound,const char * path)631 char *csoundSplitDirectoryFromPath(CSOUND* csound, const char * path)
632 {
633     char *convPath;
634     char *lastIndex;
635     char *partialPath;
636     int  len;
637 
638     if ((convPath = csoundConvertPathname(csound, path)) == NULL)
639         return NULL;
640     lastIndex = strrchr(convPath, DIRSEP);
641 
642     if (lastIndex == NULL) {  /* no DIRSEP before filename */
643 #ifdef WIN32  /* e.g. C:filename */
644         if (isalpha(convPath[0]) && convPath[1] == ':') {
645             partialPath = (char*) csound->Malloc(csound, (size_t) 3);
646             partialPath[0] = convPath[0];
647             partialPath[1] = convPath[1];
648             partialPath[2] = '\0';
649             csound->Free(csound, convPath);
650             return partialPath;
651         }
652 #endif
653         partialPath = (char*) csound->Malloc(csound, (size_t) 1);
654         partialPath[0] = '\0';
655     }
656     else {
657         len = lastIndex - convPath;
658         partialPath = (char*) csound->Malloc(csound, len+1);
659         strNcpy(partialPath, convPath, len+1);
660         //partialPath[len] = '\0';
661    }
662    csound->Free(csound, convPath);
663    return partialPath;
664 }
665 
666 /** Return just the final component of a full path */
csoundSplitFilenameFromPath(CSOUND * csound,const char * path)667 char *csoundSplitFilenameFromPath(CSOUND* csound, const char * path)
668 {
669     char *convPath;
670     char *lastIndex;
671     char *filename;
672     int  len;
673 
674     if ((convPath = csoundConvertPathname(csound, path)) == NULL)
675       return NULL;
676     lastIndex = strrchr(convPath, DIRSEP);
677     len = strlen(lastIndex);
678     filename = (char*) csound->Malloc(csound, len+1);
679     strcpy(filename, lastIndex+1);
680     csound->Free(csound, convPath);
681     return filename;
682 }
683 
684 /* given a file name as string, return full path of directory of file;
685  * Note: does not check if file exists
686  */
csoundGetDirectoryForPath(CSOUND * csound,const char * path)687 char *csoundGetDirectoryForPath(CSOUND* csound, const char * path) {
688     char *partialPath, *tempPath, *lastIndex;
689     char *retval;
690     char *cwd;
691     int  len;
692 
693     if (path == NULL) return NULL;
694 
695     tempPath = csoundConvertPathname(csound, path);
696     if (tempPath == NULL) return NULL;
697     lastIndex = strrchr(tempPath, DIRSEP);
698 
699     if (tempPath && csoundIsNameFullpath(tempPath)) {
700       /* check if root directory */
701       if (lastIndex == tempPath) {
702         partialPath = (char *)csound->Malloc(csound, 2);
703         partialPath[0] = DIRSEP;
704         partialPath[1] = '\0';
705 
706         csound->Free(csound, tempPath);
707 
708         return partialPath;
709       }
710 
711 #  ifdef WIN32
712       /* check if root directory of Windows drive */
713       if ((lastIndex - tempPath) == 2 && tempPath[1] == ':') {
714         partialPath = (char *)csound->Malloc(csound, 4);
715         partialPath[0] = tempPath[0];
716         partialPath[1] = tempPath[1];
717         partialPath[2] = tempPath[2];
718         partialPath[3] = '\0';
719 
720         csound->Free(csound, tempPath);
721 
722         return partialPath;
723       }
724 #  endif
725       len = (lastIndex - tempPath);
726 
727       partialPath = (char *)csound->Calloc(csound, len + 1);
728       strNcpy(partialPath, tempPath, len+1);
729 
730       csound->Free(csound, tempPath);
731 
732       return partialPath;
733     }
734 
735     /* do we need to worry about ~/ on *nix systems ? */
736     /* we have a relative path or just a filename */
737     len = 32;
738     cwd = csound->Malloc(csound, len);
739  again:
740     if (UNLIKELY(getcwd(cwd, len)==NULL)) {
741       // Should check ERANGE==errno
742       //csoundDie(csound, Str("Current directory path name too long\n"));
743       len =len+len; cwd = csound->ReAlloc(csound, cwd, len);
744       if (UNLIKELY(len>1024*1024))
745         csoundDie(csound, Str("Current directory path name too long\n"));
746       goto again;
747     }
748 
749     if (lastIndex == NULL) {
750       return cwd;
751     }
752 
753     len = (lastIndex - tempPath);  /* could be 0 on OS 9 */
754 
755     partialPath = (char *)csound->Calloc(csound, len + 1);
756     strNcpy(partialPath, tempPath, len+1);
757 
758     retval = csoundConcatenatePaths(csound, cwd, partialPath);
759 
760     csound->Free(csound, cwd);
761     csound->Free(csound, partialPath);
762     csound->Free(csound, tempPath);
763 
764     return retval;
765 }
766 
csoundFindFile_Std(CSOUND * csound,char ** fullName,const char * filename,const char * mode,const char * envList)767 static FILE *csoundFindFile_Std(CSOUND *csound, char **fullName,
768                                 const char *filename, const char *mode,
769                                 const char *envList)
770 {
771     FILE  *f;
772     char  *name, *name2, **searchPath;
773 
774     *fullName = NULL;
775     if ((name = csoundConvertPathname(csound, filename)) == NULL)
776       return (FILE*) NULL;
777     if (mode[0] != 'w') {
778       /* read: try the specified name first */
779       f = fopen(name, mode);
780       if (f != NULL) {
781         *fullName = name;
782         return f;
783       }
784       /* if full path, and not found: */
785       if (csoundIsNameFullpath(name)) {
786         csound->Free(csound, name);
787         return (FILE*) NULL;
788       }
789     }
790     else if (csoundIsNameFullpath(name)) {
791       /* if write and full path: */
792       f = fopen(name, mode);
793       if (f != NULL)
794         *fullName = name;
795       else
796         csound->Free(csound, name);
797       return f;
798     }
799     /* search paths defined by environment variable list */
800     if (envList != NULL && envList[0] != '\0' &&
801         (searchPath = csoundGetSearchPathFromEnv((CSOUND*) csound, envList))
802         != NULL) {
803       //len = (int) strlen(name) + 1;
804       while (*searchPath != NULL) {
805         name2 = csoundConcatenatePaths(csound, *searchPath, name);
806         f = fopen(name2, mode);
807         if (f != NULL) {
808           csound->Free(csound, name);
809           *fullName = name2;
810           return f;
811         }
812         csound->Free(csound, name2);
813         searchPath++;
814       }
815     }
816     /* if write mode, try current directory last */
817     if (mode[0] == 'w') {
818       f = fopen(name, mode);
819       if (f != NULL) {
820         *fullName = name;
821         return f;
822       }
823     }
824     /* not found */
825     csound->Free(csound, name);
826     return (FILE*) NULL;
827 }
828 
csoundFindFile_Fd(CSOUND * csound,char ** fullName,const char * filename,int write_mode,const char * envList)829 static int csoundFindFile_Fd(CSOUND *csound, char **fullName,
830                              const char *filename, int write_mode,
831                              const char *envList)
832 {
833     char  *name, *name2, **searchPath;
834     int   fd;
835 
836     *fullName = NULL;
837     if ((name = csoundConvertPathname(csound, filename)) == NULL)
838       return -1;
839     if (!write_mode) {
840       /* read: try the specified name first */
841       fd = open(name, RD_OPTS);
842       if (fd >= 0) {
843         *fullName = name;
844         return fd;
845       }
846       /* if full path, and not found: */
847       if (csoundIsNameFullpath(name)) {
848         csound->Free(csound, name);
849         return -1;
850       }
851     }
852     else if (csoundIsNameFullpath(name)) {
853       /* if write and full path: */
854       fd = open(name, WR_OPTS);
855       if (fd >= 0)
856         *fullName = name;
857       else
858         csound->Free(csound, name);
859       return fd;
860     }
861     /* search paths defined by environment variable list */
862     if (envList != NULL && envList[0] != '\0' &&
863         (searchPath = csoundGetSearchPathFromEnv((CSOUND*) csound, envList))
864         != NULL) {
865       //len = (int) strlen(name) + 1;
866       while (*searchPath != NULL) {
867         name2 = csoundConcatenatePaths(csound, *searchPath, name);
868         if (!write_mode)
869           fd = open(name2, RD_OPTS);
870         else
871           fd = open(name2, WR_OPTS);
872         if (fd >= 0) {
873           csound->Free(csound, name);
874           *fullName = name2;
875           return fd;
876         }
877         csound->Free(csound, name2);
878         searchPath++;
879       }
880     }
881     /* if write mode, try current directory last */
882     if (write_mode) {
883       fd = open(name, WR_OPTS);
884       if (fd >= 0) {
885         *fullName = name;
886         return fd;
887       }
888     }
889     /* not found */
890     csound->Free(csound, name);
891     return -1;
892 }
893 
894 /**
895  * Search for input file 'filename'.
896  * If the file name specifies full path (it begins with '.', the pathname
897  * delimiter character, or a drive letter and ':' on Windows), that exact
898  * file name is tried without searching.
899  * Otherwise, the file is searched relative to the current directory first,
900  * and if it is still not found, a pathname list that is created the
901  * following way is searched:
902  *   1. if envList is NULL or empty, no directories are searched
903  *   2. envList is parsed as a ';' or ':' separated list of environment
904  *      variable names, and all environment variables are expanded and
905  *      expected to contain a ';' or ':' separated list of directory names
906  *   2. all directories in the resulting pathname list are searched, starting
907  *      from the last and towards the first one, and the directory where the
908  *      file is found first will be used
909  * The function returns a pointer to the full name of the file if it is
910  * found, and NULL if the file could not be found in any of the search paths,
911  * or an error has occured. The caller is responsible for freeing the memory
912  * pointed to by the return value, by calling csound->Free().
913  */
csoundFindInputFile(CSOUND * csound,const char * filename,const char * envList)914 char *csoundFindInputFile(CSOUND *csound,
915                           const char *filename, const char *envList)
916 {
917     char  *name_found;
918     int   fd;
919 
920     if (csound == NULL)
921       return NULL;
922     fd = csoundFindFile_Fd(csound, &name_found, filename, 0, envList);
923     if (fd >= 0)
924       close(fd);
925     return name_found;
926 }
927 
928 /**
929  * Search for a location to write file 'filename'.
930  * If the file name specifies full path (it begins with '.', the pathname
931  * delimiter character, or a drive letter and ':' on Windows), that exact
932  * file name is tried without searching.
933  * Otherwise, a pathname list that is created the following way is searched:
934  *   1. if envList is NULL or empty, no directories are searched
935  *   2. envList is parsed as a ';' separated list of environment variable
936  *      names, and all environment variables are expanded and expected to
937  *      contain a ';' separated list of directory names
938  *   2. all directories in the resulting pathname list are searched, starting
939  *      from the last and towards the first one, and the directory that is
940  *      found first where the file can be written to will be used
941  * Finally, if the file cannot be written to any of the directories in the
942  * search paths, writing relative to the current directory is tried.
943  * The function returns a pointer to the full name of the file if a location
944  * suitable for writing the file is found, and NULL if the file cannot not be
945  * written anywhere in the search paths, or an error has occured.
946  * The caller is responsible for freeing the memory pointed to by the return
947  * value, by calling csound->Free().
948  */
csoundFindOutputFile(CSOUND * csound,const char * filename,const char * envList)949 char *csoundFindOutputFile(CSOUND *csound,
950                            const char *filename, const char *envList)
951 {
952     char  *name_found;
953     int   fd;
954 
955     if (csound == NULL)
956       return NULL;
957     fd = csoundFindFile_Fd(csound, &name_found, filename, 1, envList);
958     if (fd >= 0) {
959       close(fd);
960       if (remove(name_found)<0) csound->DebugMsg(csound, Str("Remove failed\n"));
961     }
962     return name_found;
963 }
964 
965 /**
966  * Open a file and return handle.
967  *
968  * CSOUND *csound:
969  *   Csound instance pointer
970  * void *fd:
971  *   pointer a variable of type int, FILE*, or SNDFILE*, depending on 'type',
972  *   for storing handle to be passed to file read/write functions
973  * int type:
974  *   file type, one of the following:
975  *     CSFILE_FD_R:     read file using low level interface (open())
976  *     CSFILE_FD_W:     write file using low level interface (open())
977  *     CSFILE_STD:      use ANSI C interface (fopen())
978  *     CSFILE_SND_R:    read sound file
979  *     CSFILE_SND_W:    write sound file
980  * const char *name:
981  *   file name
982  * void *param:
983  *   parameters, depending on type:
984  *     CSFILE_FD_R:     unused (should be NULL)
985  *     CSFILE_FD_W:     unused (should be NULL)
986  *     CSFILE_STD:      mode parameter (of type char*) to be passed to fopen()
987  *     CSFILE_SND_R:    SF_INFO* parameter for sf_open(), with defaults for
988  *                      raw file; the actual format paramaters of the opened
989  *                      file will be stored in this structure
990  *     CSFILE_SND_W:    SF_INFO* parameter for sf_open(), output file format
991  * const char *env:
992  *   list of environment variables for search path (see csoundFindInputFile()
993  *   for details); if NULL, the specified name is used as it is, without any
994  *   conversion or search.
995  * int csFileType:
996  *   A value from the enumeration CSOUND_FILETYPES (see soundCore.h)
997  * int isTemporary:
998  *   1 if this file will be deleted when Csound is finished.
999  *   Otherwise, 0.
1000  * return value:
1001  *   opaque handle to the opened file, for use with csoundGetFileName() or
1002  *   csoundFileClose(), or storing in FDCH.fd.
1003  *   On failure, NULL is returned.
1004  */
1005 
csoundFileOpenWithType(CSOUND * csound,void * fd,int type,const char * name,void * param,const char * env,int csFileType,int isTemporary)1006 void *csoundFileOpenWithType(CSOUND *csound, void *fd, int type,
1007                              const char *name, void *param, const char *env,
1008                              int csFileType, int isTemporary)
1009 {
1010     CSFILE  *p = NULL;
1011     char    *fullName = NULL;
1012     FILE    *tmp_f = NULL;
1013     SF_INFO sfinfo;
1014     int     tmp_fd = -1, nbytes = (int) sizeof(CSFILE);
1015 
1016 
1017     /* check file type */
1018     if (UNLIKELY((unsigned int) (type - 1) >= (unsigned int) CSFILE_SND_W)) {
1019       csoundErrorMsg(csound, Str("internal error: csoundFileOpen(): "
1020                                  "invalid type: %d"), type);
1021       return NULL;
1022     }
1023     /* get full name and open file */
1024     if (env == NULL) {
1025 #if defined(WIN32)
1026       /* To handle Widows errors in file name characters. */
1027       size_t sz = 2 * MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
1028       wchar_t *wfname = alloca(sz);
1029       wchar_t *wmode = 0;
1030 
1031       MultiByteToWideChar(CP_UTF8, 0, name, -1, wfname, sz);
1032       sz = 2 * MultiByteToWideChar(CP_UTF8, 0, param, -1, NULL, 0);
1033       wmode = alloca(sz);
1034       MultiByteToWideChar(CP_UTF8, 0, param, -1, wmode, sz);
1035       if (type == CSFILE_STD) {
1036         tmp_f = _wfopen(wfname, wmode);
1037         if (UNLIKELY(tmp_f == NULL)) {
1038           /* csoundErrorMsg(csound, Str("csound->FileOpen2(\"%s\") failed: %s."), */
1039           /*                name, strerror(errno)); */
1040           goto err_return;
1041         }
1042         fullName = (char*) name;
1043       }
1044 #else
1045       if (type == CSFILE_STD) {
1046         fullName = (char*) name;
1047         tmp_f = fopen(fullName, (char*) param);
1048         if (UNLIKELY(tmp_f == NULL)) {
1049           /* csoundErrorMsg(csound, Str("csound->FileOpen2(\"%s\") failed: %s."), */
1050           /*                name, strerror(errno)); */
1051           goto err_return;
1052         }
1053       }
1054 #endif
1055       else {
1056         fullName = (char*) name;
1057         if (type == CSFILE_SND_R || type == CSFILE_FD_R)
1058           tmp_fd = open(fullName, RD_OPTS);
1059         else
1060           tmp_fd = open(fullName, WR_OPTS);
1061         if (tmp_fd < 0)
1062           goto err_return;
1063       }
1064     }
1065     else {
1066       if (type == CSFILE_STD) {
1067         tmp_f = csoundFindFile_Std(csound, &fullName, name, (char*) param, env);
1068         if (UNLIKELY(tmp_f == NULL))
1069           goto err_return;
1070       }
1071       else {
1072         if (type == CSFILE_SND_R || type == CSFILE_FD_R)
1073           tmp_fd = csoundFindFile_Fd(csound, &fullName, name, 0, env);
1074         else
1075           tmp_fd = csoundFindFile_Fd(csound, &fullName, name, 1, env);
1076         if (UNLIKELY(tmp_fd < 0))
1077           goto err_return;
1078       }
1079     }
1080     nbytes += (int) strlen(fullName);
1081     /* allocate file structure */
1082     p = (CSFILE*) csound->Malloc(csound, (size_t) nbytes);
1083     if (UNLIKELY(p == NULL))
1084       goto err_return;
1085     p->nxt = (CSFILE*) csound->open_files;
1086     p->prv = (CSFILE*) NULL;
1087     p->type = type;
1088     p->fd = tmp_fd;
1089     p->f = tmp_f;
1090     p->sf = (SNDFILE*) NULL;
1091     strcpy(&(p->fullName[0]), fullName);
1092     if (env != NULL) {
1093       csound->Free(csound, fullName);
1094       env = NULL;
1095     }
1096 
1097     /* if sound file, re-open file descriptor with libsndfile */
1098     switch (type) {
1099     case CSFILE_STD:                          /* stdio */
1100       *((FILE**) fd) = tmp_f;
1101       break;
1102     case CSFILE_SND_R:                        /* sound file read */
1103       memcpy(&sfinfo, param, sizeof(SF_INFO));
1104       p->sf = sf_open_fd(tmp_fd, SFM_READ, &sfinfo, 0);
1105       if (p->sf == (SNDFILE*) NULL) {
1106         int   extPos;
1107         /* open failed: */
1108         extPos = (nbytes - (int) sizeof(CSFILE)) - 4;
1109         /* check for .sd2 file first */
1110         if (extPos > 0 &&
1111             p->fullName[extPos] == (char) '.' &&
1112             tolower(p->fullName[extPos + 1]) == (char) 's' &&
1113             tolower(p->fullName[extPos + 2]) == (char) 'd' &&
1114             p->fullName[extPos + 3] == (char) '2') {
1115           //memset(&sfinfo, 0, sizeof(SF_INFO));
1116           p->sf = sf_open(&(p->fullName[0]), SFM_READ, &sfinfo);
1117           if (p->sf != (SNDFILE*) NULL) {
1118             /* if successfully opened as .sd2, */
1119             /* the integer file descriptor is no longer needed */
1120             close(tmp_fd);
1121             p->fd = tmp_fd = -1;
1122             sf_command(p->sf, SFC_SET_VBR_ENCODING_QUALITY,
1123                        &csound->oparms->quality, sizeof(double));
1124             goto doneSFOpen;
1125           }
1126         }
1127 #if 0
1128         /* maybe raw file ? rewind and try again */
1129         if (lseek(tmp_fd, (off_t) 0, SEEK_SET) == (off_t) 0) {
1130           SF_INFO *sf = (SF_INFO*)param;
1131           sf->format = SF_FORMAT_RAW | SF_FORMAT_PCM_16;
1132           sf->samplerate = csound->esr;
1133           //sf->channels = 1;//csound->inchnls;
1134           csound->Warning(csound,
1135                           Str("After open failure(%s)\n"
1136                               "will try to open %s as raw\n"),
1137                           sf_strerror(NULL), fullName);
1138           p->sf = sf_open_fd(tmp_fd, SFM_READ, sf, 0);
1139         }
1140 #endif
1141         if (UNLIKELY(p->sf == (SNDFILE*) NULL)) {
1142           /* csound->Warning(csound, Str("Failed to open %s: %s\n"), */
1143           /*                 fullName, sf_strerror(NULL)); */
1144           goto err_return;
1145         }
1146       }
1147       else {
1148       doneSFOpen:
1149         memcpy((SF_INFO*) param, &sfinfo, sizeof(SF_INFO));
1150       }
1151       *((SNDFILE**) fd) = p->sf;
1152       break;
1153     case CSFILE_SND_W:                        /* sound file write */
1154       p->sf = sf_open_fd(tmp_fd, SFM_WRITE, (SF_INFO*) param, 0);
1155       if (UNLIKELY(p->sf == (SNDFILE*) NULL)) {
1156           csound->Warning(csound, Str("Failed to open %s: %s\n"),
1157                           fullName, sf_strerror(NULL));
1158         goto err_return;
1159       }
1160       sf_command(p->sf, SFC_SET_CLIPPING, NULL, SF_TRUE);
1161       sf_command(p->sf, SFC_SET_VBR_ENCODING_QUALITY,
1162                  &csound->oparms->quality, sizeof(double));
1163       *((SNDFILE**) fd) = p->sf;
1164       break;
1165     default:                                  /* low level I/O */
1166       *((int*) fd) = tmp_fd;
1167     }
1168     /* link into chain of open files */
1169     if (csound->open_files != NULL)
1170       ((CSFILE*) csound->open_files)->prv = p;
1171     csound->open_files = (void*) p;
1172     /* notify the host if it asked */
1173     if (csound->FileOpenCallback_ != NULL) {
1174       int writing = (type == CSFILE_SND_W || type == CSFILE_FD_W ||
1175                      (type == CSFILE_STD && ((char*)param)[0] == 'w'));
1176       if (csFileType == CSFTYPE_UNKNOWN_AUDIO && type == CSFILE_SND_R)
1177         csFileType = sftype2csfiletype(((SF_INFO*)param)->format);
1178       csound->FileOpenCallback_(csound, p->fullName, csFileType,
1179                                 writing, isTemporary);
1180     }
1181     /* return with opaque file handle */
1182     p->cb = NULL;
1183     p->async_flag = 0;
1184     p->buf = NULL;
1185     p->bufsize = 0;
1186     return (void*) p;
1187 
1188  err_return:
1189     /* clean up on error */
1190     if (p != NULL)
1191       csound->Free(csound, p);
1192     if (fullName != NULL && env != NULL)
1193       csound->Free(csound, fullName);
1194     if (tmp_fd >= 0)
1195       close(tmp_fd);
1196     else if (tmp_f != NULL)
1197       fclose(tmp_f);
1198     if (type > CSFILE_STD)
1199       *((SNDFILE**) fd) = (SNDFILE*) NULL;
1200     else if (type == CSFILE_STD)
1201       *((FILE**) fd) = (FILE*) NULL;
1202     else
1203       *((int*) fd) = -1;
1204     return NULL;
1205 }
1206 
1207 
1208 /**
1209  * Allocate a file handle for an existing file already opened with open(),
1210  * fopen(), or sf_open(), for later use with csoundFileClose() or
1211  * csoundGetFileName(), or storing in an FDCH structure.
1212  * Files registered this way (or opened with csoundFileOpen()) are also
1213  * automatically closed by csoundReset().
1214  * Parameters and return value are similar to csoundFileOpen(), except
1215  * fullName is the name that will be returned by a later call to
1216  * csoundGetFileName().
1217  */
1218 
csoundCreateFileHandle(CSOUND * csound,void * fd,int type,const char * fullName)1219 void *csoundCreateFileHandle(CSOUND *csound,
1220                              void *fd, int type, const char *fullName)
1221 {
1222     CSFILE  *p = NULL;
1223     int     nbytes = (int) sizeof(CSFILE);
1224 
1225     /* name should not be empty */
1226     if (fullName == NULL || fullName[0] == '\0')
1227       return NULL;
1228     nbytes += (int) strlen(fullName);
1229     /* allocate file structure */
1230     p = (CSFILE*) csound->Calloc(csound, (size_t) nbytes);
1231     if (p == NULL)
1232       return NULL;
1233     p->nxt = (CSFILE*) csound->open_files;
1234     p->prv = (CSFILE*) NULL;
1235     p->type = type;
1236     p->fd = -1;
1237     p->f = (FILE*) NULL;
1238     p->sf = (SNDFILE*) NULL;
1239     p->cb = NULL;
1240     strcpy(&(p->fullName[0]), fullName);
1241     /* open file */
1242     switch (type) {
1243     case CSFILE_FD_R:
1244     case CSFILE_FD_W:
1245       p->fd = *((int*) fd);
1246       break;
1247     case CSFILE_STD:
1248       p->f = *((FILE**) fd);
1249       break;
1250     case CSFILE_SND_R:
1251     case CSFILE_SND_W:
1252       p->sf = *((SNDFILE**) fd);
1253       break;
1254     default:
1255       csoundErrorMsg(csound, Str("internal error: csoundCreateFileHandle(): "
1256                                  "invalid type: %d"), type);
1257       csound->Free(csound, p);
1258       return NULL;
1259     }
1260     /* link into chain of open files */
1261     if (csound->open_files != NULL)
1262       ((CSFILE*) csound->open_files)->prv = p;
1263     csound->open_files = (void*) p;
1264     /* return with opaque file handle */
1265     p->cb = NULL;
1266     return (void*) p;
1267 }
1268 
1269 /**
1270  * Get the full name of a file previously opened with csoundFileOpen().
1271  */
1272 
csoundGetFileName(void * fd)1273 char *csoundGetFileName(void *fd)
1274 {
1275     return &(((CSFILE*) fd)->fullName[0]);
1276 }
1277 
1278 /**
1279  * Close a file previously opened with csoundFileOpen().
1280  */
1281 
csoundFileClose(CSOUND * csound,void * fd)1282 int csoundFileClose(CSOUND *csound, void *fd)
1283 {
1284     CSFILE  *p = (CSFILE*) fd;
1285     int     retval = -1;
1286     if (p->async_flag == ASYNC_GLOBAL) {
1287       csound->WaitThreadLockNoTimeout(csound->file_io_threadlock);
1288       /* close file */
1289       switch (p->type) {
1290       case CSFILE_FD_R:
1291       case CSFILE_FD_W:
1292         retval = close(p->fd);
1293         break;
1294       case CSFILE_STD:
1295         retval = fclose(p->f);
1296         break;
1297       case CSFILE_SND_R:
1298       case CSFILE_SND_W:
1299         if (p->sf)
1300           retval = sf_close(p->sf);
1301         p->sf = NULL;
1302         if (p->fd >= 0)
1303           retval |= close(p->fd);
1304         break;
1305       }
1306       /* unlink from chain of open files */
1307       if (p->prv == NULL)
1308         csound->open_files = (void*) p->nxt;
1309       else
1310         p->prv->nxt = p->nxt;
1311       if (p->nxt != NULL)
1312         p->nxt->prv = p->prv;
1313       if (p->buf != NULL) csound->Free(csound, p->buf);
1314       p->bufsize = 0;
1315       csound->DestroyCircularBuffer(csound, p->cb);
1316       csound->NotifyThreadLock(csound->file_io_threadlock);
1317     } else {
1318       /* close file */
1319       switch (p->type) {
1320       case CSFILE_FD_R:
1321       case CSFILE_FD_W:
1322         retval = close(p->fd);
1323         break;
1324       case CSFILE_STD:
1325         retval = fclose(p->f);
1326         break;
1327       case CSFILE_SND_R:
1328       case CSFILE_SND_W:
1329         retval = sf_close(p->sf);
1330         if (p->fd >= 0)
1331           retval |= close(p->fd);
1332         break;
1333       }
1334       /* unlink from chain of open files */
1335       if (p->prv == NULL)
1336         csound->open_files = (void*) p->nxt;
1337       else
1338         p->prv->nxt = p->nxt;
1339       if (p->nxt != NULL)
1340         p->nxt->prv = p->prv;
1341     }
1342     /* free allocated memory */
1343     csound->Free(csound, fd);
1344 
1345     /* return with error value */
1346     return retval;
1347 }
1348 
1349 /* Close all open files; called by csoundReset(). */
1350 
close_all_files(CSOUND * csound)1351 void close_all_files(CSOUND *csound)
1352 {
1353     while (csound->open_files != NULL)
1354       csoundFileClose(csound, csound->open_files);
1355     if (csound->file_io_start) {
1356 #ifndef __EMSCRIPTEN__
1357       csound->JoinThread(csound->file_io_thread);
1358 #endif
1359       if (csound->file_io_threadlock != NULL)
1360         csound->DestroyThreadLock(csound->file_io_threadlock);
1361     }
1362 }
1363 
1364 /* The fromScore parameter should be 1 if opening a score include file,
1365    0 if opening an orchestra include file */
fopen_path(CSOUND * csound,FILE ** fp,char * name,char * basename,char * env,int fromScore)1366 void *fopen_path(CSOUND *csound, FILE **fp, char *name, char *basename,
1367                  char *env, int fromScore)
1368 {
1369     void *fd;
1370     int  csftype = (fromScore ? CSFTYPE_SCO_INCLUDE : CSFTYPE_ORC_INCLUDE);
1371 
1372     /* First try to open name given */
1373     fd = csound->FileOpen2(csound, fp, CSFILE_STD, name, "r", NULL,
1374                            csftype, 0);
1375     if (fd != NULL)
1376       return fd;
1377     /* if that fails try in base directory */
1378     if (basename != NULL) {
1379       char *dir, *name_full;
1380       if ((dir = csoundSplitDirectoryFromPath(csound, basename)) != NULL) {
1381         name_full = csoundConcatenatePaths(csound, dir, name);
1382         fd = csound->FileOpen2(csound, fp, CSFILE_STD, name_full, "r", NULL,
1383                                csftype, 0);
1384         csound->Free(csound, dir);
1385         csound->Free(csound, name_full);
1386         if (fd != NULL)
1387           return fd;
1388       }
1389     }
1390     /* or use env argument */
1391     fd = csound->FileOpen2(csound, fp, CSFILE_STD, name, "r", env,
1392                            csftype, 0);
1393     return fd;
1394 }
1395 
1396 uintptr_t file_iothread(void *p);
1397 
csoundFileOpenWithType_Async(CSOUND * csound,void * fd,int type,const char * name,void * param,const char * env,int csFileType,int buffsize,int isTemporary)1398 void *csoundFileOpenWithType_Async(CSOUND *csound, void *fd, int type,
1399                                    const char *name, void *param, const char *env,
1400                                    int csFileType, int buffsize, int isTemporary)
1401 {
1402 #ifndef __EMSCRIPTEN__
1403     CSFILE *p;
1404     if ((p = (CSFILE *) csoundFileOpenWithType(csound,fd,type,name,param,env,
1405                                                csFileType,isTemporary)) == NULL)
1406       return NULL;
1407 
1408     if (csound->file_io_start == 0) {
1409       csound->file_io_start = 1;
1410       csound->file_io_threadlock = csound->CreateThreadLock();
1411       csound->NotifyThreadLock(csound->file_io_threadlock);
1412       csound->file_io_thread =
1413         csound->CreateThread(file_iothread, (void *) csound);
1414     }
1415     csound->WaitThreadLockNoTimeout(csound->file_io_threadlock);
1416     p->async_flag = ASYNC_GLOBAL;
1417 
1418     p->cb = csound->CreateCircularBuffer(csound, buffsize*4, sizeof(MYFLT));
1419     p->items = 0;
1420     p->pos = 0;
1421     p->bufsize = buffsize;
1422     p->buf = (MYFLT *) csound->Calloc(csound, sizeof(MYFLT)*buffsize);
1423     csound->NotifyThreadLock(csound->file_io_threadlock);
1424 
1425     if (p->cb == NULL || p->buf == NULL) {
1426       /* close file immediately */
1427       csoundFileClose(csound, (void *) p);
1428       return NULL;
1429     }
1430     return (void *) p;
1431 #else
1432     return NULL;
1433 #endif
1434 }
1435 
csoundReadAsync(CSOUND * csound,void * handle,MYFLT * buf,int items)1436 unsigned int csoundReadAsync(CSOUND *csound, void *handle,
1437                              MYFLT *buf, int items)
1438 {
1439     CSFILE *p = handle;
1440     if (p != NULL &&  p->cb != NULL)
1441       return csound->ReadCircularBuffer(csound, p->cb, buf, items);
1442     else return 0;
1443 }
1444 
csoundWriteAsync(CSOUND * csound,void * handle,MYFLT * buf,int items)1445 unsigned int csoundWriteAsync(CSOUND *csound, void *handle,
1446                               MYFLT *buf, int items)
1447 {
1448     CSFILE *p = handle;
1449     if (p != NULL &&  p->cb != NULL)
1450       return csound->WriteCircularBuffer(csound, p->cb, buf, items);
1451     else return 0;
1452 }
1453 
csoundFSeekAsync(CSOUND * csound,void * handle,int pos,int whence)1454 int csoundFSeekAsync(CSOUND *csound, void *handle, int pos, int whence){
1455     CSFILE *p = handle;
1456     int ret = 0;
1457     csound->WaitThreadLockNoTimeout(csound->file_io_threadlock);
1458     switch (p->type) {
1459     case CSFILE_FD_R:
1460       break;
1461     case CSFILE_FD_W:
1462       break;
1463     case CSFILE_STD:
1464       break;
1465     case CSFILE_SND_R:
1466     case CSFILE_SND_W:
1467       ret = sf_seek(p->sf,pos,whence);
1468       //csoundMessage(csound, "seek set %d\n", pos);
1469       csound->FlushCircularBuffer(csound, p->cb);
1470       p->items = 0;
1471       break;
1472     }
1473     csound->NotifyThreadLock(csound->file_io_threadlock);
1474     return ret;
1475 }
1476 
1477 
read_files(CSOUND * csound)1478 static int read_files(CSOUND *csound){
1479     CSFILE *current = (CSFILE *) csound->open_files;
1480     if (current == NULL) return 0;
1481     while (current) {
1482       if (current->async_flag == ASYNC_GLOBAL) {
1483         int m = current->pos, l, n = current->items;
1484         int items = current->bufsize;
1485         MYFLT *buf = current->buf;
1486         switch (current->type) {
1487         case CSFILE_FD_R:
1488           break;
1489         case CSFILE_FD_W:
1490           break;
1491         case CSFILE_STD:
1492           break;
1493         case CSFILE_SND_R:
1494           if (n == 0) {
1495             n = sf_read_MYFLT(current->sf, buf, items);
1496             m = 0;
1497           }
1498           l = csound->WriteCircularBuffer(csound,current->cb,&buf[m],n);
1499           m += l;
1500           n -= l;
1501           current->items = n;
1502           current->pos = m;
1503           break;
1504         case CSFILE_SND_W:
1505           items = csound->ReadCircularBuffer(csound, current->cb, buf, items);
1506           if (items == 0) { csoundSleep(10); break;}
1507           sf_write_MYFLT(current->sf, buf, items);
1508           break;
1509         }
1510       }
1511       current = current->nxt;
1512     }
1513     return 1;
1514 }
1515 
1516 
1517 
1518 
file_iothread(void * p)1519 uintptr_t file_iothread(void *p){
1520     int res = 1;
1521     CSOUND *csound = p;
1522     int wakeup = (int) (1000*csound->ksmps/csound->esr);
1523     _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
1524     if (wakeup == 0) wakeup = 1;
1525     while (res){
1526       csoundSleep(wakeup);
1527       csound->WaitThreadLockNoTimeout(csound->file_io_threadlock);
1528       res = read_files(csound);
1529       csound->NotifyThreadLock(csound->file_io_threadlock);
1530     }
1531     csound->file_io_start = 0;
1532     return (uintptr_t)NULL;
1533 }
1534