mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 08:38:27 -05:00
441 lines
12 KiB
C
441 lines
12 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include "util.h"
|
|
|
|
/* Glob-style pattern matching. */
|
|
int stringmatchlen(const char *pattern, int patternLen,
|
|
const char *string, int stringLen, int nocase)
|
|
{
|
|
while(patternLen) {
|
|
switch(pattern[0]) {
|
|
case '*':
|
|
while (pattern[1] == '*') {
|
|
pattern++;
|
|
patternLen--;
|
|
}
|
|
if (patternLen == 1)
|
|
return 1; /* match */
|
|
while(stringLen) {
|
|
if (stringmatchlen(pattern+1, patternLen-1,
|
|
string, stringLen, nocase))
|
|
return 1; /* match */
|
|
string++;
|
|
stringLen--;
|
|
}
|
|
return 0; /* no match */
|
|
break;
|
|
case '?':
|
|
if (stringLen == 0)
|
|
return 0; /* no match */
|
|
string++;
|
|
stringLen--;
|
|
break;
|
|
case '[':
|
|
{
|
|
int not, match;
|
|
|
|
pattern++;
|
|
patternLen--;
|
|
not = pattern[0] == '^';
|
|
if (not) {
|
|
pattern++;
|
|
patternLen--;
|
|
}
|
|
match = 0;
|
|
while(1) {
|
|
if (pattern[0] == '\\') {
|
|
pattern++;
|
|
patternLen--;
|
|
if (pattern[0] == string[0])
|
|
match = 1;
|
|
} else if (pattern[0] == ']') {
|
|
break;
|
|
} else if (patternLen == 0) {
|
|
pattern--;
|
|
patternLen++;
|
|
break;
|
|
} else if (pattern[1] == '-' && patternLen >= 3) {
|
|
int start = pattern[0];
|
|
int end = pattern[2];
|
|
int c = string[0];
|
|
if (start > end) {
|
|
int t = start;
|
|
start = end;
|
|
end = t;
|
|
}
|
|
if (nocase) {
|
|
start = tolower(start);
|
|
end = tolower(end);
|
|
c = tolower(c);
|
|
}
|
|
pattern += 2;
|
|
patternLen -= 2;
|
|
if (c >= start && c <= end)
|
|
match = 1;
|
|
} else {
|
|
if (!nocase) {
|
|
if (pattern[0] == string[0])
|
|
match = 1;
|
|
} else {
|
|
if (tolower((int)pattern[0]) == tolower((int)string[0]))
|
|
match = 1;
|
|
}
|
|
}
|
|
pattern++;
|
|
patternLen--;
|
|
}
|
|
if (not)
|
|
match = !match;
|
|
if (!match)
|
|
return 0; /* no match */
|
|
string++;
|
|
stringLen--;
|
|
break;
|
|
}
|
|
case '\\':
|
|
if (patternLen >= 2) {
|
|
pattern++;
|
|
patternLen--;
|
|
}
|
|
/* fall through */
|
|
default:
|
|
if (!nocase) {
|
|
if (pattern[0] != string[0])
|
|
return 0; /* no match */
|
|
} else {
|
|
if (tolower((int)pattern[0]) != tolower((int)string[0]))
|
|
return 0; /* no match */
|
|
}
|
|
string++;
|
|
stringLen--;
|
|
break;
|
|
}
|
|
pattern++;
|
|
patternLen--;
|
|
if (stringLen == 0) {
|
|
while(*pattern == '*') {
|
|
pattern++;
|
|
patternLen--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (patternLen == 0 && stringLen == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int stringmatch(const char *pattern, const char *string, int nocase) {
|
|
return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
|
|
}
|
|
|
|
/* Convert a string representing an amount of memory into the number of
|
|
* bytes, so for instance memtoll("1Gi") will return 1073741824 that is
|
|
* (1024*1024*1024).
|
|
*
|
|
* On parsing error, if *err is not NULL, it's set to 1, otherwise it's
|
|
* set to 0 */
|
|
long long memtoll(const char *p, int *err) {
|
|
const char *u;
|
|
char buf[128];
|
|
long mul; /* unit multiplier */
|
|
long long val;
|
|
unsigned int digits;
|
|
|
|
if (err) *err = 0;
|
|
/* Search the first non digit character. */
|
|
u = p;
|
|
if (*u == '-') u++;
|
|
while(*u && isdigit(*u)) u++;
|
|
if (*u == '\0' || !strcasecmp(u,"b")) {
|
|
mul = 1;
|
|
} else if (!strcasecmp(u,"k")) {
|
|
mul = 1000;
|
|
} else if (!strcasecmp(u,"kb")) {
|
|
mul = 1024;
|
|
} else if (!strcasecmp(u,"m")) {
|
|
mul = 1000*1000;
|
|
} else if (!strcasecmp(u,"mb")) {
|
|
mul = 1024*1024;
|
|
} else if (!strcasecmp(u,"g")) {
|
|
mul = 1000L*1000*1000;
|
|
} else if (!strcasecmp(u,"gb")) {
|
|
mul = 1024L*1024*1024;
|
|
} else {
|
|
if (err) *err = 1;
|
|
mul = 1;
|
|
}
|
|
digits = u-p;
|
|
if (digits >= sizeof(buf)) {
|
|
if (err) *err = 1;
|
|
return LLONG_MAX;
|
|
}
|
|
memcpy(buf,p,digits);
|
|
buf[digits] = '\0';
|
|
val = strtoll(buf,NULL,10);
|
|
return val*mul;
|
|
}
|
|
|
|
/* Convert a long long into a string. Returns the number of
|
|
* characters needed to represent the number, that can be shorter if passed
|
|
* buffer length is not enough to store the whole number. */
|
|
int ll2string(char *s, size_t len, long long value) {
|
|
char buf[32], *p;
|
|
unsigned long long v;
|
|
size_t l;
|
|
|
|
if (len == 0) return 0;
|
|
v = (value < 0) ? -value : value;
|
|
p = buf+31; /* point to the last character */
|
|
do {
|
|
*p-- = '0'+(v%10);
|
|
v /= 10;
|
|
} while(v);
|
|
if (value < 0) *p-- = '-';
|
|
p++;
|
|
l = 32-(p-buf);
|
|
if (l+1 > len) l = len-1; /* Make sure it fits, including the nul term */
|
|
memcpy(s,p,l);
|
|
s[l] = '\0';
|
|
return l;
|
|
}
|
|
|
|
/* Convert a string into a long long. Returns 1 if the string could be parsed
|
|
* into a (non-overflowing) long long, 0 otherwise. The value will be set to
|
|
* the parsed value when appropriate. */
|
|
int string2ll(char *s, size_t slen, long long *value) {
|
|
char *p = s;
|
|
size_t plen = 0;
|
|
int negative = 0;
|
|
unsigned long long v;
|
|
|
|
if (plen == slen)
|
|
return 0;
|
|
|
|
/* Special case: first and only digit is 0. */
|
|
if (slen == 1 && p[0] == '0') {
|
|
if (value != NULL) *value = 0;
|
|
return 1;
|
|
}
|
|
|
|
if (p[0] == '-') {
|
|
negative = 1;
|
|
p++; plen++;
|
|
|
|
/* Abort on only a negative sign. */
|
|
if (plen == slen)
|
|
return 0;
|
|
}
|
|
|
|
/* First digit should be 1-9, otherwise the string should just be 0. */
|
|
if (p[0] >= '1' && p[0] <= '9') {
|
|
v = p[0]-'0';
|
|
p++; plen++;
|
|
} else if (p[0] == '0' && slen == 1) {
|
|
*value = 0;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
while (plen < slen && p[0] >= '0' && p[0] <= '9') {
|
|
if (v > (ULLONG_MAX / 10)) /* Overflow. */
|
|
return 0;
|
|
v *= 10;
|
|
|
|
if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
|
|
return 0;
|
|
v += p[0]-'0';
|
|
|
|
p++; plen++;
|
|
}
|
|
|
|
/* Return if not all bytes were used. */
|
|
if (plen < slen)
|
|
return 0;
|
|
|
|
if (negative) {
|
|
if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
|
|
return 0;
|
|
if (value != NULL) *value = -v;
|
|
} else {
|
|
if (v > LLONG_MAX) /* Overflow. */
|
|
return 0;
|
|
if (value != NULL) *value = v;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Convert a string into a long. Returns 1 if the string could be parsed into a
|
|
* (non-overflowing) long, 0 otherwise. The value will be set to the parsed
|
|
* value when appropriate. */
|
|
int string2l(char *s, size_t slen, long *lval) {
|
|
long long llval;
|
|
|
|
if (!string2ll(s,slen,&llval))
|
|
return 0;
|
|
|
|
if (llval < LONG_MIN || llval > LONG_MAX)
|
|
return 0;
|
|
|
|
*lval = (long)llval;
|
|
return 1;
|
|
}
|
|
|
|
/* Convert a double to a string representation. Returns the number of bytes
|
|
* required. The representation should always be parsable by stdtod(3). */
|
|
int d2string(char *buf, size_t len, double value) {
|
|
if (isnan(value)) {
|
|
len = snprintf(buf,len,"nan");
|
|
} else if (isinf(value)) {
|
|
if (value < 0)
|
|
len = snprintf(buf,len,"-inf");
|
|
else
|
|
len = snprintf(buf,len,"inf");
|
|
} else if (value == 0) {
|
|
/* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */
|
|
if (1.0/value < 0)
|
|
len = snprintf(buf,len,"-0");
|
|
else
|
|
len = snprintf(buf,len,"0");
|
|
} else {
|
|
#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)
|
|
/* Check if the float is in a safe range to be casted into a
|
|
* long long. We are assuming that long long is 64 bit here.
|
|
* Also we are assuming that there are no implementations around where
|
|
* double has precision < 52 bit.
|
|
*
|
|
* Under this assumptions we test if a double is inside an interval
|
|
* where casting to long long is safe. Then using two castings we
|
|
* make sure the decimal part is zero. If all this is true we use
|
|
* integer printing function that is much faster. */
|
|
double min = -4503599627370495; /* (2^52)-1 */
|
|
double max = 4503599627370496; /* -(2^52) */
|
|
if (val > min && val < max && value == ((double)((long long)value)))
|
|
len = ll2string(buf,len,(long long)value);
|
|
else
|
|
#endif
|
|
len = snprintf(buf,len,"%.17g",value);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
#ifdef UTIL_TEST_MAIN
|
|
#include <assert.h>
|
|
|
|
void test_string2ll(void) {
|
|
char buf[32];
|
|
long long v;
|
|
|
|
/* May not start with +. */
|
|
strcpy(buf,"+1");
|
|
assert(string2ll(buf,strlen(buf),&v) == 0);
|
|
|
|
/* Leading space. */
|
|
strcpy(buf," 1");
|
|
assert(string2ll(buf,strlen(buf),&v) == 0);
|
|
|
|
/* Trailing space. */
|
|
strcpy(buf,"1 ");
|
|
assert(string2ll(buf,strlen(buf),&v) == 0);
|
|
|
|
/* May not start with 0. */
|
|
strcpy(buf,"01");
|
|
assert(string2ll(buf,strlen(buf),&v) == 0);
|
|
|
|
strcpy(buf,"-1");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == -1);
|
|
|
|
strcpy(buf,"0");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == 0);
|
|
|
|
strcpy(buf,"1");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == 1);
|
|
|
|
strcpy(buf,"99");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == 99);
|
|
|
|
strcpy(buf,"-99");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == -99);
|
|
|
|
strcpy(buf,"-9223372036854775808");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == LLONG_MIN);
|
|
|
|
strcpy(buf,"-9223372036854775809"); /* overflow */
|
|
assert(string2ll(buf,strlen(buf),&v) == 0);
|
|
|
|
strcpy(buf,"9223372036854775807");
|
|
assert(string2ll(buf,strlen(buf),&v) == 1);
|
|
assert(v == LLONG_MAX);
|
|
|
|
strcpy(buf,"9223372036854775808"); /* overflow */
|
|
assert(string2ll(buf,strlen(buf),&v) == 0);
|
|
}
|
|
|
|
void test_string2l(void) {
|
|
char buf[32];
|
|
long v;
|
|
|
|
/* May not start with +. */
|
|
strcpy(buf,"+1");
|
|
assert(string2l(buf,strlen(buf),&v) == 0);
|
|
|
|
/* May not start with 0. */
|
|
strcpy(buf,"01");
|
|
assert(string2l(buf,strlen(buf),&v) == 0);
|
|
|
|
strcpy(buf,"-1");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == -1);
|
|
|
|
strcpy(buf,"0");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == 0);
|
|
|
|
strcpy(buf,"1");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == 1);
|
|
|
|
strcpy(buf,"99");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == 99);
|
|
|
|
strcpy(buf,"-99");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == -99);
|
|
|
|
#if LONG_MAX != LLONG_MAX
|
|
strcpy(buf,"-2147483648");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == LONG_MIN);
|
|
|
|
strcpy(buf,"-2147483649"); /* overflow */
|
|
assert(string2l(buf,strlen(buf),&v) == 0);
|
|
|
|
strcpy(buf,"2147483647");
|
|
assert(string2l(buf,strlen(buf),&v) == 1);
|
|
assert(v == LONG_MAX);
|
|
|
|
strcpy(buf,"2147483648"); /* overflow */
|
|
assert(string2l(buf,strlen(buf),&v) == 0);
|
|
#endif
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
test_string2ll();
|
|
test_string2l();
|
|
return 0;
|
|
}
|
|
#endif
|