#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; }; 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; } 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_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; } /* 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; } /* 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; }