mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-23 00:28:26 -05:00
224 lines
6.5 KiB
C
224 lines
6.5 KiB
C
|
#include "redis.h"
|
||
|
#include <ctype.h>
|
||
|
#include <limits.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;
|
||
|
}
|
||
|
|
||
|
/* Check if the nul-terminated string 's' can be represented by a long
|
||
|
* (that is, is a number that fits into long without any other space or
|
||
|
* character before or after the digits).
|
||
|
*
|
||
|
* If so, the function returns REDIS_OK and *longval is set to the value
|
||
|
* of the number. Otherwise REDIS_ERR is returned */
|
||
|
int isStringRepresentableAsLong(sds s, long *longval) {
|
||
|
char buf[32], *endptr;
|
||
|
long value;
|
||
|
int slen;
|
||
|
|
||
|
value = strtol(s, &endptr, 10);
|
||
|
if (endptr[0] != '\0') return REDIS_ERR;
|
||
|
slen = ll2string(buf,32,value);
|
||
|
|
||
|
/* If the number converted back into a string is not identical
|
||
|
* then it's not possible to encode the string as integer */
|
||
|
if (sdslen(s) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR;
|
||
|
if (longval) *longval = value;
|
||
|
return REDIS_OK;
|
||
|
}
|