summaryrefslogtreecommitdiff
path: root/firmware/common
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2009-03-01 17:55:59 +0000
committerThomas Martitz <kugel@rockbox.org>2009-03-01 17:55:59 +0000
commitd13f1a485f0e35a6fbbd0a664f14acc3798d52a0 (patch)
tree22cacb27b6ab481c0bfc250120dde404320811f2 /firmware/common
parente6c023cb64dea599bb711b2b4ddb197efdb1d187 (diff)
downloadrockbox-d13f1a485f0e35a6fbbd0a664f14acc3798d52a0.tar.gz
rockbox-d13f1a485f0e35a6fbbd0a664f14acc3798d52a0.zip
Commit FS#8314. This adds strnat[case]cmp written by Martin Pool, which respects numbers within strings, and gives a more intuitive
sorting. This also adds a setting, so that the sorting can be used in the file browser. The implementation is very generic, and can possibly be used in other places. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20155 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/common')
-rw-r--r--firmware/common/strnatcmp.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/firmware/common/strnatcmp.c b/firmware/common/strnatcmp.c
new file mode 100644
index 0000000000..84e0d38362
--- /dev/null
+++ b/firmware/common/strnatcmp.c
@@ -0,0 +1,179 @@
1/* -*- mode: c; c-file-style: "k&r" -*-
2
3 strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
4 Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
5
6 This software is provided 'as-is', without any express or implied
7 warranty. In no event will the authors be held liable for any damages
8 arising from the use of this software.
9
10 Permission is granted to anyone to use this software for any purpose,
11 including commercial applications, and to alter it and redistribute it
12 freely, subject to the following restrictions:
13
14 1. The origin of this software must not be misrepresented; you must not
15 claim that you wrote the original software. If you use this software
16 in a product, an acknowledgment in the product documentation would be
17 appreciated but is not required.
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source distribution.
21*/
22
23
24/* partial change history:
25 *
26 * 2004-10-10 mbp: Lift out character type dependencies into macros.
27 *
28 * Eric Sosman pointed out that ctype functions take a parameter whose
29 * value must be that of an unsigned int, even on platforms that have
30 * negative chars in their default char type.
31 */
32
33#include <ctype.h>
34#include <string.h>
35#include <stdio.h>
36
37#include "strnatcmp.h"
38
39#define assert(x) /* nothing */
40
41
42/* These are defined as macros to make it easier to adapt this code to
43 * different characters types or comparison functions. */
44static inline int
45nat_isdigit(char a)
46{
47 return isdigit((unsigned char) a);
48}
49
50
51static inline int
52nat_isspace(char a)
53{
54 return a == '0' || isspace((unsigned char) a);
55}
56
57
58static inline char
59nat_toupper(char a)
60{
61 return toupper((unsigned char) a);
62}
63
64
65
66static int
67compare_right(char const *a, char const *b)
68{
69 int bias = 0;
70
71 /* The longest run of digits wins. That aside, the greatest
72 value wins, but we can't know that it will until we've scanned
73 both numbers to know that they have the same magnitude, so we
74 remember it in BIAS. */
75 for (;; a++, b++) {
76 if (!nat_isdigit(*a) && !nat_isdigit(*b))
77 return bias;
78 else if (!nat_isdigit(*a))
79 return -1;
80 else if (!nat_isdigit(*b))
81 return +1;
82 else if (*a < *b) {
83 if (!bias)
84 bias = -1;
85 } else if (*a > *b) {
86 if (!bias)
87 bias = +1;
88 } else if (!*a && !*b)
89 return bias;
90 }
91
92 return 0;
93}
94
95
96static int
97compare_left(char const *a, char const *b)
98{
99 /* Compare two left-aligned numbers: the first to have a
100 different value wins. */
101 for (;; a++, b++) {
102 if (!nat_isdigit(*a) && !nat_isdigit(*b))
103 return 0;
104 else if (!nat_isdigit(*a))
105 return -1;
106 else if (!nat_isdigit(*b))
107 return +1;
108 else if (*a < *b)
109 return -1;
110 else if (*a > *b)
111 return +1;
112 }
113
114 return 0;
115}
116
117
118static int strnatcmp0(char const *a, char const *b, int fold_case)
119{
120 int ai, bi;
121 char ca, cb;
122 int fractional, result;
123
124 assert(a && b);
125 ai = bi = 0;
126 while (1) {
127 ca = a[ai]; cb = b[bi];
128
129 /* skip over leading spaces or zeros */
130 while (nat_isspace(ca))
131 ca = a[++ai];
132
133 while (nat_isspace(cb))
134 cb = b[++bi];
135
136 /* process run of digits */
137 if (nat_isdigit(ca) && nat_isdigit(cb)) {
138 fractional = (ca == '0' || cb == '0');
139
140 if (fractional) {
141 if ((result = compare_left(a+ai, b+bi)) != 0)
142 return result;
143 } else {
144 if ((result = compare_right(a+ai, b+bi)) != 0)
145 return result;
146 }
147 }
148
149 if (!ca && !cb) {
150 /* The strings compare the same. Perhaps the caller
151 will want to call strcmp to break the tie. */
152 return 0;
153 }
154
155 if (fold_case) {
156 ca = nat_toupper(ca);
157 cb = nat_toupper(cb);
158 }
159
160 if (ca < cb)
161 return -1;
162 else if (ca > cb)
163 return +1;
164
165 ++ai; ++bi;
166 }
167}
168
169
170
171int strnatcmp(const char *a, const char *b) {
172 return strnatcmp0(a, b, 0);
173}
174
175
176/* Compare, recognizing numeric string and ignoring case. */
177int strnatcasecmp(const char *a, const char *b) {
178 return strnatcmp0(a, b, 1);
179}