int main(int argc, char* argv[]) { CURL* curl = NULL; CURLcode res; int status = 0; char error[CURL_ERROR_SIZE]; char fname[256]; char line[256]; char cookie[2048]; char query[2048]; char* buf = NULL; char* ptr = NULL; char* tmp = NULL; char* id; FILE* fp = NULL; MEMFILE* mf; // mem file MEMFILE* hf; // mem file for header // usage if (argc != 2) { fputs("usage: nicodown [video_id]\n", stderr); goto leave; } if (!(ptr = getenv("HOME"))) { #ifdef _WIN32 if (!(ptr = getenv("USERPROFILE"))) { fprintf(stderr, "failed to get HOME directory\n"); goto leave; } #else fprintf(stderr, "failed to get HOME directory\n"); goto leave; #endif } sprintf(fname, "%s/.nicodownrc", ptr); fp = fopen(fname, "r"); if (!fp || !fgets(line, sizeof(line), fp)) { if (fp) fclose(fp); fprintf(stderr, "failed to read ~/.nicodownrc\n"); goto leave; } fclose(fp); tmp = strpbrk(line, "\r\n"); if (tmp) *tmp = 0; ptr = strchr(line, ':'); if (!ptr) { fprintf(stderr, "failed to parse ~/.nicodownrc\n"); goto leave; } *ptr++ = 0; id = argv[1]; if (!strncmp(id, WATCH_URL_BASE, strlen(WATCH_URL_BASE))) { id += strlen(WATCH_URL_BASE); } sprintf(query, "mail_tel=%s&password=%s&next_url=/watch/%s", line, ptr, id); // default filename sprintf(fname, "%s.flv", id); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); // login mf = memfopen(); hf = memfopen(); curl_easy_setopt(curl, CURLOPT_URL, "https://secure.nicovideo.jp/secure/login?site=niconico"); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query); curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf); curl_easy_setopt(curl, CURLOPT_HEADERDATA, hf); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, memfwrite); res = curl_easy_perform(curl); if (res != CURLE_OK) { fputs(error, stderr); memfclose(mf); memfclose(hf); goto leave; } // parse cookie memset(cookie, 0, sizeof(cookie)); buf = memfstrdup(hf); tmp = buf; while (tmp = strstr(tmp, "Set-Cookie: ")) { ptr = tmp; tmp = strpbrk(ptr, "\r\n;"); if (tmp) *tmp = 0; strcpy(cookie, ptr + 12); if (strncmp(cookie, "user_session=user", 17) == 0) { curl_easy_setopt(curl, CURLOPT_COOKIE, cookie); break; } tmp++; } free(buf); memfclose(mf); memfclose(hf); mf = memfopen(); hf = memfopen(); curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf); curl_easy_setopt(curl, CURLOPT_HEADERDATA, hf); // redirect sprintf(query, "http://www.nicovideo.jp/watch/%s", id); curl_easy_setopt(curl, CURLOPT_URL, query); curl_easy_setopt(curl, CURLOPT_POST, 0); res = curl_easy_perform(curl); if (res != CURLE_OK) { fputs(error, stderr); memfclose(mf); memfclose(hf); goto leave; } // parse cookie buf = memfstrdup(hf); ptr = NULL; tmp = buf; while (tmp = strstr(tmp, "Set-Cookie: ")) { ptr = tmp + 12; tmp = strpbrk(ptr, "\r\n;"); if (tmp) *tmp = 0; strcat(cookie, ";"); strcat(cookie, ptr); tmp++; } curl_easy_setopt(curl, CURLOPT_COOKIE, cookie); free(buf); // parse response body buf = memfstrdup(mf); if (buf && strstr(buf, "id=\"login_bar\"")) { free(buf); fprintf(stderr, "failed to login\n"); memfclose(mf); memfclose(hf); goto leave; } free(buf); memfclose(hf); memfclose(mf); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); // get video query, and get filename sprintf(query, "http://www.nicovideo.jp/api/getthumbinfo?v=%s", id); mf = memfopen(); curl_easy_setopt(curl, CURLOPT_URL, query); curl_easy_setopt(curl, CURLOPT_POST, 0); curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); res = curl_easy_perform(curl); if (res != CURLE_OK) { fputs(error, stderr); memfclose(mf); goto leave; } #ifdef USE_LIBXML // parse title node { xmlDocPtr doc = NULL; xmlXPathContextPtr xpathctx; xmlXPathObjectPtr xpathobj; doc = xmlParseMemory(mf->data, mf->size); xpathctx = doc ? xmlXPathNewContext(doc) : NULL; xpathobj = xpathctx ? xmlXPathEvalExpression((xmlChar*) "//title", xpathctx) : NULL; if (xpathobj) { int n; xmlNodeSetPtr nodes = xpathobj->nodesetval; for(n = 0; nodes && n < xmlXPathNodeSetGetLength(nodes); n++) { xmlNodePtr node = nodes->nodeTab[n]; if(node->type != XML_ATTRIBUTE_NODE && node->type != XML_ELEMENT_NODE && node->type != XML_CDATA_SECTION_NODE) continue; if (node->type == XML_CDATA_SECTION_NODE) sprintf(fname, "%s.flv", (char*) node->content); else if (node->children) sprintf(fname, "%s.flv", (char*) node->children->content); break; } } if (xpathobj ) xmlXPathFreeObject(xpathobj); if (xpathctx) xmlXPathFreeContext(xpathctx); if (doc) xmlFreeDoc(doc); } #else buf = memfstrdup(mf); ptr = buf ? strstr(buf, "<title>") : NULL; if (ptr) { ptr += 7; tmp = strstr(ptr, "</title>"); if (*tmp) { *tmp = 0; sprintf(fname, "%s.flv", ptr); } } if (buf) free(buf); #endif memfclose(mf); #ifdef _WIN32 { UINT codePage; size_t wcssize; wchar_t* wcsstr; size_t mbssize; char* mbsstr; codePage = CP_UTF8; wcssize = MultiByteToWideChar(codePage, 0, fname, -1, NULL, 0); wcsstr = (wchar_t*) malloc(sizeof(wchar_t) * (wcssize + 1)); wcssize = MultiByteToWideChar(codePage, 0, fname, -1, wcsstr, wcssize + 1); wcsstr[wcssize] = 0; codePage = GetACP(); mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR) wcsstr,-1,NULL,0,NULL,NULL); mbsstr = (char*) malloc(mbssize+1); mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR) wcsstr, -1, mbsstr, mbssize, NULL, NULL); mbsstr[mbssize] = 0; strcpy(fname, mbsstr); free(mbsstr); free(wcsstr); } #endif // get video query sprintf(query, "http://flapi.nicovideo.jp/api/getflv?v=%s", id); mf = memfopen(); curl_easy_setopt(curl, CURLOPT_URL, query); curl_easy_setopt(curl, CURLOPT_POST, 0); curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite); res = curl_easy_perform(curl); if (res != CURLE_OK) { fputs(error, stderr); memfclose(mf); goto leave; } buf = memfstrdup(mf); ptr = strstr(buf, "url="); if (!ptr) { if (buf) free(buf); fprintf(stderr, "failed to get video info\n"); memfclose(mf); goto leave; } tmp = strstr(ptr, "&"); if (tmp) *tmp = 0; tmp = ptr; while(*tmp) { if (IS_QUOTED(tmp)) { unsigned int num = 0; sscanf(tmp+1, "%02x", &num); *tmp = (char)num; strcpy(tmp + 1, tmp + 3); } tmp++; } strcpy(query, ptr + 4); printf("URL: %s\n", query); free(buf); memfclose(mf); // sanitize filename ptr = fname; while (*ptr) { if (strchr("\\/|:<>\"?*", *ptr)) *ptr = '_'; ptr++; } // download video fp = fopen(fname, "wb"); if (!fp) { fprintf(stderr, "failed to open file: %s\n", fname); goto leave; } curl_easy_setopt(curl, CURLOPT_URL, query); curl_easy_setopt(curl, CURLOPT_POST, 0); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void*)fname); curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, BUFSIZ); /* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); */ res = curl_easy_perform(curl); fclose(fp); if (res != CURLE_OK) { fputs(error, stderr); goto leave; } leave: if (curl) curl_easy_cleanup(curl); printf("\n"); return 0; }
// // Specialize_Action_Throws: C // // Create a new ACTION! value that uses the same implementation as another, // but just takes fewer arguments or refinements. It does this by storing a // heap-based "exemplar" FRAME! in the specialized action; this stores the // values to preload in the stack frame cells when it is invoked. // // The caller may provide information on the order in which refinements are // to be specialized, using the data stack. These refinements should be // pushed in the *reverse* order of their invocation, so append/dup/part // has /DUP at DS_TOP, and /PART under it. List stops at lowest_ordered_dsp. // bool Specialize_Action_Throws( REBVAL *out, REBVAL *specializee, REBSTR *opt_specializee_name, REBVAL *opt_def, // !!! REVIEW: binding modified directly (not copied) REBDSP lowest_ordered_dsp ){ assert(out != specializee); struct Reb_Binder binder; if (opt_def) INIT_BINDER(&binder); REBACT *unspecialized = VAL_ACTION(specializee); // This produces a context where partially specialized refinement slots // will be on the stack (including any we are adding "virtually", from // the current DSP down to the lowest_ordered_dsp). // REBCTX *exemplar = Make_Context_For_Action_Push_Partials( specializee, lowest_ordered_dsp, opt_def ? &binder : nullptr, CELL_MASK_NON_STACK ); Manage_Array(CTX_VARLIST(exemplar)); // destined to be managed, guarded if (opt_def) { // code that fills the frame...fully or partially // // Bind all the SET-WORD! in the body that match params in the frame // into the frame. This means `value: value` can very likely have // `value:` bound for assignments into the frame while `value` refers // to whatever value was in the context the specialization is running // in, but this is likely the more useful behavior. // // !!! This binds the actual arg data, not a copy of it--following // OBJECT!'s lead. However, ordinary functions make a copy of the // body they are passed before rebinding. Rethink. // See Bind_Values_Core() for explanations of how the binding works. Bind_Values_Inner_Loop( &binder, VAL_ARRAY_AT(opt_def), exemplar, FLAGIT_KIND(REB_SET_WORD), // types to bind (just set-word!) 0, // types to "add midstream" to binding as we go (nothing) BIND_DEEP ); // !!! Only one binder can be in effect, and we're calling arbitrary // code. Must clean up now vs. in loop we do at the end. :-( // RELVAL *key = CTX_KEYS_HEAD(exemplar); REBVAL *var = CTX_VARS_HEAD(exemplar); for (; NOT_END(key); ++key, ++var) { if (Is_Param_Unbindable(key)) continue; // !!! is this flag still relevant? if (Is_Param_Hidden(key)) { assert(GET_CELL_FLAG(var, ARG_MARKED_CHECKED)); continue; } if (GET_CELL_FLAG(var, ARG_MARKED_CHECKED)) continue; // may be refinement from stack, now specialized out Remove_Binder_Index(&binder, VAL_KEY_CANON(key)); } SHUTDOWN_BINDER(&binder); // Run block and ignore result (unless it is thrown) // PUSH_GC_GUARD(exemplar); bool threw = Do_Any_Array_At_Throws(out, opt_def, SPECIFIED); DROP_GC_GUARD(exemplar); if (threw) { DS_DROP_TO(lowest_ordered_dsp); return true; } } REBVAL *rootkey = CTX_ROOTKEY(exemplar); // Build up the paramlist for the specialized function on the stack. // The same walk used for that is used to link and process REB_X_PARTIAL // arguments for whether they become fully specialized or not. REBDSP dsp_paramlist = DSP; Move_Value(DS_PUSH(), ACT_ARCHETYPE(unspecialized)); REBVAL *param = rootkey + 1; REBVAL *arg = CTX_VARS_HEAD(exemplar); REBDSP ordered_dsp = lowest_ordered_dsp; for (; NOT_END(param); ++param, ++arg) { if (TYPE_CHECK(param, REB_TS_REFINEMENT)) { if (IS_NULLED(arg)) { // // A refinement that is nulled is a candidate for usage at the // callsite. Hence it must be pre-empted by our ordered // overrides. -but- the overrides only apply if their slot // wasn't filled by the user code. Yet these values we are // putting in disrupt that detection (!), so use another // flag (PUSH_PARTIAL) to reflect this state. // while (ordered_dsp != dsp_paramlist) { ++ordered_dsp; REBVAL *ordered = DS_AT(ordered_dsp); if (not IS_WORD_BOUND(ordered)) // specialize 'print/asdf fail (Error_Bad_Refine_Raw(ordered)); REBVAL *slot = CTX_VAR(exemplar, VAL_WORD_INDEX(ordered)); if ( IS_NULLED(slot) or GET_CELL_FLAG(slot, PUSH_PARTIAL) ){ // It's still partial, so set up the pre-empt. // Init_Any_Word_Bound( arg, REB_SYM_WORD, VAL_STORED_CANON(ordered), exemplar, VAL_WORD_INDEX(ordered) ); SET_CELL_FLAG(arg, PUSH_PARTIAL); goto unspecialized_arg; } // Otherwise the user filled it in, so skip to next... } goto unspecialized_arg; // ran out...no pre-empt needed } if (GET_CELL_FLAG(arg, ARG_MARKED_CHECKED)) { assert( IS_BLANK(arg) or ( IS_REFINEMENT(arg) and ( VAL_REFINEMENT_SPELLING(arg) == VAL_PARAM_SPELLING(param) ) ) ); } else Typecheck_Refinement_And_Canonize(param, arg); goto specialized_arg_no_typecheck; } switch (VAL_PARAM_CLASS(param)) { case REB_P_RETURN: case REB_P_LOCAL: assert(IS_NULLED(arg)); // no bindings, you can't set these goto unspecialized_arg; default: break; } // It's an argument, either a normal one or a refinement arg. if (not IS_NULLED(arg)) goto specialized_arg_with_check; unspecialized_arg: assert(NOT_CELL_FLAG(arg, ARG_MARKED_CHECKED)); assert( IS_NULLED(arg) or (IS_SYM_WORD(arg) and TYPE_CHECK(param, REB_TS_REFINEMENT)) ); Move_Value(DS_PUSH(), param); continue; specialized_arg_with_check: // !!! If argument was previously specialized, should have been type // checked already... don't type check again (?) // if (Is_Param_Variadic(param)) fail ("Cannot currently SPECIALIZE variadic arguments."); if (TYPE_CHECK(param, REB_TS_DEQUOTE_REQUOTE) and IS_QUOTED(arg)) { // // Have to leave the quotes on, but still want to type check. if (not TYPE_CHECK(param, CELL_KIND(VAL_UNESCAPED(arg)))) fail (arg); // !!! merge w/Error_Invalid_Arg() } else if (not TYPE_CHECK(param, VAL_TYPE(arg))) fail (arg); // !!! merge w/Error_Invalid_Arg() SET_CELL_FLAG(arg, ARG_MARKED_CHECKED); specialized_arg_no_typecheck: // Specialized-out arguments must still be in the parameter list, // for enumeration in the evaluator to line up with the frame values // of the underlying function. assert(GET_CELL_FLAG(arg, ARG_MARKED_CHECKED)); Move_Value(DS_PUSH(), param); TYPE_SET(DS_TOP, REB_TS_HIDDEN); continue; } REBARR *paramlist = Pop_Stack_Values_Core( dsp_paramlist, SERIES_MASK_PARAMLIST | (SER(unspecialized)->header.bits & PARAMLIST_MASK_INHERIT) ); Manage_Array(paramlist); RELVAL *rootparam = ARR_HEAD(paramlist); VAL_ACT_PARAMLIST_NODE(rootparam) = NOD(paramlist); // Everything should have balanced out for a valid specialization // while (ordered_dsp != DSP) { ++ordered_dsp; REBVAL *ordered = DS_AT(ordered_dsp); if (not IS_WORD_BOUND(ordered)) // specialize 'print/asdf fail (Error_Bad_Refine_Raw(ordered)); REBVAL *slot = CTX_VAR(exemplar, VAL_WORD_INDEX(ordered)); assert(not IS_NULLED(slot) and NOT_CELL_FLAG(slot, PUSH_PARTIAL)); UNUSED(slot); } DS_DROP_TO(lowest_ordered_dsp); // See %sysobj.r for `specialized-meta:` object template REBVAL *example = Get_System(SYS_STANDARD, STD_SPECIALIZED_META); REBCTX *meta = Copy_Context_Shallow_Managed(VAL_CONTEXT(example)); Init_Nulled(CTX_VAR(meta, STD_SPECIALIZED_META_DESCRIPTION)); // default Move_Value( CTX_VAR(meta, STD_SPECIALIZED_META_SPECIALIZEE), specializee ); if (not opt_specializee_name) Init_Nulled(CTX_VAR(meta, STD_SPECIALIZED_META_SPECIALIZEE_NAME)); else Init_Word( CTX_VAR(meta, STD_SPECIALIZED_META_SPECIALIZEE_NAME), opt_specializee_name ); MISC_META_NODE(paramlist) = NOD(meta); REBACT *specialized = Make_Action( paramlist, &Specializer_Dispatcher, ACT_UNDERLYING(unspecialized), // same underlying action as this exemplar, // also provide a context of specialization values 1 // details array capacity ); assert(CTX_KEYLIST(exemplar) == ACT_PARAMLIST(unspecialized)); assert( GET_ACTION_FLAG(specialized, IS_INVISIBLE) == GET_ACTION_FLAG(unspecialized, IS_INVISIBLE) ); // The "body" is the FRAME! value of the specialization. It takes on the // binding we want to use (which we can't put in the exemplar archetype, // that binding has to be UNBOUND). It also remembers the original // action in the phase, so Specializer_Dispatcher() knows what to call. // RELVAL *body = ARR_HEAD(ACT_DETAILS(specialized)); Move_Value(body, CTX_ARCHETYPE(exemplar)); INIT_BINDING(body, VAL_BINDING(specializee)); INIT_VAL_CONTEXT_PHASE(body, unspecialized); Init_Action_Unbound(out, specialized); return false; // code block did not throw }