Esempio n. 1
0
File: tbox.c Progetto: waruqi/tbox
tb_version_t const* tb_version()
{
    // init version tag for binary search
    static __tb_volatile__ tb_char_t const* s_vtag = "[tbox]: [vtag]: " TB_VERSION_STRING; tb_used(s_vtag);

    // init version
    static tb_version_t s_version = {0};
    if (!s_version.major)
    {
        s_version.major = TB_VERSION_MAJOR;
        s_version.minor = TB_VERSION_MINOR;
        s_version.alter = TB_VERSION_ALTER;
        s_version.build = (tb_hize_t)tb_atoll(TB_VERSION_BUILD_STRING);
    }

    return &s_version;
}
Esempio n. 2
0
File: option.c Progetto: waruqi/tbox
tb_bool_t tb_option_done(tb_option_ref_t option, tb_size_t argc, tb_char_t** argv)
{
    // check
    tb_option_impl_t* impl = (tb_option_impl_t*)option;
    tb_assert_and_check_return_val(impl && impl->list && impl->opts, tb_false);

    // walk arguments
    tb_size_t               i = 0;
    tb_size_t               more = 0;
    tb_option_item_t const* item = impl->opts;
    tb_option_item_t const* last = tb_null;
    for (i = 0; i < argc; i++)
    {
        // the argument
        tb_char_t* p = argv[i];
        tb_char_t* e = p + tb_strlen(p);
        tb_assert_and_check_return_val(p && p < e, tb_false);

        // is long key?
        if (p + 2 < e && p[0] == '-' && p[1] == '-' && tb_isalpha(p[2]))
        {
            // the key
            tb_char_t key[512] = {0};
            {
                tb_char_t* k = key;
                tb_char_t* e = key + 511;
                for (p += 2; *p && *p != '=' && k < e; p++, k++) *k = *p; 
            }

            // the val
            tb_char_t* val = (*p == '=')? (p + 1) : tb_null;

            // trace
            tb_trace_d("[lname]: %s => %s", key, val);

            // find the item
            tb_option_item_t const* find = tb_option_item_find(impl->opts, key, '\0');
            if (find)
            {
                // check key & val
                if (!val == !(find->mode == TB_OPTION_MODE_KEY_VAL))
                {
                    // has value?
                    tb_object_ref_t object = tb_null;
                    if (val)
                    {
                        // init the value object
                        switch (find->type)
                        {
                        case TB_OPTION_TYPE_CSTR:
                            object = tb_oc_string_init_from_cstr(val);
                            break;
                        case TB_OPTION_TYPE_INTEGER:
                            tb_assert_and_check_return_val(tb_option_is_integer(val), tb_false);
                            object = tb_oc_number_init_from_sint64(tb_atoll(val));
                            break;
                        case TB_OPTION_TYPE_BOOL:
                            tb_assert_and_check_return_val(tb_option_is_bool(val), tb_false);
                            object = tb_oc_boolean_init(!tb_stricmp(val, "y")? tb_true : tb_false);
                            break;
#ifdef TB_CONFIG_TYPE_HAVE_FLOAT
                        case TB_OPTION_TYPE_FLOAT:
                            tb_assert_and_check_return_val(tb_option_is_float(val), tb_false);
                            object = tb_oc_number_init_from_double(tb_atof(val));
                            break;
#endif
                        default:
                            tb_assert_and_check_return_val(0, tb_false);
                            break;
                        }
                    }
                    else
                    {
                        // check
                        tb_assert_and_check_return_val(find->type == TB_OPTION_TYPE_BOOL, tb_false);

                        // key => true
                        object = tb_oc_boolean_init(tb_true);
                    }

                    // add the value object
                    if (object)
                    {
                        tb_oc_dictionary_insert(impl->list, key, object);
                        if (tb_isalpha(find->sname)) 
                        {
                            tb_char_t ch[2] = {0};
                            ch[0] = find->sname;
                            tb_oc_dictionary_insert(impl->list, ch, object);
                            tb_object_retain(object);
                        }
                    }
                }
                else if (val)
                {
                    // print
                    tb_trace_e("%s: unrecognized option value '--%s=%s'", impl->name, key, val);

                    // next
                    continue ;
                }
                else
                {
                    // print
                    tb_trace_e("%s: no option value '--%s='", impl->name, key);

                    // next
                    continue ;
                }
            }
            else
            {
                // print
                tb_trace_e("%s: unrecognized option '--%s'", impl->name, key);

                // next
                continue ;
            }
        }
        // is short key?
        else if (p + 1 < e && p[0] == '-' && tb_isalpha(p[1]))
        {
            // the key
            tb_char_t key[512] = {0};
            {
                tb_char_t* k = key;
                tb_char_t* e = key + 511;
                for (p += 1; *p && *p != '=' && k < e; p++, k++) *k = *p; 
            }

            // the val
            tb_char_t const* val = (*p == '=')? (p + 1) : tb_null;

            // trace
            tb_trace_d("[sname]: %s => %s", key, val);

            // is short name?
            if (tb_strlen(key) != 1)
            {
                // print
                tb_trace_e("%s: unrecognized option '-%s'", impl->name, key);

                // next
                continue ;
            }

            // find the item
            tb_option_item_t const* find = tb_option_item_find(impl->opts, tb_null, key[0]);
            if (find)
            {
                // check key & val
                if (!val == !(find->mode == TB_OPTION_MODE_KEY_VAL))
                {
                    // has value?
                    tb_object_ref_t object = tb_null;
                    if (val)
                    {
                        // add value
                        switch (find->type)
                        {
                        case TB_OPTION_TYPE_CSTR:
                            object = tb_oc_string_init_from_cstr(val);
                            break;
                        case TB_OPTION_TYPE_INTEGER:
                            tb_assert_and_check_return_val(tb_option_is_integer(val), tb_false);
                            object = tb_oc_number_init_from_sint64(tb_atoll(val));
                            break;
                        case TB_OPTION_TYPE_BOOL:
                            tb_assert_and_check_return_val(tb_option_is_bool(val), tb_false);
                            object = tb_oc_boolean_init(!tb_stricmp(val, "y")? tb_true : tb_false);
                            break;
#ifdef TB_CONFIG_TYPE_HAVE_FLOAT
                        case TB_OPTION_TYPE_FLOAT:
                            tb_assert_and_check_return_val(tb_option_is_float(val), tb_false);
                            object = tb_oc_number_init_from_double(tb_atof(val));
                            break;
#endif
                        default:
                            tb_assert_and_check_return_val(0, tb_false);
                            break;
                        }
                    }
                    else
                    {
                        // check
                        tb_assert_and_check_return_val(find->type == TB_OPTION_TYPE_BOOL, tb_false);

                        // key => true
                        object = tb_oc_boolean_init(tb_true);
                    }

                    // add the value object 
                    if (object)
                    {
                        tb_oc_dictionary_insert(impl->list, key, object);
                        if (find->lname)
                        {
                            tb_oc_dictionary_insert(impl->list, find->lname, object);
                            tb_object_retain(object);
                        }
                    }
                }
                else if (val)
                {
                    // print
                    tb_trace_e("%s: unrecognized option value '--%s=%s'", impl->name, key, val);

                    // next
                    continue ;
                }
                else
                {
                    // print
                    tb_trace_e("%s: no option value '--%s='", impl->name, key);

                    // next
                    continue ;
                }
            }
            else
            {
                // print
                tb_trace_e("%s: unrecognized option '-%s'", impl->name, key);

                // next
                continue ;
            }
        }
        // is value?
        else
        {
            // trace
            tb_trace_d("[val]: %s", p);

            // find the value item 
            while (item && item->mode != TB_OPTION_MODE_VAL && item->mode != TB_OPTION_MODE_END && item->mode != TB_OPTION_MODE_MORE)
                item++;

            // has value item?
            if (item->mode == TB_OPTION_MODE_VAL)
            {
                // check
                tb_assert_and_check_return_val(item->lname, tb_false);

                // add value
                switch (item->type)
                {
                case TB_OPTION_TYPE_CSTR:
                    tb_oc_dictionary_insert(impl->list, item->lname, tb_oc_string_init_from_cstr(p));
                    break;
                case TB_OPTION_TYPE_INTEGER:
                    tb_assert_and_check_return_val(tb_option_is_integer(p), tb_false);
                    tb_oc_dictionary_insert(impl->list, item->lname, tb_oc_number_init_from_sint64(tb_atoll(p)));
                    break;
                case TB_OPTION_TYPE_BOOL:
                    tb_assert_and_check_return_val(tb_option_is_bool(p), tb_false);
                    tb_oc_dictionary_insert(impl->list, item->lname, tb_oc_boolean_init(!tb_stricmp(p, "y")? tb_true : tb_false));
                    break;
#ifdef TB_CONFIG_TYPE_HAVE_FLOAT
                case TB_OPTION_TYPE_FLOAT:
                    tb_assert_and_check_return_val(tb_option_is_float(p), tb_false);
                    tb_oc_dictionary_insert(impl->list, item->lname, tb_oc_number_init_from_double(tb_atof(p)));
                    break;
#endif
                default:
                    tb_assert_and_check_return_val(0, tb_false);
                    break;
                }

                // save last
                last = item;

                // next item
                item++;
            }
            // has more item?
            else if (item->mode == TB_OPTION_MODE_MORE && last)
            {
                // the more name
                tb_char_t name[64] = {0};
                tb_snprintf(name, 63, "more%lu", more);

                // add value
                switch (last->type)
                {
                case TB_OPTION_TYPE_CSTR:
                    tb_oc_dictionary_insert(impl->list, name, tb_oc_string_init_from_cstr(p));
                    break;
                case TB_OPTION_TYPE_INTEGER:
                    tb_assert_and_check_return_val(tb_option_is_integer(p), tb_false);
                    tb_oc_dictionary_insert(impl->list, name, tb_oc_number_init_from_sint64(tb_atoll(p)));
                    break;
                case TB_OPTION_TYPE_BOOL:
                    tb_assert_and_check_return_val(tb_option_is_bool(p), tb_false);
                    tb_oc_dictionary_insert(impl->list, name, tb_oc_boolean_init(!tb_stricmp(p, "y")? tb_true : tb_false));
                    break;
#ifdef TB_CONFIG_TYPE_HAVE_FLOAT
                case TB_OPTION_TYPE_FLOAT:
                    tb_assert_and_check_return_val(tb_option_is_float(p), tb_false);
                    tb_oc_dictionary_insert(impl->list, name, tb_oc_number_init_from_double(tb_atof(p)));
                    break;
#endif
                default:
                    tb_assert_and_check_return_val(0, tb_false);
                    break;
                }

                // next more
                more++;
            }
        }
    }

    // ok
    return tb_true;//tb_option_check(impl);
}
Esempio n. 3
0
/* //////////////////////////////////////////////////////////////////////////////////////
 * main
 */ 
tb_int_t tb_demo_stream_async_stream_main(tb_int_t argc, tb_char_t** argv)
{
    // done
    tb_demo_context_t   context = {0};
    do
    {
        // init option
        context.option = tb_option_init("astream", "the astream demo", g_options);
        tb_assert_and_check_break(context.option);
    
        // done option
        if (tb_option_done(context.option, argc - 1, &argv[1]))
        {
            // debug and verbose
            context.debug   = tb_option_find(context.option, "debug")? tb_false : tb_true;
            context.verbose = tb_option_find(context.option, "no-verbose")? tb_false : tb_true;
        
            // done url
            if (tb_option_find(context.option, "url")) 
            {
                // init event
                context.event = tb_event_init();
                tb_assert_and_check_break(context.event);

                // init istream
                context.istream = tb_async_stream_init_from_url(tb_null, tb_option_item_cstr(context.option, "url"));
                tb_assert_and_check_break(context.istream);

                // ctrl http
                if (tb_async_stream_type(context.istream) == TB_STREAM_TYPE_HTTP) 
                {
                    // enable gzip?
                    if (tb_option_find(context.option, "gzip"))
                    {
                        // auto unzip
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_AUTO_UNZIP, 1)) break;

                        // need gzip
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Accept-Encoding", "gzip,deflate")) break;
                    }

                    // enable debug?
                    if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD_FUNC, context.debug? tb_demo_istream_head_func : tb_null)) break;

                    // custem header?
                    if (tb_option_find(context.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(context.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 (context.debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val));
                                        if (!tb_async_stream_ctrl(context.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 (context.debug) tb_printf("header: %s: %s\n", tb_string_cstr(&key), tb_string_cstr(&val));
                            if (!tb_async_stream_ctrl(context.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(context.option, "keep-alive"))
                    {
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_HEAD, "Connection", "keep-alive")) break;
                    }

                    // post-data?
                    if (tb_option_find(context.option, "post-data"))
                    {
                        tb_char_t const*    post_data = tb_option_item_cstr(context.option, "post-data");
                        tb_hize_t           post_size = tb_strlen(post_data);
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break;
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_DATA, post_data, post_size)) break;
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break;
                        if (context.debug) tb_printf("post: %llu\n", post_size);
                    }
                    // post-file?
                    else if (tb_option_find(context.option, "post-file"))
                    {
                        tb_char_t const* url = tb_option_item_cstr(context.option, "post-file");
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_METHOD, TB_HTTP_METHOD_POST)) break;
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_URL, url)) break;
                        if (!tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_POST_FUNC, tb_demo_http_post_func)) break;
                        if (context.debug) tb_printf("post: %s\n", url);
                    }
                }

                // set range
                if (tb_option_find(context.option, "range"))
                {
                    tb_char_t const* p = tb_option_item_cstr(context.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_async_stream_ctrl(context.istream, TB_STREAM_CTRL_HTTP_SET_RANGE, bof, eof)) break;
                    }
                }

                // set timeout
                if (tb_option_find(context.option, "timeout"))
                {
                    tb_size_t timeout = tb_option_item_uint32(context.option, "timeout");
                    tb_async_stream_ctrl(context.istream, TB_STREAM_CTRL_SET_TIMEOUT, &timeout);
                }

                // print verbose info
                if (context.verbose) tb_printf("open: %s: ..\n", tb_option_item_cstr(context.option, "url"));

                // open istream
                if (!tb_async_stream_open(context.istream, tb_demo_istream_open_func, &context)) 
                {
                    // print verbose info
                    if (context.verbose) tb_printf("open: failed\n");
                    break;
                }

                // wait it
                tb_event_wait(context.event, -1);
            }
            else tb_option_help(context.option);
        }
        else tb_option_help(context.option);

    } while (0);

    // exit transfer
    if (context.transfer) tb_async_transfer_exit(context.transfer);
    context.transfer = tb_null;

    // exit istream
    if (context.istream) tb_async_stream_exit(context.istream);
    context.istream = tb_null;

    // exit ostream
    if (context.ostream) tb_async_stream_exit(context.ostream);
    context.ostream = tb_null;

    // exit option
    if (context.option) tb_option_exit(context.option);
    context.option = tb_null;

    // exit event
    if (context.event) tb_event_exit(context.event);
    context.event = tb_null;

    return 0;
}
Esempio n. 4
0
/* //////////////////////////////////////////////////////////////////////////////////////
 * 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;
}