e-dbhash.c \
e-db3-utils.c \
e-file-cache.c \
+ e-iconv.c \
e-iterator.c \
e-list.c \
e-list-iterator.c \
e-memory.c \
+ e-msgport.c \
e-sexp.c \
e-source-group.c \
e-source-list.c \
e-source.c \
+ e-time-utils.c \
e-uid.c \
e-url.c \
e-util.c \
+ e-trie.c \
e-xml-hash-utils.c \
md5-utils.c
e-db3-utils.h \
e-dbhash.h \
e-file-cache.h \
+ e-iconv.h \
e-iterator.h \
e-list.h \
e-list-iterator.h \
e-memory.h \
+ e-msgport.h \
e-sexp.h \
e-source-group.h \
e-source-list.h \
e-source.h \
+ e-time-utils.h \
+ e-trie.h \
e-uid.h \
e-url.h \
e-util.h \
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-iconv.c
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jeffery Stedfast <fejj@ximian.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include "e-iconv.h"
+
+#include <locale.h>
+
+#ifdef HAVE_CODESET
+#include <langinfo.h>
+#endif
+
+#include "iconv-detect.h"
+
+#define cd(x)
+
+#ifdef G_THREADS_ENABLED
+static GStaticMutex lock = G_STATIC_MUTEX_INIT;
+#define LOCK() g_static_mutex_lock(&lock)
+#define UNLOCK() g_static_mutex_unlock(&lock)
+#else
+#define LOCK()
+#define UNLOCK()
+#endif
+
+typedef struct _EDListNode {
+ struct _EDListNode *next;
+ struct _EDListNode *prev;
+} EDListNode;
+
+typedef struct _EDList {
+ struct _EDListNode *head;
+ struct _EDListNode *tail;
+ struct _EDListNode *tailpred;
+} EDList;
+
+#define E_DLIST_INITIALISER(l) { (EDListNode *)&l.tail, 0, (EDListNode *)&l.head }
+
+struct _iconv_cache_node {
+ struct _iconv_cache_node *next;
+ struct _iconv_cache_node *prev;
+
+ struct _iconv_cache *parent;
+
+ int busy;
+ iconv_t ip;
+};
+
+struct _iconv_cache {
+ struct _iconv_cache *next;
+ struct _iconv_cache *prev;
+
+ char *conv;
+
+ EDList open; /* stores iconv_cache_nodes, busy ones up front */
+};
+
+#define E_ICONV_CACHE_SIZE (16)
+
+static EDList iconv_cache_list;
+static GHashTable *iconv_cache;
+static GHashTable *iconv_cache_open;
+static unsigned int iconv_cache_size = 0;
+
+static GHashTable *iconv_charsets = NULL;
+static char *locale_charset = NULL;
+static char *locale_lang = NULL;
+
+struct {
+ char *charset;
+ char *iconv_name;
+} known_iconv_charsets[] = {
+#if 0
+ /* charset name, iconv-friendly charset name */
+ { "iso-8859-1", "iso-8859-1" },
+ { "iso8859-1", "iso-8859-1" },
+ /* the above mostly serves as an example for iso-style charsets,
+ but we have code that will populate the iso-*'s if/when they
+ show up in e_iconv_charset_name() so I'm
+ not going to bother putting them all in here... */
+ { "windows-cp1251", "cp1251" },
+ { "windows-1251", "cp1251" },
+ { "cp1251", "cp1251" },
+ /* the above mostly serves as an example for windows-style
+ charsets, but we have code that will parse and convert them
+ to their cp#### equivalents if/when they show up in
+ e_iconv_charset_name() so I'm not going to bother
+ putting them all in here either... */
+#endif
+ /* charset name (lowercase!), iconv-friendly name (sometimes case sensitive) */
+ { "utf-8", "UTF-8" },
+
+ /* 10646 is a special case, its usually UCS-2 big endian */
+ /* This might need some checking but should be ok for solaris/linux */
+ { "iso-10646-1", "UCS-2BE" },
+ { "iso_10646-1", "UCS-2BE" },
+ { "iso10646-1", "UCS-2BE" },
+ { "iso-10646", "UCS-2BE" },
+ { "iso_10646", "UCS-2BE" },
+ { "iso10646", "UCS-2BE" },
+
+ { "ks_c_5601-1987", "EUC-KR" },
+
+ /* FIXME: Japanese/Korean/Chinese stuff needs checking */
+ { "euckr-0", "EUC-KR" },
+ { "5601", "EUC-KR" },
+ { "zh_TW-euc", "EUC-TW" },
+ { "zh_CN.euc", "gb2312" },
+ { "zh_TW-big5", "BIG5" },
+ { "euc-cn", "gb2312" },
+ { "big5-0", "BIG5" },
+ { "big5.eten-0", "BIG5" },
+ { "big5hkscs-0", "BIG5HKSCS" },
+ { "gb2312-0", "gb2312" },
+ { "gb2312.1980-0", "gb2312" },
+ { "gb-2312", "gb2312" },
+ { "gb18030-0", "gb18030" },
+ { "gbk-0", "GBK" },
+
+ { "eucjp-0", "eucJP" },
+ { "ujis-0", "ujis" },
+ { "jisx0208.1983-0","SJIS" },
+ { "jisx0212.1990-0","SJIS" },
+ { "pck", "SJIS" },
+ { NULL, NULL }
+};
+
+
+
+/* Another copy of this trivial list implementation
+ Why? This stuff gets called a lot (potentially), should run fast,
+ and g_list's are f@@#$ed up to make this a hassle */
+static void e_dlist_init(EDList *v)
+{
+ v->head = (EDListNode *)&v->tail;
+ v->tail = 0;
+ v->tailpred = (EDListNode *)&v->head;
+}
+
+static EDListNode *e_dlist_addhead(EDList *l, EDListNode *n)
+{
+ n->next = l->head;
+ n->prev = (EDListNode *)&l->head;
+ l->head->prev = n;
+ l->head = n;
+ return n;
+}
+
+static EDListNode *e_dlist_addtail(EDList *l, EDListNode *n)
+{
+ n->next = (EDListNode *)&l->tail;
+ n->prev = l->tailpred;
+ l->tailpred->next = n;
+ l->tailpred = n;
+ return n;
+}
+
+static EDListNode *e_dlist_remove(EDListNode *n)
+{
+ n->next->prev = n->prev;
+ n->prev->next = n->next;
+ return n;
+}
+
+
+/* fucking glib... */
+static const char *
+e_strdown (char *str)
+{
+ register char *s = str;
+
+ while (*s) {
+ if (*s >= 'A' && *s <= 'Z')
+ *s += 0x20;
+ s++;
+ }
+
+ return str;
+}
+
+static const char *
+e_strup (char *str)
+{
+ register char *s = str;
+
+ while (*s) {
+ if (*s >= 'a' && *s <= 'z')
+ *s -= 0x20;
+ s++;
+ }
+
+ return str;
+}
+
+
+static void
+locale_parse_lang (const char *locale)
+{
+ char *codeset, *lang;
+
+ if ((codeset = strchr (locale, '.')))
+ lang = g_strndup (locale, codeset - locale);
+ else
+ lang = g_strdup (locale);
+
+ /* validate the language */
+ if (strlen (lang) >= 2) {
+ if (lang[2] == '-' || lang[2] == '_') {
+ /* canonicalise the lang */
+ e_strdown (lang);
+
+ /* validate the country code */
+ if (strlen (lang + 3) > 2) {
+ /* invalid country code */
+ lang[2] = '\0';
+ } else {
+ lang[2] = '-';
+ e_strup (lang + 3);
+ }
+ } else if (lang[2] != '\0') {
+ /* invalid language */
+ g_free (lang);
+ lang = NULL;
+ }
+
+ locale_lang = lang;
+ } else {
+ /* invalid language */
+ locale_lang = NULL;
+ g_free (lang);
+ }
+}
+
+/* NOTE: Owns the lock on return if keep is TRUE ! */
+static void
+e_iconv_init(int keep)
+{
+ char *from, *to, *locale;
+ int i;
+
+ LOCK();
+
+ if (iconv_charsets != NULL) {
+ if (!keep)
+ UNLOCK();
+ return;
+ }
+
+ iconv_charsets = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i = 0; known_iconv_charsets[i].charset != NULL; i++) {
+ from = g_strdup(known_iconv_charsets[i].charset);
+ to = g_strdup(known_iconv_charsets[i].iconv_name);
+ e_strdown (from);
+ g_hash_table_insert(iconv_charsets, from, to);
+ }
+
+ e_dlist_init(&iconv_cache_list);
+ iconv_cache = g_hash_table_new(g_str_hash, g_str_equal);
+ iconv_cache_open = g_hash_table_new(NULL, NULL);
+
+ locale = setlocale (LC_ALL, NULL);
+
+ if (!locale || !strcmp (locale, "C") || !strcmp (locale, "POSIX")) {
+ /* The locale "C" or "POSIX" is a portable locale; its
+ * LC_CTYPE part corresponds to the 7-bit ASCII character
+ * set.
+ */
+
+ locale_charset = NULL;
+ locale_lang = NULL;
+ } else {
+#ifdef HAVE_CODESET
+ locale_charset = g_strdup (nl_langinfo (CODESET));
+ e_strdown (locale_charset);
+#else
+ /* A locale name is typically of the form language[_terri-
+ * tory][.codeset][@modifier], where language is an ISO 639
+ * language code, territory is an ISO 3166 country code, and
+ * codeset is a character set or encoding identifier like
+ * ISO-8859-1 or UTF-8.
+ */
+ char *codeset, *p;
+
+ codeset = strchr (locale, '.');
+ if (codeset) {
+ codeset++;
+
+ /* ; is a hack for debian systems and / is a hack for Solaris systems */
+ for (p = codeset; *p && !strchr ("@;/", *p); p++);
+ locale_charset = g_strndup (codeset, p - codeset);
+ e_strdown (locale_charset);
+ } else {
+ /* charset unknown */
+ locale_charset = NULL;
+ }
+#endif
+
+ /* parse the locale lang */
+ locale_parse_lang (locale);
+
+ }
+
+ if (!keep)
+ UNLOCK();
+}
+
+const char *e_iconv_charset_name(const char *charset)
+{
+ char *name, *ret, *tmp;
+
+ if (charset == NULL)
+ return NULL;
+
+ name = g_alloca (strlen (charset) + 1);
+ strcpy (name, charset);
+ e_strdown (name);
+
+ e_iconv_init(TRUE);
+ ret = g_hash_table_lookup(iconv_charsets, name);
+ if (ret != NULL) {
+ UNLOCK();
+ return ret;
+ }
+
+ /* Unknown, try canonicalise some basic charset types to something that should work */
+ if (strncmp(name, "iso", 3) == 0) {
+ /* Convert iso-nnnn-n or isonnnn-n or iso_nnnn-n to iso-nnnn-n or isonnnn-n */
+ int iso, codepage;
+ char *p;
+
+ tmp = name + 3;
+ if (*tmp == '-' || *tmp == '_')
+ tmp++;
+
+ iso = strtoul (tmp, &p, 10);
+
+ if (iso == 10646) {
+ /* they all become ICONV_10646 */
+ ret = g_strdup (ICONV_10646);
+ } else {
+ tmp = p;
+ if (*tmp == '-' || *tmp == '_')
+ tmp++;
+
+ codepage = strtoul (tmp, &p, 10);
+
+ if (p > tmp) {
+ /* codepage is numeric */
+#ifdef __aix__
+ if (codepage == 13)
+ ret = g_strdup ("IBM-921");
+ else
+#endif /* __aix__ */
+ ret = g_strdup_printf (ICONV_ISO_D_FORMAT, iso, codepage);
+ } else {
+ /* codepage is a string - probably iso-2022-jp or something */
+ ret = g_strdup_printf (ICONV_ISO_S_FORMAT, iso, p);
+ }
+ }
+ } else if (strncmp(name, "windows-", 8) == 0) {
+ /* Convert windows-nnnnn or windows-cpnnnnn to cpnnnn */
+ tmp = name+8;
+ if (!strncmp(tmp, "cp", 2))
+ tmp+=2;
+ ret = g_strdup_printf("CP%s", tmp);
+ } else if (strncmp(name, "microsoft-", 10) == 0) {
+ /* Convert microsoft-nnnnn or microsoft-cpnnnnn to cpnnnn */
+ tmp = name+10;
+ if (!strncmp(tmp, "cp", 2))
+ tmp+=2;
+ ret = g_strdup_printf("CP%s", tmp);
+ } else {
+ /* Just assume its ok enough as is, case and all */
+ ret = g_strdup(charset);
+ }
+
+ g_hash_table_insert(iconv_charsets, g_strdup(name), ret);
+ UNLOCK();
+
+ return ret;
+}
+
+static void
+flush_entry(struct _iconv_cache *ic)
+{
+ struct _iconv_cache_node *in, *nn;
+
+ in = (struct _iconv_cache_node *)ic->open.head;
+ nn = in->next;
+ while (nn) {
+ if (in->ip != (iconv_t)-1) {
+ g_hash_table_remove(iconv_cache_open, in->ip);
+ iconv_close(in->ip);
+ }
+ g_free(in);
+ in = nn;
+ nn = in->next;
+ }
+ g_free(ic->conv);
+ g_free(ic);
+}
+
+/* This should run pretty quick, its called a lot */
+iconv_t e_iconv_open(const char *oto, const char *ofrom)
+{
+ const char *to, *from;
+ char *tofrom;
+ struct _iconv_cache *ic;
+ struct _iconv_cache_node *in;
+ int errnosav;
+ iconv_t ip;
+
+ if (oto == NULL || ofrom == NULL) {
+ errno = EINVAL;
+ return (iconv_t) -1;
+ }
+
+ to = e_iconv_charset_name (oto);
+ from = e_iconv_charset_name (ofrom);
+ tofrom = g_alloca (strlen (to) + strlen (from) + 2);
+ sprintf(tofrom, "%s%%%s", to, from);
+
+ LOCK();
+
+ ic = g_hash_table_lookup(iconv_cache, tofrom);
+ if (ic) {
+ e_dlist_remove((EDListNode *)ic);
+ } else {
+ struct _iconv_cache *last = (struct _iconv_cache *)iconv_cache_list.tailpred;
+ struct _iconv_cache *prev;
+
+ prev = last->prev;
+ while (prev && iconv_cache_size > E_ICONV_CACHE_SIZE) {
+ in = (struct _iconv_cache_node *)last->open.head;
+ if (in->next && !in->busy) {
+ cd(printf("Flushing iconv converter '%s'\n", last->conv));
+ e_dlist_remove((EDListNode *)last);
+ g_hash_table_remove(iconv_cache, last->conv);
+ flush_entry(last);
+ iconv_cache_size--;
+ }
+ last = prev;
+ prev = last->prev;
+ }
+
+ iconv_cache_size++;
+
+ ic = g_malloc(sizeof(*ic));
+ e_dlist_init(&ic->open);
+ ic->conv = g_strdup(tofrom);
+ g_hash_table_insert(iconv_cache, ic->conv, ic);
+
+ cd(printf("Creating iconv converter '%s'\n", ic->conv));
+ }
+ e_dlist_addhead(&iconv_cache_list, (EDListNode *)ic);
+
+ /* If we have a free iconv, use it */
+ in = (struct _iconv_cache_node *)ic->open.tailpred;
+ if (in->prev && !in->busy) {
+ cd(printf("using existing iconv converter '%s'\n", ic->conv));
+ ip = in->ip;
+ if (ip != (iconv_t)-1) {
+ /* work around some broken iconv implementations
+ * that die if the length arguments are NULL
+ */
+ size_t buggy_iconv_len = 0;
+ char *buggy_iconv_buf = NULL;
+
+ /* resets the converter */
+ iconv(ip, &buggy_iconv_buf, &buggy_iconv_len, &buggy_iconv_buf, &buggy_iconv_len);
+ in->busy = TRUE;
+ e_dlist_remove((EDListNode *)in);
+ e_dlist_addhead(&ic->open, (EDListNode *)in);
+ }
+ } else {
+ cd(printf("creating new iconv converter '%s'\n", ic->conv));
+ ip = iconv_open(to, from);
+ in = g_malloc(sizeof(*in));
+ in->ip = ip;
+ in->parent = ic;
+ e_dlist_addhead(&ic->open, (EDListNode *)in);
+ if (ip != (iconv_t)-1) {
+ g_hash_table_insert(iconv_cache_open, ip, in);
+ in->busy = TRUE;
+ } else {
+ errnosav = errno;
+ g_warning("Could not open converter for '%s' to '%s' charset", from, to);
+ in->busy = FALSE;
+ errno = errnosav;
+ }
+ }
+
+ UNLOCK();
+
+ return ip;
+}
+
+size_t e_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char ** outbuf, size_t *outbytesleft)
+{
+ return iconv(cd, (char **) inbuf, inbytesleft, outbuf, outbytesleft);
+}
+
+void
+e_iconv_close(iconv_t ip)
+{
+ struct _iconv_cache_node *in;
+
+ if (ip == (iconv_t)-1)
+ return;
+
+ LOCK();
+ in = g_hash_table_lookup(iconv_cache_open, ip);
+ if (in) {
+ cd(printf("closing iconv converter '%s'\n", in->parent->conv));
+ e_dlist_remove((EDListNode *)in);
+ in->busy = FALSE;
+ e_dlist_addtail(&in->parent->open, (EDListNode *)in);
+ } else {
+ g_warning("trying to close iconv i dont know about: %p", ip);
+ iconv_close(ip);
+ }
+ UNLOCK();
+
+}
+
+const char *e_iconv_locale_charset(void)
+{
+ e_iconv_init(FALSE);
+
+ return locale_charset;
+}
+
+
+const char *
+e_iconv_locale_language (void)
+{
+ e_iconv_init (FALSE);
+
+ return locale_lang;
+}
+
+/* map CJKR charsets to their language code */
+/* NOTE: only support charset names that will be returned by
+ * e_iconv_charset_name() so that we don't have to keep track of all
+ * the aliases too. */
+static struct {
+ char *charset;
+ char *lang;
+} cjkr_lang_map[] = {
+ { "Big5", "zh" },
+ { "BIG5HKSCS", "zh" },
+ { "gb2312", "zh" },
+ { "gb18030", "zh" },
+ { "gbk", "zh" },
+ { "euc-tw", "zh" },
+ { "iso-2022-jp", "ja" },
+ { "sjis", "ja" },
+ { "ujis", "ja" },
+ { "eucJP", "ja" },
+ { "euc-jp", "ja" },
+ { "euc-kr", "ko" },
+ { "koi8-r", "ru" },
+ { "koi8-u", "uk" }
+};
+
+#define NUM_CJKR_LANGS (sizeof (cjkr_lang_map) / sizeof (cjkr_lang_map[0]))
+
+const char *
+e_iconv_charset_language (const char *charset)
+{
+ int i;
+
+ if (!charset)
+ return NULL;
+
+ charset = e_iconv_charset_name (charset);
+ for (i = 0; i < NUM_CJKR_LANGS; i++) {
+ if (!strcasecmp (cjkr_lang_map[i].charset, charset))
+ return cjkr_lang_map[i].lang;
+ }
+
+ return NULL;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * e-iconv.h
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ * Michael Zucchi <notzed@ximian.com>
+ * Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _E_ICONV_H_
+#define _E_ICONV_H_
+
+#include <iconv.h>
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+const char *e_iconv_charset_name(const char *charset);
+iconv_t e_iconv_open(const char *oto, const char *ofrom);
+size_t e_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char ** outbuf, size_t *outbytesleft);
+void e_iconv_close(iconv_t ip);
+const char *e_iconv_locale_charset(void);
+
+/* languages */
+const char *e_iconv_locale_language (void);
+const char *e_iconv_charset_language (const char *charset);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* !_E_ICONV_H_ */
#include <string.h> /* memset() */
#include <stdlib.h> /* alloca() */
+#include <glib.h>
#define s(x) /* strv debug */
#define p(x) /* poolv debug */
/* first, setup the tree/list so we can map free block addresses to block addresses */
tree = g_tree_new((GCompareFunc)tree_compare);
for (i=0;i<m->blocks->len;i++) {
- ci = g_alloca(sizeof(*ci));
+ ci = alloca(sizeof(*ci));
ci->count = 0;
ci->base = m->blocks->pdata[i];
ci->size = m->blocksize * m->atomsize;
{
if (pool) {
e_mempool_flush(pool, 1);
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_lock(&mempool_mutex);
+#endif
e_memchunk_free(mempool_memchunk, pool);
+#ifdef G_THREADS_ENABLED
+ g_static_mutex_unlock(&mempool_mutex);
+#endif
}
}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ * Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <pthread.h>
+
+#include <glib.h>
+
+#ifdef HAVE_NSS
+#include <nspr.h>
+#endif
+
+#include "e-msgport.h"
+
+#define m(x) /* msgport debug */
+#define t(x) /* thread debug */
+#define c(x) /* cache debug */
+
+void e_dlist_init(EDList *v)
+{
+ v->head = (EDListNode *)&v->tail;
+ v->tail = 0;
+ v->tailpred = (EDListNode *)&v->head;
+}
+
+EDListNode *e_dlist_addhead(EDList *l, EDListNode *n)
+{
+ n->next = l->head;
+ n->prev = (EDListNode *)&l->head;
+ l->head->prev = n;
+ l->head = n;
+ return n;
+}
+
+EDListNode *e_dlist_addtail(EDList *l, EDListNode *n)
+{
+ n->next = (EDListNode *)&l->tail;
+ n->prev = l->tailpred;
+ l->tailpred->next = n;
+ l->tailpred = n;
+ return n;
+}
+
+EDListNode *e_dlist_remove(EDListNode *n)
+{
+ n->next->prev = n->prev;
+ n->prev->next = n->next;
+ return n;
+}
+
+EDListNode *e_dlist_remhead(EDList *l)
+{
+ EDListNode *n, *nn;
+
+ n = l->head;
+ nn = n->next;
+ if (nn) {
+ nn->prev = n->prev;
+ l->head = nn;
+ return n;
+ }
+ return NULL;
+}
+
+EDListNode *e_dlist_remtail(EDList *l)
+{
+ EDListNode *n, *np;
+
+ n = l->tailpred;
+ np = n->prev;
+ if (np) {
+ np->next = n->next;
+ l->tailpred = np;
+ return n;
+ }
+ return NULL;
+}
+
+int e_dlist_empty(EDList *l)
+{
+ return (l->head == (EDListNode *)&l->tail);
+}
+
+int e_dlist_length(EDList *l)
+{
+ EDListNode *n, *nn;
+ int count = 0;
+
+ n = l->head;
+ nn = n->next;
+ while (nn) {
+ count++;
+ n = nn;
+ nn = n->next;
+ }
+
+ return count;
+}
+
+struct _EMCache {
+ GMutex *lock;
+ GHashTable *key_table;
+ EDList lru_list;
+ size_t node_size;
+ int node_count;
+ time_t timeout;
+ GFreeFunc node_free;
+};
+
+/**
+ * em_cache_new:
+ * @timeout:
+ * @nodesize:
+ * @nodefree:
+ *
+ * Setup a new timeout cache. @nodesize is the size of nodes in the
+ * cache, and @nodefree will be called to free YOUR content.
+ *
+ * Return value:
+ **/
+EMCache *
+em_cache_new(time_t timeout, size_t nodesize, GFreeFunc nodefree)
+{
+ struct _EMCache *emc;
+
+ emc = g_malloc0(sizeof(*emc));
+ emc->node_size = nodesize;
+ emc->key_table = g_hash_table_new(g_str_hash, g_str_equal);
+ emc->node_free = nodefree;
+ e_dlist_init(&emc->lru_list);
+ emc->lock = g_mutex_new();
+ emc->timeout = timeout;
+
+ return emc;
+}
+
+/**
+ * em_cache_destroy:
+ * @emc:
+ *
+ * destroy the cache, duh.
+ **/
+void
+em_cache_destroy(EMCache *emc)
+{
+ em_cache_clear(emc);
+ g_mutex_free(emc->lock);
+ g_free(emc);
+}
+
+/**
+ * em_cache_lookup:
+ * @emc:
+ * @key:
+ *
+ * Lookup a cache node. once you're finished with it, you need to
+ * unref it.
+ *
+ * Return value:
+ **/
+EMCacheNode *
+em_cache_lookup(EMCache *emc, const char *key)
+{
+ EMCacheNode *n;
+
+ g_mutex_lock(emc->lock);
+ n = g_hash_table_lookup(emc->key_table, key);
+ if (n) {
+ e_dlist_remove((EDListNode *)n);
+ e_dlist_addhead(&emc->lru_list, (EDListNode *)n);
+ n->stamp = time(0);
+ n->ref_count++;
+ }
+ g_mutex_unlock(emc->lock);
+
+ c(printf("looking up '%s' %s\n", key, n?"found":"not found"));
+
+ return n;
+}
+
+/**
+ * em_cache_node_new:
+ * @emc:
+ * @key:
+ *
+ * Create a new key'd cache node. The node will not be added to the
+ * cache until you insert it.
+ *
+ * Return value:
+ **/
+EMCacheNode *
+em_cache_node_new(EMCache *emc, const char *key)
+{
+ EMCacheNode *n;
+
+ /* this could use memchunks, but its probably overkill */
+ n = g_malloc0(emc->node_size);
+ n->key = g_strdup(key);
+
+ return n;
+}
+
+/**
+ * em_cache_node_unref:
+ * @emc:
+ * @n:
+ *
+ * unref a cache node, you can only unref nodes which have been looked
+ * up.
+ **/
+void
+em_cache_node_unref(EMCache *emc, EMCacheNode *n)
+{
+ g_mutex_lock(emc->lock);
+ g_assert(n->ref_count > 0);
+ n->ref_count--;
+ g_mutex_unlock(emc->lock);
+}
+
+/**
+ * em_cache_add:
+ * @emc:
+ * @n:
+ *
+ * Add a cache node to the cache, once added the memory is owned by
+ * the cache. If there are conflicts and the old node is still in
+ * use, then the new node is not added, otherwise it is added and any
+ * nodes older than the expire time are flushed.
+ **/
+void
+em_cache_add(EMCache *emc, EMCacheNode *n)
+{
+ EMCacheNode *old, *prev;
+ EDList old_nodes;
+
+ e_dlist_init(&old_nodes);
+
+ g_mutex_lock(emc->lock);
+ old = g_hash_table_lookup(emc->key_table, n->key);
+ if (old != NULL) {
+ if (old->ref_count == 0) {
+ g_hash_table_remove(emc->key_table, old->key);
+ e_dlist_remove((EDListNode *)old);
+ e_dlist_addtail(&old_nodes, (EDListNode *)old);
+ goto insert;
+ } else {
+ e_dlist_addtail(&old_nodes, (EDListNode *)n);
+ }
+ } else {
+ time_t now;
+ insert:
+ now = time(0);
+ g_hash_table_insert(emc->key_table, n->key, n);
+ e_dlist_addhead(&emc->lru_list, (EDListNode *)n);
+ n->stamp = now;
+ emc->node_count++;
+
+ c(printf("inserting node %s\n", n->key));
+
+ old = (EMCacheNode *)emc->lru_list.tailpred;
+ prev = old->prev;
+ while (prev && old->stamp < now - emc->timeout) {
+ if (old->ref_count == 0) {
+ c(printf("expiring node %s\n", old->key));
+ g_hash_table_remove(emc->key_table, old->key);
+ e_dlist_remove((EDListNode *)old);
+ e_dlist_addtail(&old_nodes, (EDListNode *)old);
+ }
+ old = prev;
+ prev = prev->prev;
+ }
+ }
+
+ g_mutex_unlock(emc->lock);
+
+ while ((old = (EMCacheNode *)e_dlist_remhead(&old_nodes))) {
+ emc->node_free(old);
+ g_free(old->key);
+ g_free(old);
+ }
+}
+
+/**
+ * em_cache_clear:
+ * @emc:
+ *
+ * clear the cache. just for api completeness.
+ **/
+void
+em_cache_clear(EMCache *emc)
+{
+ EMCacheNode *node;
+ EDList old_nodes;
+
+ e_dlist_init(&old_nodes);
+ g_mutex_lock(emc->lock);
+ while ((node = (EMCacheNode *)e_dlist_remhead(&emc->lru_list)))
+ e_dlist_addtail(&old_nodes, (EDListNode *)node);
+ g_mutex_unlock(emc->lock);
+
+ while ((node = (EMCacheNode *)e_dlist_remhead(&old_nodes))) {
+ emc->node_free(node);
+ g_free(node->key);
+ g_free(node);
+ }
+}
+
+struct _EMsgPort {
+ EDList queue;
+ int condwait; /* how many waiting in condwait */
+ union {
+ int pipe[2];
+ struct {
+ int read;
+ int write;
+ } fd;
+ } pipe;
+#ifdef HAVE_NSS
+ struct {
+ PRFileDesc *read;
+ PRFileDesc *write;
+ } prpipe;
+#endif
+ /* @#@$#$ glib stuff */
+ GCond *cond;
+ GMutex *lock;
+};
+
+EMsgPort *e_msgport_new(void)
+{
+ EMsgPort *mp;
+
+ mp = g_malloc(sizeof(*mp));
+ e_dlist_init(&mp->queue);
+ mp->lock = g_mutex_new();
+ mp->cond = g_cond_new();
+ mp->pipe.fd.read = -1;
+ mp->pipe.fd.write = -1;
+#ifdef HAVE_NSS
+ mp->prpipe.read = NULL;
+ mp->prpipe.write = NULL;
+#endif
+ mp->condwait = 0;
+
+ return mp;
+}
+
+void e_msgport_destroy(EMsgPort *mp)
+{
+ g_mutex_free(mp->lock);
+ g_cond_free(mp->cond);
+ if (mp->pipe.fd.read != -1) {
+ close(mp->pipe.fd.read);
+ close(mp->pipe.fd.write);
+ }
+#ifdef HAVE_NSS
+ if (mp->prpipe.read) {
+ PR_Close(mp->prpipe.read);
+ PR_Close(mp->prpipe.write);
+ }
+#endif
+ g_free(mp);
+}
+
+/* get a fd that can be used to wait on the port asynchronously */
+int e_msgport_fd(EMsgPort *mp)
+{
+ int fd;
+
+ g_mutex_lock(mp->lock);
+ fd = mp->pipe.fd.read;
+ if (fd == -1) {
+ pipe(mp->pipe.pipe);
+ fd = mp->pipe.fd.read;
+ }
+ g_mutex_unlock(mp->lock);
+
+ return fd;
+}
+
+#ifdef HAVE_NSS
+PRFileDesc *e_msgport_prfd(EMsgPort *mp)
+{
+ PRFileDesc *fd;
+
+ g_mutex_lock(mp->lock);
+ fd = mp->prpipe.read;
+ if (fd == NULL) {
+ PR_CreatePipe(&mp->prpipe.read, &mp->prpipe.write);
+ fd = mp->prpipe.read;
+ }
+ g_mutex_unlock(mp->lock);
+
+ return fd;
+}
+#endif
+
+void e_msgport_put(EMsgPort *mp, EMsg *msg)
+{
+ int fd;
+#ifdef HAVE_NSS
+ PRFileDesc *prfd;
+#endif
+
+ m(printf("put:\n"));
+ g_mutex_lock(mp->lock);
+ e_dlist_addtail(&mp->queue, &msg->ln);
+ if (mp->condwait > 0) {
+ m(printf("put: condwait > 0, waking up\n"));
+ g_cond_signal(mp->cond);
+ }
+ fd = mp->pipe.fd.write;
+#ifdef HAVE_NSS
+ prfd = mp->prpipe.write;
+#endif
+ g_mutex_unlock(mp->lock);
+
+ if (fd != -1) {
+ m(printf("put: have pipe, writing notification to it\n"));
+ write(fd, "", 1);
+ }
+
+#ifdef HAVE_NSS
+ if (prfd != NULL) {
+ m(printf("put: have pr pipe, writing notification to it\n"));
+ PR_Write(prfd, "", 1);
+ }
+#endif
+ m(printf("put: done\n"));
+}
+
+static void
+msgport_cleanlock(void *data)
+{
+ EMsgPort *mp = data;
+
+ g_mutex_unlock(mp->lock);
+}
+
+EMsg *e_msgport_wait(EMsgPort *mp)
+{
+ EMsg *msg;
+
+ m(printf("wait:\n"));
+ g_mutex_lock(mp->lock);
+ while (e_dlist_empty(&mp->queue)) {
+ if (mp->pipe.fd.read != -1) {
+ fd_set rfds;
+ int retry;
+
+ m(printf("wait: waitng on pipe\n"));
+ g_mutex_unlock(mp->lock);
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(mp->pipe.fd.read, &rfds);
+ retry = select(mp->pipe.fd.read+1, &rfds, NULL, NULL, NULL) == -1 && errno == EINTR;
+ pthread_testcancel();
+ } while (retry);
+ g_mutex_lock(mp->lock);
+ m(printf("wait: got pipe\n"));
+#ifdef HAVE_NSS
+ } else if (mp->prpipe.read != NULL) {
+ PRPollDesc polltable[1];
+ int retry;
+
+ m(printf("wait: waitng on pr pipe\n"));
+ g_mutex_unlock(mp->lock);
+ do {
+ polltable[0].fd = mp->prpipe.read;
+ polltable[0].in_flags = PR_POLL_READ|PR_POLL_ERR;
+ retry = PR_Poll(polltable, 1, PR_INTERVAL_NO_TIMEOUT) == -1 && PR_GetError() == PR_PENDING_INTERRUPT_ERROR;
+ pthread_testcancel();
+ } while (retry);
+ g_mutex_lock(mp->lock);
+ m(printf("wait: got pr pipe\n"));
+#endif /* HAVE_NSS */
+ } else {
+ m(printf("wait: waiting on condition\n"));
+ mp->condwait++;
+ /* if we are cancelled in the cond-wait, then we need to unlock our lock when we cleanup */
+ pthread_cleanup_push(msgport_cleanlock, mp);
+ g_cond_wait(mp->cond, mp->lock);
+ pthread_cleanup_pop(0);
+ m(printf("wait: got condition\n"));
+ mp->condwait--;
+ }
+ }
+ msg = (EMsg *)mp->queue.head;
+ m(printf("wait: message = %p\n", msg));
+ g_mutex_unlock(mp->lock);
+ m(printf("wait: done\n"));
+ return msg;
+}
+
+EMsg *e_msgport_get(EMsgPort *mp)
+{
+ EMsg *msg;
+ char dummy[1];
+
+ g_mutex_lock(mp->lock);
+ msg = (EMsg *)e_dlist_remhead(&mp->queue);
+ if (msg) {
+ if (mp->pipe.fd.read != -1)
+ read(mp->pipe.fd.read, dummy, 1);
+#ifdef HAVE_NSS
+ if (mp->prpipe.read != NULL) {
+ int c;
+ c = PR_Read(mp->prpipe.read, dummy, 1);
+ g_assert(c == 1);
+ }
+#endif
+ }
+ m(printf("get: message = %p\n", msg));
+ g_mutex_unlock(mp->lock);
+
+ return msg;
+}
+
+void e_msgport_reply(EMsg *msg)
+{
+ if (msg->reply_port) {
+ e_msgport_put(msg->reply_port, msg);
+ }
+ /* else lost? */
+}
+
+struct _thread_info {
+ pthread_t id;
+ int busy;
+};
+
+struct _EThread {
+ struct _EThread *next;
+ struct _EThread *prev;
+
+ EMsgPort *server_port;
+ EMsgPort *reply_port;
+ pthread_mutex_t mutex;
+ e_thread_t type;
+ int queue_limit;
+
+ int waiting; /* if we are waiting for a new message, count of waiting processes */
+ pthread_t id; /* id of our running child thread */
+ GList *id_list; /* if THREAD_NEW, then a list of our child threads in thread_info structs */
+
+ EThreadFunc destroy;
+ void *destroy_data;
+
+ EThreadFunc received;
+ void *received_data;
+
+ EThreadFunc lost;
+ void *lost_data;
+};
+
+/* All active threads */
+static EDList ethread_list = E_DLIST_INITIALISER(ethread_list);
+static pthread_mutex_t ethread_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define E_THREAD_NONE ((pthread_t)~0)
+#define E_THREAD_QUIT_REPLYPORT ((struct _EMsgPort *)~0)
+
+static void thread_destroy_msg(EThread *e, EMsg *m);
+
+static struct _thread_info *thread_find(EThread *e, pthread_t id)
+{
+ GList *node;
+ struct _thread_info *info;
+
+ node = e->id_list;
+ while (node) {
+ info = node->data;
+ if (info->id == id)
+ return info;
+ node = node->next;
+ }
+ return NULL;
+}
+
+#if 0
+static void thread_remove(EThread *e, pthread_t id)
+{
+ GList *node;
+ struct _thread_info *info;
+
+ node = e->id_list;
+ while (node) {
+ info = node->data;
+ if (info->id == id) {
+ e->id_list = g_list_remove(e->id_list, info);
+ g_free(info);
+ }
+ node = node->next;
+ }
+}
+#endif
+
+EThread *e_thread_new(e_thread_t type)
+{
+ EThread *e;
+
+ e = g_malloc0(sizeof(*e));
+ pthread_mutex_init(&e->mutex, 0);
+ e->type = type;
+ e->server_port = e_msgport_new();
+ e->id = E_THREAD_NONE;
+ e->queue_limit = INT_MAX;
+
+ pthread_mutex_lock(ðread_lock);
+ e_dlist_addtail(ðread_list, (EDListNode *)e);
+ pthread_mutex_unlock(ðread_lock);
+
+ return e;
+}
+
+/* close down the threads & resources etc */
+void e_thread_destroy(EThread *e)
+{
+ int busy = FALSE;
+ EMsg *msg;
+ struct _thread_info *info;
+ GList *l;
+
+ /* make sure we soak up all the messages first */
+ while ( (msg = e_msgport_get(e->server_port)) ) {
+ thread_destroy_msg(e, msg);
+ }
+
+ pthread_mutex_lock(&e->mutex);
+
+ switch(e->type) {
+ case E_THREAD_QUEUE:
+ case E_THREAD_DROP:
+ /* if we have a thread, 'kill' it */
+ if (e->id != E_THREAD_NONE) {
+ pthread_t id = e->id;
+
+ t(printf("Sending thread '%d' quit message\n", id));
+
+ e->id = E_THREAD_NONE;
+
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = E_THREAD_QUIT_REPLYPORT;
+ e_msgport_put(e->server_port, msg);
+
+ pthread_mutex_unlock(&e->mutex);
+ t(printf("Joining thread '%d'\n", id));
+ pthread_join(id, 0);
+ t(printf("Joined thread '%d'!\n", id));
+ pthread_mutex_lock(&e->mutex);
+ }
+ busy = e->id != E_THREAD_NONE;
+ break;
+ case E_THREAD_NEW:
+ /* first, send everyone a quit message */
+ l = e->id_list;
+ while (l) {
+ info = l->data;
+ t(printf("Sending thread '%d' quit message\n", info->id));
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = E_THREAD_QUIT_REPLYPORT;
+ e_msgport_put(e->server_port, msg);
+ l = l->next;
+ }
+
+ /* then, wait for everyone to quit */
+ while (e->id_list) {
+ info = e->id_list->data;
+ e->id_list = g_list_remove(e->id_list, info);
+ pthread_mutex_unlock(&e->mutex);
+ t(printf("Joining thread '%d'\n", info->id));
+ pthread_join(info->id, 0);
+ t(printf("Joined thread '%d'!\n", info->id));
+ pthread_mutex_lock(&e->mutex);
+ g_free(info);
+ }
+ busy = g_list_length(e->id_list) != 0;
+ break;
+ }
+
+ pthread_mutex_unlock(&e->mutex);
+
+ /* and clean up, if we can */
+ if (busy) {
+ g_warning("threads were busy, leaked EThread");
+ return;
+ }
+
+ pthread_mutex_lock(ðread_lock);
+ e_dlist_remove((EDListNode *)e);
+ pthread_mutex_unlock(ðread_lock);
+
+ pthread_mutex_destroy(&e->mutex);
+ e_msgport_destroy(e->server_port);
+ g_free(e);
+}
+
+/* set the queue maximum depth, what happens when the queue
+ fills up depends on the queue type */
+void e_thread_set_queue_limit(EThread *e, int limit)
+{
+ e->queue_limit = limit;
+}
+
+/* set a msg destroy callback, this can not call any e_thread functions on @e */
+void e_thread_set_msg_destroy(EThread *e, EThreadFunc destroy, void *data)
+{
+ pthread_mutex_lock(&e->mutex);
+ e->destroy = destroy;
+ e->destroy_data = data;
+ pthread_mutex_unlock(&e->mutex);
+}
+
+/* set a message lost callback, called if any message is discarded */
+void e_thread_set_msg_lost(EThread *e, EThreadFunc lost, void *data)
+{
+ pthread_mutex_lock(&e->mutex);
+ e->lost = lost;
+ e->lost_data = lost;
+ pthread_mutex_unlock(&e->mutex);
+}
+
+/* set a reply port, if set, then send messages back once finished */
+void e_thread_set_reply_port(EThread *e, EMsgPort *reply_port)
+{
+ e->reply_port = reply_port;
+}
+
+/* set a received data callback */
+void e_thread_set_msg_received(EThread *e, EThreadFunc received, void *data)
+{
+ pthread_mutex_lock(&e->mutex);
+ e->received = received;
+ e->received_data = data;
+ pthread_mutex_unlock(&e->mutex);
+}
+
+/* find out if we're busy doing any work, e==NULL, check for all work */
+int e_thread_busy(EThread *e)
+{
+ int busy = FALSE;
+
+ if (e == NULL) {
+ pthread_mutex_lock(ðread_lock);
+ e = (EThread *)ethread_list.head;
+ while (e->next && !busy) {
+ busy = e_thread_busy(e);
+ e = e->next;
+ }
+ pthread_mutex_unlock(ðread_lock);
+ } else {
+ pthread_mutex_lock(&e->mutex);
+ switch (e->type) {
+ case E_THREAD_QUEUE:
+ case E_THREAD_DROP:
+ busy = e->waiting != 1 && e->id != E_THREAD_NONE;
+ break;
+ case E_THREAD_NEW:
+ busy = e->waiting != g_list_length(e->id_list);
+ break;
+ }
+ pthread_mutex_unlock(&e->mutex);
+ }
+
+ return busy;
+}
+
+static void
+thread_destroy_msg(EThread *e, EMsg *m)
+{
+ EThreadFunc func;
+ void *func_data;
+
+ /* we do this so we never get an incomplete/unmatched callback + data */
+ pthread_mutex_lock(&e->mutex);
+ func = e->destroy;
+ func_data = e->destroy_data;
+ pthread_mutex_unlock(&e->mutex);
+
+ if (func)
+ func(e, m, func_data);
+}
+
+static void
+thread_received_msg(EThread *e, EMsg *m)
+{
+ EThreadFunc func;
+ void *func_data;
+
+ /* we do this so we never get an incomplete/unmatched callback + data */
+ pthread_mutex_lock(&e->mutex);
+ func = e->received;
+ func_data = e->received_data;
+ pthread_mutex_unlock(&e->mutex);
+
+ if (func)
+ func(e, m, func_data);
+ else
+ g_warning("No processing callback for EThread, message unprocessed");
+}
+
+static void
+thread_lost_msg(EThread *e, EMsg *m)
+{
+ EThreadFunc func;
+ void *func_data;
+
+ /* we do this so we never get an incomplete/unmatched callback + data */
+ pthread_mutex_lock(&e->mutex);
+ func = e->lost;
+ func_data = e->lost_data;
+ pthread_mutex_unlock(&e->mutex);
+
+ if (func)
+ func(e, m, func_data);
+}
+
+/* the actual thread dispatcher */
+static void *
+thread_dispatch(void *din)
+{
+ EThread *e = din;
+ EMsg *m;
+ struct _thread_info *info;
+ pthread_t self = pthread_self();
+
+ t(printf("dispatch thread started: %ld\n", pthread_self()));
+
+ while (1) {
+ pthread_mutex_lock(&e->mutex);
+ m = e_msgport_get(e->server_port);
+ if (m == NULL) {
+ /* nothing to do? If we are a 'new' type thread, just quit.
+ Otherwise, go into waiting (can be cancelled here) */
+ info = NULL;
+ switch (e->type) {
+ case E_THREAD_NEW:
+ case E_THREAD_QUEUE:
+ case E_THREAD_DROP:
+ info = thread_find(e, self);
+ if (info)
+ info->busy = FALSE;
+ e->waiting++;
+ pthread_mutex_unlock(&e->mutex);
+ e_msgport_wait(e->server_port);
+ pthread_mutex_lock(&e->mutex);
+ e->waiting--;
+ pthread_mutex_unlock(&e->mutex);
+ break;
+#if 0
+ case E_THREAD_NEW:
+ e->id_list = g_list_remove(e->id_list, (void *)pthread_self());
+ pthread_mutex_unlock(&e->mutex);
+ return 0;
+#endif
+ }
+
+ continue;
+ } else if (m->reply_port == E_THREAD_QUIT_REPLYPORT) {
+ t(printf("Thread %d got quit message\n", self));
+ /* Handle a quit message, say we're quitting, free the message, and break out of the loop */
+ info = thread_find(e, self);
+ if (info)
+ info->busy = 2;
+ pthread_mutex_unlock(&e->mutex);
+ g_free(m);
+ break;
+ } else {
+ info = thread_find(e, self);
+ if (info)
+ info->busy = TRUE;
+ }
+ pthread_mutex_unlock(&e->mutex);
+
+ t(printf("got message in dispatch thread\n"));
+
+ /* process it */
+ thread_received_msg(e, m);
+
+ /* if we have a reply port, send it back, otherwise, lose it */
+ if (m->reply_port) {
+ e_msgport_reply(m);
+ } else {
+ thread_destroy_msg(e, m);
+ }
+ }
+
+ return NULL;
+}
+
+/* send a message to the thread, start thread if necessary */
+void e_thread_put(EThread *e, EMsg *msg)
+{
+ pthread_t id;
+ EMsg *dmsg = NULL;
+
+ pthread_mutex_lock(&e->mutex);
+
+ /* the caller forgot to tell us what to do, well, we can't do anything can we */
+ if (e->received == NULL) {
+ pthread_mutex_unlock(&e->mutex);
+ g_warning("EThread called with no receiver function, no work to do!");
+ thread_destroy_msg(e, msg);
+ return;
+ }
+
+ msg->reply_port = e->reply_port;
+
+ switch(e->type) {
+ case E_THREAD_QUEUE:
+ /* if the queue is full, lose this new addition */
+ if (e_dlist_length(&e->server_port->queue) < e->queue_limit) {
+ e_msgport_put(e->server_port, msg);
+ } else {
+ printf("queue limit reached, dropping new message\n");
+ dmsg = msg;
+ }
+ break;
+ case E_THREAD_DROP:
+ /* if the queue is full, lose the oldest (unprocessed) message */
+ if (e_dlist_length(&e->server_port->queue) < e->queue_limit) {
+ e_msgport_put(e->server_port, msg);
+ } else {
+ printf("queue limit reached, dropping old message\n");
+ e_msgport_put(e->server_port, msg);
+ dmsg = e_msgport_get(e->server_port);
+ }
+ break;
+ case E_THREAD_NEW:
+ /* it is possible that an existing thread can catch this message, so
+ we might create a thread with no work to do.
+ but that doesn't matter, the other alternative that it be lost is worse */
+ e_msgport_put(e->server_port, msg);
+ if (e->waiting == 0
+ && g_list_length(e->id_list) < e->queue_limit
+ && pthread_create(&id, NULL, thread_dispatch, e) == 0) {
+ struct _thread_info *info = g_malloc0(sizeof(*info));
+ t(printf("created NEW thread %ld\n", id));
+ info->id = id;
+ info->busy = TRUE;
+ e->id_list = g_list_append(e->id_list, info);
+ }
+ pthread_mutex_unlock(&e->mutex);
+ return;
+ }
+
+ /* create the thread, if there is none to receive it yet */
+ if (e->id == E_THREAD_NONE) {
+ int err;
+
+ if ((err = pthread_create(&e->id, NULL, thread_dispatch, e)) != 0) {
+ g_warning("Could not create dispatcher thread, message queued?: %s", strerror(err));
+ e->id = E_THREAD_NONE;
+ }
+ }
+
+ pthread_mutex_unlock(&e->mutex);
+
+ if (dmsg) {
+ thread_lost_msg(e, dmsg);
+ thread_destroy_msg(e, dmsg);
+ }
+}
+
+/* yet-another-mutex interface */
+struct _EMutex {
+ int type;
+ pthread_t owner;
+ short waiters;
+ short depth;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+
+/* sigh, this is just painful to have to need, but recursive
+ read/write, etc mutexes just aren't very common in thread
+ implementations */
+/* TODO: Just make it use recursive mutexes if they are available */
+EMutex *e_mutex_new(e_mutex_t type)
+{
+ struct _EMutex *m;
+
+ m = g_malloc(sizeof(*m));
+ m->type = type;
+ m->waiters = 0;
+ m->depth = 0;
+ m->owner = E_THREAD_NONE;
+
+ switch (type) {
+ case E_MUTEX_SIMPLE:
+ pthread_mutex_init(&m->mutex, 0);
+ break;
+ case E_MUTEX_REC:
+ pthread_mutex_init(&m->mutex, 0);
+ pthread_cond_init(&m->cond, 0);
+ break;
+ /* read / write ? flags for same? */
+ }
+
+ return m;
+}
+
+int e_mutex_destroy(EMutex *m)
+{
+ int ret = 0;
+
+ switch (m->type) {
+ case E_MUTEX_SIMPLE:
+ ret = pthread_mutex_destroy(&m->mutex);
+ if (ret == -1)
+ g_warning("EMutex destroy failed: %s", strerror(errno));
+ g_free(m);
+ break;
+ case E_MUTEX_REC:
+ ret = pthread_mutex_destroy(&m->mutex);
+ if (ret == -1)
+ g_warning("EMutex destroy failed: %s", strerror(errno));
+ ret = pthread_cond_destroy(&m->cond);
+ if (ret == -1)
+ g_warning("EMutex destroy failed: %s", strerror(errno));
+ g_free(m);
+
+ }
+ return ret;
+}
+
+int e_mutex_lock(EMutex *m)
+{
+ pthread_t id;
+ int err;
+
+ switch (m->type) {
+ case E_MUTEX_SIMPLE:
+ return pthread_mutex_lock(&m->mutex);
+ case E_MUTEX_REC:
+ id = pthread_self();
+ if ((err = pthread_mutex_lock(&m->mutex)) != 0)
+ return err;
+ while (1) {
+ if (m->owner == E_THREAD_NONE) {
+ m->owner = id;
+ m->depth = 1;
+ break;
+ } else if (id == m->owner) {
+ m->depth++;
+ break;
+ } else {
+ m->waiters++;
+ if ((err = pthread_cond_wait(&m->cond, &m->mutex)) != 0)
+ return err;
+ m->waiters--;
+ }
+ }
+ return pthread_mutex_unlock(&m->mutex);
+ }
+
+ return EINVAL;
+}
+
+int e_mutex_unlock(EMutex *m)
+{
+ int err;
+
+ switch (m->type) {
+ case E_MUTEX_SIMPLE:
+ return pthread_mutex_unlock(&m->mutex);
+ case E_MUTEX_REC:
+ if ((err = pthread_mutex_lock(&m->mutex)) != 0)
+ return err;
+ g_assert(m->owner == pthread_self());
+
+ m->depth--;
+ if (m->depth == 0) {
+ m->owner = E_THREAD_NONE;
+ if (m->waiters > 0)
+ pthread_cond_signal(&m->cond);
+ }
+ return pthread_mutex_unlock(&m->mutex);
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+void e_mutex_assert_locked(EMutex *m)
+{
+ g_return_if_fail (m->type == E_MUTEX_REC);
+ pthread_mutex_lock(&m->mutex);
+ g_assert(m->owner == pthread_self());
+ pthread_mutex_unlock(&m->mutex);
+}
+
+int e_mutex_cond_wait(void *vcond, EMutex *m)
+{
+ int ret;
+ pthread_cond_t *cond = vcond;
+
+ switch(m->type) {
+ case E_MUTEX_SIMPLE:
+ return pthread_cond_wait(cond, &m->mutex);
+ case E_MUTEX_REC:
+ if ((ret = pthread_mutex_lock(&m->mutex)) != 0)
+ return ret;
+ g_assert(m->owner == pthread_self());
+ ret = pthread_cond_wait(cond, &m->mutex);
+ g_assert(m->owner == pthread_self());
+ pthread_mutex_unlock(&m->mutex);
+ return ret;
+ default:
+ g_return_val_if_reached(-1);
+ }
+}
+
+#ifdef STANDALONE
+EMsgPort *server_port;
+
+
+void *fdserver(void *data)
+{
+ int fd;
+ EMsg *msg;
+ int id = (int)data;
+ fd_set rfds;
+
+ fd = e_msgport_fd(server_port);
+
+ while (1) {
+ int count = 0;
+
+ printf("server %d: waiting on fd %d\n", id, fd);
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ select(fd+1, &rfds, NULL, NULL, NULL);
+ printf("server %d: Got async notification, checking for messages\n", id);
+ while ((msg = e_msgport_get(server_port))) {
+ printf("server %d: got message\n", id);
+ sleep(1);
+ printf("server %d: replying\n", id);
+ e_msgport_reply(msg);
+ count++;
+ }
+ printf("server %d: got %d messages\n", id, count);
+ }
+}
+
+void *server(void *data)
+{
+ EMsg *msg;
+ int id = (int)data;
+
+ while (1) {
+ printf("server %d: waiting\n", id);
+ msg = e_msgport_wait(server_port);
+ msg = e_msgport_get(server_port);
+ if (msg) {
+ printf("server %d: got message\n", id);
+ sleep(1);
+ printf("server %d: replying\n", id);
+ e_msgport_reply(msg);
+ } else {
+ printf("server %d: didn't get message\n", id);
+ }
+ }
+}
+
+void *client(void *data)
+{
+ EMsg *msg;
+ EMsgPort *replyport;
+ int i;
+
+ replyport = e_msgport_new();
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = replyport;
+ for (i=0;i<10;i++) {
+ /* synchronous operation */
+ printf("client: sending\n");
+ e_msgport_put(server_port, msg);
+ printf("client: waiting for reply\n");
+ e_msgport_wait(replyport);
+ e_msgport_get(replyport);
+ printf("client: got reply\n");
+ }
+
+ printf("client: sleeping ...\n");
+ sleep(2);
+ printf("client: sending multiple\n");
+
+ for (i=0;i<10;i++) {
+ msg = g_malloc0(sizeof(*msg));
+ msg->reply_port = replyport;
+ e_msgport_put(server_port, msg);
+ }
+
+ printf("client: receiving multiple\n");
+ for (i=0;i<10;i++) {
+ e_msgport_wait(replyport);
+ msg = e_msgport_get(replyport);
+ g_free(msg);
+ }
+
+ printf("client: done\n");
+}
+
+int main(int argc, char **argv)
+{
+ pthread_t serverid, clientid;
+
+ g_thread_init(NULL);
+
+ server_port = e_msgport_new();
+
+ /*pthread_create(&serverid, NULL, server, (void *)1);*/
+ pthread_create(&serverid, NULL, fdserver, (void *)1);
+ pthread_create(&clientid, NULL, client, NULL);
+
+ sleep(60);
+
+ return 0;
+}
+#endif
--- /dev/null
+
+#ifndef _E_MSGPORT_H
+#define _E_MSGPORT_H
+
+#include <time.h>
+#include <glib.h>
+
+/* double-linked list yeah another one, deal */
+typedef struct _EDListNode {
+ struct _EDListNode *next;
+ struct _EDListNode *prev;
+} EDListNode;
+
+typedef struct _EDList {
+ struct _EDListNode *head;
+ struct _EDListNode *tail;
+ struct _EDListNode *tailpred;
+} EDList;
+
+#define E_DLIST_INITIALISER(l) { (EDListNode *)&l.tail, 0, (EDListNode *)&l.head }
+
+void e_dlist_init(EDList *v);
+EDListNode *e_dlist_addhead(EDList *l, EDListNode *n);
+EDListNode *e_dlist_addtail(EDList *l, EDListNode *n);
+EDListNode *e_dlist_remove(EDListNode *n);
+EDListNode *e_dlist_remhead(EDList *l);
+EDListNode *e_dlist_remtail(EDList *l);
+int e_dlist_empty(EDList *l);
+int e_dlist_length(EDList *l);
+
+/* a time-based cache */
+typedef struct _EMCache EMCache;
+typedef struct _EMCacheNode EMCacheNode;
+
+/* subclass this for your data nodes, EMCache is opaque */
+struct _EMCacheNode {
+ struct _EMCacheNode *next, *prev;
+ char *key;
+ int ref_count;
+ time_t stamp;
+};
+
+EMCache *em_cache_new(time_t timeout, size_t nodesize, GFreeFunc nodefree);
+void em_cache_destroy(EMCache *emc);
+EMCacheNode *em_cache_lookup(EMCache *emc, const char *key);
+EMCacheNode *em_cache_node_new(EMCache *emc, const char *key);
+void em_cache_node_unref(EMCache *emc, EMCacheNode *n);
+void em_cache_add(EMCache *emc, EMCacheNode *n);
+void em_cache_clear(EMCache *emc);
+
+/* message ports - a simple inter-thread 'ipc' primitive */
+/* opaque handle */
+typedef struct _EMsgPort EMsgPort;
+
+/* header for any message */
+typedef struct _EMsg {
+ EDListNode ln;
+ EMsgPort *reply_port;
+} EMsg;
+
+EMsgPort *e_msgport_new(void);
+void e_msgport_destroy(EMsgPort *mp);
+/* get a fd that can be used to wait on the port asynchronously */
+int e_msgport_fd(EMsgPort *mp);
+void e_msgport_put(EMsgPort *mp, EMsg *msg);
+EMsg *e_msgport_wait(EMsgPort *mp);
+EMsg *e_msgport_get(EMsgPort *mp);
+void e_msgport_reply(EMsg *msg);
+#ifdef HAVE_NSS
+struct PRFileDesc *e_msgport_prfd(EMsgPort *mp);
+#endif
+
+/* e threads, a server thread with a message based request-response, and flexible queuing */
+typedef struct _EThread EThread;
+
+typedef enum {
+ E_THREAD_QUEUE = 0, /* run one by one, until done, if the queue_limit is reached, discard new request */
+ E_THREAD_DROP, /* run one by one, until done, if the queue_limit is reached, discard oldest requests */
+ E_THREAD_NEW, /* always run in a new thread, if the queue limit is reached, new requests are
+ stored in the queue until a thread becomes available for it, creating a thread pool */
+} e_thread_t;
+
+typedef void (*EThreadFunc)(EThread *, EMsg *, void *data);
+
+EThread *e_thread_new(e_thread_t type);
+void e_thread_destroy(EThread *e);
+void e_thread_set_queue_limit(EThread *e, int limit);
+void e_thread_set_msg_lost(EThread *e, EThreadFunc destroy, void *data);
+void e_thread_set_msg_destroy(EThread *e, EThreadFunc destroy, void *data);
+void e_thread_set_reply_port(EThread *e, EMsgPort *reply_port);
+void e_thread_set_msg_received(EThread *e, EThreadFunc received, void *data);
+void e_thread_put(EThread *e, EMsg *msg);
+int e_thread_busy(EThread *e);
+
+/* sigh, another mutex interface, this one allows different mutex types, portably */
+typedef struct _EMutex EMutex;
+
+typedef enum _e_mutex_t {
+ E_MUTEX_SIMPLE, /* == pthread_mutex */
+ E_MUTEX_REC, /* recursive mutex */
+} e_mutex_t;
+
+EMutex *e_mutex_new(e_mutex_t type);
+int e_mutex_destroy(EMutex *m);
+int e_mutex_lock(EMutex *m);
+int e_mutex_unlock(EMutex *m);
+void e_mutex_assert_locked(EMutex *m);
+/* this uses pthread cond's */
+int e_mutex_cond_wait(void *cond, EMutex *m);
+
+#endif
break;
case ESEXP_TERM_FUNC:
/* first evaluate all arguments to result types */
- argv = g_alloca(sizeof(argv[0]) * t->value.func.termcount);
+ argv = alloca(sizeof(argv[0]) * t->value.func.termcount);
for (i=0;i<t->value.func.termcount;i++) {
argv[i] = e_sexp_term_eval(f, t->value.func.terms[i]);
}
p(printf("got brace, its a list!\n"));
return parse_list(f, TRUE);
case G_TOKEN_STRING:
- p(printf("got string\n"));
+ p(printf("got string '%s'\n", g_scanner_cur_value(gs).v_string));
t = parse_term_new(f, ESEXP_TERM_STRING);
t->value.string = g_strdup(g_scanner_cur_value(gs).v_string);
break;
t->value.number = g_scanner_cur_value(gs).v_int;
if (negative)
t->value.number = -t->value.number;
- p(printf("got int\n"));
+ p(printf("got int %d\n", t->value.number));
break;
case '#': {
char *str;
break; }
case G_TOKEN_SYMBOL:
s = g_scanner_cur_value(gs).v_symbol;
+ p(printf("got symbol '%s'\n", s->name));
switch (s->type) {
case ESEXP_TERM_FUNC:
case ESEXP_TERM_IFUNC:
}
break;
case G_TOKEN_IDENTIFIER:
+ p(printf("got unknown identifider '%s'\n", g_scanner_cur_value(gs).v_identifier));
e_sexp_fatal_error(f, "Unknown identifier: %s", g_scanner_cur_value(gs).v_identifier);
break;
default:
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Time utility functions
+ *
+ * Author:
+ * Damon Chaplin (damon@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#include <config.h>
+
+#ifdef __linux__
+/* We need this to get a prototype for strptime. */
+#define _GNU_SOURCE
+#endif /* __linux__ */
+
+#include <time.h>
+#include <sys/time.h>
+
+#ifdef __linux__
+#undef _GNU_SOURCE
+#endif /* __linux__ */
+
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include <libgnome/gnome-i18n.h>
+#include "e-time-utils.h"
+#include "e-util.h"
+
+/* Returns whether a string is NULL, empty, or full of whitespace */
+static gboolean
+string_is_empty (const char *value)
+{
+ const char *p;
+ gboolean empty = TRUE;
+
+ if (value) {
+ p = value;
+ while (*p) {
+ if (!isspace (*p)) {
+ empty = FALSE;
+ break;
+ }
+ p++;
+ }
+ }
+ return empty;
+}
+
+
+/* Takes a number of format strings for strptime() and attempts to parse a
+ * string with them.
+ */
+static ETimeParseStatus
+parse_with_strptime (const char *value, struct tm *result, const char **formats, int n_formats)
+{
+ const char *parse_end = NULL, *pos;
+ gchar *locale_str;
+ gchar *format_str;
+ ETimeParseStatus parse_ret;
+ gboolean parsed = FALSE;
+ int i;
+
+ if (string_is_empty (value)) {
+ memset (result, 0, sizeof (*result));
+ result->tm_isdst = -1;
+ return E_TIME_PARSE_NONE;
+ }
+
+ locale_str = g_locale_from_utf8(value, strlen(value), NULL, NULL, NULL);
+
+ pos = (const char *) locale_str;
+
+ /* Skip whitespace */
+ while (isspace (*pos))
+ pos++;
+
+ /* Try each of the formats in turn */
+
+ for (i = 0; i < n_formats; i++) {
+ memset (result, 0, sizeof (*result));
+ format_str = g_locale_from_utf8(formats[i], strlen(formats[i]), NULL, NULL, NULL);
+ parse_end = strptime (pos, format_str, result);
+ g_free (format_str);
+ if (parse_end) {
+ parsed = TRUE;
+ break;
+ }
+ }
+
+ result->tm_isdst = -1;
+
+ parse_ret = E_TIME_PARSE_INVALID;
+
+ /* If we parsed something, make sure we parsed the entire string. */
+ if (parsed) {
+ /* Skip whitespace */
+ while (isspace (*parse_end))
+ parse_end++;
+
+ if (*parse_end == '\0')
+ parse_ret = E_TIME_PARSE_OK;
+ }
+
+ g_free (locale_str);
+
+ return (parse_ret);
+
+}
+
+
+/* Returns TRUE if the locale has 'am' and 'pm' strings defined, in which
+ case the user can choose between 12 and 24-hour time formats. */
+static gboolean
+locale_supports_12_hour_format (void)
+{
+ struct tm tmp_tm = { 0 };
+ char s[16];
+
+ e_utf8_strftime (s, sizeof (s), "%p", &tmp_tm);
+ return s[0] != '\0';
+}
+
+
+/*
+ * Parses a string containing a date and a time. The date is expected to be
+ * in a format something like "Wed 3/13/00 14:20:00", though we use gettext
+ * to support the appropriate local formats and we try to accept slightly
+ * different formats, e.g. the weekday can be skipped and we can accept 12-hour
+ * formats with an am/pm string.
+ *
+ * Returns E_TIME_PARSE_OK if it could not be parsed, E_TIME_PARSE_NONE if it
+ * was empty, or E_TIME_PARSE_INVALID if it couldn't be parsed.
+ */
+ETimeParseStatus
+e_time_parse_date_and_time (const char *value,
+ struct tm *result)
+{
+ struct tm *today_tm;
+ time_t t;
+ const char *format[16];
+ int num_formats = 0;
+ gboolean use_12_hour_formats = locale_supports_12_hour_format ();
+ ETimeParseStatus status;
+
+ if (string_is_empty (value)) {
+ memset (result, 0, sizeof (*result));
+ result->tm_isdst = -1;
+ return E_TIME_PARSE_NONE;
+ }
+
+ /* We'll parse the whole date and time in one go, otherwise we get
+ into i18n problems. We attempt to parse with several formats,
+ longest first. Note that we only use the '%p' specifier if the
+ locale actually has 'am' and 'pm' strings defined, otherwise we
+ will get incorrect results. Note also that we try to use exactly
+ the same strings as in e_time_format_date_and_time(), to try to
+ avoid i18n problems. We also use cut-down versions, so users don't
+ have to type in the weekday or the seconds, for example.
+ Note that all these formats include the full date, and the time
+ will be set to 00:00:00 before parsing, so we don't need to worry
+ about filling in any missing fields after parsing. */
+
+ /*
+ * Try the full times, with the weekday. Then try without seconds,
+ * and without minutes, and finally with no time at all.
+ */
+ if (use_12_hour_formats) {
+ /* strptime format of a weekday, a date and a time,
+ in 12-hour format. */
+ format[num_formats++] = _("%a %m/%d/%Y %I:%M:%S %p");
+ }
+
+ /* strptime format of a weekday, a date and a time,
+ in 24-hour format. */
+ format[num_formats++] = _("%a %m/%d/%Y %H:%M:%S");
+
+ if (use_12_hour_formats) {
+ /* strptime format of a weekday, a date and a time,
+ in 12-hour format, without seconds. */
+ format[num_formats++] = _("%a %m/%d/%Y %I:%M %p");
+ }
+
+ /* strptime format of a weekday, a date and a time,
+ in 24-hour format, without seconds. */
+ format[num_formats++] = _("%a %m/%d/%Y %H:%M");
+
+ if (use_12_hour_formats) {
+ /* strptime format of a weekday, a date and a time,
+ in 12-hour format, without minutes or seconds. */
+ format[num_formats++] = _("%a %m/%d/%Y %I %p");
+ }
+
+ /* strptime format of a weekday, a date and a time,
+ in 24-hour format, without minutes or seconds. */
+ format[num_formats++] = _("%a %m/%d/%Y %H");
+
+ /* strptime format of a weekday and a date. */
+ format[num_formats++] = _("%a %m/%d/%Y");
+
+
+ /*
+ * Now try all the above formats again, but without the weekday.
+ */
+ if (use_12_hour_formats) {
+ /* strptime format of a date and a time, in 12-hour format. */
+ format[num_formats++] = _("%m/%d/%Y %I:%M:%S %p");
+ }
+
+ /* strptime format of a date and a time, in 24-hour format. */
+ format[num_formats++] = _("%m/%d/%Y %H:%M:%S");
+
+ if (use_12_hour_formats) {
+ /* strptime format of a date and a time, in 12-hour format,
+ without seconds. */
+ format[num_formats++] = _("%m/%d/%Y %I:%M %p");
+ }
+
+ /* strptime format of a date and a time, in 24-hour format,
+ without seconds. */
+ format[num_formats++] = _("%m/%d/%Y %H:%M");
+
+ if (use_12_hour_formats) {
+ /* strptime format of a date and a time, in 12-hour format,
+ without minutes or seconds. */
+ format[num_formats++] = _("%m/%d/%Y %I %p");
+ }
+
+ /* strptime format of a date and a time, in 24-hour format,
+ without minutes or seconds. */
+ format[num_formats++] = _("%m/%d/%Y %H");
+
+ /* strptime format of a weekday and a date. */
+ format[num_formats++] = _("%m/%d/%Y");
+
+
+ status = parse_with_strptime (value, result, format, num_formats);
+ /* Note that we checked if it was empty already, so it is either OK
+ or INVALID here. */
+ if (status == E_TIME_PARSE_OK) {
+ /* If a 2-digit year was used we use the current century. */
+ if (result->tm_year < 0) {
+ t = time (NULL);
+ today_tm = localtime (&t);
+
+ /* This should convert it into a value from 0 to 99. */
+ result->tm_year += 1900;
+
+ /* Now add on the century. */
+ result->tm_year += today_tm->tm_year
+ - (today_tm->tm_year % 100);
+ }
+ } else {
+ /* Now we try to just parse a time, assuming the current day.*/
+ status = e_time_parse_time (value, result);
+ if (status == E_TIME_PARSE_OK) {
+ /* We fill in the current day. */
+ t = time (NULL);
+ today_tm = localtime (&t);
+ result->tm_mday = today_tm->tm_mday;
+ result->tm_mon = today_tm->tm_mon;
+ result->tm_year = today_tm->tm_year;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * e_time_parse_date:
+ * @value: A date string.
+ * @result: Return value for the parsed date.
+ *
+ * Takes in a date string entered by the user and tries to convert it to
+ * a struct tm.
+ *
+ * Return value: Result code indicating whether the @value was an empty
+ * string, a valid date, or an invalid date.
+ **/
+ETimeParseStatus
+e_time_parse_date (const char *value, struct tm *result)
+{
+ const char *format[2];
+ struct tm *today_tm;
+ time_t t;
+ ETimeParseStatus status;
+
+ g_return_val_if_fail (value != NULL, E_TIME_PARSE_INVALID);
+ g_return_val_if_fail (result != NULL, E_TIME_PARSE_INVALID);
+
+ /* strptime format of a weekday and a date. */
+ format[0] = _("%a %m/%d/%Y");
+
+ /* This is the preferred date format for the locale. */
+ format[1] = _("%m/%d/%Y");
+
+ status = parse_with_strptime (value, result, format, sizeof (format) / sizeof (format[0]));
+ if (status == E_TIME_PARSE_OK) {
+ /* If a 2-digit year was used we use the current century. */
+ if (result->tm_year < 0) {
+ t = time (NULL);
+ today_tm = localtime (&t);
+
+ /* This should convert it into a value from 0 to 99. */
+ result->tm_year += 1900;
+
+ /* Now add on the century. */
+ result->tm_year += today_tm->tm_year
+ - (today_tm->tm_year % 100);
+ }
+ }
+
+ return status;
+}
+
+
+/*
+ * Parses a string containing a time. It is expected to be in a format
+ * something like "14:20:00", though we use gettext to support the appropriate
+ * local formats and we try to accept slightly different formats, e.g. we can
+ * accept 12-hour formats with an am/pm string.
+ *
+ * Returns E_TIME_PARSE_OK if it could not be parsed, E_TIME_PARSE_NONE if it
+ * was empty, or E_TIME_PARSE_INVALID if it couldn't be parsed.
+ */
+ETimeParseStatus
+e_time_parse_time (const char *value, struct tm *result)
+{
+ const char *format[6];
+ int num_formats = 0;
+ gboolean use_12_hour_formats = locale_supports_12_hour_format ();
+
+ if (use_12_hour_formats) {
+ /* strptime format for a time of day, in 12-hour format. */
+ format[num_formats++] = _("%I:%M:%S %p");
+ }
+
+ /* strptime format for a time of day, in 24-hour format. */
+ format[num_formats++] = _("%H:%M:%S");
+
+ if (use_12_hour_formats) {
+ /* strptime format for time of day, without seconds,
+ in 12-hour format. */
+ format[num_formats++] = _("%I:%M %p");
+ }
+
+ /* strptime format for time of day, without seconds 24-hour format. */
+ format[num_formats++] = _("%H:%M");
+
+ if (use_12_hour_formats) {
+ /* strptime format for hour and AM/PM, 12-hour format. */
+ format[num_formats++] = _("%I %p");
+ }
+
+ /* strptime format for hour, 24-hour format. */
+ format[num_formats++] = "%H";
+
+ return parse_with_strptime (value, result, format, num_formats);
+}
+
+
+/* Creates a string representation of a time value and stores it in buffer.
+ buffer_size should be about 64 to be safe. If show_midnight is FALSE, and
+ the time is midnight, then we just show the date. If show_zero_seconds
+ is FALSE, then if the time has zero seconds only the hour and minute are
+ shown. */
+void
+e_time_format_date_and_time (struct tm *date_tm,
+ gboolean use_24_hour_format,
+ gboolean show_midnight,
+ gboolean show_zero_seconds,
+ char *buffer,
+ int buffer_size)
+{
+ char *format;
+
+ if (!show_midnight && date_tm->tm_hour == 0
+ && date_tm->tm_min == 0 && date_tm->tm_sec == 0) {
+ /* strftime format of a weekday and a date. */
+ format = _("%a %m/%d/%Y");
+ } else if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday, a date and a
+ time, in 24-hour format, without seconds. */
+ format = _("%a %m/%d/%Y %H:%M");
+ else
+ /* strftime format of a weekday, a date and a
+ time, in 24-hour format. */
+ format = _("%a %m/%d/%Y %H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a weekday, a date and a
+ time, in 12-hour format, without seconds. */
+ format = _("%a %m/%d/%Y %I:%M %p");
+ else
+ /* strftime format of a weekday, a date and a
+ time, in 12-hour format. */
+ format = _("%a %m/%d/%Y %I:%M:%S %p");
+ }
+
+ /* strftime returns 0 if the string doesn't fit, and leaves the buffer
+ undefined, so we set it to the empty string in that case. */
+ if (e_utf8_strftime (buffer, buffer_size, format, date_tm) == 0)
+ buffer[0] = '\0';
+}
+
+
+/* Creates a string representation of a time value and stores it in buffer.
+ buffer_size should be about 64 to be safe. */
+void
+e_time_format_time (struct tm *date_tm,
+ gboolean use_24_hour_format,
+ gboolean show_zero_seconds,
+ char *buffer,
+ int buffer_size)
+{
+ char *format;
+
+ if (use_24_hour_format) {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a time in 24-hour format,
+ without seconds. */
+ format = _("%H:%M");
+ else
+ /* strftime format of a time in 24-hour format. */
+ format = _("%H:%M:%S");
+ } else {
+ if (!show_zero_seconds && date_tm->tm_sec == 0)
+ /* strftime format of a time in 12-hour format,
+ without seconds. */
+ format = _("%I:%M %p");
+ else
+ /* strftime format of a time in 12-hour format. */
+ format = _("%I:%M:%S %p");
+ }
+
+ /* strftime returns 0 if the string doesn't fit, and leaves the buffer
+ undefined, so we set it to the empty string in that case. */
+ if (e_utf8_strftime (buffer, buffer_size, format, date_tm) == 0)
+ buffer[0] = '\0';
+}
+
+
+/* Like mktime(3), but assumes UTC instead of local timezone. */
+time_t
+e_mktime_utc (struct tm *tm)
+{
+ time_t tt;
+
+ tm->tm_isdst = -1;
+ tt = mktime (tm);
+
+#if defined (HAVE_TM_GMTOFF)
+ tt += tm->tm_gmtoff;
+#elif defined (HAVE_TIMEZONE)
+ if (tm->tm_isdst > 0) {
+ #if defined (HAVE_ALTZONE)
+ tt -= altzone;
+ #else /* !defined (HAVE_ALTZONE) */
+ tt -= (timezone - 3600);
+ #endif
+ } else
+ tt -= timezone;
+#endif
+
+ return tt;
+}
+
+/* Like localtime_r(3), but also returns an offset in seconds after UTC.
+ (Calling gmtime with tt + offset would generate the same tm) */
+void
+e_localtime_with_offset (time_t tt, struct tm *tm, int *offset)
+{
+ localtime_r (&tt, tm);
+
+#if defined (HAVE_TM_GMTOFF)
+ *offset = tm->tm_gmtoff;
+#elif defined (HAVE_TIMEZONE)
+ if (tm->tm_isdst > 0) {
+ #if defined (HAVE_ALTZONE)
+ *offset = -altzone;
+ #else /* !defined (HAVE_ALTZONE) */
+ *offset = -(timezone - 3600);
+ #endif
+ } else
+ *offset = -timezone;
+#endif
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Time utility functions
+ *
+ * Author:
+ * Damon Chaplin (damon@ximian.com)
+ *
+ * (C) 2001 Ximian, Inc.
+ */
+
+#ifndef E_TIME_UTILS
+#define E_TIME_UTILS
+
+#include <time.h>
+#include <glib.h>
+
+typedef enum {
+ E_TIME_PARSE_OK,
+ E_TIME_PARSE_NONE,
+ E_TIME_PARSE_INVALID
+} ETimeParseStatus;
+
+/* Tries to parse a string containing a date and time. */
+ETimeParseStatus e_time_parse_date_and_time (const char *value,
+ struct tm *result);
+
+/* Tries to parse a string containing a date. */
+ETimeParseStatus e_time_parse_date (const char *value,
+ struct tm *result);
+
+/* Tries to parse a string containing a time. */
+ETimeParseStatus e_time_parse_time (const char *value,
+ struct tm *result);
+
+/* Turns a struct tm into a string like "Wed 3/12/00 12:00:00 AM". */
+void e_time_format_date_and_time (struct tm *date_tm,
+ gboolean use_24_hour_format,
+ gboolean show_midnight,
+ gboolean show_zero_seconds,
+ char *buffer,
+ int buffer_size);
+
+/* Formats a time from a struct tm, e.g. "01:59 PM". */
+void e_time_format_time (struct tm *date_tm,
+ gboolean use_24_hour_format,
+ gboolean show_zero_seconds,
+ char *buffer,
+ int buffer_size);
+
+
+/* Like mktime(3), but assumes UTC instead of local timezone. */
+time_t e_mktime_utc (struct tm *timeptr);
+
+/* Like localtime_r(3), but also returns an offset in minutes after UTC.
+ (Calling gmtime with tt + offset would generate the same tm) */
+void e_localtime_with_offset (time_t tt, struct tm *tm, int *offset);
+
+#endif /* E_TIME_UTILS */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "e-trie.h"
+#include "e-memory.h"
+
+#define d(x)
+
+struct _trie_state {
+ struct _trie_state *next;
+ struct _trie_state *fail;
+ struct _trie_match *match;
+ unsigned int final;
+ int id;
+};
+
+struct _trie_match {
+ struct _trie_match *next;
+ struct _trie_state *state;
+ gunichar c;
+};
+
+struct _ETrie {
+ struct _trie_state root;
+ GPtrArray *fail_states;
+ gboolean icase;
+
+ EMemChunk *match_chunks;
+ EMemChunk *state_chunks;
+};
+
+
+static inline gunichar
+trie_utf8_getc (const unsigned char **in, size_t inlen)
+{
+ register const unsigned char *inptr = *in;
+ const unsigned char *inend = inptr + inlen;
+ register unsigned char c, r;
+ register gunichar u, m;
+
+ if (inlen == 0)
+ return 0;
+
+ r = *inptr++;
+ if (r < 0x80) {
+ *in = inptr;
+ u = r;
+ } else if (r < 0xfe) { /* valid start char? */
+ u = r;
+ m = 0x7f80; /* used to mask out the length bits */
+ do {
+ if (inptr >= inend)
+ return 0;
+
+ c = *inptr++;
+ if ((c & 0xc0) != 0x80)
+ goto error;
+
+ u = (u << 6) | (c & 0x3f);
+ r <<= 1;
+ m <<= 5;
+ } while (r & 0x40);
+
+ *in = inptr;
+
+ u &= ~m;
+ } else {
+ error:
+ *in = (*in)+1;
+ u = 0xfffe;
+ }
+
+ return u;
+}
+
+
+ETrie *
+e_trie_new (gboolean icase)
+{
+ ETrie *trie;
+
+ trie = g_new (ETrie, 1);
+ trie->root.next = NULL;
+ trie->root.fail = NULL;
+ trie->root.match = NULL;
+ trie->root.final = 0;
+
+ trie->fail_states = g_ptr_array_sized_new (8);
+ trie->icase = icase;
+
+ trie->match_chunks = e_memchunk_new (8, sizeof (struct _trie_match));
+ trie->state_chunks = e_memchunk_new (8, sizeof (struct _trie_state));
+
+ return trie;
+}
+
+void
+e_trie_free (ETrie *trie)
+{
+ g_ptr_array_free (trie->fail_states, TRUE);
+ e_memchunk_destroy (trie->match_chunks);
+ e_memchunk_destroy (trie->state_chunks);
+ g_free (trie);
+}
+
+
+
+static struct _trie_match *
+g (struct _trie_state *s, gunichar c)
+{
+ struct _trie_match *m = s->match;
+
+ while (m && m->c != c)
+ m = m->next;
+
+ return m;
+}
+
+static struct _trie_state *
+trie_insert (ETrie *trie, int depth, struct _trie_state *q, gunichar c)
+{
+ struct _trie_match *m;
+
+ m = e_memchunk_alloc (trie->match_chunks);
+ m->next = q->match;
+ m->c = c;
+
+ q->match = m;
+ q = m->state = e_memchunk_alloc (trie->state_chunks);
+ q->match = NULL;
+ q->fail = &trie->root;
+ q->final = 0;
+ q->id = -1;
+
+ if (trie->fail_states->len < depth + 1) {
+ unsigned int size = trie->fail_states->len;
+
+ size = MAX (size + 64, depth + 1);
+ g_ptr_array_set_size (trie->fail_states, size);
+ }
+
+ q->next = trie->fail_states->pdata[depth];
+ trie->fail_states->pdata[depth] = q;
+
+ return q;
+}
+
+
+#if 1
+static void
+dump_trie (struct _trie_state *s, int depth)
+{
+ char *p = g_alloca ((depth * 2) + 1);
+ struct _trie_match *m;
+
+ memset (p, ' ', depth * 2);
+ p[depth * 2] = '\0';
+
+ fprintf (stderr, "%s[state] %p: final=%d; pattern-id=%d; fail=%p\n",
+ p, s, s->final, s->id, s->fail);
+ m = s->match;
+ while (m) {
+ fprintf (stderr, " %s'%c' -> %p\n", p, m->c, m->state);
+ if (m->state)
+ dump_trie (m->state, depth + 1);
+
+ m = m->next;
+ }
+}
+#endif
+
+
+/*
+ * final = empty set
+ * FOR p = 1 TO #pat
+ * q = root
+ * FOR j = 1 TO m[p]
+ * IF g(q, pat[p][j]) == null
+ * insert(q, pat[p][j])
+ * ENDIF
+ * q = g(q, pat[p][j])
+ * ENDFOR
+ * final = union(final, q)
+ * ENDFOR
+*/
+
+void
+e_trie_add (ETrie *trie, const char *pattern, int pattern_id)
+{
+ const unsigned char *inptr = (const unsigned char *) pattern;
+ struct _trie_state *q, *q1, *r;
+ struct _trie_match *m, *n;
+ int i, depth = 0;
+ gunichar c;
+
+ /* Step 1: add the pattern to the trie */
+
+ q = &trie->root;
+
+ while ((c = trie_utf8_getc (&inptr, -1))) {
+ if (trie->icase)
+ c = g_unichar_tolower (c);
+
+ m = g (q, c);
+ if (m == NULL) {
+ q = trie_insert (trie, depth, q, c);
+ } else {
+ q = m->state;
+ }
+
+ depth++;
+ }
+
+ q->final = depth;
+ q->id = pattern_id;
+
+ /* Step 2: compute failure graph */
+
+ for (i = 0; i < trie->fail_states->len; i++) {
+ q = trie->fail_states->pdata[i];
+ while (q) {
+ m = q->match;
+ while (m) {
+ c = m->c;
+ q1 = m->state;
+ r = q->fail;
+ while (r && (n = g (r, c)) == NULL)
+ r = r->fail;
+
+ if (r != NULL) {
+ q1->fail = n->state;
+ if (q1->fail->final > q1->final)
+ q1->final = q1->fail->final;
+ } else {
+ if ((n = g (&trie->root, c)))
+ q1->fail = n->state;
+ else
+ q1->fail = &trie->root;
+ }
+
+ m = m->next;
+ }
+
+ q = q->next;
+ }
+ }
+
+ d(fprintf (stderr, "\nafter adding pattern '%s' to trie %p:\n", pattern, trie));
+ d(dump_trie (&trie->root, 0));
+}
+
+/*
+ * Aho-Corasick
+ *
+ * q = root
+ * FOR i = 1 TO n
+ * WHILE q != fail AND g(q, text[i]) == fail
+ * q = h(q)
+ * ENDWHILE
+ * IF q == fail
+ * q = root
+ * ELSE
+ * q = g(q, text[i])
+ * ENDIF
+ * IF isElement(q, final)
+ * RETURN TRUE
+ * ENDIF
+ * ENDFOR
+ * RETURN FALSE
+ */
+
+const char *
+e_trie_search (ETrie *trie, const char *buffer, size_t buflen, int *matched_id)
+{
+ const unsigned char *inptr, *inend, *prev, *pat;
+ register size_t inlen = buflen;
+ struct _trie_state *q;
+ struct _trie_match *m;
+ gunichar c;
+
+ inptr = (const unsigned char *) buffer;
+ inend = inptr + buflen;
+
+ q = &trie->root;
+ pat = prev = inptr;
+ while ((c = trie_utf8_getc (&inptr, inlen))) {
+ inlen = (inend - inptr);
+
+ if (c != 0xfffe) {
+ if (trie->icase)
+ c = g_unichar_tolower (c);
+
+ while (q != NULL && (m = g (q, c)) == NULL)
+ q = q->fail;
+
+ if (q == &trie->root)
+ pat = prev;
+
+ if (q == NULL) {
+ q = &trie->root;
+ pat = inptr;
+ } else if (m != NULL) {
+ q = m->state;
+
+ if (q->final) {
+ if (matched_id)
+ *matched_id = q->id;
+
+ return (const char *) pat;
+ }
+ }
+ }
+
+ prev = inptr;
+ }
+
+ return NULL;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Authors: Jeffrey Stedfast <fejj@ximian.com>
+ *
+ * Copyright 2002 Ximian, Inc. (www.ximian.com)
+ *
+ * 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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef __E_TRIE_H__
+#define __E_TRIE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+#include <glib.h>
+
+typedef struct _ETrie ETrie;
+
+ETrie *e_trie_new (gboolean icase);
+void e_trie_free (ETrie *trie);
+
+void e_trie_add (ETrie *trie, const char *pattern, int pattern_id);
+
+const char *e_trie_search (ETrie *trie, const char *buffer, size_t buflen, int *matched_id);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __E_TRIE_H__ */
* Authors: Rodrigo Moya <rodrigo@ximian.com>
*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include <string.h>
#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
#include <glib/gfileutils.h>
#include <glib/gmem.h>
#include <glib/gunicode.h>
#include <glib/gutils.h>
#include <glib/galloca.h>
+#include <glib/gconvert.h>
#include "e-util.h"
int
return NULL;
}
+
+size_t e_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+#ifdef HAVE_LKSTRFTIME
+ return strftime(s, max, fmt, tm);
+#else
+ char *c, *ffmt, *ff;
+ size_t ret;
+
+ ffmt = g_strdup(fmt);
+ ff = ffmt;
+ while ((c = strstr(ff, "%l")) != NULL) {
+ c[1] = 'I';
+ ff = c;
+ }
+
+ ff = fmt;
+ while ((c = strstr(ff, "%k")) != NULL) {
+ c[1] = 'H';
+ ff = c;
+ }
+
+ ret = strftime(s, max, ffmt, tm);
+ g_free(ffmt);
+ return ret;
+#endif
+}
+
+size_t
+e_utf8_strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
+{
+ size_t sz, ret;
+ char *locale_fmt, *buf;
+
+ locale_fmt = g_locale_from_utf8(fmt, -1, NULL, &sz, NULL);
+ if (!locale_fmt)
+ return 0;
+
+ ret = e_strftime(s, max, locale_fmt, tm);
+ if (!ret) {
+ g_free (locale_fmt);
+ return 0;
+ }
+
+ buf = g_locale_to_utf8(s, ret, NULL, &sz, NULL);
+ if (!buf) {
+ g_free (locale_fmt);
+ return 0;
+ }
+
+ if (sz >= max) {
+ char *tmp = buf + max - 1;
+ tmp = g_utf8_find_prev_char(buf, tmp);
+ if (tmp)
+ sz = tmp - buf;
+ else
+ sz = 0;
+ }
+ memcpy(s, buf, sz);
+ s[sz] = '\0';
+ g_free(locale_fmt);
+ g_free(buf);
+ return sz;
+}
#include <sys/types.h>
#include <glib/gmacros.h>
#include <glib/gtypes.h>
+#include <glib/gunicode.h>
G_BEGIN_DECLS
+struct tm;
+
int e_util_mkdir_hier (const char *path, mode_t mode);
gchar *e_util_strstrcase (const gchar *haystack, const gchar *needle);
const gchar *e_util_utf8_strstrcase (const gchar *s1, const gchar *s2);
const gchar *e_util_utf8_strstrcasedecomp (const gchar *haystack, const gchar *needle);
+size_t e_utf8_strftime(char *s, size_t max, const char *fmt, const struct tm *tm);
+size_t e_strftime(char *s, size_t max, const char *fmt, const struct tm *tm);
+
G_END_DECLS
#endif