diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index 4090d9f..517538b 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -3175,26 +3175,98 @@ thunar_file_destroy (ThunarFile *file) static gint compare_by_name_using_number (const gchar *ap, - const gchar *bp) + const gchar *bp, + gint leadingzero) { - guint64 anum; - guint64 bnum; + const gchar *ai; + const gchar *bi; + guint ac; + guint bc; - /* determine the numbers in ap and bp */ - anum = strtouq (ap, NULL, 10); - bnum = strtouq (bp, NULL, 10); + /* up until now the numbers match. Now compare the numbers by digit + * count, the longest number is the largest. If the length are equal + * compare the digits. + */ - /* compare the numbers */ - if (anum < bnum) - return -1; - else if (anum > bnum) + /* determine the largest number */ + for (ai = ap, bi = bp;; ++ai, ++bi) + { + ac = *((const guchar *) ai); + bc = *((const guchar *) bi); + if (!g_ascii_isdigit (ac) || !g_ascii_isdigit (bc)) + break; + } + + /* if one of the numbers still has a digit, that number is the largest. */ + if (g_ascii_isdigit (ac)) return 1; + else if (g_ascii_isdigit (bc)) + return -1; + + /* if leading zeros were skipped *ap and *bp might equal. look for + * a digit that differs. + */ + for (;; ++ap, ++bp) + { + /* check if the characters differ or we have a non-digit char */ + ac = *((const guchar *) ap); + bc = *((const guchar *) bp); + if (ac != bc || !g_ascii_isdigit (ac)) + break; + } + + /* if leading zeros were skipped there might not be a difference in the + * remaining digits. use the leasingzero count instead. + */ + if (G_UNLIKELY (!g_ascii_isdigit (ac) || !g_ascii_isdigit (bc))) + return -leadingzero; + + /* the numbers are equal in length, and so the lower first digit should + * be sorted first. + */ + return (ac - bc); +} + + + +static gint +skip_leading_zeros (const gchar **ap, + const gchar *name) +{ + const gchar *bp; + gchar bc = '0'; + gint zerocount = 0; + + /* if **ap isn't '0' there are no zeros to skip */ + if (G_LIKELY (**ap != '0')) + return 0; - /* the numbers are equal, and so the higher first digit should - * be sorted first, i.e. 'file10' before 'file010', since we - * also sort 'file10' before 'file011'. + /* do a backward search to check if the number starts with a '0' + * start with *ap - 1 since **ap == '0' and doesn't need to be checked again */ - return (*bp - *ap); + for (bp = *ap - 1; bp >= name; --bp) + { + bc = *bp; + if (bc != '0') + break; + } + + /* if the number starts with a '0' skip all following '0' */ + if (!g_ascii_isdigit (bc) || bc == '0') + { + /* start with *ap + 1 since **ap == '0' and doesn't need to be checked */ + for (bp = *ap + 1;; ++bp) + { + ++zerocount; + if (*bp != '0') + break; + } + + /* move *ap to the first non '0' character */ + *ap = bp; + } + + return zerocount; } @@ -3295,17 +3367,23 @@ thunar_file_compare_by_name (const ThunarFile *file_a, /* if both strings differ in a digit, we use a smarter comparison * to get sorting 'file1', 'file5', 'file10' done the right way. */ - if (g_ascii_isdigit (ac) && g_ascii_isdigit (bc)) - return compare_by_name_using_number (ap, bp); - + if ((g_ascii_isdigit (ac) && g_ascii_isdigit (bc)) /* a second case is '20 file' and '2file', where comparison by number * makes sense, if the previous char for both strings is a digit. */ - if (ap > thunar_file_get_display_name (file_a) + || (ap > thunar_file_get_display_name (file_a) && bp > thunar_file_get_display_name (file_b) - && g_ascii_isdigit (*(ap - 1)) && g_ascii_isdigit (*(bp - 1))) + && g_ascii_isdigit (*(ap - 1)) && g_ascii_isdigit (*(bp - 1)))) { - return compare_by_name_using_number (ap - 1, bp - 1); + gint leadingzero; + gint skipped_a; + gint skipped_b; + /* skip any leading zeros before comparing the numbers */ + skipped_a = skip_leading_zeros(&ap, thunar_file_get_display_name (file_a)); + skipped_b = skip_leading_zeros(&bp, thunar_file_get_display_name (file_b)); + /* provide the difference in leading zeros to the number compare */ + leadingzero = skipped_a - skipped_b; + return compare_by_name_using_number (ap, bp, leadingzero); } } @@ -3337,8 +3415,8 @@ thunar_file_compare_by_name (const ThunarFile *file_a, /* transform the unicode chars to strings and * make sure the strings are nul-terminated. */ - abuf[g_unichar_to_utf8 (ac, abuf)] = '\0'; - bbuf[g_unichar_to_utf8 (bc, bbuf)] = '\0'; + abuf[g_unichar_to_utf8 (g_unichar_tolower(ac), abuf)] = '\0'; + bbuf[g_unichar_to_utf8 (g_unichar_tolower(bc), bbuf)] = '\0'; /* compare the unicode chars (as strings) */ return strcoll (abuf, bbuf);