5bb47fc9aa
* NEW: Some little cosmetic changes. * BUG: Do not use gb.UseLanguage anymore. [INTERPRETER] * NEW: The gb.UseLanguage and gb.Case constants have been removed. * NEW: Array.Find() methods now take an optional argument that defines the search start index. * NEW: gb.Like is a new comparison mode that internally uses the LIKE operator. git-svn-id: svn://localhost/gambas/trunk@2260 867c0c6c-44f3-4631-809d-bfa615b0a4ec
344 lines
6.3 KiB
C
344 lines
6.3 KiB
C
/***************************************************************************
|
|
|
|
gbx_compare.c
|
|
|
|
(c) 2000-2009 Benoît Minisini <gambas@users.sourceforge.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************************/
|
|
|
|
#define __GBX_COMPARE_C
|
|
|
|
#include "gb_common.h"
|
|
#include "gb_common_case.h"
|
|
|
|
#include <wctype.h>
|
|
#include <wchar.h>
|
|
#include <iconv.h>
|
|
|
|
#include "gbx_type.h"
|
|
#include "gbx_compare.h"
|
|
#include "gbx_date.h"
|
|
#include "gbx_object.h"
|
|
#include "gbx_class.h"
|
|
#include "gbx_exec.h"
|
|
#include "gbx_regexp.h"
|
|
|
|
static bool _descent = FALSE;
|
|
|
|
|
|
int compare_nothing(void *a, void *b)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int compare_integer(int *a, int *b)
|
|
{
|
|
bool comp;
|
|
|
|
if (*a < *b)
|
|
comp = -1;
|
|
else if (*a > *b)
|
|
comp = 1;
|
|
else
|
|
return 0;
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
int compare_short(short *a, short *b)
|
|
{
|
|
bool comp;
|
|
|
|
if (*a < *b)
|
|
comp = -1;
|
|
else if (*a > *b)
|
|
comp = 1;
|
|
else
|
|
return 0;
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
|
|
int compare_byte(unsigned char *a, unsigned char *b)
|
|
{
|
|
bool comp;
|
|
|
|
if (*a < *b)
|
|
comp = -1;
|
|
else if (*a > *b)
|
|
comp = 1;
|
|
else
|
|
return 0;
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
|
|
int compare_long(int64_t *a, int64_t *b)
|
|
{
|
|
bool comp;
|
|
|
|
if (*a < *b)
|
|
comp = -1;
|
|
else if (*a > *b)
|
|
comp = 1;
|
|
else
|
|
return 0;
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
|
|
int compare_float(double *a, double *b)
|
|
{
|
|
bool comp;
|
|
|
|
if (*a < *b)
|
|
comp = -1;
|
|
else if (*a > *b)
|
|
comp = 1;
|
|
else
|
|
return 0;
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
|
|
int compare_single(float *a, float *b)
|
|
{
|
|
bool comp;
|
|
|
|
if (*a < *b)
|
|
comp = -1;
|
|
else if (*a > *b)
|
|
comp = 1;
|
|
else
|
|
return 0;
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
|
|
int compare_date(DATE *a, DATE *b)
|
|
{
|
|
bool comp;
|
|
|
|
comp = DATE_comp(a, b);
|
|
|
|
if (_descent)
|
|
comp = -comp;
|
|
|
|
return comp;
|
|
}
|
|
|
|
#define IMPLEMENT_COMPARE_STRING(_name, _func) \
|
|
int compare_string_##_name(char **pa, char **pb) \
|
|
{ \
|
|
char *a; \
|
|
char *b; \
|
|
int comp; \
|
|
\
|
|
a = *pa; \
|
|
if (!a) \
|
|
a = ""; \
|
|
\
|
|
b = *pb; \
|
|
if (!b) \
|
|
b = ""; \
|
|
\
|
|
comp = _func(a, b); \
|
|
if (_descent) \
|
|
comp = -comp; \
|
|
return comp; \
|
|
}
|
|
|
|
IMPLEMENT_COMPARE_STRING(binary, strcmp)
|
|
IMPLEMENT_COMPARE_STRING(text, strcasecmp)
|
|
//IMPLEMENT_COMPARE_STRING(lang, strcoll)
|
|
|
|
int COMPARE_string_lang(char *s1, int l1, char *s2, int l2, bool nocase, bool throw)
|
|
{
|
|
wchar_t *t1 = NULL;
|
|
wchar_t *t2 = NULL;
|
|
int i, cmp;
|
|
int lt1, lt2;
|
|
|
|
if (l1 < 0)
|
|
l1 = s1 ? strlen(s1) : 0;
|
|
|
|
if (l2 < 0)
|
|
l2 = s2 ? strlen(s2) : 0;
|
|
|
|
if (l1 == 0)
|
|
{
|
|
if (l2 == 0)
|
|
return 0;
|
|
else
|
|
return (-1);
|
|
}
|
|
else if (l2 == 0)
|
|
return 1;
|
|
|
|
if (STRING_conv((char **)(void *)&t1, s1, l1, "UTF-8", SC_UNICODE, throw)
|
|
|| STRING_conv((char **)(void *)&t2, s2, l2, "UTF-8", SC_UNICODE, throw))
|
|
goto __FAILED;
|
|
|
|
lt1 = STRING_length((char *)t1) / sizeof(wchar_t);
|
|
lt2 = STRING_length((char *)t2) / sizeof(wchar_t);
|
|
|
|
if (nocase)
|
|
{
|
|
for (i = 0; i < lt1; i++)
|
|
t1[i] = towlower(t1[i]);
|
|
for (i = 0; i < lt2; i++)
|
|
t2[i] = towlower(t2[i]);
|
|
}
|
|
|
|
errno = 0;
|
|
cmp = wcscoll(t1, t2);
|
|
if (!errno)
|
|
return cmp;
|
|
|
|
__FAILED:
|
|
|
|
return nocase ? TABLE_compare_ignore_case(s1, l1, s2, l2) : TABLE_compare(s1, l1, s2, l2);
|
|
}
|
|
|
|
static int compare_string_lang(char **pa, char **pb)
|
|
{
|
|
int diff = COMPARE_string_lang(*pa, -1, *pb, -1, FALSE, TRUE);
|
|
return _descent ? (-diff) : diff;
|
|
}
|
|
|
|
static int compare_string_lang_case(char **pa, char **pb)
|
|
{
|
|
int diff = COMPARE_string_lang(*pa, -1, *pb, -1, TRUE, TRUE);
|
|
return _descent ? (-diff) : diff;
|
|
}
|
|
|
|
static int compare_string_like(char **pa, char **pb)
|
|
{
|
|
int la = *pa ? strlen(*pa) : 0;
|
|
int lb = *pb ? strlen(*pb) : 0;
|
|
return REGEXP_match(*pb, lb, *pa, la) ? 0 : TABLE_compare_ignore_case(*pa, la, *pb, lb);
|
|
}
|
|
|
|
int COMPARE_object(void **a, void **b)
|
|
{
|
|
bool comp;
|
|
CLASS *ca, *cb;
|
|
|
|
ca = OBJECT_class(*a);
|
|
cb = OBJECT_class(*b);
|
|
|
|
if (ca && cb && ca->special[SPEC_COMPARE] != NO_SYMBOL)
|
|
{
|
|
STACK_check(1);
|
|
SP->_object.class = cb;
|
|
SP->_object.object = *b;
|
|
OBJECT_REF(*b, "compare_object");
|
|
SP++;
|
|
EXEC_special(SPEC_COMPARE, ca, *a, 1, FALSE);
|
|
VALUE_conv(&SP[-1], T_INTEGER);
|
|
SP--;
|
|
comp = SP->_integer.value;
|
|
}
|
|
else if (ca && cb && cb->special[SPEC_COMPARE] != NO_SYMBOL)
|
|
{
|
|
STACK_check(1);
|
|
SP->_object.class = ca;
|
|
SP->_object.object = *a;
|
|
OBJECT_REF(*a, "compare_object");
|
|
SP++;
|
|
EXEC_special(SPEC_COMPARE, cb, *b, 1, FALSE);
|
|
VALUE_conv(&SP[-1], T_INTEGER);
|
|
SP--;
|
|
comp = (- SP->_integer.value);
|
|
}
|
|
else
|
|
{
|
|
comp = (*a == *b) ? 0 : (*a > *b) ? 1 : -1;
|
|
}
|
|
|
|
return _descent ? (-comp) : comp;
|
|
}
|
|
|
|
COMPARE_FUNC COMPARE_get(TYPE type, int mode)
|
|
{
|
|
_descent = (mode & GB_COMP_DESCENT) != 0;
|
|
mode &= GB_COMP_TYPE_MASK;
|
|
|
|
if (type >= T_OBJECT)
|
|
return (COMPARE_FUNC)COMPARE_object;
|
|
|
|
switch(type)
|
|
{
|
|
case T_INTEGER:
|
|
return (COMPARE_FUNC)compare_integer;
|
|
|
|
case T_SHORT:
|
|
return (COMPARE_FUNC)compare_short;
|
|
|
|
case T_BYTE:
|
|
case T_BOOLEAN:
|
|
return (COMPARE_FUNC)compare_byte;
|
|
|
|
case T_LONG:
|
|
return (COMPARE_FUNC)compare_long;
|
|
|
|
case T_FLOAT:
|
|
return (COMPARE_FUNC)compare_float;
|
|
|
|
case T_SINGLE:
|
|
return (COMPARE_FUNC)compare_single;
|
|
|
|
case T_DATE:
|
|
return (COMPARE_FUNC)compare_date;
|
|
|
|
case T_STRING:
|
|
switch(mode)
|
|
{
|
|
case GB_COMP_LANG | GB_COMP_TEXT: return (COMPARE_FUNC)compare_string_lang_case;
|
|
case GB_COMP_LANG: return (COMPARE_FUNC)compare_string_lang;
|
|
case GB_COMP_TEXT: return (COMPARE_FUNC)compare_string_text;
|
|
case GB_COMP_LIKE: return (COMPARE_FUNC)compare_string_like;
|
|
default: return (COMPARE_FUNC)compare_string_binary;
|
|
}
|
|
|
|
default:
|
|
return (COMPARE_FUNC)compare_nothing;
|
|
}
|
|
}
|