Branch data Line data Source code
1 : : /* gunicollate.c - Collation
2 : : *
3 : : * Copyright 2001,2005 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public License
18 : : * along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include "config.h"
22 : :
23 : : #include <locale.h>
24 : : #include <string.h>
25 : : #ifdef HAVE_WCHAR_H
26 : : #include <wchar.h>
27 : : #endif
28 : :
29 : : #ifdef HAVE_CARBON
30 : : #include <CoreServices/CoreServices.h>
31 : : #endif
32 : :
33 : : #include "gmem.h"
34 : : #include "gunicode.h"
35 : : #include "gunicodeprivate.h"
36 : : #include "gstring.h"
37 : : #include "gstrfuncs.h"
38 : : #include "gtestutils.h"
39 : : #include "gcharset.h"
40 : : #include "gconvert.h"
41 : :
42 : : #if SIZEOF_WCHAR_T == 4 && defined(__STDC_ISO_10646__)
43 : : #define GUNICHAR_EQUALS_WCHAR_T 1
44 : : #endif
45 : :
46 : : #ifdef _MSC_VER
47 : : /* Workaround for bug in MSVCR80.DLL */
48 : : static gsize
49 : : msc_strxfrm_wrapper (char *string1,
50 : : const char *string2,
51 : : gsize count)
52 : : {
53 : : if (!string1 || count <= 0)
54 : : {
55 : : char tmp;
56 : :
57 : : return strxfrm (&tmp, string2, 1);
58 : : }
59 : : return strxfrm (string1, string2, count);
60 : : }
61 : : #define strxfrm msc_strxfrm_wrapper
62 : : #endif
63 : :
64 : : /**
65 : : * g_utf8_collate:
66 : : * @str1: a UTF-8 encoded string
67 : : * @str2: a UTF-8 encoded string
68 : : *
69 : : * Compares two strings for ordering using the linguistically
70 : : * correct rules for the [current locale](running.html#locale).
71 : : * When sorting a large number of strings, it will be significantly
72 : : * faster to obtain collation keys with g_utf8_collate_key() and
73 : : * compare the keys with strcmp() when sorting instead of sorting
74 : : * the original strings.
75 : : *
76 : : * If the two strings are not comparable due to being in different collation
77 : : * sequences, the result is undefined. This can happen if the strings are in
78 : : * different language scripts, for example.
79 : : *
80 : : * Returns: < 0 if @str1 compares before @str2,
81 : : * 0 if they compare equal, > 0 if @str1 compares after @str2.
82 : : **/
83 : : gint
84 : 89 : g_utf8_collate (const gchar *str1,
85 : : const gchar *str2)
86 : : {
87 : : gint result;
88 : :
89 : : #ifdef HAVE_CARBON
90 : :
91 : : UniChar *str1_utf16;
92 : : UniChar *str2_utf16;
93 : : glong len1;
94 : : glong len2;
95 : : SInt32 retval = 0;
96 : :
97 : : g_return_val_if_fail (str1 != NULL, 0);
98 : : g_return_val_if_fail (str2 != NULL, 0);
99 : :
100 : : str1_utf16 = g_utf8_to_utf16 (str1, -1, NULL, &len1, NULL);
101 : : str2_utf16 = g_utf8_to_utf16 (str2, -1, NULL, &len2, NULL);
102 : :
103 : : UCCompareTextDefault (kUCCollateStandardOptions,
104 : : str1_utf16, len1, str2_utf16, len2,
105 : : NULL, &retval);
106 : : result = retval;
107 : :
108 : : g_free (str2_utf16);
109 : : g_free (str1_utf16);
110 : :
111 : : #elif defined(HAVE_WCHAR_H) && defined(GUNICHAR_EQUALS_WCHAR_T)
112 : :
113 : : gunichar *str1_norm;
114 : : gunichar *str2_norm;
115 : :
116 : 89 : g_return_val_if_fail (str1 != NULL, 0);
117 : 89 : g_return_val_if_fail (str2 != NULL, 0);
118 : :
119 : 89 : str1_norm = _g_utf8_normalize_wc (str1, -1, G_NORMALIZE_ALL_COMPOSE);
120 : 89 : str2_norm = _g_utf8_normalize_wc (str2, -1, G_NORMALIZE_ALL_COMPOSE);
121 : :
122 : 89 : result = wcscoll ((wchar_t *)str1_norm, (wchar_t *)str2_norm);
123 : :
124 : 89 : g_free (str1_norm);
125 : 89 : g_free (str2_norm);
126 : :
127 : : #else
128 : :
129 : : const gchar *charset;
130 : : gchar *str1_norm;
131 : : gchar *str2_norm;
132 : :
133 : : g_return_val_if_fail (str1 != NULL, 0);
134 : : g_return_val_if_fail (str2 != NULL, 0);
135 : :
136 : : str1_norm = g_utf8_normalize (str1, -1, G_NORMALIZE_ALL_COMPOSE);
137 : : str2_norm = g_utf8_normalize (str2, -1, G_NORMALIZE_ALL_COMPOSE);
138 : :
139 : : if (g_get_charset (&charset))
140 : : {
141 : : result = strcoll (str1_norm, str2_norm);
142 : : }
143 : : else
144 : : {
145 : : gchar *str1_locale = g_convert (str1_norm, -1, charset, "UTF-8", NULL, NULL, NULL);
146 : : gchar *str2_locale = g_convert (str2_norm, -1, charset, "UTF-8", NULL, NULL, NULL);
147 : :
148 : : if (str1_locale && str2_locale)
149 : : result = strcoll (str1_locale, str2_locale);
150 : : else if (str1_locale)
151 : : result = -1;
152 : : else if (str2_locale)
153 : : result = 1;
154 : : else
155 : : result = strcmp (str1_norm, str2_norm);
156 : :
157 : : g_free (str1_locale);
158 : : g_free (str2_locale);
159 : : }
160 : :
161 : : g_free (str1_norm);
162 : : g_free (str2_norm);
163 : :
164 : : #endif
165 : :
166 : 89 : return result;
167 : : }
168 : :
169 : : #if defined(HAVE_WCHAR_H) && defined(GUNICHAR_EQUALS_WCHAR_T)
170 : : /* We need UTF-8 encoding of numbers to encode the weights if
171 : : * we are using wcsxfrm. However, we aren't encoding Unicode
172 : : * characters, so we can't simply use g_unichar_to_utf8.
173 : : *
174 : : * The following routine is taken (with modification) from GNU
175 : : * libc's strxfrm routine:
176 : : *
177 : : * Copyright (C) 1995-1999,2000,2001 Free Software Foundation, Inc.
178 : : * Written by Ulrich Drepper <drepper@cygnus.com>, 1995.
179 : : */
180 : : static inline int
181 : 9072 : utf8_encode (char *buf, wchar_t val)
182 : : {
183 : : int retval;
184 : :
185 : 9072 : if (val < 0x80)
186 : : {
187 : 7956 : if (buf)
188 : 3978 : *buf++ = (char) val;
189 : 7956 : retval = 1;
190 : : }
191 : : else
192 : : {
193 : : int step;
194 : :
195 : 1152 : for (step = 2; step < 6; ++step)
196 : 1152 : if ((val & (~(guint32)0 << (5 * step + 1))) == 0)
197 : 1116 : break;
198 : 1116 : retval = step;
199 : :
200 : 1116 : if (buf)
201 : : {
202 : 558 : *buf = (unsigned char) (~0xff >> step);
203 : 558 : --step;
204 : : do
205 : : {
206 : 576 : buf[step] = 0x80 | (val & 0x3f);
207 : 576 : val >>= 6;
208 : : }
209 : 576 : while (--step > 0);
210 : 558 : *buf |= val;
211 : : }
212 : : }
213 : :
214 : 9072 : return retval;
215 : : }
216 : : #endif
217 : :
218 : : #ifdef HAVE_CARBON
219 : :
220 : : static gchar *
221 : : collate_key_to_string (UCCollationValue *key,
222 : : gsize key_len)
223 : : {
224 : : gchar *result;
225 : : gsize result_len;
226 : : long *lkey = (long *) key;
227 : :
228 : : /* UCCollationValue format:
229 : : *
230 : : * UCCollateOptions (32/64 bits)
231 : : * SizeInBytes (32/64 bits)
232 : : * Value (8 bits array)
233 : : *
234 : : * UCCollateOptions: ordering option mask of the collator
235 : : * used to create the key. Size changes on 32-bit / 64-bit
236 : : * hosts. On 64-bits also the extra half-word seems to have
237 : : * some extra (unknown) meaning.
238 : : * SizeInBytes: size of the whole structure, in bytes
239 : : * (including UCCollateOptions and SizeInBytes fields). Size
240 : : * changes on 32-bit & 64-bit hosts.
241 : : * Value: array of bytes containing the comparison weights.
242 : : * Seems to have several sub-strings separated by \001 and \002
243 : : * chars. Also, experience shows this is directly strcmp-able.
244 : : */
245 : :
246 : : result_len = lkey[1];
247 : : result = g_malloc (result_len + 1);
248 : : memcpy (result, &lkey[2], result_len);
249 : : result[result_len] = '\0';
250 : :
251 : : return result;
252 : : }
253 : :
254 : : static gchar *
255 : : carbon_collate_key_with_collator (const gchar *str,
256 : : gssize len,
257 : : CollatorRef collator)
258 : : {
259 : : UniChar *str_utf16 = NULL;
260 : : glong len_utf16;
261 : : OSStatus ret;
262 : : UCCollationValue staticbuf[512];
263 : : UCCollationValue *freeme = NULL;
264 : : UCCollationValue *buf;
265 : : ItemCount buf_len;
266 : : ItemCount key_len;
267 : : ItemCount try_len;
268 : : gchar *result = NULL;
269 : :
270 : : str_utf16 = g_utf8_to_utf16 (str, len, NULL, &len_utf16, NULL);
271 : : try_len = len_utf16 * 5 + 2;
272 : :
273 : : if (try_len <= sizeof staticbuf)
274 : : {
275 : : buf = staticbuf;
276 : : buf_len = sizeof staticbuf;
277 : : }
278 : : else
279 : : {
280 : : freeme = g_new (UCCollationValue, try_len);
281 : : buf = freeme;
282 : : buf_len = try_len;
283 : : }
284 : :
285 : : ret = UCGetCollationKey (collator, str_utf16, len_utf16,
286 : : buf_len, &key_len, buf);
287 : :
288 : : if (ret == kCollateBufferTooSmall)
289 : : {
290 : : freeme = g_renew (UCCollationValue, freeme, try_len * 2);
291 : : buf = freeme;
292 : : buf_len = try_len * 2;
293 : : ret = UCGetCollationKey (collator, str_utf16, len_utf16,
294 : : buf_len, &key_len, buf);
295 : : }
296 : :
297 : : if (ret == 0)
298 : : result = collate_key_to_string (buf, key_len);
299 : : else
300 : : result = g_strdup ("");
301 : :
302 : : g_free (freeme);
303 : : g_free (str_utf16);
304 : : return result;
305 : : }
306 : :
307 : : static gchar *
308 : : carbon_collate_key (const gchar *str,
309 : : gssize len)
310 : : {
311 : : static CollatorRef collator;
312 : :
313 : : if (G_UNLIKELY (!collator))
314 : : {
315 : : UCCreateCollator (NULL, 0, kUCCollateStandardOptions, &collator);
316 : :
317 : : if (!collator)
318 : : {
319 : : static gboolean been_here;
320 : : if (!been_here)
321 : : g_warning ("%s: UCCreateCollator failed", G_STRLOC);
322 : : been_here = TRUE;
323 : : return g_strdup ("");
324 : : }
325 : : }
326 : :
327 : : return carbon_collate_key_with_collator (str, len, collator);
328 : : }
329 : :
330 : : static gchar *
331 : : carbon_collate_key_for_filename (const gchar *str,
332 : : gssize len)
333 : : {
334 : : static CollatorRef collator;
335 : :
336 : : if (G_UNLIKELY (!collator))
337 : : {
338 : : /* http://developer.apple.com/qa/qa2004/qa1159.html */
339 : : UCCreateCollator (NULL, 0,
340 : : kUCCollateComposeInsensitiveMask
341 : : | kUCCollateWidthInsensitiveMask
342 : : | kUCCollateCaseInsensitiveMask
343 : : | kUCCollateDigitsOverrideMask
344 : : | kUCCollateDigitsAsNumberMask
345 : : | kUCCollatePunctuationSignificantMask,
346 : : &collator);
347 : :
348 : : if (!collator)
349 : : {
350 : : static gboolean been_here;
351 : : if (!been_here)
352 : : g_warning ("%s: UCCreateCollator failed", G_STRLOC);
353 : : been_here = TRUE;
354 : : return g_strdup ("");
355 : : }
356 : : }
357 : :
358 : : return carbon_collate_key_with_collator (str, len, collator);
359 : : }
360 : :
361 : : #endif /* HAVE_CARBON */
362 : :
363 : : /**
364 : : * g_utf8_collate_key:
365 : : * @str: a UTF-8 encoded string.
366 : : * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
367 : : *
368 : : * Converts a string into a collation key that can be compared
369 : : * with other collation keys produced by the same function using
370 : : * strcmp().
371 : : *
372 : : * The results of comparing the collation keys of two strings
373 : : * with strcmp() will always be the same as comparing the two
374 : : * original keys with g_utf8_collate().
375 : : *
376 : : * Note that this function depends on the [current locale](running.html#locale).
377 : : *
378 : : * Note that the returned string is not guaranteed to be in any
379 : : * encoding, especially UTF-8. The returned value is meant to be
380 : : * used only for comparisons.
381 : : *
382 : : * Returns: (transfer full) (type filename): a newly allocated string.
383 : : * The contents of the string are only meant to be used when sorting.
384 : : * This string should be freed with g_free() when you are done with it.
385 : : **/
386 : : gchar *
387 : 299 : g_utf8_collate_key (const gchar *str,
388 : : gssize len)
389 : : {
390 : : gchar *result;
391 : :
392 : : #ifdef HAVE_CARBON
393 : :
394 : : g_return_val_if_fail (str != NULL, NULL);
395 : : result = carbon_collate_key (str, len);
396 : :
397 : : #elif defined(HAVE_WCHAR_H) && defined(GUNICHAR_EQUALS_WCHAR_T)
398 : :
399 : : gsize xfrm_len;
400 : : gunichar *str_norm;
401 : : wchar_t *result_wc;
402 : : gsize i;
403 : 299 : gsize result_len = 0;
404 : :
405 : 299 : g_return_val_if_fail (str != NULL, NULL);
406 : :
407 : 299 : str_norm = _g_utf8_normalize_wc (str, len, G_NORMALIZE_ALL_COMPOSE);
408 : :
409 : 299 : g_return_val_if_fail (str_norm != NULL, NULL);
410 : :
411 : 299 : xfrm_len = wcsxfrm (NULL, (wchar_t *)str_norm, 0);
412 : 299 : result_wc = g_new (wchar_t, xfrm_len + 1);
413 : 299 : wcsxfrm (result_wc, (wchar_t *)str_norm, xfrm_len + 1);
414 : :
415 : 4835 : for (i = 0; i < xfrm_len; i++)
416 : 4536 : result_len += utf8_encode (NULL, result_wc[i]);
417 : :
418 : 299 : result = g_malloc (result_len + 1);
419 : 299 : result_len = 0;
420 : 4835 : for (i = 0; i < xfrm_len; i++)
421 : 4536 : result_len += utf8_encode (result + result_len, result_wc[i]);
422 : :
423 : 299 : result[result_len] = '\0';
424 : :
425 : 299 : g_free (result_wc);
426 : 299 : g_free (str_norm);
427 : :
428 : 299 : return result;
429 : : #else
430 : :
431 : : gsize xfrm_len = 0;
432 : : const gchar *charset;
433 : : gchar *str_norm;
434 : :
435 : : g_return_val_if_fail (str != NULL, NULL);
436 : :
437 : : str_norm = g_utf8_normalize (str, len, G_NORMALIZE_ALL_COMPOSE);
438 : :
439 : : result = NULL;
440 : :
441 : : if (g_get_charset (&charset))
442 : : {
443 : : xfrm_len = strxfrm (NULL, str_norm, 0);
444 : : if (xfrm_len < G_MAXINT - 2)
445 : : {
446 : : result = g_malloc (xfrm_len + 1);
447 : : strxfrm (result, str_norm, xfrm_len + 1);
448 : : }
449 : : }
450 : : else
451 : : {
452 : : gchar *str_locale = g_convert (str_norm, -1, charset, "UTF-8", NULL, NULL, NULL);
453 : :
454 : : if (str_locale)
455 : : {
456 : : xfrm_len = strxfrm (NULL, str_locale, 0);
457 : : if (xfrm_len >= G_MAXINT - 2)
458 : : {
459 : : g_free (str_locale);
460 : : str_locale = NULL;
461 : : }
462 : : }
463 : : if (str_locale)
464 : : {
465 : : result = g_malloc (xfrm_len + 2);
466 : : result[0] = 'A';
467 : : strxfrm (result + 1, str_locale, xfrm_len + 1);
468 : :
469 : : g_free (str_locale);
470 : : }
471 : : }
472 : :
473 : : if (!result)
474 : : {
475 : : xfrm_len = strlen (str_norm);
476 : : result = g_malloc (xfrm_len + 2);
477 : : result[0] = 'B';
478 : : memcpy (result + 1, str_norm, xfrm_len);
479 : : result[xfrm_len+1] = '\0';
480 : : }
481 : :
482 : : g_free (str_norm);
483 : : #endif
484 : :
485 : : return result;
486 : : }
487 : :
488 : : /* This is a collation key that is very very likely to sort before any
489 : : * collation key that libc strxfrm generates. We use this before any
490 : : * special case (dot or number) to make sure that its sorted before
491 : : * anything else.
492 : : */
493 : : #define COLLATION_SENTINEL "\1\1\1"
494 : :
495 : : /**
496 : : * g_utf8_collate_key_for_filename:
497 : : * @str: a UTF-8 encoded string.
498 : : * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
499 : : *
500 : : * Converts a string into a collation key that can be compared
501 : : * with other collation keys produced by the same function using strcmp().
502 : : *
503 : : * In order to sort filenames correctly, this function treats the dot '.'
504 : : * as a special case. Most dictionary orderings seem to consider it
505 : : * insignificant, thus producing the ordering "event.c" "eventgenerator.c"
506 : : * "event.h" instead of "event.c" "event.h" "eventgenerator.c". Also, we
507 : : * would like to treat numbers intelligently so that "file1" "file10" "file5"
508 : : * is sorted as "file1" "file5" "file10".
509 : : *
510 : : * Note that this function depends on the [current locale](running.html#locale).
511 : : *
512 : : * Note that the returned string is not guaranteed to be in any
513 : : * encoding, especially UTF-8. The returned value is meant to be
514 : : * used only for comparisons.
515 : : *
516 : : * Returns: (transfer full) (type filename): a newly allocated string.
517 : : * The contents of the string are only meant to be used when sorting.
518 : : * This string should be freed with g_free() when you are done with it.
519 : : *
520 : : * Since: 2.8
521 : : */
522 : : gchar *
523 : 36 : g_utf8_collate_key_for_filename (const gchar *str,
524 : : gssize len)
525 : : {
526 : : #ifndef HAVE_CARBON
527 : : GString *result;
528 : : GString *append;
529 : : const gchar *p;
530 : : const gchar *prev;
531 : : const gchar *end;
532 : : gchar *collate_key;
533 : : gint digits;
534 : : gint leading_zeros;
535 : :
536 : : /*
537 : : * How it works:
538 : : *
539 : : * Split the filename into collatable substrings which do
540 : : * not contain [.0-9] and special-cased substrings. The collatable
541 : : * substrings are run through the normal g_utf8_collate_key() and the
542 : : * resulting keys are concatenated with keys generated from the
543 : : * special-cased substrings.
544 : : *
545 : : * Special cases: Dots are handled by replacing them with '\1' which
546 : : * implies that short dot-delimited substrings are before long ones,
547 : : * e.g.
548 : : *
549 : : * a\1a (a.a)
550 : : * a-\1a (a-.a)
551 : : * aa\1a (aa.a)
552 : : *
553 : : * Numbers are handled by prepending to each number d-1 superdigits
554 : : * where d = number of digits in the number and SUPERDIGIT is a
555 : : * character with an integer value higher than any digit (for instance
556 : : * ':'). This ensures that single-digit numbers are sorted before
557 : : * double-digit numbers which in turn are sorted separately from
558 : : * triple-digit numbers, etc. To avoid strange side-effects when
559 : : * sorting strings that already contain SUPERDIGITs, a '\2'
560 : : * is also prepended, like this
561 : : *
562 : : * file\21 (file1)
563 : : * file\25 (file5)
564 : : * file\2:10 (file10)
565 : : * file\2:26 (file26)
566 : : * file\2::100 (file100)
567 : : * file:foo (file:foo)
568 : : *
569 : : * This has the side-effect of sorting numbers before everything else (except
570 : : * dots), but this is probably OK.
571 : : *
572 : : * Leading digits are ignored when doing the above. To discriminate
573 : : * numbers which differ only in the number of leading digits, we append
574 : : * the number of leading digits as a byte at the very end of the collation
575 : : * key.
576 : : *
577 : : * To try avoid conflict with any collation key sequence generated by libc we
578 : : * start each switch to a special cased part with a sentinel that hopefully
579 : : * will sort before anything libc will generate.
580 : : */
581 : :
582 : 36 : if (len < 0)
583 : 36 : len = strlen (str);
584 : :
585 : 36 : result = g_string_sized_new (len * 2);
586 : 36 : append = g_string_sized_new (0);
587 : :
588 : 36 : end = str + len;
589 : :
590 : : /* No need to use utf8 functions, since we're only looking for ascii chars */
591 : 216 : for (prev = p = str; p < end; p++)
592 : : {
593 : 180 : switch (*p)
594 : : {
595 : 10 : case '.':
596 : 10 : if (prev != p)
597 : : {
598 : 8 : collate_key = g_utf8_collate_key (prev, p - prev);
599 : : g_string_append (result, collate_key);
600 : 8 : g_free (collate_key);
601 : : }
602 : :
603 : 10 : g_string_append (result, COLLATION_SENTINEL "\1");
604 : :
605 : : /* skip the dot */
606 : 10 : prev = p + 1;
607 : 10 : break;
608 : :
609 : 20 : case '0':
610 : : case '1':
611 : : case '2':
612 : : case '3':
613 : : case '4':
614 : : case '5':
615 : : case '6':
616 : : case '7':
617 : : case '8':
618 : : case '9':
619 : 20 : if (prev != p)
620 : : {
621 : 19 : collate_key = g_utf8_collate_key (prev, p - prev);
622 : : g_string_append (result, collate_key);
623 : 19 : g_free (collate_key);
624 : : }
625 : :
626 : 20 : g_string_append (result, COLLATION_SENTINEL "\2");
627 : :
628 : 20 : prev = p;
629 : :
630 : : /* write d-1 colons */
631 : 20 : if (*p == '0')
632 : : {
633 : 7 : leading_zeros = 1;
634 : 7 : digits = 0;
635 : : }
636 : : else
637 : : {
638 : 13 : leading_zeros = 0;
639 : 13 : digits = 1;
640 : : }
641 : :
642 : 44 : while (++p < end)
643 : : {
644 : 27 : if (*p == '0' && !digits)
645 : 7 : ++leading_zeros;
646 : 20 : else if (g_ascii_isdigit(*p))
647 : 17 : ++digits;
648 : : else
649 : : {
650 : : /* count an all-zero sequence as
651 : : * one digit plus leading zeros
652 : : */
653 : 3 : if (!digits)
654 : : {
655 : 1 : ++digits;
656 : 1 : --leading_zeros;
657 : : }
658 : 3 : break;
659 : : }
660 : : }
661 : :
662 : 32 : while (digits > 1)
663 : : {
664 : : g_string_append_c (result, ':');
665 : 12 : --digits;
666 : : }
667 : :
668 : 20 : if (leading_zeros > 0)
669 : : {
670 : 7 : g_string_append_c (append, (char)leading_zeros);
671 : 7 : prev += leading_zeros;
672 : : }
673 : :
674 : : /* write the number itself */
675 : 20 : g_string_append_len (result, prev, p - prev);
676 : :
677 : 20 : prev = p;
678 : 20 : --p; /* go one step back to avoid disturbing outer loop */
679 : 20 : break;
680 : :
681 : 150 : default:
682 : : /* other characters just accumulate */
683 : 150 : break;
684 : : }
685 : : }
686 : :
687 : 36 : if (prev != p)
688 : : {
689 : 19 : collate_key = g_utf8_collate_key (prev, p - prev);
690 : : g_string_append (result, collate_key);
691 : 19 : g_free (collate_key);
692 : : }
693 : :
694 : 36 : g_string_append (result, append->str);
695 : 36 : g_string_free (append, TRUE);
696 : :
697 : 36 : return g_string_free (result, FALSE);
698 : : #else /* HAVE_CARBON */
699 : : return carbon_collate_key_for_filename (str, len);
700 : : #endif
701 : : }
|