tb_bool_t tb_object_data_writ_to_url(tb_object_ref_t object, tb_char_t const* url) { // check tb_object_data_t* data = tb_object_data_cast(object); tb_assert_and_check_return_val(data && tb_object_data_getp((tb_object_ref_t)data) && url, tb_false); // make stream tb_stream_ref_t stream = tb_stream_init_from_url(url); tb_assert_and_check_return_val(stream, tb_false); // ctrl if (tb_stream_type(stream) == TB_STREAM_TYPE_FILE) tb_stream_ctrl(stream, TB_STREAM_CTRL_FILE_SET_MODE, TB_FILE_MODE_WO | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); // open stream tb_bool_t ok = tb_false; if (tb_stream_open(stream)) { // writ stream if (tb_stream_bwrit(stream, (tb_byte_t const*)tb_object_data_getp((tb_object_ref_t)data), tb_object_data_size((tb_object_ref_t)data))) ok = tb_true; } // exit stream tb_stream_exit(stream); // ok? return ok; }
/* ////////////////////////////////////////////////////////////////////////////////////// * 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; }
/* ////////////////////////////////////////////////////////////////////////////////////// * interfaces */ tb_object_ref_t tb_object_data_init_from_url(tb_char_t const* url) { // check tb_assert_and_check_return_val(url, tb_null); // init stream tb_stream_ref_t stream = tb_stream_init_from_url(url); tb_assert_and_check_return_val(stream, tb_null); // make stream tb_object_ref_t object = tb_null; if (tb_stream_open(stream)) { // read all data tb_size_t size = 0; tb_byte_t* data = (tb_byte_t*)tb_stream_bread_all(stream, tb_false, &size); if (data) { // make object object = tb_object_data_init_from_data(data, size); // exit data tb_free(data); } // exit stream tb_stream_exit(stream); } // ok? return object; }
/* ////////////////////////////////////////////////////////////////////////////////////// * 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; }
gb_bitmap_ref_t gb_bitmap_init_from_url(tb_size_t pixfmt, tb_char_t const* url) { // check tb_assert_and_check_return_val(GB_PIXFMT_OK(pixfmt) && url, tb_null); // init stream tb_stream_ref_t stream = tb_stream_init_from_url(url); tb_assert_and_check_return_val(stream, tb_null); // init bitmap from stream gb_bitmap_ref_t bitmap = tb_null; if (tb_stream_open(stream)) bitmap = gb_bitmap_init_from_stream(pixfmt, stream); // exit stream tb_stream_exit(stream); // ok? return bitmap; }
tb_long_t tb_object_writ_to_data(tb_object_ref_t object, tb_byte_t* data, tb_size_t size, tb_size_t format) { // check tb_assert_and_check_return_val(object && data && size, -1); // make stream tb_long_t writ = -1; tb_stream_ref_t stream = tb_stream_init_from_data(data, size); if (stream) { // open and writ stream if (tb_stream_open(stream)) writ = tb_object_writ(object, stream, format); // exit stream tb_stream_exit(stream); } // ok? return writ; }
tb_void_t tb_dump_data_from_url(tb_char_t const* url) { // check tb_assert_and_check_return(url); // init stream tb_stream_ref_t stream = tb_stream_init_from_url(url); if (stream) { // open stream if (tb_stream_open(stream)) { // dump stream tb_dump_data_from_stream(stream); } // exit stream tb_stream_exit(stream); } }
tb_void_t tb_dump_data(tb_byte_t const* data, tb_size_t size) { // check tb_assert_and_check_return(data && size); // init stream tb_stream_ref_t stream = tb_stream_init_from_data(data, size); if (stream) { // open stream if (tb_stream_open(stream)) { // dump stream tb_dump_data_from_stream(stream); } // exit stream tb_stream_exit(stream); } }
tb_object_ref_t tb_object_read_from_data(tb_byte_t const* data, tb_size_t size) { // check tb_assert_and_check_return_val(data && size, tb_null); // init tb_object_ref_t object = tb_null; // make stream tb_stream_ref_t stream = tb_stream_init_from_data(data, size); tb_assert_and_check_return_val(stream, tb_null); // read object if (tb_stream_open(stream)) object = tb_object_read(stream); // exit stream tb_stream_exit(stream); // ok? return object; }
tb_object_ref_t tb_object_read_from_url(tb_char_t const* url) { // check tb_assert_and_check_return_val(url, tb_null); // init tb_object_ref_t object = tb_null; // make stream tb_stream_ref_t stream = tb_stream_init_from_url(url); tb_assert_and_check_return_val(stream, tb_null); // read object if (tb_stream_open(stream)) object = tb_object_read(stream); // exit stream tb_stream_exit(stream); // ok? return object; }
/* ////////////////////////////////////////////////////////////////////////////////////// * interfaces */ tb_object_ref_t tb_object_data_init_from_url(tb_char_t const* url) { // check tb_assert_and_check_return_val(url, tb_null); // init stream tb_stream_ref_t stream = tb_stream_init_from_url(url); tb_assert_and_check_return_val(stream, tb_null); // make stream tb_object_ref_t object = tb_null; if (tb_stream_open(stream)) { // size tb_hong_t size = tb_stream_size(stream); if (size > 0 && size < TB_MAXS32) { tb_byte_t* data = tb_malloc0_bytes((tb_size_t)size); if (data) { if (tb_stream_bread(stream, data, (tb_size_t)size)) object = tb_object_data_init_from_data(data, (tb_size_t)size); tb_free(data); } } else object = tb_object_data_init_from_data(tb_null, 0); // check, TODO: read stream if no size tb_assert(size >= 0 && size < TB_MAXS32); // exit stream tb_stream_exit(stream); } // ok? return object; }
tb_long_t tb_object_writ_to_url(tb_object_ref_t object, tb_char_t const* url, tb_size_t format) { // check tb_assert_and_check_return_val(object && url, -1); // make stream tb_long_t writ = -1; tb_stream_ref_t stream = tb_stream_init_from_url(url); if (stream) { // ctrl stream if (tb_stream_type(stream) == TB_STREAM_TYPE_FILE) tb_stream_ctrl(stream, TB_STREAM_CTRL_FILE_SET_MODE, TB_FILE_MODE_RW | TB_FILE_MODE_BINARY | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); // open and writ stream if (tb_stream_open(stream)) writ = tb_object_writ(object, stream, format); // exit stream tb_stream_exit(stream); } // ok? return writ; }
/* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ static tb_bool_t tb_demo_spider_parser_open_html(tb_stream_ref_t stream, tb_char_t const* url) { // check tb_assert_and_check_return_val(stream && url, tb_false); // done tb_bool_t ok = tb_false; do { // find the .suffix tb_char_t const* p = tb_strrchr(url, '.'); if (p) { // not html? tb_check_break ( tb_stricmp(p, ".css") && tb_stricmp(p, ".js") && tb_stricmp(p, ".png") && tb_stricmp(p, ".jpg") && tb_stricmp(p, ".gif") && tb_stricmp(p, ".ico") && tb_stricmp(p, ".bmp") && tb_stricmp(p, ".mp4") && tb_stricmp(p, ".mp3") && tb_stricmp(p, ".flv") && tb_stricmp(p, ".avi") && tb_stricmp(p, ".exe") && tb_stricmp(p, ".msi") && tb_stricmp(p, ".zip") && tb_stricmp(p, ".rar") && tb_stricmp(p, ".7z")); } // ctrl stream if (!tb_stream_ctrl(stream, TB_STREAM_CTRL_SET_URL, url)) break; // open stream if (!tb_stream_open(stream)) break; // the stream size tb_hong_t size = tb_stream_size(stream); tb_check_break(size); // prefetch some data tb_byte_t* data = tb_null; tb_size_t need = tb_min((tb_size_t)size, 256); if (!tb_stream_need(stream, &data, need)) break; // is html? if (tb_strnistr((tb_char_t const*)data, need, "<!DOCTYPE html>")) { ok = tb_true; break; } // is html? ok = tb_strnistr((tb_char_t const*)data, need, "<html")? tb_true : tb_false; } while (0); // failed? if (!ok) { // clos stream if (stream) tb_stream_clos(stream); } // ok? return ok; }
/* ////////////////////////////////////////////////////////////////////////////////////// * main */ tb_int_t tb_demo_stream_main(tb_int_t argc, tb_char_t** argv) { // done tb_option_ref_t option = tb_null; tb_stream_ref_t istream = tb_null; tb_stream_ref_t ostream = tb_null; tb_stream_ref_t pstream = tb_null; do { // init option option = tb_option_init("stream", "the stream demo", g_options); tb_assert_and_check_break(option); // done option if (tb_option_done(option, argc - 1, &argv[1])) { // debug & verbose tb_bool_t debug = tb_option_find(option, "debug"); tb_bool_t verbose = tb_option_find(option, "no-verbose")? tb_false : tb_true; // done url if (tb_option_find(option, "url")) { // init istream istream = tb_stream_init_from_url(tb_option_item_cstr(option, "url")); tb_assert_and_check_break(istream); // ctrl http if (tb_stream_type(istream) == TB_STREAM_TYPE_HTTP) { // enable gzip? if (tb_option_find(option, "gzip")) { // auto unzip if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_AUTO_UNZIP, 1)) break; // need gzip if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Accept-Encoding", "gzip,deflate")) break; } // enable debug? if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD_FUNC, debug? tb_demo_stream_head_func : tb_null)) break; // custem header? if (tb_option_find(option, "header")) { // init tb_string_t key; tb_string_t val; tb_string_init(&key); tb_string_init(&val); // done tb_bool_t k = tb_true; tb_char_t const* p = tb_option_item_cstr(option, "header"); while (*p) { // is key? if (k) { if (*p != ':' && !tb_isspace(*p)) tb_string_chrcat(&key, *p++); else if (*p == ':') { // skip ':' p++; // skip space while (*p && tb_isspace(*p)) p++; // is val now k = tb_false; } else p++; } // is val? else { if (*p != ';') tb_string_chrcat(&val, *p++); else { // skip ';' p++; // skip space while (*p && tb_isspace(*p)) p++; // set header if (tb_string_size(&key) && tb_string_size(&val)) { if (debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val)); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, tb_string_cstr(&key), tb_string_cstr(&val))) break; } // is key now k = tb_true; // clear key & val tb_string_clear(&key); tb_string_clear(&val); } } } // set header if (tb_string_size(&key) && tb_string_size(&val)) { if (debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val)); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, tb_string_cstr(&key), tb_string_cstr(&val))) break; } // exit tb_string_exit(&key); tb_string_exit(&val); } // keep alive? if (tb_option_find(option, "keep-alive")) { if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Connection", "keep-alive")) break; } // post-data? if (tb_option_find(option, "post-data")) { tb_char_t const* post_data = tb_option_item_cstr(option, "post-data"); tb_hize_t post_size = tb_strlen(post_data); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_DATA, post_data, post_size)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break; if (debug) tb_printf("post: %llu\n", post_size); } // post-file? else if (tb_option_find(option, "post-file")) { tb_char_t const* url = tb_option_item_cstr(option, "post-file"); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_URL, url)) break; if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break; if (debug) tb_printf("post: %s\n", url); } } // set range if (tb_option_find(option, "range")) { tb_char_t const* p = tb_option_item_cstr(option, "range"); if (p) { // the bof tb_hize_t eof = 0; tb_hize_t bof = tb_atoll(p); while (*p && tb_isdigit(*p)) p++; if (*p == '-') { p++; eof = tb_atoll(p); } if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_HTTP_SET_RANGE, bof, eof)) break; } } // set timeout if (tb_option_find(option, "timeout")) { tb_size_t timeout = tb_option_item_uint32(option, "timeout"); if (!tb_stream_ctrl(istream, TB_STREAM_CTRL_SET_TIMEOUT, timeout)) break; } // print verbose info if (verbose) tb_printf("open: %s: ..\n", tb_option_item_cstr(option, "url")); // open istream if (!tb_stream_open(istream)) { // print verbose info if (verbose) tb_printf("open: %s\n", tb_state_cstr(tb_stream_state(istream))); break; } // print verbose info if (verbose) tb_printf("open: ok\n"); // init ostream if (tb_option_find(option, "more0")) { // the path tb_char_t const* path = tb_option_item_cstr(option, "more0"); // init ostream = tb_stream_init_from_file(path, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_BINARY | TB_FILE_MODE_TRUNC); // print verbose info if (verbose) tb_printf("save: %s\n", path); } else { // the name tb_char_t const* name = tb_strrchr(tb_option_item_cstr(option, "url"), '/'); if (!name) name = tb_strrchr(tb_option_item_cstr(option, "url"), '\\'); if (!name) name = "/stream.file"; // the path tb_char_t path[TB_PATH_MAXN] = {0}; if (tb_directory_current(path, TB_PATH_MAXN)) tb_strcat(path, name); else break; // init file ostream = tb_stream_init_from_file(path, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_BINARY | TB_FILE_MODE_TRUNC); // print verbose info if (verbose) tb_printf("save: %s\n", path); } tb_assert_and_check_break(ostream); // the limit rate tb_size_t limitrate = 0; if (tb_option_find(option, "limitrate")) limitrate = tb_option_item_uint32(option, "limitrate"); // save it tb_hong_t save = 0; tb_demo_context_t context = {0}; context.verbose = verbose; if ((save = tb_transfer_done(istream, ostream, limitrate, tb_demo_stream_save_func, &context)) < 0) break; } else tb_option_help(option); } else tb_option_help(option); } while (0); // exit pstream if (pstream) tb_stream_exit(pstream); pstream = tb_null; // exit istream if (istream) tb_stream_exit(istream); istream = tb_null; // exit ostream if (ostream) tb_stream_exit(ostream); ostream = tb_null; // exit option if (option) tb_option_exit(option); option = tb_null; return 0; }
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; }
tb_bool_t tb_xml_reader_open(tb_xml_reader_ref_t reader, tb_stream_ref_t stream, tb_bool_t bowner) { // check tb_xml_reader_impl_t* impl = (tb_xml_reader_impl_t*)reader; tb_assert_and_check_return_val(impl && stream, tb_false); // done tb_bool_t ok = tb_false; do { // check tb_assert_and_check_break(!impl->rstream && !impl->istream); // init level impl->level = 0; // init owner impl->bowner = bowner; // init the input stream impl->istream = stream; // init the reader stream impl->rstream = stream; // open the reader stream if be not opened if (!tb_stream_is_opened(impl->rstream) && !tb_stream_open(impl->rstream)) break; // clear text tb_string_clear(&impl->text); // clear element tb_string_clear(&impl->element); // clear name tb_string_clear(&impl->element_name); // clear attribute name tb_string_clear(&impl->attribute_name); // clear attribute data tb_string_clear(&impl->attribute_data); // clear attributes tb_long_t i = 0; for (i = 0; i < TB_XML_READER_ATTRIBUTES_MAXN; i++) { tb_xml_node_ref_t node = (tb_xml_node_ref_t)(impl->attributes + i); tb_string_clear(&node->name); tb_string_clear(&node->data); } // ok ok = tb_true; } while (0); // failed? close it if (!ok) tb_xml_reader_clos(reader); // ok? return ok; }