mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
5142 lines
137 KiB
C
5142 lines
137 KiB
C
/* ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c library
|
|
//
|
|
// = FILENAME
|
|
// utility.c
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================ */
|
|
|
|
/*
|
|
#define DEBUG_DEBUG
|
|
*/
|
|
|
|
#include <ulib/base/utility.h>
|
|
|
|
#include <sched.h>
|
|
|
|
#ifdef HAVE_SYSEXITS_H
|
|
# include <sysexits.h>
|
|
#else
|
|
# include <ulib/base/replace/sysexits.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_FNMATCH
|
|
# include <fnmatch.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_THREAD
|
|
# if defined(__NetBSD__) || defined(__UNIKERNEL__)
|
|
# include <lwp.h>
|
|
# endif
|
|
# ifdef HAVE_SYS_SYSCALL_H
|
|
# include <sys/syscall.h>
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef _MSWINDOWS_
|
|
# include <pwd.h>
|
|
# if defined(U_LINUX) && defined(HAVE_LIBCAP)
|
|
# include <sys/prctl.h>
|
|
# include <sys/capability.h>
|
|
# ifdef SECBIT_KEEP_CAPS
|
|
# define U_PR_SET_KEEPCAPS SECBIT_KEEP_CAPS
|
|
# else
|
|
# define U_PR_SET_KEEPCAPS PR_SET_KEEPCAPS
|
|
# endif
|
|
# elif defined(__APPLE__)
|
|
# include <mach/mach.h>
|
|
# include <mach/mach_port.h>
|
|
# include <mach/mach_traps.h>
|
|
# endif
|
|
#endif
|
|
|
|
__pure unsigned long u_hex2int(const char* restrict s, uint32_t len) /* handle up to 16 digits */
|
|
{
|
|
unsigned long val = 0UL;
|
|
|
|
U_INTERNAL_TRACE("u_hex2int(%p,%u)", s, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(len, 0)
|
|
|
|
#ifndef U_COVERITY_FALSE_POSITIVE /* Control flow issues (MISSING_BREAK) */
|
|
switch (len)
|
|
{
|
|
case 16: val = (val << 4) | u__hexc2int(s[len-16]); /* FALL THRU */
|
|
case 15: val = (val << 4) | u__hexc2int(s[len-15]); /* FALL THRU */
|
|
case 14: val = (val << 4) | u__hexc2int(s[len-14]); /* FALL THRU */
|
|
case 13: val = (val << 4) | u__hexc2int(s[len-13]); /* FALL THRU */
|
|
case 12: val = (val << 4) | u__hexc2int(s[len-12]); /* FALL THRU */
|
|
case 11: val = (val << 4) | u__hexc2int(s[len-11]); /* FALL THRU */
|
|
case 10: val = (val << 4) | u__hexc2int(s[len-10]); /* FALL THRU */
|
|
case 9: val = (val << 4) | u__hexc2int(s[len- 9]); /* FALL THRU */
|
|
case 8: val = (val << 4) | u__hexc2int(s[len- 8]); /* FALL THRU */
|
|
case 7: val = (val << 4) | u__hexc2int(s[len- 7]); /* FALL THRU */
|
|
case 6: val = (val << 4) | u__hexc2int(s[len- 6]); /* FALL THRU */
|
|
case 5: val = (val << 4) | u__hexc2int(s[len- 5]); /* FALL THRU */
|
|
case 4: val = (val << 4) | u__hexc2int(s[len- 4]); /* FALL THRU */
|
|
case 3: val = (val << 4) | u__hexc2int(s[len- 3]); /* FALL THRU */
|
|
case 2: val = (val << 4) | u__hexc2int(s[len- 2]); /* FALL THRU */
|
|
case 1: val = (val << 4) | u__hexc2int(s[len- 1]); /* FALL THRU */
|
|
}
|
|
#endif
|
|
|
|
U_INTERNAL_PRINT("val = %lu", val)
|
|
|
|
return val;
|
|
}
|
|
|
|
__pure unsigned long u__strtoul(const char* restrict s, uint32_t len) /* handle up to 10 digits */
|
|
{
|
|
unsigned long val = 0UL;
|
|
|
|
U_INTERNAL_TRACE("u__strtoul(%p,%u)", s, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(len, 0)
|
|
|
|
#ifndef U_COVERITY_FALSE_POSITIVE /* Control flow issues (MISSING_BREAK) */
|
|
switch (len)
|
|
{
|
|
case 10: val += (s[len-10] - '0') * 1000000000UL; /* FALL THRU */
|
|
case 9: val += (s[len- 9] - '0') * 100000000UL; /* FALL THRU */
|
|
case 8: val += (s[len- 8] - '0') * 10000000UL; /* FALL THRU */
|
|
case 7: val += (s[len- 7] - '0') * 1000000UL; /* FALL THRU */
|
|
case 6: val += (s[len- 6] - '0') * 100000UL; /* FALL THRU */
|
|
case 5: val += (s[len- 5] - '0') * 10000UL; /* FALL THRU */
|
|
case 4: val += (s[len- 4] - '0') * 1000UL; /* FALL THRU */
|
|
case 3: val += (s[len- 3] - '0') * 100UL; /* FALL THRU */
|
|
case 2: val += (s[len- 2] - '0') * 10UL; /* FALL THRU */
|
|
case 1: val += (s[len- 1] - '0');
|
|
}
|
|
#endif
|
|
|
|
U_INTERNAL_PRINT("val = %lu", val)
|
|
|
|
return val;
|
|
}
|
|
|
|
__pure uint64_t u__strtoull(const char* restrict s, uint32_t len)
|
|
{
|
|
uint64_t val = 0UL;
|
|
|
|
U_INTERNAL_TRACE("u__strtoull(%p,%u)", s, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(len, 0)
|
|
|
|
#ifndef U_COVERITY_FALSE_POSITIVE /* Control flow issues (MISSING_BREAK) */
|
|
switch (len)
|
|
{
|
|
case 20: val += (s[len-20] - '0') * 10000000000000000000ULL; /* FALL THRU */
|
|
case 19: val += (s[len-19] - '0') * 1000000000000000000ULL; /* FALL THRU */
|
|
case 18: val += (s[len-18] - '0') * 100000000000000000ULL; /* FALL THRU */
|
|
case 17: val += (s[len-17] - '0') * 10000000000000000ULL; /* FALL THRU */
|
|
case 16: val += (s[len-16] - '0') * 1000000000000000ULL; /* FALL THRU */
|
|
case 15: val += (s[len-15] - '0') * 100000000000000ULL; /* FALL THRU */
|
|
case 14: val += (s[len-14] - '0') * 10000000000000ULL; /* FALL THRU */
|
|
case 13: val += (s[len-13] - '0') * 1000000000000ULL; /* FALL THRU */
|
|
case 12: val += (s[len-12] - '0') * 100000000000ULL; /* FALL THRU */
|
|
case 11: val += (s[len-11] - '0') * 10000000000ULL; /* FALL THRU */
|
|
case 10: val += (s[len-10] - '0') * 1000000000ULL; /* FALL THRU */
|
|
case 9: val += (s[len- 9] - '0') * 100000000ULL; /* FALL THRU */
|
|
case 8: val += (s[len- 8] - '0') * 10000000ULL; /* FALL THRU */
|
|
case 7: val += (s[len- 7] - '0') * 1000000ULL; /* FALL THRU */
|
|
case 6: val += (s[len- 6] - '0') * 100000ULL; /* FALL THRU */
|
|
case 5: val += (s[len- 5] - '0') * 10000ULL; /* FALL THRU */
|
|
case 4: val += (s[len- 4] - '0') * 1000ULL; /* FALL THRU */
|
|
case 3: val += (s[len- 3] - '0') * 100ULL; /* FALL THRU */
|
|
case 2: val += (s[len- 2] - '0') * 10ULL; /* FALL THRU */
|
|
case 1: val += (s[len- 1] - '0'); /* FALL THRU */
|
|
}
|
|
#endif
|
|
|
|
U_INTERNAL_PRINT("val = %llu", val)
|
|
|
|
return val;
|
|
}
|
|
|
|
unsigned long u_strtoulp(const char** restrict s)
|
|
{
|
|
const char* restrict ptr;
|
|
const char* restrict p = *s;
|
|
|
|
U_INTERNAL_TRACE("u_strtolp(%p)", s)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
if (u__isdigitw0(*p) == false &&
|
|
u__isspace(*++p))
|
|
{
|
|
/* NB: we have something as '0 12 ..' */
|
|
|
|
*s = p+1;
|
|
|
|
return 0UL;
|
|
}
|
|
|
|
ptr = p;
|
|
|
|
while (u__isdigit(*p)) ++p;
|
|
|
|
*s = p+1;
|
|
|
|
return u_strtoul(ptr, p);
|
|
}
|
|
|
|
uint64_t u_strtoullp(const char** restrict s)
|
|
{
|
|
const char* restrict ptr;
|
|
const char* restrict p = *s;
|
|
|
|
U_INTERNAL_TRACE("u_strtollp(%p)", s)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
if (u__isdigitw0(*p) == false &&
|
|
u__isspace(*++p))
|
|
{
|
|
/* NB: we have something as '0 12 ..' */
|
|
|
|
*s = p+1;
|
|
|
|
return 0ULL;
|
|
}
|
|
|
|
ptr = p;
|
|
|
|
while (u__isdigit(*p)) ++p;
|
|
|
|
*s = p+1;
|
|
|
|
return u_strtoull(ptr, p);
|
|
}
|
|
|
|
__pure long u__strtol(const char* restrict s, uint32_t len)
|
|
{
|
|
int sign = 1;
|
|
|
|
U_INTERNAL_TRACE("u__strtol(%p,%u)", s, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(len, 0)
|
|
|
|
// while (u__isdigit(*s) == false) ++s;
|
|
|
|
if (*s == '-')
|
|
{
|
|
++s;
|
|
--len;
|
|
|
|
sign = -1;
|
|
}
|
|
else
|
|
{
|
|
if (*s == '+' ||
|
|
*s == '0')
|
|
{
|
|
++s;
|
|
--len;
|
|
}
|
|
}
|
|
|
|
return (len ? sign * u__strtoul(s, len) : 0L);
|
|
}
|
|
|
|
__pure int64_t u__strtoll(const char* restrict s, uint32_t len)
|
|
{
|
|
int sign = 1;
|
|
|
|
U_INTERNAL_TRACE("u__strtoll(%p,%u)", s, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(len, 0)
|
|
|
|
// while (u__isdigit(*s) == false) ++s;
|
|
|
|
if (*s == '-')
|
|
{
|
|
++s;
|
|
--len;
|
|
|
|
sign = -1;
|
|
}
|
|
else
|
|
{
|
|
if (*s == '+' ||
|
|
*s == '0')
|
|
{
|
|
++s;
|
|
--len;
|
|
}
|
|
}
|
|
|
|
return (len ? sign * u__strtoull(s, len) : 0LL);
|
|
}
|
|
|
|
__pure unsigned long u__atoi(const char* restrict s)
|
|
{
|
|
char c;
|
|
const char* restrict ptr;
|
|
|
|
U_INTERNAL_TRACE("u__atoi(%12s)", s)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
|
|
c = *s;
|
|
|
|
if (u__isalpha(c) ||
|
|
(c == '0' &&
|
|
u__isdigit(*++s) == false))
|
|
{
|
|
return 0UL;
|
|
}
|
|
|
|
while (u__isdigitw0(*s) == false) ++s;
|
|
|
|
ptr = s;
|
|
|
|
while (u__isdigit(*s)) ++s;
|
|
|
|
return u__strtoul(ptr, s-ptr);
|
|
}
|
|
|
|
__pure int8_t u_log2(uint64_t value)
|
|
{
|
|
static int8_t table[64] = {
|
|
63, 0, 58, 1, 59, 47, 53, 2,
|
|
60, 39, 48, 27, 54, 33, 42, 3,
|
|
61, 51, 37, 40, 49, 18, 28, 20,
|
|
55, 30, 34, 11, 43, 14, 22, 4,
|
|
62, 57, 46, 52, 38, 26, 32, 41,
|
|
50, 36, 17, 19, 29, 10, 13, 21,
|
|
56, 45, 25, 31, 35, 16, 9, 12,
|
|
44, 24, 15, 8, 23, 7, 6, 5 };
|
|
|
|
U_INTERNAL_TRACE("u_log2(%llu)", value)
|
|
|
|
value |= value >> 1;
|
|
value |= value >> 2;
|
|
value |= value >> 4;
|
|
value |= value >> 8;
|
|
value |= value >> 16;
|
|
value |= value >> 32;
|
|
|
|
return table[((value - (value >> 1)) * 0x07EDD5E59A4E28C2) >> 58];
|
|
}
|
|
|
|
/* To avoid libc locale overhead */
|
|
|
|
__pure int u__strncasecmp(const char* restrict s1, const char* restrict s2, size_t n)
|
|
{
|
|
U_INTERNAL_TRACE("u__strncasecmp(%p,%p,%lu)", s1, s2, n)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
U_INTERNAL_ASSERT_POINTER(s1)
|
|
U_INTERNAL_ASSERT_POINTER(s2)
|
|
|
|
while (n)
|
|
{
|
|
char c1 = u__tolower(*s1),
|
|
c2 = u__tolower(*s2);
|
|
|
|
if (c1 == c2)
|
|
{
|
|
if (c1)
|
|
{
|
|
--n;
|
|
++s1;
|
|
++s2;
|
|
|
|
continue;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return (c1 - c2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t u_gettid(void)
|
|
{
|
|
#ifndef ENABLE_THREAD
|
|
return getpid();
|
|
#else
|
|
uint32_t tid =
|
|
# ifdef _MSWINDOWS_
|
|
GetCurrentThreadId();
|
|
# elif defined(HAVE_PTHREAD_GETTHREADID_NP)
|
|
pthread_getthreadid_np();
|
|
# elif defined(U_LINUX)
|
|
syscall(SYS_gettid);
|
|
# elif defined(__sun)
|
|
pthread_self();
|
|
# elif defined(__APPLE__)
|
|
mach_thread_self();
|
|
mach_port_deallocate(mach_task_self(), tid);
|
|
# elif defined(__NetBSD__) || defined(__UNIKERNEL__)
|
|
_lwp_self();
|
|
# elif defined(__FreeBSD__)
|
|
thr_self(&tid);
|
|
# elif defined(__DragonFly__)
|
|
lwp_gettid();
|
|
# endif
|
|
return tid;
|
|
#endif
|
|
}
|
|
|
|
/* Security functions */
|
|
|
|
#ifndef _MSWINDOWS_
|
|
static uid_t real_uid = (uid_t)-1;
|
|
static gid_t real_gid = (gid_t)-1;
|
|
static uid_t effective_uid = (uid_t)-1;
|
|
static gid_t effective_gid = (gid_t)-1;
|
|
#endif
|
|
|
|
void u_init_security(void)
|
|
{
|
|
/**
|
|
* Run this at the beginning of the program to initialize this code and
|
|
* to drop privileges before someone uses them to shoot us in the foot
|
|
*/
|
|
#ifndef _MSWINDOWS_
|
|
int leffective_uid;
|
|
|
|
U_INTERNAL_TRACE("u_init_security()")
|
|
|
|
alarm(0); /* can be inherited from parent process */
|
|
|
|
real_uid = getuid();
|
|
leffective_uid = geteuid();
|
|
|
|
/* sanity check */
|
|
|
|
if (leffective_uid != (int)real_uid &&
|
|
leffective_uid != 0)
|
|
{
|
|
U_WARNING("Setuid but not to root (uid=%ld, euid=%d), dropping setuid privileges now", (long) real_uid, leffective_uid);
|
|
|
|
u_never_need_root();
|
|
}
|
|
else
|
|
{
|
|
effective_uid = leffective_uid;
|
|
}
|
|
|
|
real_gid = getgid();
|
|
effective_gid = getegid();
|
|
|
|
u_dont_need_root();
|
|
u_dont_need_group();
|
|
#endif
|
|
}
|
|
|
|
/* Temporarily gain root privileges */
|
|
|
|
void u_need_root(bool necessary)
|
|
{
|
|
U_INTERNAL_TRACE("u_need_root(%d)", necessary)
|
|
|
|
#ifndef _MSWINDOWS_
|
|
U_INTERNAL_PRINT("(_euid_=%d, uid=%d), current=%d", effective_uid, real_uid, geteuid())
|
|
|
|
if (effective_uid)
|
|
{
|
|
if (necessary) U_ERROR("Require root privilege but not setuid root");
|
|
U_DEBUG("Require root privilege but not setuid root");
|
|
|
|
return;
|
|
}
|
|
|
|
if (real_uid == (uid_t)(-1)) U_ERROR("u_init_security() not called");
|
|
|
|
if (geteuid() == 0) return; /* nothing to do */
|
|
|
|
if (seteuid(effective_uid) == -1 ||
|
|
geteuid() != 0)
|
|
{
|
|
if (necessary) U_ERROR("Did not get root privilege");
|
|
U_DEBUG("Did not get root privilege");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Temporarily drop root privileges */
|
|
|
|
void u_dont_need_root(void)
|
|
{
|
|
U_INTERNAL_TRACE("u_dont_need_root()")
|
|
|
|
#ifndef _MSWINDOWS_
|
|
U_INTERNAL_PRINT("(_euid_=%d, uid=%d), current=%d", effective_uid, real_uid, geteuid())
|
|
|
|
if (effective_uid) return;
|
|
|
|
if (real_uid == (uid_t)(-1)) U_ERROR("u_init_security() not called");
|
|
|
|
if (geteuid() != 0) return; /* nothing to do */
|
|
|
|
if (seteuid(real_uid) == -1 ||
|
|
geteuid() != real_uid)
|
|
{
|
|
U_ERROR("Did not drop root privilege");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Permanently drop root privileges */
|
|
|
|
void u_never_need_root(void)
|
|
{
|
|
#ifndef _MSWINDOWS_
|
|
# if defined(U_LINUX) && defined(HAVE_LIBCAP)
|
|
/*
|
|
cap_list[] = {
|
|
{"chown", CAP_CHOWN},
|
|
{"dac_override", CAP_DAC_OVERRIDE},
|
|
{"dac_read_search", CAP_DAC_READ_SEARCH},
|
|
{"fowner", CAP_FOWNER},
|
|
{"fsetid", CAP_FSETID},
|
|
{"kill", CAP_KILL},
|
|
{"setgid", CAP_SETGID},
|
|
{"setuid", CAP_SETUID},
|
|
{"setpcap", CAP_SETPCAP},
|
|
{"linux_immutable", CAP_LINUX_IMMUTABLE},
|
|
{"net_bind_service", CAP_NET_BIND_SERVICE},
|
|
{"net_broadcast", CAP_NET_BROADCAST},
|
|
{"net_admin", CAP_NET_ADMIN},
|
|
{"net_raw", CAP_NET_RAW},
|
|
{"ipc_lock", CAP_IPC_LOCK},
|
|
{"ipc_owner", CAP_IPC_OWNER},
|
|
{"sys_module", CAP_SYS_MODULE},
|
|
{"sys_rawio", CAP_SYS_RAWIO},
|
|
{"sys_chroot", CAP_SYS_CHROOT},
|
|
{"sys_ptrace", CAP_SYS_PTRACE},
|
|
{"sys_pacct", CAP_SYS_PACCT},
|
|
{"sys_admin", CAP_SYS_ADMIN},
|
|
{"sys_boot", CAP_SYS_BOOT},
|
|
{"sys_nice", CAP_SYS_NICE},
|
|
{"sys_resource", CAP_SYS_RESOURCE},
|
|
{"sys_time", CAP_SYS_TIME},
|
|
{"sys_tty_config", CAP_SYS_TTY_CONFIG},
|
|
{"mknod", CAP_MKNOD},
|
|
#ifdef CAP_LEASE
|
|
{"lease", CAP_LEASE},
|
|
#endif
|
|
#ifdef CAP_AUDIT_WRITE
|
|
{"audit_write", CAP_AUDIT_WRITE},
|
|
#endif
|
|
#ifdef CAP_AUDIT_CONTROL
|
|
{"audit_control", CAP_AUDIT_CONTROL},
|
|
#endif
|
|
#ifdef CAP_SETFCAP
|
|
{"setfcap", CAP_SETFCAP},
|
|
#endif
|
|
#ifdef CAP_MAC_OVERRIDE
|
|
{"mac_override", CAP_MAC_OVERRIDE},
|
|
#endif
|
|
#ifdef CAP_MAC_ADMIN
|
|
{"mac_admin", CAP_MAC_ADMIN},
|
|
#endif
|
|
#ifdef CAP_SYSLOG
|
|
{"syslog", CAP_SYSLOG},
|
|
#endif
|
|
#ifdef CAP_WAKE_ALARM
|
|
{"wake_alarm", CAP_WAKE_ALARM},
|
|
#endif
|
|
{0, -1}
|
|
};
|
|
*/
|
|
|
|
# ifndef DEBUG
|
|
cap_value_t minimal_cap_values[] = { CAP_SETUID, CAP_SETGID, CAP_SETPCAP };
|
|
# else
|
|
cap_value_t minimal_cap_values[] = { CAP_SETUID, CAP_SETGID, CAP_SETPCAP, CAP_SYS_PTRACE };
|
|
# endif
|
|
|
|
cap_t caps = cap_init();
|
|
|
|
if (caps == 0) U_ERROR_SYSCALL("cap_init() failed");
|
|
|
|
(void) cap_clear(caps);
|
|
|
|
(void) cap_set_flag(caps, CAP_EFFECTIVE, 3, minimal_cap_values, CAP_SET);
|
|
(void) cap_set_flag(caps, CAP_PERMITTED, 3, minimal_cap_values, CAP_SET);
|
|
(void) cap_set_flag(caps, CAP_INHERITABLE, 3, minimal_cap_values, CAP_SET);
|
|
|
|
if (cap_set_proc(caps) < 0) U_ERROR_SYSCALL("cap_set_proc() failed");
|
|
|
|
(void) cap_free(caps);
|
|
|
|
if (prctl(U_PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) U_ERROR_SYSCALL("prctl() failed");
|
|
# endif
|
|
|
|
U_INTERNAL_TRACE("u_never_need_root()")
|
|
|
|
U_INTERNAL_PRINT("_euid_ = %d, uid = %d", effective_uid, real_uid)
|
|
|
|
if (real_uid == (uid_t)(-1)) U_ERROR("u_init_security() not called");
|
|
|
|
if (geteuid() == 0) (void) setuid(real_uid);
|
|
|
|
if (geteuid() != real_uid ||
|
|
getuid() != real_uid)
|
|
{
|
|
U_ERROR_SYSCALL("Did not drop root privilege");
|
|
}
|
|
|
|
effective_uid = real_uid;
|
|
#endif
|
|
}
|
|
|
|
/* Temporarily gain group privileges */
|
|
|
|
void u_need_group(bool necessary)
|
|
{
|
|
U_INTERNAL_TRACE("u_need_group(%d)", necessary)
|
|
|
|
#ifndef _MSWINDOWS_
|
|
U_INTERNAL_PRINT("(egid_=%d, gid=%d)", effective_gid, real_gid)
|
|
|
|
if (real_gid == (gid_t)(-1)) U_ERROR("u_init_security() not called");
|
|
|
|
if (getegid() == effective_gid) return; /* nothing to do */
|
|
|
|
if (setegid(effective_gid) == -1 ||
|
|
getegid() != effective_gid)
|
|
{
|
|
if (necessary) U_ERROR("Did not get group privilege");
|
|
U_DEBUG("Did not get group privilege");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Temporarily drop group privileges */
|
|
|
|
void u_dont_need_group(void)
|
|
{
|
|
U_INTERNAL_TRACE("u_dont_need_group()")
|
|
|
|
#ifndef _MSWINDOWS_
|
|
U_INTERNAL_PRINT("egid_ = %d, gid = %d", effective_gid, real_gid)
|
|
|
|
if (real_gid == (gid_t)(-1)) U_ERROR("u_init_security() not called");
|
|
|
|
if (getegid() != effective_gid) return; /* nothing to do */
|
|
|
|
if (setegid(real_gid) == -1 ||
|
|
getegid() != real_gid)
|
|
{
|
|
U_ERROR_SYSCALL("Did not drop group privilege");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Permanently drop group privileges */
|
|
|
|
void u_never_need_group(void)
|
|
{
|
|
U_INTERNAL_TRACE("u_never_need_group()")
|
|
|
|
#ifndef _MSWINDOWS_
|
|
U_INTERNAL_PRINT("egid_ = %d, gid = %d", effective_gid, real_gid)
|
|
|
|
if (real_gid == (gid_t)(-1)) U_ERROR("u_init_security() not called");
|
|
|
|
if (getegid() != effective_gid) (void) setgid(real_gid);
|
|
|
|
if (getegid() != real_gid ||
|
|
getgid() != real_gid)
|
|
{
|
|
U_ERROR_SYSCALL("Did not drop group privilege");
|
|
}
|
|
|
|
effective_gid = real_gid;
|
|
#endif
|
|
}
|
|
|
|
/* Change the current working directory to the `user` user's home dir, and downgrade security to that user account */
|
|
|
|
bool u_runAsUser(const char* restrict user, bool change_dir)
|
|
{
|
|
#ifdef _MSWINDOWS_
|
|
return false;
|
|
#else
|
|
struct passwd* restrict pw;
|
|
|
|
U_INTERNAL_TRACE("u_runAsUser(%s,%d)", user, change_dir)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(user)
|
|
|
|
if (!(pw = getpwnam(user)) ||
|
|
setgid(pw->pw_gid) ||
|
|
setuid(pw->pw_uid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
(void) memcpy(u_user_name, user, (u_user_name_len = u__strlen(user, __PRETTY_FUNCTION__))+1); /* change user name */
|
|
|
|
if (change_dir &&
|
|
pw->pw_dir &&
|
|
pw->pw_dir[0])
|
|
{
|
|
(void) chdir(pw->pw_dir);
|
|
|
|
u_getcwd(); /* get current working directory */
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(strcmp(pw->pw_dir,u_cwd), 0)
|
|
}
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
char* u_getPathRelativ(const char* restrict path, uint32_t* restrict ptr_path_len)
|
|
{
|
|
U_INTERNAL_TRACE("u_getPathRelativ(%s,%u)", path, *ptr_path_len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(path)
|
|
U_INTERNAL_ASSERT_POINTER(u_cwd)
|
|
|
|
U_INTERNAL_PRINT("u_cwd = %s", u_cwd)
|
|
|
|
if (path[0] == u_cwd[0])
|
|
{
|
|
uint32_t path_len = *ptr_path_len;
|
|
|
|
while (path[path_len-1] == '/') --path_len;
|
|
|
|
if (path_len >= u_cwd_len &&
|
|
memcmp(path, u_cwd, u_cwd_len) == 0)
|
|
{
|
|
if (path_len == u_cwd_len)
|
|
{
|
|
path = ".";
|
|
path_len = 1;
|
|
}
|
|
else if ( u_cwd_len == 1 ||
|
|
path[u_cwd_len] == '/')
|
|
{
|
|
uint32_t len = u_cwd_len + (u_cwd_len > 1);
|
|
|
|
path += len;
|
|
path_len -= len;
|
|
|
|
while (path[0] == '/')
|
|
{
|
|
++path;
|
|
--path_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
*ptr_path_len = path_len;
|
|
|
|
U_INTERNAL_PRINT("path(%u) = %.*s", path_len, path_len, path)
|
|
}
|
|
|
|
if (path[0] == '.' &&
|
|
path[1] == '/')
|
|
{
|
|
path += 2;
|
|
*ptr_path_len -= 2;
|
|
|
|
while (path[0] == '/')
|
|
{
|
|
++path;
|
|
--(*ptr_path_len);
|
|
}
|
|
|
|
U_INTERNAL_PRINT("path(%u) = %.*s", *ptr_path_len, *ptr_path_len, path)
|
|
}
|
|
|
|
return (char*)path;
|
|
}
|
|
|
|
/* find sequence of U_LF2 or U_CRLF2 */
|
|
|
|
__pure uint32_t u_findEndHeader(const char* restrict str, uint32_t n)
|
|
{
|
|
const char* restrict p;
|
|
const char* restrict end = str + n;
|
|
const char* restrict ptr = str;
|
|
|
|
uint32_t pos, endHeader = U_NOT_FOUND;
|
|
|
|
U_INTERNAL_TRACE("u_findEndHeader(%.*s,%u)", U_min(n,128), str, n)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(str)
|
|
|
|
while (ptr < end)
|
|
{
|
|
p = (const char* restrict) memchr(ptr, '\n', end - ptr);
|
|
|
|
if (p == 0) break;
|
|
|
|
if (u_get_unalignedp32(p-1) == U_MULTICHAR_CONSTANT32('\r','\n','\r','\n'))
|
|
{
|
|
pos = p - str + 3;
|
|
|
|
if (pos <= n)
|
|
{
|
|
endHeader = pos;
|
|
|
|
/* U_line_terminator_len = 2; */
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* \n\n (U_LF2) */
|
|
|
|
if (p[1] == '\n')
|
|
{
|
|
U_INTERNAL_ASSERT_EQUALS(u_get_unalignedp16(p), U_MULTICHAR_CONSTANT16('\n','\n'))
|
|
|
|
pos = p - str + 2;
|
|
|
|
if (pos <= n)
|
|
{
|
|
endHeader = pos;
|
|
|
|
/* U_line_terminator_len = 1; */
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
ptr = p + 1;
|
|
}
|
|
|
|
return endHeader;
|
|
}
|
|
|
|
/* Determine the width of the terminal we're running on */
|
|
|
|
__pure int u_getScreenWidth(void)
|
|
{
|
|
#ifdef TIOCGWINSZ
|
|
struct winsize wsz;
|
|
#endif
|
|
|
|
U_INTERNAL_TRACE("u_getScreenWidth()")
|
|
|
|
/* If there's a way to get the terminal size using POSIX tcgetattr(), somebody please tell me */
|
|
|
|
#ifdef TIOCGWINSZ
|
|
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &wsz) != -1) /* most likely ENOTTY */
|
|
{
|
|
U_INTERNAL_PRINT("wsz.ws_col = %d", wsz.ws_col)
|
|
|
|
return wsz.ws_col;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Calculate the download rate and trim it as appropriate for the speed. Appropriate means that
|
|
* if rate is greater than 1K/s, kilobytes are used, and if rate is greater than 1MB/s, megabytes
|
|
* are used. UNITS is zero for B/s, one for KB/s, two for MB/s, and three for GB/s
|
|
*/
|
|
|
|
double u_calcRate(uint64_t bytes, uint32_t msecs, int* restrict units)
|
|
{
|
|
int i;
|
|
double rate = (double)1000. * bytes / (double)msecs;
|
|
|
|
U_INTERNAL_TRACE("u_calcRate(%u,%u,%p)", bytes, msecs, units)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(units)
|
|
U_INTERNAL_ASSERT_MAJOR(bytes, 0)
|
|
U_INTERNAL_ASSERT_MAJOR(msecs, 0)
|
|
|
|
for (i = 0; rate > 1024. && u_short_units[i+1]; ++i) rate /= 1024.;
|
|
|
|
*units = i;
|
|
|
|
U_INTERNAL_PRINT("rate = %7.2f%s", rate, u_short_units[i])
|
|
|
|
return rate;
|
|
}
|
|
|
|
uint32_t u_printSize(char* restrict buffer, uint64_t bytes)
|
|
{
|
|
int units;
|
|
double size;
|
|
uint32_t len;
|
|
|
|
U_INTERNAL_TRACE("u_printSize(%p,%llu)", buffer, bytes)
|
|
|
|
if (bytes == 0)
|
|
{
|
|
u_put_unalignedp32(buffer, U_MULTICHAR_CONSTANT32('0',' ','B','y'));
|
|
u_put_unalignedp16(buffer+4, U_MULTICHAR_CONSTANT16('t','e'));
|
|
|
|
len = 6;
|
|
}
|
|
else
|
|
{
|
|
size = u_calcRate(bytes, 1000, &units);
|
|
|
|
len = (units ? sprintf(buffer, "%5.2f %s", size, u_short_units[units])
|
|
: sprintf(buffer, "%7.0f Bytes", size));
|
|
}
|
|
|
|
buffer[len] = '\0';
|
|
|
|
return len;
|
|
}
|
|
|
|
uint32_t u_memory_dump(char* restrict bp, unsigned char* restrict cp, uint32_t n)
|
|
{
|
|
char text[16];
|
|
unsigned char c;
|
|
unsigned int offset = 0;
|
|
char* restrict start_buffer = bp;
|
|
int i, j, line, remain, _remain = 16;
|
|
bool prev_is_zero = false, print_nothing = false;
|
|
|
|
static char bufzero[16];
|
|
|
|
U_INTERNAL_TRACE("u_memory_dump(%p,%p,%u)", bp, cp, n)
|
|
|
|
line = n / 16;
|
|
remain = n % 16;
|
|
|
|
for (i = 0; i < line; ++i, offset += 16)
|
|
{
|
|
if (memcmp(cp, bufzero, sizeof(bufzero))) prev_is_zero = print_nothing = false;
|
|
else
|
|
{
|
|
if (prev_is_zero == false) prev_is_zero = true;
|
|
else
|
|
{
|
|
if (print_nothing == false)
|
|
{
|
|
print_nothing = true;
|
|
|
|
u_put_unalignedp16(bp, U_MULTICHAR_CONSTANT16('*','\n'));
|
|
|
|
bp += 2;
|
|
}
|
|
|
|
cp += 16;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
iteration:
|
|
(void) sprintf(bp, "%07X|", offset);
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(strlen(bp), 8)
|
|
|
|
bp += 8;
|
|
|
|
for (j = 0; j < 16; ++j)
|
|
{
|
|
if (j < _remain)
|
|
{
|
|
c = *cp++;
|
|
|
|
u_put_unalignedp16(bp, U_MULTICHAR_CONSTANT16("0123456789abcdef"[((c >> 4) & 0x0F)],
|
|
"0123456789abcdef"[( c & 0x0F)]));
|
|
|
|
bp += 2;
|
|
|
|
text[j] = (u__isprint(c) ? c : '.');
|
|
}
|
|
else
|
|
{
|
|
u_put_unalignedp16(bp, U_MULTICHAR_CONSTANT16(' ',' '));
|
|
|
|
bp += 2;
|
|
|
|
text[j] = ' ';
|
|
}
|
|
|
|
*bp++ = (j == 7 ? ':' : ' ');
|
|
}
|
|
|
|
*bp++ = '|';
|
|
|
|
for (j = 0; j < 16; ++j) *bp++ = text[j];
|
|
|
|
*bp++ = '\n';
|
|
}
|
|
|
|
if (remain &&
|
|
remain != _remain)
|
|
{
|
|
_remain = remain;
|
|
|
|
goto iteration;
|
|
}
|
|
|
|
if (print_nothing)
|
|
{
|
|
(void) sprintf(bp, "%07X\n", offset);
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(strlen(bp), 8)
|
|
|
|
bp += 8;
|
|
}
|
|
|
|
return (bp - start_buffer);
|
|
}
|
|
|
|
char* u_memoryDump(char* restrict bp, unsigned char* restrict cp, uint32_t n)
|
|
{
|
|
uint32_t written;
|
|
|
|
U_INTERNAL_TRACE("u_memoryDump(%p,%p,%u)", bp, cp, n)
|
|
|
|
written = u_memory_dump(bp, cp, n);
|
|
|
|
U_INTERNAL_ASSERT_MINOR(written, 4096)
|
|
|
|
bp[written] = '\0';
|
|
|
|
return bp;
|
|
}
|
|
|
|
/* get the number of the processors including offline CPUs */
|
|
|
|
static inline const char* nexttoken(const char* q, int sep)
|
|
{
|
|
if (q) q = strchr(q, sep);
|
|
if (q) ++q;
|
|
|
|
return q;
|
|
}
|
|
|
|
/**
|
|
* When parsing bitmask lists, only allow numbers, separated by one
|
|
* of the allowed next characters.
|
|
*
|
|
* The parameter 'sret' is the return from a sscanf "%u%c". It is
|
|
* -1 if the sscanf input string was empty. It is 0 if the first
|
|
* character in the sscanf input string was not a decimal number.
|
|
* It is 1 if the unsigned number matching the "%u" was the end of the
|
|
* input string. It is 2 if one or more additional characters followed
|
|
* the matched unsigned number. If it is 2, then 'nextc' is the first
|
|
* character following the number. The parameter 'ok_next_chars'
|
|
* is the nul-terminated list of allowed next characters.
|
|
*
|
|
* The mask term just scanned was ok if and only if either the numbers
|
|
* matching the %u were all of the input or if the next character in
|
|
* the input past the numbers was one of the allowed next characters
|
|
*/
|
|
|
|
static inline bool scan_was_ok(int sret, char nextc, const char* ok_next_chars) { return (sret == 1 || (sret == 2 && strchr(ok_next_chars, nextc))); }
|
|
|
|
int u_get_num_cpu(void)
|
|
{
|
|
U_INTERNAL_TRACE("u_get_num_cpu()")
|
|
|
|
if (u_num_cpu == -1)
|
|
{
|
|
# ifdef _SC_NPROCESSORS_ONLN
|
|
u_num_cpu = sysconf(_SC_NPROCESSORS_ONLN);
|
|
# elif defined(_SC_NPROCESSORS_CONF)
|
|
u_num_cpu = sysconf(_SC_NPROCESSORS_CONF);
|
|
# elif defined(U_LINUX)
|
|
FILE* fp = fopen("/sys/devices/system/cpu/present", "r");
|
|
|
|
if (fp)
|
|
{
|
|
char buf[128];
|
|
const char* q;
|
|
|
|
char nextc; /* char after sscanf %u match */
|
|
unsigned int a; /* begin of range */
|
|
unsigned int b; /* end of range */
|
|
unsigned int s; /* stride */
|
|
|
|
if (fgets(buf, sizeof(buf), fp))
|
|
{
|
|
const char* p;
|
|
|
|
q = buf;
|
|
|
|
buf[u__strlen(buf, __PRETTY_FUNCTION__) - 1] = '\0';
|
|
|
|
/**
|
|
* Parses a comma-separated list of numbers and ranges
|
|
* of numbers, with optional ':%u' strides modifying ranges.
|
|
*
|
|
* Some examples of input lists and their equivalent simple list:
|
|
*
|
|
* Input Equivalent to
|
|
* 0-3 0,1,2,3
|
|
* 0-7:2 0,2,4,6
|
|
* 1,3,5-7 1,3,5,6,7
|
|
* 0-3:2,8-15:4 0,2,8,12
|
|
*/
|
|
|
|
while (p = q, q = nexttoken(q, ','), p)
|
|
{
|
|
const char* c1;
|
|
const char* c2;
|
|
int sret = sscanf(p, "%u%c", &a, &nextc);
|
|
|
|
if (scan_was_ok(sret, nextc, ",-") == false) break;
|
|
|
|
b = a;
|
|
s = 1;
|
|
c1 = nexttoken(p, '-');
|
|
c2 = nexttoken(p, ',');
|
|
|
|
if (c1 != 0 && (c2 == 0 || c1 < c2))
|
|
{
|
|
sret = sscanf(c1, "%u%c", &b, &nextc);
|
|
|
|
if (scan_was_ok(sret, nextc, ",:") == false) break;
|
|
|
|
c1 = nexttoken(c1, ':');
|
|
|
|
if (c1 != 0 && (c2 == 0 || c1 < c2))
|
|
{
|
|
sret = sscanf(c1, "%u%c", &s, &nextc);
|
|
|
|
if (scan_was_ok(sret, nextc, ",") == false) break;
|
|
}
|
|
}
|
|
|
|
if (!(a <= b)) break;
|
|
|
|
while (a <= b)
|
|
{
|
|
u_num_cpu = a + 1; /* Number of highest set bit +1 is the number of the CPUs */
|
|
|
|
a += s;
|
|
}
|
|
}
|
|
}
|
|
|
|
(void) fclose(fp);
|
|
}
|
|
# endif
|
|
}
|
|
|
|
return u_num_cpu;
|
|
}
|
|
|
|
/* Pin the process to a particular core */
|
|
|
|
void u_bind2cpu(cpu_set_t* cpuset, int n)
|
|
{
|
|
U_INTERNAL_TRACE("u_bind2cpu(%p,%d)", cpuset, n)
|
|
|
|
/**
|
|
* CPU mask of CPUs available to this process,
|
|
* conceptually, each bit represents a logical CPU, ie:
|
|
*
|
|
* mask = 3 (11b): cpu0, 1
|
|
* mask = 13 (1101b): cpu0, 2, 3
|
|
*/
|
|
|
|
#ifdef HAVE_SCHED_GETAFFINITY
|
|
CPU_SET(n, cpuset);
|
|
|
|
(void) sched_setaffinity(u_pid, sizeof(cpu_set_t), cpuset);
|
|
|
|
CPU_ZERO(cpuset);
|
|
|
|
(void) sched_getaffinity(u_pid, sizeof(cpu_set_t), cpuset);
|
|
|
|
U_INTERNAL_PRINT("cpuset = %ld", CPUSET_BITS(cpuset)[0])
|
|
#endif
|
|
}
|
|
|
|
void u_switch_to_realtime_priority(void)
|
|
{
|
|
#if defined(_POSIX_PRIORITY_SCHEDULING) && \
|
|
(_POSIX_PRIORITY_SCHEDULING > 0) && (defined(HAVE_SCHED_H) || defined(HAVE_SYS_SCHED_H))
|
|
struct sched_param sp;
|
|
|
|
U_INTERNAL_TRACE("u_switch_to_realtime_priority()")
|
|
|
|
/* sched_getscheduler(u_pid); // SCHED_FIFO | SCHED_RR | SCHED_OTHER */
|
|
|
|
(void) sched_getparam(u_pid, &sp);
|
|
|
|
sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
|
|
|
U_INTERNAL_PRINT("sp.sched_priority = %d", sp.sched_priority)
|
|
|
|
/*
|
|
struct rlimit rlim_old, rlim_new = { sp.sched_priority, sp.sched_priority };
|
|
|
|
(void) prlimit(u_pid, RLIMIT_RTPRIO, &rlim_new, &rlim_old);
|
|
|
|
U_INTERNAL_PRINT("Previous RLIMIT_RTPRIO limits: soft=%lld; hard=%lld\n", (long long)rlim_old.rlim_cur, (long long)rlim_old.rlim_max);
|
|
|
|
(void) prlimit(u_pid, RLIMIT_RTPRIO, 0, &rlim_old);
|
|
|
|
U_INTERNAL_PRINT("New RLIMIT_RTPRIO limits: soft=%lld; hard=%lld\n", (long long)rlim_old.rlim_cur, (long long)rlim_old.rlim_max);
|
|
*/
|
|
|
|
if (sched_setscheduler(u_pid, SCHED_FIFO, &sp) == -1) U_WARNING("Cannot set posix realtime scheduling policy");
|
|
#endif
|
|
}
|
|
|
|
void u_get_memusage(unsigned long* vsz, unsigned long* rss)
|
|
{
|
|
#if defined(U_LINUX) && !defined(U_COVERITY_FALSE_POSITIVE)
|
|
static int fd_stat;
|
|
|
|
char* p;
|
|
char* field[256];
|
|
char buffer[4096];
|
|
ssize_t bytes_read;
|
|
|
|
U_INTERNAL_TRACE("u_get_memusage(%p,%p)", vsz, rss)
|
|
|
|
if (fd_stat == 0) fd_stat = open("/proc/self/stat", O_RDONLY);
|
|
|
|
U_INTERNAL_ASSERT_DIFFERS(fd_stat, -1)
|
|
|
|
bytes_read = pread(fd_stat, buffer, sizeof(buffer), 0);
|
|
|
|
if (bytes_read > 0)
|
|
{
|
|
U_INTERNAL_ASSERT_RANGE(1,bytes_read,(int)sizeof(buffer))
|
|
|
|
/**
|
|
* The fields, in order, with their proper scanf(3) format specifiers, are:
|
|
*
|
|
* pid %d The process ID.
|
|
* comm %s The filename of the executable, in parentheses. This is visible whether or not the executable is swapped out.
|
|
* state %c R is running, S is sleeping, D is waiting, Z is zombie, T is traced or stopped (on a signal), and W is paging.
|
|
* ppid %d The PID of the parent.
|
|
* pgrp %d The process group ID of the process.
|
|
* session %d The session ID of the process.
|
|
* tty_nr %d The controlling terminal of the process.
|
|
* tpgid %d The ID of the foreground process group of the controlling terminal of the process.
|
|
* flags %u The kernel flags word of the process.
|
|
* minflt %lu The number of minor faults the process has made which have not required loading a memory page from disk.
|
|
* cminflt %lu The number of minor faults that the process's waited-for children have made.
|
|
* majflt %lu The number of major faults the process has made which have required loading a memory page from disk.
|
|
* cmajflt %lu The number of major faults that the process's waited-for children have made.
|
|
* utime %lu Amount of time that this process has been scheduled in user mode, measured in clock ticks.
|
|
* stime %lu Amount of time that this process has been scheduled in kernel mode, measured in clock ticks.
|
|
* cutime %ld Amount of time that this process's waited-for children have been scheduled in user mode, measured in clock ticks.
|
|
* cstime %ld Amount of time that this process's waited-for children have been scheduled in kernel mode, measured in clock ticks.
|
|
* priority %ld For processes running a real-time scheduling policy , this is the negated scheduling priority, minus one.
|
|
* nice %ld The nice value (see setpriority(2)), a value in the range 19 (low priority) to -20 (high priority).
|
|
* num_threads %ld Number of threads in this process.
|
|
* itrealvalue %ld The time in jiffies before the next SIGALRM is sent to the process due to an interval timer.
|
|
* starttime %llu The time in jiffies the process started after system boot.
|
|
*
|
|
* (22) vsize %lu Virtual memory size in bytes.
|
|
* (23) rss %ld Resident Set Size: number of pages the process has in real memory.
|
|
* This is just the pages which count toward text, data, or stack space.
|
|
* This does not include pages which have not been demand-loaded in, or which are swapped out
|
|
* Ex:
|
|
* 1890 (cat) R 1052 1890 1052 34819 1890 4194304 79 0 0 0 0 0 0 0 20 0 1 0 731066 4530176 187
|
|
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
|
* 18446744073709551615 94751693697024 94751693749876 140733378884480 0 0 0 0 0 0 0 0 0 17 3 0 0 0 0 0
|
|
* 94751695849896 94751695851712 94751700852736 140733378890964 140733378890984 140733378890984 140733378895855 0
|
|
*/
|
|
|
|
(void) u_split(buffer, bytes_read, field, 0);
|
|
|
|
p = field[22];
|
|
*vsz = u__strtoul(p, u__strlen(p,__PRETTY_FUNCTION__));
|
|
|
|
p = field[23];
|
|
*rss = u__strtoul(p, u__strlen(p,__PRETTY_FUNCTION__)) * PAGESIZE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uint8_t u_get_loadavg(void)
|
|
{
|
|
/**
|
|
* /proc/loadavg (ex: 0.19 1.37 0.97 1/263 26041)
|
|
*
|
|
* The first three fields in this file are load average figures giving the number of jobs in the run queue (state R) or waiting for disk I/O (state D)
|
|
* averaged over 1, 5, and 15 minutes. They are the same as the load average numbers given by uptime(1) and other programs. The fourth field consists
|
|
* of two numbers separated by a slash (/). The first of these is the number of currently runnable kernel scheduling entities (processes, threads).
|
|
* The value after the slash is the number of kernel scheduling entities that currently exist on the system. The fifth field is the PID of the process
|
|
* that was most recently created on the system
|
|
*/
|
|
|
|
#if defined(U_LINUX) && !defined(U_COVERITY_FALSE_POSITIVE)
|
|
static int fd_loadavg;
|
|
|
|
char buffer[8];
|
|
ssize_t bytes_read;
|
|
|
|
U_INTERNAL_TRACE("u_get_loadavg()")
|
|
|
|
if (fd_loadavg == 0) fd_loadavg = open("/proc/loadavg", O_RDONLY);
|
|
|
|
U_INTERNAL_ASSERT_DIFFERS(fd_loadavg, -1)
|
|
|
|
bytes_read = pread(fd_loadavg, buffer, sizeof(buffer), 0);
|
|
|
|
if (bytes_read > 0)
|
|
{
|
|
U_INTERNAL_ASSERT_RANGE(1,bytes_read,(int)sizeof(buffer))
|
|
|
|
if (buffer[1] == '.') return u_loadavg(buffer); // 0.19 => 2, 4.56 => 46, ...
|
|
}
|
|
#endif
|
|
|
|
return 255;
|
|
}
|
|
|
|
uint32_t u_set_uptime(char* buffer)
|
|
{
|
|
/**
|
|
* /proc/uptime (ex: 1753.44 6478.08)
|
|
*
|
|
* This file contains two numbers: how long the system has been running (seconds), and the amount of time spent in idle process (seconds)
|
|
*/
|
|
|
|
#if defined(U_LINUX) && !defined(U_COVERITY_FALSE_POSITIVE)
|
|
static int fd_uptime;
|
|
|
|
uint8_t i;
|
|
ssize_t bytes_read;
|
|
|
|
U_INTERNAL_TRACE("u_get_uptime()")
|
|
|
|
if (fd_uptime == 0) fd_uptime = open("/proc/uptime", O_RDONLY);
|
|
|
|
U_INTERNAL_ASSERT_DIFFERS(fd_uptime, -1)
|
|
|
|
bytes_read = pread(fd_uptime, buffer, 12, 0);
|
|
|
|
if (bytes_read > 0)
|
|
{
|
|
U_INTERNAL_ASSERT_RANGE(1,bytes_read,12)
|
|
|
|
U_INTERNAL_PRINT("result = %u", u_atoi(buffer))
|
|
|
|
U_INTERNAL_ASSERT_MINOR(u_atoi(buffer), 12 * U_ONE_YEAR_IN_SECOND)
|
|
|
|
for (i = 0; i < 12; ++i)
|
|
{
|
|
if (buffer[i] == '.') break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Search a string for any of a set of characters.
|
|
* Locates the first occurrence in the string s of any of the characters in the string accept
|
|
*/
|
|
|
|
__pure const char* u__strpbrk(const char* restrict s, uint32_t slen, const char* restrict _accept)
|
|
{
|
|
const char* restrict c;
|
|
const char* restrict end = s + slen;
|
|
|
|
U_INTERNAL_TRACE("u__strpbrk(%.*s,%u,%s)", U_min(slen,128), s, slen, _accept)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(slen, 0)
|
|
U_INTERNAL_ASSERT_POINTER(_accept)
|
|
|
|
while (s < end)
|
|
{
|
|
for (c = _accept; *c; ++c)
|
|
{
|
|
if (*s == *c) return s;
|
|
}
|
|
|
|
++s;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Search a string for a terminator of a group of delimitator {} [] () <%%>...*/
|
|
|
|
__pure const char* u_strpend(const char* restrict s, uint32_t slen, const char* restrict group_delimitor, uint32_t group_delimitor_len, char skip_line_comment)
|
|
{
|
|
char c;
|
|
int level = 1;
|
|
const char* restrict end = s + slen;
|
|
uint32_t i, n = group_delimitor_len / 2;
|
|
|
|
U_INTERNAL_TRACE("u_strpend(%.*s,%u,%s,%u,%d)", U_min(slen,128), s, slen, group_delimitor, group_delimitor_len, skip_line_comment)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(slen,0)
|
|
U_INTERNAL_ASSERT_POINTER(group_delimitor)
|
|
U_INTERNAL_ASSERT_EQUALS(s[0], group_delimitor[n-1])
|
|
U_INTERNAL_ASSERT_EQUALS(group_delimitor_len & 1, 0)
|
|
|
|
while (s < end)
|
|
{
|
|
loop: c = *++s;
|
|
|
|
if (u__isspace(c)) continue;
|
|
|
|
if (c == skip_line_comment)
|
|
{
|
|
/* skip line comment */
|
|
|
|
s = (const char* restrict) memchr(s, '\n', end - s);
|
|
|
|
if (s == 0) break;
|
|
}
|
|
else if (c == group_delimitor[0] &&
|
|
*(s-1) != '\\')
|
|
{
|
|
U_INTERNAL_PRINT("c = %c level = %d s = %.*s", c, level, 10, s)
|
|
|
|
for (i = 1; i < n; ++i)
|
|
{
|
|
U_INTERNAL_PRINT("s[%d] = %c group_delimitor[%d] = %c", i, s[i], i, group_delimitor[i])
|
|
|
|
if (s[i] != group_delimitor[i]) goto loop;
|
|
}
|
|
|
|
++level;
|
|
}
|
|
else if (c == group_delimitor[n] &&
|
|
*(s-1) != '\\')
|
|
{
|
|
U_INTERNAL_PRINT("c = %c level = %d s = %.*s", c, level, 10, s)
|
|
|
|
for (i = 1; i < n; ++i)
|
|
{
|
|
U_INTERNAL_PRINT("s[%d] = %c group_delimitor[%d] = %c", i, s[i], n+i, group_delimitor[n+i])
|
|
|
|
if (s[i] != group_delimitor[n+i]) goto loop;
|
|
}
|
|
|
|
if (--level == 0) return s;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("level = %d s = %.*s", level, 10, s)
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
__pure bool u_isNumber(const char* restrict s, uint32_t n)
|
|
{
|
|
int vdigit[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
|
|
const char* restrict end = s + n;
|
|
|
|
U_INTERNAL_TRACE("u_isNumber(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(n,0)
|
|
|
|
if (*s == '+' ||
|
|
*s == '-')
|
|
{
|
|
++s;
|
|
}
|
|
|
|
while (s < end &&
|
|
((*(const unsigned char* restrict)s) >> 4) == 0x03 &&
|
|
vdigit[(*(const unsigned char* restrict)s) & 0x0f])
|
|
{
|
|
U_INTERNAL_PRINT("*s = %c, *s >> 4 = %c ", *s, (*(char* restrict)s) >> 4)
|
|
|
|
++s;
|
|
}
|
|
|
|
return (s == end);
|
|
}
|
|
|
|
/* skip string delimiter or white space and line comment */
|
|
|
|
__pure const char* u_skip(const char* restrict s, const char* restrict end, const char* restrict delim, char line_comment)
|
|
{
|
|
U_INTERNAL_TRACE("u_skip(%.*s,%p,%s,%d)", U_min(end-s,128), s, end, delim, line_comment)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_POINTER(end)
|
|
|
|
if (delim)
|
|
{
|
|
/* skip string delimiter */
|
|
|
|
while (s < end &&
|
|
strchr(delim, *s))
|
|
{
|
|
++s;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
skipws:
|
|
while (s < end &&
|
|
u__isspace(*s))
|
|
{
|
|
++s;
|
|
}
|
|
|
|
if (line_comment)
|
|
{
|
|
if (*s == line_comment)
|
|
{
|
|
/* skip line comment */
|
|
|
|
s = (const char* restrict) memchr(s, '\n', end - s);
|
|
|
|
if (s) goto skipws;
|
|
|
|
return end;
|
|
}
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/* delimit token */
|
|
|
|
const char* u_delimit_token(const char* restrict s, const char** restrict pold, const char* restrict end, const char* restrict delim, char skip_line_comment)
|
|
{
|
|
char c;
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_POINTER(end)
|
|
U_INTERNAL_ASSERT_POINTER(pold)
|
|
|
|
U_INTERNAL_TRACE("u_delimit_token(%.*s,%p,%p,%s,%d)", U_min(end-s,128), s, pold, end, delim, skip_line_comment)
|
|
|
|
U_INTERNAL_PRINT("s = %p", s)
|
|
|
|
s = u_skip(s, end, delim, skip_line_comment);
|
|
|
|
U_INTERNAL_PRINT("s = %p", s)
|
|
|
|
if (s == end)
|
|
{
|
|
*pold = 0;
|
|
|
|
return ++end;
|
|
}
|
|
|
|
*pold = s;
|
|
c = *s++;
|
|
|
|
/* NB: we don't search for delimiter in block text... */
|
|
|
|
if (u__isquote(c))
|
|
{
|
|
s = u_find_char(s, end, c);
|
|
|
|
U_INTERNAL_PRINT("s = %p", s)
|
|
|
|
if (delim)
|
|
{
|
|
if (++s < end) goto next; /* NB: goto next char (skip '"')... */
|
|
|
|
/* NB: we consider this block text with '"' as token... */
|
|
|
|
s = end;
|
|
}
|
|
else
|
|
{
|
|
/* NB: we consider this block text without '"' as token... */
|
|
|
|
++(*pold);
|
|
}
|
|
|
|
goto end;
|
|
}
|
|
|
|
if (delim)
|
|
{
|
|
next: s = (const char* restrict) (s < end ? u__strpbrk(s, end - s, delim) : 0);
|
|
|
|
if (s == 0) return end;
|
|
}
|
|
else
|
|
{
|
|
/* find next white space */
|
|
|
|
while (s < end &&
|
|
u__isspace(*s) == false)
|
|
{
|
|
++s;
|
|
}
|
|
}
|
|
|
|
end:
|
|
U_INTERNAL_PRINT("s = %p end = %p result = \"%.*s\"", s, end, (s-(*pold)), *pold)
|
|
|
|
return s;
|
|
}
|
|
|
|
uint32_t u_split(char* restrict s, uint32_t n, char** restrict argv, const char* restrict delim)
|
|
{
|
|
const char* restrict p;
|
|
char* restrict end = s + n;
|
|
char** restrict ptr = argv;
|
|
|
|
U_INTERNAL_TRACE("u_split(%.*s,%u,%p,%s)", U_min(n,128), s, n, argv, delim)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
U_INTERNAL_ASSERT_POINTER(argv)
|
|
U_INTERNAL_ASSERT_EQUALS(u_isBinary((const unsigned char*)s,n),false)
|
|
|
|
while (s < end)
|
|
{
|
|
s = (char* restrict) u_delimit_token(s, (const char** restrict)&p, end, delim, 0);
|
|
|
|
U_INTERNAL_PRINT("s = %.*s", 20, s)
|
|
|
|
if (s <= end)
|
|
{
|
|
*argv++ = (char* restrict) p;
|
|
|
|
*s++ = '\0';
|
|
|
|
U_INTERNAL_PRINT("u_split(%u) = %s", argv-ptr-1, p)
|
|
}
|
|
}
|
|
|
|
*argv = 0;
|
|
|
|
n = (argv - ptr);
|
|
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* Match STRING against the filename pattern MASK, returning true if it matches, false if not, inversion if flags contain FNM_INVERT
|
|
*
|
|
* '?' matches any single character
|
|
* '*' matches any string, including the empty string
|
|
*/
|
|
|
|
__pure bool u_dosmatch(const char* restrict s, uint32_t n1, const char* restrict mask, uint32_t n2, int flags)
|
|
{
|
|
const char* restrict cp = 0;
|
|
const char* restrict mp = 0;
|
|
unsigned char c1 = 0, c2 = 0;
|
|
|
|
const char* restrict end_s = s + n1;
|
|
const char* restrict end_mask = mask + n2;
|
|
|
|
U_INTERNAL_TRACE("u_dosmatch(%.*s,%u,%.*s,%u,%d)", U_min(n1,128), s, n1, n2, mask, n2, flags)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(n1, 0)
|
|
U_INTERNAL_ASSERT_MAJOR(n2, 0)
|
|
U_INTERNAL_ASSERT_POINTER(mask)
|
|
|
|
if ((flags & FNM_IGNORECASE) != 0)
|
|
{
|
|
while (s < end_s)
|
|
{
|
|
c2 = u__tolower(*mask);
|
|
|
|
if (c2 == '*') break;
|
|
|
|
c1 = u__tolower(*s);
|
|
|
|
if (c2 != c1 &&
|
|
c2 != '?')
|
|
{
|
|
return ((flags & FNM_INVERT) != 0); /* no match */
|
|
}
|
|
|
|
++s;
|
|
++mask;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("c1 = %c c2 = %c", c1, c2)
|
|
|
|
while (true)
|
|
{
|
|
if (s >= end_s)
|
|
{
|
|
if (mask == 0) return ((flags & FNM_INVERT) != 0); /* no match */
|
|
|
|
while (*mask == '*') ++mask;
|
|
|
|
if ((flags & FNM_INVERT) == 0)
|
|
{
|
|
if (mask >= end_mask) return true; /* match */
|
|
|
|
return false; /* no match */
|
|
}
|
|
|
|
if (mask >= end_mask) return false; /* match */
|
|
|
|
return true; /* no match */
|
|
}
|
|
|
|
c2 = (mask ? u__tolower(*mask) : 0);
|
|
|
|
if (c2 == '*')
|
|
{
|
|
if (++mask >= end_mask) return ((flags & FNM_INVERT) == 0); /* match */
|
|
|
|
cp = s+1;
|
|
mp = mask;
|
|
|
|
continue;
|
|
}
|
|
|
|
c1 = u__tolower(*s);
|
|
|
|
U_INTERNAL_PRINT("c1 = %c c2 = %c", c1, c2)
|
|
|
|
if (c2 == c1 ||
|
|
c2 == '?')
|
|
{
|
|
++s;
|
|
++mask;
|
|
|
|
continue;
|
|
}
|
|
|
|
s = (cp ? cp++ : end_s);
|
|
mask = mp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (s < end_s)
|
|
{
|
|
c2 = *mask;
|
|
|
|
if (c2 == '*') break;
|
|
|
|
c1 = *s;
|
|
|
|
if (c2 != c1 &&
|
|
c2 != '?')
|
|
{
|
|
return ((flags & FNM_INVERT) != 0); /* no match */
|
|
}
|
|
|
|
++s;
|
|
++mask;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("c1 = %c c2 = %c", c1, c2)
|
|
|
|
while (true)
|
|
{
|
|
if (s >= end_s)
|
|
{
|
|
if (mask == 0) return ((flags & FNM_INVERT) != 0); /* no match */
|
|
|
|
while (*mask == '*') ++mask;
|
|
|
|
if ((flags & FNM_INVERT) == 0)
|
|
{
|
|
if (mask >= end_mask) return true; /* match */
|
|
|
|
return false; /* no match */
|
|
}
|
|
|
|
if (mask >= end_mask) return false; /* match */
|
|
|
|
return true; /* no match */
|
|
}
|
|
|
|
c2 = *mask;
|
|
|
|
if (c2 == '*')
|
|
{
|
|
if (++mask >= end_mask) return ((flags & FNM_INVERT) == 0); /* match */
|
|
|
|
cp = s + 1;
|
|
mp = mask;
|
|
|
|
continue;
|
|
}
|
|
|
|
c1 = *s;
|
|
|
|
U_INTERNAL_PRINT("c1 = %c c2 = %c", c1, c2)
|
|
|
|
if (c2 == c1 ||
|
|
c2 == '?')
|
|
{
|
|
++s;
|
|
++mask;
|
|
|
|
continue;
|
|
}
|
|
|
|
s = cp++;
|
|
mask = mp;
|
|
}
|
|
}
|
|
}
|
|
|
|
__pure bool u_dosmatch_ext(const char* restrict s, uint32_t n1, const char* restrict mask, uint32_t n2, int flags)
|
|
{
|
|
U_INTERNAL_TRACE("u_dosmatch_ext(%.*s,%u,%.*s,%u,%d)", U_min(n1,128), s, n1, n2, mask, n2, flags)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(n1, 0)
|
|
U_INTERNAL_ASSERT_MAJOR(n2, 0)
|
|
U_INTERNAL_ASSERT_POINTER(mask)
|
|
|
|
while (n2)
|
|
{
|
|
U_INTERNAL_PRINT("switch: s[0] = %c n1 = %u mask[0] = %c n2 = %u", s[0], n1, mask[0], n2)
|
|
|
|
switch (*mask)
|
|
{
|
|
case '*':
|
|
{
|
|
while (mask[1] == '*')
|
|
{
|
|
++mask;
|
|
--n2;
|
|
}
|
|
|
|
if (n2 == 1) return ((flags & FNM_INVERT) == 0); /* match */
|
|
|
|
while (n1)
|
|
{
|
|
if (u_dosmatch_ext(s, n1, mask+1, n2-1, flags & ~FNM_INVERT)) return ((flags & FNM_INVERT) == 0); /* match */
|
|
|
|
++s;
|
|
--n1;
|
|
}
|
|
|
|
return ((flags & FNM_INVERT) != 0); /* no match */
|
|
}
|
|
break;
|
|
|
|
case '?':
|
|
{
|
|
if (n1 == 0) return ((flags & FNM_INVERT) != 0); /* no match */
|
|
|
|
++s;
|
|
--n1;
|
|
}
|
|
break;
|
|
|
|
case '[':
|
|
{
|
|
bool match = false,
|
|
bnot = (mask[1] == '^');
|
|
|
|
if (bnot)
|
|
{
|
|
n2 -= 2;
|
|
mask += 2;
|
|
}
|
|
else
|
|
{
|
|
--n2;
|
|
++mask;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("s[0] = %c n1 = %u mask[0] = %c n2 = %u", s[0], n1, mask[0], n2)
|
|
|
|
while (true)
|
|
{
|
|
if (*mask == '\\')
|
|
{
|
|
if (*++mask == s[0]) match = true;
|
|
|
|
--n2;
|
|
}
|
|
else
|
|
{
|
|
if (*mask == ']') break;
|
|
|
|
if (n2 == 0)
|
|
{
|
|
++n2;
|
|
--mask;
|
|
|
|
break;
|
|
}
|
|
|
|
if (n2 >= 3 &&
|
|
mask[1] == '-')
|
|
{
|
|
int start = mask[0],
|
|
end = mask[2],
|
|
c = s[0];
|
|
|
|
if (start > end)
|
|
{
|
|
int t = start;
|
|
start = end;
|
|
end = t;
|
|
}
|
|
|
|
if ((flags & FNM_IGNORECASE) != 0)
|
|
{
|
|
start = u__tolower((unsigned char)start);
|
|
end = u__tolower((unsigned char)end);
|
|
c = u__tolower((unsigned char)c);
|
|
}
|
|
|
|
mask += 2;
|
|
n2 -= 2;
|
|
|
|
if (c >= start &&
|
|
c <= end)
|
|
{
|
|
match = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((flags & FNM_IGNORECASE) == 0)
|
|
{
|
|
if (mask[0] == s[0]) match = true;
|
|
}
|
|
else
|
|
{
|
|
if (u__tolower((unsigned char)mask[0]) == u__tolower((unsigned char)s[0])) match = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
++mask;
|
|
--n2;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("match = %d bnot = %d", match, bnot)
|
|
|
|
if (match == false || bnot) return ((flags & FNM_INVERT) != 0); /* no match */
|
|
|
|
U_INTERNAL_PRINT("s[0] = %c n1 = %u mask[0] = %c n2 = %u", s[0], n1, mask[0], n2)
|
|
|
|
++s;
|
|
--n1;
|
|
}
|
|
break;
|
|
|
|
case '\\':
|
|
{
|
|
if (n2 >= 2)
|
|
{
|
|
--n2;
|
|
++mask;
|
|
}
|
|
}
|
|
|
|
/* FALL THRU */
|
|
|
|
default:
|
|
{
|
|
U_INTERNAL_PRINT("default: s[0] = %c n1 = %u mask[0] = %c n2 = %u", s[0], n1, mask[0], n2)
|
|
|
|
if ((flags & FNM_IGNORECASE) == 0)
|
|
{
|
|
if (mask[0] != s[0]) return ((flags & FNM_INVERT) != 0); /* no match */
|
|
}
|
|
else
|
|
{
|
|
if (u__tolower((unsigned char)mask[0]) != u__tolower((unsigned char)s[0])) return ((flags & FNM_INVERT) != 0); /* no match */
|
|
}
|
|
|
|
++s;
|
|
--n1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
--n2;
|
|
++mask;
|
|
|
|
if (n1 == 0)
|
|
{
|
|
while (*mask == '*')
|
|
{
|
|
++mask;
|
|
--n2;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_PRINT("n1 = %u n2 = %u", n1, n2)
|
|
|
|
if (n2 == 0 &&
|
|
n1 == 0)
|
|
{
|
|
return ((flags & FNM_INVERT) == 0); /* match */
|
|
}
|
|
|
|
return ((flags & FNM_INVERT) != 0); /* no match */
|
|
}
|
|
|
|
const char* restrict u_pOR;
|
|
|
|
uint32_t u_match_with_OR(bPFpcupcud pfn_match, const char* restrict s, uint32_t n1, const char* restrict pattern, uint32_t n2, int flags)
|
|
{
|
|
uint32_t n0;
|
|
const char* restrict end = pattern + n2;
|
|
|
|
U_INTERNAL_TRACE("u_match_with_OR(%p,%.*s,%u,%.*s,%u,%d)", pfn_match, U_min(n1,128), s, n1, n2, pattern, n2, flags)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(n1, 0)
|
|
U_INTERNAL_ASSERT_MAJOR(n2, 0)
|
|
U_INTERNAL_ASSERT_POINTER(pattern)
|
|
|
|
loop:
|
|
u_pOR = (const char* restrict) memchr(pattern, '|', n2);
|
|
|
|
U_INTERNAL_PRINT("u_pOR = %p pattern(%u) = %.*s", u_pOR, n2, n2, pattern)
|
|
|
|
if (u_pOR == U_NULLPTR) return (pfn_match(s, n1, (u_pOR = pattern), n2, flags) ? n2 : 0);
|
|
|
|
n0 = (u_pOR - pattern);
|
|
|
|
if (pfn_match(s, n1, pattern, n0, (flags & ~FNM_INVERT)))
|
|
{
|
|
u_pOR = pattern;
|
|
|
|
return ((flags & FNM_INVERT) == 0 ? n0 : 0);
|
|
}
|
|
|
|
n2 = end - (pattern = (u_pOR + 1));
|
|
|
|
goto loop;
|
|
}
|
|
|
|
/**
|
|
* Verifies that the passed string is actually an e-mail address
|
|
*
|
|
* see: http://www.remote.org/jochen/mail/info/chars.html
|
|
*/
|
|
|
|
#define RFC822_SPECIALS "()<>@,;:\\\"[]"
|
|
|
|
__pure bool u_validate_email_address(const char* restrict address, uint32_t address_len)
|
|
{
|
|
int count;
|
|
const char* restrict c;
|
|
const char* restrict end;
|
|
const char* restrict domain;
|
|
|
|
U_INTERNAL_TRACE("u_validate_email_address(%.*s,%u)", U_min(address_len,128), address, address_len)
|
|
|
|
if (address_len < 3) return false;
|
|
|
|
/* first we validate the name portion (name@domain) */
|
|
|
|
for (c = address, end = address + address_len; c < end; ++c)
|
|
{
|
|
U_INTERNAL_PRINT("c = %c", *c)
|
|
|
|
if (*c == '\"' &&
|
|
(c == address || *(c-1) == '.' || *(c-1) == '\"'))
|
|
{
|
|
while (++c < end)
|
|
{
|
|
U_INTERNAL_PRINT("c = %c", *c)
|
|
|
|
if (*c == '\"') break;
|
|
|
|
if (*c == '\\' && (*++c == ' ')) continue;
|
|
|
|
if (*c <= ' ' || *c >= 127) return false;
|
|
}
|
|
|
|
if (c++ >= end) return false;
|
|
|
|
U_INTERNAL_PRINT("c = %c", *c)
|
|
|
|
if (*c == '@') break;
|
|
|
|
if (*c != '.') return false;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (*c == '@') break;
|
|
|
|
if (*c <= ' ' ||
|
|
*c >= 127)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (strchr(RFC822_SPECIALS, *c)) return false;
|
|
}
|
|
|
|
if (c == address || *(c-1) == '.') return false;
|
|
|
|
/* next we validate the domain portion (name@domain) */
|
|
|
|
if ((domain = ++c) >= end) return false;
|
|
|
|
count = 0;
|
|
|
|
do {
|
|
U_INTERNAL_PRINT("c = %c", *c)
|
|
|
|
if (*c == '.')
|
|
{
|
|
if (c == domain || *(c-1) == '.') return false;
|
|
|
|
++count;
|
|
}
|
|
|
|
if (*c <= ' ' ||
|
|
*c >= 127)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (strchr(RFC822_SPECIALS, *c)) return false;
|
|
}
|
|
while (++c < end);
|
|
|
|
return (count >= 1);
|
|
}
|
|
|
|
/* Perform 'natural order' comparisons of strings */
|
|
|
|
__pure int u_strnatcmp(char const* restrict a, char const* restrict b)
|
|
{
|
|
char ca, cb;
|
|
int ai = 0, bi = 0;
|
|
|
|
U_INTERNAL_TRACE("u_strnatcmp(%s,%s)", a, b)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(a)
|
|
U_INTERNAL_ASSERT_POINTER(b)
|
|
|
|
while (true)
|
|
{
|
|
ca = a[ai];
|
|
cb = b[bi];
|
|
|
|
/* skip over leading spaces or zeros */
|
|
|
|
while (u__isspace(ca) || ca == '0') ca = a[++ai];
|
|
while (u__isspace(cb) || cb == '0') cb = b[++bi];
|
|
|
|
/* process run of digits */
|
|
|
|
if (u__isdigit(ca) &&
|
|
u__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 (true)
|
|
{
|
|
if (!u__isdigit(ca) &&
|
|
!u__isdigit(cb))
|
|
{
|
|
goto done_number;
|
|
}
|
|
|
|
else if (!u__isdigit(ca)) return -1;
|
|
else if (!u__isdigit(cb)) return 1;
|
|
else if (ca < cb)
|
|
{
|
|
if (!bias) bias = -1;
|
|
}
|
|
else if (ca > cb)
|
|
{
|
|
if (!bias) bias = 1;
|
|
}
|
|
else if (!ca &&
|
|
!cb)
|
|
{
|
|
return bias;
|
|
}
|
|
|
|
ca = a[++ai];
|
|
cb = b[++bi];
|
|
}
|
|
|
|
done_number:
|
|
if (bias) return bias;
|
|
}
|
|
|
|
if (!ca &&
|
|
!cb)
|
|
{
|
|
/* The strings compare the same. Perhaps the caller will want to call strcmp to break the tie */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
if (fold_case)
|
|
{
|
|
ca = u__toupper(ca);
|
|
cb = u__toupper(cb);
|
|
}
|
|
*/
|
|
|
|
if (ca < cb) return -1;
|
|
else if (ca > cb) return 1;
|
|
|
|
++ai;
|
|
++bi;
|
|
}
|
|
}
|
|
|
|
#ifdef _MSWINDOWS_
|
|
# define PATH_LIST_SEP ';'
|
|
#else
|
|
# define PATH_LIST_SEP ':'
|
|
#endif
|
|
|
|
/**
|
|
* Given a string containing units of information separated by colons, return the next one pointed to by (p_index),
|
|
* or NULL if there are no more. Advance (p_index) to the character after the colon
|
|
*/
|
|
|
|
static inline char* extract_colon_unit(char* restrict pzDir, const char* restrict string, uint32_t string_len, uint32_t* restrict p_index)
|
|
{
|
|
char* restrict pzDest = pzDir;
|
|
const char* restrict pzSrc = string + *p_index;
|
|
|
|
U_INTERNAL_TRACE("extract_colon_unit(%s,%.*s,%u,%p)", pzDir, string_len, string, string_len, p_index)
|
|
|
|
if ((string == 0) ||
|
|
(*p_index >= string_len))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
while (*pzSrc == PATH_LIST_SEP) pzSrc++;
|
|
|
|
while (true)
|
|
{
|
|
char ch = (*pzDest = *pzSrc);
|
|
|
|
if (ch == '\0') break;
|
|
|
|
if (ch == PATH_LIST_SEP)
|
|
{
|
|
*pzDest = '\0';
|
|
|
|
break;
|
|
}
|
|
|
|
pzDest++;
|
|
pzSrc++;
|
|
}
|
|
|
|
if (*pzDir == '\0') return 0;
|
|
|
|
*p_index = (pzSrc - string);
|
|
|
|
return pzDir;
|
|
}
|
|
|
|
/* Turn STRING (a pathname) into an absolute pathname, assuming that DOT_PATH contains the symbolic location of '.' */
|
|
|
|
static inline void make_absolute(char* restrict result, const char* restrict dot_path, const char* restrict string)
|
|
{
|
|
int result_len;
|
|
|
|
U_INTERNAL_TRACE("make_absolute(%p,%s,%s)", result, dot_path, string)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(dot_path)
|
|
|
|
if (dot_path[0])
|
|
{
|
|
u__strcpy(result, dot_path);
|
|
|
|
result_len = u__strlen(result, __PRETTY_FUNCTION__);
|
|
|
|
if (result[result_len-1] != PATH_SEPARATOR)
|
|
{
|
|
result[result_len++] = PATH_SEPARATOR;
|
|
result[result_len] = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result[0] = '.';
|
|
result[1] = PATH_SEPARATOR;
|
|
result[2] = '\0';
|
|
|
|
result_len = 2;
|
|
}
|
|
|
|
u__strcpy(result+result_len, string);
|
|
}
|
|
|
|
/**
|
|
* find a FILE MODE along PATH
|
|
*
|
|
* pathfind looks for a a file with name FILENAME and MODE access along colon
|
|
* delimited PATH, and build the full pathname as a string, or NULL if not found
|
|
*/
|
|
|
|
#ifdef _MSWINDOWS_
|
|
# define U_PATH_DEFAULT "C:\\msys\\1.0\\bin;C:\\MinGW\\bin;C:\\windows;C:\\windows\\system;C:\\windows\\system32"
|
|
|
|
static const char* u_check_for_suffix_exe(const char* restrict program)
|
|
{
|
|
static char program_w32[MAX_FILENAME_LEN + 1];
|
|
|
|
int len = u__strlen(program, __PRETTY_FUNCTION__);
|
|
|
|
U_INTERNAL_TRACE("u_check_for_suffix_exe(%s)", program)
|
|
|
|
if (u_endsWith(program, len, U_CONSTANT_TO_PARAM(".exe")) == false)
|
|
{
|
|
u__memcpy(program_w32, program, len, __PRETTY_FUNCTION__);
|
|
|
|
u_put_unalignedp32(program_w32+len, U_MULTICHAR_CONSTANT32('.','e','x','e'));
|
|
|
|
program = program_w32;
|
|
|
|
U_INTERNAL_PRINT("program = %s", program)
|
|
}
|
|
|
|
return program;
|
|
}
|
|
#else
|
|
# define U_PATH_DEFAULT "/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin"
|
|
#endif
|
|
|
|
bool u_pathfind(char* restrict result, const char* restrict path, uint32_t path_len, const char* restrict filename, int mode)
|
|
{
|
|
uint32_t p_index = 0;
|
|
char zPath[U_PATH_MAX+1];
|
|
|
|
U_INTERNAL_TRACE("u_pathfind(%p,%.*s,%u,%s,%d)", result, path_len, path, path_len, filename, mode)
|
|
|
|
if (path_len == 0)
|
|
{
|
|
path = getenv("PATH");
|
|
|
|
if (path) path_len = u__strlen(path, __PRETTY_FUNCTION__);
|
|
else
|
|
{
|
|
path = U_PATH_DEFAULT;
|
|
path_len = U_CONSTANT_SIZE(U_PATH_DEFAULT);
|
|
}
|
|
|
|
U_INTERNAL_PRINT("path(%u) = %.*s", path_len, path_len, path)
|
|
}
|
|
|
|
#ifdef _MSWINDOWS_
|
|
if (mode & X_OK) filename = u_check_for_suffix_exe(filename);
|
|
#endif
|
|
|
|
/* FOR each non-null entry in the colon-separated path, DO ... */
|
|
|
|
zPath[0] = '\0';
|
|
|
|
while (true)
|
|
{
|
|
char* restrict colon_unit = extract_colon_unit(zPath, path, path_len, &p_index);
|
|
|
|
/* IF no more entries, THEN quit */
|
|
|
|
if (colon_unit == 0) break;
|
|
|
|
make_absolute(result, colon_unit, filename);
|
|
|
|
/* Make sure we can access it in the way we want */
|
|
|
|
if (access(result, mode) >= 0)
|
|
{
|
|
/* We can, so normalize the name and return it below */
|
|
|
|
(void) u_canonicalize_pathname(result, strlen(result));
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Canonicalize path by building a new path. The new path differs from original in that:
|
|
*
|
|
* Multiple '/' are collapsed to a single '/'
|
|
* Trailing '/' are removed
|
|
* Leading './' and trailing '/.' are removed
|
|
* Non-leading '../' and trailing '..' are handled by removing portions of the path
|
|
*/
|
|
|
|
uint32_t u_canonicalize_pathname(char* restrict path, uint32_t sz)
|
|
{
|
|
char c;
|
|
bool bflag;
|
|
uint32_t len;
|
|
char* restrict p;
|
|
char* restrict s;
|
|
char* restrict src;
|
|
char* restrict dst;
|
|
char* restrict end = path+sz;
|
|
|
|
U_INTERNAL_TRACE("u_canonicalize_pathname(%.*s,%u)", U_min(sz,128), path, sz)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(sz, 1)
|
|
U_INTERNAL_ASSERT_EQUALS(*end, '\0')
|
|
|
|
/* Remove leading "./" */
|
|
|
|
if (u_get_unalignedp16(path) == U_MULTICHAR_CONSTANT16('.','/'))
|
|
{
|
|
for (p = path+2; p < end; p += 2)
|
|
{
|
|
if (u_get_unalignedp16(p) != U_MULTICHAR_CONSTANT16('.','/')) break;
|
|
}
|
|
|
|
len = p-path;
|
|
sz -= len;
|
|
end -= len;
|
|
|
|
if (sz <= 2)
|
|
{
|
|
end: path[1] = '\0';
|
|
|
|
return 1;
|
|
}
|
|
|
|
for (dst = path, src = p; (*dst = *src); ++src, ++dst) {}
|
|
|
|
U_INTERNAL_PRINT("Remove leading \"./\": sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(*end, '\0')
|
|
}
|
|
|
|
/* Remove trailing "/." */
|
|
|
|
if (u_get_unalignedp16(end-2) == U_MULTICHAR_CONSTANT16('/','.'))
|
|
{
|
|
for (p = end-2; p > path; p -= 2)
|
|
{
|
|
if (u_get_unalignedp16(p) != U_MULTICHAR_CONSTANT16('/','.')) break;
|
|
}
|
|
|
|
sz -= end-p;
|
|
|
|
if (sz <= 2) goto end;
|
|
|
|
(end = p)[0] = '\0';
|
|
|
|
U_INTERNAL_PRINT("Remove trailing \"/.\": sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
}
|
|
|
|
/* Remove trailing "/" */
|
|
|
|
if (end[-1] == '/')
|
|
{
|
|
for (p = end-2; p > path; --p)
|
|
{
|
|
if (*p != '/') break;
|
|
}
|
|
|
|
sz -= end - ++p;
|
|
|
|
if (sz <= 2) goto end;
|
|
|
|
(end = p)[0] = '\0';
|
|
|
|
U_INTERNAL_PRINT("Remove trailing \"/\": sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
}
|
|
|
|
/* Collapse multiple slashes */
|
|
|
|
bflag = false;
|
|
|
|
for (p = path; p < end; ++p)
|
|
{
|
|
c = *p;
|
|
|
|
if (c == '.') bflag = true;
|
|
else if (c == '/')
|
|
{
|
|
if (u_get_unalignedp16(p) == U_MULTICHAR_CONSTANT16('/','/'))
|
|
{
|
|
s = ++p;
|
|
|
|
while (*++s == '/') {}
|
|
|
|
for (dst = p, src = s; (*dst = *src); ++src, ++dst) {}
|
|
|
|
sz -= s-p;
|
|
|
|
if (sz <= 2) goto end;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_PRINT("Collapse multiple slashes: sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
|
|
if (bflag == false) return sz;
|
|
|
|
/* Collapse "/./" -> "/" */
|
|
|
|
end = (p = path) + sz;
|
|
|
|
while (p < end)
|
|
{
|
|
if (memcmp(p, U_CONSTANT_TO_PARAM("/./")) != 0) ++p;
|
|
else
|
|
{
|
|
s = p+3;
|
|
|
|
loop: if ((sz -= 2) <= 2) goto end;
|
|
|
|
end -= 2;
|
|
|
|
if (u_get_unalignedp16(s) == U_MULTICHAR_CONSTANT16('.','/'))
|
|
{
|
|
s += 2;
|
|
|
|
goto loop;
|
|
}
|
|
|
|
for (dst = p+1, src = s; (*dst = *src); ++src, ++dst) {}
|
|
}
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(*end, '\0')
|
|
|
|
U_INTERNAL_PRINT("Collapse \"/./\" -> \"/\": sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
|
|
/* Collapse "/.." with the previous part of path */
|
|
|
|
p = path;
|
|
|
|
while (p[0] &&
|
|
p[1] &&
|
|
p[2])
|
|
{
|
|
if ((p[0] != '/' ||
|
|
p[1] != '.' ||
|
|
p[2] != '.') ||
|
|
(p[3] != '/' &&
|
|
p[3] != '\0'))
|
|
{
|
|
++p;
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Search for the previous token */
|
|
|
|
s = p-1;
|
|
|
|
while ( s >= path &&
|
|
*s != '/')
|
|
{
|
|
--s;
|
|
}
|
|
|
|
++s;
|
|
|
|
/* If the previous token is "..", we cannot collapse it */
|
|
|
|
if ((s+2) == p &&
|
|
u_get_unalignedp16(s) == U_MULTICHAR_CONSTANT16('.','.'))
|
|
{
|
|
p += 3;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (p[3] != '\0')
|
|
{
|
|
/* "/../foo" -> "/foo" */
|
|
/* "token/../foo" -> "foo" */
|
|
|
|
p += 4;
|
|
|
|
bflag = (s == path && *s == '/');
|
|
|
|
for (dst = s+bflag, src = p; (*dst = *src); ++src, ++dst) {}
|
|
|
|
sz -= p-(s+bflag);
|
|
|
|
U_INTERNAL_PRINT("sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
|
|
if (sz <= 2) goto end;
|
|
|
|
p = s - (s > path);
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Trailing ".." */
|
|
|
|
if (s == path)
|
|
{
|
|
/* "token/.." -> "." */
|
|
|
|
if (path[0] != '/') path[0] = '.';
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* "foo/token/.." -> "foo" */
|
|
|
|
if (s != (path+1)) --s;
|
|
|
|
*s = '\0';
|
|
|
|
U_INTERNAL_PRINT("sz = %u s - path = %u", sz, s - path)
|
|
|
|
sz = (s - path);
|
|
|
|
break;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("sz = %u path(%u) = %s", sz, strlen(path), path)
|
|
|
|
return sz;
|
|
}
|
|
|
|
/* Prepare command for call to exec() */
|
|
|
|
int u_splitCommand(char* restrict s, uint32_t n, char** restrict argv, char* restrict pathbuf, uint32_t pathbuf_size)
|
|
{
|
|
char c;
|
|
uint32_t i = 0;
|
|
bool bpath = false;
|
|
int result = u_split(s, n, argv+1, 0);
|
|
|
|
U_INTERNAL_TRACE("u_splitCommand(%.*s,%u,%p,%p,%u)", U_min(n,128), s, n, argv, pathbuf, pathbuf_size)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(s)
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
U_INTERNAL_ASSERT_MAJOR(pathbuf_size, 0)
|
|
|
|
/* Check if command have path separator */
|
|
|
|
while ((c = argv[1][i++]))
|
|
{
|
|
if (IS_DIR_SEPARATOR(c))
|
|
{
|
|
bpath = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bpath)
|
|
{
|
|
argv[0] = argv[1];
|
|
argv[1] = (char* restrict) u_basename(argv[0], u__strlen(argv[0], __PRETTY_FUNCTION__));
|
|
|
|
pathbuf[0] = '\0';
|
|
}
|
|
else
|
|
{
|
|
argv[0] = pathbuf;
|
|
|
|
# ifdef _MSWINDOWS_
|
|
argv[1] = (char* restrict) u_check_for_suffix_exe(argv[1]);
|
|
# endif
|
|
|
|
if (u_pathfind(pathbuf, 0, 0, argv[1], R_OK | X_OK) == false) return -1;
|
|
|
|
U_INTERNAL_ASSERT_MINOR(u__strlen(pathbuf, __PRETTY_FUNCTION__), pathbuf_size)
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. Compares a filename or pathname to a pattern
|
|
*/
|
|
|
|
static const char* restrict end_p;
|
|
static const char* restrict end_s;
|
|
|
|
static inline int rangematch(const char* restrict pattern, char test, int flags, char** restrict newp)
|
|
{
|
|
char c;
|
|
int negate, ok;
|
|
|
|
U_INTERNAL_TRACE("rangematch(%.*s,%c)", end_p - pattern, pattern, test)
|
|
|
|
/**
|
|
* A bracket expression starting with an unquoted circumflex
|
|
* character produces unspecified results (IEEE 1003.2-1992, 3.13.2).
|
|
* This implementation treats it like '!', for consistency with the
|
|
* regular expression syntax. J.T. Conklin (conklin@ngai.kaleida.com)
|
|
*/
|
|
|
|
if ((negate = (*pattern == '!' || *pattern == '^')) != 0) ++pattern;
|
|
|
|
if (flags & FNM_CASEFOLD) test = u__tolower((unsigned char)test);
|
|
|
|
/**
|
|
* A right bracket shall lose its special meaning and represent itself in a bracket expression if it occurs first in the list. -- POSIX.2 2.8.3.2
|
|
*/
|
|
|
|
ok = 0;
|
|
c = *pattern++;
|
|
|
|
do {
|
|
char c2;
|
|
|
|
if (c == '\\' && (flags & FNM_NOESCAPE) == 0) c = *pattern++;
|
|
|
|
U_INTERNAL_PRINT("c = %c test = %c", c, test)
|
|
|
|
if (pattern > end_p) return -1; /* if (c == EOS) return (RANGE_ERROR); */
|
|
|
|
if (c == '/' && (flags & FNM_PATHNAME) != 0) return 0;
|
|
|
|
if (flags & FNM_CASEFOLD) c = u__tolower((unsigned char)c);
|
|
|
|
if ( * pattern == '-' &&
|
|
(c2 = *(pattern+1)) != ']' &&
|
|
(pattern+1) != end_p)
|
|
{
|
|
pattern += 2;
|
|
|
|
if (c2 == '\\' && (flags & FNM_NOESCAPE) == 0) c2 = *pattern++;
|
|
|
|
if (pattern > end_p) return -1; /* if (c2 == EOS) return (RANGE_ERROR); */
|
|
|
|
if ((flags & FNM_CASEFOLD) != 0) c2 = u__tolower((unsigned char)c2);
|
|
|
|
if (c <= test &&
|
|
test <= c2)
|
|
{
|
|
ok = 1;
|
|
}
|
|
}
|
|
else if (c == test)
|
|
{
|
|
ok = 1;
|
|
}
|
|
}
|
|
while ((c = *pattern++) != ']');
|
|
|
|
*newp = (char* restrict) pattern;
|
|
|
|
return (ok != negate);
|
|
}
|
|
|
|
__pure static int kfnmatch(const char* restrict pattern, const char* restrict string, int flags, int nesting)
|
|
{
|
|
char c, test;
|
|
char* restrict newp;
|
|
const char* restrict stringstart;
|
|
|
|
U_INTERNAL_TRACE("kfnmatch(%.*s,%.*s,%d,%d)", end_p - pattern, pattern, end_s - string, string, flags, nesting)
|
|
|
|
if (nesting == 20) return 1;
|
|
|
|
for (stringstart = string;;)
|
|
{
|
|
c = *pattern++;
|
|
|
|
if (pattern > end_p)
|
|
{
|
|
if ((flags & FNM_LEADING_DIR) != 0 && *string == '/') return 0;
|
|
|
|
return (string != end_s);
|
|
}
|
|
|
|
switch (c)
|
|
{
|
|
case '?':
|
|
{
|
|
if (string == end_s) return 1;
|
|
|
|
if (*string == '/' && (flags & FNM_PATHNAME) != 0) return 1;
|
|
|
|
if (*string == '.' && (flags & FNM_PERIOD) != 0 &&
|
|
(string == stringstart || ((flags & FNM_PATHNAME) != 0 && *(string - 1) == '/')))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
++string;
|
|
}
|
|
break;
|
|
|
|
case '*':
|
|
{
|
|
c = *pattern;
|
|
|
|
/* Collapse multiple stars */
|
|
|
|
while (c == '*') c = *++pattern;
|
|
|
|
if (*string == '.' && (flags & FNM_PERIOD) != 0 &&
|
|
(string == stringstart || ((flags & FNM_PATHNAME) != 0 && *(string - 1) == '/')))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* Optimize for pattern with * at end or before / */
|
|
|
|
if (pattern == end_p) /* if (c == EOS) */
|
|
{
|
|
if ((flags & FNM_PATHNAME) != 0) return ((flags & FNM_LEADING_DIR) != 0 || memchr(string, '/', end_s - string) == 0 ? 0 : 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (c == '/' && (flags & FNM_PATHNAME) != 0)
|
|
{
|
|
if ((string = (const char* restrict)memchr(string, '/', end_s - string)) == 0) return 1;
|
|
|
|
break;
|
|
}
|
|
|
|
/* General case, use recursion */
|
|
|
|
while (string < end_s)
|
|
{
|
|
test = *string;
|
|
|
|
if (!kfnmatch(pattern, string, flags & ~FNM_PERIOD, nesting + 1)) return 0;
|
|
|
|
if (test == '/' && (flags & FNM_PATHNAME) != 0) break;
|
|
|
|
++string;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
case '[':
|
|
{
|
|
if (string == end_s) return 1;
|
|
|
|
if (*string == '/' && (flags & FNM_PATHNAME) != 0) return 1;
|
|
|
|
if (*string == '.' && (flags & FNM_PERIOD) != 0 &&
|
|
(string == stringstart || ((flags & FNM_PATHNAME) != 0 && *(string - 1) == '/')))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
switch (rangematch(pattern, (char)*string, flags, (char** restrict)&newp))
|
|
{
|
|
case -1: goto norm;
|
|
case 1: pattern = newp; break;
|
|
case 0: return 1;
|
|
}
|
|
|
|
++string;
|
|
}
|
|
break;
|
|
|
|
case '\\':
|
|
{
|
|
if ((flags & FNM_NOESCAPE) == 0)
|
|
{
|
|
c = *pattern++;
|
|
|
|
if (pattern > end_p) /* if ((c = *pattern++) == EOS) */
|
|
{
|
|
c = '\\';
|
|
|
|
--pattern;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
{
|
|
norm: if (c == *string)
|
|
{
|
|
}
|
|
else if ((flags & FNM_CASEFOLD) != 0 && (u__tolower((unsigned char)c) == u__tolower((unsigned char)*string)))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
string++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
#define __FNM_FLAGS (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR | FNM_CASEFOLD | FNM_INVERT)
|
|
|
|
bool u_fnmatch(const char* restrict string, uint32_t n1, const char* restrict pattern, uint32_t n2, int flags)
|
|
{
|
|
int result;
|
|
|
|
U_INTERNAL_TRACE("u_fnmatch(%.*s,%u,%.*s,%u,%d)", U_min(n1,128), string, n1, n2, pattern, n2, flags)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(n1, 0)
|
|
U_INTERNAL_ASSERT_MAJOR(n2, 0)
|
|
U_INTERNAL_ASSERT_POINTER(string)
|
|
U_INTERNAL_ASSERT_POINTER(pattern)
|
|
U_INTERNAL_ASSERT_EQUALS(flags & ~__FNM_FLAGS, 0)
|
|
|
|
end_s = string + n1;
|
|
end_p = pattern + n2;
|
|
|
|
result = kfnmatch(pattern, string, flags, 0);
|
|
|
|
return ((flags & FNM_INVERT) != 0 ? (result != 0) : (result == 0));
|
|
}
|
|
|
|
/* buffer type identification - Assumed an ISO-1 character set */
|
|
|
|
#define U_LOOP_STRING( exec_code ) unsigned char c = *s; while (n--) { exec_code ; c = *(++s); }
|
|
|
|
/**
|
|
* #if !defined(GCOV)
|
|
*
|
|
* (Duff's device) This is INCREDIBLY ugly, but fast. We break the string up into 8 byte units. On the first
|
|
* time through the loop we get the "leftover bytes" (strlen % 8). On every other iteration, we perform 8 BODY's
|
|
* so we handle all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If this routine is heavily
|
|
* used enough, it's worth the ugly coding
|
|
*
|
|
* #undef U_LOOP_STRING
|
|
* #define U_LOOP_STRING( exec_code ) { \
|
|
* unsigned char c; \
|
|
* uint32_t U_LOOP_CNT = (n + 8 - 1) >> 3; \
|
|
* switch (n & (8 - 1)) { \
|
|
* case 0: \
|
|
* do { { c = *s++; exec_code; } \
|
|
* case 7: { c = *s++; exec_code; } \
|
|
* case 6: { c = *s++; exec_code; } \
|
|
* case 5: { c = *s++; exec_code; } \
|
|
* case 4: { c = *s++; exec_code; } \
|
|
* case 3: { c = *s++; exec_code; } \
|
|
* case 2: { c = *s++; exec_code; } \
|
|
* case 1: { c = *s++; exec_code; } \
|
|
* } while (--U_LOOP_CNT); } }
|
|
*
|
|
* #endif
|
|
*/
|
|
|
|
__pure bool u_isName(const char* restrict s, uint32_t n)
|
|
{
|
|
U_LOOP_STRING( if (u__isname(c) == false) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isName(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isDigit(const char* restrict s, uint32_t n)
|
|
{
|
|
U_LOOP_STRING( if (u__isdigit(c) == false) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isDigit(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isWhiteSpace(const char* restrict s, uint32_t n)
|
|
{
|
|
U_LOOP_STRING( if (u__isspace(c) == false) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isWhiteSpace(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isText(const unsigned char* restrict s, uint32_t n)
|
|
{
|
|
U_LOOP_STRING( if (u__istext(c) == false) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isText(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isBase64(const char* restrict s, uint32_t n)
|
|
{
|
|
/* u__isalnum(c) || (c == '+') || (c == '/') || (c == '=') */
|
|
|
|
U_LOOP_STRING( if (u__isbase64(c) == false) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isBase64(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isBase64Url(const char* restrict s, uint32_t n)
|
|
{
|
|
/* u__isalnum(c) || (c == '+') || (c == '/') || (c == '=') */
|
|
|
|
U_LOOP_STRING( if (u__isb64url(c) == false) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isBase64Url(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isPrintable(const char* restrict s, uint32_t n, bool bline)
|
|
{
|
|
U_LOOP_STRING( if (u__isprint(c) == false && (bline == false || u__islterm(c) == false)) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isPrintable(%.*s,%u,%d)", U_min(n,128), s, n, bline)
|
|
|
|
return true;
|
|
}
|
|
|
|
/* The NT file naming convention specifies that all characters greater than ASCII 31 to be used except for the following: "/:<>*?\| */
|
|
|
|
__pure bool u_isFileName(const char* restrict s, uint32_t n)
|
|
{
|
|
U_LOOP_STRING( if (u__isvalidchar(c) == false || u__isfnameinvalid(c)) return false )
|
|
|
|
U_INTERNAL_TRACE("u_isFileName(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isUrlEncoded(const char* restrict s, uint32_t n, bool bquery)
|
|
{
|
|
bool benc = false;
|
|
unsigned char c = *s;
|
|
|
|
U_INTERNAL_TRACE("u_isUrlEncoded(%.*s,%u,%d)", U_min(n,128), s, n, bquery)
|
|
|
|
U_INTERNAL_ASSERT_MAJOR(n, 0)
|
|
|
|
while (n--)
|
|
{
|
|
U_INTERNAL_PRINT("c = %c n = %u", c, n)
|
|
|
|
if (bquery == false ||
|
|
u__isurlqry(c) == false) /* URL: char FROM query '&' (38 0x26) | '=' (61 0x3D) | '#' (35 0x23) */
|
|
{
|
|
if (c == '%')
|
|
{
|
|
if (n >= 2)
|
|
{
|
|
if (u__isxdigit(s[1]) &&
|
|
u__isxdigit(s[2]))
|
|
{
|
|
benc = true;
|
|
}
|
|
|
|
s += 2;
|
|
n -= 2;
|
|
}
|
|
}
|
|
else if (c == '+') benc = true;
|
|
else if (u__is2urlenc(c)) return false;
|
|
}
|
|
|
|
c = *(++s);
|
|
}
|
|
|
|
return benc;
|
|
}
|
|
|
|
__pure bool u_isUrlEncodeNeeded(const char* restrict s, uint32_t n)
|
|
{
|
|
unsigned char c = *s;
|
|
|
|
U_INTERNAL_TRACE("u_isUrlEncodeNeeded(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
while (n--)
|
|
{
|
|
if (u__is2urlenc(c) &&
|
|
(c != '%' ||
|
|
u__isxdigit(s[1]) == false ||
|
|
u__isxdigit(s[2]) == false))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
c = *(++s);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
__pure bool u_isIPv4Addr(const char* restrict s, uint32_t n)
|
|
{
|
|
U_INTERNAL_TRACE("u_isIPv4Addr(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
/* u__isdigit(c) || (c == '.') */
|
|
|
|
if (n >= U_CONSTANT_SIZE("8.8.8.8") &&
|
|
n <= U_CONSTANT_SIZE("255.255.255.255") &&
|
|
u__isdigit(s[0]) &&
|
|
u__isdigit(s[--n]))
|
|
{
|
|
uint32_t count = 0;
|
|
|
|
while (n--)
|
|
{
|
|
unsigned char c = *(++s);
|
|
|
|
U_INTERNAL_PRINT("c = %c n = %u count = %u", c, n, count)
|
|
|
|
if (c == '.')
|
|
{
|
|
c = *(++s);
|
|
|
|
--n;
|
|
++count;
|
|
}
|
|
|
|
if (u__isdigit(c) == false) return false;
|
|
}
|
|
|
|
if (count == 3) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
__pure bool u_isIPv6Addr(const char* restrict s, uint32_t n)
|
|
{
|
|
/* u__isxdigit(c) || (c == '.') || (c == ':') */
|
|
|
|
uint32_t count1 = 0,
|
|
count2 = 0;
|
|
unsigned char c = *s;
|
|
|
|
U_INTERNAL_TRACE("u_isIPv6Addr(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
while (n--)
|
|
{
|
|
if (c == '.') ++count1;
|
|
else if (c == ':') ++count2;
|
|
else if (u__isxdigit(c) == false) return false;
|
|
|
|
c = *(++s);
|
|
}
|
|
|
|
if (count1 != 3 &&
|
|
count2 < 5)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure bool u_isHostName(const char* restrict ptr, uint32_t len)
|
|
{
|
|
int ch;
|
|
const char* restrict end = ptr + len;
|
|
|
|
U_INTERNAL_TRACE("u_isHostName(%.*s,%u)", U_min(len,128), ptr, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(ptr)
|
|
|
|
if (ptr[ 0] == '[' &&
|
|
end[-1] == ']')
|
|
{
|
|
if (u_isIPv6Addr(ptr+1, len-1)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
ch = *(unsigned char*)ptr;
|
|
|
|
/**
|
|
* Host names may contain only alphanumeric characters, minus signs ("-"), and periods (".").
|
|
* They must begin with an alphabetic character and end with an alphanumeric character
|
|
*
|
|
* Several well known Internet and technology companies have DNS records that use the underscore:
|
|
*
|
|
* see http://domainkeys.sourceforge.net/underscore.html
|
|
*/
|
|
|
|
if (u__isalpha(ch))
|
|
{
|
|
if (u__isalnum(end[-1]) == false) return false;
|
|
|
|
while (++ptr < end)
|
|
{
|
|
ch = *(unsigned char*)ptr;
|
|
|
|
if (u__ishname(ch) == false) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
while (ptr < end)
|
|
{
|
|
ch = *(unsigned char*)ptr;
|
|
|
|
if (u__isipv4(ch) == false &&
|
|
u__isipv6(ch) == false)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
++ptr;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
__pure const char* u_isUrlScheme(const char* restrict url, uint32_t len)
|
|
{
|
|
const char* restrict ptr;
|
|
const char* restrict first_slash;
|
|
|
|
U_INTERNAL_TRACE("u_isUrlScheme(%.*s,%u)", U_min(len,128), url, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(url)
|
|
|
|
first_slash = (const char* restrict) memchr(url, '/', len);
|
|
|
|
if (first_slash == 0 ||
|
|
first_slash == url || /* Input with no slash at all or slash first can't be URL */
|
|
first_slash[-1] != ':' ||
|
|
first_slash[ 1] != '/' || /* Character before must be : and next must be / */
|
|
first_slash == (url + 1)) /* There must be something before the :// */
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* Check all characters up to first slash - 1. Only alphanum is allowed */
|
|
|
|
ptr = url;
|
|
|
|
--first_slash;
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(first_slash[0], ':')
|
|
|
|
while (ptr < first_slash)
|
|
{
|
|
int ch = *(unsigned char*)ptr;
|
|
|
|
/**
|
|
* The set of valid URL schemes, as per STD66 (RFC3986) is '[A-Za-z][A-Za-z0-9+.-]*'.
|
|
* But use sightly looser check of '[A-Za-z0-9][A-Za-z0-9+.-]*' because earlier version
|
|
* of check used '[A-Za-z0-9]+' so not to break any remote helpers
|
|
*/
|
|
|
|
if (u__isalnum(ch) == false &&
|
|
(ptr != url && u__ispecial(ch)) == false)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
++ptr;
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(ptr[1], '/')
|
|
U_INTERNAL_ASSERT_EQUALS(ptr[2], '/')
|
|
|
|
return ptr+3;
|
|
}
|
|
|
|
__pure bool u_isURL(const char* restrict url, uint32_t len)
|
|
{
|
|
const char* restrict ptr;
|
|
|
|
U_INTERNAL_TRACE("u_isURL(%.*s,%u)", U_min(len,128), url, len)
|
|
|
|
/* proto://hostname[:port]/[path]?[query] */
|
|
|
|
ptr = (const char* restrict) u_isUrlScheme(url, len);
|
|
|
|
if (ptr)
|
|
{
|
|
int ch;
|
|
const char* restrict end;
|
|
const char* restrict tmp;
|
|
|
|
len -= (ptr - url);
|
|
|
|
tmp = ptr;
|
|
end = ptr + len;
|
|
|
|
while (tmp < end)
|
|
{
|
|
ch = *(unsigned char*)tmp;
|
|
|
|
if (ch == '/' ||
|
|
ch == '?')
|
|
{
|
|
len = (end = tmp) - ptr;
|
|
|
|
break;
|
|
}
|
|
|
|
++tmp;
|
|
}
|
|
|
|
U_INTERNAL_PRINT("ptr = %.*s", U_min(len,128), ptr)
|
|
|
|
tmp = (const char* restrict) memrchr(ptr, ':', len);
|
|
|
|
if ( tmp &&
|
|
((end - tmp) <= 5)) /* NB: port number: 0-65536 */
|
|
{
|
|
len = tmp - ptr;
|
|
|
|
while (++tmp < end)
|
|
{
|
|
ch = *(unsigned char*)tmp;
|
|
|
|
U_INTERNAL_PRINT("ch = %c", ch)
|
|
|
|
if (u__isdigit(ch) == 0) return false;
|
|
}
|
|
}
|
|
|
|
if (u_isHostName(ptr, len)) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
__pure bool u_isHTML(const char* restrict ptr)
|
|
{
|
|
/* NB: we check for <(h(1-6|tml)|!DOCTYPE) */
|
|
|
|
U_INTERNAL_TRACE("u_isHTML(%.*s)", 12, ptr)
|
|
|
|
switch (u_get_unalignedp32(ptr))
|
|
{
|
|
case U_MULTICHAR_CONSTANT32('<','h','1','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','1','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','h','2','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','2','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','h','3','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','3','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','h','4','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','4','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','h','5','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','5','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','h','6','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','6','>'):
|
|
case U_MULTICHAR_CONSTANT32('<','h','t','m'):
|
|
case U_MULTICHAR_CONSTANT32('<','H','T','M'):
|
|
case U_MULTICHAR_CONSTANT32('<','!','d','o'):
|
|
case U_MULTICHAR_CONSTANT32('<','!','D','O'): return true;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
__pure bool u_isMacAddr(const char* restrict p, uint32_t len)
|
|
{
|
|
uint32_t c;
|
|
|
|
U_INTERNAL_TRACE("u_isMacAddr(%.*s,%u)", U_min(len,128), p, len)
|
|
|
|
/* windows-style: 01-23-45-67-89-ab, 01:23:45:67:89:ab */
|
|
|
|
if (len == (6 * 3) - 1)
|
|
{
|
|
for (c = 0; c < len; ++c)
|
|
{
|
|
if ((c % 3) == 2)
|
|
{
|
|
if (p[c] != ':' &&
|
|
p[c] != '-') return false;
|
|
}
|
|
else
|
|
{
|
|
if (!u__isxdigit(p[c])) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* cisco-style: 0123.4567.89ab */
|
|
|
|
if (len == (3 * 5) - 1)
|
|
{
|
|
for (c = 0; c < len; ++c)
|
|
{
|
|
if ((c % 5) == 4)
|
|
{
|
|
if (p[c] != '.') return false;
|
|
}
|
|
else
|
|
{
|
|
if (!u__isxdigit(p[c])) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
__pure bool u_isXMacAddr(const char* restrict s, uint32_t n)
|
|
{
|
|
U_INTERNAL_TRACE("u_isXMacAddr(%.*s,%u)", U_min(n,128), s, n)
|
|
|
|
if (n == 12)
|
|
{
|
|
U_LOOP_STRING( if (u__isxdigit(c) == false) return false )
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************
|
|
* From rfc2044: encoding of the Unicode values on UTF-8: *
|
|
* *
|
|
* UCS-4 range (hex.) UTF-8 octet sequence (binary) *
|
|
* 0000 0000-0000 007F 0xxxxxxx *
|
|
* 0000 0080-0000 07FF 110xxxxx 10xxxxxx *
|
|
* 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx *
|
|
************************************************************************/
|
|
|
|
const unsigned char u_validate_utf8[] = {
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 00..1f */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20..3f */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40..5f */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60..7f */
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, /* 80..9f */
|
|
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* a0..bf */
|
|
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* c0..df */
|
|
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, /* e0..ef */
|
|
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, /* f0..ff */
|
|
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, /* s0..s0 */
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, /* s1..s2 */
|
|
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, /* s3..s4 */
|
|
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, /* s5..s6 */
|
|
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* s7..s8 */
|
|
};
|
|
|
|
__pure bool u_isUTF8(const unsigned char* restrict buf, uint32_t len)
|
|
{
|
|
/*
|
|
bool result = false;
|
|
uint32_t j, following;
|
|
*/
|
|
uint32_t code = 0, state = 0;
|
|
const unsigned char* restrict end = buf + len;
|
|
|
|
U_INTERNAL_TRACE("u_isUTF8(%.*s,%u)", U_min(len,128), buf, len)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(buf)
|
|
U_INTERNAL_ASSERT_MAJOR(len, 0)
|
|
|
|
while (buf < end)
|
|
{
|
|
/**
|
|
* UTF is a string of 1, 2, 3 or 4 bytes. The valid strings are as follows (in "bit format"):
|
|
*
|
|
* 0xxxxxxx valid 1-byte
|
|
* 110xxxxx 10xxxxxx valid 2-byte
|
|
* 1110xxxx 10xxxxxx 10xxxxxx valid 3-byte
|
|
* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx valid 4-byte
|
|
* ........
|
|
*/
|
|
|
|
unsigned char c = *buf++;
|
|
|
|
/**
|
|
* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
|
*
|
|
* see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details
|
|
*/
|
|
|
|
uint32_t type = u_validate_utf8[c];
|
|
|
|
code = (state != 0 ? (c & 0x3fu) | (code << 6)
|
|
: (0xff >> type) & (uint32_t)(c));
|
|
|
|
state = u_validate_utf8[256 + state * 16 + type];
|
|
|
|
U_INTERNAL_PRINT("c = %u type = %u code = %u state = %u", c, type, code, state)
|
|
|
|
if (state == 1) return false;
|
|
|
|
/**
|
|
* if ((c & 0x80) == 0) // 0xxxxxxx is plain ASCII
|
|
* {
|
|
* // Even if the whole file is valid UTF-8 sequences, still reject it if it uses weird control characters
|
|
*
|
|
* if ((u_cttab(c) & 0x0200) == 0) return false;
|
|
* }
|
|
* else if ((c & 0x40) == 0) return false; // 10xxxxxx never 1st byte
|
|
* else
|
|
* {
|
|
* // 11xxxxxx begins UTF-8
|
|
*
|
|
* if ((c & 0x20) == 0) following = 1; // 110xxxxx
|
|
* else if ((c & 0x10) == 0) following = 2; // 1110xxxx
|
|
* else if ((c & 0x08) == 0) following = 3; // 11110xxx
|
|
* else if ((c & 0x04) == 0) following = 4; // 111110xx
|
|
* else if ((c & 0x02) == 0) following = 5; // 1111110x
|
|
* else return false;
|
|
*
|
|
* for (j = 0; j < following; j++)
|
|
* {
|
|
* if (buf >= end) return result;
|
|
*
|
|
* c = *buf++;
|
|
*
|
|
* if ((c & 0x80) == 0 ||
|
|
* (c & 0x40))
|
|
* {
|
|
* return false;
|
|
* }
|
|
* }
|
|
*
|
|
* result = true;
|
|
* }
|
|
*
|
|
* return result;
|
|
*/
|
|
}
|
|
|
|
return (state == 0);
|
|
}
|
|
|
|
__pure int u_isUTF16(const unsigned char* restrict buf, uint32_t len)
|
|
{
|
|
uint32_t be, i;
|
|
|
|
U_INTERNAL_TRACE("u_isUTF16(%.*s,%u)", U_min(len,128), buf, len)
|
|
|
|
if (len < 2) return 0;
|
|
|
|
if (u_get_unalignedp16(buf) == U_MULTICHAR_CONSTANT16(0xff,0xfe)) be = 0;
|
|
else if (u_get_unalignedp16(buf) == U_MULTICHAR_CONSTANT16(0xfe,0xff)) be = 1;
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (i = 2; i + 1 < len; i += 2)
|
|
{
|
|
uint32_t c = (be ? buf[i+1] + 256 * buf[i]
|
|
: buf[i] + 256 * buf[i+1]);
|
|
|
|
if ( c == 0xfffe ||
|
|
(c < 128 && ((u_cttab(c) & 0x0200) == 0)))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return (1 + be);
|
|
}
|
|
|
|
/**
|
|
* From RFC 3986
|
|
*
|
|
* #define U_URI_UNRESERVED 0 // ALPHA (%41-%5A and %61-%7A) DIGIT (%30-%39) '-' '.' '_' '~'
|
|
* #define U_URI_PCT_ENCODED 1
|
|
* #define U_URI_GEN_DELIMS 2 // ':' '/' '?' '#' '[' ']' '@'
|
|
* #define U_URI_SUB_DELIMS 4 // '!' '$' '&' '\'' '(' ')' '*' '+' ',' ';' '='
|
|
*
|
|
* unsigned int u_uri_encoded_char_mask = (U_URI_PCT_ENCODED | U_URI_GEN_DELIMS | U_URI_SUB_DELIMS);
|
|
*
|
|
* const unsigned char u_uri_encoded_char[256] = {
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 - 0x0f
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 - 0x1f
|
|
* 1, 4, 1, 2, 4, 1, 4, 4, 4, 4, 4, 4, 4, 0, 0, 2, // !"#$%&'()*+,-./
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, 4, 1, 2, // 0123456789:;<=>?
|
|
* 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // @ABCDEFGHIJKLMNO
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 0, // PQRSTUVWXYZ[\]^_
|
|
* 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // `abcdefghijklmno
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, // pqrstuvwxyz{|}~
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
|
* };
|
|
*/
|
|
|
|
#define U__S 0x00000001 /* character space ' ' (32 0x20) */
|
|
#define U__E 0x00000002 /* character used in printf format */
|
|
#define U__H 0x00000004 /* character '+' (43 0x2B) */
|
|
#define U__V 0x00000008 /* character ',' (44 0x2C) */
|
|
#define U__O 0x00000010 /* character minus '-' (45 0x2D) */
|
|
#define U__N 0x00000020 /* character point '.' (46 0x2E) */
|
|
#define U__G 0x00000040 /* character ':' (58 0x3A) */
|
|
#define U__Q 0x00000080 /* character underbar '_' (95 0x5F) */
|
|
#define U__B 0x00000100 /* character tab \t (09 0x09) */
|
|
#define U__R 0x00000200 /* carriage return or new line (a \r or \n) */
|
|
#define U__W 0x00000400 /* WhiteSpace */
|
|
#define U__C 0x00000800 /* Control character */
|
|
#define U__D 0x00001000 /* Digit */
|
|
#define U__L 0x00002000 /* Lowercase */
|
|
#define U__I 0x00004000 /* Punctuation */
|
|
#define U__U 0x00008000 /* Uppercase */
|
|
#define U__Z 0x00010000 /* Octal */
|
|
#define U__F 0x00020000 /* character never appears in plain ASCII text */
|
|
#define U__T 0x00040000 /* character appears in plain ASCII text */
|
|
#define U__X 0x00080000 /* Hexadecimal */
|
|
#define U__A 0x00100000 /* BASE64 encoded: '+' (43 0x2B) | '/' (47 0x2F) | '=' (61 0x3D) */
|
|
#define U__M 0x00200000 /* HTTP request/response (COPY, DELETE, GET, HEAD|HTTP, OPTIONS, POST/PUT/PATCH) */
|
|
#define U__Y 0x00400000 /* HTTP header (Accept...,Host,Range,Cookie,Referer,X-Real-IP,User-Agent,Connection,Content-...) */
|
|
#define U__K 0x00800000 /* string quote: '"' (34 0x22) | ''' (39 0x27) */
|
|
#define U__J 0x01000000 /* HTML special: '&' (38 0x26) | '<' (60 0x3C) | '>' (62 0x3E) */
|
|
#define U__UE 0x02000000 /* TO URL encode: ' ' (32 0x20) | ... */
|
|
#define U__UQ 0x04000000 /* FROM URL query: '&' (38 0x26) | '=' (61 0x3D) | '#' (35 0x23) */
|
|
#define U__UF 0x08000000 /* filename invalid char: '"' '*' ':' '<' '>' '?' '\' '|' */
|
|
#define U__XM 0x10000000 /* char >= (32 0x20) */
|
|
#define U__XE 0x20000000 /* char '}' | ']' */
|
|
#define U__XD 0x40000000 /* char [1-9] */
|
|
|
|
#define LU (U__L | U__U)
|
|
#define LX (U__L | U__X)
|
|
#define UX (U__U | U__X)
|
|
#define LT (U__L | U__T | U__XM)
|
|
#define UT (U__U | U__T | U__XM)
|
|
#define DT (U__D | U__T | U__E | U__XM)
|
|
#define IT (U__I | U__T | U__E | U__UF | U__XM)
|
|
#define ITK (U__I | U__T | U__K | U__UF | U__XM)
|
|
#define ITF (U__I | U__T | U__XM)
|
|
#define ITA (U__I | U__T | U__A | U__XM)
|
|
#define ITQ (U__I | U__T | U__Q | U__XM)
|
|
#define LTE (U__L | U__T | U__E | U__XM)
|
|
#define LTY (U__L | U__T | U__Y | U__E | U__XM)
|
|
#define UXT (U__U | U__X | U__T | U__E | U__XM)
|
|
#define ITN (U__I | U__T | U__N | U__E | U__XM)
|
|
#define ITO (U__I | U__T | U__O | U__E | U__XM)
|
|
#define DTZ (U__D | U__T | U__Z | U__E | U__XM)
|
|
#define DTW (U__D | U__T | U__E | U__XM | U__XD)
|
|
#define UTE (U__U | U__T | U__E | U__XM)
|
|
#define UTY (U__U | U__T | U__Y | U__E | U__XM)
|
|
#define UTM (U__U | U__T | U__M | U__E | U__XM)
|
|
#define LTM (U__L | U__T | U__M | U__E | U__XM)
|
|
#define LXT (U__L | U__X | U__T | U__E | U__XM)
|
|
#define DTZW (U__D | U__T | U__Z | U__E | U__XM | U__XD)
|
|
#define LTMY (U__L | U__T | U__M | U__Y | U__E | U__XM)
|
|
#define UTMY (U__U | U__T | U__M | U__Y | U__E | U__XM)
|
|
#define UXTM (U__U | U__X | U__T | U__M | U__E | U__XM)
|
|
#define UXTY (U__U | U__X | U__T | U__Y | U__E | U__XM)
|
|
#define LXTM (U__L | U__X | U__T | U__M | U__E | U__XM)
|
|
#define LXTY (U__L | U__X | U__T | U__Y | U__E | U__XM)
|
|
#define LXTMY (U__L | U__X | U__T | U__M | U__E | U__Y | U__XM)
|
|
#define UXTMY (U__U | U__X | U__T | U__M | U__E | U__Y | U__XM)
|
|
|
|
#define IF (U__I | U__F | U__UE)
|
|
#define CF (U__C | U__F | U__UE)
|
|
#define CT (U__C | U__T | U__UE)
|
|
#define WF (U__W | U__F | U__UE)
|
|
#define FUE (U__F | U__UE | U__XM)
|
|
#define CWT (U__C | U__W | U__T | U__UE)
|
|
#define CWF (U__C | U__W | U__F | U__UE)
|
|
#define ITG (U__I | U__T | U__G | U__UF | U__UE | U__XM)
|
|
#define ITJ (U__I | U__T | U__J | U__UF | U__XM)
|
|
#define SWT (U__S | U__W | U__T | U__UE | U__XM | U__E)
|
|
#define ITUE (U__I | U__T | U__UF | U__UE | U__XM)
|
|
#define CWBT (U__C | U__W | U__B | U__T | U__UE)
|
|
#define CWRT (U__C | U__W | U__R | U__T | U__UE)
|
|
#define ITUQ (U__I | U__T | U__UE | U__XM)
|
|
#define ITAH (U__I | U__T | U__A | U__H | U__UE | U__XM | U__E)
|
|
#define ITAU (U__I | U__T | U__A | U__UE | U__XM | U__UQ)
|
|
#define ITJU (U__I | U__T | U__J | U__UE | U__XM | U__UQ)
|
|
#define ITVF (U__I | U__T | U__V | U__XM)
|
|
#define ITKF (U__I | U__T | U__K | U__XM | U__E)
|
|
|
|
#define ITUEF (U__I | U__T | U__UE | U__XM)
|
|
#define ITUEFX (U__I | U__T | U__UE | U__XM | U__XE)
|
|
#define ITUEFQ (U__I | U__T | U__UE | U__XM | U__UQ | U__E)
|
|
|
|
const unsigned int u__ct_tab[256] = {
|
|
/* BEL BS HT LF FF CR */
|
|
CF, CF, CF, CF, CF, CF, CF, CT, CT, CWBT, CWRT, CWF, CWT, CWRT, CF, CF,/* 0x00 */
|
|
/* ESC */
|
|
CF, CF, CF, CF, CF, CF, CF, CF, CF, CF, CF, CT, CF, CF, CF, CF,/* 0x10 */
|
|
|
|
/* ' ' '!' '"' '#' '$' '%' '&' '\'' '(' ')' '*' '+' ',' '-' '.' '/' */
|
|
SWT, ITF,ITK,ITUEFQ,ITF,ITUQ,ITJU,ITKF,ITF,ITF, IT,ITAH,ITVF, ITO, ITN, ITA, /* 0x20 */
|
|
/* '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' ':' ';' '<' '=' '>' '?' */
|
|
DTZ,DTZW,DTZW,DTZW,DTZW,DTZW,DTZW,DTZW,DTW,DTW,ITG,ITUEF,ITJ, ITAU, ITJ,ITUE, /* 0x30 */
|
|
/* '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' */
|
|
ITF,UXTY,UXT,UXTMY,UXTM, UXT,UXT, UTM,UTMY, UTY,UTE,UT, UTE, UTE, UTE, UTM, /* 0x40 */
|
|
/* 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' '[' '\' ']' '^' '_' */
|
|
UTM,UTE,UTY, UTY, UTE, UTY, UTE,UTE, UTY, UTE, UT,ITUEF,ITUE,ITUEFX,ITUEF,ITQ,/* 0x50 */
|
|
/* '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' */
|
|
ITUEF,LXTY,LXT,LXTMY,LXTM,LXT, LXT,LTM,LTMY, LTY,LTE, LT, LTE, LT, LTE, LTM, /* 0x60 */
|
|
/* 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' '{' '|' '}' '~' */
|
|
LTM,LTE,LTY, LTY, LT, LTY, LTE,LTE, LTY, LT, LT,ITUEF,ITUE,ITUEFX,ITF, CF, /* 0x70 */
|
|
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0x80 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0x90 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0xa0 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0xb0 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0xc0 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0xd0 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, /* 0xe0 */
|
|
FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE, FUE /* 0xf0 */
|
|
|
|
/**
|
|
* ISO-1 character set
|
|
*
|
|
* C, C, C, C, C, CT, C, C, C, C, C, C, C, C, C, C,
|
|
* C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C,
|
|
* W, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
|
|
* I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I,
|
|
* U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U,
|
|
* U, U, U, U, U, U, U, I, U, U, U, U, U, U, U, LU,
|
|
* L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L,
|
|
* L, L, L, L, L, L, L, I, L, L, L, L, L, L, L, L
|
|
*/
|
|
};
|
|
|
|
#undef U__S
|
|
#undef U__H
|
|
#undef U__V
|
|
#undef U__O
|
|
#undef U__N
|
|
#undef U__G
|
|
#undef U__Q
|
|
#undef U__B
|
|
#undef U__R
|
|
#undef U__W
|
|
#undef U__C
|
|
#undef U__D
|
|
#undef U__L
|
|
#undef U__I
|
|
#undef U__U
|
|
#undef U__Z
|
|
#undef U__F
|
|
#undef U__T
|
|
#undef U__X
|
|
#undef U__A
|
|
#undef U__M
|
|
#undef U__Y
|
|
#undef U__K
|
|
#undef U__J
|
|
#undef U__UE
|
|
#undef U__UQ
|
|
#undef U__UF
|
|
#undef U__XM
|
|
#undef U__XE
|
|
#undef U__XD
|
|
|
|
#undef CF
|
|
#undef CT
|
|
#undef DT
|
|
#undef LU
|
|
#undef LX
|
|
#undef LT
|
|
#undef LTE
|
|
#undef UT
|
|
#undef UTE
|
|
#undef UX
|
|
#undef WF
|
|
#undef IF
|
|
#undef IT
|
|
#undef FUE
|
|
#undef CWT
|
|
#undef CWF
|
|
#undef DTZ
|
|
#undef DTW
|
|
#undef ITA
|
|
#undef ITF
|
|
#undef ITG
|
|
#undef ITK
|
|
#undef ITJ
|
|
#undef ITN
|
|
#undef ITO
|
|
#undef ITQ
|
|
#undef LTM
|
|
#undef LTY
|
|
#undef LXT
|
|
#undef UXT
|
|
#undef SWT
|
|
#undef UTY
|
|
#undef UTM
|
|
#undef DTZW
|
|
#undef ITVF
|
|
#undef ITKF
|
|
#undef ITUE
|
|
#undef ITAH
|
|
#undef CWRT
|
|
#undef LTMY
|
|
#undef LXTM
|
|
#undef LXTY
|
|
#undef UXTM
|
|
#undef UTMY
|
|
#undef UXTY
|
|
#undef ITAU
|
|
#undef ITJU
|
|
#undef ITUQ
|
|
#undef ITUEF
|
|
#undef ITUEFX
|
|
#undef ITUEFQ
|
|
#undef LXTMY
|
|
#undef UXTMY
|
|
|
|
/* Table for converting to lower-case */
|
|
|
|
const unsigned char u__ct_tol[256] = {
|
|
'\0', '\01', '\02', '\03', '\04', '\05', '\06', '\07',
|
|
'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
|
|
'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
|
|
'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
|
|
' ', '!', '\"', '#', '$', '%', '&', '\'',
|
|
'(', ')', '*', '+', ',', '-', '.', '/',
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', ':', ';', '<', '=', '>', '?',
|
|
'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
'x', 'y', 'z', '[', '\\', ']', '^', '_',
|
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
|
'x', 'y', 'z', '{', '|', '}', '~', '\177',
|
|
|
|
/* ISO-1 */
|
|
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
|
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
|
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7,
|
|
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf,
|
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
|
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
|
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
|
};
|
|
|
|
/* Table for converting to upper-case */
|
|
|
|
const unsigned char u__ct_tou[256] = {
|
|
'\0', '\01', '\02', '\03', '\04', '\05', '\06', '\07',
|
|
'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
|
|
'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
|
|
'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
|
|
' ', '!', '\"', '#', '$', '%', '&', '\'',
|
|
'(', ')', '*', '+', ',', '-', '.', '/',
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', ':', ';', '<', '=', '>', '?',
|
|
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
|
|
'`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
'X', 'Y', 'Z', '{', '|', '}', '~', '\177',
|
|
|
|
/* ISO-1 */
|
|
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
|
|
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
|
|
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7,
|
|
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff
|
|
};
|
|
|
|
/* Table for calculate hex digit => unsigned int */
|
|
|
|
const unsigned char u__ct_hex2int[112] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* !"#$%&'()*+,-./ */
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */
|
|
0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */
|
|
0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* `abcdefghijklmno */
|
|
/**
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // pqrstuvwxyz{|}~
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
*/
|
|
};
|
|
|
|
/* MIME TYPE */
|
|
|
|
typedef struct mimeentry {
|
|
const char* restrict type;
|
|
const char* restrict name;
|
|
uint32_t name_len;
|
|
} mimeentry;
|
|
|
|
#define MIME_ENTRY(name,type) { type, name+1, U_CONSTANT_SIZE(name)-1 }
|
|
|
|
/**
|
|
* Complete list of MIME types
|
|
*
|
|
*.3dm x-world/x-3dmf
|
|
*.3dmf x-world/x-3dmf
|
|
*
|
|
*.a application/octet-stream
|
|
*.aab application/x-authorware-bin
|
|
*.aam application/x-authorware-map
|
|
*.aas application/x-authorware-seg
|
|
*.abc text/vnd.abc
|
|
*.acgi text/html
|
|
*.afl video/animaflex
|
|
*.ai application/postscript
|
|
*.aif audio/aiff
|
|
*.aif audio/x-aiff
|
|
*.aifc audio/aiff
|
|
*.aifc audio/x-aiff
|
|
*.aiff audio/aiff
|
|
*.aiff audio/x-aiff
|
|
*.aim application/x-aim
|
|
*.aip text/x-audiosoft-intra
|
|
*.ani application/x-navi-animation
|
|
*.aos application/x-nokia-9000-communicator-add-on-software
|
|
*.aps application/mime
|
|
*.arc application/octet-stream
|
|
*.arj application/arj
|
|
*.arj application/octet-stream
|
|
*.art image/x-jg
|
|
*.asf video/x-ms-asf
|
|
*.asm text/x-asm
|
|
*.asp text/asp
|
|
*.asx application/x-mplayer2
|
|
*.asx video/x-ms-asf
|
|
*.asx video/x-ms-asf-plugin
|
|
*.au audio/basic
|
|
*.au audio/x-au
|
|
*.avi application/x-troff-msvideo
|
|
*.avi video/avi
|
|
*.avi video/msvideo
|
|
*.avi video/x-msvideo
|
|
*.avs video/avs-video
|
|
*
|
|
*.bcpio application/x-bcpio
|
|
*.bin application/mac-binary
|
|
*.bin application/macbinary
|
|
*.bin application/octet-stream
|
|
*.bin application/x-binary
|
|
*.bin application/x-macbinary
|
|
*.bm image/bmp
|
|
*.bmp image/bmp
|
|
*.bmp image/x-windows-bmp
|
|
*.boo application/book
|
|
*.book application/book
|
|
*.boz application/x-bzip2
|
|
*.bsh application/x-bsh
|
|
*.bz application/x-bzip
|
|
*.bz2 application/x-bzip2
|
|
*
|
|
*.c text/plain
|
|
*.c text/x-c
|
|
*.c++ text/plain
|
|
*.cat application/vnd.ms-pki.seccat
|
|
*.cc text/plain
|
|
*.cc text/x-c
|
|
*.ccad application/clariscad
|
|
*.cco application/x-cocoa
|
|
*.cdf application/cdf
|
|
*.cdf application/x-cdf
|
|
*.cdf application/x-netcdf
|
|
*.cer application/pkix-cert
|
|
*.cer application/x-x509-ca-cert
|
|
*.cha application/x-chat
|
|
*.chat application/x-chat
|
|
*.class application/java
|
|
*.class application/java-byte-code
|
|
*.class application/x-java-class
|
|
*.com application/octet-stream
|
|
*.com text/plain
|
|
*.conf text/plain
|
|
*.cpio application/x-cpio
|
|
*.cpp text/x-c
|
|
*.cpt application/mac-compactpro
|
|
*.cpt application/x-compactpro
|
|
*.cpt application/x-cpt
|
|
*.crl application/pkcs-crl
|
|
*.crl application/pkix-crl
|
|
*.crt application/pkix-cert
|
|
*.crt application/x-x509-ca-cert
|
|
*.crt application/x-x509-user-cert
|
|
*.csh application/x-csh
|
|
*.csh text/x-script.csh
|
|
*.css application/x-pointplus
|
|
*.css text/css
|
|
*.cxx text/plain
|
|
*
|
|
*.dcr application/x-director
|
|
*.deepv application/x-deepv
|
|
*.def text/plain
|
|
*.der application/x-x509-ca-cert
|
|
*.dif video/x-dv
|
|
*.dir application/x-director
|
|
*.dl video/dl
|
|
*.dl video/x-dl
|
|
*.doc application/msword
|
|
*.dot application/msword
|
|
*.dp application/commonground
|
|
*.drw application/drafting
|
|
*.dump application/octet-stream
|
|
*.dv video/x-dv
|
|
*.dvi application/x-dvi
|
|
*.dwf drawing/x-dwf (old)
|
|
*.dwf model/vnd.dwf
|
|
*.dwg application/acad
|
|
*.dwg image/vnd.dwg
|
|
*.dwg image/x-dwg
|
|
*.dxf application/dxf
|
|
*.dxf image/vnd.dwg
|
|
*.dxf image/x-dwg
|
|
*.dxr application/x-director
|
|
*
|
|
*.el text/x-script.elisp
|
|
*.elc application/x-bytecode.elisp (compiled elisp)
|
|
*.elc application/x-elc
|
|
*.env application/x-envoy
|
|
*.eps application/postscript
|
|
*.es application/x-esrehber
|
|
*.etx text/x-setext
|
|
*.evy application/envoy
|
|
*.evy application/x-envoy
|
|
*.exe application/octet-stream
|
|
*
|
|
*.f text/plain
|
|
*.f text/x-fortran
|
|
*.f77 text/x-fortran
|
|
*.f90 text/plain
|
|
*.f90 text/x-fortran
|
|
*.fdf application/vnd.fdf
|
|
*.fif application/fractals
|
|
*.fif image/fif
|
|
*.fli video/fli
|
|
*.fli video/x-fli
|
|
*.flo image/florian
|
|
*.flx text/vnd.fmi.flexstor
|
|
*.fmf video/x-atomic3d-feature
|
|
*.for text/plain
|
|
*.for text/x-fortran
|
|
*.fpx image/vnd.fpx
|
|
*.fpx image/vnd.net-fpx
|
|
*.frl application/freeloader
|
|
*.funk audio/make
|
|
*
|
|
*.g text/plain
|
|
*.g3 image/g3fax
|
|
*.gif image/gif
|
|
*.gl video/gl
|
|
*.gl video/x-gl
|
|
*.gsd audio/x-gsm
|
|
*.gsm audio/x-gsm
|
|
*.gsp application/x-gsp
|
|
*.gss application/x-gss
|
|
*.gtar application/x-gtar
|
|
*.gz application/x-compressed
|
|
*.gz application/x-gzip
|
|
*.gzip application/x-gzip
|
|
*.gzip multipart/x-gzip
|
|
*
|
|
*.h text/plain
|
|
*.h text/x-h
|
|
*.hdf application/x-hdf
|
|
*.help application/x-helpfile
|
|
*.hgl application/vnd.hp-hpgl
|
|
*.hh text/plain
|
|
*.hh text/x-h
|
|
*.hlb text/x-script
|
|
*.hlp application/hlp
|
|
*.hlp application/x-helpfile
|
|
*.hlp application/x-winhelp
|
|
*.hpg application/vnd.hp-hpgl
|
|
*.hpgl application/vnd.hp-hpgl
|
|
*.hqx application/binhex
|
|
*.hqx application/binhex4
|
|
*.hqx application/mac-binhex
|
|
*.hqx application/mac-binhex40
|
|
*.hqx application/x-binhex40
|
|
*.hqx application/x-mac-binhex40
|
|
*.hta application/hta
|
|
*.htc text/x-component
|
|
*.htm text/html
|
|
*.html text/html
|
|
*.htmls text/html
|
|
*.htt text/webviewhtml
|
|
*.htx text/html
|
|
*
|
|
*.ice x-conference/x-cooltalk
|
|
*.ico image/x-icon
|
|
*.idc text/plain
|
|
*.ief image/ief
|
|
*.iefs image/ief
|
|
*.iges application/iges
|
|
*.iges model/iges
|
|
*.igs application/iges
|
|
*.igs model/iges
|
|
*.ima application/x-ima
|
|
*.imap application/x-httpd-imap
|
|
*.inf application/inf
|
|
*.ins application/x-internett-signup
|
|
*.ip application/x-ip2
|
|
*.isu video/x-isvideo
|
|
*.it audio/it
|
|
*.iv application/x-inventor
|
|
*.ivr i-world/i-vrml
|
|
*.ivy application/x-livescreen
|
|
*
|
|
*.jam audio/x-jam
|
|
*.jav text/plain
|
|
*.jav text/x-java-source
|
|
*.java text/plain
|
|
*.java text/x-java-source
|
|
*.jcm application/x-java-commerce
|
|
*.jfif image/jpeg
|
|
*.jfif image/pjpeg
|
|
*.jfif-tbnl image/jpeg
|
|
*.jpe image/jpeg
|
|
*.jpe image/pjpeg
|
|
*.jpeg image/jpeg
|
|
*.jpeg image/pjpeg
|
|
*.jpg image/jpeg
|
|
*.jpg image/pjpeg
|
|
*.jps image/x-jps
|
|
*.js application/x-javascript
|
|
*.js application/javascript
|
|
*.js application/ecmascript
|
|
*.js text/javascript
|
|
*.js text/ecmascript
|
|
*.jut image/jutvision
|
|
*
|
|
*.kar audio/midi
|
|
*.kar music/x-karaoke
|
|
*.ksh application/x-ksh
|
|
*.ksh text/x-script.ksh
|
|
*
|
|
*.la audio/nspaudio
|
|
*.la audio/x-nspaudio
|
|
*.lam audio/x-liveaudio
|
|
*.latex application/x-latex
|
|
*.lha application/lha
|
|
*.lha application/octet-stream
|
|
*.lha application/x-lha
|
|
*.lhx application/octet-stream
|
|
*.list text/plain
|
|
*.lma audio/nspaudio
|
|
*.lma audio/x-nspaudio
|
|
*.log text/plain
|
|
*.lsp application/x-lisp
|
|
*.lsp text/x-script.lisp
|
|
*.lst text/plain
|
|
*.lsx text/x-la-asf
|
|
*.ltx application/x-latex
|
|
*.lzh application/octet-stream
|
|
*.lzh application/x-lzh
|
|
*.lzx application/lzx
|
|
*.lzx application/octet-stream
|
|
*.lzx application/x-lzx
|
|
*
|
|
*.m text/plain
|
|
*.m text/x-m
|
|
*.m1v video/mpeg
|
|
*.m2a audio/mpeg
|
|
*.m2v video/mpeg
|
|
*.m3u audio/x-mpequrl
|
|
*.man application/x-troff-man
|
|
*.map application/x-navimap
|
|
*.mar text/plain
|
|
*.mbd application/mbedlet
|
|
*.mc$ application/x-magic-cap-package-1.0
|
|
*.mcd application/mcad
|
|
*.mcd application/x-mathcad
|
|
*.mcf image/vasa
|
|
*.mcf text/mcf
|
|
*.mcp application/netmc
|
|
*.me application/x-troff-me
|
|
*.mht message/rfc822
|
|
*.mhtml message/rfc822
|
|
*.mid application/x-midi
|
|
*.mid audio/midi
|
|
*.mid audio/x-mid
|
|
*.mid audio/x-midi
|
|
*.mid music/crescendo
|
|
*.mid x-music/x-midi
|
|
*.midi application/x-midi
|
|
*.midi audio/midi
|
|
*.midi audio/x-mid
|
|
*.midi audio/x-midi
|
|
*.midi music/crescendo
|
|
*.midi x-music/x-midi
|
|
*.mif application/x-frame
|
|
*.mif application/x-mif
|
|
*.mime message/rfc822
|
|
*.mime www/mime
|
|
*.mjf audio/x-vnd.audioexplosion.mjuicemediafile
|
|
*.mjpg video/x-motion-jpeg
|
|
*.mm application/base64
|
|
*.mm application/x-meme
|
|
*.mme application/base64
|
|
*.mod audio/mod
|
|
*.mod audio/x-mod
|
|
*.moov video/quicktime
|
|
*.mov video/quicktime
|
|
*.movie video/x-sgi-movie
|
|
*.mp2 audio/mpeg
|
|
*.mp2 audio/x-mpeg
|
|
*.mp2 video/mpeg
|
|
*.mp2 video/x-mpeg
|
|
*.mp2 video/x-mpeq2a
|
|
*.mp3 audio/mpeg3
|
|
*.mp3 audio/x-mpeg-3
|
|
*.mp3 video/mpeg
|
|
*.mp3 video/x-mpeg
|
|
*.mpa audio/mpeg
|
|
*.mpa video/mpeg
|
|
*.mpc application/x-project
|
|
*.mpe video/mpeg
|
|
*.mpeg video/mpeg
|
|
*.mpg audio/mpeg
|
|
*.mpg video/mpeg
|
|
*.mpga audio/mpeg
|
|
*.mpp application/vnd.ms-project
|
|
*.mpt application/x-project
|
|
*.mpv application/x-project
|
|
*.mpx application/x-project
|
|
*.mrc application/marc
|
|
*.ms application/x-troff-ms
|
|
*.mv video/x-sgi-movie
|
|
*.my audio/make
|
|
*.mzz application/x-vnd.audioexplosion.mzz
|
|
*
|
|
*.nap image/naplps
|
|
*.naplps image/naplps
|
|
*.nc application/x-netcdf
|
|
*.ncm application/vnd.nokia.configuration-message
|
|
*.nif image/x-niff
|
|
*.niff image/x-niff
|
|
*.nix application/x-mix-transfer
|
|
*.nsc application/x-conference
|
|
*.nvd application/x-navidoc
|
|
*
|
|
*.o application/octet-stream
|
|
*.oda application/oda
|
|
*.omc application/x-omc
|
|
*.omcd application/x-omcdatamaker
|
|
*.omcr application/x-omcregerator
|
|
*
|
|
*.p text/x-pascal
|
|
*.p10 application/pkcs10
|
|
*.p10 application/x-pkcs10
|
|
*.p12 application/pkcs-12
|
|
*.p12 application/x-pkcs12
|
|
*.p7a application/x-pkcs7-signature
|
|
*.p7c application/pkcs7-mime
|
|
*.p7c application/x-pkcs7-mime
|
|
*.p7m application/pkcs7-mime
|
|
*.p7m application/x-pkcs7-mime
|
|
*.p7r application/x-pkcs7-certreqresp
|
|
*.p7s application/pkcs7-signature
|
|
*.part application/pro_eng
|
|
*.pas text/pascal
|
|
*.pbm image/x-portable-bitmap
|
|
*.pcl application/vnd.hp-pcl
|
|
*.pcl application/x-pcl
|
|
*.pct image/x-pict
|
|
*.pcx image/x-pcx
|
|
*.pdb chemical/x-pdb
|
|
*.pdf application/pdf
|
|
*.pfunk audio/make
|
|
*.pfunk audio/make.my.funk
|
|
*.pgm image/x-portable-graymap
|
|
*.pgm image/x-portable-greymap
|
|
*.pic image/pict
|
|
*.pict image/pict
|
|
*.pkg application/x-newton-compatible-pkg
|
|
*.pko application/vnd.ms-pki.pko
|
|
*.pl text/plain
|
|
*.pl text/x-script.perl
|
|
*.plx application/x-pixclscript
|
|
*.pm image/x-xpixmap
|
|
*.pm text/x-script.perl-module
|
|
*.pm4 application/x-pagemaker
|
|
*.pm5 application/x-pagemaker
|
|
*.png image/png
|
|
*.pnm application/x-portable-anymap
|
|
*.pnm image/x-portable-anymap
|
|
*.pot application/mspowerpoint
|
|
*.pot application/vnd.ms-powerpoint
|
|
*.pov model/x-pov
|
|
*.ppa application/vnd.ms-powerpoint
|
|
*.ppm image/x-portable-pixmap
|
|
*.pps application/mspowerpoint
|
|
*.pps application/vnd.ms-powerpoint
|
|
*.ppt application/mspowerpoint
|
|
*.ppt application/powerpoint
|
|
*.ppt application/vnd.ms-powerpoint
|
|
*.ppt application/x-mspowerpoint
|
|
*.ppz application/mspowerpoint
|
|
*.pre application/x-freelance
|
|
*.prt application/pro_eng
|
|
*.ps application/postscript
|
|
*.psd application/octet-stream
|
|
*.pvu paleovu/x-pv
|
|
*.pwz application/vnd.ms-powerpoint
|
|
*.py text/x-script.phyton
|
|
*.pyc applicaiton/x-bytecode.python
|
|
*
|
|
*.qcp audio/vnd.qcelp
|
|
*.qd3 x-world/x-3dmf
|
|
*.qd3d x-world/x-3dmf
|
|
*.qif image/x-quicktime
|
|
*.qt video/quicktime
|
|
*.qtc video/x-qtc
|
|
*.qti image/x-quicktime
|
|
*.qtif image/x-quicktime
|
|
*
|
|
*.ra audio/x-pn-realaudio
|
|
*.ra audio/x-pn-realaudio-plugin
|
|
*.ra audio/x-realaudio
|
|
*.ram audio/x-pn-realaudio
|
|
*.ras application/x-cmu-raster
|
|
*.ras image/cmu-raster
|
|
*.ras image/x-cmu-raster
|
|
*.rast image/cmu-raster
|
|
*.rexx text/x-script.rexx
|
|
*.rf image/vnd.rn-realflash
|
|
*.rgb image/x-rgb
|
|
*.rm application/vnd.rn-realmedia
|
|
*.rm audio/x-pn-realaudio
|
|
*.rmi audio/mid
|
|
*.rmm audio/x-pn-realaudio
|
|
*.rmp audio/x-pn-realaudio
|
|
*.rmp audio/x-pn-realaudio-plugin
|
|
*.rng application/ringing-tones
|
|
*.rng application/vnd.nokia.ringing-tone
|
|
*.rnx application/vnd.rn-realplayer
|
|
*.roff application/x-troff
|
|
*.rp image/vnd.rn-realpix
|
|
*.rpm audio/x-pn-realaudio-plugin
|
|
*.rt text/richtext
|
|
*.rt text/vnd.rn-realtext
|
|
*.rtf application/rtf
|
|
*.rtf application/x-rtf
|
|
*.rtf text/richtext
|
|
*.rtx application/rtf
|
|
*.rtx text/richtext
|
|
*.rv video/vnd.rn-realvideo
|
|
*
|
|
*.s text/x-asm
|
|
*.s3m audio/s3m
|
|
*.saveme application/octet-stream
|
|
*.sbk application/x-tbook
|
|
*.scm application/x-lotusscreencam
|
|
*.scm text/x-script.guile
|
|
*.scm text/x-script.scheme
|
|
*.scm video/x-scm
|
|
*.sdml text/plain
|
|
*.sdp application/sdp
|
|
*.sdp application/x-sdp
|
|
*.sdr application/sounder
|
|
*.sea application/sea
|
|
*.sea application/x-sea
|
|
*.set application/set
|
|
*.sgm text/sgml
|
|
*.sgm text/x-sgml
|
|
*.sgml text/sgml
|
|
*.sgml text/x-sgml
|
|
*.sh application/x-bsh
|
|
*.sh application/x-sh
|
|
*.sh application/x-shar
|
|
*.sh text/x-script.sh
|
|
*.shar application/x-bsh
|
|
*.shar application/x-shar
|
|
*.shtml text/html
|
|
*.shtml text/x-server-parsed-html
|
|
*.sid audio/x-psid
|
|
*.sit application/x-sit
|
|
*.sit application/x-stuffit
|
|
*.skd application/x-koan
|
|
*.skm application/x-koan
|
|
*.skp application/x-koan
|
|
*.skt application/x-koan
|
|
*.sl application/x-seelogo
|
|
*.smi application/smil
|
|
*.smil application/smil
|
|
*.snd audio/basic
|
|
*.snd audio/x-adpcm
|
|
*.sol application/solids
|
|
*.spc application/x-pkcs7-certificates
|
|
*.spc text/x-speech
|
|
*.spl application/futuresplash
|
|
*.spr application/x-sprite
|
|
*.sprite application/x-sprite
|
|
*.src application/x-wais-source
|
|
*.ssi text/x-server-parsed-html
|
|
*.ssm application/streamingmedia
|
|
*.sst application/vnd.ms-pki.certstore
|
|
*.step application/step
|
|
*.stl application/sla
|
|
*.stl application/vnd.ms-pki.stl
|
|
*.stl application/x-navistyle
|
|
*.stp application/step
|
|
*.sv4cpio application/x-sv4cpio
|
|
*.sv4crc application/x-sv4crc
|
|
*.svf image/vnd.dwg
|
|
*.svf image/x-dwg
|
|
*.svr application/x-world
|
|
*.svr x-world/x-svr
|
|
*.swf application/x-shockwave-flash
|
|
*
|
|
*.t application/x-troff
|
|
*.talk text/x-speech
|
|
*.tar application/x-tar
|
|
*.tbk application/toolbook
|
|
*.tbk application/x-tbook
|
|
*.tcl application/x-tcl
|
|
*.tcl text/x-script.tcl
|
|
*.tcsh text/x-script.tcsh
|
|
*.tex application/x-tex
|
|
*.texi application/x-texinfo
|
|
*.texinfo application/x-texinfo
|
|
*.text application/plain
|
|
*.text text/plain
|
|
*.tgz application/gnutar
|
|
*.tgz application/x-compressed
|
|
*.tif image/tiff
|
|
*.tif image/x-tiff
|
|
*.tiff image/tiff
|
|
*.tiff image/x-tiff
|
|
*.tr application/x-troff
|
|
*.tsi audio/tsp-audio
|
|
*.tsp application/dsptype
|
|
*.tsp audio/tsplayer
|
|
*.tsv text/tab-separated-values
|
|
*.turbot image/florian
|
|
*.txt text/plain
|
|
*
|
|
*.uil text/x-uil
|
|
*.uni text/uri-list
|
|
*.unis text/uri-list
|
|
*.unv application/i-deas
|
|
*.uri text/uri-list
|
|
*.uris text/uri-list
|
|
*.ustar application/x-ustar
|
|
*.ustar multipart/x-ustar
|
|
*.uu application/octet-stream
|
|
*.uu text/x-uuencode
|
|
*.uue text/x-uuencode
|
|
*
|
|
*.vcd application/x-cdlink
|
|
*.vcs text/x-vcalendar
|
|
*.vda application/vda
|
|
*.vdo video/vdo
|
|
*.vew application/groupwise
|
|
*.viv video/vivo
|
|
*.viv video/vnd.vivo
|
|
*.vivo video/vivo
|
|
*.vivo video/vnd.vivo
|
|
*.vmd application/vocaltec-media-desc
|
|
*.vmf application/vocaltec-media-file
|
|
*.voc audio/voc
|
|
*.voc audio/x-voc
|
|
*.vos video/vosaic
|
|
*.vox audio/voxware
|
|
*.vqe audio/x-twinvq-plugin
|
|
*.vqf audio/x-twinvq
|
|
*.vql audio/x-twinvq-plugin
|
|
*.vrml application/x-vrml
|
|
*.vrml model/vrml
|
|
*.vrml x-world/x-vrml
|
|
*.vrt x-world/x-vrt
|
|
*.vsd application/x-visio
|
|
*.vst application/x-visio
|
|
*.vsw application/x-visio
|
|
*
|
|
*.w60 application/wordperfect6.0
|
|
*.w61 application/wordperfect6.1
|
|
*.w6w application/msword
|
|
*.wav audio/wav
|
|
*.wav audio/x-wav
|
|
*.wb1 application/x-qpro
|
|
*.wbmp image/vnd.wap.wbmp
|
|
*.web application/vnd.xara
|
|
*.wiz application/msword
|
|
*.wk1 application/x-123
|
|
*.wmf windows/metafile
|
|
*.wml text/vnd.wap.wml
|
|
*.wmlc application/vnd.wap.wmlc
|
|
*.wmls text/vnd.wap.wmlscript
|
|
*.wmlsc application/vnd.wap.wmlscriptc
|
|
*.word application/msword
|
|
*.wp application/wordperfect
|
|
*.wp5 application/wordperfect
|
|
*.wp5 application/wordperfect6.0
|
|
*.wp6 application/wordperfect
|
|
*.wpd application/wordperfect
|
|
*.wpd application/x-wpwin
|
|
*.wq1 application/x-lotus
|
|
*.wri application/mswrite
|
|
*.wri application/x-wri
|
|
*.wrl application/x-world
|
|
*.wrl model/vrml
|
|
*.wrl x-world/x-vrml
|
|
*.wrz model/vrml
|
|
*.wrz x-world/x-vrml
|
|
*.wsc text/scriplet
|
|
*.wsrc application/x-wais-source
|
|
*.wtk application/x-wintalk
|
|
*
|
|
*.xbm image/x-xbitmap
|
|
*.xbm image/x-xbm
|
|
*.xbm image/xbm
|
|
*.xdr video/x-amt-demorun
|
|
*.xgz xgl/drawing
|
|
*.xif image/vnd.xiff
|
|
*.xl application/excel
|
|
*.xla application/excel
|
|
*.xla application/x-excel
|
|
*.xla application/x-msexcel
|
|
*.xlb application/excel
|
|
*.xlb application/vnd.ms-excel
|
|
*.xlb application/x-excel
|
|
*.xlc application/excel
|
|
*.xlc application/vnd.ms-excel
|
|
*.xlc application/x-excel
|
|
*.xld application/excel
|
|
*.xld application/x-excel
|
|
*.xlk application/excel
|
|
*.xlk application/x-excel
|
|
*.xll application/excel
|
|
*.xll application/vnd.ms-excel
|
|
*.xll application/x-excel
|
|
*.xlm application/excel
|
|
*.xlm application/vnd.ms-excel
|
|
*.xlm application/x-excel
|
|
*.xls application/excel
|
|
*.xls application/vnd.ms-excel
|
|
*.xls application/x-excel
|
|
*.xls application/x-msexcel
|
|
*.xlt application/excel
|
|
*.xlt application/x-excel
|
|
*.xlv application/excel
|
|
*.xlv application/x-excel
|
|
*.xlw application/excel
|
|
*.xlw application/vnd.ms-excel
|
|
*.xlw application/x-excel
|
|
*.xlw application/x-msexcel
|
|
*.xm audio/xm
|
|
*.xml application/xml
|
|
*.xml text/xml
|
|
*.xmz xgl/movie
|
|
*.xpix application/x-vnd.ls-xpix
|
|
*.xpm image/x-xpixmap
|
|
*.xpm image/xpm
|
|
*.x-png image/png
|
|
*.xsr video/x-amt-showrun
|
|
*.xwd image/x-xwd
|
|
*.xwd image/x-xwindowdump
|
|
*.xyz chemical/x-pdb
|
|
*
|
|
*.z application/x-compress
|
|
*.z application/x-compressed
|
|
*.zip application/x-compressed
|
|
*.zip application/x-zip-compressed
|
|
*.zip application/zip
|
|
*.zip multipart/x-zip
|
|
*.zoo application/octet-stream
|
|
*.zsh text/x-script.zsh
|
|
*/
|
|
|
|
static struct mimeentry mimetab_a[] = {
|
|
MIME_ENTRY( "ai", "application/postscript" ),
|
|
MIME_ENTRY( "arj", "application/x-arj-compressed" ),
|
|
MIME_ENTRY( "asx", "video/x-ms-asf" ),
|
|
MIME_ENTRY( "atom", "application/atom+xml" ),
|
|
MIME_ENTRY( "avi", "video/x-msvideo" ),
|
|
MIME_ENTRY( "appcache", "text/cache-manifest" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "aif", "audio/x-aiff" ), (aifc, aiff)
|
|
MIME_ENTRY( "api", "application/postscript" ),
|
|
MIME_ENTRY( "asc", "text/plain" ),
|
|
MIME_ENTRY( "au", "audio/basic" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_b[] = {
|
|
MIME_ENTRY( "bash", "application/x-bsh" ),
|
|
MIME_ENTRY( "bat", "application/x-msdos-program" ),
|
|
MIME_ENTRY( "bmp", "image/x-ms-bmp" ),
|
|
MIME_ENTRY( "bild", "image/jpeg" ),
|
|
MIME_ENTRY( "boz", "application/x-bzip2" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "bin", "application/x-bcpio" ),
|
|
MIME_ENTRY( "bcpio", "application/octet-stream" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_c[] = {
|
|
|
|
/**
|
|
* The certificate being downloaded represents a Certificate Authority.
|
|
* When it is downloaded the user will be shown a sequence of dialogs that
|
|
* will guide them through the process of accepting the Certificate Authority
|
|
* and deciding if they wish to trust sites certified by the CA. If a certificate
|
|
* chain is being imported then the first certificate in the chain must be the CA
|
|
* certificate, and any subsequent certificates will be added as untrusted CA
|
|
* certificates to the local database
|
|
*/
|
|
MIME_ENTRY( "crt", "application/x-x509-ca-cert" ),
|
|
MIME_ENTRY( "cer", "application/x-x509-ca-cert" ),
|
|
MIME_ENTRY( "crx", "application/x-chrome-extension crx" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "c++", "text/x-c++src" ),
|
|
MIME_ENTRY( "c4d", "application/vnd.clonk.c4group" ),
|
|
MIME_ENTRY( "cac", "chemical/x-cache" ),
|
|
MIME_ENTRY( "cascii", "chemical/x-cactvs-binary" ),
|
|
MIME_ENTRY( "cct", "application/x-director" ),
|
|
MIME_ENTRY( "cdf", "application/x-netcdf" ),
|
|
MIME_ENTRY( "cef", "chemical/x-cxf" ),
|
|
MIME_ENTRY( "cls", "text/x-tex" ),
|
|
MIME_ENTRY( "cpio", "application/x-cpio" ),
|
|
MIME_ENTRY( "cpt", "application/mac-compactpro" ),
|
|
MIME_ENTRY( "csm", "chemical/x-csml" ),
|
|
MIME_ENTRY( "csh", "application/x-csh" ),
|
|
MIME_ENTRY( "cdf", "application/x-netcdf" ),
|
|
MIME_ENTRY( "c", "text/x-c" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_d[] = {
|
|
MIME_ENTRY( "der", "application/x-x509-ca-cert" ),
|
|
MIME_ENTRY( "doc", "application/msword" ),
|
|
MIME_ENTRY( "dtd", "application/xml" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "dcr", "application/x-director" ),
|
|
MIME_ENTRY( "deb", "application/x-debian-package" ),
|
|
MIME_ENTRY( "dir", "application/x-director" ),
|
|
MIME_ENTRY( "dll", "application/octet-stream" ),
|
|
MIME_ENTRY( "dms", "application/octet-stream" ),
|
|
MIME_ENTRY( "dvi", "application/x-dvi" ),
|
|
MIME_ENTRY( "dxr", "application/x-director" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_e[] = {
|
|
MIME_ENTRY( "eps", "application/postscript" ),
|
|
MIME_ENTRY( "eot", "application/vnd.ms-fontobject" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "etx", "text/x-setext" ),
|
|
MIME_ENTRY( "ez", "application/andrew-inset" ),
|
|
MIME_ENTRY( "exe", "application/octet-stream" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
/*
|
|
static struct mimeentry mimetab_f[] = {
|
|
{ 0, 0, 0 }
|
|
};
|
|
*/
|
|
|
|
static struct mimeentry mimetab_g[] = {
|
|
MIME_ENTRY( "gtar", "application/x-gtar" ),
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_h[] = {
|
|
MIME_ENTRY( "htc", "text/x-component" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "hdf", "application/x-hdf" ),
|
|
MIME_ENTRY( "hqx", "application/mac-binhex40" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
/*
|
|
static struct mimeentry mimetab_i[] = {
|
|
MIME_ENTRY( "ice", "x-conference/x-cooltalk" ),
|
|
MIME_ENTRY( "ief", "image/ief" ),
|
|
MIME_ENTRY( "ig", "model/iges" ), (igs, iges)
|
|
{ 0, 0, 0 }
|
|
};
|
|
*/
|
|
|
|
static struct mimeentry mimetab_j[] = {
|
|
MIME_ENTRY( "json", "application/json" ),
|
|
MIME_ENTRY( "jp", "image/jpeg" ), /* (jpg, jpe, jpeg) */
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
/*
|
|
static struct mimeentry mimetab_k[] = {
|
|
MIME_ENTRY( "kar", "audio/midi" ),
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_l[] = {
|
|
MIME_ENTRY( "latex", "application/x-latex" ),
|
|
MIME_ENTRY( "lh", "application/octet-stream" ), (lha, lhz)
|
|
{ 0, 0, 0 }
|
|
};
|
|
*/
|
|
|
|
static struct mimeentry mimetab_m[] = {
|
|
MIME_ENTRY( "mng", "video/x-mng" ),
|
|
MIME_ENTRY( "mp4", "video/mp4" ),
|
|
MIME_ENTRY( "m4a", "audio/mp4" ),
|
|
MIME_ENTRY( "mp", "video/mpeg" ), /* (mp2, mp3, mpg, mpe, mpeg, mpga) */
|
|
MIME_ENTRY( "md5", "text/plain" ),
|
|
MIME_ENTRY( "mov", "video/quicktime" ),
|
|
MIME_ENTRY( "mf", "text/cache-manifest" ),
|
|
MIME_ENTRY( "manifest", "text/cache-manifest" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "m3u", "audio/x-mpegurl" ),
|
|
MIME_ENTRY( "man", "application/x-troff-man" ),
|
|
MIME_ENTRY( "me", "application/x-troff-me" ),
|
|
MIME_ENTRY( "mesh", "model/mesh" ),
|
|
MIME_ENTRY( "msh", "model/mesh" ),
|
|
MIME_ENTRY( "mif", "application/vnd.mif" ),
|
|
MIME_ENTRY( "mi", "audio/midi" ), (mid, midi)
|
|
MIME_ENTRY( "movie","video/x-sgi-movie" ),
|
|
MIME_ENTRY( "ms", "application/x-troff-ms" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
/*
|
|
static struct mimeentry mimetab_n[] = {
|
|
MIME_ENTRY( "nc", "application/x-netcdf" ),
|
|
{ 0, 0, 0 }
|
|
};
|
|
*/
|
|
|
|
static struct mimeentry mimetab_o[] = {
|
|
MIME_ENTRY( "ogv", "video/ogg" ),
|
|
MIME_ENTRY( "og", "audio/ogg" ), /* (oga, ogg) */
|
|
MIME_ENTRY( "otf", "font/opentype" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "oda", "application/oda" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_p[] = {
|
|
MIME_ENTRY( "pdf", "application/pdf" ),
|
|
MIME_ENTRY( "pem", "application/x-x509-ca-cert" ),
|
|
MIME_ENTRY( "pbm", "image/x-portable-bitmap" ),
|
|
MIME_ENTRY( "pgm", "image/x-portable-graymap" ),
|
|
MIME_ENTRY( "pnm", "image/x-portable-anymap" ),
|
|
MIME_ENTRY( "ppm", "image/x-portable-pixmap" ),
|
|
MIME_ENTRY( "ps", "application/postscript" ),
|
|
MIME_ENTRY( "p7b", "application/x-pkcs7-certificates" ),
|
|
MIME_ENTRY( "p7c", "application/x-pkcs7-mime" ),
|
|
MIME_ENTRY( "p12", "application/x-pkcs12" ),
|
|
MIME_ENTRY( "ppt", "application/vnd.ms-powerpoint" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "pac", "application/x-ns-proxy-autoconfig" ),
|
|
MIME_ENTRY( "pdb", "chemical/x-pdb" ),
|
|
MIME_ENTRY( "pgn", "application/x-chess-pgn" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_q[] = {
|
|
MIME_ENTRY( "qt", "video/quicktime" ),
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_r[] = {
|
|
MIME_ENTRY( "rar", "application/x-rar-compressed" ),
|
|
MIME_ENTRY( "rtf", "text/rtf" ),
|
|
MIME_ENTRY( "ru", "text/x-ruby" ),
|
|
MIME_ENTRY( "rtx", "text/richtext" ),
|
|
MIME_ENTRY( "rdf", "application/rdf+xml" ),
|
|
MIME_ENTRY( "roff", "application/x-troff" ),
|
|
MIME_ENTRY( "rss", "application/rss+xml" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "ra", "audio/x-realaudio" ),
|
|
MIME_ENTRY( "ras", "image/x-cmu-raster" ),
|
|
MIME_ENTRY( "rgb", "image/x-rgb" ),
|
|
MIME_ENTRY( "rpm", "audio/x-pn-realaudio-plugin" ),
|
|
MIME_ENTRY( "ram", "audio/x-pn-realaudio" ),
|
|
MIME_ENTRY( "rm", "audio/x-pn-realaudio" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_s[] = {
|
|
MIME_ENTRY( "swf", "application/x-shockwave-flash" ),
|
|
MIME_ENTRY( "sgm", "text/sgml" ), /* (sgml) */
|
|
MIME_ENTRY( "sh", "application/x-sh" ),
|
|
MIME_ENTRY( "safariextz", "application/octet-stream" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "snd", "audio/basic" ),
|
|
MIME_ENTRY( "shar", "application/x-shar" ),
|
|
MIME_ENTRY( "sit", "application/x-stuffit" ),
|
|
MIME_ENTRY( "silo", "model/mesh" ),
|
|
MIME_ENTRY( "sig", "application/pgp-signature" ),
|
|
MIME_ENTRY( "spl", "application/x-futuresplash" ),
|
|
MIME_ENTRY( "src", "application/x-wais-source" ),
|
|
MIME_ENTRY( "smi", "application/smil" ), (smil)
|
|
MIME_ENTRY( "sk", "application/x-koan" ), (skd, skm, skp, skt)
|
|
MIME_ENTRY( "sv4cpio", "application/x-sv4cpio" ),
|
|
MIME_ENTRY( "sv4crc", "application/x-sv4crc" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_t[] = {
|
|
MIME_ENTRY( "tar", "application/x-tar" ),
|
|
MIME_ENTRY( "tgz", "application/x-tar-gz" ),
|
|
MIME_ENTRY( "tif", "image/tiff" ), /* (tiff) */
|
|
MIME_ENTRY( "ttf", "font/truetype" ),
|
|
MIME_ENTRY( "ttl", "text/turtle" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "texi", "application/x-texinfo" ), (texinfo)
|
|
MIME_ENTRY( "tex", "application/x-tex" ),
|
|
MIME_ENTRY( "tr", "application/x-troff" ),
|
|
MIME_ENTRY( "tcl", "application/x-tcl" ),
|
|
MIME_ENTRY( "tsv", "text/tab-separated-values" ),
|
|
MIME_ENTRY( "torrent", "application/x-bittorrent" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
/*
|
|
static struct mimeentry mimetab_u[] = {
|
|
MIME_ENTRY( "untar", "application/x-ustar" ),
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_v[] = {
|
|
MIME_ENTRY( "vbxml", "application/vnd.wap.wbxml" ),
|
|
MIME_ENTRY( "vcd", "application/x-cdlink" ),
|
|
MIME_ENTRY( "vmrl", "model/vrml" ),
|
|
{ 0, 0, 0 }
|
|
};
|
|
*/
|
|
|
|
static struct mimeentry mimetab_w[] = {
|
|
MIME_ENTRY( "wav", "audio/x-wav" ),
|
|
MIME_ENTRY( "wmv", "video/x-ms-wmv" ),
|
|
MIME_ENTRY( "webm", "video/webm" ),
|
|
MIME_ENTRY( "woff", "application/x-font-woff" ),
|
|
MIME_ENTRY( "webp", "image/webp" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "wbmp", "image/vnd.wap.wbmp" ),
|
|
MIME_ENTRY( "wml", "text/vnd.wap.wml" ),
|
|
MIME_ENTRY( "wmls", "text/vnd.wap.wmlscript" ),
|
|
MIME_ENTRY( "wmlc", "application/vnd.wap.wmlc" ),
|
|
MIME_ENTRY( "wmlsc", "application/vnd.wap.wmlscriptc" ),
|
|
MIME_ENTRY( "wrl", "model/vrml" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_x[] = {
|
|
MIME_ENTRY( "xml", "text/xml" ),
|
|
MIME_ENTRY( "xsl", "text/xml" ),
|
|
MIME_ENTRY( "xls", "application/vnd.ms-excel" ),
|
|
MIME_ENTRY( "xpm", "image/x-xpixmap" ),
|
|
MIME_ENTRY( "xbm", "image/x-xbitmap" ),
|
|
MIME_ENTRY( "xpi", "application/x-xpinstall" ),
|
|
MIME_ENTRY( "xrdf", "application/rdf+xml" ),
|
|
|
|
/*
|
|
MIME_ENTRY( "xyz", "chemical/x-pdb" ),
|
|
MIME_ENTRY( "xwd", "image/x-xwindowdump" ),
|
|
*/
|
|
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_z[] = {
|
|
MIME_ENTRY( "zip", "application/zip" ),
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static struct mimeentry mimetab_null[] = {
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
const char* u_get_mimetype(const char* restrict suffix, int* pmime_index)
|
|
{
|
|
static const int dispatch_table[] = {
|
|
0,/* 'a' */
|
|
(char*)&&case_b-(char*)&&case_a,/* 'b' */
|
|
(char*)&&case_c-(char*)&&case_a,/* 'c' */
|
|
(char*)&&case_d-(char*)&&case_a,/* 'd' */
|
|
(char*)&&case_e-(char*)&&case_a,/* 'e' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'f' */
|
|
(char*)&&case_g-(char*)&&case_a,/* 'g' */
|
|
(char*)&&case_h-(char*)&&case_a,/* 'h' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'i' */
|
|
(char*)&&case_j-(char*)&&case_a,/* 'j' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'k' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'l' */
|
|
(char*)&&case_m-(char*)&&case_a,/* 'm' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'n' */
|
|
(char*)&&case_o-(char*)&&case_a,/* 'o' */
|
|
(char*)&&case_p-(char*)&&case_a,/* 'p' */
|
|
(char*)&&case_q-(char*)&&case_a,/* 'q' */
|
|
(char*)&&case_r-(char*)&&case_a,/* 'r' */
|
|
(char*)&&case_s-(char*)&&case_a,/* 's' */
|
|
(char*)&&case_t-(char*)&&case_a,/* 't' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'u' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'v' */
|
|
(char*)&&case_w-(char*)&&case_a,/* 'w' */
|
|
(char*)&&case_x-(char*)&&case_a,/* 'x' */
|
|
(char*)&&cdefault-(char*)&&case_a,/* 'y' */
|
|
(char*)&&case_z-(char*)&&case_a /* 'z' */
|
|
};
|
|
|
|
uint32_t i;
|
|
struct mimeentry* ptr;
|
|
|
|
U_INTERNAL_TRACE("u_get_mimetype(%s,%p)", suffix, pmime_index)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(suffix)
|
|
|
|
i = u_get_unalignedp32(suffix);
|
|
|
|
switch (i)
|
|
{
|
|
case U_MULTICHAR_CONSTANT32('c','s','s',0):
|
|
{
|
|
if (pmime_index) *pmime_index = U_css;
|
|
|
|
return "text/css";
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('f','l','v',0):
|
|
{
|
|
if (pmime_index) *pmime_index = U_flv;
|
|
|
|
return "video/x-flv";
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('g','i','f',0):
|
|
{
|
|
if (pmime_index) *pmime_index = U_gif;
|
|
|
|
return "image/gif";
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('i','c','o',0):
|
|
{
|
|
if (pmime_index) *pmime_index = U_ico;
|
|
|
|
return "image/x-icon";
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('p','n','g',0):
|
|
{
|
|
if (pmime_index) *pmime_index = U_png;
|
|
|
|
return "image/png";
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('j','p','g',0):
|
|
{
|
|
if (pmime_index) *pmime_index = U_jpg;
|
|
|
|
return "image/jpg";
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('s','h','t','m'):
|
|
case U_MULTICHAR_CONSTANT32('h','t','m','l'):
|
|
{
|
|
if (pmime_index) *pmime_index = (i == U_MULTICHAR_CONSTANT32('h','t','m','l') ? U_html : U_ssi);
|
|
|
|
return U_CTYPE_HTML;
|
|
}
|
|
case U_MULTICHAR_CONSTANT32('t','x','t',0):
|
|
case U_MULTICHAR_CONSTANT32('u','s','p',0):
|
|
case U_MULTICHAR_CONSTANT32('c','s','p',0):
|
|
case U_MULTICHAR_CONSTANT32('c','g','i',0):
|
|
case U_MULTICHAR_CONSTANT32('p','h','p',0):
|
|
case U_MULTICHAR_CONSTANT32('s','v','g',0):
|
|
{
|
|
if (pmime_index)
|
|
{
|
|
switch (i)
|
|
{
|
|
case U_MULTICHAR_CONSTANT32('t','x','t',0): *pmime_index = U_txt; break;
|
|
case U_MULTICHAR_CONSTANT32('u','s','p',0): *pmime_index = U_usp; break;
|
|
case U_MULTICHAR_CONSTANT32('c','s','p',0): *pmime_index = U_csp; break;
|
|
case U_MULTICHAR_CONSTANT32('c','g','i',0): *pmime_index = U_cgi; break;
|
|
case U_MULTICHAR_CONSTANT32('s','v','g',0): *pmime_index = U_svg; return "image/svg+xml";
|
|
case U_MULTICHAR_CONSTANT32('p','h','p',0): *pmime_index = U_php; return "application/x-httpd-php";
|
|
}
|
|
}
|
|
|
|
return U_CTYPE_TEXT_WITH_CHARSET;
|
|
}
|
|
}
|
|
|
|
switch (u_get_unalignedp16(suffix))
|
|
{
|
|
case U_MULTICHAR_CONSTANT16('g','z'):
|
|
{
|
|
if (pmime_index) *pmime_index = U_gz;
|
|
|
|
return "application/x-gzip";
|
|
}
|
|
case U_MULTICHAR_CONSTANT16('b','r'):
|
|
{
|
|
if (pmime_index) *pmime_index = U_br;
|
|
|
|
return "application/x-brotli";
|
|
}
|
|
case U_MULTICHAR_CONSTANT16('p','l'):
|
|
{
|
|
if (pmime_index) *pmime_index = U_perl;
|
|
|
|
return "text/x-script.perl";
|
|
}
|
|
case U_MULTICHAR_CONSTANT16('r','b'):
|
|
{
|
|
if (pmime_index) *pmime_index = U_ruby;
|
|
|
|
return "text/x-ruby";
|
|
}
|
|
case U_MULTICHAR_CONSTANT16('p','y'):
|
|
{
|
|
if (pmime_index) *pmime_index = U_python;
|
|
|
|
return "text/x-script.phyton";
|
|
}
|
|
}
|
|
|
|
i = *suffix;
|
|
|
|
if (i < 'a' || i > 'z') goto cdefault;
|
|
|
|
goto *((char*)&&case_a + dispatch_table[i-'a']);
|
|
|
|
case_a: ptr = mimetab_a; goto loop;
|
|
case_b: ptr = mimetab_b; goto loop;
|
|
case_c: ptr = mimetab_c; goto loop;
|
|
case_d: ptr = mimetab_d; goto loop;
|
|
case_e: ptr = mimetab_e; goto loop;
|
|
|
|
case_g: ptr = mimetab_g; goto loop;
|
|
case_h: ptr = mimetab_h; goto loop;
|
|
|
|
case_j: ptr = mimetab_j; goto loop;
|
|
|
|
case_m: ptr = mimetab_m; goto loop;
|
|
|
|
case_o: ptr = mimetab_o; goto loop;
|
|
case_p: ptr = mimetab_p; goto loop;
|
|
case_q: ptr = mimetab_q; goto loop;
|
|
case_r: ptr = mimetab_r; goto loop;
|
|
case_s: ptr = mimetab_s; goto loop;
|
|
case_t: ptr = mimetab_t; goto loop;
|
|
|
|
case_w: ptr = mimetab_w; goto loop;
|
|
case_x: ptr = mimetab_x; goto loop;
|
|
case_z: ptr = mimetab_z; goto loop;
|
|
|
|
cdefault: ptr = mimetab_null;
|
|
|
|
loop:
|
|
while (ptr->name)
|
|
{
|
|
U_INTERNAL_PRINT("mimetab = %p (%s,%u,%s)", ptr, ptr->name, ptr->name_len, ptr->type)
|
|
|
|
for (i = 0; i < ptr->name_len; ++i)
|
|
{
|
|
if (suffix[i+1] != ptr->name[i])
|
|
{
|
|
++ptr;
|
|
|
|
goto loop;
|
|
}
|
|
}
|
|
|
|
if (pmime_index) *pmime_index = U_know;
|
|
|
|
return ptr->type;
|
|
}
|
|
|
|
if (u_get_unalignedp16(suffix) == U_MULTICHAR_CONSTANT16('j','s')) // NB: must be here because of conflit with .json
|
|
{
|
|
if (pmime_index) *pmime_index = U_js;
|
|
|
|
return "application/javascript"; // RFC 4329 (2006) now recommends the use of application/javascript
|
|
}
|
|
|
|
if (pmime_index) *pmime_index = U_unknow;
|
|
|
|
return 0;
|
|
}
|