[evolution-data-server/gnome-3-10] Bug 710989 - Replace most uses of strcpy()
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/gnome-3-10] Bug 710989 - Replace most uses of strcpy()
- Date: Fri, 1 Nov 2013 14:41:57 +0000 (UTC)
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]