blob: 1ca3d67a1688ef281895b16616921563dad7148d [file] [log] [blame]
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <ctype.h>
#include <dlfcn.h>
#include <dirent.h>
#include <gmain.h>
struct timeout {
guint id;
guint interval;
struct timeval expiration;
gpointer data;
GSourceFunc function;
};
struct _GIOChannel {
int fd;
int ref_count;
gboolean closed;
gboolean close_on_unref;
};
struct child_watch {
guint id;
GPid pid;
GChildWatchFunc function;
gpointer user_data;
};
struct _GMainContext {
guint next_id;
glong next_timeout;
GSList *timeouts;
GSList *proc_timeouts;
gboolean timeout_lock;
GSList *io_watches;
GSList *proc_io_watches;
gboolean io_lock;
GSList *child_watches;
GSList *proc_child_watches;
gboolean child_lock;
};
struct _GMainLoop {
gboolean is_running;
GMainContext *context;
};
struct _GDir
{
DIR *dirp;
};
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;
}
GIOError g_io_channel_write(GIOChannel *channel, const gchar *buf, gsize count,
gsize *bytes_written)
{
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 = write(fd, buf, count);
if (result < 0) {
*bytes_written = 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_written = 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->ref_count > 0)
return;
if (channel->close_on_unref && channel->fd >= 0)
g_io_channel_close(channel);
g_free(channel);
}
GIOChannel *g_io_channel_ref(GIOChannel *channel)
{
channel->ref_count++;
return channel;
}
GIOChannel *g_io_channel_unix_new(int fd)
{
GIOChannel *channel;
channel = g_new0(GIOChannel, 1);
channel->fd = fd;
channel->ref_count = 1;
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;
}
static int set_flags(int fd, long flags)
{
long arg;
arg = fcntl(fd, F_GETFL);
if (arg < 0)
return -errno;
/* Return if already set */
if ((arg & flags) == flags)
return 0;
arg |= flags;
if (fcntl(fd, F_SETFL, arg) < 0)
return -errno;
return 0;
}
GIOStatus g_io_channel_set_flags(GIOChannel *channel, GIOFlags flags,
GError **error)
{
int err, fd;
long fd_flags = 0;
if (!channel || channel->closed)
return G_IO_STATUS_ERROR;
fd = g_io_channel_unix_get_fd(channel);
if (flags & G_IO_FLAG_APPEND)
fd_flags |= O_APPEND;
if (flags & G_IO_FLAG_NONBLOCK)
fd_flags |= O_NONBLOCK;
err = set_flags(fd, fd_flags);
if (err < 0) {
if (error)
g_set_error(error, 0, 0, "Unable to set flags: %s",
strerror(-err));
return G_IO_STATUS_ERROR;
}
return G_IO_STATUS_NORMAL;
}
struct io_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 io_watch *watch)
{
if (watch->destroy)
watch->destroy(watch->user_data);
g_io_channel_unref(watch->channel);
g_free(watch);
}
static GMainContext *g_main_context_default()
{
if (default_context)
return default_context;
default_context = g_new0(GMainContext, 1);
default_context->next_timeout = -1;
default_context->next_id = 1;
return default_context;
}
static gboolean g_io_remove_watch(GMainContext *context, guint id)
{
GSList *l;
struct io_watch *w;
for (l = context->io_watches; l != NULL; l = l->next) {
w = l->data;
if (w->id != id)
continue;
context->io_watches = g_slist_remove(context->io_watches, w);
watch_free(w);
return TRUE;
}
for (l = context->proc_io_watches; l != NULL; l = l->next) {
w = l->data;
if (w->id != id)
continue;
context->proc_io_watches = g_slist_remove(context->proc_io_watches, w);
watch_free(w);
return TRUE;
}
return FALSE;
}
static gboolean g_timeout_remove(GMainContext *context, const guint id)
{
GSList *l;
struct timeout *t;
l = context->timeouts;
while (l) {
t = l->data;
l = l->next;
if (t->id != id)
continue;
context->timeouts = g_slist_remove(context->timeouts, t);
g_free(t);
return TRUE;
}
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);
g_free(t);
return TRUE;
}
return FALSE;
}
int watch_prio_cmp(struct io_watch *w1, struct io_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 io_watch *watch;
GMainContext *context = g_main_context_default();
watch = g_new(struct io_watch, 1);
watch->id = context->next_id++;
watch->channel = g_io_channel_ref(channel);
watch->priority = priority;
watch->condition = condition;
watch->func = func;
watch->user_data = user_data;
watch->destroy = notify;
if (context->io_lock)
context->proc_io_watches = watch_list_add(context->proc_io_watches, watch);
else
context->io_watches = watch_list_add(context->io_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();
ml = g_new0(GMainLoop, 1);
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) {
g_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 = g_new(struct pollfd, open_max);
loop->is_running = TRUE;
while (loop->is_running) {
int nfds;
GSList *l;
struct io_watch *w;
for (nfds = 0, l = context->io_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->io_lock = TRUE;
while (context->io_watches) {
gboolean ret;
w = context->io_watches->data;
if (!*w->revents) {
context->io_watches = g_slist_remove(context->io_watches, w);
context->proc_io_watches = watch_list_add(context->proc_io_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->io_watches, w, ptr_cmp))
continue;
context->io_watches = g_slist_remove(context->io_watches, w);
if (!ret) {
watch_free(w);
continue;
}
context->proc_io_watches = watch_list_add(context->proc_io_watches, w);
}
context->io_watches = context->proc_io_watches;
context->proc_io_watches = NULL;
context->io_lock = FALSE;
/* check expired timers */
timeout_handlers_check(loop->context);
}
g_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->io_watches, (GFunc)watch_free, NULL);
g_slist_free(loop->context->io_watches);
g_slist_foreach(loop->context->timeouts, (GFunc)g_free, NULL);
g_slist_free(loop->context->timeouts);
g_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;
t = g_new0(struct timeout, 1);
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;
}
guint g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data)
{
return g_timeout_add(interval, function, data);
}
guint g_idle_add(GSourceFunc function, gpointer data)
{
return g_timeout_add(1, function, data);
}
/* GError */
GError* g_error_new_literal(GQuark domain, gint code, const gchar *message)
{
GError *err;
err = g_new(GError, 1);
err->domain = domain;
err->code = code;
err->message = g_strdup(message);
return err;
}
void g_set_error(GError **err, GQuark domain, gint code,
const gchar *format, ...)
{
gchar msg[1024];
va_list ap;
if (!err)
return;
va_start(ap, format);
vsnprintf(msg, sizeof(msg) - 1, format, ap);
va_end(ap);
*err = g_error_new_literal(domain, code, msg);
}
void g_error_free(GError *err)
{
g_free(err->message);
g_free(err);
}
/* Spawning related functions */
static int child_watch_pipe[2] = { -1, -1 };
static void sigchld_handler(int signal)
{
int ret;
ret = write(child_watch_pipe[1], "B", 1);
}
static gboolean child_watch_remove(GMainContext *context, guint id)
{
GSList *l;
struct child_watch *w;
for (l = context->child_watches; l != NULL; l = l->next) {
w = l->data;
if (w->id != id)
continue;
context->child_watches =
g_slist_remove(context->child_watches, w);
g_free(w);
return TRUE;
}
for (l = context->proc_child_watches; l != NULL; l = l->next) {
w = l->data;
if (w->id != id)
continue;
context->proc_child_watches =
g_slist_remove(context->proc_child_watches, w);
g_free(w);
return TRUE;
}
return FALSE;
}
static gboolean child_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
{
int ret;
char b[20];
GMainContext *context = g_main_context_default();
ret = read(child_watch_pipe[0], b, 20);
context->child_lock = TRUE;
while (context->child_watches) {
gint status;
struct child_watch *w = context->child_watches->data;
if (waitpid(w->pid, &status, WNOHANG) <= 0) {
context->child_watches =
g_slist_remove(context->child_watches, w);
context->proc_child_watches =
watch_list_add(context->proc_child_watches, w);
continue;
}
w->function(w->pid, status, w->user_data);
/* Check if the callback already removed us */
if (!g_slist_find(context->child_watches, w))
continue;
context->child_watches = g_slist_remove(context->child_watches, w);
g_free(w);
}
context->child_watches = context->proc_child_watches;
context->proc_child_watches = NULL;
context->child_lock = FALSE;
return TRUE;
}
static void init_child_pipe(void)
{
struct sigaction action;
GIOChannel *io;
if (pipe(child_watch_pipe) < 0) {
fprintf(stderr, "Unable to initialize child watch pipe: %s (%d)\n",
strerror(errno), errno);
abort();
}
fcntl(child_watch_pipe[1], F_SETFL,
O_NONBLOCK | fcntl(child_watch_pipe[1], F_GETFL));
action.sa_handler = sigchld_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_NOCLDSTOP;
sigaction(SIGCHLD, &action, NULL);
io = g_io_channel_unix_new(child_watch_pipe[0]);
g_io_add_watch(io, G_IO_IN, child_watch, NULL);
g_io_channel_unref(io);
}
static void exec_child(const gchar *working_directory,
gchar **argv, gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data)
{
int null;
if (working_directory && chdir(working_directory) < 0)
_exit(EXIT_FAILURE);
if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN)) {
int open_max, fd, ret;
ret = 0;
open_max = sysconf(_SC_OPEN_MAX);
for (fd = 3; fd < open_max && ret == 0; fd++)
ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
}
null = open("/dev/null", O_RDWR);
if (!(flags & G_SPAWN_CHILD_INHERITS_STDIN))
dup2(null, STDIN_FILENO);
if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
dup2(null, STDOUT_FILENO);
if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
dup2(null, STDERR_FILENO);
if (null > 2)
close(null);
if (child_setup)
child_setup(user_data);
if (envp)
execve(argv[0], argv, envp);
else
execv(argv[0], argv);
/* exec failed if we get here */
_exit(EXIT_FAILURE);
}
gboolean g_spawn_async(const gchar *working_directory,
gchar **argv, gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
GPid *child_pid,
GError **error)
{
GPid pid;
if (access(argv[0], X_OK) < 0) {
g_set_error(error, 0, 0, "%s is not executable", argv[0]);
return FALSE;
}
if (child_watch_pipe[0] < 0)
init_child_pipe();
/* Flush output streams so child doesn't get them */
fflush(NULL);
switch (pid = fork()) {
case -1:
g_set_error(error, 0, 0, "fork failed: %s", strerror(errno));
return FALSE;
case 0:
exec_child(working_directory, argv, envp, flags,
child_setup, user_data);
break;
default:
if (child_pid)
*child_pid = pid;
return TRUE;
}
/* Never reached */
return FALSE;
}
void g_spawn_close_pid(GPid pid)
{
return;
}
guint g_child_watch_add(GPid pid, GChildWatchFunc func, gpointer user_data)
{
struct child_watch *w;
GMainContext *context = g_main_context_default();
if (child_watch_pipe[0] < 0)
init_child_pipe();
w = g_new(struct child_watch, 1);
w->id = context->next_id++;
w->pid = pid;
w->function = func;
w->user_data = user_data;
if (context->child_lock)
context->proc_child_watches =
watch_list_add(context->proc_child_watches, w);
else
context->child_watches =
watch_list_add(context->child_watches, w);
return w->id;
}
gboolean g_source_remove(guint tag)
{
GMainContext *context = g_main_context_default();
if (g_io_remove_watch(context, tag))
return TRUE;
if (g_timeout_remove(context, tag))
return TRUE;
if (child_watch_remove(context, tag))
return TRUE;
return FALSE;
}
/* 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 = g_new(GSList, 1);
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 = g_new(GSList, 1);
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 = g_new(GSList, 1);
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;
g_free(match);
/* If the head was removed, return the next element */
if (!prev)
return next;
prev->next = next;
return list;
}
GSList *g_slist_find(GSList *list, gconstpointer data)
{
GSList *l;
for (l = list; l != NULL; l = l->next) {
if (l->data == data)
return l;
}
return NULL;
}
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;
g_free(l);
}
}
GSList *g_slist_nth(GSList *list, guint n)
{
while (n-- > 0 && list)
list = list->next;
return list;
}
gpointer g_slist_nth_data(GSList *list, guint n)
{
while (n-- > 0 && list)
list = list->next;
return list ? list->data : NULL;
}
gint g_slist_position(GSList *list, GSList *link)
{
gint i;
for (i = 0; list; list = list->next, i++) {
if (list == link)
return i;
}
return -1;
}
GSList* g_slist_last(GSList *list)
{
if (list)
while (list->next)
list = list->next;
return list;
}
static inline GSList* _g_slist_remove_link(GSList *list, GSList *link)
{
GSList *tmp;
GSList *prev;
prev = NULL;
tmp = list;
while (tmp) {
if (tmp == link) {
if (prev)
prev->next = tmp->next;
if (list == tmp)
list = list->next;
tmp->next = NULL;
break;
}
prev = tmp;
tmp = tmp->next;
}
return list;
}
GSList* g_slist_delete_link(GSList *list, GSList *link)
{
list = _g_slist_remove_link(list, link);
g_free(link);
return list;
}
/* Memory allocation functions */
gpointer g_malloc(gulong n_bytes)
{
gpointer mem;
if (!n_bytes)
return NULL;
mem = malloc((size_t) n_bytes);
if (!mem) {
fprintf(stderr, "g_malloc: failed to allocate %lu bytes",
n_bytes);
abort();
}
return mem;
}
gpointer g_malloc0(gulong n_bytes)
{
gpointer mem;
if (!n_bytes)
return NULL;
mem = g_malloc(n_bytes);
memset(mem, 0, (size_t) n_bytes);
return mem;
}
gpointer g_try_malloc(gulong n_bytes)
{
if (!n_bytes)
return NULL;
return malloc((size_t) n_bytes);
}
gpointer g_try_malloc0(gulong n_bytes)
{
gpointer mem;
mem = g_try_malloc(n_bytes);
if (mem)
memset(mem, 0, (size_t) n_bytes);
return mem;
}
gpointer g_realloc(gpointer mem, gulong n_bytes)
{
mem = realloc(mem, n_bytes);
if (!mem) {
fprintf(stderr, "g_realloc: failed to allocate %lu bytes",
n_bytes);
abort();
}
return mem;
}
void g_free(gpointer mem)
{
if (mem)
free(mem);
}
gchar *g_strdup(const gchar *str)
{
gchar *s;
if (!str)
return NULL;
s = strdup(str);
if (!s) {
fprintf(stderr, "strdup: failed to allocate new string");
abort();
}
return s;
}
gchar *g_strdup_printf(const gchar *format, ...)
{
va_list args;
gchar buffer[1024];
gint length;
va_start(args, format);
length = vsnprintf(buffer, sizeof(buffer) - 1, format, args);
va_end(args);
return g_strdup(buffer);
}
gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar new_delim)
{
register gchar *c;
if (!string)
return NULL;
for (c = string; *c; c++)
if (strchr(delimiters, *c))
*c = new_delim;
return string;
}
gchar *g_strconcat(const gchar *string1, ...)
{
gsize l;
va_list args;
gchar *s, *concat;
if (!string1)
return NULL;
l = 1 + strlen(string1);
va_start(args, string1);
s = va_arg(args, gchar *);
while (s) {
l += strlen(s);
s = va_arg(args, gchar *);
}
va_end (args);
concat = g_new(gchar, l);
concat[0] = '\0';
va_start(args, string1);
s = va_arg(args, gchar*);
while (s) {
strcat(concat, s);
s = va_arg(args, gchar *);
}
va_end (args);
return concat;
}
gsize g_strlcat(gchar *dest, const gchar *src, gsize dest_size)
{
gchar *d = dest;
const gchar *s = src;
gsize bytes_left = dest_size;
gsize dlength; /* Logically, MIN(strlen(d), dest_size) */
if (!d || !s)
return 0;
/* Find the end of dst and adjust bytes left but don't go past end */
while (*d != 0 && bytes_left-- != 0)
d++;
dlength = d - dest;
bytes_left = dest_size - dlength;
if (bytes_left == 0)
return dlength + strlen(s);
while (*s != 0) {
if (bytes_left != 1) {
*d++ = *s;
bytes_left--;
}
s++;
}
*d = 0;
return dlength + (s - src); /* count does not include NULL */
}
gchar **g_strsplit(const gchar *string, const gchar *delimiter, gint max_tokens)
{
GSList *string_list = NULL, *slist;
gchar **str_array, *s;
guint n = 0;
const gchar *remainder;
if (string == NULL || delimiter == NULL || delimiter[0] == '\0')
return NULL;
if (max_tokens < 1)
max_tokens = INT_MAX;
remainder = string;
s = strstr(remainder, delimiter);
if (s) {
gsize delimiter_len = strlen(delimiter);
while (--max_tokens && s) {
gsize len;
gchar *tmp;
len = s - remainder;
tmp = g_new(char, len);
memcpy(tmp, remainder, len);
string_list = g_slist_prepend(string_list, tmp);
n++;
remainder = s + delimiter_len;
s = strstr(remainder, delimiter);
}
}
if (*string) {
n++;
string_list = g_slist_prepend(string_list, g_strdup(remainder));
}
str_array = g_new(gchar *, n + 1);
str_array[n--] = NULL;
for (slist = string_list; slist; slist = slist->next)
str_array[n--] = slist->data;
g_slist_free(string_list);
return str_array;
}
gchar *g_ascii_strup(const gchar *str, gssize len)
{
int i;
gchar *s;
s = g_strdup(str);
if (!s)
return NULL;
if (len < 0)
len = strlen(s);
for (i = 0; i < len; i++)
s[i] = toupper(s[i]);
return s;
}
gboolean g_str_equal(gconstpointer v1, gconstpointer v2)
{
const gchar *string1 = v1;
const gchar *string2 = v2;
return strcmp(string1, string2) == 0;
}
gboolean g_str_has_prefix(const gchar *str, const gchar *prefix)
{
int str_len;
int prefix_len;
if (str == NULL || prefix == NULL)
return FALSE;
str_len = strlen (str);
prefix_len = strlen (prefix);
if (str_len < prefix_len)
return FALSE;
return strncmp(str, prefix, prefix_len) == 0;
}
gboolean g_str_has_suffix(const gchar *str, const gchar *suffix)
{
int str_len;
int suffix_len;
if (!str || !suffix)
return FALSE;
str_len = strlen(str);
suffix_len = strlen(suffix);
if (str_len < suffix_len)
return FALSE;
return strcmp(str + str_len - suffix_len, suffix) == 0;
}
void g_strfreev(gchar **str_array)
{
int i;
if (str_array == NULL)
return;
for(i = 0; str_array[i] != NULL; i++)
g_free(str_array[i]);
g_free(str_array);
}
/* GKeyFile */
struct _GKeyFile {
gchar *filename;
};
GKeyFile *g_key_file_new(void)
{
return g_new0(GKeyFile, 1);
}
void g_key_file_free(GKeyFile *key_file)
{
g_free(key_file->filename);
g_free(key_file);
}
gboolean g_key_file_load_from_file(GKeyFile *key_file,
const gchar *file,
GKeyFileFlags flags,
GError **error)
{
key_file->filename = g_strdup(file);
return TRUE;
}
static char *next_line(const char *ptr)
{
char *nl;
nl = strchr(ptr, '\n');
if (!nl)
return NULL;
if (nl[1] == '\0')
return NULL;
return nl + 1;
}
gchar *g_key_file_get_string(GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error)
{
struct stat st;
char *map, *line, *group = NULL, *value = NULL;
off_t size;
size_t key_len, group_len;
int fd, err = 0;
fd = open(key_file->filename, O_RDONLY);
if (fd < 0) {
g_set_error(error, 0, 0, "%s: %s", key_file->filename,
strerror(errno));
return NULL;
}
if (flock(fd, LOCK_SH) < 0) {
err = errno;
goto close;
}
if (fstat(fd, &st) < 0) {
err = errno;
goto unlock;
}
size = st.st_size;
map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (!map || map == MAP_FAILED) {
err = errno;
goto unlock;
}
group_len = strlen(group_name);
key_len = strlen(key);
for (line = map; line != NULL; line = next_line(line)) {
int i;
size_t to_copy, value_len;
char tmp[1024], *nl;
if (*line == '#')
continue;
if (!group) {
if (line[0] != '[' || strncmp(line + 1, group_name, group_len))
continue;
if (line[group_len + 1] == ']')
group = line + 1;
continue;
}
if (strncmp(line, key, key_len))
continue;
for (i = key_len; line[i] != '\n'; i++) {
if (line[i] == '=')
break;
if (!isspace(line[i]))
break;
}
if (line[i] != '=')
continue;
nl = strchr(line, '\n');
if (!nl)
continue;
value_len = nl - (line + i + 1);
to_copy = value_len > (sizeof(tmp) - 1) ? sizeof(tmp) - 1 : value_len;
memset(tmp, 0, sizeof(tmp));
strncpy(tmp, line + i + 1, to_copy);
value = g_strdup(tmp);
break;
}
munmap(map, size);
unlock:
flock(fd, LOCK_UN);
close:
close(fd);
if (err)
g_set_error(error, 0, 0, "%s: %s", key_file->filename,
strerror(err));
else if (!group)
g_set_error(error, 0, 0, "%s: group %s not found",
key_file->filename, group_name);
else if (!value)
g_set_error(error, 0, 0, "%s: key %s not found",
key_file->filename, key);
return value;
}
gboolean g_key_file_get_boolean(GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error)
{
gboolean ret;
gchar *str;
str = g_key_file_get_string(key_file, group_name, key, error);
if (!str)
return FALSE;
if (strcmp(str, "true") == 0 || strcmp(str, "1") == 0)
ret = TRUE;
else
ret = FALSE;
g_free(str);
return ret;
}
gint g_key_file_get_integer(GKeyFile *key_file,
const gchar *group_name,
const gchar *key,
GError **error)
{
int ret;
gchar *str;
str = g_key_file_get_string(key_file, group_name, key, error);
if (!str)
return 0;
ret = atoi(str);
g_free(str);
return ret;
}
gchar **g_key_file_get_string_list(GKeyFile *key_file, const gchar *group_name,
const gchar *key, gsize *length,
GError **error)
{
gchar *str, *item, **list;
int items = 0;
str = g_key_file_get_string(key_file, group_name, key, error);
if (!str)
return NULL;
items = 0;
list = g_new0(char *, 1);
item = strtok(str, ",");
while (item) {
items++;
list = g_renew(char *, list, items + 1);
list[items - 1] = g_strdup(item);
list[items] = NULL;
item = strtok(NULL, ",");
}
g_free(str);
return list;
}
/* GString */
#define MY_MAXSIZE ((gsize)-1)
static gsize nearest_power(gsize base, gsize num)
{
gsize n = base;
if (num > MY_MAXSIZE / 2)
return MY_MAXSIZE;
while (n < num)
n <<= 1;
return n;
}
static void g_string_maybe_expand(GString *string, gsize len)
{
if (string->len + len < string->allocated_len)
return;
string->allocated_len = nearest_power(1, string->len + len + 1);
string->str = g_realloc(string->str, string->allocated_len);
}
static GString *g_string_sized_new(gsize dfl_size)
{
GString *string;
string = g_new0(GString, 1);
g_string_maybe_expand(string, dfl_size);
string->str[0] = '\0';
return string;
}
static GString *g_string_append_len(GString *string, const gchar *val, gssize len)
{
g_string_maybe_expand(string, len);
if (len == 1)
string->str[string->len] = *val;
else
memcpy(string->str + string->len, val, len);
string->len += len;
string->str[string->len] = '\0';
return string;
}
GString *g_string_new(const gchar *init)
{
GString *string;
gint len;
if (init == NULL || *init == '\0')
return g_string_sized_new(2);
len = strlen(init);
string = g_string_sized_new(len + 2);
g_string_append_len(string, init, len);
return string;
}
void g_string_append_printf(GString *string, const gchar *format, ...)
{
gchar buffer[1024];
gint length;
va_list args;
va_start(args, format);
length = vsnprintf(buffer, sizeof(buffer) - 1, format, args);
va_end(args);
g_string_append_len(string, buffer, length);
}
gchar *g_string_free(GString *string, gboolean free_segment)
{
gchar *segment;
if (free_segment) {
g_free(string->str);
segment = NULL;
} else
segment = string->str;
g_free(string);
return segment;
}
/* GMarkup */
struct _GMarkupParseContext {
char dummy;
};
GMarkupParseContext *g_markup_parse_context_new(const GMarkupParser *parser,
GMarkupParseFlags flags,
gpointer user_data,
GDestroyNotify user_data_dnotify)
{
return g_new0(GMarkupParseContext, 1);
}
gboolean g_markup_parse_context_parse(GMarkupParseContext *context,
const gchar *text, gssize text_len,
GError **error)
{
g_set_error(error, 0, 0, "Not implemented");
return FALSE;
}
void g_markup_parse_context_free(GMarkupParseContext *context)
{
g_free(context);
}
static gchar *g_build_pathname_va(const gchar *first_element,
va_list args, gpointer *data)
{
gchar result[PATH_MAX], *element;
strncpy(result, first_element, PATH_MAX);
element = va_arg(args, gchar *);
while (element) {
g_strlcat(result, "/", PATH_MAX);
g_strlcat(result, element, PATH_MAX);
element = va_arg(args, gchar *);
}
va_end(args);
return g_strdup(result);
}
gchar *g_build_filename(const gchar *first_element, ...)
{
gchar *str;
va_list args;
va_start(args, first_element);
str = g_build_pathname_va(first_element, args, NULL);
va_end(args);
return str;
}
/* GDir */
GDir *g_dir_open(const gchar *path, guint flags, GError **error)
{
GDir *dir;
if (path == NULL)
return NULL;
dir = g_new(GDir, 1);
dir->dirp = opendir(path);
if (dir->dirp)
return dir;
/* error case */
g_set_error(error, 0, 0, "Error opening directory '%s': %s",
path, strerror(errno));
g_free(dir);
return NULL;
}
const gchar *g_dir_read_name(GDir *dir)
{
struct dirent *entry;
if (dir == NULL)
return NULL;
entry = readdir(dir->dirp);
while (entry && (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0))
entry = readdir(dir->dirp);
return entry ? entry->d_name : NULL;
}
void g_dir_close(GDir *dir)
{
if (dir == NULL)
return;
closedir(dir->dirp);
g_free(dir);
}