Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (Revision 19773) +++ apps/lang/english.lang (Arbeitskopie) @@ -12359,3 +12359,17 @@ *: "Credits" + + id: LANG_SORT_HUMAN + desc: browser sorting setting + user: + + *: "Human alphabetical" + + + *: "Human alphabetical" + + + *: "Human alphabetical" + + Index: apps/settings.h =================================================================== --- apps/settings.h (Revision 19773) +++ apps/settings.h (Arbeitskopie) @@ -145,8 +145,8 @@ SHOW_WPS, SHOW_RWPS, SHOW_FMR, SHOW_CFG, SHOW_LNG, SHOW_MOD, SHOW_FONT, SHOW_PLUGINS}; /* file and dir sort options */ -enum { SORT_ALPHA, SORT_DATE, SORT_DATE_REVERSED, SORT_TYPE, /* available as settings */ - SORT_ALPHA_REVERSED, SORT_TYPE_REVERSED }; /* internal use only */ +enum { SORT_HUMAN, SORT_ALPHA, SORT_DATE, SORT_DATE_REVERSED, SORT_TYPE, /* available as settings */ + SORT_HUMAN_REVERSED, SORT_ALPHA_REVERSED, SORT_TYPE_REVERSED }; /* internal use only */ /* recursive dir insert options */ enum { RECURSE_OFF, RECURSE_ON, RECURSE_ASK }; Index: apps/settings_list.c =================================================================== --- apps/settings_list.c (Revision 19773) +++ apps/settings_list.c (Arbeitskopie) @@ -784,7 +784,6 @@ "all,supported,music,playlists", NULL, 4, ID2P(LANG_ALL), ID2P(LANG_FILTER_SUPPORTED), ID2P(LANG_FILTER_MUSIC), ID2P(LANG_PLAYLISTS)), - OFFON_SETTING(0, sort_case, LANG_SORT_CASE, false, "sort case", NULL), CHOICE_SETTING(0, show_filename_ext, LANG_SHOW_FILENAME_EXT, 3, "show filename exts", "off,on,unknown,view_all", NULL , 4 , ID2P(LANG_OFF), ID2P(LANG_ON), ID2P(LANG_UNKNOWN_TYPES), @@ -895,12 +894,15 @@ "Announce Battery Level", NULL), /* file sorting */ - CHOICE_SETTING(0, sort_file, LANG_SORT_FILE, 0 , - "sort files", "alpha,oldest,newest,type", NULL, 4, + OFFON_SETTING(0, sort_case, LANG_SORT_CASE, false, "sort case", NULL), + CHOICE_SETTING(0, sort_file, LANG_SORT_FILE, SORT_HUMAN , + "sort files", "human,alpha,oldest,newest,type", NULL, 4, + ID2P(LANG_SORT_HUMAN), ID2P(LANG_SORT_ALPHA), ID2P(LANG_SORT_DATE), ID2P(LANG_SORT_DATE_REVERSE) , ID2P(LANG_SORT_TYPE)), - CHOICE_SETTING(0, sort_dir, LANG_SORT_DIR, 0 , - "sort dirs", "alpha,oldest,newest", NULL, 3, + CHOICE_SETTING(0, sort_dir, LANG_SORT_DIR, SORT_HUMAN , + "sort dirs", "human,alpha,oldest,newest", NULL, 3, + ID2P(LANG_SORT_HUMAN), ID2P(LANG_SORT_ALPHA), ID2P(LANG_SORT_DATE), ID2P(LANG_SORT_DATE_REVERSE)), Index: apps/filetree.c =================================================================== --- apps/filetree.c (Revision 19773) +++ apps/filetree.c (Arbeitskopie) @@ -28,6 +28,7 @@ #include #include #include "bookmark.h" +#include #include "tree.h" #include "settings.h" #include "filetypes.h" @@ -181,7 +182,27 @@ } closedir(dir); } - +static int alphabetic_sort(struct entry* e1, struct entry* e2, int criteria) +{ + if (criteria == SORT_ALPHA || criteria == SORT_ALPHA_REVERSED) + { + if (global_settings.sort_case) + return strncmp(e1->name, e2->name, MAX_PATH) + * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); + else + return strncasecmp(e1->name, e2->name, MAX_PATH) + * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); + } + else /* else if SORT_HUMAN, and as fall back for other criterias */ + { + if (global_settings.sort_case) + return strnatcmp(e1->name, e2->name) + * (criteria == SORT_HUMAN_REVERSED ? -1 : 1); + else + return strnatcasecmp(e1->name, e2->name) + * (criteria == SORT_HUMAN_REVERSED ? -1 : 1); + } +} /* support function for qsort() */ static int compare(const void* p1, const void* p2) { @@ -197,7 +218,9 @@ if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME) { /* a volume identifier is involved */ if (e1->attr & ATTR_VOLUME && e2->attr & ATTR_VOLUME) - criteria = SORT_ALPHA; /* two volumes: sort alphabetically */ + { + return alphabetic_sort(e1, e2, criteria); + } else /* only one is a volume: volume first */ return (e2->attr & ATTR_VOLUME) - (e1->attr & ATTR_VOLUME); } @@ -226,29 +249,23 @@ if (t1 != t2) /* if different */ return (t1 - t2) * (criteria == SORT_TYPE_REVERSED ? -1 : 1); - /* else fall through to alphabetical sorting */ + /* else fall back to alphabetical sort */ + return alphabetic_sort(e1, e2, SORT_HUMAN); } case SORT_DATE: case SORT_DATE_REVERSED: - /* Ignore SORT_TYPE */ - if (criteria == SORT_DATE || criteria == SORT_DATE_REVERSED) - { - if (e1->time_write != e2->time_write) - return (e1->time_write - e2->time_write) - * (criteria == SORT_DATE_REVERSED ? -1 : 1); - /* else fall through to alphabetical sorting */ - } + if (e1->time_write != e2->time_write) + return (e1->time_write - e2->time_write) + * (criteria == SORT_DATE_REVERSED ? -1 : 1); + /* else fall back to alphabetical sort */ + return alphabetic_sort(e1, e2, SORT_HUMAN); + case SORT_HUMAN: + case SORT_HUMAN_REVERSED: case SORT_ALPHA: case SORT_ALPHA_REVERSED: - if (global_settings.sort_case) - return strncmp(e1->name, e2->name, MAX_PATH) - * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); - else - return strncasecmp(e1->name, e2->name, MAX_PATH) - * (criteria == SORT_ALPHA_REVERSED ? -1 : 1); - + return alphabetic_sort(e1, e2, criteria); } return 0; /* never reached */ } Index: firmware/include/strnatcmp.h =================================================================== --- firmware/include/strnatcmp.h (Revision 0) +++ firmware/include/strnatcmp.h (Revision 0) @@ -0,0 +1,30 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +/* CUSTOMIZATION SECTION + * + * You can change this typedef, but must then also change the inline + * functions in strnatcmp.c */ + +int strnatcmp(const char *a, const char *b); +int strnatcasecmp(const char *a, const char *b); Index: firmware/SOURCES =================================================================== --- firmware/SOURCES (Revision 19773) +++ firmware/SOURCES (Arbeitskopie) @@ -48,6 +48,7 @@ common/strcat.c common/strchr.c common/strcmp.c +common/strnatcmp.c common/strcpy.c common/strncmp.c common/strncpy.c Index: firmware/common/strnatcmp.c =================================================================== --- firmware/common/strnatcmp.c (Revision 0) +++ firmware/common/strnatcmp.c (Revision 0) @@ -0,0 +1,179 @@ +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +/* partial change history: + * + * 2004-10-10 mbp: Lift out character type dependencies into macros. + * + * Eric Sosman pointed out that ctype functions take a parameter whose + * value must be that of an unsigned int, even on platforms that have + * negative chars in their default char type. + */ + +#include +#include +#include + +#include "strnatcmp.h" + +#define assert(x) /* nothing */ + + +/* These are defined as macros to make it easier to adapt this code to + * different characters types or comparison functions. */ +static inline int +nat_isdigit(char a) +{ + return isdigit((unsigned char) a); +} + + +static inline int +nat_isspace(char a) +{ + return a == '0' || isspace((unsigned char) a); +} + + +static inline char +nat_toupper(char a) +{ + return toupper((unsigned char) a); +} + + + +static int +compare_right(char const *a, char const *b) +{ + int bias = 0; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + for (;; a++, b++) { + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return bias; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) { + if (!bias) + bias = -1; + } else if (*a > *b) { + if (!bias) + bias = +1; + } else if (!*a && !*b) + return bias; + } + + return 0; +} + + +static int +compare_left(char const *a, char const *b) +{ + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++) { + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return 0; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) + return -1; + else if (*a > *b) + return +1; + } + + return 0; +} + + +static int strnatcmp0(char const *a, char const *b, int fold_case) +{ + int ai, bi; + char ca, cb; + int fractional, result; + + assert(a && b); + ai = bi = 0; + while (1) { + ca = a[ai]; cb = b[bi]; + + /* skip over leading spaces or zeros */ + while (nat_isspace(ca)) + ca = a[++ai]; + + while (nat_isspace(cb)) + cb = b[++bi]; + + /* process run of digits */ + if (nat_isdigit(ca) && nat_isdigit(cb)) { + fractional = (ca == '0' || cb == '0'); + + if (fractional) { + if ((result = compare_left(a+ai, b+bi)) != 0) + return result; + } else { + if ((result = compare_right(a+ai, b+bi)) != 0) + return result; + } + } + + if (!ca && !cb) { + /* The strings compare the same. Perhaps the caller + will want to call strcmp to break the tie. */ + return 0; + } + + if (fold_case) { + ca = nat_toupper(ca); + cb = nat_toupper(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return +1; + + ++ai; ++bi; + } +} + + + +int strnatcmp(const char *a, const char *b) { + return strnatcmp0(a, b, 0); +} + + +/* Compare, recognizing numeric string and ignoring case. */ +int strnatcasecmp(const char *a, const char *b) { + return strnatcmp0(a, b, 1); +}