summaryrefslogtreecommitdiff
path: root/src/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common.c')
-rw-r--r--src/common.c1386
1 files changed, 1386 insertions, 0 deletions
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..f24dd9a
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,1386 @@
+/*
+ TiMidity++ -- MIDI to WAVE converter and player
+ Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
+ Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ common.c
+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif /* TIME_WITH_SYS_TIME */
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif /* HAVE_SYS_STAT_H */
+#include <fcntl.h>
+
+#ifndef NO_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include <ctype.h>
+#ifndef __W32__
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#else
+#include <process.h>
+#include <io.h>
+#endif /* __W32__ */
+#include "timidity.h"
+#include "common.h"
+#include "output.h"
+#include "controls.h"
+#include "arc.h"
+#include "nkflib.h"
+#include "wrd.h"
+#include "strtab.h"
+#include "support.h"
+
+/* RAND_MAX must defined in stdlib.h
+ * Why RAND_MAX is not defined at SunOS?
+ */
+#if defined(sun) && !defined(SOLARIS) && !defined(RAND_MAX)
+#define RAND_MAX ((1<<15)-1)
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* #define MIME_CONVERSION */
+
+char *program_name, current_filename[1024];
+MBlockList tmpbuffer;
+char *output_text_code = NULL;
+int open_file_noise_mode = OF_NORMAL;
+
+#ifdef DEFAULT_PATH
+ /* The paths in this list will be tried whenever we're reading a file */
+ static PathList defaultpathlist={DEFAULT_PATH,0};
+ static PathList *pathlist=&defaultpathlist; /* This is a linked list */
+#else
+ static PathList *pathlist=0;
+#endif
+
+const char *note_name[] =
+{
+ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
+};
+
+
+#ifndef TMP_MAX
+#define TMP_MAX 238328
+#endif
+
+int
+tmdy_mkstemp(char *tmpl)
+{
+ char *XXXXXX;
+ static uint32 value;
+ uint32 random_time_bits;
+ int count, fd = -1;
+ int save_errno = errno;
+
+ /* These are the characters used in temporary filenames. */
+ static const char letters[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ /* This is where the Xs start. */
+ XXXXXX = strstr(tmpl, "XXXXXX");
+ if (XXXXXX == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Get some more or less random data. */
+#if HAVE_GETTIMEOFDAY
+ {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ random_time_bits = (uint32)((tv.tv_usec << 16) ^ tv.tv_sec);
+ }
+#else
+ random_time_bits = (uint32)time(NULL);
+#endif
+
+ value += random_time_bits ^ getpid();
+
+ for (count = 0; count < TMP_MAX; value += 7777, ++count) {
+ uint32 v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+
+ v = (v << 16) ^ value;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+#if defined(_MSC_VER) || defined(__DMC__)
+#define S_IRUSR 0
+#define S_IWUSR 0
+#endif
+
+ fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
+
+ if (fd >= 0) {
+ errno = save_errno;
+ return fd;
+ }
+ if (errno != EEXIST)
+ return -1;
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ errno = EEXIST;
+ return -1;
+}
+
+
+static char *
+url_dumpfile(URL url, const char *ext)
+{
+ char filename[1024];
+ char *tmpdir;
+ int fd;
+ FILE *fp;
+ int n;
+ char buff[BUFSIZ];
+
+#ifdef TMPDIR
+ tmpdir = TMPDIR;
+#else
+ tmpdir = getenv("TMPDIR");
+#endif
+ if(tmpdir == NULL || strlen(tmpdir) == 0)
+ tmpdir = PATH_STRING "tmp" PATH_STRING;
+ if(IS_PATH_SEP(tmpdir[strlen(tmpdir) - 1]))
+ snprintf(filename, sizeof(filename), "%sXXXXXX.%s", tmpdir, ext);
+ else
+ snprintf(filename, sizeof(filename), "%s" PATH_STRING "XXXXXX.%s",
+ tmpdir, ext);
+
+ fd = tmdy_mkstemp(filename);
+
+ if (fd == -1)
+ return NULL;
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ close(fd);
+ unlink(filename);
+ return NULL;
+ }
+
+ while((n = url_read(url, buff, sizeof(buff))) > 0) {
+ size_t dummy = fwrite(buff, 1, n, fp); ++dummy;
+ }
+ fclose(fp);
+ return safe_strdup(filename);
+}
+
+
+/* Try to open a file for reading. If the filename ends in one of the
+ defined compressor extensions, pipe the file through the decompressor */
+struct timidity_file *try_to_open(char *name, int decompress)
+{
+ struct timidity_file *tf;
+ URL url;
+ int len;
+
+ if((url = url_arc_open(name)) == NULL)
+ if((url = url_open(name)) == NULL)
+ return NULL;
+
+ tf = (struct timidity_file *)safe_malloc(sizeof(struct timidity_file));
+ tf->url = url;
+ tf->tmpname = NULL;
+
+ len = strlen(name);
+ if(decompress && len >= 3 && strcasecmp(name + len - 3, ".gz") == 0)
+ {
+ int method;
+
+ if(!IS_URL_SEEK_SAFE(tf->url))
+ {
+ if((tf->url = url_cache_open(tf->url, 1)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+ }
+
+ method = skip_gzip_header(tf->url);
+ if(method == ARCHIVEC_DEFLATED)
+ {
+ url_cache_disable(tf->url);
+ if((tf->url = url_inflate_open(tf->url, -1, 1)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ /* success */
+ return tf;
+ }
+ /* fail */
+ url_rewind(tf->url);
+ url_cache_disable(tf->url);
+ }
+
+#ifdef __W32__
+ /* Sorry, DECOMPRESSOR_LIST and PATCH_CONVERTERS are not worked yet. */
+ return tf;
+#endif /* __W32__ */
+
+#if defined(DECOMPRESSOR_LIST)
+ if(decompress)
+ {
+ static char *decompressor_list[] = DECOMPRESSOR_LIST, **dec;
+ char tmp[1024];
+
+ /* Check if it's a compressed file */
+ for(dec = decompressor_list; *dec; dec += 2)
+ {
+ if(!check_file_extension(name, *dec, 0))
+ continue;
+
+ tf->tmpname = url_dumpfile(tf->url, *dec);
+ if (tf->tmpname == NULL) {
+ close_file(tf);
+ return NULL;
+ }
+
+ url_close(tf->url);
+ snprintf(tmp, sizeof(tmp), *(dec+1), tf->tmpname);
+ if((tf->url = url_pipe_open(tmp)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ break;
+ }
+ }
+#endif /* DECOMPRESSOR_LIST */
+
+#if defined(PATCH_CONVERTERS)
+ if(decompress == 2)
+ {
+ static char *decompressor_list[] = PATCH_CONVERTERS, **dec;
+ char tmp[1024];
+
+ /* Check if it's a compressed file */
+ for(dec = decompressor_list; *dec; dec += 2)
+ {
+ if(!check_file_extension(name, *dec, 0))
+ continue;
+
+ tf->tmpname = url_dumpfile(tf->url, *dec);
+ if (tf->tmpname == NULL) {
+ close_file(tf);
+ return NULL;
+ }
+
+ url_close(tf->url);
+ sprintf(tmp, *(dec+1), tf->tmpname);
+ if((tf->url = url_pipe_open(tmp)) == NULL)
+ {
+ close_file(tf);
+ return NULL;
+ }
+
+ break;
+ }
+ }
+#endif /* PATCH_CONVERTERS */
+
+ return tf;
+}
+
+int is_url_prefix(const char *name)
+{
+ int i;
+
+ static char *url_proto_names[] =
+ {
+ "file:",
+#ifdef SUPPORT_SOCKET
+ "http://",
+ "ftp://",
+ "news://",
+#endif /* SUPPORT_SOCKET */
+ "mime:",
+ NULL
+ };
+ for(i = 0; url_proto_names[i]; i++)
+ if(strncmp(name, url_proto_names[i], strlen(url_proto_names[i])) == 0)
+ return 1;
+ return 0;
+}
+
+static int is_abs_path(const char *name)
+{
+#ifndef __MACOS__
+ if (IS_PATH_SEP(name[0]))
+ return 1;
+#else
+ if (!IS_PATH_SEP(name[0]) && strchr(name, PATH_SEP) != NULL)
+ return 1;
+#endif /* __MACOS__ */
+#ifdef __W32__
+ /* [A-Za-z]: (for Windows) */
+ if (isalpha(name[0]) && name[1] == ':')
+ return 1;
+#endif /* __W32__ */
+ if (is_url_prefix(name))
+ return 1; /* assuming relative notation is excluded */
+ return 0;
+}
+
+struct timidity_file *open_with_mem(char *mem, int32 memlen, int noise_mode)
+{
+ URL url;
+ struct timidity_file *tf;
+
+ errno = 0;
+ if((url = url_mem_open(mem, memlen, 0)) == NULL)
+ {
+ if(noise_mode >= 2)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Can't open.");
+ return NULL;
+ }
+ tf = (struct timidity_file *)safe_malloc(sizeof(struct timidity_file));
+ tf->url = url;
+ tf->tmpname = NULL;
+ return tf;
+}
+
+/*
+ * This is meant to find and open files for reading, possibly piping
+ * them through a decompressor.
+ */
+struct timidity_file *open_file(char *name, int decompress, int noise_mode)
+{
+ struct timidity_file *tf;
+ PathList *plp = pathlist;
+ int l;
+
+ open_file_noise_mode = noise_mode;
+ if (!name || !(*name)) {
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Attempted to open nameless file.");
+ return 0;
+ }
+ /* First try the given name */
+ strncpy(current_filename, url_unexpand_home_dir(name), 1023);
+ current_filename[1023] = '\0';
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s",
+ current_filename);
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if (errno) {
+#else
+ if (errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ if (!is_abs_path(name))
+ while (plp) { /* Try along the path then */
+ *current_filename = 0;
+ if((l = strlen(plp->path))) {
+ strncpy(current_filename, plp->path,
+ sizeof(current_filename));
+ if (!IS_PATH_SEP(current_filename[l - 1])
+ && current_filename[l - 1] != '#'
+ && name[0] != '#')
+ strncat(current_filename, PATH_STRING,
+ sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ }
+ strncat(current_filename, name, sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "Trying to open %s", current_filename);
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if(errno) {
+#else
+ if(errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ plp = plp->next;
+ }
+ /* Nothing could be opened. */
+ *current_filename = 0;
+ if (noise_mode >= 2)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", name,
+ (errno) ? strerror(errno) : "Can't open file");
+ return 0;
+}
+
+/*
+ * This is meant to find and open regular files for reading, possibly
+ * piping them through a decompressor.
+ */
+struct timidity_file *open_file_r(char *name, int decompress, int noise_mode)
+{
+ struct stat st;
+ struct timidity_file *tf;
+ PathList *plp = pathlist;
+ int l;
+
+ open_file_noise_mode = noise_mode;
+ if (!name || !(*name)) {
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Attempted to open nameless file.");
+ return 0;
+ }
+ /* First try the given name */
+ strncpy(current_filename, url_unexpand_home_dir(name), 1023);
+ current_filename[1023] = '\0';
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG, "Trying to open %s",
+ current_filename);
+ stat(current_filename, &st);
+ if (!S_ISDIR(st.st_mode))
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if (errno) {
+#else
+ if (errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ if (!is_abs_path(name))
+ while (plp) { /* Try along the path then */
+ *current_filename = 0;
+ if((l = strlen(plp->path))) {
+ strncpy(current_filename, plp->path,
+ sizeof(current_filename));
+ if (!IS_PATH_SEP(current_filename[l - 1])
+ && current_filename[l - 1] != '#'
+ && name[0] != '#')
+ strncat(current_filename, PATH_STRING,
+ sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ }
+ strncat(current_filename, name, sizeof(current_filename)
+ - strlen(current_filename) - 1);
+ if (noise_mode)
+ ctl->cmsg(CMSG_INFO, VERB_DEBUG,
+ "Trying to open %s", current_filename);
+ stat(current_filename, &st);
+ if (!S_ISDIR(st.st_mode))
+ if ((tf = try_to_open(current_filename, decompress)))
+ return tf;
+#ifdef __MACOS__
+ if(errno) {
+#else
+ if(errno && errno != ENOENT) {
+#endif
+ if (noise_mode)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s",
+ current_filename, strerror(errno));
+ return 0;
+ }
+ plp = plp->next;
+ }
+ /* Nothing could be opened. */
+ *current_filename = 0;
+ if (noise_mode >= 2)
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", name,
+ (errno) ? strerror(errno) : "Can't open file");
+ return 0;
+}
+
+/* This closes files opened with open_file */
+void close_file(struct timidity_file *tf)
+{
+ int save_errno = errno;
+ if(tf->url != NULL)
+ {
+#ifndef __W32__
+ if(tf->tmpname != NULL)
+ {
+ int i;
+ /* dispose the pipe garbage */
+ for(i = 0; tf_getc(tf) != EOF && i < 0xFFFF; i++)
+ ;
+ }
+#endif /* __W32__ */
+ url_close(tf->url);
+ }
+ if(tf->tmpname != NULL)
+ {
+ unlink(tf->tmpname); /* remove temporary file */
+ free(tf->tmpname);
+ }
+ free(tf);
+ errno = save_errno;
+}
+
+/* This is meant for skipping a few bytes. */
+void skip(struct timidity_file *tf, size_t len)
+{
+ url_skip(tf->url, (long)len);
+}
+
+char *tf_gets(char *buff, int n, struct timidity_file *tf)
+{
+ return url_gets(tf->url, buff, n);
+}
+
+long tf_read(void *buff, int32 size, int32 nitems, struct timidity_file *tf)
+{
+ return url_nread(tf->url, buff, size * nitems) / size;
+}
+
+long tf_seek(struct timidity_file *tf, long offset, int whence)
+{
+ long prevpos;
+
+ prevpos = url_seek(tf->url, offset, whence);
+ if(prevpos == -1)
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "Warning: Can't seek file position");
+ return prevpos;
+}
+
+long tf_tell(struct timidity_file *tf)
+{
+ long pos;
+
+ pos = url_tell(tf->url);
+ if(pos == -1)
+ {
+ ctl->cmsg(CMSG_WARNING, VERB_NORMAL,
+ "Warning: Can't get current file position");
+ return (long)tf->url->nread;
+ }
+
+ return pos;
+}
+
+void safe_exit(int status)
+{
+ if(play_mode->fd != -1)
+ {
+ play_mode->acntl(PM_REQ_DISCARD, NULL);
+ play_mode->close_output();
+ }
+ ctl->close();
+ wrdt->close();
+ exit(status);
+ /*NOTREACHED*/
+}
+
+/* This'll allocate memory or die. */
+void *safe_malloc(size_t count)
+{
+ void *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+ if(count > MAX_SAFE_MALLOC_SIZE)
+ {
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Strange, I feel like allocating %d bytes. "
+ "This must be a bug.", count);
+ }
+ else {
+ if(count == 0)
+ /* Some malloc routine return NULL if count is zero, such as
+ * malloc routine from libmalloc.a of Solaris.
+ * But TiMidity doesn't want to return NULL even if count is zero.
+ */
+ count = 1;
+ if((p = (void *)malloc(count)) != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Sorry. Couldn't malloc %d bytes.", count);
+ }
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+void *safe_large_malloc(size_t count)
+{
+ void *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+ if(count == 0)
+ /* Some malloc routine return NULL if count is zero, such as
+ * malloc routine from libmalloc.a of Solaris.
+ * But TiMidity doesn't want to return NULL even if count is zero.
+ */
+ count = 1;
+ if((p = (void *)malloc(count)) != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Sorry. Couldn't malloc %d bytes.", count);
+
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+void *safe_realloc(void *ptr, size_t count)
+{
+ void *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+ if(count > MAX_SAFE_MALLOC_SIZE)
+ {
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Strange, I feel like allocating %d bytes. "
+ "This must be a bug.", count);
+ }
+ else {
+ if (ptr == NULL)
+ return safe_malloc(count);
+ if(count == 0)
+ /* Some malloc routine return NULL if count is zero, such as
+ * malloc routine from libmalloc.a of Solaris.
+ * But TiMidity doesn't want to return NULL even if count is zero.
+ */
+ count = 1;
+ if((p = (void *)realloc(ptr, count)) != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL,
+ "Sorry. Couldn't malloc %d bytes.", count);
+ }
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+/* This'll allocate memory or die. */
+char *safe_strdup(const char *s)
+{
+ char *p;
+ static int errflag = 0;
+
+ if(errflag)
+ safe_exit(10);
+
+ if(s == NULL)
+ p = strdup("");
+ else
+ p = strdup(s);
+ if(p != NULL)
+ return p;
+ errflag = 1;
+ ctl->cmsg(CMSG_FATAL, VERB_NORMAL, "Sorry. Couldn't alloc memory.");
+#ifdef ABORT_AT_FATAL
+ abort();
+#endif /* ABORT_AT_FATAL */
+ safe_exit(10);
+ /*NOTREACHED*/
+ return 0;
+}
+
+/* free ((void **)ptr_list)[0..count-1] and ptr_list itself */
+void free_ptr_list(void *ptr_list, int count)
+{
+ int i;
+ for(i = 0; i < count; i++)
+ free(((void **)ptr_list)[i]);
+ free(ptr_list);
+}
+
+static int atoi_limited(const char *string, int v_min, int v_max)
+{
+ int value = atoi(string);
+
+ if (value <= v_min)
+ value = v_min;
+ else if (value > v_max)
+ value = v_max;
+ return value;
+}
+
+int string_to_7bit_range(const char *string_, int *start, int *end)
+{
+ const char *string = string_;
+
+ if(isdigit(*string)) {
+ *start = atoi_limited(string, 0, 127);
+ while(isdigit(*++string)) ;
+ } else
+ *start = 0;
+ if (*string == '-') {
+ string++;
+ *end = isdigit(*string) ? atoi_limited(string, 0, 127) : 127;
+ if(*start > *end)
+ *end = *start;
+ } else
+ *end = *start;
+ return string != string_;
+}
+
+/* This adds a directory to the path list */
+void add_to_pathlist(char *s)
+{
+ PathList *cur, *prev, *plp;
+
+ /* Check duplicated path in the pathlist. */
+ plp = prev = NULL;
+ for(cur = pathlist; cur; prev = cur, cur = cur->next)
+ if(pathcmp(s, cur->path, 0) == 0)
+ {
+ plp = cur;
+ break;
+ }
+
+ if(plp) /* found */
+ {
+ if(prev == NULL) /* first */
+ pathlist = pathlist->next;
+ else
+ prev->next = plp->next;
+ }
+ else
+ {
+ /* Allocate new path */
+ plp = safe_malloc(sizeof(PathList));
+ plp->path = safe_strdup(s);
+ }
+
+ plp->next = pathlist;
+ pathlist = plp;
+}
+
+void clean_up_pathlist(void)
+{
+ PathList *cur, *next;
+
+ cur = pathlist;
+ while (cur) {
+ next = cur->next;
+#ifdef DEFAULT_PATH
+ if (cur == &defaultpathlist) {
+ cur = next;
+ continue;
+ }
+#endif
+ free(cur->path);
+ free(cur);
+ cur = next;
+ }
+
+#ifdef DEFAULT_PATH
+ pathlist = &defaultpathlist;
+#else
+ pathlist = NULL;
+#endif
+}
+
+#ifndef HAVE_VOLATILE
+/*ARGSUSED*/
+int volatile_touch(void *dmy) {return 1;}
+#endif /* HAVE_VOLATILE */
+
+/* code converters */
+static unsigned char
+ w2k[] = {128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
+ 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
+ 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
+ 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209};
+
+static void code_convert_cp1251(char *in, char *out, int maxlen)
+{
+ int i;
+ if(out == NULL)
+ out = in;
+ for(i = 0; i < maxlen && in[i]; i++)
+ {
+ if(in[i] & 0200)
+ out[i] = w2k[in[i] & 0177];
+ else
+ out[i] = in[i];
+ }
+ out[i]='\0';
+}
+
+static void code_convert_dump(char *in, char *out, int maxlen, char *ocode)
+{
+ if(ocode == NULL)
+ ocode = output_text_code;
+
+ if(ocode != NULL && ocode != (char *)-1
+ && (strstr(ocode, "ASCII") || strstr(ocode, "ascii")))
+ {
+ int i;
+
+ if(out == NULL)
+ out = in;
+ for(i = 0; i < maxlen && in[i]; i++)
+ if(in[i] < ' ' || in[i] >= 127)
+ out[i] = '.';
+ else
+ out[i] = in[i];
+ out[i]='\0';
+ }
+ else /* "NOCNV" */
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, maxlen);
+ out[maxlen] = '\0';
+ }
+}
+
+#ifdef JAPANESE
+static void code_convert_japan(char *in, char *out, int maxlen,
+ char *icode, char *ocode)
+{
+ static char *mode = NULL, *wrd_mode = NULL;
+
+ if(ocode != NULL && ocode != (char *)-1)
+ {
+ nkf_convert(in, out, maxlen, icode, ocode);
+ if(out != NULL)
+ out[maxlen] = '\0';
+ return;
+ }
+
+ if(mode == NULL || wrd_mode == NULL)
+ {
+ mode = output_text_code;
+ if(mode == NULL || strstr(mode, "AUTO"))
+ {
+#ifndef __W32__
+ mode = getenv("LANG");
+#else
+ mode = "SJIS";
+ wrd_mode = "SJISK";
+#endif
+ if(mode == NULL || *mode == '\0')
+ {
+ mode = "ASCII";
+ wrd_mode = mode;
+ }
+ }
+
+ if(strstr(mode, "ASCII") ||
+ strstr(mode, "ascii"))
+ {
+ mode = "ASCII";
+ wrd_mode = mode;
+ }
+ else if(strstr(mode, "NOCNV") ||
+ strstr(mode, "nocnv"))
+ {
+ mode = "NOCNV";
+ wrd_mode = mode;
+ }
+#ifndef HPUX
+ else if(strstr(mode, "EUC") ||
+ strstr(mode, "euc") ||
+ strstr(mode, "ujis") ||
+ strcmp(mode, "japanese") == 0)
+ {
+ mode = "EUC";
+ wrd_mode = "EUCK";
+ }
+ else if(strstr(mode, "SJIS") ||
+ strstr(mode, "sjis"))
+ {
+ mode = "SJIS";
+ wrd_mode = "SJISK";
+ }
+#else
+ else if(strstr(mode, "EUC") ||
+ strstr(mode, "euc") ||
+ strstr(mode, "ujis"))
+ {
+ mode = "EUC";
+ wrd_mode = "EUCK";
+ }
+ else if(strstr(mode, "SJIS") ||
+ strstr(mode, "sjis") ||
+ strcmp(mode, "japanese") == 0)
+ {
+ mode = "SJIS";
+ wrd_mode = "SJISK";
+ }
+#endif /* HPUX */
+ else if(strstr(mode,"JISk")||
+ strstr(mode,"jisk"))
+ {
+ mode = "JISK";
+ wrd_mode = mode;
+ }
+ else if(strstr(mode, "JIS") ||
+ strstr(mode, "jis"))
+ {
+ mode = "JIS";
+ wrd_mode = "JISK";
+ }
+ else if(strcmp(mode, "ja") == 0)
+ {
+ mode = "EUC";
+ wrd_mode = "EUCK";
+ }
+ else
+ {
+ mode = "NOCNV";
+ wrd_mode = mode;
+ }
+ }
+
+ if(ocode == NULL)
+ {
+ if(strcmp(mode, "NOCNV") == 0)
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, maxlen);
+ out[maxlen] = '\0';
+ }
+ else if(strcmp(mode, "ASCII") == 0)
+ code_convert_dump(in, out, maxlen, "ASCII");
+ else
+ {
+ nkf_convert(in, out, maxlen, icode, mode);
+ if(out != NULL)
+ out[maxlen] = '\0';
+ }
+ }
+ else if(ocode == (char *)-1)
+ {
+ if(strcmp(wrd_mode, "NOCNV") == 0)
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, maxlen);
+ out[maxlen] = '\0';
+ }
+ else if(strcmp(wrd_mode, "ASCII") == 0)
+ code_convert_dump(in, out, maxlen, "ASCII");
+ else
+ {
+ nkf_convert(in, out, maxlen, icode, wrd_mode);
+ if(out != NULL)
+ out[maxlen] = '\0';
+ }
+ }
+}
+#endif /* JAPANESE */
+
+void code_convert(char *in, char *out, int outsiz, char *icode, char *ocode)
+{
+#if !defined(MIME_CONVERSION) && defined(JAPANESE)
+ int i;
+ /* check ASCII string */
+ for(i = 0; in[i]; i++)
+ if(in[i] < ' ' || in[i] >= 127)
+ break;
+ if(!in[i])
+ {
+ if(out == NULL)
+ return;
+ strncpy(out, in, outsiz - 1);
+ out[outsiz - 1] = '\0';
+ return; /* All ASCII string */
+ }
+#endif /* MIME_CONVERSION */
+
+ if(ocode != NULL && ocode != (char *)-1)
+ {
+ if(strcasecmp(ocode, "nocnv") == 0)
+ {
+ if(out == NULL)
+ return;
+ outsiz--;
+ strncpy(out, in, outsiz);
+ out[outsiz] = '\0';
+ return;
+ }
+
+ if(strcasecmp(ocode, "ascii") == 0)
+ {
+ code_convert_dump(in, out, outsiz - 1, "ASCII");
+ return;
+ }
+
+ if(strcasecmp(ocode, "1251") == 0)
+ {
+ code_convert_cp1251(in, out, outsiz - 1);
+ return;
+ }
+ }
+
+#if defined(JAPANESE)
+ code_convert_japan(in, out, outsiz - 1, icode, ocode);
+#else
+ code_convert_dump(in, out, outsiz - 1, ocode);
+#endif
+}
+
+/* EAW -- insert stuff from playlist files
+ *
+ * Tue Apr 6 1999: Modified by Masanao Izumo <mo@goice.co.jp>
+ * One pass implemented.
+ */
+static char **expand_file_lists(char **files, int *nfiles_in_out)
+{
+ int nfiles;
+ int i;
+ char input_line[256];
+ char *pfile;
+ static const char *testext = ".m3u.pls.asx.M3U.PLS.ASX.tpl";
+ struct timidity_file *list_file;
+ char *one_file[1];
+ int one;
+
+ /* Recusive global */
+ static StringTable st;
+ static int error_outflag = 0;
+ static int depth = 0;
+
+ if(depth >= 16)
+ {
+ if(!error_outflag)
+ {
+ ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
+ "Probable loop in playlist files");
+ error_outflag = 1;
+ }
+ return NULL;
+ }
+
+ if(depth == 0)
+ {
+ error_outflag = 0;
+ init_string_table(&st);
+ }
+ nfiles = *nfiles_in_out;
+
+ /* Expand playlist recursively */
+ for(i = 0; i < nfiles; i++)
+ {
+ /* extract the file extension */
+ pfile = strrchr(files[i], '.');
+
+ if(*files[i] == '@' || (pfile != NULL && strstr(testext, pfile)))
+ {
+ /* Playlist file */
+ if(*files[i] == '@')
+ list_file = open_file(files[i] + 1, 1, 1);
+ else
+ list_file = open_file(files[i], 1, 1);
+ if(list_file)
+ {
+ while(tf_gets(input_line, sizeof(input_line), list_file)
+ != NULL) {
+ if(*input_line == '\n' || *input_line == '\r')
+ continue;
+ if((pfile = strchr(input_line, '\r')))
+ *pfile = '\0';
+ if((pfile = strchr(input_line, '\n')))
+ *pfile = '\0';
+ one_file[0] = input_line;
+ one = 1;
+ depth++;
+ expand_file_lists(one_file, &one);
+ depth--;
+ }
+ close_file(list_file);
+ }
+ }
+ else /* Other file */
+ put_string_table(&st, files[i], strlen(files[i]));
+ }
+
+ if(depth)
+ return NULL;
+ *nfiles_in_out = st.nstring;
+ return make_string_array(&st);
+}
+
+char **expand_file_archives(char **files, int *nfiles_in_out)
+{
+ int nfiles;
+ char **new_files;
+ int new_nfiles;
+
+ /* First, expand playlist files */
+ nfiles = *nfiles_in_out;
+ files = expand_file_lists(files, &nfiles);
+ if(files == NULL)
+ {
+ *nfiles_in_out = 0;
+ return NULL;
+ }
+
+ /* Second, expand archive files */
+ new_nfiles = nfiles;
+ open_file_noise_mode = OF_NORMAL;
+ new_files = expand_archive_names(&new_nfiles, files);
+ free(files[0]);
+ free(files);
+
+ *nfiles_in_out = new_nfiles;
+ return new_files;
+}
+
+#ifdef RAND_MAX
+int int_rand(int n)
+{
+ if(n < 0)
+ {
+ if(n == -1)
+ srand(time(NULL));
+ else
+ srand(-n);
+ return n;
+ }
+ return (int)(n * (double)rand() * (1.0 / (RAND_MAX + 1.0)));
+}
+#else
+int int_rand(int n)
+{
+ static unsigned long rnd_seed = 0xabcd0123;
+
+ if(n < 0)
+ {
+ if(n == -1)
+ rnd_seed = time(NULL);
+ else
+ rnd_seed = -n;
+ return n;
+ }
+
+ rnd_seed *= 69069UL;
+ return (int)(n * (double)(rnd_seed & 0xffffffff) *
+ (1.0 / (0xffffffff + 1.0)));
+}
+#endif /* RAND_MAX */
+
+int check_file_extension(char *filename, char *ext, int decompress)
+{
+ int len, elen, i;
+#if defined(DECOMPRESSOR_LIST)
+ char *dlist[] = DECOMPRESSOR_LIST;
+#endif /* DECOMPRESSOR_LIST */
+
+ len = strlen(filename);
+ elen = strlen(ext);
+ if(len > elen && strncasecmp(filename + len - elen, ext, elen) == 0)
+ return 1;
+
+ if(decompress)
+ {
+ /* Check gzip'ed file name */
+
+ if(len > 3 + elen &&
+ strncasecmp(filename + len - elen - 3 , ext, elen) == 0 &&
+ strncasecmp(filename + len - 3, ".gz", 3) == 0)
+ return 1;
+
+#if defined(DECOMPRESSOR_LIST)
+ for(i = 0; dlist[i]; i += 2)
+ {
+ int dlen;
+
+ dlen = strlen(dlist[i]);
+ if(len > dlen + elen &&
+ strncasecmp(filename + len - elen - dlen , ext, elen) == 0 &&
+ strncasecmp(filename + len - dlen, dlist[i], dlen) == 0)
+ return 1;
+ }
+#endif /* DECOMPRESSOR_LIST */
+ }
+ return 0;
+}
+
+void randomize_string_list(char **strlist, int n)
+{
+ int i, j;
+ char *tmp;
+ for(i = 0; i < n; i++)
+ {
+ j = int_rand(n - i);
+ tmp = strlist[j];
+ strlist[j] = strlist[n - i - 1];
+ strlist[n - i - 1] = tmp;
+ }
+}
+
+int pathcmp(const char *p1, const char *p2, int ignore_case)
+{
+ int c1, c2;
+
+#ifdef __W32__
+ ignore_case = 1; /* Always ignore the case */
+#endif
+
+ do {
+ c1 = *p1++ & 0xff;
+ c2 = *p2++ & 0xff;
+ if(ignore_case)
+ {
+ c1 = tolower(c1);
+ c2 = tolower(c2);
+ }
+ if(IS_PATH_SEP(c1)) c1 = *p1 ? 0x100 : 0;
+ if(IS_PATH_SEP(c2)) c2 = *p2 ? 0x100 : 0;
+ } while(c1 == c2 && c1 /* && c2 */);
+
+ return c1 - c2;
+}
+
+static int pathcmp_qsort(const char **p1,
+ const char **p2)
+{
+ return pathcmp(*(const char **)p1, *(const char **)p2, 1);
+}
+
+void sort_pathname(char **files, int nfiles)
+{
+ qsort(files, nfiles, sizeof(char *),
+ (int (*)(const void *, const void *))pathcmp_qsort);
+}
+
+char *pathsep_strchr(const char *path)
+{
+#ifdef PATH_SEP2
+ while(*path)
+ {
+ if(*path == PATH_SEP || *path == PATH_SEP2)
+ return (char *)path;
+ path++;
+ }
+ return NULL;
+#else
+ return strchr(path, PATH_SEP);
+#endif
+}
+
+char *pathsep_strrchr(const char *path)
+{
+#ifdef PATH_SEP2
+ char *last_sep = NULL;
+ while(*path)
+ {
+ if(*path == PATH_SEP || *path == PATH_SEP2)
+ last_sep = (char *)path;
+ path++;
+ }
+ return last_sep;
+#else
+ return strrchr(path, PATH_SEP);
+#endif
+}
+
+int str2mID(char *str)
+{
+ int val;
+
+ if(strncasecmp(str, "gs", 2) == 0)
+ val = 0x41;
+ else if(strncasecmp(str, "xg", 2) == 0)
+ val = 0x43;
+ else if(strncasecmp(str, "gm", 2) == 0)
+ val = 0x7e;
+ else
+ {
+ int i, v;
+ val = 0;
+ for(i = 0; i < 2; i++)
+ {
+ v = str[i];
+ if('0' <= v && v <= '9')
+ v = v - '0';
+ else if('A' <= v && v <= 'F')
+ v = v - 'A' + 10;
+ else if('a' <= v && v <= 'f')
+ v = v - 'a' + 10;
+ else
+ return 0;
+ val = (val << 4 | v);
+ }
+ }
+ return val;
+}