eBookReaderSwitch/source/fitz/string.c

794 lines
16 KiB
C

#include "mupdf/fitz.h"
#include <string.h>
#include <errno.h>
#include <math.h>
#include <float.h>
#include <stdlib.h>
static inline int
fz_tolower(int c)
{
if (c >= 'A' && c <= 'Z')
return c + 32;
return c;
}
/*
Return strlen(s), if that is less than maxlen, or maxlen if
there is no null byte ('\0') among the first maxlen bytes.
*/
size_t
fz_strnlen(const char *s, size_t n)
{
const char *p = memchr(s, 0, n);
return p ? (size_t) (p - s) : n;
}
int
fz_strncasecmp(const char *a, const char *b, int n)
{
if (!n--)
return 0;
for (; *a && *b && n && (*a == *b || fz_tolower(*a) == fz_tolower(*b)); a++, b++, n--)
;
return fz_tolower(*a) - fz_tolower(*b);
}
/*
Case insensitive (ASCII only) string comparison.
*/
int
fz_strcasecmp(const char *a, const char *b)
{
while (fz_tolower(*a) == fz_tolower(*b))
{
if (*a++ == 0)
return 0;
b++;
}
return fz_tolower(*a) - fz_tolower(*b);
}
/*
Given a pointer to a C string (or a pointer to NULL) break
it at the first occurrence of a delimiter char (from a given set).
stringp: Pointer to a C string pointer (or NULL). Updated on exit to
point to the first char of the string after the delimiter that was
found. The string pointed to by stringp will be corrupted by this
call (as the found delimiter will be overwritten by 0).
delim: A C string of acceptable delimiter characters.
Returns a pointer to a C string containing the chars of stringp up
to the first delimiter char (or the end of the string), or NULL.
*/
char *
fz_strsep(char **stringp, const char *delim)
{
char *ret = *stringp;
if (!ret) return NULL;
if ((*stringp = strpbrk(*stringp, delim)) != NULL)
*((*stringp)++) = '\0';
return ret;
}
/*
Copy at most n-1 chars of a string into a destination
buffer with null termination, returning the real length of the
initial string (excluding terminator).
dst: Destination buffer, at least n bytes long.
src: C string (non-NULL).
n: Size of dst buffer in bytes.
Returns the length (excluding terminator) of src.
*/
size_t
fz_strlcpy(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
/*
Concatenate 2 strings, with a maximum length.
dst: pointer to first string in a buffer of n bytes.
src: pointer to string to concatenate.
n: Size (in bytes) of buffer that dst is in.
Returns the real length that a concatenated dst + src would have been
(not including terminator).
*/
size_t
fz_strlcat(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (*d != '\0' && n-- != 0)
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return dlen + strlen(s);
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return dlen + (s - src); /* count does not include NUL */
}
/*
extract the directory component from a path.
*/
void
fz_dirname(char *dir, const char *path, size_t n)
{
size_t i;
if (!path || !path[0])
{
fz_strlcpy(dir, ".", n);
return;
}
fz_strlcpy(dir, path, n);
i = strlen(dir);
for(; dir[i] == '/'; --i) if (!i) { fz_strlcpy(dir, "/", n); return; }
for(; dir[i] != '/'; --i) if (!i) { fz_strlcpy(dir, ".", n); return; }
for(; dir[i] == '/'; --i) if (!i) { fz_strlcpy(dir, "/", n); return; }
dir[i+1] = 0;
}
static inline int ishex(int a)
{
return (a >= 'A' && a <= 'F') ||
(a >= 'a' && a <= 'f') ||
(a >= '0' && a <= '9');
}
static inline int tohex(int c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 0xA;
if (c >= 'A' && c <= 'F') return c - 'A' + 0xA;
return 0;
}
/*
decode url escapes.
*/
char *
fz_urldecode(char *url)
{
char *s = url;
char *p = url;
while (*s)
{
int c = (unsigned char) *s++;
if (c == '%' && ishex(s[0]) && ishex(s[1]))
{
int a = tohex(*s++);
int b = tohex(*s++);
*p++ = a << 4 | b;
}
else
{
*p++ = c;
}
}
*p = 0;
return url;
}
/*
create output file name using a template.
If the path contains %[0-9]*d, the first such pattern will be replaced
with the page number. If the template does not contain such a pattern, the page
number will be inserted before the filename extension. If the template does not have
a filename extension, the page number will be added to the end.
*/
void
fz_format_output_path(fz_context *ctx, char *path, size_t size, const char *fmt, int page)
{
const char *s, *p;
char num[40];
int i, n;
int z = 0;
for (i = 0; page; page /= 10)
num[i++] = '0' + page % 10;
num[i] = 0;
s = p = strchr(fmt, '%');
if (p)
{
++p;
while (*p >= '0' && *p <= '9')
z = z * 10 + (*p++ - '0');
}
if (p && *p == 'd')
{
++p;
}
else
{
s = p = strrchr(fmt, '.');
if (!p)
s = p = fmt + strlen(fmt);
}
if (z < 1)
z = 1;
while (i < z && i < (int)sizeof num)
num[i++] = '0';
n = s - fmt;
if (n + i + strlen(p) >= size)
fz_throw(ctx, FZ_ERROR_GENERIC, "path name buffer overflow");
memcpy(path, fmt, n);
while (i > 0)
path[n++] = num[--i];
fz_strlcpy(path + n, p, size - n);
}
#define SEP(x) ((x)=='/' || (x) == 0)
/*
rewrite path to the shortest string that names the same path.
Eliminates multiple and trailing slashes, interprets "." and "..".
Overwrites the string in place.
*/
char *
fz_cleanname(char *name)
{
char *p, *q, *dotdot;
int rooted;
rooted = name[0] == '/';
/*
* invariants:
* p points at beginning of path element we're considering.
* q points just past the last path element we wrote (no slash).
* dotdot points just past the point where .. cannot backtrack
* any further (no slash).
*/
p = q = dotdot = name + rooted;
while (*p)
{
if(p[0] == '/') /* null element */
p++;
else if (p[0] == '.' && SEP(p[1]))
p += 1; /* don't count the separator in case it is nul */
else if (p[0] == '.' && p[1] == '.' && SEP(p[2]))
{
p += 2;
if (q > dotdot) /* can backtrack */
{
while(--q > dotdot && *q != '/')
;
}
else if (!rooted) /* /.. is / but ./../ is .. */
{
if (q != name)
*q++ = '/';
*q++ = '.';
*q++ = '.';
dotdot = q;
}
}
else /* real path element */
{
if (q != name+rooted)
*q++ = '/';
while ((*q = *p) != '/' && *q != 0)
p++, q++;
}
}
if (q == name) /* empty string is really "." */
*q++ = '.';
*q = '\0';
return name;
}
enum
{
UTFmax = 4, /* maximum bytes per rune */
Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */
Runeself = 0x80, /* rune and UTF sequences are the same (<) */
Runeerror = 0xFFFD, /* decoding error in UTF */
Runemax = 0x10FFFF, /* maximum rune value */
};
enum
{
Bit1 = 7,
Bitx = 6,
Bit2 = 5,
Bit3 = 4,
Bit4 = 3,
Bit5 = 2,
T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */
Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */
T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */
T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */
T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */
T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */
Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0111 1111 */
Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0111 1111 1111 */
Rune3 = (1<<(Bit3+2*Bitx))-1, /* 1111 1111 1111 1111 */
Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0001 1111 1111 1111 1111 1111 */
Maskx = (1<<Bitx)-1, /* 0011 1111 */
Testx = Maskx ^ 0xFF, /* 1100 0000 */
Bad = Runeerror,
};
/*
UTF8 decode a single rune from a sequence of chars.
rune: Pointer to an int to assign the decoded 'rune' to.
str: Pointer to a UTF8 encoded string.
Returns the number of bytes consumed.
*/
int
fz_chartorune(int *rune, const char *str)
{
int c, c1, c2, c3;
int l;
/*
* one character sequence
* 00000-0007F => T1
*/
c = *(const unsigned char*)str;
if(c < Tx) {
*rune = c;
return 1;
}
/*
* two character sequence
* 0080-07FF => T2 Tx
*/
c1 = *(const unsigned char*)(str+1) ^ Tx;
if(c1 & Testx)
goto bad;
if(c < T3) {
if(c < T2)
goto bad;
l = ((c << Bitx) | c1) & Rune2;
if(l <= Rune1)
goto bad;
*rune = l;
return 2;
}
/*
* three character sequence
* 0800-FFFF => T3 Tx Tx
*/
c2 = *(const unsigned char*)(str+2) ^ Tx;
if(c2 & Testx)
goto bad;
if(c < T4) {
l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3;
if(l <= Rune2)
goto bad;
*rune = l;
return 3;
}
/*
* four character sequence (21-bit value)
* 10000-1FFFFF => T4 Tx Tx Tx
*/
c3 = *(const unsigned char*)(str+3) ^ Tx;
if (c3 & Testx)
goto bad;
if (c < T5) {
l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4;
if (l <= Rune3)
goto bad;
*rune = l;
return 4;
}
/*
* Support for 5-byte or longer UTF-8 would go here, but
* since we don't have that, we'll just fall through to bad.
*/
/*
* bad decoding
*/
bad:
*rune = Bad;
return 1;
}
/*
UTF8 encode a rune to a sequence of chars.
str: Pointer to a place to put the UTF8 encoded character.
rune: Pointer to a 'rune'.
Returns the number of bytes the rune took to output.
*/
int
fz_runetochar(char *str, int rune)
{
/* Runes are signed, so convert to unsigned for range check. */
unsigned int c = (unsigned int)rune;
/*
* one character sequence
* 00000-0007F => 00-7F
*/
if(c <= Rune1) {
str[0] = c;
return 1;
}
/*
* two character sequence
* 0080-07FF => T2 Tx
*/
if(c <= Rune2) {
str[0] = T2 | (c >> 1*Bitx);
str[1] = Tx | (c & Maskx);
return 2;
}
/*
* If the Rune is out of range, convert it to the error rune.
* Do this test here because the error rune encodes to three bytes.
* Doing it earlier would duplicate work, since an out of range
* Rune wouldn't have fit in one or two bytes.
*/
if (c > Runemax)
c = Runeerror;
/*
* three character sequence
* 0800-FFFF => T3 Tx Tx
*/
if (c <= Rune3) {
str[0] = T3 | (c >> 2*Bitx);
str[1] = Tx | ((c >> 1*Bitx) & Maskx);
str[2] = Tx | (c & Maskx);
return 3;
}
/*
* four character sequence (21-bit value)
* 10000-1FFFFF => T4 Tx Tx Tx
*/
str[0] = T4 | (c >> 3*Bitx);
str[1] = Tx | ((c >> 2*Bitx) & Maskx);
str[2] = Tx | ((c >> 1*Bitx) & Maskx);
str[3] = Tx | (c & Maskx);
return 4;
}
/*
Count how many chars are required to represent a rune.
rune: The rune to encode.
Returns the number of bytes required to represent this run in UTF8.
*/
int
fz_runelen(int c)
{
char str[10];
return fz_runetochar(str, c);
}
/*
Count how many runes the UTF-8 encoded string
consists of.
s: The UTF-8 encoded, NUL-terminated text string.
Returns the number of runes in the string.
*/
int
fz_utflen(const char *s)
{
int c, n, rune;
n = 0;
for(;;) {
c = *(const unsigned char*)s;
if(c < Runeself) {
if(c == 0)
return n;
s++;
} else
s += fz_chartorune(&rune, s);
n++;
}
return 0;
}
/*
Range checking atof
*/
float fz_atof(const char *s)
{
float result;
if (s == NULL)
return 0;
errno = 0;
result = fz_strtof(s, NULL);
if ((errno == ERANGE && result == 0) || isnan(result))
/* Return 1.0 on underflow, as it's a small known value that won't cause a divide by 0. */
return 1;
result = fz_clamp(result, -FLT_MAX, FLT_MAX);
return result;
}
/*
atoi that copes with NULL
*/
int fz_atoi(const char *s)
{
if (s == NULL)
return 0;
return atoi(s);
}
int64_t fz_atoi64(const char *s)
{
if (s == NULL)
return 0;
return atoll(s);
}
/*
Check and parse string into page ranges:
( ','? ([0-9]+|'N') ( '-' ([0-9]+|N) )? )+
*/
int fz_is_page_range(fz_context *ctx, const char *s)
{
/* TODO: check the actual syntax... */
while (*s)
{
if ((*s < '0' || *s > '9') && *s != 'N' && *s != '-' && *s != ',')
return 0;
s++;
}
return 1;
}
const char *fz_parse_page_range(fz_context *ctx, const char *s, int *a, int *b, int n)
{
if (!s || !s[0])
return NULL;
if (s[0] == ',')
s += 1;
if (s[0] == 'N')
{
*a = n;
s += 1;
}
else
*a = strtol(s, (char**)&s, 10);
if (s[0] == '-')
{
if (s[1] == 'N')
{
*b = n;
s += 2;
}
else
*b = strtol(s+1, (char**)&s, 10);
}
else
*b = *a;
*a = fz_clampi(*a, 1, n);
*b = fz_clampi(*b, 1, n);
return s;
}
/* memmem from musl */
#define MAX(a,b) ((a)>(b)?(a):(b))
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
static char *twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
{
uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
for (h++, k--; k; k--, hw = hw<<8 | *++h)
if (hw == nw) return (char *)h-1;
return 0;
}
static char *threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
{
uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8;
uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8;
for (h+=2, k-=2; k; k--, hw = (hw|*++h)<<8)
if (hw == nw) return (char *)h-2;
return 0;
}
static char *fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
{
uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
for (h+=3, k-=3; k; k--, hw = hw<<8 | *++h)
if (hw == nw) return (char *)h-3;
return 0;
}
static char *twoway_memmem(const unsigned char *h, const unsigned char *z, const unsigned char *n, size_t l)
{
size_t i, ip, jp, k, p, ms, p0, mem, mem0;
size_t byteset[32 / sizeof(size_t)] = { 0 };
size_t shift[256];
/* Computing length of needle and fill shift table */
for (i=0; i<l; i++)
BITOP(byteset, n[i], |=), shift[n[i]] = i+1;
/* Compute maximal suffix */
ip = -1; jp = 0; k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else k++;
} else if (n[ip+k] > n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
ms = ip;
p0 = p;
/* And with the opposite comparison */
ip = -1; jp = 0; k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else k++;
} else if (n[ip+k] < n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
if (ip+1 > ms+1) ms = ip;
else p = p0;
/* Periodic needle? */
if (memcmp(n, n+p, ms+1)) {
mem0 = 0;
p = MAX(ms, l-ms-1) + 1;
} else mem0 = l-p;
mem = 0;
/* Search loop */
for (;;) {
/* If remainder of haystack is shorter than needle, done */
if ((size_t)(z-h) < l) return 0;
/* Check last byte first; advance by shift on mismatch */
if (BITOP(byteset, h[l-1], &)) {
k = l-shift[h[l-1]];
if (k) {
if (mem0 && mem && k < p) k = l-p;
h += k;
mem = 0;
continue;
}
} else {
h += l;
mem = 0;
continue;
}
/* Compare right half */
for (k=MAX(ms+1,mem); k<l && n[k] == h[k]; k++);
if (k < l) {
h += k-ms;
mem = 0;
continue;
}
/* Compare left half */
for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
if (k <= mem) return (char *)h;
h += p;
mem = mem0;
}
}
/*
Find the start of the first occurrence of the substring needle in haystack.
*/
void *fz_memmem(const void *h0, size_t k, const void *n0, size_t l)
{
const unsigned char *h = h0, *n = n0;
/* Return immediately on empty needle */
if (!l) return (void *)h;
/* Return immediately when needle is longer than haystack */
if (k<l) return 0;
/* Use faster algorithms for short needles */
h = memchr(h0, *n, k);
if (!h || l==1) return (void *)h;
k -= h - (const unsigned char *)h0;
if (k<l) return 0;
if (l==2) return twobyte_memmem(h, k, n);
if (l==3) return threebyte_memmem(h, k, n);
if (l==4) return fourbyte_memmem(h, k, n);
return twoway_memmem(h, h+k, n, l);
}