diff options
-rw-r--r-- | eglib/Makefile.am | 2 | ||||
-rw-r--r-- | eglib/glib.h | 1 | ||||
-rw-r--r-- | eglib/gmain.c | 815 | ||||
-rw-r--r-- | eglib/gmain.h | 139 |
4 files changed, 956 insertions, 1 deletions
diff --git a/eglib/Makefile.am b/eglib/Makefile.am index 4d41ad92..009c70e1 100644 --- a/eglib/Makefile.am +++ b/eglib/Makefile.am @@ -2,7 +2,7 @@ if !GLIB noinst_LTLIBRARIES = libeglib.la -libeglib_la_SOURCES = glib.h +libeglib_la_SOURCES = glib.h gmain.h gmain.c endif MAINTAINERCLEANFILES = Makefile.in diff --git a/eglib/glib.h b/eglib/glib.h index e69de29b..5600a33e 100644 --- a/eglib/glib.h +++ b/eglib/glib.h @@ -0,0 +1 @@ +#include <gmain.h> diff --git a/eglib/gmain.c b/eglib/gmain.c new file mode 100644 index 00000000..a3bf3760 --- /dev/null +++ b/eglib/gmain.c @@ -0,0 +1,815 @@ +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <malloc.h> +#include <string.h> +#include <limits.h> +#include <sys/time.h> +#include <time.h> + +#include <gmain.h> + +struct timeout { + guint id; + guint interval; + struct timeval expiration; + gpointer data; + GSourceFunc function; +}; + +struct _GIOChannel { + int fd; + gboolean closed; + gboolean close_on_unref; +}; + +struct _GMainContext { + guint next_id; + glong next_timeout; + + GSList *timeouts; + GSList *proc_timeouts; + gboolean timeout_lock; + + GSList *watches; + GSList *proc_watches; + gboolean watch_lock; +}; + +struct _GMainLoop { + gboolean is_running; + GMainContext *context; +}; + +GIOError g_io_channel_read(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read) +{ + int fd = channel->fd; + gssize result; + + if (channel->closed) + return G_IO_STATUS_ERROR; + + /* At least according to the Debian manpage for read */ + if (count > SSIZE_MAX) + count = SSIZE_MAX; + +retry: + result = read (fd, buf, count); + + if (result < 0) { + *bytes_read = 0; + + switch (errno) { +#ifdef EINTR + case EINTR: + goto retry; +#endif +#ifdef EAGAIN + case EAGAIN: + return G_IO_STATUS_AGAIN; +#endif + default: + return G_IO_STATUS_ERROR; + } + } + + *bytes_read = result; + + return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF; +} + +void g_io_channel_close(GIOChannel *channel) +{ + if (!channel || channel->closed) + return; + + close(channel->fd); + + channel->closed = TRUE; +} + +void g_io_channel_unref(GIOChannel *channel) +{ + if (!channel) + return; + + if (channel->close_on_unref && channel->fd >= 0) + g_io_channel_close(channel); + + free(channel); +} + +GIOChannel *g_io_channel_unix_new(int fd) +{ + GIOChannel *channel; + + channel = malloc(sizeof(GIOChannel)); + if (!channel) + return NULL; + + memset(channel, 0, sizeof(GIOChannel)); + + channel->fd = fd; + + return channel; +} + +void g_io_channel_set_close_on_unref(GIOChannel *channel, gboolean do_close) +{ + channel->close_on_unref = do_close; +} + +gint g_io_channel_unix_get_fd(GIOChannel *channel) +{ + if (channel->closed) + return -1; + + return channel->fd; +} + +struct watch { + guint id; + GIOChannel *channel; + gint priority; + GIOCondition condition; + short *revents; + GIOFunc func; + gpointer user_data; + GDestroyNotify destroy; +}; + +static GMainContext *default_context = NULL; + +static void watch_free(struct watch *watch) +{ + if (watch->destroy) + watch->destroy(watch->user_data); + free(watch); +} + +static GMainContext *g_main_context_default() +{ + if (default_context) + return default_context; + + default_context = malloc(sizeof(GMainContext)); + if (!default_context) + return NULL; + + memset(default_context, 0, sizeof(GMainContext)); + + default_context->next_timeout = -1; + default_context->next_id = 1; + + return default_context; +} + +void g_io_remove_watch(guint id) +{ + GMainContext *context = g_main_context_default(); + GSList *l; + struct watch *w; + + if (!context) + return; + + for (l = context->watches; l != NULL; l = l->next) { + w = l->data; + + if (w->id != id) + continue; + + context->watches = g_slist_remove(context->watches, w); + watch_free(w); + + return; + } + + for (l = context->proc_watches; l != NULL; l = l->next) { + w = l->data; + + if (w->id != id) + continue; + + context->proc_watches = g_slist_remove(context->proc_watches, w); + watch_free(w); + + return; + } +} + +int watch_prio_cmp(struct watch *w1, struct watch *w2) +{ + return w1->priority - w2->priority; +} + +#define watch_list_add(l, w) g_slist_insert_sorted((l), (w), (GCompareFunc) watch_prio_cmp) + +guint g_io_add_watch_full(GIOChannel *channel, gint priority, + GIOCondition condition, GIOFunc func, + gpointer user_data, GDestroyNotify notify) +{ + struct watch *watch; + GMainContext *context = g_main_context_default(); + + if (!context) + return 0; + + watch = malloc(sizeof(struct watch)); + if (!watch) + return 0; + + watch->id = context->next_id++; + watch->channel = channel; + watch->priority = priority; + watch->condition = condition; + watch->func = func; + watch->user_data = user_data; + watch->destroy = notify; + + if (context->watch_lock) + context->proc_watches = watch_list_add(context->proc_watches, watch); + else + context->watches = watch_list_add(context->watches, watch); + + return watch->id; +} + +guint g_io_add_watch(GIOChannel *channel, GIOCondition condition, + GIOFunc func, gpointer user_data) +{ + return g_io_add_watch_full(channel, 0, condition, + func, user_data, NULL); +} + +GMainLoop *g_main_loop_new(GMainContext *context, gboolean is_running) +{ + GMainLoop *ml; + + if (!context) + context = g_main_context_default(); + + if (!context) + return NULL; + + ml = malloc(sizeof(GMainLoop)); + if (!ml) + return NULL; + + memset(ml, 0, sizeof(GMainLoop)); + + ml->context = context; + ml->is_running = is_running; + + return ml; +} + +static void timeout_handlers_prepare(GMainContext *context) +{ + GSList *l; + struct timeval tv; + glong msec, timeout = LONG_MAX; + + gettimeofday(&tv, NULL); + + for (l = context->timeouts; l != NULL; l = l->next) { + struct timeout *t = l->data; + + /* calculate the remainning time */ + msec = (t->expiration.tv_sec - tv.tv_sec) * 1000 + + (t->expiration.tv_usec - tv.tv_usec) / 1000; + if (msec < 0) + msec = 0; + + timeout = MIN_TIMEOUT(timeout, msec); + } + + /* set to min value found or NO timeout */ + context->next_timeout = (timeout != LONG_MAX ? timeout : -1); +} + +static int ptr_cmp(const void *t1, const void *t2) +{ + return t1 - t2; +} + +static void timeout_handlers_check(GMainContext *context) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + context->timeout_lock = TRUE; + + while (context->timeouts) { + struct timeout *t = context->timeouts->data; + glong secs, msecs; + gboolean ret; + + if (timercmp(&tv, &t->expiration, <)) { + context->timeouts = g_slist_remove(context->timeouts, t); + context->proc_timeouts = g_slist_append(context->proc_timeouts, t); + continue; + } + + ret = t->function(t->data); + + /* Check if the handler was removed/freed by the callback + * function */ + if (!g_slist_find_custom(context->timeouts, t, ptr_cmp)) + continue; + + context->timeouts = g_slist_remove(context->timeouts, t); + + if (!ret) { + free(t); + continue; + } + + /* update the next expiration time */ + secs = t->interval / 1000; + msecs = t->interval - secs * 1000; + + t->expiration.tv_sec = tv.tv_sec + secs; + t->expiration.tv_usec = tv.tv_usec + msecs * 1000; + if (t->expiration.tv_usec >= 1000000) { + t->expiration.tv_usec -= 1000000; + t->expiration.tv_sec++; + } + + context->proc_timeouts = g_slist_append(context->proc_timeouts, t); + } + + context->timeouts = context->proc_timeouts; + context->proc_timeouts = NULL; + context->timeout_lock = FALSE; +} + +void g_main_loop_run(GMainLoop *loop) +{ + int open_max = sysconf(_SC_OPEN_MAX); + struct pollfd *ufds; + GMainContext *context = loop->context; + + ufds = malloc(open_max * sizeof(struct pollfd)); + if (!ufds) + return; + + loop->is_running = TRUE; + + while (loop->is_running) { + int nfds; + GSList *l; + struct watch *w; + + for (nfds = 0, l = context->watches; l != NULL; l = l->next, nfds++) { + w = l->data; + ufds[nfds].fd = w->channel->fd; + ufds[nfds].events = w->condition; + ufds[nfds].revents = 0; + w->revents = &ufds[nfds].revents; + } + + /* calculate the next timeout */ + timeout_handlers_prepare(context); + + if (poll(ufds, nfds, context->next_timeout) < 0) + continue; + + context->watch_lock = TRUE; + + while (context->watches) { + gboolean ret; + + w = context->watches->data; + + if (!*w->revents) { + context->watches = g_slist_remove(context->watches, w); + context->proc_watches = watch_list_add(context->proc_watches, w); + continue; + } + + ret = w->func(w->channel, *w->revents, w->user_data); + + /* Check if the watch was removed/freed by the callback + * function */ + if (!g_slist_find_custom(context->watches, w, ptr_cmp)) + continue; + + context->watches = g_slist_remove(context->watches, w); + + if (!ret) { + watch_free(w); + continue; + } + + context->proc_watches = watch_list_add(context->proc_watches, w); + } + + context->watches = context->proc_watches; + context->proc_watches = NULL; + context->watch_lock = FALSE; + + /* check expired timers */ + timeout_handlers_check(loop->context); + } + + free(ufds); +} + +void g_main_loop_quit(GMainLoop *loop) +{ + loop->is_running = FALSE; +} + +void g_main_loop_unref(GMainLoop *loop) +{ + if (!loop->context) + return; + + g_slist_foreach(loop->context->watches, (GFunc)watch_free, NULL); + g_slist_free(loop->context->watches); + + g_slist_foreach(loop->context->timeouts, (GFunc)free, NULL); + g_slist_free(loop->context->timeouts); + + free(loop->context); + loop->context = NULL; +} + +guint g_timeout_add(guint interval, GSourceFunc function, gpointer data) +{ + GMainContext *context = g_main_context_default(); + struct timeval tv; + guint secs; + guint msecs; + struct timeout *t; + + if (!context || !function) + return 0; + + t = malloc(sizeof(*t)); + + if (!t) + return 0; + + memset(t, 0, sizeof(*t)); + t->interval = interval; + t->function = function; + t->data = data; + + gettimeofday(&tv, NULL); + + secs = interval /1000; + msecs = interval - secs * 1000; + + t->expiration.tv_sec = tv.tv_sec + secs; + t->expiration.tv_usec = tv.tv_usec + msecs * 1000; + + if (t->expiration.tv_usec >= 1000000) { + t->expiration.tv_usec -= 1000000; + t->expiration.tv_sec++; + } + + /* attach the timeout the default context */ + t->id = context->next_id++; + + if (context->timeout_lock) + context->proc_timeouts = g_slist_prepend(context->proc_timeouts, t); + else + context->timeouts = g_slist_prepend(context->timeouts, t); + + return t->id; +} + +gint g_timeout_remove(const guint id) +{ + GMainContext *context = g_main_context_default(); + GSList *l; + struct timeout *t; + + if (!context) + return -1; + + l = context->timeouts; + + while (l) { + t = l->data; + l = l->next; + + if (t->id != id) + continue; + + context->timeouts = g_slist_remove(context->timeouts, t); + free(t); + + return 0; + } + + l = context->proc_timeouts; + + while (l) { + t = l->data; + l = l->next; + + if (t->id != id) + continue; + + context->proc_timeouts = g_slist_remove(context->proc_timeouts, t); + free(t); + + return 0; + } + + return -1; +} + +/* UTF-8 Validation: approximate copy/paste from glib2. */ + +#define UNICODE_VALID(c) \ + ((c) < 0x110000 && \ + (((c) & 0xFFFFF800) != 0xD800) && \ + ((c) < 0xFDD0 || (c) > 0xFDEF) && \ + ((c) & 0xFFFE) != 0xFFFE) + +#define CONTINUATION_CHAR(c, val) \ + do { \ + if (((c) & 0xc0) != 0x80) /* 10xxxxxx */ \ + goto failed; \ + (val) <<= 6; \ + (val) |= (c) & 0x3f; \ + } while (0) + +#define INCREMENT_AND_CHECK_MAX(p, i, max_len) \ + do { \ + (i)++; \ + if ((p)[(i)] == '\0' || ((max_len) >= 0 && (i) >= (max_len))) \ + goto failed; \ + } while (0) + + +gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end) +{ + unsigned long val, min, i; + const unsigned char *p, *last; + + min = val = 0; + + for (p = (unsigned char *) str, i = 0; p[i]; i++) { + if (max_len >= 0 && i >= max_len) + break; + + if (p[i] < 128) + continue; + + last = &p[i]; + + if ((p[i] & 0xe0) == 0xc0) { /* 110xxxxx */ + if ((p[i] & 0x1e) == 0) + goto failed; + INCREMENT_AND_CHECK_MAX(p, i, max_len); + if ((p[i] & 0xc0) != 0x80) + goto failed; /* 10xxxxxx */ + } else { + if ((p[i] & 0xf0) == 0xe0) { + /* 1110xxxx */ + min = (1 << 11); + val = p[i] & 0x0f; + goto two_remaining; + } else if ((p[i] & 0xf8) == 0xf0) { + /* 11110xxx */ + min = (1 << 16); + val = p[i] & 0x07; + } else + goto failed; + + INCREMENT_AND_CHECK_MAX(p, i, max_len); + CONTINUATION_CHAR(p[i], val); +two_remaining: + INCREMENT_AND_CHECK_MAX(p, i, max_len); + CONTINUATION_CHAR(p[i], val); + + INCREMENT_AND_CHECK_MAX(p, i, max_len); + CONTINUATION_CHAR(p[i], val); + + if (val < min || !UNICODE_VALID(val)) + goto failed; + } + } + + if (end) + *end = (const gchar *) &p[i]; + + return TRUE; + +failed: + if (end) + *end = (const gchar *) last; + + return FALSE; +} + +/* GSList functions */ + +GSList *g_slist_append(GSList *list, void *data) +{ + GSList *entry, *tail; + + entry = malloc(sizeof(GSList)); + /* FIXME: this currently just silently fails */ + if (!entry) + return list; + + entry->data = data; + entry->next = NULL; + + if (!list) + return entry; + + /* Find the end of the list */ + for (tail = list; tail->next; tail = tail->next); + + tail->next = entry; + + return list; +} + +GSList *g_slist_prepend(GSList *list, void *data) +{ + GSList *entry; + + entry = malloc(sizeof(GSList)); + /* FIXME: this currently just silently fails */ + if (!entry) + return list; + + entry->data = data; + entry->next = list; + + return entry; +} + +GSList *g_slist_insert_sorted(GSList *list, void *data, GCompareFunc cmp_func) +{ + GSList *tmp, *prev, *entry; + int cmp; + + entry = malloc(sizeof(GSList)); + if (!entry) + return list; + + entry->data = data; + entry->next = NULL; + + if (!list) + return entry; + + prev = NULL; + tmp = list; + + cmp = cmp_func(data, tmp->data); + + while (tmp->next && cmp > 0) { + prev = tmp; + tmp = tmp->next; + + cmp = cmp_func(data, tmp->data); + } + + if (!tmp->next && cmp > 0) { + tmp->next = entry; + return list; + } + + if (prev) { + prev->next = entry; + entry->next = tmp; + return list; + } else { + entry->next = list; + return entry; + } +} + +GSList *g_slist_remove(GSList *list, void *data) +{ + GSList *l, *next, *prev = NULL, *match = NULL; + + if (!list) + return NULL; + + for (l = list; l != NULL; l = l->next) { + if (l->data == data) { + match = l; + break; + } + prev = l; + } + + if (!match) + return list; + + next = match->next; + + free(match); + + /* If the head was removed, return the next element */ + if (!prev) + return next; + + prev->next = next; + + return list; +} + +GSList *g_slist_find_custom(GSList *list, const void *data, + GCompareFunc cmp_func) +{ + GSList *l; + + for (l = list; l != NULL; l = l->next) { + if (!cmp_func(l->data, data)) + return l; + } + + return NULL; +} + +static GSList *g_slist_sort_merge(GSList *l1, GSList *l2, + GCompareFunc cmp_func) +{ + GSList list, *l; + int cmp; + + l = &list; + + while (l1 && l2) { + cmp = cmp_func(l1->data, l2->data); + + if (cmp <= 0) { + l = l->next = l1; + l1 = l1->next; + } else { + l = l->next = l2; + l2 = l2->next; + } + } + + l->next = l1 ? l1 : l2; + + return list.next; +} + +GSList *g_slist_sort(GSList *list, GCompareFunc cmp_func) +{ + GSList *l1, *l2; + + if (!list || !list->next) + return list; + + l1 = list; + l2 = list->next; + + while ((l2 = l2->next) != NULL) { + if ((l2 = l2->next) == NULL) + break; + l1 = l1->next; + } + + l2 = l1->next; + l1->next = NULL; + + return g_slist_sort_merge(g_slist_sort(list, cmp_func), + g_slist_sort(l2, cmp_func), cmp_func); +} + +int g_slist_length(GSList *list) +{ + int len; + + for (len = 0; list != NULL; list = list->next) + len++; + + return len; +} + +void g_slist_foreach(GSList *list, GFunc func, void *user_data) +{ + while (list) { + GSList *next = list->next; + func(list->data, user_data); + list = next; + } +} + +void g_slist_free(GSList *list) +{ + GSList *l, *next; + + for (l = list; l != NULL; l = next) { + next = l->next; + free(l); + } +} diff --git a/eglib/gmain.h b/eglib/gmain.h new file mode 100644 index 00000000..950d4170 --- /dev/null +++ b/eglib/gmain.h @@ -0,0 +1,139 @@ +#ifndef __GMAIN_H +#define __GMAIN_H + +#include <stdlib.h> +#include <sys/poll.h> + +typedef char gchar; +typedef short gshort; +typedef long glong; +typedef int gint; +typedef gint gboolean; + +typedef unsigned char guchar; +typedef unsigned short gushort; +typedef unsigned long gulong; +typedef unsigned int guint; + +typedef float gfloat; +typedef double gdouble; + +typedef void * gpointer; +typedef const void * gconstpointer; + +typedef size_t gsize; +typedef ssize_t gssize; + +#ifndef SSIZE_MAX +#define SSIZE_MAX INT_MAX +#endif + +#define MIN_TIMEOUT(a, b) (((a) < (b)) ? (a) : (b)) + +typedef struct _GIOChannel GIOChannel; + +typedef gboolean (*GSourceFunc) (gpointer data); + +typedef struct _GMainContext GMainContext; + +typedef struct _GMainLoop GMainLoop; + +typedef enum { + G_IO_ERROR_NONE, + G_IO_ERROR_AGAIN, + G_IO_ERROR_INVAL, + G_IO_ERROR_UNKNOWN +} GIOError; + +typedef enum { + G_IO_STATUS_ERROR = -1, + G_IO_STATUS_NORMAL = 0, + G_IO_STATUS_EOF = 1, + G_IO_STATUS_AGAIN = 2 +} GIOStatus; + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +typedef enum { + G_IO_IN = POLLIN, + G_IO_OUT = POLLOUT, + G_IO_PRI = POLLPRI, + G_IO_ERR = POLLERR, + G_IO_HUP = POLLHUP, + G_IO_NVAL = POLLNVAL +} GIOCondition; + +#define G_PRIORITY_HIGH -100 +#define G_PRIORITY_DEFAULT 0 +#define G_PRIORITY_HIGH_IDLE 100 +#define G_PRIORITY_DEFAULT_IDLE 200 +#define G_PRIORITY_LOW 300 + +typedef void (*GDestroyNotify) (gpointer data); +typedef gboolean (*GIOFunc) (GIOChannel *source, GIOCondition condition, gpointer data); + +GIOError g_io_channel_read(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read); +void g_io_channel_close(GIOChannel *channel); + +GIOChannel *g_io_channel_unix_new(int fd); +void g_io_channel_unref(GIOChannel *channel); +void g_io_channel_set_close_on_unref(GIOChannel *channel, gboolean do_close); +gint g_io_channel_unix_get_fd(GIOChannel *channel); +guint g_io_add_watch(GIOChannel *channel, GIOCondition condition, + GIOFunc func, gpointer user_data); +guint g_io_add_watch_full(GIOChannel *channel, gint priority, + GIOCondition condition, GIOFunc func, + gpointer user_data, GDestroyNotify notify); +void g_io_remove_watch(guint id); + +GMainLoop *g_main_loop_new(GMainContext *context, gboolean is_running); +void g_main_loop_run(GMainLoop *loop); +void g_main_loop_quit(GMainLoop *loop); +void g_main_loop_unref(GMainLoop *loop); +guint g_timeout_add(guint interval, GSourceFunc function, gpointer data); +gint g_timeout_remove(const guint id); + +gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end); + +#define g_main_new(is_running) g_main_loop_new(NULL, is_running); +#define g_main_run(loop) g_main_loop_run(loop) +#define g_main_quit(loop) g_main_loop_quit(loop) +#define g_main_unref(loop) g_main_loop_unref(loop) + +/* Begin GSList declarations */ + +typedef struct _GSList { + void *data; + struct _GSList *next; +} GSList; + +typedef int (*GCompareFunc)(const void *a, const void *b); +typedef void (*GFunc)(void *data, void *user_data); + +GSList *g_slist_append(GSList *list, void *data); + +GSList *g_slist_prepend(GSList *list, void *data); + +GSList *g_slist_insert_sorted(GSList *list, void *data, GCompareFunc cmp_func); + +GSList *g_slist_remove(GSList *list, void *data); + +GSList *g_slist_find_custom(GSList *list, const void *data, + GCompareFunc cmp_func); + +GSList *g_slist_sort(GSList *list, GCompareFunc cmp_func); + +int g_slist_length(GSList *list); + +void g_slist_foreach(GSList *list, GFunc func, void *user_data); +void g_slist_free(GSList *list); + +/* End GSList declarations */ + +#endif /* __GMAIN_H */ |