summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_sys.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_sys.c')
-rw-r--r--libs/fluidsynth/src/fluid_sys.c1296
1 files changed, 1296 insertions, 0 deletions
diff --git a/libs/fluidsynth/src/fluid_sys.c b/libs/fluidsynth/src/fluid_sys.c
new file mode 100644
index 0000000000..600b04e88f
--- /dev/null
+++ b/libs/fluidsynth/src/fluid_sys.c
@@ -0,0 +1,1296 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include "fluid_sys.h"
+
+
+#if WITH_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#ifdef DBUS_SUPPORT
+#include "fluid_rtkit.h"
+#endif
+
+/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket.
+ * Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */
+#define WIN32_SOCKET_FLAG 0x40000000
+
+/* SCHED_FIFO priority for high priority timer threads */
+#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10
+
+
+typedef struct
+{
+ fluid_thread_func_t func;
+ void *data;
+ int prio_level;
+} fluid_thread_info_t;
+
+struct _fluid_timer_t
+{
+ long msec;
+ fluid_timer_callback_t callback;
+ void *data;
+ fluid_thread_t *thread;
+ int cont;
+ int auto_destroy;
+};
+
+struct _fluid_server_socket_t
+{
+ fluid_socket_t socket;
+ fluid_thread_t *thread;
+ int cont;
+ fluid_server_func_t func;
+ void *data;
+};
+
+
+static int fluid_istream_gets(fluid_istream_t in, char* buf, int len);
+
+
+static char fluid_errbuf[512]; /* buffer for error message */
+
+static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL];
+static void* fluid_log_user_data[LAST_LOG_LEVEL];
+static int fluid_log_initialized = 0;
+
+static char* fluid_libname = "fluidsynth";
+
+
+void fluid_sys_config()
+{
+ fluid_log_config();
+}
+
+
+unsigned int fluid_debug_flags = 0;
+
+#if DEBUG
+/*
+ * fluid_debug
+ */
+int fluid_debug(int level, char * fmt, ...)
+{
+ if (fluid_debug_flags & level) {
+ fluid_log_function_t fun;
+ va_list args;
+
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ fun = fluid_log_function[FLUID_DBG];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
+ }
+ }
+ return 0;
+}
+#endif
+
+/**
+ * Installs a new log function for a specified log level.
+ * @param level Log level to install handler for.
+ * @param fun Callback function handler to call for logged messages
+ * @param data User supplied data pointer to pass to log function
+ * @return The previously installed function.
+ */
+fluid_log_function_t
+fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
+{
+ fluid_log_function_t old = NULL;
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ old = fluid_log_function[level];
+ fluid_log_function[level] = fun;
+ fluid_log_user_data[level] = data;
+ }
+ return old;
+}
+
+/**
+ * Default log function which prints to the stderr.
+ * @param level Log level
+ * @param message Log message
+ * @param data User supplied data (not used)
+ */
+void
+fluid_default_log_function(int level, char* message, void* data)
+{
+ FILE* out;
+
+#if defined(WIN32)
+ out = stdout;
+#else
+ out = stderr;
+#endif
+
+ if (fluid_log_initialized == 0) {
+ fluid_log_config();
+ }
+
+ switch (level) {
+ case FLUID_PANIC:
+ FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
+ break;
+ case FLUID_ERR:
+ FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
+ break;
+ case FLUID_WARN:
+ FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
+ break;
+ case FLUID_INFO:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ case FLUID_DBG:
+#if DEBUG
+ FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
+#endif
+ break;
+ default:
+ FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
+ break;
+ }
+ fflush(out);
+}
+
+/*
+ * fluid_init_log
+ */
+void
+fluid_log_config(void)
+{
+ if (fluid_log_initialized == 0) {
+
+ fluid_log_initialized = 1;
+
+ if (fluid_log_function[FLUID_PANIC] == NULL) {
+ fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_ERR] == NULL) {
+ fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_WARN] == NULL) {
+ fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_INFO] == NULL) {
+ fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
+ }
+
+ if (fluid_log_function[FLUID_DBG] == NULL) {
+ fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
+ }
+ }
+}
+
+/**
+ * Print a message to the log.
+ * @param level Log level (#fluid_log_level).
+ * @param fmt Printf style format string for log message
+ * @param ... Arguments for printf 'fmt' message string
+ * @return Always returns #FLUID_FAILED
+ */
+int
+fluid_log(int level, const char* fmt, ...)
+{
+ fluid_log_function_t fun = NULL;
+
+ va_list args;
+ va_start (args, fmt);
+ vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
+ va_end (args);
+
+ if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
+ fun = fluid_log_function[level];
+ if (fun != NULL) {
+ (*fun)(level, fluid_errbuf, fluid_log_user_data[level]);
+ }
+ }
+ return FLUID_FAILED;
+}
+
+/**
+ * An improved strtok, still trashes the input string, but is portable and
+ * thread safe. Also skips token chars at beginning of token string and never
+ * returns an empty token (will return NULL if source ends in token chars though).
+ * NOTE: NOT part of public API
+ * @internal
+ * @param str Pointer to a string pointer of source to tokenize. Pointer gets
+ * updated on each invocation to point to beginning of next token. Note that
+ * token char get's overwritten with a 0 byte. String pointer is set to NULL
+ * when final token is returned.
+ * @param delim String of delimiter chars.
+ * @return Pointer to the next token or NULL if no more tokens.
+ */
+char *fluid_strtok (char **str, char *delim)
+{
+ char *s, *d, *token;
+ char c;
+
+ if (str == NULL || delim == NULL || !*delim)
+ {
+ FLUID_LOG(FLUID_ERR, "Null pointer");
+ return NULL;
+ }
+
+ s = *str;
+ if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */
+
+ /* skip delimiter chars at beginning of token */
+ do
+ {
+ c = *s;
+ if (!c) /* end of source string? */
+ {
+ *str = NULL;
+ return NULL;
+ }
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ s++; /* advance to next source char */
+ break;
+ }
+ }
+ } while (*d); /* while token char match */
+
+ token = s; /* start of token found */
+
+ /* search for next token char or end of source string */
+ for (s = s+1; *s; s++)
+ {
+ c = *s;
+
+ for (d = delim; *d; d++) /* is source char a token char? */
+ {
+ if (c == *d) /* token char match? */
+ {
+ *s = '\0'; /* overwrite token char with zero byte to terminate token */
+ *str = s+1; /* update str to point to beginning of next token */
+ return token;
+ }
+ }
+ }
+
+ /* we get here only if source string ended */
+ *str = NULL;
+ return token;
+}
+
+/*
+ * fluid_error
+ */
+char*
+fluid_error()
+{
+ return fluid_errbuf;
+}
+
+/**
+ * Check if a file is a MIDI file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a MIDI file, FALSE otherwise
+ *
+ * The current implementation only checks for the "MThd" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int
+fluid_is_midifile(const char *filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "MThd", 4) == 0;
+}
+
+/**
+ * Check if a file is a SoundFont file.
+ * @param filename Path to the file to check
+ * @return TRUE if it could be a SoundFont, FALSE otherwise
+ *
+ * The current implementation only checks for the "RIFF" header in the file.
+ * It is useful only to distinguish between SoundFont and MIDI files.
+ */
+int
+fluid_is_soundfont(const char *filename)
+{
+ FILE* fp = fopen(filename, "rb");
+ char id[4];
+
+ if (fp == NULL) {
+ return 0;
+ }
+ if (fread((void*) id, 1, 4, fp) != 4) {
+ fclose(fp);
+ return 0;
+ }
+ fclose(fp);
+
+ return strncmp(id, "RIFF", 4) == 0;
+}
+
+/**
+ * Get time in milliseconds to be used in relative timing operations.
+ * @return Unix time in milliseconds.
+ */
+unsigned int fluid_curtime(void)
+{
+ static glong initial_seconds = 0;
+ GTimeVal timeval;
+
+ if (initial_seconds == 0) {
+ g_get_current_time (&timeval);
+ initial_seconds = timeval.tv_sec;
+ }
+
+ g_get_current_time (&timeval);
+
+ return (unsigned int)((timeval.tv_sec - initial_seconds) * 1000.0 + timeval.tv_usec / 1000.0);
+}
+
+/**
+ * Get time in microseconds to be used in relative timing operations.
+ * @return Unix time in microseconds.
+ */
+double
+fluid_utime (void)
+{
+ GTimeVal timeval;
+
+ g_get_current_time (&timeval);
+
+ return (timeval.tv_sec * 1000000.0 + timeval.tv_usec);
+}
+
+
+#if defined(WIN32) /* Windoze specific stuff */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ if (prio_level > 0)
+ SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+}
+
+
+#elif defined(__OS2__) /* OS/2 specific stuff */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ if (prio_level > 0)
+ DosSetPriority (PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);
+}
+
+#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */
+
+void
+fluid_thread_self_set_prio (int prio_level)
+{
+ struct sched_param priority;
+
+ if (prio_level > 0)
+ {
+
+ memset(&priority, 0, sizeof(priority));
+ priority.sched_priority = prio_level;
+
+ if (pthread_setschedparam (pthread_self (), SCHED_FIFO, &priority) == 0) {
+ return;
+ }
+#ifdef DBUS_SUPPORT
+/* Try to gain high priority via rtkit */
+
+ if (fluid_rtkit_make_realtime(0, prio_level) == 0) {
+ return;
+ }
+#endif
+ FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority");
+ }
+}
+
+#ifdef FPE_CHECK
+
+/***************************************************************
+ *
+ * Floating point exceptions
+ *
+ * The floating point exception functions were taken from Ircam's
+ * jMax source code. http://www.ircam.fr/jmax
+ *
+ * FIXME: check in config for i386 machine
+ *
+ * Currently not used. I leave the code here in case we want to pick
+ * this up again some time later.
+ */
+
+/* Exception flags */
+#define _FPU_STATUS_IE 0x001 /* Invalid Operation */
+#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */
+#define _FPU_STATUS_ZE 0x004 /* Zero Divide */
+#define _FPU_STATUS_OE 0x008 /* Overflow */
+#define _FPU_STATUS_UE 0x010 /* Underflow */
+#define _FPU_STATUS_PE 0x020 /* Precision */
+#define _FPU_STATUS_SF 0x040 /* Stack Fault */
+#define _FPU_STATUS_ES 0x080 /* Error Summary Status */
+
+/* Macros for accessing the FPU status word. */
+
+/* get the FPU status */
+#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw))
+
+/* clear the FPU status */
+#define _FPU_CLR_SW() __asm__ ("fnclex" : : )
+
+/* Purpose:
+ * Checks, if the floating point unit has produced an exception, print a message
+ * if so and clear the exception.
+ */
+unsigned int fluid_check_fpe_i386(char* explanation)
+{
+ unsigned int s;
+
+ _FPU_GET_SW(s);
+ _FPU_CLR_SW();
+
+ s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;
+
+ if (s)
+ {
+ FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation,
+ (s & _FPU_STATUS_IE) ? "Invalid operation " : "",
+ (s & _FPU_STATUS_DE) ? "Denormal number " : "",
+ (s & _FPU_STATUS_ZE) ? "Zero divide " : "",
+ (s & _FPU_STATUS_OE) ? "Overflow " : "",
+ (s & _FPU_STATUS_UE) ? "Underflow " : "");
+ }
+
+ return s;
+}
+
+/* Purpose:
+ * Clear floating point exception.
+ */
+void fluid_clear_fpe_i386 (void)
+{
+ _FPU_CLR_SW();
+}
+
+#endif // ifdef FPE_CHECK
+
+
+#endif // #else (its POSIX)
+
+
+/***************************************************************
+ *
+ * Profiling (Linux, i586 only)
+ *
+ */
+
+#if WITH_PROFILING
+
+fluid_profile_data_t fluid_profile_data[] =
+{
+ { FLUID_PROF_WRITE, "fluid_synth_write_* ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0},
+ { FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0}
+};
+
+
+void fluid_profiling_print(void)
+{
+ int i;
+
+ printf("fluid_profiling_print\n");
+
+ FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
+
+ for (i = 0; i < FLUID_PROF_LAST; i++) {
+ if (fluid_profile_data[i].count > 0) {
+ FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",
+ fluid_profile_data[i].description,
+ fluid_profile_data[i].min,
+ fluid_profile_data[i].total / fluid_profile_data[i].count,
+ fluid_profile_data[i].max);
+ } else {
+ FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description);
+ }
+ }
+}
+
+
+#endif /* WITH_PROFILING */
+
+
+
+/***************************************************************
+ *
+ * Threads
+ *
+ */
+
+#if OLD_GLIB_THREAD_API
+
+/* Rather than inline this one, we just declare it as a function, to prevent
+ * GCC warning about inline failure. */
+fluid_cond_t *
+new_fluid_cond (void)
+{
+ if (!g_thread_supported ()) g_thread_init (NULL);
+ return g_cond_new ();
+}
+
+#endif
+
+static gpointer
+fluid_thread_high_prio (gpointer data)
+{
+ fluid_thread_info_t *info = data;
+
+ fluid_thread_self_set_prio (info->prio_level);
+
+ info->func (info->data);
+ FLUID_FREE (info);
+
+ return NULL;
+}
+
+/**
+ * Create a new thread.
+ * @param func Function to execute in new thread context
+ * @param data User defined data to pass to func
+ * @param prio_level Priority level. If greater than 0 then high priority scheduling will
+ * be used, with the given priority level (used by pthreads only). 0 uses normal scheduling.
+ * @param detach If TRUE, 'join' does not work and the thread destroys itself when finished.
+ * @return New thread pointer or NULL on error
+ */
+fluid_thread_t *
+new_fluid_thread (const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach)
+{
+ GThread *thread;
+ fluid_thread_info_t *info;
+ GError *err = NULL;
+
+ g_return_val_if_fail (func != NULL, NULL);
+
+#if OLD_GLIB_THREAD_API
+ /* Make sure g_thread_init has been called.
+ * FIXME - Probably not a good idea in a shared library,
+ * but what can we do *and* remain backwards compatible? */
+ if (!g_thread_supported ()) g_thread_init (NULL);
+#endif
+
+ if (prio_level > 0)
+ {
+ info = FLUID_NEW (fluid_thread_info_t);
+
+ if (!info)
+ {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ info->func = func;
+ info->data = data;
+ info->prio_level = prio_level;
+#if NEW_GLIB_THREAD_API
+ thread = g_thread_try_new (name, fluid_thread_high_prio, info, &err);
+#else
+ thread = g_thread_create (fluid_thread_high_prio, info, detach == FALSE, &err);
+#endif
+ }
+#if NEW_GLIB_THREAD_API
+ else thread = g_thread_try_new (name, (GThreadFunc)func, data, &err);
+#else
+ else thread = g_thread_create ((GThreadFunc)func, data, detach == FALSE, &err);
+#endif
+
+ if (!thread)
+ {
+ FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s",
+ fluid_gerror_message (err));
+ g_clear_error (&err);
+ return NULL;
+ }
+
+#if NEW_GLIB_THREAD_API
+ if (detach) g_thread_unref (thread); // Release thread reference, if caller wants to detach
+#endif
+
+ return thread;
+}
+
+/**
+ * Frees data associated with a thread (does not actually stop thread).
+ * @param thread Thread to free
+ */
+void
+delete_fluid_thread(fluid_thread_t* thread)
+{
+ /* Threads free themselves when they quit, nothing to do */
+}
+
+/**
+ * Join a thread (wait for it to terminate).
+ * @param thread Thread to join
+ * @return FLUID_OK
+ */
+int
+fluid_thread_join(fluid_thread_t* thread)
+{
+ g_thread_join (thread);
+
+ return FLUID_OK;
+}
+
+
+static void
+fluid_timer_run (void *data)
+{
+ fluid_timer_t *timer;
+ int count = 0;
+ int cont;
+ long start;
+ long delay;
+
+ timer = (fluid_timer_t *)data;
+
+ /* keep track of the start time for absolute positioning */
+ start = fluid_curtime ();
+
+ while (timer->cont)
+ {
+ cont = (*timer->callback)(timer->data, fluid_curtime() - start);
+
+ count++;
+ if (!cont) break;
+
+ /* to avoid incremental time errors, calculate the delay between
+ two callbacks bringing in the "absolute" time (count *
+ timer->msec) */
+ delay = (count * timer->msec) - (fluid_curtime() - start);
+ if (delay > 0) g_usleep (delay * 1000);
+ }
+
+ FLUID_LOG (FLUID_DBG, "Timer thread finished");
+
+ if (timer->auto_destroy)
+ FLUID_FREE (timer);
+
+ return;
+}
+
+fluid_timer_t*
+new_fluid_timer (int msec, fluid_timer_callback_t callback, void* data,
+ int new_thread, int auto_destroy, int high_priority)
+{
+ fluid_timer_t *timer;
+
+ timer = FLUID_NEW (fluid_timer_t);
+
+ if (timer == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+
+ timer->msec = msec;
+ timer->callback = callback;
+ timer->data = data;
+ timer->cont = TRUE ;
+ timer->thread = NULL;
+ timer->auto_destroy = auto_destroy;
+
+ if (new_thread)
+ {
+ timer->thread = new_fluid_thread ("timer", fluid_timer_run, timer, high_priority
+ ? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE);
+ if (!timer->thread)
+ {
+ FLUID_FREE (timer);
+ return NULL;
+ }
+ }
+ else fluid_timer_run (timer); /* Run directly, instead of as a separate thread */
+
+ return timer;
+}
+
+int
+delete_fluid_timer (fluid_timer_t *timer)
+{
+ int auto_destroy = timer->auto_destroy;
+
+ timer->cont = 0;
+ fluid_timer_join (timer);
+
+ /* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */
+
+ if (!auto_destroy) FLUID_FREE (timer);
+
+ return FLUID_OK;
+}
+
+int
+fluid_timer_join (fluid_timer_t *timer)
+{
+ int auto_destroy;
+
+ if (timer->thread)
+ {
+ auto_destroy = timer->auto_destroy;
+ fluid_thread_join (timer->thread);
+
+ if (!auto_destroy) timer->thread = NULL;
+ }
+
+ return FLUID_OK;
+}
+
+
+/***************************************************************
+ *
+ * Sockets and I/O
+ *
+ */
+
+/**
+ * Get standard in stream handle.
+ * @return Standard in stream.
+ */
+fluid_istream_t
+fluid_get_stdin (void)
+{
+ return STDIN_FILENO;
+}
+
+/**
+ * Get standard output stream handle.
+ * @return Standard out stream.
+ */
+fluid_ostream_t
+fluid_get_stdout (void)
+{
+ return STDOUT_FILENO;
+}
+
+/**
+ * Read a line from an input stream.
+ * @return 0 if end-of-stream, -1 if error, non zero otherwise
+ */
+int
+fluid_istream_readline (fluid_istream_t in, fluid_ostream_t out, char* prompt,
+ char* buf, int len)
+{
+#if WITH_READLINE
+ if (in == fluid_get_stdin ())
+ {
+ char *line;
+
+ line = readline (prompt);
+
+ if (line == NULL)
+ return -1;
+
+ snprintf(buf, len, "%s", line);
+ buf[len - 1] = 0;
+
+ free(line);
+ return 1;
+ }
+ else
+#endif
+ {
+ fluid_ostream_printf (out, "%s", prompt);
+ return fluid_istream_gets (in, buf, len);
+ }
+}
+
+/**
+ * Reads a line from an input stream (socket).
+ * @param in The input socket
+ * @param buf Buffer to store data to
+ * @param len Maximum length to store to buf
+ * @return 1 if a line was read, 0 on end of stream, -1 on error
+ */
+static int
+fluid_istream_gets (fluid_istream_t in, char* buf, int len)
+{
+ char c;
+ int n;
+
+ buf[len - 1] = 0;
+
+ while (--len > 0)
+ {
+#ifndef WIN32
+ n = read(in, &c, 1);
+ if (n == -1) return -1;
+#else
+ /* Handle read differently depending on if its a socket or file descriptor */
+ if (!(in & WIN32_SOCKET_FLAG))
+ {
+ n = read(in, &c, 1);
+ if (n == -1) return -1;
+ }
+ else
+ {
+ n = recv(in & ~WIN32_SOCKET_FLAG, &c, 1, 0);
+ if (n == SOCKET_ERROR) return -1;
+ }
+#endif
+
+ if (n == 0)
+ {
+ *buf++ = 0;
+ return 0;
+ }
+
+ if ((c == '\n'))
+ {
+ *buf++ = 0;
+ return 1;
+ }
+
+ /* Store all characters excluding CR */
+ if (c != '\r') *buf++ = c;
+ }
+
+ return -1;
+}
+
+/**
+ * Send a printf style string with arguments to an output stream (socket).
+ * @param out Output stream
+ * @param format printf style format string
+ * @param ... Arguments for the printf format string
+ * @return Number of bytes written or -1 on error
+ */
+int
+fluid_ostream_printf (fluid_ostream_t out, char* format, ...)
+{
+ char buf[4096];
+ va_list args;
+ int len;
+
+ va_start (args, format);
+ len = vsnprintf (buf, 4095, format, args);
+ va_end (args);
+
+ if (len == 0)
+ {
+ return 0;
+ }
+
+ if (len < 0)
+ {
+ printf("fluid_ostream_printf: buffer overflow");
+ return -1;
+ }
+
+ buf[4095] = 0;
+
+#ifndef WIN32
+ return write (out, buf, strlen (buf));
+#else
+ {
+ int retval;
+
+ /* Handle write differently depending on if its a socket or file descriptor */
+ if (!(out & WIN32_SOCKET_FLAG))
+ return write(out, buf, strlen (buf));
+
+ /* Socket */
+ retval = send (out & ~WIN32_SOCKET_FLAG, buf, strlen (buf), 0);
+
+ return retval != SOCKET_ERROR ? retval : -1;
+ }
+#endif
+}
+
+int fluid_server_socket_join(fluid_server_socket_t *server_socket)
+{
+ return fluid_thread_join (server_socket->thread);
+}
+
+
+#ifndef WIN32 // Not win32?
+
+#define SOCKET_ERROR -1
+
+fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
+{
+ return sock;
+}
+
+fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
+{
+ return sock;
+}
+
+void fluid_socket_close(fluid_socket_t sock)
+{
+ if (sock != INVALID_SOCKET)
+ close (sock);
+}
+
+static void
+fluid_server_socket_run (void *data)
+{
+ fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+ fluid_socket_t client_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+ char straddr[INET6_ADDRSTRLEN];
+#else
+ struct sockaddr_in addr;
+ char straddr[INET_ADDRSTRLEN];
+#endif
+ socklen_t addrlen = sizeof (addr);
+ int retval;
+ FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
+
+ FLUID_LOG (FLUID_DBG, "Server listening for connections");
+
+ while (server_socket->cont)
+ {
+ client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+
+ FLUID_LOG (FLUID_DBG, "New client connection");
+
+ if (client_socket == INVALID_SOCKET)
+ {
+ if (server_socket->cont)
+ FLUID_LOG(FLUID_ERR, "Failed to accept connection");
+
+ server_socket->cont = 0;
+ return;
+ } else {
+#ifdef IPV6
+ inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+ inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
+ retval = server_socket->func (server_socket->data, client_socket,
+ straddr);
+
+ if (retval != 0)
+ fluid_socket_close(client_socket);
+ }
+ }
+
+ FLUID_LOG(FLUID_DBG, "Server closing");
+}
+
+fluid_server_socket_t*
+new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
+{
+ fluid_server_socket_t* server_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+#else
+ struct sockaddr_in addr;
+#endif
+ fluid_socket_t sock;
+
+ g_return_val_if_fail (func != NULL, NULL);
+#ifdef IPV6
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket");
+ return NULL;
+ }
+
+ FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in6));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = in6addr_any;
+ addr.sin6_port = htons(port);
+#else
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ FLUID_LOG(FLUID_ERR, "Failed to create server socket");
+ return NULL;
+ }
+
+ FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+#endif
+ if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+ FLUID_LOG(FLUID_ERR, "Failed to bind server socket");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ if (listen(sock, 10) == SOCKET_ERROR) {
+ FLUID_LOG(FLUID_ERR, "Failed listen on server socket");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ server_socket = FLUID_NEW(fluid_server_socket_t);
+ if (server_socket == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ server_socket->socket = sock;
+ server_socket->func = func;
+ server_socket->data = data;
+ server_socket->cont = 1;
+
+ server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+ 0, FALSE);
+ if (server_socket->thread == NULL) {
+ FLUID_FREE(server_socket);
+ fluid_socket_close(sock);
+ return NULL;
+ }
+
+ return server_socket;
+}
+
+int delete_fluid_server_socket(fluid_server_socket_t* server_socket)
+{
+ server_socket->cont = 0;
+ if (server_socket->socket != INVALID_SOCKET) {
+ fluid_socket_close(server_socket->socket);
+ }
+ if (server_socket->thread) {
+ delete_fluid_thread(server_socket->thread);
+ }
+ FLUID_FREE(server_socket);
+ return FLUID_OK;
+}
+
+
+#else // Win32 is "special"
+
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+fluid_istream_t fluid_socket_get_istream (fluid_socket_t sock)
+{
+ return sock | WIN32_SOCKET_FLAG;
+}
+
+fluid_ostream_t fluid_socket_get_ostream (fluid_socket_t sock)
+{
+ return sock | WIN32_SOCKET_FLAG;
+}
+
+void fluid_socket_close (fluid_socket_t sock)
+{
+ if (sock != INVALID_SOCKET)
+ closesocket (sock);
+}
+
+static void fluid_server_socket_run (void *data)
+{
+ fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;
+ fluid_socket_t client_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+ char straddr[INET6_ADDRSTRLEN];
+#else
+ struct sockaddr_in addr;
+ char straddr[INET_ADDRSTRLEN];
+#endif
+ socklen_t addrlen = sizeof (addr);
+ int r;
+ FLUID_MEMSET((char *)&addr, 0, sizeof(addr));
+
+ FLUID_LOG(FLUID_DBG, "Server listening for connections");
+
+ while (server_socket->cont)
+ {
+ client_socket = accept (server_socket->socket, (struct sockaddr *)&addr, &addrlen);
+
+ FLUID_LOG (FLUID_DBG, "New client connection");
+
+ if (client_socket == INVALID_SOCKET)
+ {
+ if (server_socket->cont)
+ FLUID_LOG (FLUID_ERR, "Failed to accept connection: %ld", WSAGetLastError ());
+
+ server_socket->cont = 0;
+ return;
+ }
+ else
+ {
+#ifdef IPV6
+ inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));
+#else
+ inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));
+#endif
+ r = server_socket->func (server_socket->data, client_socket,
+ straddr);
+ if (r != 0)
+ fluid_socket_close (client_socket);
+ }
+ }
+
+ FLUID_LOG (FLUID_DBG, "Server closing");
+}
+
+fluid_server_socket_t*
+new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
+{
+ fluid_server_socket_t* server_socket;
+#ifdef IPV6
+ struct sockaddr_in6 addr;
+#else
+ struct sockaddr_in addr;
+#endif
+
+ fluid_socket_t sock;
+ WSADATA wsaData;
+ int retval;
+
+ g_return_val_if_fail (func != NULL, NULL);
+
+ // Win32 requires initialization of winsock
+ retval = WSAStartup (MAKEWORD (2,2), &wsaData);
+
+ if (retval != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", retval);
+ return NULL;
+ }
+#ifdef IPV6
+ sock = socket (AF_INET6, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
+ WSACleanup ();
+ return NULL;
+ }
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons (port);
+ addr.sin6_addr = in6addr_any;
+#else
+
+ sock = socket (AF_INET, SOCK_STREAM, 0);
+
+ if (sock == INVALID_SOCKET)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to create server socket: %ld", WSAGetLastError ());
+ WSACleanup ();
+ return NULL;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons (port);
+ addr.sin_addr.s_addr = htonl (INADDR_ANY);
+#endif
+ retval = bind (sock, (struct sockaddr *)&addr, sizeof (addr));
+
+ if (retval == SOCKET_ERROR)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to bind server socket: %ld", WSAGetLastError ());
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ if (listen (sock, SOMAXCONN) == SOCKET_ERROR)
+ {
+ FLUID_LOG (FLUID_ERR, "Failed to listen on server socket: %ld", WSAGetLastError ());
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ server_socket = FLUID_NEW (fluid_server_socket_t);
+
+ if (server_socket == NULL)
+ {
+ FLUID_LOG (FLUID_ERR, "Out of memory");
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ server_socket->socket = sock;
+ server_socket->func = func;
+ server_socket->data = data;
+ server_socket->cont = 1;
+
+ server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,
+ 0, FALSE);
+ if (server_socket->thread == NULL)
+ {
+ FLUID_FREE (server_socket);
+ fluid_socket_close (sock);
+ WSACleanup ();
+ return NULL;
+ }
+
+ return server_socket;
+}
+
+int delete_fluid_server_socket(fluid_server_socket_t *server_socket)
+{
+ server_socket->cont = 0;
+
+ if (server_socket->socket != INVALID_SOCKET)
+ fluid_socket_close (server_socket->socket);
+
+ if (server_socket->thread)
+ delete_fluid_thread (server_socket->thread);
+
+ FLUID_FREE (server_socket);
+
+ WSACleanup (); // Should be called the same number of times as WSAStartup
+
+ return FLUID_OK;
+}
+
+#endif