static tb_bool_t tb_demo_spider_make_ourl(tb_demo_spider_t* spider, tb_char_t const* url, tb_char_t* data, tb_size_t maxn) { // check tb_assert_and_check_return_val(spider && url && data && maxn, tb_false); // skip protocol tb_char_t* p = (tb_char_t*)url; if (!tb_strnicmp(p, "http://", 7)) p += 7; else if (!tb_strnicmp(p, "https://", 8)) p += 8; // skip space while (*p && tb_isspace(*p)) p++; // format ourl tb_long_t n = tb_snprintf(data, maxn, "%s/%s", spider->root, p); tb_assert_and_check_return_val(n > 0 && n < maxn, tb_false); // no root? append '/' if (!tb_strchr(p, '/') && !tb_strchr(p, '\\')) data[n++] = '/'; tb_assert_and_check_return_val(n < maxn, tb_false); // '\\' => '/' if (data[n - 1] == '/') data[n - 1] = '/'; // directory? append index.html if (data[n - 1] == '/') n += tb_snprintf(data + n, maxn - n, "%s", "index.html"); tb_assert_and_check_return_val(n > 0 && n < maxn, tb_false); // end data[n] = '\0'; // replace '?' => '_' p = data; while (*p) { // replace if (*p == '?') *p = '_'; // next p++; } // trace tb_trace_d("make: %s => %s", url, data); // ok? return n > 0? tb_true : tb_false; }
/* ////////////////////////////////////////////////////////////////////////////////////// * interfaces */ tb_size_t tb_database_sqlite3_probe(tb_url_ref_t url) { // check tb_assert_and_check_return_val(url, 0); // done tb_size_t score = 0; tb_stream_ref_t stream = tb_null; do { // the url arguments tb_char_t const* args = tb_url_args(url); if (args) { // find the database type tb_char_t const* ptype = tb_stristr(args, "type="); if (ptype && !tb_strnicmp(ptype + 5, "sqlite3", 7)) { // ok score = 100; break; } } // has host or port? no sqlite3 if (tb_url_host(url) || tb_url_port(url)) break; // the database path tb_char_t const* path = tb_url_cstr((tb_url_ref_t)url); tb_assert_and_check_break(path); // is file? if (tb_url_protocol(url) == TB_URL_PROTOCOL_FILE) score += 20; // init stream stream = tb_stream_init_from_url(path); tb_assert_and_check_break(stream); // open stream if (!tb_stream_open(stream)) break; // read head tb_char_t head[16] = {0}; if (!tb_stream_bread(stream, (tb_byte_t*)head, 15)) break; // is sqlite3? if (!tb_stricmp(head, "SQLite format 3")) score = 100; } while (0); // exit stream if (stream) tb_stream_exit(stream); stream = tb_null; // trace tb_trace_d("probe: %s, score: %lu", tb_url_cstr((tb_url_ref_t)url), score); // ok? return score; }
/* ////////////////////////////////////////////////////////////////////////////////////// * comparator */ static tb_long_t gb_named_color_comp(tb_iterator_ref_t iterator, tb_cpointer_t item, tb_cpointer_t name) { // check tb_assert_return_val(item && name, 0); // comp return tb_strnicmp(((gb_named_color_t const*)item)->name, (tb_char_t const*)name, tb_strlen(((gb_named_color_t const*)item)->name)); }
tb_bool_t vm86_parser_get_offset_value(tb_char_t const** pp, tb_char_t const* e, tb_uint32_t* value, tb_hash_map_ref_t proc_labels, vm86_data_ref_t data) { // check tb_assert(pp && e && value && proc_labels); // done tb_bool_t ok = tb_false; tb_char_t const* p = *pp; do { // attempt to get segment name tb_char_t segment[16] = {0}; tb_bool_t has_segment = vm86_parser_get_segment_name(&p, e, segment, sizeof(segment)); // skip "short ..." if (p + 6 < e && !tb_strnicmp(p, "short ", 6)) p += 6; // skip the space while (p < e && tb_isspace(*p)) p++; // get instruction name tb_char_t name[256] = {0}; if (!vm86_parser_get_variable_name(&p, e, name, sizeof(name))) break; // is .data segment? if (has_segment && !tb_stricmp(segment, "ds")) *value = vm86_data_get(data, name, tb_null); // is .code segment? else if (has_segment && !tb_stricmp(segment, "cs")) *value = (tb_uint32_t)tb_hash_map_get(proc_labels, name); else { // get value if (tb_hash_map_find(proc_labels, name) != tb_iterator_tail(proc_labels)) *value = (tb_uint32_t)tb_hash_map_get(proc_labels, name); else if (vm86_data_is(data, name)) *value = vm86_data_get(data, name, tb_null); else break; } // check tb_assert(*value < TB_MAXU32); // trace tb_trace_d("offset: %s: %x", name, *value); // ok ok = tb_true; } while (0); // update the code pointer if ok if (ok) *pp = p; // ok? return ok; }
/* ////////////////////////////////////////////////////////////////////////////////////// * interfaces */ tb_bool_t tb_dns_init() { // done tb_size_t count = 0; if (tb_file_info("/etc/resolv.conf", tb_null)) { /* try get list from "/etc/resolv.conf" * * # Generated by NetworkManager * nameserver 10.1.20.10 * nameserver 8.8.8.8 * */ tb_stream_ref_t stream = tb_stream_init_from_url("/etc/resolv.conf"); if (stream) { // open if (tb_stream_open(stream)) { // read tb_long_t size = 0; tb_char_t line[8192]; while ((size = tb_stream_bread_line(stream, line, 8192)) >= 0) { if (size && !tb_strnicmp(line, "nameserver", 10)) { // seek to server tb_char_t const* p = line + 10; while (*p && !tb_isdigit(*p)) p++; tb_check_continue(*p); // add server tb_dns_server_add(p); // count++ count++; } } } // exit tb_stream_exit(stream); } } // no server? add the default server if (!count) { tb_dns_server_add("8.8.8.8"); tb_dns_server_add("8.8.8.4"); } // ok return tb_true; }
static tb_void_t tb_test_strnicmp(tb_char_t const* s1, tb_char_t const* s2, tb_size_t size) { __tb_volatile__ tb_long_t n = 1000000; __tb_volatile__ tb_long_t r = 0; tb_hong_t t = tb_mclock(); while (n--) { r = tb_strnicmp(s1, s2, size); } t = tb_mclock() - t; tb_printf("%lld ms, tb_test_strnicmp(%s, %s, %u) = %ld\n", t, s1, s2, size, r); }
static tb_size_t tb_object_bin_reader_probe(tb_stream_ref_t stream) { // check tb_assert_and_check_return_val(stream, 0); // need it tb_byte_t* p = tb_null; if (!tb_stream_need(stream, &p, 3)) return 0; tb_assert_and_check_return_val(p, 0); // ok? return !tb_strnicmp((tb_char_t const*)p, "tbo", 3)? 80 : 0; }
tb_bool_t tb_path_is_absolute(tb_char_t const* path) { // check tb_assert_and_check_return_val(path, tb_false); // is absolute? #ifdef TB_CONFIG_OS_WINDOWS return ( path[0] == '~' || (tb_isalpha(path[0]) && path[1] == ':' && tb_path_is_separator(path[2]))); #else return ( path[0] == '/' || path[0] == '\\' || path[0] == '~' || !tb_strnicmp(path, "file:", 5)); #endif }
static tb_object_ref_t tb_object_bin_reader_done(tb_stream_ref_t stream) { // read bin header tb_byte_t data[32] = {0}; if (!tb_stream_bread(stream, data, 5)) return tb_null; // check if (tb_strnicmp((tb_char_t const*)data, "tbo00", 5)) return tb_null; // init tb_object_ref_t object = tb_null; tb_object_bin_reader_t reader = {0}; // init reader reader.stream = stream; reader.list = tb_vector_init(256, tb_item_func_obj()); tb_assert_and_check_return_val(reader.list, tb_null); // the type & size tb_size_t type = 0; tb_uint64_t size = 0; tb_object_reader_bin_type_size(stream, &type, &size); // trace tb_trace_d("root: type: %lu, size: %llu", type, size); // the func tb_object_bin_reader_func_t func = tb_object_bin_reader_func(type); // check tb_assert(func); // read it if (func) object = func(&reader, type, size); // exit the list if (reader.list) tb_vector_exit(reader.list); // ok? return object; }
static tb_size_t tb_object_xplist_reader_probe(tb_stream_ref_t stream) { // check tb_assert_and_check_return_val(stream, 0); // need it tb_byte_t* p = tb_null; if (!tb_stream_need(stream, &p, 5)) return 0; tb_assert_and_check_return_val(p, 0); // is xml data? if (!tb_strnicmp((tb_char_t const*)p, "<?xml", 5)) { // need more data if (!tb_stream_need(stream, &p, 256)) return 5; tb_assert_and_check_return_val(p, 5); // is xplist? return tb_strnistr((tb_char_t const*)p, 256, "DOCTYPE plist")? 80 : 10; } // ok? return 0; }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t main(tb_int_t argc, tb_char_t** argv) { // init tbox #if 0 if (!tb_init(tb_null, (tb_byte_t*)malloc(300 * 1024 * 1024), 300 * 1024 * 1024)) return 0; #else if (!tb_init(tb_null, tb_null, 0)) return 0; #endif // init tb_int_t ok = 0; tb_char_t const* name = tb_null; // find the main func from the .demo file if (!name) { // init file tb_file_ref_t file = tb_file_init(".demo", TB_FILE_MODE_RO); if (file) { // read line tb_char_t line[8192] = {0}; if (tb_file_read(file, (tb_byte_t*)line, sizeof(line) - 1)) { tb_size_t i = 0; tb_size_t n = tb_arrayn(g_demo); for (i = 0; i < n; i++) { // find it? if (g_demo[i].name && !tb_strnicmp(g_demo[i].name, line, tb_strlen(g_demo[i].name))) { // save name name = g_demo[i].name; // done main ok = g_demo[i].main(argc, argv); break; } } } // exit file tb_file_exit(file); } } // find the main func from the first argument if (!name && argc > 1 && argv[1]) { tb_size_t i = 0; tb_size_t n = tb_arrayn(g_demo); for (i = 0; i < n; i++) { // find it? if (g_demo[i].name && !tb_stricmp(g_demo[i].name, argv[1])) { // save name name = g_demo[i].name; // done main ok = g_demo[i].main(argc - 1, argv + 1); break; } } } // no this demo? help it if (!name) { tb_trace_i("======================================================================"); tb_trace_i("help: echo \"name\" > ./.demo"); tb_trace_i("help: ./demo.b args ..."); tb_trace_i("help: or"); tb_trace_i("help: ./demo.b name args ..."); tb_trace_i("help: "); tb_trace_i("help: example: echo \"stream\" > ./.demo"); tb_trace_i("help: example: ./demo.b --help"); tb_trace_i("help: example: ./demo.b http://www.xxxxx.com /tmp/a"); tb_trace_i("help: example: or"); tb_trace_i("help: example: ./demo.b stream http://www.xxxxx.com /tmp/a"); tb_trace_i("help: "); // walk name tb_size_t i = 0; tb_size_t n = tb_arrayn(g_demo); for (i = 0; i < n; i++) tb_trace_i("help: name: %s", g_demo[i].name); } // exit tbox tb_exit(); // ok? return ok; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_size_t tb_path_translate(tb_char_t* path, tb_size_t size, tb_size_t maxn) { // check tb_assert_and_check_return_val(path, 0); // file://? tb_char_t* p = path; if (!tb_strnicmp(p, "file:", 5)) p += 5; // is user directory? else if (path[0] == '~') { // get the home directory tb_char_t home[TB_PATH_MAXN]; tb_size_t home_size = tb_directory_home(home, sizeof(home) - 1); tb_assert_and_check_return_val(home_size, 0); // check the path space tb_size_t path_size = size? size : tb_strlen(path); tb_assert_and_check_return_val(home_size + path_size - 1 < maxn, 0); // move the path and ensure the enough space for the home directory tb_memmov(path + home_size, path + 1, path_size - 1); // copy the home directory tb_memcpy(path, home, home_size); path[home_size + path_size - 1] = '\0'; } // remove repeat separator tb_char_t* q = path; tb_size_t repeat = 0; for (; *p; p++) { if (tb_path_is_separator(*p)) { // save the separator if not exists if (!repeat) *q++ = TB_PATH_SEPARATOR; // repeat it repeat++; } else { // save character *q++ = *p; // clear repeat repeat = 0; } } // remove the tail separator and not root: '/' if (q > path + 1 && *(q - 1) == TB_PATH_SEPARATOR) q--; // end *q = '\0'; // is windows path? if (q > path + 1 && tb_isalpha(path[0]) && path[1] == ':') { // get the upper drive prefix path[0] = tb_toupper(path[0]); // append the drive separator if not exists if (q - path == 2) { *q++ = TB_PATH_SEPARATOR; *q = '\0'; } } // trace tb_trace_d("translate: %s", path); // ok return q - path; }
tb_size_t tb_xml_reader_next(tb_xml_reader_ref_t reader) { // check tb_xml_reader_impl_t* impl = (tb_xml_reader_impl_t*)reader; tb_assert_and_check_return_val(impl && impl->rstream, TB_XML_READER_EVENT_NONE); // reset event impl->event = TB_XML_READER_EVENT_NONE; // next while (!impl->event) { // peek character tb_char_t* pc = tb_null; if (!tb_stream_need(impl->rstream, (tb_byte_t**)&pc, 1) || !pc) break; // is element? if (*pc == '<') { // parse element: <...> tb_char_t const* element = tb_xml_reader_element_parse(impl); tb_assert_and_check_break(element); // is document begin: <?xml version="..." charset=".." ?> tb_size_t size = tb_string_size(&impl->element); if (size > 4 && !tb_strnicmp(element, "?xml", 4)) { // update event impl->event = TB_XML_READER_EVENT_DOCUMENT; // update version & charset tb_xml_node_ref_t attr = (tb_xml_node_ref_t)tb_xml_reader_attributes(reader); for (; attr; attr = attr->next) { if (!tb_string_cstricmp(&attr->name, "version")) tb_string_strcpy(&impl->version, &attr->data); if (!tb_string_cstricmp(&attr->name, "encoding")) tb_string_strcpy(&impl->charset, &attr->data); } // transform stream => utf-8 if (tb_string_cstricmp(&impl->charset, "utf-8") && tb_string_cstricmp(&impl->charset, "utf8")) { // charset tb_size_t charset = TB_CHARSET_TYPE_UTF8; if (!tb_string_cstricmp(&impl->charset, "gb2312") || !tb_string_cstricmp(&impl->charset, "gbk")) charset = TB_CHARSET_TYPE_GB2312; else tb_trace_e("the charset: %s is not supported", tb_string_cstr(&impl->charset)); // init transform stream if (charset != TB_CHARSET_TYPE_UTF8) { #ifdef TB_CONFIG_MODULE_HAVE_CHARSET // init the filter stream if (!impl->fstream) impl->fstream = tb_stream_init_filter_from_charset(impl->istream, charset, TB_CHARSET_TYPE_UTF8); else { // ctrl stream if (!tb_stream_ctrl(impl->fstream, TB_STREAM_CTRL_FLTR_SET_STREAM, impl->istream)) break; // the filter tb_stream_filter_ref_t filter = tb_null; if (!tb_stream_ctrl(impl->fstream, TB_STREAM_CTRL_FLTR_GET_FILTER, &filter)) break; tb_assert_and_check_break(filter); // ctrl filter if (!tb_stream_filter_ctrl(filter, TB_STREAM_FILTER_CTRL_CHARSET_SET_FTYPE, charset)) break; } // open the filter stream if (impl->fstream && tb_stream_open(impl->fstream)) impl->rstream = impl->fstream; tb_string_cstrcpy(&impl->charset, "utf-8"); #else // trace tb_trace_e("unicode type is not supported, please enable charset module config if you want to use it!"); #endif } } } // is document type: <!DOCTYPE ... > else if (size > 8 && !tb_strnicmp(element, "!DOCTYPE", 8)) { // update event impl->event = TB_XML_READER_EVENT_DOCUMENT_TYPE; } // is element end: </name> else if (size > 1 && element[0] == '/') { // check tb_check_break(impl->level); // update event impl->event = TB_XML_READER_EVENT_ELEMENT_END; // leave impl->level--; } // is comment: <!-- text --> else if (size >= 3 && !tb_strncmp(element, "!--", 3)) { // no comment end? if (element[size - 2] != '-' || element[size - 1] != '-') { // patch '>' tb_string_chrcat(&impl->element, '>'); // seek to comment end tb_char_t ch = '\0'; tb_int_t n = 0; while ((ch = tb_stream_bread_s8(impl->rstream))) { // --> if (n == 2 && ch == '>') break; else { // append it tb_string_chrcat(&impl->element, ch); if (ch == '-') n++; else n = 0; } } // update event if (ch != '\0') impl->event = TB_XML_READER_EVENT_COMMENT; } else impl->event = TB_XML_READER_EVENT_COMMENT; } // is cdata: <![CDATA[ text ]]> else if (size >= 8 && !tb_strnicmp(element, "![CDATA[", 8)) { if (element[size - 2] != ']' || element[size - 1] != ']') { // patch '>' tb_string_chrcat(&impl->element, '>'); // seek to cdata end tb_char_t ch = '\0'; tb_int_t n = 0; while ((ch = tb_stream_bread_s8(impl->rstream))) { // ]]> if (n == 2 && ch == '>') break; else { // append it tb_string_chrcat(&impl->element, ch); if (ch == ']') n++; else n = 0; } } // update event if (ch != '\0') impl->event = TB_XML_READER_EVENT_CDATA; } else impl->event = TB_XML_READER_EVENT_CDATA; } // is empty element: <name/> else if (size > 1 && element[size - 1] == '/') { // update event impl->event = TB_XML_READER_EVENT_ELEMENT_EMPTY; } // is element begin: <name> else { // update event impl->event = TB_XML_READER_EVENT_ELEMENT_BEG; // enter impl->level++; } // trace // tb_trace_d("<%s>", element); } // is text: <> text </> else if (*pc) { // parse text: <> ... <> tb_char_t const* text = tb_xml_reader_text_parse(impl); if (text && tb_string_cstrcmp(&impl->text, "\r\n") && tb_string_cstrcmp(&impl->text, "\n")) impl->event = TB_XML_READER_EVENT_TEXT; // trace // tb_trace_d("%s", text); } else { // skip the invalid character if (!tb_stream_skip(impl->rstream, 1)) break; } } // ok? return impl->event; }
static tb_void_t tb_demo_http_session_head_parse(tb_demo_http_session_ref_t session) { // check tb_assert_and_check_return(session); // the first line? tb_char_t const* p = session->line; if (!session->line_index) { // parse get if (!tb_strnicmp(p, "GET", 3)) { session->code = TB_HTTP_CODE_OK; session->method = TB_HTTP_METHOD_GET; p += 3; } // parse post else if (!tb_strnicmp(p, "POST", 4)) { session->code = TB_HTTP_CODE_NOT_IMPLEMENTED; session->method = TB_HTTP_METHOD_POST; p += 4; } // other method is not implemented else session->code = TB_HTTP_CODE_NOT_IMPLEMENTED; // get or post? parse the path if ( session->method == TB_HTTP_METHOD_GET || session->method == TB_HTTP_METHOD_POST) { // skip space while (*p && tb_isspace(*p)) p++; // append path tb_size_t i = 0; while (*p && !tb_isspace(*p) && i < sizeof(session->path) - 1) session->path[i++] = *p++; session->path[i] = '\0'; } } // key: value? else { // seek to value while (*p && *p != ':') p++; tb_assert_and_check_return(*p); p++; while (*p && tb_isspace(*p)) p++; // no value tb_check_return(*p); // parse content-length if (!tb_strnicmp(session->line, "Content-Length", 14)) session->content_size = tb_stou64(p); // parse connection else if (!tb_strnicmp(session->line, "Connection", 10)) session->keep_alive = !tb_stricmp(p, "keep-alive"); // parse range else if (!tb_strnicmp(session->line, "Range", 5)) session->code = TB_HTTP_CODE_NOT_IMPLEMENTED; } }
tb_long_t tb_string_cstrnicmp(tb_string_ref_t string, tb_char_t const* s, tb_size_t n) { // check tb_assert_and_check_return_val(string && s, 0); return tb_strnicmp(tb_string_cstr(string), s, n); }
static pid_t it_pid(tb_char_t const* name) { // check tb_assert_and_check_return_val(name, 0); // is pid? tb_size_t pid = tb_atoi(name); if (pid) return pid; // init struct kinfo_proc* p = tb_null; struct kinfo_proc* q = tb_null; tb_int_t mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; tb_size_t miblen = 4; tb_size_t size = 0; tb_long_t ok = sysctl(mib, miblen, tb_null, &size, tb_null, 0); // walk do { // grow size += size / 10; q = tb_ralloc(p, size); // no memory? if (!q) { if (p) tb_free(p); return 0; } // list p = q; ok = sysctl(mib, miblen, p, &size, tb_null, 0); } while (ok == -1 && errno == ENOMEM); // ok? if (ok == 0) { if (!(size % sizeof(struct kinfo_proc))) { tb_size_t i = 0; tb_size_t n = size / sizeof(struct kinfo_proc); // try accurate name for (i = 0; i < n; i++) { if (!tb_stricmp(p[i].kp_proc.p_comm, name)) { tb_trace_i("name: %s, pid: %u", p[i].kp_proc.p_comm, p[i].kp_proc.p_pid); pid = p[i].kp_proc.p_pid; break; } } // try other name if (!pid) { for (i = 0; i < n; i++) { if (!tb_strnicmp(p[i].kp_proc.p_comm, name, tb_strlen(name))) { tb_trace_i("name: %s, pid: %u", p[i].kp_proc.p_comm, p[i].kp_proc.p_pid); pid = p[i].kp_proc.p_pid; break; } } } } } // free if (p) tb_free(p); // ok return pid; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_time_t tb_http_date_from_cstr(tb_char_t const* cstr, tb_size_t size) { // check tb_assert_and_check_return_val(cstr && size, 0); // done tb_tm_t tm = {0}; tb_time_t date = 0; tb_char_t const* p = cstr; tb_char_t const* e = cstr + size; do { // skip space while (p < e && tb_isspace(*p)) p++; // ignore #if 0 // parse week if ((p + 6 < e && !tb_strnicmp(p, "Monday", 6)) || (p + 3 < e && !tb_strnicmp(p, "Mon", 3))) tm.week = 1; else if ((p + 7 < e && !tb_strnicmp(p, "Tuesday", 7)) || (p + 3 < e && !tb_strnicmp(p, "Tue", 3))) tm.week = 2; else if ((p + 9 < e && !tb_strnicmp(p, "Wednesday", 9)) || (p + 3 < e && !tb_strnicmp(p, "Wed", 3))) tm.week = 3; else if ((p + 8 < e && !tb_strnicmp(p, "Thursday", 8)) || (p + 3 < e && !tb_strnicmp(p, "Thu", 3))) tm.week = 4; else if ((p + 6 < e && !tb_strnicmp(p, "Friday", 6)) || (p + 3 < e && !tb_strnicmp(p, "Fri", 3))) tm.week = 5; else if ((p + 8 < e && !tb_strnicmp(p, "Saturday", 8)) || (p + 3 < e && !tb_strnicmp(p, "Sat", 3))) tm.week = 6; else if ((p + 6 < e && !tb_strnicmp(p, "Sunday", 6)) || (p + 3 < e && !tb_strnicmp(p, "Sun", 3))) tm.week = 7; #endif // skip week while (p < e && *p != ',' && !tb_isspace(*p)) p++; if (p < e && (*p == ',' || tb_isspace(*p))) p++; // skip space while (p < e && tb_isspace(*p)) p++; // is day? tb_bool_t year_suffix = tb_true; if (p < e && tb_isdigit(*p)) { /* prefix year * * .e.g * year_suffix == false: Sun, 06-Nov-1994 08:49:37 * year_suffix == true: Sun Nov 6 08:49:37 1994 */ year_suffix = tb_false; // parse day tm.mday = tb_s10tou32(p); // skip day while (p < e && *p != '-' && !tb_isspace(*p)) p++; if (p < e && (*p == '-' || tb_isspace(*p))) p++; } // parse month if (p + 3 < e && !tb_strnicmp(p, "Jan", 3)) tm.month = 1; else if (p + 3 < e && !tb_strnicmp(p, "Feb", 3)) tm.month = 2; else if (p + 3 < e && !tb_strnicmp(p, "Mar", 3)) tm.month = 3; else if (p + 3 < e && !tb_strnicmp(p, "Apr", 3)) tm.month = 4; else if (p + 3 < e && !tb_strnicmp(p, "May", 3)) tm.month = 5; else if (p + 3 < e && !tb_strnicmp(p, "Jun", 3)) tm.month = 6; else if (p + 3 < e && !tb_strnicmp(p, "Jul", 3)) tm.month = 7; else if (p + 3 < e && !tb_strnicmp(p, "Aug", 3)) tm.month = 8; else if (p + 3 < e && !tb_strnicmp(p, "Sep", 3)) tm.month = 9; else if (p + 3 < e && !tb_strnicmp(p, "Oct", 3)) tm.month = 10; else if (p + 3 < e && !tb_strnicmp(p, "Nov", 3)) tm.month = 11; else if (p + 3 < e && !tb_strnicmp(p, "Dec", 3)) tm.month = 12; // skip month while (p < e && *p != '-' && !tb_isspace(*p)) p++; if (p < e && (*p == '-' || tb_isspace(*p))) p++; // year suffix? if (year_suffix) { // parse day tm.mday = tb_s10tou32(p); } else { // parse year tm.year = tb_s10tou32(p); if (tm.year < 100) tm.year += 2000; } // skip year or day while (p < e && !tb_isspace(*p)) p++; while (p < e && tb_isspace(*p)) p++; // parse hour tm.hour = tb_s10tou32(p); // skip hour while (p < e && *p != ':') p++; if (p < e && *p == ':') p++; // parse minute tm.minute = tb_s10tou32(p); // skip minute while (p < e && *p != ':') p++; if (p < e && *p == ':') p++; // parse second tm.second = tb_s10tou32(p); // year suffix? if (year_suffix) { // skip time while (p < e && !tb_isspace(*p)) p++; while (p < e && tb_isspace(*p)) p++; // parse year tm.year = tb_s10tou32(p); if (tm.year < 100) tm.year += 1900; } // make date date = tb_gmmktime(&tm); } while (0); // ok? return date; }