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