/* ============================================================================ // // = LIBRARY // ULib - c library // // = FILENAME // utility.c // // = AUTHOR // Stefano Casazza // // ============================================================================ */ /* #define DEBUG_DEBUG */ #include #include #ifdef HAVE_SYSEXITS_H # include #else # include #endif #ifdef HAVE_FNMATCH # include #endif #ifdef ENABLE_THREAD # if defined(__NetBSD__) || defined(__UNIKERNEL__) # include # endif # ifdef HAVE_SYS_SYSCALL_H # include # endif #endif #ifndef _MSWINDOWS_ # include # if defined(U_LINUX) && defined(HAVE_LIBCAP) # include # include # 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 # include # include # 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 * * 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; }