1 /*
2 * Copyright (C) 2010, Nokia <ivan.frade@nokia.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include <glib.h>
22 #include <string.h>
23 #include <locale.h>
24
25 #include <libtracker-common/tracker-locale.h>
26 #include "tracker-collation.h"
27
28 /* If defined, will dump additional traces */
29 #ifdef ENABLE_TRACE
30 #define trace(message, ...) \
31 g_debug (message, ##__VA_ARGS__)
32 #else
33 #define trace(...)
34 #endif /* ENABLE_TRACE */
35
36 #ifdef HAVE_LIBUNISTRING
37 /* libunistring versions prior to 9.1.2 need this hack */
38 #define _UNUSED_PARAMETER_
39 #include <unistr.h>
40 #elif HAVE_LIBICU
41 #include <unicode/ucol.h>
42 #include <unicode/utypes.h>
43 #endif
44
45 /* If string lenth less than this value, allocating from the stack */
46 #define MAX_STACK_STR_SIZE 8192
47
48 #ifdef HAVE_LIBUNISTRING /* ---- GNU libunistring based collation ---- */
49
50 gpointer
51 tracker_collation_init (void)
52 {
53 gchar *locale;
54
55 /* Get locale! */
56 locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
57 g_debug ("[libunistring collation] Initializing collator for locale '%s'", locale);
58 g_free (locale);
59 /* Nothing to do */
60 return NULL;
61 }
62
63 void
64 tracker_collation_shutdown (gpointer collator)
65 {
66 /* Nothing to do */
67 }
68
69 gint
70 tracker_collation_utf8 (gpointer collator,
71 gint len1,
72 gconstpointer str1,
73 gint len2,
74 gconstpointer str2)
75 {
76 gint result;
77 gchar *aux1;
78 gchar *aux2;
79
80 /* Note: str1 and str2 are NOT NUL-terminated */
81 aux1 = (len1 < MAX_STACK_STR_SIZE) ? g_alloca (len1+1) : g_malloc (len1+1);
82 aux2 = (len2 < MAX_STACK_STR_SIZE) ? g_alloca (len2+1) : g_malloc (len2+1);
83
84 memcpy (aux1, str1, len1); aux1[len1] = '\0';
85 memcpy (aux2, str2, len2); aux2[len2] = '\0';
86
87 result = u8_strcoll (aux1, aux2);
pointer targets in passing argument 1 of 'u8_strcoll' differ in signedness
(emitted by gcc)
88
89 trace ("(libunistring) Collating '%s' and '%s' (%d)",
90 aux1, aux2, result);
91
92 if (len1 >= MAX_STACK_STR_SIZE)
93 g_free (aux1);
94 if (len2 >= MAX_STACK_STR_SIZE)
95 g_free (aux2);
96 return result;
97 }
98
99 #elif HAVE_LIBICU /* ---- ICU based collation (UTF-16) ----*/
100
101 gpointer
102 tracker_collation_init (void)
103 {
104 UCollator *collator = NULL;
105 UErrorCode status = U_ZERO_ERROR;
106 gchar *locale;
107
108 /* Get locale! */
109 locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
110
111 #ifdef HAVE_MEEGOTOUCH
112 /* libmeegotouch ships modified ICU data */
113 u_setDataDirectory ("/usr/share/meegotouch/icu");
114 #endif
115
116 g_debug ("[ICU collation] Initializing collator for locale '%s'", locale);
117 collator = ucol_open (locale, &status);
118 if (!collator) {
119 g_warning ("[ICU collation] Collator for locale '%s' cannot be created: %s",
120 locale, u_errorName (status));
121 /* Try to get UCA collator then... */
122 status = U_ZERO_ERROR;
123 collator = ucol_open ("root", &status);
124 if (!collator) {
125 g_critical ("[ICU collation] UCA Collator cannot be created: %s",
126 u_errorName (status));
127 }
128 }
129 g_free (locale);
130 return collator;
131 }
132
133 void
134 tracker_collation_shutdown (gpointer collator)
135 {
136 if (collator)
137 ucol_close ((UCollator *)collator);
138 }
139
140 gint
141 tracker_collation_utf8 (gpointer collator,
142 gint len1,
143 gconstpointer str1,
144 gint len2,
145 gconstpointer str2)
146 {
147 UErrorCode status = U_ZERO_ERROR;
148 UCharIterator iter1;
149 UCharIterator iter2;
150 UCollationResult result;
151
152 /* Collator must be created before trying to collate */
153 g_return_val_if_fail (collator, -1);
154
155 /* Setup iterators */
156 uiter_setUTF8 (&iter1, str1, len1);
157 uiter_setUTF8 (&iter2, str2, len2);
158
159 result = ucol_strcollIter ((UCollator *)collator,
160 &iter1,
161 &iter2,
162 &status);
163 if (status != U_ZERO_ERROR)
164 g_critical ("Error collating: %s", u_errorName (status));
165
166 #ifdef ENABLE_TRACE
167 {
168 gchar *aux1;
169 gchar *aux2;
170
171 /* Note: str1 and str2 are NOT NUL-terminated */
172 aux1 = (len1 < MAX_STACK_STR_SIZE) ? g_alloca (len1+1) : g_malloc (len1+1);
173 aux2 = (len2 < MAX_STACK_STR_SIZE) ? g_alloca (len2+1) : g_malloc (len2+1);
174
175 memcpy (aux1, str1, len1); aux1[len1] = '\0';
176 memcpy (aux2, str2, len2); aux2[len2] = '\0';
177
178 trace ("(ICU) Collating '%s' and '%s' (%d)",
179 aux1, aux2, result);
180
181 if (len1 >= MAX_STACK_STR_SIZE)
182 g_free (aux1);
183 if (len2 >= MAX_STACK_STR_SIZE)
184 g_free (aux2);
185 }
186 #endif /* ENABLE_TRACE */
187
188 if (result == UCOL_GREATER)
189 return 1;
190 if (result == UCOL_LESS)
191 return -1;
192 return 0;
193 }
194
195 #else /* ---- GLib based collation ---- */
196
197 gpointer
198 tracker_collation_init (void)
199 {
200 gchar *locale;
201
202 /* Get locale! */
203 locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
204 g_debug ("[GLib collation] Initializing collator for locale '%s'", locale);
205 g_free (locale);
206 /* Nothing to do */
207 return NULL;
208 }
209
210 void
211 tracker_collation_shutdown (gpointer collator)
212 {
213 /* Nothing to do */
214 }
215
216 gint
217 tracker_collation_utf8 (gpointer collator,
218 gint len1,
219 gconstpointer str1,
220 gint len2,
221 gconstpointer str2)
222 {
223 gint result;
224 gchar *aux1;
225 gchar *aux2;
226
227 /* Note: str1 and str2 are NOT NUL-terminated */
228 aux1 = (len1 < MAX_STACK_STR_SIZE) ? g_alloca (len1+1) : g_malloc (len1+1);
229 aux2 = (len2 < MAX_STACK_STR_SIZE) ? g_alloca (len2+1) : g_malloc (len2+1);
230
231 memcpy (aux1, str1, len1); aux1[len1] = '\0';
232 memcpy (aux2, str2, len2); aux2[len2] = '\0';
233
234 result = g_utf8_collate (aux1, aux2);
235
236 trace ("(GLib) Collating '%s' and '%s' (%d)",
237 aux1, aux2, result);
238
239 if (len1 >= MAX_STACK_STR_SIZE)
240 g_free (aux1);
241 if (len2 >= MAX_STACK_STR_SIZE)
242 g_free (aux2);
243 return result;
244 }
245
246 #endif