2000-04-13 03:39:02 +08:00
|
|
|
/* -*- mode: c; c-file-style: "k&r" -*-
|
|
|
|
|
|
|
|
Modified for PHP by Andrei Zmievski <andrei@ispi.net>
|
|
|
|
|
|
|
|
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
|
|
|
|
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "php.h"
|
|
|
|
#include "php_string.h"
|
|
|
|
|
2001-06-06 21:06:12 +08:00
|
|
|
/* {{{ compare_right
|
|
|
|
*/
|
2000-04-30 02:57:06 +08:00
|
|
|
static int
|
|
|
|
compare_right(char const **a, char const *aend, char const **b, char const *bend)
|
|
|
|
{
|
|
|
|
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)++) {
|
2003-04-17 05:10:29 +08:00
|
|
|
if ((*a == aend || !isdigit((int)(unsigned char)**a)) &&
|
|
|
|
(*b == bend || !isdigit((int)(unsigned char)**b)))
|
2000-04-30 02:57:06 +08:00
|
|
|
return bias;
|
2003-04-17 05:10:29 +08:00
|
|
|
else if (*a == aend || !isdigit((int)(unsigned char)**a))
|
2000-04-30 02:57:06 +08:00
|
|
|
return -1;
|
2003-04-17 05:10:29 +08:00
|
|
|
else if (*b == bend || !isdigit((int)(unsigned char)**b))
|
2000-04-30 02:57:06 +08:00
|
|
|
return +1;
|
|
|
|
else if (**a < **b) {
|
|
|
|
if (!bias)
|
|
|
|
bias = -1;
|
|
|
|
} else if (**a > **b) {
|
|
|
|
if (!bias)
|
|
|
|
bias = +1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2001-06-06 21:06:12 +08:00
|
|
|
/* }}} */
|
2000-04-30 02:57:06 +08:00
|
|
|
|
2001-06-06 21:06:12 +08:00
|
|
|
/* {{{ compare_left
|
|
|
|
*/
|
2000-04-30 02:57:06 +08:00
|
|
|
static int
|
|
|
|
compare_left(char const **a, char const *aend, char const **b, char const *bend)
|
|
|
|
{
|
|
|
|
/* Compare two left-aligned numbers: the first to have a
|
|
|
|
different value wins. */
|
|
|
|
for(;; (*a)++, (*b)++) {
|
2003-04-17 05:10:29 +08:00
|
|
|
if ((*a == aend || !isdigit((int)(unsigned char)**a)) &&
|
|
|
|
(*b == bend || !isdigit((int)(unsigned char)**b)))
|
2000-04-30 02:57:06 +08:00
|
|
|
return 0;
|
2003-04-17 05:10:29 +08:00
|
|
|
else if (*a == aend || !isdigit((int)(unsigned char)**a))
|
2000-04-30 02:57:06 +08:00
|
|
|
return -1;
|
2003-04-17 05:10:29 +08:00
|
|
|
else if (*b == bend || !isdigit((int)(unsigned char)**b))
|
2000-04-30 02:57:06 +08:00
|
|
|
return +1;
|
|
|
|
else if (**a < **b)
|
|
|
|
return -1;
|
|
|
|
else if (**a > **b)
|
|
|
|
return +1;
|
|
|
|
}
|
2015-01-03 17:22:58 +08:00
|
|
|
|
2000-04-30 02:57:06 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2001-06-06 21:06:12 +08:00
|
|
|
/* }}} */
|
2000-04-30 02:57:06 +08:00
|
|
|
|
2001-06-06 21:06:12 +08:00
|
|
|
/* {{{ strnatcmp_ex
|
|
|
|
*/
|
2000-04-13 03:39:02 +08:00
|
|
|
PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, int fold_case)
|
|
|
|
{
|
2009-07-17 19:13:51 +08:00
|
|
|
unsigned char ca, cb;
|
2000-04-30 02:57:06 +08:00
|
|
|
char const *ap, *bp;
|
|
|
|
char const *aend = a + a_len,
|
2000-04-13 03:39:02 +08:00
|
|
|
*bend = b + b_len;
|
2009-08-08 01:32:31 +08:00
|
|
|
int fractional, result;
|
|
|
|
short leading = 1;
|
2000-04-13 03:39:02 +08:00
|
|
|
|
2014-09-14 22:37:38 +08:00
|
|
|
if (a_len == 0 || b_len == 0) {
|
2014-09-15 18:54:46 +08:00
|
|
|
return (a_len == b_len ? 0 : (a_len > b_len ? 1 : -1));
|
2014-09-14 22:37:38 +08:00
|
|
|
}
|
2000-04-13 03:39:02 +08:00
|
|
|
|
2000-04-30 02:57:06 +08:00
|
|
|
ap = a;
|
|
|
|
bp = b;
|
2000-04-13 03:39:02 +08:00
|
|
|
while (1) {
|
2000-04-30 02:57:06 +08:00
|
|
|
ca = *ap; cb = *bp;
|
2000-04-13 03:39:02 +08:00
|
|
|
|
2009-09-28 21:29:53 +08:00
|
|
|
/* skip over leading zeros */
|
2014-12-14 06:06:27 +08:00
|
|
|
while (leading && ca == '0' && (ap+1 < aend) && isdigit((int)(unsigned char)*(ap+1))) {
|
2000-04-30 02:57:06 +08:00
|
|
|
ca = *++ap;
|
2009-08-08 01:14:19 +08:00
|
|
|
}
|
2000-04-13 03:39:02 +08:00
|
|
|
|
2014-12-14 06:06:27 +08:00
|
|
|
while (leading && cb == '0' && (bp+1 < bend) && isdigit((int)(unsigned char)*(bp+1))) {
|
2000-04-30 02:57:06 +08:00
|
|
|
cb = *++bp;
|
2009-08-08 01:14:19 +08:00
|
|
|
}
|
|
|
|
|
2009-08-08 01:32:31 +08:00
|
|
|
leading = 0;
|
2000-04-13 03:39:02 +08:00
|
|
|
|
2009-08-08 22:39:34 +08:00
|
|
|
/* Skip consecutive whitespace */
|
|
|
|
while (isspace((int)(unsigned char)ca)) {
|
|
|
|
ca = *++ap;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (isspace((int)(unsigned char)cb)) {
|
|
|
|
cb = *++bp;
|
|
|
|
}
|
|
|
|
|
2000-04-13 03:39:02 +08:00
|
|
|
/* process run of digits */
|
2003-04-17 05:10:29 +08:00
|
|
|
if (isdigit((int)(unsigned char)ca) && isdigit((int)(unsigned char)cb)) {
|
2000-04-30 02:57:06 +08:00
|
|
|
fractional = (ca == '0' || cb == '0');
|
|
|
|
|
|
|
|
if (fractional)
|
|
|
|
result = compare_left(&ap, aend, &bp, bend);
|
|
|
|
else
|
|
|
|
result = compare_right(&ap, aend, &bp, bend);
|
|
|
|
|
|
|
|
if (result != 0)
|
|
|
|
return result;
|
|
|
|
else if (ap == aend && bp == bend)
|
|
|
|
/* End of the strings. Let caller sort them out. */
|
|
|
|
return 0;
|
|
|
|
else {
|
|
|
|
/* Keep on comparing from the current point. */
|
|
|
|
ca = *ap; cb = *bp;
|
2000-04-13 03:39:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fold_case) {
|
2003-04-17 05:10:29 +08:00
|
|
|
ca = toupper((int)(unsigned char)ca);
|
|
|
|
cb = toupper((int)(unsigned char)cb);
|
2000-04-13 03:39:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ca < cb)
|
|
|
|
return -1;
|
|
|
|
else if (ca > cb)
|
|
|
|
return +1;
|
|
|
|
|
2000-04-30 02:57:06 +08:00
|
|
|
++ap; ++bp;
|
2004-07-15 09:26:03 +08:00
|
|
|
if (ap >= aend && bp >= bend)
|
2000-04-13 03:39:02 +08:00
|
|
|
/* The strings compare the same. Perhaps the caller
|
|
|
|
will want to call strcmp to break the tie. */
|
|
|
|
return 0;
|
2004-07-15 09:26:03 +08:00
|
|
|
else if (ap >= aend)
|
2000-04-13 03:39:02 +08:00
|
|
|
return -1;
|
2004-07-15 09:26:03 +08:00
|
|
|
else if (bp >= bend)
|
2000-04-13 03:39:02 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2001-06-06 21:06:12 +08:00
|
|
|
/* }}} */
|
2001-06-05 21:12:10 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* tab-width: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* End:
|
2001-09-09 21:29:31 +08:00
|
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
|
|
* vim<600: sw=4 ts=4
|
2001-06-05 21:12:10 +08:00
|
|
|
*/
|