changeset 1726:0a689ca43d7a

Automated merge with ssh://hg.atheme.org//hg/audacious-plugins
author William Pitcock <nenolod@atheme.org>
date Tue, 18 Sep 2007 09:26:05 -0500
parents f9856ca98943 (current diff) 655949f889ec (diff)
children 9d6de95dd7ed
files
diffstat 11 files changed, 1716 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Tue Sep 18 09:25:56 2007 -0500
+++ b/configure.ac	Tue Sep 18 09:26:05 2007 -0500
@@ -447,6 +447,27 @@
     GENERAL_PLUGINS="$GENERAL_PLUGINS hotkey"
 fi
 
+dnl *** Gnome Shortcuts Plugin
+
+AC_ARG_ENABLE(gnomeshortcuts,
+    [  --disable-gnomeshortcuts        disable gnome shortcuts (default=enabled)],
+    [enable_gnomeshortcuts=$enableval],
+    [enable_gnomeshortcuts="yes"]
+)
+
+if test "x$enable_gnomeshortcuts" = "xyes"; then
+    have_gnomeshortcuts="yes"
+    PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.60 dbus-glib-1 >= 0.60],,
+         [AC_MSG_ERROR([Cannot find dbus-glib >= 0.60 for gnomeshortcuts plugin])])
+else
+    AC_MSG_RESULT([*** Gnome Shortcuts Plugin disabled per user request ***])
+    have_gnomeshortcuts="no"
+fi
+
+if test "x$have_gnomeshortcuts" = "xyes"; then
+    GENERAL_PLUGINS="$GENERAL_PLUGINS gnomeshortcuts"
+fi
+
 dnl *** Status Icon plugin (for X11 only)
 
 AC_ARG_ENABLE(statusicon,
@@ -1253,6 +1274,24 @@
 
 dnl *** End of Scrobbler checks ***
 
+dnl *** neon http plugin ***
+
+AC_ARG_ENABLE(neon,
+[  --enable-neon           enable neon support (experimental).  (default=disabled)],
+[have_neon=$enableval],
+[have_neon=no])
+
+if test "x$have_neon" = "xyes"; then
+    PKG_CHECK_MODULES(NEON, [neon >= 0.27], [TRANSPORT_PLUGINS="$TRANSPORT_PLUGINS neon" ], [have_neon="no"])
+    NEON_LIBS=`pkg-config --libs neon`
+    NEON_CFLAGS=`pkg-config --cflags neon`
+else
+    have_neon=no
+fi
+AC_SUBST(NEON_LIBS)
+AC_SUBST(NEON_CLFAGS)
+
+
 dnl *** MMS
 
 AC_ARG_ENABLE(mms,
@@ -1563,6 +1602,7 @@
 echo "    -> X Composite support:               $have_aosd_xcomp"
 echo "  Control via event device (evdev-plug):  $have_evdevplug"
 echo "  Global Hotkey Plugin:                   $have_hotkey"
+echo "  Gnome Shortcuts Plugin:                 $have_gnomeshortcuts"
 echo "  LIRC:                                   $have_lirc"
 echo "  AudioScrobbler Client:                  $scrobbler"
 echo "  Upload to MTP device:                   $have_mtp_up"
@@ -1589,6 +1629,7 @@
 echo "  ---------"
 echo "  stdio transport:                        yes"
 echo "  curl-based http/https:                  $have_curl"
+echo "  neon-based http/https:                  $have_neon"
 echo "  libmms-based mms:                       $have_mms"
 echo "  lastfm transport:                       $have_lastfm"
 echo
--- a/mk/rules.mk.in	Tue Sep 18 09:25:56 2007 -0500
+++ b/mk/rules.mk.in	Tue Sep 18 09:26:05 2007 -0500
@@ -332,6 +332,8 @@
 CONFIGDB_BACKEND ?= @CONFIGDB_BACKEND@
 CURL_CFLAGS ?= @CURL_CFLAGS@
 CURL_LIBS ?= @CURL_LIBS@
+NEON_CFLAGS ?= @NEON_CFLAGS@
+NEON_LIBS ?= @NEON_LIBS@
 CHARDET_LIBS ?= @CHARDET_LIBS@
 SUBDIR_GUESS ?= @SUBDIR_GUESS@
 LIBNOTIFY_CFLAGS ?= @LIBNOTIFY_CFLAGS@
--- a/src/arts/arts.c	Tue Sep 18 09:25:56 2007 -0500
+++ b/src/arts/arts.c	Tue Sep 18 09:26:05 2007 -0500
@@ -32,25 +32,23 @@
 
 OutputPlugin arts_op =
 {
-	NULL,
-	NULL,
-	"aRts Output Plugin",
-	artsxmms_init,
-	NULL,
-	about,
-	artsxmms_configure,
-	artsxmms_get_volume,
-	artsxmms_set_volume,
-	artsxmms_open,
-	artsxmms_write,
-	artsxmms_close,
-	artsxmms_flush,
-	artsxmms_pause,
-	artsxmms_free,
-	artsxmms_playing,
-	artsxmms_get_output_time,
-	artsxmms_get_written_time,
-	artsxmms_tell_audio
+	.description = "aRts Output Plugin",
+	.init = artsxmms_init,
+	.cleanup = NULL,
+	.about = about,
+	.configure = artsxmms_configure,
+	.get_volume = artsxmms_get_volume,
+	.set_volume = artsxmms_set_volume,
+	.open_audio = artsxmms_open,
+	.write_audio = artsxmms_write,
+	.close_audio = artsxmms_close,
+	.flush = artsxmms_flush,
+	.pause = artsxmms_pause,
+	.buffer_free = artsxmms_free,
+	.buffer_playing = artsxmms_playing,
+	.output_time = artsxmms_get_output_time,
+	.written_time = artsxmms_get_written_time,
+	.tell_audio = artsxmms_tell_audio
 };
 
 OutputPlugin *arts_oplist[] = { &arts_op, NULL };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gnomeshortcuts/Makefile	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,16 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = libgnomeshortcuts$(SHARED_SUFFIX)
+
+LIBDIR = $(plugindir)/$(GENERAL_PLUGIN_DIR)
+
+LIBADD = $(GLIB_LIBS) $(DBUS_LIBS)
+SOURCES = gnomeshortcuts.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \
+	-I../../intl -I../.. -I..
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gnomeshortcuts/gnomeshortcuts.c	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,307 @@
+/* -*- Mode: C; indent-tabs: t; c-basic-offset: 9; tab-width: 9 -*- */
+/*
+ *  This file is part of audacious-gnome-shortcut plugin for audacious
+ *
+ *  Copyright (c) 2007        Sascha Hlusiak <contact@saschahlusiak.de>
+ *  Name: plugin.c
+ *  Description: plugin.c
+ * 
+ *  audacious-gnome-shortcut 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.
+ *
+ *  audacious-gnome-shortcut 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 audacious-gnome-shortcut; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-bindings.h>
+#include <glib-object.h>
+
+#include <audacious/plugin.h>
+#include <audacious/auddrct.h>
+
+#include <audacious/i18n.h>
+#include <audacious/util.h>
+
+
+static void init (void);
+static void about (void);
+static void cleanup (void);
+void gnome_remote_init();
+void gnome_remote_uninit();
+
+static gboolean loaded = FALSE;
+static DBusGProxy *media_player_keys_proxy = NULL;
+
+static GeneralPlugin audaciousgnomeshortcuts =
+{
+	.description = "Gnome Shortcuts",
+	.init = init,
+	.about = about,
+	.cleanup = cleanup
+};
+
+GeneralPlugin *gnomeshortcuts_gplist[] = { &audaciousgnomeshortcuts, NULL };
+SIMPLE_GENERAL_PLUGIN(gnomeshortcuts, gnomeshortcuts_gplist);
+
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+
+
+static void
+hotkey_marshal_VOID__STRING_STRING (GClosure     *closure,
+                                   GValue       *return_value,
+                                   guint         n_param_values,
+                                   const GValue *param_values,
+                                   gpointer      invocation_hint,
+                                   gpointer      marshal_data)
+{
+	typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer     data1,
+							gpointer     arg_1,
+							gpointer     arg_2);
+	register GMarshalFunc_VOID__STRING_STRING callback;
+	register GCClosure *cc = (GCClosure*) closure;
+	register gpointer data1;
+	
+	g_return_if_fail (n_param_values == 3);
+	
+	if (G_CCLOSURE_SWAP_DATA (closure))
+	{
+		data1 = closure->data;
+	} else {
+		data1 = g_value_peek_pointer (param_values + 0);
+	}
+	callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+	
+	callback (data1,
+		g_marshal_value_peek_string (param_values + 1),
+		g_marshal_value_peek_string (param_values + 2));
+}
+
+static void
+on_media_player_key_pressed (DBusGProxy *proxy, const gchar *application, const gchar *key)
+{
+	if (strcmp ("Audacious", application) == 0) {
+		gint current_volume, old_volume;
+		static gint volume_static = 0;
+		gboolean play, mute;
+		
+		/* playing or not */
+		play = audacious_drct_is_playing ();
+		
+		/* get current volume */
+		audacious_drct_get_volume_main (&current_volume);
+		old_volume = current_volume;
+		if (current_volume)
+		{
+			/* volume is not mute */
+			mute = FALSE;
+		} else {
+			/* volume is mute */
+			mute = TRUE;
+		}
+			
+		/* mute the playback */
+		if (strcmp ("Mute", key) == 0)
+		{
+			if (!mute)
+			{
+				volume_static = current_volume;
+				audacious_drct_set_main_volume (0);
+				mute = TRUE;
+			} else {
+				audacious_drct_set_main_volume (volume_static);
+				mute = FALSE;
+			}
+			return;
+		}
+		
+		/* decreace volume */
+/*		if ((keycode == plugin_cfg.vol_down) && (state == plugin_cfg.vol_down_mask))
+		{
+			if (mute)
+			{
+				current_volume = old_volume;
+				old_volume = 0;
+				mute = FALSE;
+			}
+				
+			if ((current_volume -= plugin_cfg.vol_decrement) < 0)
+			{
+				current_volume = 0;
+			}
+				
+			if (current_volume != old_volume)
+			{
+				xmms_remote_set_main_volume (audacioushotkey.xmms_session,
+							current_volume);
+			}
+				
+			old_volume = current_volume;
+			return TRUE;
+		}*/
+		
+		/* increase volume */
+/*		if ((keycode == plugin_cfg.vol_up) && (state == plugin_cfg.vol_up_mask))
+		{
+			if (mute)
+			{
+				current_volume = old_volume;
+				old_volume = 0;
+				mute = FALSE;
+			}
+				
+			if ((current_volume += plugin_cfg.vol_increment) > 100)
+			{
+				current_volume = 100;
+			}
+				
+			if (current_volume != old_volume)
+			{
+				xmms_remote_set_main_volume (audacioushotkey.xmms_session,
+							current_volume);
+			}
+				
+			old_volume = current_volume;
+			return TRUE;
+		}*/
+		
+		/* play */
+		if (strcmp ("Play", key) == 0)
+		{
+			if (!play)
+			{
+				audacious_drct_play ();
+			} else {
+				audacious_drct_pause ();
+			}
+			return;
+		}
+	
+		/* pause */
+		if (strcmp ("Pause", key) == 0)
+		{
+			if (!play) audacious_drct_play ();
+			else audacious_drct_pause ();
+	
+			return;
+		}
+		
+		/* stop */
+		if (strcmp ("Stop", key) == 0)
+		{
+			audacious_drct_stop ();
+			return;
+		}
+		
+		/* prev track */	
+		if (strcmp ("Previous", key) == 0)
+		{
+			audacious_drct_playlist_prev ();
+			return;
+		}
+		
+		/* next track */
+		if (strcmp ("Next", key) == 0)
+		{
+			audacious_drct_playlist_next ();
+			return;
+		}
+	}
+}
+
+void gnome_remote_uninit ()
+{
+	GError *error = NULL;
+	if (media_player_keys_proxy == NULL) return;
+
+	dbus_g_proxy_disconnect_signal (media_player_keys_proxy, "MediaPlayerKeyPressed",
+			G_CALLBACK (on_media_player_key_pressed), NULL);
+
+	dbus_g_proxy_call (media_player_keys_proxy,
+				"ReleaseMediaPlayerKeys", &error,
+				G_TYPE_STRING, "Audacious",
+				G_TYPE_INVALID, G_TYPE_INVALID);
+	if (error != NULL) {
+		g_warning ("Could not release media player keys: %s", error->message);
+		g_error_free (error);
+	}
+	g_object_unref(media_player_keys_proxy);
+	media_player_keys_proxy = NULL;
+}
+
+void gnome_remote_init ()
+{
+	DBusGConnection *bus;
+	GError *error = NULL;
+	dbus_g_thread_init();
+
+	bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+	if ((bus == NULL) || error) {
+		g_warning ("Error connecting to DBus: %s", error->message);
+	} else {
+		media_player_keys_proxy = dbus_g_proxy_new_for_name (bus,
+			"org.gnome.SettingsDaemon", 
+			"/org/gnome/SettingsDaemon",
+			"org.gnome.SettingsDaemon");
+		if (media_player_keys_proxy == NULL) return;
+
+		dbus_g_proxy_call (media_player_keys_proxy,
+				"GrabMediaPlayerKeys", &error,
+				G_TYPE_STRING, "Audacious",
+				G_TYPE_UINT, 0,
+				G_TYPE_INVALID,
+				G_TYPE_INVALID);
+		if (error != NULL) {
+			g_warning ("Could not release media player keys: %s", error->message);
+			g_error_free (error);
+		}
+
+		dbus_g_object_register_marshaller (hotkey_marshal_VOID__STRING_STRING,
+				G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+
+		dbus_g_proxy_add_signal (media_player_keys_proxy, "MediaPlayerKeyPressed",
+				G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+
+		dbus_g_proxy_connect_signal (media_player_keys_proxy, "MediaPlayerKeyPressed",
+				G_CALLBACK (on_media_player_key_pressed), NULL, NULL);
+	}
+}
+
+static void about (void)
+{
+	static GtkWidget *dialog;
+
+	dialog = audacious_info_dialog (_("About Gnome Shortcut Plugin"),
+				_("Gnome Shortcut Plugin\n"
+				"Let's you control the player with Gnome's shortcuts.\n\n"
+				"Copyright (C) 2007 Sascha Hlusiak <contact@saschahlusiak.de>\n\n"
+                         	),
+                         	_("OK"), TRUE, NULL, NULL);
+
+	gtk_signal_connect(GTK_OBJECT(dialog), "destroy",
+			   GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dialog);						
+}
+
+static void init (void)
+{
+	gnome_remote_init();
+	loaded = TRUE;
+}
+
+static void cleanup (void)
+{
+	if (!loaded) return;
+	gnome_remote_uninit();
+	loaded = FALSE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/neon/Makefile	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,24 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+SUBDIRS = 
+
+OBJECTIVE_LIBS = libneonsrc$(SHARED_SUFFIX)
+
+LIBDIR = $(plugindir)/$(TRANSPORT_PLUGIN_DIR)
+
+LIBADD += $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(NEON_LIBS)
+
+SOURCES = neon.c rb.c
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(BEEP_DEFINES) $(NEON_CFLAGS) -I../../intl -I../..
+
+#CFLAGS += -Wpointer-arith -Wimplicit -Wnested-externs -Wcast-align \
+#-Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \
+#-Wmissing-declarations -W -Wno-unused -Wshadow -Wmissing-noreturn \
+#-Wundef -Wpacked -Wnested-externs -Wbad-function-cast -Wredundant-decls \
+#-Wfloat-equal -Wdisabled-optimization -pedantic -std=c99
+
+OBJECTS = ${SOURCES:.c=.o}
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/neon/debug.h	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2005 Ralf Ertzinger
+ *
+ *  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.
+ */
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdio.h>
+
+#define _ENTER _DEBUG("enter")
+#define _LEAVE _DEBUG("leave"); return
+#define _MESSAGE(tag, string, ...) do { fprintf(stderr, "%s: libneonsrc.so: %s:%d (%s): " string "\n", \
+    tag, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0)
+
+#define _ERROR(...) _MESSAGE("ERROR", __VA_ARGS__)
+
+#ifdef NEON_DEBUG
+#define _DEBUG(...) _MESSAGE("DEBUG",  __VA_ARGS__)
+#else
+#define _DEBUG(...) {}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/neon/neon.c	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,883 @@
+#include <audacious/vfs.h>
+#include <audacious/plugin.h>
+
+#include <ne_socket.h>
+#include <ne_utils.h>
+#include <ne_redirect.h>
+#include <ne_request.h>
+
+#include "debug.h"
+#include "neon.h"
+#include "rb.h"
+
+#define NBUFSIZ (128u*1024u)
+#define NETBLKSIZ (4096u)
+
+DECLARE_PLUGIN(neon, init, fini)
+
+VFSConstructor neon_http_const = {
+    "http://",
+    neon_vfs_fopen_impl,
+    neon_vfs_fclose_impl,
+    neon_vfs_fread_impl,
+    neon_vfs_fwrite_impl,
+    neon_vfs_getc_impl,
+    neon_vfs_ungetc_impl,
+    neon_vfs_fseek_impl,
+    neon_vfs_rewind_impl,
+    neon_vfs_ftell_impl,
+    neon_vfs_feof_impl,
+    neon_vfs_truncate_impl,
+    neon_vfs_fsize_impl,
+    neon_vfs_metadata_impl
+};
+
+/*
+ * ========
+ */
+
+static struct neon_handle* handle_init(void) {
+
+    struct neon_handle* h;
+
+    _ENTER;
+
+    if (NULL == (h = malloc(sizeof(struct neon_handle)))) {
+        _ERROR("Could not allocate memory for handle");
+        _LEAVE NULL;
+    }
+
+    if (0 != init_rb(&(h->rb), NBUFSIZ)) {
+        _ERROR("Could not initialize buffer");
+        free(h);
+        _LEAVE NULL;
+    }
+
+    h->url = NULL;
+    h->purl = &purl;
+    memset(h->purl, 0, sizeof(ne_uri));
+    h->session = NULL;
+    h->request = NULL;
+    h->redircount = 0;
+    h->pos = 0;
+    h->content_length = -1;
+    h->can_ranges = FALSE;
+    h->reader = NULL;
+    h->reader_status.mutex = g_mutex_new();
+    h->reader_status.cond = g_cond_new();
+    h->reader_status.reading = FALSE;
+    h->reader_status.status = NEON_READER_INIT;
+    h->eof = FALSE;
+
+    _LEAVE h;
+}
+
+/*
+ * -----
+ */
+
+static void handle_free(struct neon_handle* h) {
+
+    _ENTER;
+
+    ne_uri_free(h->purl);
+    destroy_rb(&h->rb);
+    free(h);
+
+    _LEAVE;
+}
+
+/*
+ * ----
+ */
+
+static void init(void) {
+
+    int ret;
+
+    _ENTER;
+
+    if (0 != (ret = ne_sock_init())) {
+        _ERROR("Could not initialize neon library: %d\n", ret);
+        _LEAVE;
+    }
+
+    vfs_register_transport(&neon_http_const);
+
+    if (0 != ne_has_support(NE_FEATURE_SSL)) {
+        _DEBUG("neon compiled with thread-safe SSL, enabling https:// transport");
+    }
+
+    _LEAVE;
+}
+
+/*
+ * -----
+ */
+
+static void fini(void) {
+
+    _ENTER;
+
+    ne_sock_exit();
+
+    _LEAVE;
+}
+
+/*
+ * -----
+ */
+
+static void kill_reader(struct neon_handle* h) {
+
+    _ENTER;
+
+    _DEBUG("Signaling reader thread to terminate");
+    g_mutex_lock(h->reader_status.mutex);
+    h->reader_status.reading = FALSE;
+    g_cond_signal(h->reader_status.cond);
+    g_mutex_unlock(h->reader_status.mutex);
+
+    _DEBUG("Waiting for reader thread to die...");
+    g_thread_join(h->reader);
+    _DEBUG("Reader thread has died");
+    h->reader = NULL;
+}
+
+/*
+ * -----
+ */
+
+static void handle_headers(struct neon_handle* h) {
+
+    const gchar* name;
+    const gchar* value;
+    void* cursor = NULL;
+    long len;
+    gchar* endptr;
+
+    _ENTER;
+
+    _DEBUG("Header responses:");
+    while(NULL != (cursor = ne_response_header_iterate(h->request, cursor, &name, &value))) {
+        _DEBUG("HEADER: %s: %s", name, value);
+        if (0 == g_ascii_strncasecmp("accept-ranges", name, 13)) {
+            /*
+             * The server advertises range capability. we need "bytes"
+             */
+            if (NULL != g_strrstr(value, "bytes")) {
+                _DEBUG("server can_ranges");
+                h->can_ranges = TRUE;
+            }
+        }
+
+        if (0 == g_ascii_strncasecmp("content-length", name, 14)) {
+            /*
+             * The server sent us the content length. Parse and store.
+             */
+            len = strtol(value, &endptr, 10);
+            if ((*value != '\0') && (*endptr == '\0')) {
+                /*
+                 * Valid data.
+                 */
+                _DEBUG("Content length as advertised by server: %d", len);
+                h->content_length = len;
+            }
+        }
+    }
+
+    _LEAVE;
+}
+
+/*
+ * -----
+ */
+
+static int open_request(struct neon_handle* handle, unsigned long startbyte) {
+
+    int ret;
+
+    _ENTER;
+
+    handle->request = ne_request_create(handle->session, "GET", handle->purl->path);
+    ne_print_request_header(handle->request, "Range", "bytes=%ld-", startbyte);
+
+    /*
+     * Try to connect to the server.
+     */
+    _DEBUG("Connecting...");
+    ret = ne_begin_request(handle->request);
+
+    switch (ret) {
+        case NE_OK:
+            /* URL opened OK */
+            _DEBUG("URL opened OK");
+            handle->content_start = startbyte;
+            handle->pos = startbyte;
+            handle_headers(handle);
+            _LEAVE 0;
+            break;
+
+        case NE_REDIRECT:
+            /* We hit a redirect. Handle it. */
+            _DEBUG("Redirect encountered");
+            handle->redircount += 1;
+            handle->purl = ne_redirect_location(handle->session);
+            ne_request_destroy(handle->request);
+            if (NULL == handle->purl) {
+                _ERROR("Could not parse redirect response");
+                _LEAVE -1;
+            }
+            _LEAVE 1;
+            break;
+
+        default:
+            /* Something went wrong. */
+            _ERROR("Could not open URL: %d", ret);
+            if (1 == ret) {
+                _ERROR("neon error string: %s", ne_get_error(handle->session));
+            }
+            ne_request_destroy(handle->request);
+            _LEAVE -1;
+            break;
+    }
+}
+
+/*
+ * -----
+ */
+
+static int open_handle(struct neon_handle* handle, unsigned long startbyte) {
+
+    int ret;
+
+    _ENTER;
+
+    handle->redircount = 0;
+
+    _DEBUG("Parsing URL");
+    if (0 != ne_uri_parse(handle->url, handle->purl)) {
+        _ERROR("Could not parse URL '%s'", handle->url);
+        _LEAVE -1;
+    }
+
+    if (0 == handle->purl->port) {
+        handle->purl->port = 80;
+    }
+
+    while (handle->redircount < 10) {
+
+        _DEBUG("Creating session");
+        handle->session = ne_session_create(handle->purl->scheme, handle->purl->host, handle->purl->port);
+        ne_set_session_flag(handle->session, NE_SESSFLAG_ICYPROTO, 1);
+        ne_set_session_flag(handle->session, NE_SESSFLAG_PERSIST, 0);
+        ne_redirect_register(handle->session);
+
+        _DEBUG("Creating request");
+        ret = open_request(handle, startbyte);
+
+        if (0 == ret) {
+            _LEAVE 0;
+        } else if (-1 == ret) {
+            ne_session_destroy(handle->session);
+            _LEAVE -1;
+        }
+    }
+
+    /*
+     * If we get here, our redirect count exceeded
+     */
+
+    _ERROR("Redirect count exceeded for URL");
+
+    _LEAVE 1;
+}
+
+/*
+ * -----
+ */
+
+static int fill_buffer(struct neon_handle* h) {
+
+    ssize_t bsize;
+    char buffer[NETBLKSIZ];
+    ssize_t to_read;
+
+    _ENTER;
+
+    bsize = free_rb(&h->rb);
+    to_read = MIN(bsize, NETBLKSIZ);
+
+    _DEBUG("%d bytes free in buffer, trying to read %d bytes max", bsize, to_read);
+
+    _DEBUG("Reading from the network....");
+    if (0 >= (bsize = ne_read_response_block(h->request, buffer, to_read))) {
+        if (0 == bsize) {
+            _DEBUG("End of file encountered");
+            _LEAVE 1;
+        } else {
+            _ERROR("Error while reading from the network");
+            _LEAVE -1;
+        }
+    }
+    _DEBUG("Read %d bytes from the network", bsize);
+
+    if (0 != write_rb(&(h->rb), buffer, bsize)) {
+        _ERROR("Error putting data into buffer");
+        _LEAVE -1;
+    }
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+static int fill_buffer_limit(struct neon_handle* h, unsigned int maxfree) {
+
+    ssize_t bfree;
+    int ret;
+
+    _ENTER;
+
+    bfree = free_rb(&h->rb);
+    _DEBUG("Filling buffer up to max %d bytes free, %d bytes free now", maxfree, bfree);
+
+    while (bfree > maxfree) {
+        ret = fill_buffer(h);
+        if (-1 == ret) {
+            _ERROR("Error while filling buffer");
+            _LEAVE ret;
+        } else if (1 == ret) {
+            /*
+             * EOF while filling the buffer. Return what we have.
+             */
+            _LEAVE 0;
+        }
+
+        bfree = free_rb(&h->rb);
+    }
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+static gpointer reader_thread(void* data) {
+
+    struct neon_handle* h = (struct neon_handle*)data;
+    int ret;
+
+    _ENTER;
+
+    g_mutex_lock(h->reader_status.mutex);
+
+    while(h->reader_status.reading) {
+        g_mutex_unlock(h->reader_status.mutex);
+
+        /*
+         * Hit the network only if we have more than NETBLKSIZ of free buffer
+         */
+        if (NETBLKSIZ < free_rb(&h->rb)) {
+
+            _DEBUG("Filling buffer...");
+            ret = fill_buffer(h);
+
+            g_mutex_lock(h->reader_status.mutex);
+            if (-1 == ret) {
+                /*
+                 * Error encountered while reading from the network.
+                 * Set the error flag and terminate the
+                 * reader thread.
+                 */
+                _DEBUG("Error while reading from the network. Terminating reader thread");
+                h->reader_status.status = NEON_READER_ERROR;
+                g_mutex_unlock(h->reader_status.mutex);
+                _LEAVE NULL;
+            } else if (1 == ret) {
+                /*
+                 * EOF encountered while reading from the
+                 * network. Set the EOF status and exit.
+                 */
+                _DEBUG("EOF encountered while reading from the network. Terminating reader thread");
+                h->reader_status.status = NEON_READER_EOF;
+                g_mutex_unlock(h->reader_status.mutex);
+                _LEAVE NULL;
+            }
+
+            /*
+             * So we actually got some data out of the stream.
+             */
+            _DEBUG("Network read succeeded");
+        } else {
+            /*
+             * Not enough free space in the buffer.
+             * Sleep until the main thread wakes us up.
+             */
+            g_mutex_lock(h->reader_status.mutex);
+            if (h->reader_status.reading) {
+                _DEBUG("Reader thread going to sleep");
+                g_cond_wait(h->reader_status.cond, h->reader_status.mutex);
+                _DEBUG("Reader thread woke up");
+            } else {
+                /*
+                 * Main thread has ordered termination of this thread.
+                 * Leave the loop.
+                 */
+                break;
+            }
+        }
+    }
+
+    _DEBUG("Reader thread terminating gracefully");
+    h->reader_status.status = NEON_READER_TERM;
+    g_mutex_unlock(h->reader_status.mutex);
+
+    _LEAVE NULL;
+}
+
+/*
+ * -----
+ */
+
+VFSFile* neon_vfs_fopen_impl(const gchar* path, const gchar* mode) {
+
+    VFSFile* file;
+    struct neon_handle* handle;
+
+    _ENTER;
+
+    _DEBUG("Trying to open '%s' with neon", path);
+
+    if (NULL == (file = malloc(sizeof(VFSFile)))) {
+        _ERROR("Could not allocate memory for filehandle");
+        _LEAVE NULL;
+    }
+
+    if (NULL == (handle = handle_init())) {
+        _ERROR("Could not allocate memory for neon handle");
+        free(file);
+        _LEAVE NULL;
+    }
+
+    if (NULL == (handle->url = strdup(path))) {
+        _ERROR("Could not copy URL string");
+        handle_free(handle);
+        free(file);
+        _LEAVE NULL;
+    }
+
+    if (0 != open_handle(handle, 0)) {
+        _ERROR("Could not open URL");
+        handle_free(handle);
+        free(file);
+        _LEAVE NULL;
+    }
+
+    file->handle = handle;
+    file->base = &neon_http_const;
+
+    _LEAVE file;
+}
+
+/*
+ * ----
+ */
+
+gint neon_vfs_fclose_impl(VFSFile* file) {
+
+    struct neon_handle* h = (struct neon_handle *)file->handle;
+
+    _ENTER;
+
+    if (NULL != h->reader) {
+        kill_reader(h);
+    }
+
+    _DEBUG("Destroying request");
+    if (NULL != h->request) {
+        ne_request_destroy(h->request);
+    }
+
+    _DEBUG("Destroying session");
+    ne_session_destroy(h->session);
+
+    handle_free(h);
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file) {
+
+    struct neon_handle* h = (struct neon_handle*)file->handle;
+    int belem;
+    int ret;
+
+    _ENTER;
+
+    if (NULL == h->request) {
+        _ERROR("No request to read from, seek gone wrong?");
+        _LEAVE 0;
+    }
+
+    _DEBUG("Requesting %d elements of %d bytes size each (%d bytes total), to be stored at %p",
+            nmemb, size, (nmemb*size), ptr_);
+
+    /*
+     * Look how much data is in the buffer
+     */
+    belem = used_rb(&h->rb) / size;
+
+    if ((NULL != h->reader) && (0 == belem)) {
+        /*
+         * There is a reader thread, but the buffer is empty.
+         * If we are running normally we will have to rebuffer.
+         * Kill the reader thread and restart.
+         */
+        g_mutex_lock(h->reader_status.mutex);
+        if (NEON_READER_RUN == h->reader_status.status) {
+            g_mutex_unlock(h->reader_status.mutex);
+            _ERROR("Buffer underrun, trying rebuffering");
+            kill_reader(h);
+        } else {
+            g_mutex_unlock(h->reader_status.mutex);
+        }
+    }
+
+    if (NULL == h->reader) {
+        /*
+         * There is no reader thread yet. Read the first bytes from
+         * the network ourselves, and then fire up the reader thread
+         * to keep the buffer filled up.
+         */
+        _DEBUG("Doing initial buffer fill");
+        ret = fill_buffer_limit(h, NBUFSIZ/2);
+
+        if (-1 == ret) {
+            _ERROR("Error while reading from the network");
+            _LEAVE 0;
+        } else if (1 == ret) {
+            _ERROR("EOF during initial read");
+            _LEAVE 0;
+        }
+
+        /*
+         * We have some data in the buffer now.
+         * Start the reader thread.
+         */
+        h->reader_status.reading = TRUE;
+        if (NULL == (h->reader = g_thread_create(reader_thread, h, TRUE, NULL))) {
+            h->reader_status.reading = FALSE;
+            _ERROR("Error creating reader thread!");
+            _LEAVE 0;
+        }
+        g_mutex_lock(h->reader_status.mutex);
+        h->reader_status.status = NEON_READER_RUN;
+        g_mutex_unlock(h->reader_status.mutex);
+    } else {
+        /*
+         * There already is a reader thread. Look if it is in good
+         * shape.
+         */
+        g_mutex_lock(h->reader_status.mutex);
+        _DEBUG("Reader thread status: %d", h->reader_status.status);
+        switch (h->reader_status.status) {
+            case NEON_READER_INIT:
+            case NEON_READER_RUN:
+                /*
+                 * All is well, nothing to be done.
+                 */
+                break;
+            case NEON_READER_EOF:
+                /*
+                 * If there still is data in the buffer, carry on.
+                 * If not, terminate the reader thread and return 0.
+                 */
+                if (0 == used_rb(&h->rb)) {
+                    _DEBUG("Reached end of stream");
+                    g_mutex_unlock(h->reader_status.mutex);
+                    kill_reader(h);
+                    h->eof = TRUE;
+                    _LEAVE 0;
+                }
+                break;
+            case NEON_READER_ERROR:
+                /* Terminate the reader and return 0 */
+                g_mutex_unlock(h->reader_status.mutex);
+                kill_reader(h);
+                _LEAVE 0;
+                break;
+            case NEON_READER_TERM:
+                /*
+                 * The reader thread terminated gracefully, most
+                 * likely on our own request.
+                 * We should not get here.
+                 */
+                _ERROR("Reader thread terminated and fread() called. How did we get here?");
+                g_mutex_unlock(h->reader_status.mutex);
+                kill_reader(h);
+                _LEAVE 0;
+        }
+        g_mutex_unlock(h->reader_status.mutex);
+    }
+
+    /*
+     * Deliver data from the buffer
+     */
+    belem = used_rb(&h->rb) / size;
+
+    if (0 == belem) {
+        /*
+         * The buffer is empty, we can deliver no data!
+         */
+        _ERROR("Buffer still underrun, fatal.");
+        _LEAVE 0;
+    }
+
+    _DEBUG("%d elements of data in the buffer", belem);
+    read_rb(&h->rb, ptr_, MIN(belem, nmemb)*size);
+
+    /*
+     * Signal the network thread to continue reading
+     */
+    _DEBUG("Waking up reader thread");
+    g_mutex_lock(h->reader_status.mutex);
+    g_cond_signal(h->reader_status.cond);
+    g_mutex_unlock(h->reader_status.mutex);
+
+    h->pos += (MIN(belem, nmemb)*size);
+
+    _DEBUG("Returning %d elements", MIN(belem, nmemb));
+
+    _LEAVE MIN(belem, nmemb);
+}
+
+
+/*
+ * -----
+ */
+
+size_t neon_vfs_fwrite_impl(gconstpointer ptr, size_t size, size_t nmemb, VFSFile* file) {
+
+    _ENTER;
+
+    _ERROR("NOT IMPLEMENTED");
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+gint neon_vfs_getc_impl(VFSFile* file) {
+
+    gchar c;
+
+    _ENTER;
+
+    if (1 != neon_vfs_fread_impl(&c, 1, 1, file)) {
+        _ERROR("Could not getc()!");
+        _LEAVE -1;
+    }
+
+    _LEAVE c;
+}
+
+/*
+ * -----
+ */
+
+gint neon_vfs_ungetc_impl(gint c, VFSFile* stream) {
+
+    _ENTER;
+
+    _ERROR("NOT IMPLEMENTED");
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+void neon_vfs_rewind_impl(VFSFile* file) {
+
+    _ENTER;
+
+    (void)neon_vfs_fseek_impl(file, 0L, SEEK_SET);
+
+    _LEAVE;
+}
+
+/*
+ * -----
+ */
+
+glong neon_vfs_ftell_impl(VFSFile* file) {
+
+    struct neon_handle* h = (struct neon_handle *)file->handle;
+
+    _ENTER;
+
+    _DEBUG("Current file position: %d", h->pos);
+
+    _LEAVE h->pos;
+}
+
+/*
+ * -----
+ */
+
+gboolean neon_vfs_feof_impl(VFSFile* file) {
+
+    struct neon_handle* h = (struct neon_handle*)file->handle;
+
+    _ENTER;
+
+    _LEAVE h->eof;
+}
+
+/*
+ * -----
+ */
+
+gint neon_vfs_truncate_impl(VFSFile* file, glong size) {
+
+    _ENTER;
+
+    _ERROR("NOT IMPLEMENTED");
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+gint neon_vfs_fseek_impl(VFSFile* file, glong offset, gint whence) {
+
+    struct neon_handle* h = (struct neon_handle*)file->handle;
+    long newpos;
+    long content_length;
+
+    _ENTER;
+
+    _DEBUG("Seek requested: offset %ld, whence %d", offset, whence);
+    /*
+     * Two things must be satisfied for us to be able to seek:
+     * - the server must advertise a content-length
+     * - the server must advertise accept-ranges: bytes
+     */
+    if ((-1 == h->content_length) || !h->can_ranges) {
+        _DEBUG("Can not seek due to server restrictions");
+        _LEAVE -1;
+    }
+
+    content_length = h->content_length + h->content_start;
+
+    switch (whence) {
+        case SEEK_SET:
+            newpos = offset;
+            break;
+        case SEEK_CUR:
+            newpos = h->pos + offset;
+            break;
+        case SEEK_END:
+            newpos = content_length + offset;
+            break;
+        default:
+            _ERROR("Invalid whence specified");
+            _LEAVE -1;
+    }
+
+    _DEBUG("Position to seek to: %ld, current: %ld", newpos, h->pos);
+    if (0 > newpos) {
+        _ERROR("Can not seek before start of stream");
+        _LEAVE -1;
+    }
+
+    if (newpos > content_length) {
+        _ERROR("Can not seek beyond end of stream");
+        _LEAVE -1;
+    }
+
+    if (newpos == h->pos) {
+        _LEAVE 0;
+    }
+
+    /*
+     * To seek to the new position we have to
+     * - stop the current reader thread, if there is one
+     * - destroy the current request
+     * - dump all data currently in the ringbuffer
+     * - create a new request starting at newpos
+     */
+    if (NULL != h->reader) {
+        /*
+         * There may be a thread still running.
+         */
+        kill_reader(h);
+    }
+
+    ne_request_destroy(h->request);
+    ne_session_destroy(h->session);
+    reset_rb(&h->rb);
+
+    if (0 != open_handle(h, newpos)) {
+        /*
+         * Something went wrong while creating the new request.
+         * There is not much we can do now, we'll set the request
+         * to NULL, so that fread() will error out on the next
+         * read request
+         */
+        _ERROR("Error while creating new request!");
+        h->request = NULL;
+        _LEAVE -1;
+    }
+
+    /*
+     * Things seem to have worked. The next read request will start
+     * the reader thread again.
+     */
+
+    _LEAVE 0;
+}
+
+/*
+ * -----
+ */
+
+gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field) {
+
+    _ENTER;
+
+    _ERROR("NOT IMPLEMENTED");
+
+    _LEAVE NULL;
+}
+
+/*
+ * -----
+ */
+
+off_t neon_vfs_fsize_impl(VFSFile* file) {
+
+    struct neon_handle* h = (struct neon_handle*)file->handle;
+
+    _ENTER;
+
+    if (-1 == h->content_length) {
+        _DEBUG("Unknown content length");
+        _LEAVE 0;
+    }
+
+    _LEAVE (h->content_start + h->content_length);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/neon/neon.h	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,63 @@
+#ifndef _NEON_PLUGIN_H
+#define _NEON_PLUGIN_H
+
+#include <glib.h>
+#include <audacious/vfs.h>
+#include <ne_session.h>
+#include <ne_request.h>
+#include <ne_uri.h>
+#include "rb.h"
+
+
+static void init(void);
+static void fini(void);
+
+VFSFile *neon_vfs_fopen_impl(const gchar* path, const gchar* mode);
+gint neon_vfs_fclose_impl(VFSFile* file);
+size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file);
+size_t neon_vfs_fwrite_impl(gconstpointer ptr, size_t size, size_t nmemb, VFSFile* file);
+gint neon_vfs_getc_impl(VFSFile* file);
+gint neon_vfs_ungetc_impl(gint c, VFSFile* file);
+void neon_vfs_rewind_impl(VFSFile* file);
+glong neon_vfs_ftell_impl(VFSFile* file);
+gboolean neon_vfs_feof_impl(VFSFile* file);
+gint neon_vfs_truncate_impl(VFSFile* file, glong size);
+gint neon_vfs_fseek_impl(VFSFile* file, glong offset, gint whence);
+gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field);
+off_t neon_vfs_fsize_impl(VFSFile* file);
+
+ne_uri purl;
+
+typedef enum {
+    NEON_READER_INIT=0,
+    NEON_READER_RUN=1,
+    NEON_READER_ERROR,
+    NEON_READER_EOF,
+    NEON_READER_TERM
+} neon_reader_t;
+
+struct reader_status {
+    GMutex* mutex;
+    GCond* cond;
+    gboolean reading;
+    neon_reader_t status;
+};
+
+struct neon_handle {
+    gchar* url;                         /* The URL, as passed to us */
+    ne_uri* purl;                       /* The URL, parsed into a structure */
+    struct ringbuf rb;                  /* Ringbuffer for our data */
+    unsigned char redircount;           /* Redirect count for the opened URL */
+    long pos;                           /* Current position in the stream (number of last byte delivered to the player) */
+    unsigned long content_start;        /* Start position in the stream */
+    long content_length;                /* Total content length, counting from content_start, if known. -1 if unknown */
+    gboolean can_ranges;                /* TRUE if the webserver advertised accept-range: bytes */
+    ne_session* session;
+    ne_request* request;
+    GThread* reader;
+    struct reader_status reader_status;
+    gboolean eof;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/neon/rb.c	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,294 @@
+/*
+ * Ringbuffer implementation
+ *
+ * GPL
+ */
+#include <string.h>
+#include "rb.h"
+#include "debug.h"
+
+#ifdef RB_DEBUG
+/*
+ * An internal assertion function to make sure that the
+ * ringbuffer structure is consistient.
+ *
+ * WARNING: This function will call abort() if the ringbuffer
+ * is found to be inconsistient.
+ */
+static void _assert_rb(struct ringbuf* rb) {
+
+    unsigned int realused;
+
+    _ENTER;
+
+    _DEBUG("rb->buf=%p, rb->end=%p, rb->wp=%p, rb->rp=%p, rb->free=%u, rb->used=%u, rb->size=%u",
+            rb->buf, rb->end, rb->wp, rb->rp, rb->free, rb->used, rb->size);
+
+    if (0 == rb->size) {
+        _ERROR("Buffer size is 0");
+        abort();
+    }
+
+    if (NULL == rb->buf) {
+        _ERROR("Buffer start is NULL");
+        abort();
+    }
+
+    if (rb->used+rb->free != rb->size) {
+        _ERROR("rb->free and rb->used do not add up to rb->size");
+        abort();
+    }
+
+    if (rb->buf+(rb->size-1) != rb->end) {
+        _ERROR("rb->buf and rb->end not rb->size bytes apart");
+        abort();
+    }
+
+    if ((rb->wp < rb->buf) || (rb->wp > rb->end)) {
+        _ERROR("Write pointer outside buffer space");
+        abort();
+    }
+
+    if ((rb->rp < rb->buf) || (rb->rp > rb->end)) {
+        _ERROR("Read pointer outside buffer space");
+        abort();
+    }
+
+    if (rb->rp <= rb->wp) {
+        realused = rb->wp - rb->rp;
+    } else {
+        realused = (rb->end - rb->rp) + 1 + (rb->wp-rb->buf);
+    }
+
+    if (rb->used != realused) {
+        _ERROR("Usage count is inconsistient (is %d, should be %d)", rb->used, realused);
+        abort();
+    }
+
+    _LEAVE;
+}
+#endif
+
+/*
+ * Reset a ringbuffer structure (i.e. discard
+ * all data inside of it)
+ */
+void reset_rb(struct ringbuf* rb) {
+
+    _ENTER;
+
+    pthread_mutex_lock(&rb->lock);
+
+    rb->wp = rb->buf;
+    rb->rp = rb->buf;
+    rb->free = rb->size;
+    rb->used = 0;
+    rb->end = rb->buf+(rb->size-1);
+
+    pthread_mutex_unlock(&rb->lock);
+
+    _LEAVE;
+}
+
+/* 
+ * Initialize a ringbuffer structure (including
+ * memory allocation.
+ *
+ * Return -1 on error
+ */
+int init_rb(struct ringbuf* rb, unsigned int size) {
+
+    _ENTER;
+
+    if (0 == size) {
+        _LEAVE -1;
+    }
+
+    if (0 != pthread_mutex_init(&rb->lock, NULL)) {
+        _LEAVE -1;
+    }
+
+    if (NULL == (rb->buf = malloc(size))) {
+        _LEAVE -1;
+    }
+    rb->size = size;
+    reset_rb(rb);
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/*
+ * Write size bytes at buf into the ringbuffer.
+ * Return -1 on error (not enough space in buffer)
+ */
+int write_rb(struct ringbuf* rb, void* buf, unsigned int size) {
+
+    int ret = -1;
+    int endfree;
+
+    _ENTER;
+
+    pthread_mutex_lock(&rb->lock);
+
+    ASSERT_RB(rb);
+
+    if (rb->free < size) {
+        ret = -1;
+        goto out;
+    }
+
+    endfree = (rb->end - rb->wp)+1;
+    if (endfree < size) {
+        /*
+         * There is enough space in the buffer, but not in
+         * one piece. We need to split the copy into two parts.
+         */
+        memcpy(rb->wp, buf, endfree);
+        memcpy(rb->buf, buf+endfree, size-endfree);
+        rb->wp = rb->buf + (size-endfree);
+    } else if (endfree > size) {
+        /*
+         * There is more space than needed at the end
+         */
+        memcpy(rb->wp, buf, size);
+        rb->wp += size;
+    } else {
+        /*
+         * There is exactly the space needed at the end.
+         * We need to wrap around the read pointer.
+         */
+        memcpy(rb->wp, buf, size);
+        rb->wp = rb->buf;
+    }
+
+    rb->free -= size;
+    rb->used += size;
+
+    ret = 0;
+
+out:
+    ASSERT_RB(rb);
+    pthread_mutex_unlock(&rb->lock);
+
+    _LEAVE ret;
+}
+
+/*
+ * Read size byes from buffer into buf.
+ * Return -1 on error (not enough data in buffer)
+ */
+int read_rb(struct ringbuf* rb, void* buf, unsigned int size) {
+
+    int ret;
+
+    _ENTER;
+
+    pthread_mutex_lock(&rb->lock);
+    ret = read_rb_locked(rb, buf, size);
+    pthread_mutex_unlock(&rb->lock);
+
+    _LEAVE ret;
+}
+
+/*
+ * Read size bytes from buffer into buf, assuming the buffer lock
+ * is already held.
+ * Return -1 on error (not enough data in buffer)
+ */
+int read_rb_locked(struct ringbuf* rb, void* buf, unsigned int size) {
+
+    int endused;
+
+    _ENTER;
+
+    ASSERT_RB(rb);
+
+    if (rb->used < size) {
+        /* Not enough bytes in buffer */
+        _LEAVE -1;
+    }
+
+    if (rb->rp < rb->wp) {
+        /*
+        Read pointer is behind write pointer, all the data is available in one cunk
+        */
+        memcpy(buf, rb->rp, size);
+        rb->rp += size;
+    } else {
+        /*
+         * Read pointer is before write pointer
+         */
+        endused = (rb->end - rb->rp)+1;
+
+        if (size < endused) {
+            /*
+             * Data is available in one chunk
+             */
+            memcpy(buf, rb->rp, size);
+            rb->rp += size;
+        } else {
+            /*
+             * There is enough data in the buffer, but it is fragmented.
+             */
+            memcpy(buf, rb->rp, endused);
+            memcpy(buf+endused, rb->buf, size-endused);
+            rb->rp = rb->buf + (size-endused);
+        }
+    }
+
+    rb->free += size;
+    rb->used -= size;
+
+    ASSERT_RB(rb);
+
+    _LEAVE 0;
+}
+
+/*
+ * Return the amount of free space currently in the rb
+ */
+unsigned int free_rb(struct ringbuf* rb) {
+
+    unsigned int f;
+
+    _ENTER;
+
+    pthread_mutex_lock(&rb->lock);
+    f = rb->free;
+    pthread_mutex_unlock(&rb->lock);
+
+    _LEAVE f;
+}
+
+
+/*
+ * Return the amount of used space currently in the rb
+ */
+unsigned int used_rb(struct ringbuf* rb) {
+
+    unsigned int u;
+
+    _ENTER;
+
+    pthread_mutex_lock(&rb->lock);
+    u = rb->used;
+    pthread_mutex_unlock(&rb->lock);
+
+    _LEAVE u;
+}
+
+
+/*
+ * destroy a ringbuffer
+ */
+void destroy_rb(struct ringbuf* rb) {
+
+    _ENTER;
+    pthread_mutex_lock(&rb->lock);
+    free(rb->buf);
+    pthread_mutex_unlock(&rb->lock);
+
+    _LEAVE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/neon/rb.h	Tue Sep 18 09:26:05 2007 -0500
@@ -0,0 +1,33 @@
+#ifndef _RB_H
+#define _RB_H
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#ifdef RB_DEBUG
+#define ASSERT_RB(buf) _assert_rb(buf)
+#else
+#define ASSERT_RB(buf)
+#endif
+
+struct ringbuf {
+    pthread_mutex_t lock;
+    char* buf;
+    char* end;
+    char* wp;
+    char* rp;
+    unsigned int free;
+    unsigned int used;
+    unsigned int size;
+};
+
+int init_rb(struct ringbuf* rb, unsigned int size);
+int write_rb(struct ringbuf* rb, void* buf, unsigned int size);
+int read_rb(struct ringbuf* rb, void* buf, unsigned int size);
+int read_rb_locked(struct ringbuf* rb, void* buf, unsigned int size);
+void reset_rb(struct ringbuf* rb);
+unsigned int free_rb(struct ringbuf* rb);
+unsigned int used_rb(struct ringbuf* rb);
+void destroy_rb(struct ringbuf* rb);
+
+#endif