formatted_print.c fsock.c head.c html.c image.c info.c iptc.c lcg.c \
link.c mail.c math.c md5.c metaphone.c microtime.c pack.c pageinfo.c \
parsedate.c quot_print.c rand.c reg.c soundex.c string.c \
- syslog.c type.c uniqid.c url.c url_scanner.c var.c output.c assert.c
+ syslog.c type.c uniqid.c url.c url_scanner.c var.c output.c assert.c \
+ strnatcmp.c
include $(topsrcdir)/build/ltlib.mk
#include "php_globals.h"
#include "php_array.h"
#include "basic_functions.h"
+#include "php_string.h"
#ifdef ZTS
int array_globals_id;
return array_data_compare(a,b)*-1;
}
+static int array_natural_general_compare(const void *a, const void *b, int fold_case)
+{
+ Bucket *f, *s;
+ zval *fval, *sval;
+ zval first, second;
+ int result;
+
+ f = *((Bucket **) a);
+ s = *((Bucket **) b);
+
+ fval = *((pval **) f->pData);
+ sval = *((pval **) s->pData);
+ first = *fval;
+ second = *sval;
+ if (fval->type != IS_STRING) {
+ zval_copy_ctor(&first);
+ convert_to_string(&first);
+ }
+ if (sval->type != IS_STRING) {
+ zval_copy_ctor(&first);
+ convert_to_string(&second);
+ }
+
+ result = strnatcmp_ex(first.value.str.val, first.value.str.len,
+ second.value.str.val, second.value.str.len, fold_case);
+
+ if (fval->type != IS_STRING)
+ zval_dtor(&first);
+ if (sval->type != IS_STRING)
+ zval_dtor(&second);
+
+ return result;
+}
+
+static int array_natural_compare(const void *a, const void *b)
+{
+ return array_natural_general_compare(a, b, 0);
+}
+
+static int array_natural_case_compare(const void *a, const void *b)
+{
+ return array_natural_general_compare(a, b, 1);
+}
+
+static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
+{
+ zval **array;
+ HashTable *target_hash;
+
+ if (ARG_COUNT(ht) != 1 || zend_get_parameters_ex(1, &array) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ target_hash = HASH_OF(*array);
+ if (!target_hash) {
+ php_error(E_WARNING, "Wrong datatype in %s() call",
+ get_active_function_name());
+ return;
+ }
+
+ if (fold_case) {
+ if (zend_hash_sort(target_hash, qsort, array_natural_case_compare, 0) == FAILURE) {
+ return;
+ }
+ } else {
+ if (zend_hash_sort(target_hash, qsort, array_natural_compare, 0) == FAILURE) {
+ return;
+ }
+ }
+
+ RETURN_TRUE;
+}
+
+
+/* {{{ proto void natsort(array array_arg)
+ Sort an array using natural sort */
+PHP_FUNCTION(natsort)
+{
+ php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+/* }}} */
+
+
+/* {{{ proto void natcasesort(array array_arg)
+ Sort an array using case-insensitive natural sort */
+PHP_FUNCTION(natcasesort)
+{
+ php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+/* }}} */
+
+
/* {{{ proto void asort(array array_arg)
Sort an array and maintain index association */
PHP_FUNCTION(asort)
PHP_FE(php_logo_guid, NULL)
PHP_FE(zend_logo_guid, NULL)
+ PHP_FE(strnatcmp, NULL)
+ PHP_FE(strnatcasecmp, NULL)
PHP_FE(strspn, NULL)
PHP_FE(strcspn, NULL)
PHP_FE(strtok, NULL)
/* functions from array.c */
PHP_FE(ksort, first_arg_force_ref)
PHP_FE(krsort, first_arg_force_ref)
+ PHP_FE(natsort, first_arg_force_ref)
+ PHP_FE(natcasesort, first_arg_force_ref)
PHP_FE(asort, first_arg_force_ref)
PHP_FE(arsort, first_arg_force_ref)
PHP_FE(sort, first_arg_force_ref)
PHP_FUNCTION(ksort);
PHP_FUNCTION(krsort);
+PHP_FUNCTION(natsort);
+PHP_FUNCTION(natcasesort);
PHP_FUNCTION(asort);
PHP_FUNCTION(arsort);
PHP_FUNCTION(sort);
PHP_FUNCTION(strip_tags);
PHP_FUNCTION(str_repeat);
PHP_FUNCTION(substr_replace);
+PHP_FUNCTION(strnatcmp);
+PHP_FUNCTION(strnatcasecmp);
+
+#define strnatcmp(a, b) \
+ strnatcmp_ex(a, strlen(a), b, strlen(b), 0)
+#define strnatcasecmp(a, b) \
+ strnatcmp_ex(a, strlen(a), b, strlen(b), 1)
+PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, int fold_case);
PHPAPI char *php_strtoupper(char *s, size_t len);
PHPAPI char *php_strtolower(char *s, size_t len);
}
}
/* }}} */
+
+static void php_strnatcmp(INTERNAL_FUNCTION_PARAMETERS, int fold_case)
+{
+ zval **s1, **s2;
+
+ if (ARG_COUNT(ht)!=2 || zend_get_parameters_ex(2, &s1, &s2) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+
+ convert_to_string_ex(s1);
+ convert_to_string_ex(s2);
+
+ RETURN_LONG(strnatcmp_ex((*s1)->value.str.val, (*s1)->value.str.len,
+ (*s2)->value.str.val, (*s2)->value.str.len,
+ fold_case));
+}
+
+PHP_FUNCTION(strnatcmp)
+{
+ php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+
+PHP_FUNCTION(strnatcasecmp)
+{
+ php_strnatcmp(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
--- /dev/null
+/* -*- 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"
+
+#if defined(__GNUC__)
+# define UNUSED __attribute__((__unused__))
+#endif
+
+static char const *version UNUSED =
+ "$Id$";
+
+PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, int fold_case)
+{
+ int ai, bi;
+ char ca, cb;
+ const char *aend = a + a_len,
+ *bend = b + b_len;
+
+ if (a_len == 0 || b_len == 0)
+ return a_len - b_len;
+
+ assert(a && b);
+ ai = bi = 0;
+ while (1) {
+ ca = a[ai]; cb = b[bi];
+
+ /* skip over leading spaces or zeros */
+ while (isspace(ca) || ca == '0')
+ ca = a[++ai];
+
+ while (isspace(cb) || cb == '0')
+ cb = b[++bi];
+
+ /* process run of digits */
+ if (isdigit(ca) && isdigit(cb)) {
+ int bias = 0;
+ /* The longest run of digits (stripping off leading
+ zeros) 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. */
+ while (1) {
+ if (!isdigit(ca) && !isdigit(cb))
+ goto done_number;
+ else if (!isdigit(ca))
+ return -1;
+ else if (!isdigit(cb))
+ return +1;
+ else if (ca < cb) {
+ if (!bias)
+ bias = -1;
+ } else if (ca > cb) {
+ if (!bias)
+ bias = +1;
+ }
+
+ ++ai; ++bi;
+ if (a + ai == aend && b + bi == bend)
+ /* Return the current bias if at the end of both strings. */
+ return bias;
+ else if (a + ai == aend)
+ return -1;
+ else if (b + bi == bend)
+ return 1;
+
+ ca = a[ai]; cb = b[bi];
+ }
+done_number:
+ if (bias)
+ return bias;
+ }
+
+ if (fold_case) {
+ ca = toupper(ca);
+ cb = toupper(cb);
+ }
+
+ if (ca < cb)
+ return -1;
+ else if (ca > cb)
+ return +1;
+
+ ++ai; ++bi;
+ if (a + ai == aend && b + bi == bend)
+ /* The strings compare the same. Perhaps the caller
+ will want to call strcmp to break the tie. */
+ return 0;
+ else if (a + ai == aend)
+ return -1;
+ else if (b + bi == bend)
+ return 1;
+ }
+}