[evolution-data-server/gnome-3-10] Bug 710989 - Replace most uses of strcpy()



commit 1a6efe8419789661bf5bf47d2d59f9b9e978d77f
Author: Murray Cumming <murrayc murrayc com>
Date:   Fri Nov 1 10:32:23 2013 -0400

    Bug 710989 - Replace most uses of strcpy()
    
    Because strcpy() can overwrite its buffer.  As with the previous
    replacement of sprintf(), some of these could probably be replaced by
    g_strdup(), but there could be some performance impact.
    
    (cherry picked from commit 61bf9fb860ecb4c6ace37cf3414d68d298ec26cf)

 calendar/libedata-cal/e-data-cal-view.c         |   10 +-
 camel/camel-debug.c                             |    6 +-
 camel/camel-folder-summary.c                    |    8 +-
 camel/camel-iconv.c                             |    6 +-
 camel/camel-imapx-utils.c                       |   12 +-
 camel/camel-lock-helper.c                       |    2 +-
 camel/camel-mempool.c                           |    6 +-
 camel/camel-multipart.c                         |    2 +-
 camel/camel-net-utils.c                         |    2 +-
 camel/camel-sasl-digest-md5.c                   |    2 +-
 camel/camel-text-index.c                        |    2 +-
 camel/camel-vee-store.c                         |   12 +-
 camel/providers/nntp/camel-nntp-folder.c        |   18 +-
 camel/providers/nntp/camel-nntp-store-summary.c |    8 +-
 camel/providers/nntp/camel-nntp-summary.c       |    7 +-
 libedataserver/e-collator.c                     |  650 +++++++++++++++++++++++
 16 files changed, 716 insertions(+), 37 deletions(-)
---
diff --git a/calendar/libedata-cal/e-data-cal-view.c b/calendar/libedata-cal/e-data-cal-view.c
index 5b3f4ba..82c454a 100644
--- a/calendar/libedata-cal/e-data-cal-view.c
+++ b/calendar/libedata-cal/e-data-cal-view.c
@@ -761,6 +761,7 @@ notify_remove (EDataCalView *view,
                ECalComponentId *id)
 {
        gchar *ids;
+       gsize ids_len, ids_offset;
        gchar *uid, *rid;
        gsize uid_len, rid_len;
 
@@ -791,12 +792,13 @@ notify_remove (EDataCalView *view,
                uid = NULL;
        } else {
                /* concatenate */
-               ids = g_malloc (uid_len + rid_len + (rid_len ? 2 : 1));
+               ids_len = uid_len + rid_len + (rid_len ? 2 : 1);
+               ids = g_malloc (ids_len);
                if (uid_len)
-                       strcpy (ids, uid);
+                       g_strlcpy (ids, uid, ids_len);
                if (rid_len) {
-                       ids[uid_len] = '\n';
-                       strcpy (ids + uid_len + 1, rid);
+                       ids_offset = uid_len + 1;
+                       g_strlcpy (ids + ids_offset, rid, ids_len - ids_offset);
                }
        }
        g_array_append_val (view->priv->removes, ids);
diff --git a/camel/camel-debug.c b/camel/camel-debug.c
index cbcd77a..787a7da 100644
--- a/camel/camel-debug.c
+++ b/camel/camel-debug.c
@@ -98,6 +98,7 @@ gboolean camel_debug (const gchar *mode)
        if (debug_table) {
                gchar *colon;
                gchar *fallback;
+               gsize fallback_len;
 
                if (g_hash_table_lookup (debug_table, mode))
                        return TRUE;
@@ -105,8 +106,9 @@ gboolean camel_debug (const gchar *mode)
                /* Check for fully qualified debug */
                colon = strchr (mode, ':');
                if (colon) {
-                       fallback = g_alloca (strlen (mode) + 1);
-                       strcpy (fallback, mode);
+                       fallback_len = strlen (mode) + 1;
+                       fallback = g_alloca (fallback_len);
+                       g_strlcpy (fallback, mode, fallback_len);
                        colon = (colon - mode) + fallback;
                        /* Now check 'module[:*]' */
                        *colon = 0;
diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c
index 887afd4..43b51be 100644
--- a/camel/camel-folder-summary.c
+++ b/camel/camel-folder-summary.c
@@ -4186,6 +4186,7 @@ camel_flag_set (CamelFlag **list,
                 gboolean value)
 {
        CamelFlag *flag, *tmp;
+       gsize tmp_len = 0;
 
        if (!name)
                return TRUE;
@@ -4205,8 +4206,9 @@ camel_flag_set (CamelFlag **list,
        }
 
        if (value) {
-               tmp = g_malloc (sizeof (*tmp) + strlen (name));
-               strcpy (tmp->name, name);
+               tmp_len = sizeof (*tmp) + strlen (name);
+               tmp = g_malloc (tmp_len);
+               g_strlcpy (tmp->name, name, tmp_len);
                tmp->next = NULL;
                flag->next = tmp;
        }
@@ -4359,7 +4361,7 @@ camel_tag_set (CamelTag **list,
 
        if (value) {
                tmp = g_malloc (sizeof (*tmp) + strlen (name));
-               strcpy (tmp->name, name);
+               g_strlcpy (tmp->name, name, sizeof (tmp->name));
                tmp->value = g_strdup (value);
                tmp->next = NULL;
                tag->next = tmp;
diff --git a/camel/camel-iconv.c b/camel/camel-iconv.c
index 3cbf712..21f29ca 100644
--- a/camel/camel-iconv.c
+++ b/camel/camel-iconv.c
@@ -282,12 +282,14 @@ const gchar *
 camel_iconv_charset_name (const gchar *charset)
 {
        gchar *name, *ret, *tmp;
+       gsize name_len;
 
        if (charset == NULL)
                return NULL;
 
-       name = g_alloca (strlen (charset) + 1);
-       strcpy (name, charset);
+       name_len = strlen (charset) + 1;
+       name = g_alloca (name_len);
+       g_strlcpy (name, charset, name_len);
        e_strdown (name);
 
        iconv_init (TRUE);
diff --git a/camel/camel-imapx-utils.c b/camel/camel-imapx-utils.c
index 73bc508..bdf8de8 100644
--- a/camel/camel-imapx-utils.c
+++ b/camel/camel-imapx-utils.c
@@ -864,6 +864,7 @@ imapx_parse_param_list (CamelIMAPXStream *is,
        guint len;
        guchar *token;
        gchar *param;
+       gsize param_len;
 
        p (is->tagprefix, "body_fld_param\n");
 
@@ -877,8 +878,9 @@ imapx_parse_param_list (CamelIMAPXStream *is,
                        camel_imapx_stream_ungettoken (is, tok, token, len);
 
                        camel_imapx_stream_astring (is, &token, cancellable, NULL);
-                       param = alloca (strlen ((gchar *) token) + 1);
-                       strcpy (param, (gchar *) token);
+                       param_len = strlen ((gchar *) token) + 1;
+                       param = alloca (param_len);
+                       g_strlcpy (param, (gchar *) token, param_len);
                        camel_imapx_stream_astring (is, &token, cancellable, NULL);
                        camel_header_set_param (plist, param, (gchar *) token);
                }
@@ -987,6 +989,7 @@ imapx_parse_body_fields (CamelIMAPXStream *is,
 {
        guchar *token;
        gchar  *type;
+       gsize type_len;
        guint64 number;
        struct _CamelMessageContentInfo *cinfo;
 
@@ -1001,8 +1004,9 @@ imapx_parse_body_fields (CamelIMAPXStream *is,
        /* this should be string not astring */
        if (!camel_imapx_stream_astring (is, &token, cancellable, error))
                goto error;
-       type = alloca (strlen ((gchar *) token) + 1);
-       strcpy (type, (gchar *) token);
+       type_len = strlen ((gchar *) token) + 1;
+       type = alloca (type_len);
+       g_strlcpy (type, (gchar *) token, type_len);
        if (!camel_imapx_stream_astring (is, &token, cancellable, error))
                goto error;
        cinfo->type = camel_content_type_new (type, (gchar *) token);
diff --git a/camel/camel-lock-helper.c b/camel/camel-lock-helper.c
index 9efe35b..835d4e8 100644
--- a/camel/camel-lock-helper.c
+++ b/camel/camel-lock-helper.c
@@ -176,7 +176,7 @@ lock_path (const gchar *path,
                info->uid = lock_real_uid;
        }
 
-       strcpy (info->path, path);
+       g_strlcpy (info->path, path, sizeof (info->path));
        info->id = lock_id;
        info->depth = 1;
        info->next = lock_info_list;
diff --git a/camel/camel-mempool.c b/camel/camel-mempool.c
index e1b6f2f..c65c495 100644
--- a/camel/camel-mempool.c
+++ b/camel/camel-mempool.c
@@ -150,9 +150,11 @@ camel_mempool_strdup (CamelMemPool *pool,
                       const gchar *str)
 {
        gchar *out;
+       gsize out_len;
 
-       out = camel_mempool_alloc (pool, strlen (str) + 1);
-       strcpy (out, str);
+       out_len = strlen (str) + 1;
+       out = camel_mempool_alloc (pool, out_len);
+       g_strlcpy (out, str, out_len);
 
        return out;
 }
diff --git a/camel/camel-multipart.c b/camel/camel-multipart.c
index 8599701..e136353 100644
--- a/camel/camel-multipart.c
+++ b/camel/camel-multipart.c
@@ -268,7 +268,7 @@ multipart_set_boundary (CamelMultipart *multipart,
                g_checksum_free (checksum);
 
                g_free (bgen);
-               strcpy (bbuf, "=-");
+               g_strlcpy (bbuf, "=-", sizeof (bbuf));
                p = bbuf + 2;
                state = save = 0;
                p += g_base64_encode_step (
diff --git a/camel/camel-net-utils.c b/camel/camel-net-utils.c
index a83c847..79003f3 100644
--- a/camel/camel-net-utils.c
+++ b/camel/camel-net-utils.c
@@ -236,7 +236,7 @@ camel_gethostbyname_r (const gchar *name,
                return ERANGE;
 
        /* h_name */
-       strcpy (buf, res->ai_canonname);
+       g_strlcpy (buf, res->ai_canonname, buflen);
        host->h_name = buf;
        buf += len;
 
diff --git a/camel/camel-sasl-digest-md5.c b/camel/camel-sasl-digest-md5.c
index bf81570..12bf48c 100644
--- a/camel/camel-sasl-digest-md5.c
+++ b/camel/camel-sasl-digest-md5.c
@@ -614,7 +614,7 @@ generate_response (struct _DigestChallenge *challenge,
        resp->cnonce = g_base64_encode ((guchar *) digest, 8);
 
        /* we don't support re-auth so the nonce count is always 1 */
-       strcpy (resp->nc, "00000001");
+       g_strlcpy (resp->nc, "00000001", sizeof (resp->nc));
 
        /* choose the QOP */
        /* FIXME: choose - probably choose "auth" ??? */
diff --git a/camel/camel-text-index.c b/camel/camel-text-index.c
index 83fe856..47a6c6a 100644
--- a/camel/camel-text-index.c
+++ b/camel/camel-text-index.c
@@ -457,7 +457,7 @@ text_index_compress_nosync (CamelIndex *idx)
        newpath = alloca (i);
        savepath = alloca (i);
 
-       strcpy (oldpath, idx->path);
+       g_strlcpy (oldpath, idx->path, i);
        oldpath[strlen (oldpath) - strlen (".index")] = 0;
 
        tmp_name (oldpath, newpath, i);
diff --git a/camel/camel-vee-store.c b/camel/camel-vee-store.c
index c4e1c1b..e8dd900 100644
--- a/camel/camel-vee-store.c
+++ b/camel/camel-vee-store.c
@@ -209,6 +209,7 @@ vee_store_get_folder_sync (CamelStore *store,
        CamelVeeFolder *vf;
        CamelFolder *folder;
        gchar *name, *p;
+       gsize name_len;
 
        vf = (CamelVeeFolder *) camel_vee_folder_new (store, folder_name, flags);
        if (vf && ((vf->flags & CAMEL_STORE_FOLDER_PRIVATE) == 0)) {
@@ -217,8 +218,9 @@ vee_store_get_folder_sync (CamelStore *store,
                full_name = camel_folder_get_full_name (CAMEL_FOLDER (vf));
 
                /* Check that parents exist, if not, create dummy ones */
-               name = alloca (strlen (full_name) + 1);
-               strcpy (name, full_name);
+               name_len = strlen (full_name) + 1;
+               name = alloca (name_len);
+               g_strlcpy (name, full_name, name_len);
                p = name;
                while ( (p = strchr (p, '/'))) {
                        *p = 0;
@@ -456,6 +458,7 @@ vee_store_rename_folder_sync (CamelStore *store,
 {
        CamelFolder *folder, *oldfolder;
        gchar *p, *name;
+       gsize name_len;
 
        d (printf ("vee rename folder '%s' '%s'\n", old, new));
 
@@ -478,8 +481,9 @@ vee_store_rename_folder_sync (CamelStore *store,
        }
 
        /* Check that new parents exist, if not, create dummy ones */
-       name = alloca (strlen (new) + 1);
-       strcpy (name, new);
+       name_len = strlen (new) + 1;
+       name = alloca (name_len);
+       g_strlcpy (name, new, name_len);
        p = name;
        while ( (p = strchr (p, '/'))) {
                *p = 0;
diff --git a/camel/providers/nntp/camel-nntp-folder.c b/camel/providers/nntp/camel-nntp-folder.c
index 17ea4ce..55b209d 100644
--- a/camel/providers/nntp/camel-nntp-folder.c
+++ b/camel/providers/nntp/camel-nntp-folder.c
@@ -271,13 +271,15 @@ nntp_get_filename (CamelFolder *folder,
        CamelDataCache *nntp_cache;
        CamelNNTPStore *nntp_store;
        gchar *article, *msgid;
+       gsize article_len;
        gchar *filename;
 
        parent_store = camel_folder_get_parent_store (folder);
        nntp_store = CAMEL_NNTP_STORE (parent_store);
 
-       article = alloca (strlen (uid) + 1);
-       strcpy (article, uid);
+       article_len = strlen (uid) + 1;
+       article = alloca (article_len);
+       g_strlcpy (article, uid, article_len);
        msgid = strchr (article, ',');
        if (msgid == NULL) {
                g_set_error (
@@ -377,10 +379,12 @@ nntp_folder_cache_message (CamelDiscoFolder *disco_folder,
 {
        CamelStream *stream;
        gchar *article, *msgid;
+       gsize article_len;
        gboolean success = TRUE;
 
-       article = alloca (strlen (uid) + 1);
-       strcpy (article, uid);
+       article_len = strlen (uid) + 1;
+       article = alloca (article_len);
+       g_strlcpy (article, uid, article_len);
        msgid = strchr (article, ',');
        if (!msgid) {
                g_set_error (
@@ -495,14 +499,16 @@ nntp_folder_get_message_sync (CamelFolder *folder,
        CamelNNTPFolder *nntp_folder;
        CamelStream *stream = NULL;
        gchar *article, *msgid;
+       gsize article_len;
 
        parent_store = camel_folder_get_parent_store (folder);
 
        nntp_folder = CAMEL_NNTP_FOLDER (folder);
        nntp_store = CAMEL_NNTP_STORE (parent_store);
 
-       article = alloca (strlen (uid) + 1);
-       strcpy (article, uid);
+       article_len = strlen (uid) + 1;
+       article = alloca (article_len);
+       g_strlcpy (article, uid, article_len);
        msgid = strchr (article, ',');
        if (msgid == NULL) {
                g_set_error (
diff --git a/camel/providers/nntp/camel-nntp-store-summary.c b/camel/providers/nntp/camel-nntp-store-summary.c
index a04291f..cf89ba4 100644
--- a/camel/providers/nntp/camel-nntp-store-summary.c
+++ b/camel/providers/nntp/camel-nntp-store-summary.c
@@ -180,11 +180,13 @@ camel_nntp_store_summary_path_to_full (CamelNNTPStoreSummary *s,
        const gchar *p;
        gint state = 0;
        gchar *subpath, *last = NULL;
+       gsize subpath_len = 0;
        CamelStoreInfo *si;
 
        /* check to see if we have a subpath of path already defined */
-       subpath = g_alloca (strlen (path) + 1);
-       strcpy (subpath, path);
+       subpath_len = strlen (path) + 1;
+       subpath = g_alloca (subpath_len);
+       g_strlcpy (subpath, path, subpath_len);
        do {
                si = camel_store_summary_path ((CamelStoreSummary *) s, subpath);
                if (si == NULL) {
@@ -257,7 +259,7 @@ camel_nntp_store_summary_add_from_full (CamelNNTPStoreSummary *s,
 
        len = strlen (full);
        full_name = g_alloca (len + 1);
-       strcpy (full_name, full);
+       g_strlcpy (full_name, full, len + 1);
        if (full_name[len - 1] == dir_sep)
                full_name[len - 1] = 0;
 
diff --git a/camel/providers/nntp/camel-nntp-summary.c b/camel/providers/nntp/camel-nntp-summary.c
index 63060d4..6f8f712 100644
--- a/camel/providers/nntp/camel-nntp-summary.c
+++ b/camel/providers/nntp/camel-nntp-summary.c
@@ -455,13 +455,16 @@ camel_nntp_summary_check (CamelNNTPSummary *cns,
        l = strtoul (line, &line, 10);
        if (line[0] == ' ') {
                gchar *tmp;
+               gsize tmp_len;
 
                folder = line + 1;
                tmp = strchr (folder, ' ');
                if (tmp)
                        *tmp = 0;
-               tmp = g_alloca (strlen (folder) + 1);
-               strcpy (tmp, folder);
+
+               tmp_len = strlen (folder) + 1;
+               tmp = g_alloca (tmp_len);
+               g_strlcpy (tmp, folder, tmp_len);
                folder = tmp;
        }
 
diff --git a/libedataserver/e-collator.c b/libedataserver/e-collator.c
new file mode 100644
index 0000000..88c8709
--- /dev/null
+++ b/libedataserver/e-collator.c
@@ -0,0 +1,650 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Tristan Van Berkom <tristanvb openismus com>
+ */
+
+/**
+ * SECTION: e-collator
+ * @include: libedataserver/libedataserver.h
+ * @short_description: Collation services for locale sensitive sorting
+ *
+ * The #ECollator is a wrapper object around ICU collation services and
+ * provides features to sort words in locale specific ways. The collator
+ * also provides some API for determining features of the active alphabet
+ * in the user's locale, and which words should be sorted under which
+ * letter in the user's alphabet.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+/* ICU includes */
+#include <unicode/uclean.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+
+#include "e-collator.h"
+#include "e-alphabet-index-private.h"
+#include "e-transliterator-private.h"
+
+#define CONVERT_BUFFER_LEN        512
+#define COLLATION_KEY_BUFFER_LEN  1024
+#define LOCALE_BUFFER_LEN         256
+
+#define ENABLE_DEBUGGING 0
+
+G_DEFINE_QUARK (e-collator-error-quark, e_collator_error)
+
+G_DEFINE_BOXED_TYPE (ECollator,
+                    e_collator,
+                    e_collator_ref, 
+                    e_collator_unref)
+
+struct _ECollator
+{
+       UCollator       *coll;
+       volatile gint    ref_count;
+
+       EAlphabetIndex  *alpha_index;
+       gchar          **labels;
+       gint             n_labels;
+       gint             underflow;
+       gint             inflow;
+       gint             overflow;
+
+       ETransliterator *transliterator;
+};
+
+/*****************************************************
+ *                ICU Helper Functions               *
+ *****************************************************/
+#if ENABLE_DEBUGGING
+static void
+print_available_locales (void)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       UChar result[100];
+       gchar printable[100 * 4];
+       gint count, i;
+
+       u_init (&status);
+
+       g_printerr ("List of available locales (default locale is: %s)\n", uloc_getDefault());
+
+       count = uloc_countAvailable();
+       for (i = 0; i < count; i++) {
+               UEnumeration *keywords;
+               const gchar *keyword;
+
+               uloc_getDisplayName(uloc_getAvailable(i), NULL, result, 100, &status);
+
+               u_austrncpy (printable, result, sizeof (printable));
+
+               /* print result */
+               g_printerr ("\t%s - %s", uloc_getAvailable(i), printable);
+
+               keywords = uloc_openKeywords (uloc_getAvailable(i), &status);
+               if (keywords) {
+                       UErrorCode kstatus = U_ZERO_ERROR;
+
+                       g_printerr ("[");
+
+                       while ((keyword = uenum_next (keywords, NULL, &kstatus)) != NULL)
+                               g_printerr (" %s ", keyword);
+
+                       g_printerr ("]");
+
+                       uenum_close (keywords);
+               }
+               g_printerr ("\n");
+       }
+}
+#endif
+
+static gchar *
+canonicalize_locale (const gchar  *posix_locale,
+                    gchar       **language_code,
+                    GError      **error)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       gchar  locale_buffer[LOCALE_BUFFER_LEN];
+       gchar  language_buffer[8];
+       gchar *icu_locale;
+       gchar *final_locale;
+       gint   len;
+       const gchar *collation_type = NULL;
+
+       len = uloc_canonicalize (posix_locale, locale_buffer, LOCALE_BUFFER_LEN, &status);
+
+       if (U_FAILURE (status)) {
+               g_set_error (error, E_COLLATOR_ERROR,
+                            E_COLLATOR_ERROR_INVALID_LOCALE,
+                            "Failed to interpret locale '%s' (%s)",
+                            posix_locale,
+                            u_errorName (status));
+               return NULL;
+       }
+
+       if (len > LOCALE_BUFFER_LEN) {
+               icu_locale = g_malloc (len);
+
+               uloc_canonicalize (posix_locale, icu_locale, len, &status);
+       } else {
+               icu_locale = g_strndup (locale_buffer, len);
+       }
+
+       status = U_ZERO_ERROR;
+       len = uloc_getLanguage (icu_locale, language_buffer, 8, &status);
+
+       if (U_FAILURE (status)) {
+               g_set_error (error, E_COLLATOR_ERROR,
+                            E_COLLATOR_ERROR_INVALID_LOCALE,
+                            "Failed to interpret language for locale '%s': %s",
+                            icu_locale,
+                            u_errorName (status));
+               g_free (icu_locale);
+               return NULL;
+       }
+
+       /* Add 'phonebook' tailoring to certain locales */
+       if (len < 8 &&
+           (strcmp (language_buffer, "de") == 0 ||
+            strcmp (language_buffer, "fi") == 0)) {
+
+               collation_type = "phonebook";
+       }
+
+       if (collation_type != NULL)
+               final_locale = g_strconcat (icu_locale, "@collation=", collation_type, NULL);
+       else {
+               final_locale = icu_locale;
+               icu_locale = NULL;
+       }
+
+       g_free (icu_locale);
+
+       if (language_code)
+               *language_code = g_strdup (language_buffer);
+
+       return final_locale;
+}
+
+/* All purpose character encoding function, encodes text
+ * to a UChar from UTF-8 and first ensures that the string
+ * is valid UTF-8
+ */
+static const UChar *
+convert_to_ustring (const gchar  *string,
+                   UChar        *buffer,
+                   gint          buffer_len,
+                   gint         *result_len,
+                   UChar       **free_me,
+                   GError      **error)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       const gchar *source_utf8;
+       gchar *alloc_utf8 = NULL;
+       gint   converted_len = 0;
+       UChar *converted_buffer;
+
+       /* First make sure we're dealing with utf8 */
+       if (g_utf8_validate (string, -1, NULL))
+               source_utf8 = string;
+       else {
+               alloc_utf8 = e_util_utf8_make_valid (string);
+               source_utf8 = alloc_utf8;
+       }
+
+       /* First pass, try converting to UChar in the given buffer */
+       converted_buffer = u_strFromUTF8Lenient (buffer,
+                                                buffer_len,
+                                                &converted_len,
+                                                source_utf8,
+                                                -1,
+                                                &status);
+
+       /* Set the result length right away... */
+       *result_len = converted_len;
+
+       if (U_FAILURE (status)) {
+               converted_buffer = NULL;
+               goto out;
+       }
+
+       /* Second pass, allocate a buffer big enough and then convert */
+       if (converted_len > buffer_len) {
+               *free_me = g_new (UChar, converted_len);
+
+               converted_buffer = u_strFromUTF8Lenient (*free_me,
+                                                        converted_len,
+                                                        NULL,
+                                                        source_utf8,
+                                                        -1,
+                                                        &status);
+
+               if (U_FAILURE (status)) {
+                       g_free (*free_me);
+                       *free_me = NULL;
+                       converted_buffer = NULL;
+                       goto out;
+               }
+       }
+
+ out:
+       g_free (alloc_utf8);
+
+       if (U_FAILURE (status))
+               g_set_error (error, E_COLLATOR_ERROR,
+                            E_COLLATOR_ERROR_CONVERSION,
+                            "Error occured while converting character encoding (%s)",
+                            u_errorName (status));
+
+       return converted_buffer;
+}
+
+/*****************************************************
+ *                        API                        *
+ *****************************************************/
+
+/**
+ * e_collator_new:
+ * @locale: The locale under which to sort
+ * @error: (allow none): A location to store a #GError from the #E_COLLATOR_ERROR domain
+ *
+ * Creates a new #ECollator for the given @locale,
+ * the returned collator should be freed with e_collator_unref().
+ *
+ * Returns: (transfer full): A newly created #ECollator.
+ *
+ * Since: 3.12
+ */
+ECollator *
+e_collator_new (const gchar     *locale,
+               GError         **error)
+{
+       ECollator *collator;
+       UCollator *coll;
+       UErrorCode status = U_ZERO_ERROR;
+       gchar     *icu_locale;
+       gchar     *language_code = NULL;
+
+       g_return_val_if_fail (locale && locale[0], NULL);
+
+#if ENABLE_DEBUGGING
+       print_available_locales ();
+#endif
+
+       icu_locale = canonicalize_locale (locale, &language_code, error);
+       if (!icu_locale)
+               return NULL;
+
+       coll = ucol_open (icu_locale, &status);
+
+       if (U_FAILURE (status)) {
+               g_set_error (error, E_COLLATOR_ERROR,
+                            E_COLLATOR_ERROR_OPEN,
+                            "Unable to open collator for locale '%s' (%s)",
+                            icu_locale,
+                            u_errorName (status));
+
+               g_free (language_code);
+               g_free (icu_locale);
+               ucol_close (coll);
+               return NULL;
+       }
+
+       g_free (icu_locale);
+
+       ucol_setStrength (coll, UCOL_DEFAULT_STRENGTH);
+
+       collator = g_slice_new0 (ECollator);
+       collator->coll = coll;
+       collator->ref_count = 1;
+
+       /* In Chinese we use transliteration services to sort latin 
+        * names interleaved with Chinese names in a latin AlphabeticIndex
+        */
+       if (g_strcmp0 (language_code, "zh") == 0)
+               collator->transliterator = _e_transliterator_cxx_new ("Han-Latin");
+
+       collator->alpha_index = _e_alphabet_index_cxx_new_for_language (language_code);
+       collator->labels = _e_alphabet_index_cxx_get_labels (collator->alpha_index,
+                                                            &collator->n_labels,
+                                                            &collator->underflow,
+                                                            &collator->inflow,
+                                                            &collator->overflow);
+
+       g_free (language_code);
+
+       return collator;
+}
+
+/**
+ * e_collator_ref:
+ * @collator: An #ECollator
+ *
+ * Increases the reference count of @collator.
+ *
+ * Returns: (transfer full): @collator
+ *
+ * Since: 3.12
+ */
+ECollator *
+e_collator_ref (ECollator *collator)
+{
+       g_return_val_if_fail (collator != NULL, NULL);
+
+       g_atomic_int_inc (&collator->ref_count);
+
+       return collator;
+}
+
+/**
+ * e_collator_unref:
+ * @collator: An #ECollator
+ *
+ * Decreases the reference count of @collator.
+ * If the reference count reaches 0 then the collator is freed
+ *
+ * Since: 3.12
+ */
+void
+e_collator_unref (ECollator *collator)
+{
+       g_return_if_fail (collator != NULL);
+
+       if (g_atomic_int_dec_and_test (&collator->ref_count)) {
+
+               if (collator->coll)
+                       ucol_close (collator->coll);
+
+               _e_alphabet_index_cxx_free (collator->alpha_index);
+               g_strfreev (collator->labels);
+
+               /* The transliterator is only used for specialized sorting in some locales,
+                * notably Chinese locales
+                */
+               if (collator->transliterator)
+                       _e_transliterator_cxx_free (collator->transliterator);
+
+               g_slice_free (ECollator, collator);
+       }
+}
+
+/**
+ * e_collator_generate_key:
+ * @collator: An #ECollator
+ * @str: The string to generate a collation key for
+ * @error: (allow none): A location to store a #GError from the #E_COLLATOR_ERROR domain
+ *
+ * Generates a collation key for @str, the result of comparing
+ * two collation keys with strcmp() will be the same result
+ * of calling e_collator_collate() on the same original strings.
+ *
+ * This function will first ensure that @str is valid UTF-8 encoded.
+ *
+ * Returns: (transfer full): A collation key for @str, or %NULL on failure with @error set.
+ *
+ * Since: 3.12
+ */
+gchar *
+e_collator_generate_key (ECollator    *collator,
+                        const gchar  *str,
+                        GError      **error)
+{
+       UChar  source_buffer[CONVERT_BUFFER_LEN];
+       UChar *free_me = NULL;
+       const UChar *source;
+       gchar stack_buffer[COLLATION_KEY_BUFFER_LEN];
+       gchar *collation_key;
+       gint key_len, source_len = 0;
+       gint alphabet_index;
+       gchar *translit_str = NULL;
+       const gchar *input_str;
+
+       g_return_val_if_fail (collator != NULL, NULL);
+       g_return_val_if_fail (str != NULL, NULL);
+
+       /* We may need to perform a conversion before generating the sort key */
+       if (collator->transliterator) {
+               translit_str = _e_transliterator_cxx_transliterate (collator->transliterator, str);
+               input_str = translit_str;
+       } else {
+               input_str = str;
+       }
+
+       source = convert_to_ustring (input_str,
+                                    source_buffer,
+                                    CONVERT_BUFFER_LEN,
+                                    &source_len,
+                                    &free_me,
+                                    error);
+
+       if (!source) {
+               g_free (translit_str);
+               return NULL;
+       }
+
+       /* Get the numerical index for this string */
+       alphabet_index = _e_alphabet_index_cxx_get_index (collator->alpha_index, input_str);
+
+       /* First try to generate a key in a predefined buffer size */
+       key_len = ucol_getSortKey (collator->coll, source, source_len,
+                                  (guchar *)stack_buffer, COLLATION_KEY_BUFFER_LEN);
+
+       if (key_len > COLLATION_KEY_BUFFER_LEN) {
+
+               /* Stack buffer wasn't large enough, regenerate into a new buffer
+                * (add a byte for a trailing NULL char)
+                *
+                * Note we allocate 4 extra chars to hold the prefixed alphabetic
+                * index into the first 4 charachters (the 5th extra char is the trailing
+                * null character).
+                */
+               collation_key = g_malloc (key_len + 5);
+
+               /* Format the alphabetic index into the first 4 chars */
+               snprintf (collation_key, 4, "%03d-", alphabet_index);
+
+               /* Get the sort key and put it in &collation_key[4] */
+               ucol_getSortKey (collator->coll, source, source_len,
+                                (guchar *)(collation_key + 4), key_len);
+
+               /* Just being paranoid, make sure we're null terminated since the API
+                * doesn't specify if the result length is null character inclusive
+                */
+               collation_key[key_len + 4] = '\0';
+       } else {
+               GString *string = g_string_new (NULL);
+
+               /* Format the alphabetic index into the first 4 chars */
+               g_string_append_printf (string, "%03d-", alphabet_index);
+
+               /* Insert the rest of the sort key from the stack buffer into the allocated buffer */
+               g_string_insert_len (string, 4, stack_buffer, key_len);
+
+               collation_key = g_string_free (string, FALSE);
+       }
+
+       g_free (free_me);
+       g_free (translit_str);
+
+       return (gchar *)collation_key;
+}
+
+/**
+ * e_collator_generate_key_for_index:
+ * @collator: An #ECollator
+ * @index: An index into the alphabetic labels
+ *
+ * Generates a sort key for the given alphabetic @index.
+ *
+ * The generated sort key is guaranteed to sort below
+ * any sort keys for words beginning with any variant of
+ * the given letter.
+ *
+ * For instance, a sort key generated for the index 5 of
+ * a latin alphabet, where the fifth index is 'E' will sort
+ * below any sort keys generated for words starting with
+ * the characters 'e', 'E', 'é', 'É', 'è' or 'È'. It will also
+ * sort above any sort keys generated for words starting with
+ * the characters 'd' or 'D'.
+ *
+ * Returns: (transfer full): A sort key for the given index
+ *
+ * Since: 3.12
+ */
+gchar *
+e_collator_generate_key_for_index (ECollator       *collator,
+                                  gint             index)
+{
+       g_return_val_if_fail (collator != NULL, NULL);
+       g_return_val_if_fail (index >= 0 && index < collator->n_labels, NULL);
+
+       return g_strdup_printf ("%03d", index);
+}
+
+/**
+ * e_collator_collate:
+ * @collator: An #ECollator
+ * @str_a: A string to compare
+ * @str_b: The string to compare with @str_a
+ * @result: (out): A location to store the comparison result
+ * @error: (allow none): A location to store a #GError from the #E_COLLATOR_ERROR domain
+ *
+ * Compares @str_a with @str_b, the order of strings is determined by the parameters of @collator.
+ *
+ * The @result will be set to integer less than, equal to, or greater than zero if @str_a is found,
+ * respectively, to be less than, to match, or be greater than @str_b.
+ *
+ * This function will first ensure that both strings are valid UTF-8.
+ *
+ * Returns: %TRUE on success, otherwise if %FALSE is returned then @error will be set.
+ *
+ * Since: 3.12
+ */
+gboolean
+e_collator_collate (ECollator    *collator,
+                   const gchar  *str_a,
+                   const gchar  *str_b,
+                   gint         *result,
+                   GError      **error)
+{
+       gchar *sort_key_a, *sort_key_b;
+
+       g_return_val_if_fail (collator != NULL, -1);
+       g_return_val_if_fail (str_a != NULL, -1);
+       g_return_val_if_fail (str_b != NULL, -1);
+       g_return_val_if_fail (result != NULL, -1);
+
+       sort_key_a = e_collator_generate_key (collator, str_a, error);
+       if (!sort_key_a)
+               return FALSE;
+
+       sort_key_b = e_collator_generate_key (collator, str_b, error);
+       if (!sort_key_b) {
+               g_free (sort_key_a);
+               return FALSE;
+       }
+
+       *result = strcmp (sort_key_a, sort_key_b);
+
+       g_free (sort_key_a);
+       g_free (sort_key_b);
+
+       return TRUE;
+}
+
+/**
+ * e_collator_get_index_labels:
+ * @collator: An #ECollator
+ * @n_labels: (out): The number of labels/indexes available for @collator
+ * @underflow: (allow-none) (out): The underflow index, for any words which sort below the active alphabet(s)
+ * @inflow: (allow-none) (out): The inflow index, for any words which sort between the active alphabets (if 
there is more than one)
+ * @overflow: (allow-none) (out): The overflow index, for any words which sort above the active alphabet(s)
+ *
+ * Fetches the displayable labels and index positions for the active alphabet.
+ *
+ * Returns: (array zero-terminated=1) (element-type utf8) (transfer none):
+ *   The array of displayable labels for each index in the active alphabet(s).
+ *
+ * Since: 3.12
+ */
+const gchar *const  *
+e_collator_get_index_labels (ECollator       *collator,
+                            gint            *n_labels,
+                            gint            *underflow,
+                            gint            *inflow,
+                            gint            *overflow)
+{
+       g_return_val_if_fail (collator != NULL, NULL);
+
+       if (n_labels)
+               *n_labels = collator->n_labels;
+       if (underflow)
+               *underflow = collator->underflow;
+       if (inflow)
+               *inflow = collator->inflow;
+       if (overflow)
+               *overflow = collator->overflow;
+
+       return (const gchar *const  *)collator->labels;
+}
+
+/**
+ * e_collator_get_index:
+ * @collator: An #ECollator
+ * @str: A string
+ *
+ * Checks which index, as determined by e_collator_get_index_labels(),
+ * that @str should sort under.
+ *
+ * Returns: The alphabetic index under which @str would sort
+ *
+ * Since: 3.12
+ */
+gint
+e_collator_get_index (ECollator       *collator,
+                     const gchar     *str)
+{
+       gint index;
+       gchar *translit_str = NULL;
+       const gchar *input_str;
+
+       g_return_val_if_fail (collator != NULL, -1);
+       g_return_val_if_fail (str != NULL, -1);
+
+       /* We may need to perform a conversion before generating the sort key */
+       if (collator->transliterator) {
+               translit_str = _e_transliterator_cxx_transliterate (collator->transliterator, str);
+               input_str = translit_str;
+       } else {
+               input_str = str;
+       }
+
+       index = _e_alphabet_index_cxx_get_index (collator->alpha_index, input_str);
+
+       g_free (translit_str);
+
+       return index;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]