24#define BROWSCAP_NUM_CONTAINS 5
60#define BROWSCAP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(browscap, v)
62#define DEFAULT_SECTION_NAME "Default Browser Capability Settings"
66static void browscap_entry_dtor(
zval *zvalue)
76static void browscap_entry_dtor_persistent(
zval *zvalue)
86static inline bool is_placeholder(
char c) {
87 return c ==
'?' || c ==
'*';
91static uint8_t browscap_compute_prefix_len(
const zend_string *pattern) {
93 for (i = 0; i <
ZSTR_LEN(pattern); i++) {
94 if (is_placeholder(
ZSTR_VAL(pattern)[i])) {
98 return (uint8_t)
MIN(i, UINT8_MAX);
101static size_t browscap_compute_contains(
103 uint16_t *contains_start, uint8_t *contains_len) {
104 size_t i = start_pos;
106 for (; i <
ZSTR_LEN(pattern); i++) {
107 if (!is_placeholder(
ZSTR_VAL(pattern)[i])) {
111 !is_placeholder(
ZSTR_VAL(pattern)[i + 1])) {
116 *contains_start = (uint16_t)i;
119 for (; i <
ZSTR_LEN(pattern); i++) {
120 if (is_placeholder(
ZSTR_VAL(pattern)[i])) {
124 *contains_len = (uint8_t)
MIN(i - *contains_start, UINT8_MAX);
129static size_t browscap_compute_regex_len(
const zend_string *pattern) {
131 for (i = 0; i <
ZSTR_LEN(pattern); i++) {
145 return len +
sizeof(
"~^$~")-1;
154 res = zend_string_alloc(browscap_compute_regex_len(pattern),
persistent);
160 for (i = 0; i <
ZSTR_LEN(pattern); i++,
j++) {
220 zend_string_addref(interned);
222 interned = zend_string_copy(str);
226 zend_hash_add_new_ptr(&ctx->
str_interned, interned, interned);
243 zend_string_addref(interned);
249 zend_hash_add_new_ptr(&ctx->
str_interned, interned, interned);
256static void browscap_add_kv(
269 for (uint32_t i = entry->
kv_start; i < entry->kv_end; i++) {
282 ZSTR_H(
key) = zend_inline_hash_func(
"browser_name_regex",
sizeof(
"browser_name_regex")-1);
288 ZSTR_H(
key) = zend_inline_hash_func(
"browser_name_pattern",
sizeof(
"browser_name_pattern")-1);
295 ZSTR_H(
key) = zend_inline_hash_func(
"parent",
sizeof(
"parent")-1);
300 browscap_entry_add_kv_to_existing_array(bdata, entry,
ht);
315 switch (callback_type) {
342 "'Parent' value cannot be same as the section name: %s "
354 browscap_add_kv(bdata, new_key, new_value,
persistent);
366 if (
ZSTR_LEN(pattern) > UINT16_MAX) {
368 "Skipping excessively long pattern of length %zd",
ZSTR_LEN(pattern));
377 zend_string_release(pattern);
383 zend_hash_update_ptr(bdata->
htab, pattern, entry);
390 entry->
pattern = zend_string_copy(pattern);
394 pos = entry->
prefix_len = browscap_compute_prefix_len(pattern);
396 pos = browscap_compute_contains(pattern,
pos,
411 if (filename ==
NULL || filename[0] ==
'\0') {
431 ctx.
bdata = browdata;
453static void browscap_globals_ctor(zend_browscap_globals *browscap_globals)
455 browscap_globals->activation_bdata.htab =
NULL;
456 browscap_globals->activation_bdata.kv =
NULL;
457 browscap_globals->activation_bdata.filename[0] =
'\0';
471 for (i = 0; i < bdata->
kv_used; i++) {
472 zend_string_release(bdata->
kv[i].
key);
473 zend_string_release(bdata->
kv[i].
value);
491 browscap_bdata_dtor(bdata, 0);
505 char *browscap =
INI_STR(
"browscap");
508 ts_allocate_id(&browscap_globals_id,
sizeof(
browser_data), (ts_allocate_ctor) browscap_globals_ctor,
NULL);
512 if (browscap && browscap[0]) {
513 if (browscap_read_file(browscap, &global_bdata,
true) ==
FAILURE) {
526 browscap_bdata_dtor(bdata, 0);
535 browscap_bdata_dtor(&global_bdata, 1);
541static inline size_t browscap_get_minimum_length(
const browscap_entry *entry) {
550static bool browscap_match_string_wildcard(
const char *
s,
const char *s_end,
const char *pattern,
const char *pattern_end)
552 const char *pattern_current = pattern;
553 const char *s_current =
s;
555 const char *wildcard_pattern_restore_pos =
NULL;
556 const char *wildcard_s_restore_pos =
NULL;
558 while (s_current < s_end) {
559 char pattern_char = *pattern_current;
560 char s_char = *s_current;
562 if (pattern_char ==
'*') {
565 while (pattern_current < pattern_end && *pattern_current ==
'*') {
570 if (pattern_current == pattern_end) {
576 if (*pattern_current !=
'?') {
577 while (s_current < s_end && *s_current != *pattern_current) {
585 wildcard_pattern_restore_pos = pattern_current;
586 wildcard_s_restore_pos = s_current;
589 }
else if (pattern_char == s_char || pattern_char ==
'?') {
595 if (pattern_current == pattern_end) {
596 if (s_current == s_end) {
606 if (wildcard_pattern_restore_pos) {
607 pattern_current = wildcard_pattern_restore_pos;
608 wildcard_s_restore_pos++;
609 s_current = wildcard_s_restore_pos;
617 while (pattern_current < pattern_end && *pattern_current ==
'*') {
622 return pattern_current == pattern_end;
640 cur = zend_memnstr(cur,
653 if (zend_string_equals(agent_name, pattern_lc)) {
654 *found_entry_ptr = entry;
660 if (browscap_match_string_wildcard(
671 for (
size_t i = curr_len; i <
ZSTR_LEN(current_match); i++) {
672 switch (
ZSTR_VAL(current_match)[i]) {
686 if (*cached_prev_len < curr_len) {
687 *found_entry_ptr = entry;
688 *cached_prev_len = curr_len;
691 *found_entry_ptr = entry;
692 *cached_prev_len = curr_len;
705 bool return_array = 0;
724 if (!global_bdata.htab) {
728 bdata = &global_bdata;
731 if (agent_name ==
NULL) {
737 "HTTP_USER_AGENT",
sizeof(
"HTTP_USER_AGENT")-1);
739 if (http_user_agent ==
NULL) {
743 agent_name =
Z_STR_P(http_user_agent);
746 lookup_browser_name = zend_string_tolower(agent_name);
747 found_entry = zend_hash_find_ptr(bdata->
htab, lookup_browser_name);
748 if (found_entry ==
NULL) {
750 size_t cached_prev_len = 0;
758 if (
ZSTR_LEN(lookup_browser_name) < browscap_get_minimum_length(entry)) {
763 bool prefix_matches =
true;
764 for (
size_t i = 0; i < entry->
prefix_len; i++) {
766 prefix_matches =
false;
770 if (!prefix_matches) {
774 if (browser_reg_compare(entry, lookup_browser_name, &found_entry, &cached_prev_len)) {
779 if (found_entry ==
NULL) {
780 found_entry = zend_hash_str_find_ptr(bdata->
htab,
782 if (found_entry ==
NULL) {
791 agent_ht = browscap_entry_to_array(bdata, found_entry);
801 while (found_entry->
parent) {
802 found_entry = zend_hash_find_ptr(bdata->
htab, found_entry->
parent);
803 if (found_entry ==
NULL) {
807 browscap_entry_add_kv_to_existing_array(bdata, found_entry, target_ht);
get_browser(?string $user_agent=null, bool $return_array=false)
#define BROWSCAP_NUM_CONTAINS
browser_data activation_bdata
struct _browscap_parser_ctx browscap_parser_ctx
#define DEFAULT_SECTION_NAME
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
#define PHP_MSHUTDOWN_FUNCTION
#define PHP_MINIT_FUNCTION
#define PHP_RSHUTDOWN_FUNCTION
unsigned const char * pos
#define TRACK_VARS_SERVER
#define PHP_INI_STAGE_ACTIVATE
#define PHP_INI_STAGE_STARTUP
unsigned char key[REFLECTION_KEY_LEN]
browscap_entry * current_entry
zend_string * current_section_name
uint8_t contains_len[BROWSCAP_NUM_CONTAINS]
uint16_t contains_start[BROWSCAP_NUM_CONTAINS]
char filename[MAXPATHLEN]
ZEND_API zend_class_entry * zend_standard_class_def
ZEND_API ZEND_COLD void zend_error(int type, const char *format,...)
ZEND_API zend_result object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties)
#define ZEND_PARSE_PARAMETERS_END()
#define Z_PARAM_STR_OR_NULL(dest)
#define ZEND_DECLARE_MODULE_GLOBALS(module_name)
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
#define ZEND_END_MODULE_GLOBALS(module_name)
#define Z_PARAM_BOOL(dest)
#define ZEND_BEGIN_MODULE_GLOBALS(module_name)
#define pefree(ptr, persistent)
#define pemalloc(size, persistent)
#define safe_perealloc(ptr, nmemb, size, offset, persistent)
zend_string_release_ex(func->internal_function.function_name, 0)
ZEND_API bool zend_is_auto_global(zend_string *name)
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
ZEND_API zval *ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *str, size_t len)
ZEND_API zval *ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
ZEND_API zval *ZEND_FASTCALL zend_hash_add(HashTable *ht, zend_string *key, zval *pData)
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
#define ZEND_HASH_MAP_FOREACH_PTR(ht, _ptr)
#define zend_new_array(size)
#define ZEND_HASH_FOREACH_END()
ZEND_API zend_result zend_parse_ini_file(zend_file_handle *fh, bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg)
#define ZEND_INI_PARSER_ENTRY
void(* zend_ini_parser_cb_t)(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg)
#define ZEND_INI_PARSER_SECTION
struct _zend_file_handle zend_file_handle
#define ZEND_INI_SCANNER_RAW
struct _zend_string zend_string
ZEND_API char *ZEND_FASTCALL zend_str_tolower_copy(char *dest, const char *source, size_t length)
#define zend_tolower_ascii(c)
#define ALLOCA_FLAG(name)
ZEND_API void zend_stream_init_fp(zend_file_handle *handle, FILE *fp, const char *filename)
ZEND_API zend_new_interned_string_func_t zend_new_interned_string
#define ZSTR_ALLOCA_FREE(str, use_heap)
#define ZSTR_IS_INTERNED(s)
#define ZSTR_INIT_LITERAL(s, persistent)
#define ZSTR_EMPTY_ALLOC()
#define zend_string_equals_ci(s1, s2)
#define zend_string_equals_literal_ci(str, c)
#define ZSTR_ALLOCA_ALLOC(str, _len, use_heap)
#define Z_ARRVAL_P(zval_p)
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
#define Z_OBJPROP_P(zval_p)
ZEND_RESULT_CODE zend_result
#define Z_TYPE_FLAGS_P(zval_p)
#define IS_ARRAY_PERSISTENT
#define VCWD_FOPEN(path, mode)
#define VCWD_REALPATH(path, real_path)