include config.h.
authorNot Zed <NotZed@Ximian.com>
Fri, 3 Dec 2004 03:33:06 +0000 (03:33 +0000)
committerMichael Zucci <zucchi@src.gnome.org>
Fri, 3 Dec 2004 03:33:06 +0000 (03:33 +0000)
2004-11-15  Not Zed  <NotZed@Ximian.com>

        * libedataserver/e-util.c: include config.h.

        * libedataserver/e-sexp.c: updated from evolution/e-util.

        * libedataserver/e-memory.c: updated from evolution/e-util.

        * iconv-detect.c: added iconv format checker.

        * configure.in: add stftime checks and iconv charset format checks.

        * libedataserver/e-util.c (e_strftime): copied from gal/e-util.h.

        * libedataserver/e-time-utils.c (parse_with_strptime): reove
        e-utf8 depenedncy.

        * libedataserver/e-iconv.[ch]: Moved from gal/util.

        * libedataserver/e-trie.[ch]: Moved from evolution/e-util.
        * libedataserver/e-msgport.[ch]: Moved from evolution/e-util.
        * libedataserver/e-time-utils.[ch]: Moved from evolution/e-util.

13 files changed:
libedataserver/Makefile.am
libedataserver/e-iconv.c [new file with mode: 0644]
libedataserver/e-iconv.h [new file with mode: 0644]
libedataserver/e-memory.c
libedataserver/e-msgport.c [new file with mode: 0644]
libedataserver/e-msgport.h [new file with mode: 0644]
libedataserver/e-sexp.c
libedataserver/e-time-utils.c [new file with mode: 0644]
libedataserver/e-time-utils.h [new file with mode: 0644]
libedataserver/e-trie.c [new file with mode: 0644]
libedataserver/e-trie.h [new file with mode: 0644]
libedataserver/e-util.c
libedataserver/e-util.h

index 0ece08b..8784afe 100644 (file)
@@ -21,17 +21,21 @@ libedataserver_1_2_la_SOURCES =             \
        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
 
@@ -52,14 +56,18 @@ libedataserverinclude_HEADERS =             \
        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                        \
diff --git a/libedataserver/e-iconv.c b/libedataserver/e-iconv.c
new file mode 100644 (file)
index 0000000..3236521
--- /dev/null
@@ -0,0 +1,614 @@
+/* -*- 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;
+}
diff --git a/libedataserver/e-iconv.h b/libedataserver/e-iconv.h
new file mode 100644 (file)
index 0000000..14b9385
--- /dev/null
@@ -0,0 +1,49 @@
+/* -*- 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_ */
index ae882d7..455e263 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <string.h> /* memset() */
 #include <stdlib.h> /* alloca() */
+#include <glib.h>
 
 #define s(x)                   /* strv debug */
 #define p(x)   /* poolv debug */
@@ -282,7 +283,7 @@ e_memchunk_clean(MemChunk *m)
        /* 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;
@@ -535,7 +536,13 @@ void e_mempool_destroy(MemPool *pool)
 {
        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
        }
 }
 
diff --git a/libedataserver/e-msgport.c b/libedataserver/e-msgport.c
new file mode 100644 (file)
index 0000000..f360a3f
--- /dev/null
@@ -0,0 +1,1250 @@
+/* -*- 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(&ethread_lock);
+       e_dlist_addtail(&ethread_list, (EDListNode *)e);
+       pthread_mutex_unlock(&ethread_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(&ethread_lock);
+       e_dlist_remove((EDListNode *)e);
+       pthread_mutex_unlock(&ethread_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(&ethread_lock);
+               e = (EThread *)ethread_list.head;
+               while (e->next && !busy) {
+                       busy = e_thread_busy(e);
+                       e = e->next;
+               }
+               pthread_mutex_unlock(&ethread_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
diff --git a/libedataserver/e-msgport.h b/libedataserver/e-msgport.h
new file mode 100644 (file)
index 0000000..6347564
--- /dev/null
@@ -0,0 +1,111 @@
+
+#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
index 3e6e661..724859f 100644 (file)
@@ -701,7 +701,7 @@ e_sexp_term_eval(struct _ESExp *f, struct _ESExpTerm *t)
                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]);
                }
@@ -903,7 +903,7 @@ parse_value(ESExp *f)
                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;
@@ -922,7 +922,7 @@ parse_value(ESExp *f)
                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;
@@ -947,6 +947,7 @@ parse_value(ESExp *f)
                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:
@@ -965,6 +966,7 @@ parse_value(ESExp *f)
                }
                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:
diff --git a/libedataserver/e-time-utils.c b/libedataserver/e-time-utils.c
new file mode 100644 (file)
index 0000000..4666b80
--- /dev/null
@@ -0,0 +1,490 @@
+/* -*- 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
+}
diff --git a/libedataserver/e-time-utils.h b/libedataserver/e-time-utils.h
new file mode 100644 (file)
index 0000000..0b081da
--- /dev/null
@@ -0,0 +1,58 @@
+/* -*- 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 */
diff --git a/libedataserver/e-trie.c b/libedataserver/e-trie.c
new file mode 100644 (file)
index 0000000..2edf94d
--- /dev/null
@@ -0,0 +1,345 @@
+/* -*- 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;
+}
diff --git a/libedataserver/e-trie.h b/libedataserver/e-trie.h
new file mode 100644 (file)
index 0000000..cabf5fc
--- /dev/null
@@ -0,0 +1,47 @@
+/* -*- 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__ */
index 2209c6d..b103af7 100644 (file)
  * 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>
@@ -29,6 +34,7 @@
 #include <glib/gunicode.h>
 #include <glib/gutils.h>
 #include <glib/galloca.h>
+#include <glib/gconvert.h>
 #include "e-util.h"
 
 int
@@ -1543,3 +1549,67 @@ e_util_utf8_strstrcasedecomp (const gchar *haystack, const gchar *needle)
                                                                                 
         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;
+}
index 55340ab..45922bf 100644 (file)
 #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);
@@ -35,6 +38,9 @@ gchar       *e_util_unicode_get_utf8 (const gchar *text, gunichar *out);
 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