diff options
author | Robin Gareus <robin@gareus.org> | 2016-08-23 18:50:54 +0200 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2016-08-23 22:21:03 +0200 |
commit | ac05f050238d4b51c7b6a042e25cee59dc34da80 (patch) | |
tree | 868cbbe114171e9907ecb3f049c86257039fdb74 /libs/fluidsynth/src/fluid_sys.c | |
parent | ac8617017ae94bf17cdd5b1f6e38e1434bcb2c54 (diff) |
Import libfluidsynth into the Ardour codebase
Diffstat (limited to 'libs/fluidsynth/src/fluid_sys.c')
-rw-r--r-- | libs/fluidsynth/src/fluid_sys.c | 1296 |
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 |