/*JSON{ "type":"method", "class": "Function", "name" : "replaceWith", "description" : ["This replaces the function with the one in the argument - while keeping the old function's scope. This allows inner functions to be edited, and is used when edit() is called on an inner function."], "generate" : "jswrap_function_replaceWith", "params" : [ [ "newFunc", "JsVar", "The new function to replace this function with"] ] }*/ void jswrap_function_replaceWith(JsVar *oldFunc, JsVar *newFunc) { if (!jsvIsFunction(newFunc)) { jsWarn("First argument of replaceWith should be a function - ignoring"); return; } // Grab scope - the one thing we want to keep JsVar *scope = jsvFindChildFromString(oldFunc, JSPARSE_FUNCTION_SCOPE_NAME, false); // so now remove all existing entries jsvRemoveAllChildren(oldFunc); // now re-add scope jsvAddName(oldFunc, scope); jsvUnLock(scope); // now re-add other entries JsObjectIterator it; jsvObjectIteratorNew(&it, newFunc); while (jsvObjectIteratorHasElement(&it)) { JsVar *el = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); if (!jsvIsStringEqual(el, JSPARSE_FUNCTION_SCOPE_NAME)) { JsVar *copy = jsvCopy(el); if (copy) { jsvAddName(oldFunc, copy); jsvUnLock(copy); } } } jsvObjectIteratorFree(&it); }
/*JSON{ "type":"method", "class": "String", "name" : "split", "description" : "Return an array made by splitting this string up by the separator. eg. ```'1,2,3'.split(',')==[1,2,3]```", "generate" : "jswrap_string_split", "params" : [ [ "separator", "JsVar", "The start character index"] ], "return" : ["JsVar", "Part of this string from start for len characters"] }*/ JsVar *jswrap_string_split(JsVar *parent, JsVar *split) { JsVar *array; int last, idx, arraylen=0; int splitlen = (int)jsvGetStringLength(split); int l = (int)jsvGetStringLength(parent) - splitlen; last = 0; array = jsvNewWithFlags(JSV_ARRAY); if (!array) return 0; // out of memory for (idx=0;idx<=l;idx++) { if (idx==l || jsvCompareString(parent, split, idx, 0, true)==0) { JsVar *part = jsvNewFromEmptyString(); if (!part) break; // out of memory JsVar *idxvar = jsvMakeIntoVariableName(jsvNewFromInteger(arraylen++), part); if (idxvar) { // could be out of memory if (idx==l) idx=l+splitlen; // if the last element, do to the end of the string jsvAppendStringVar(part, parent, last, idx-last); jsvAddName(array, idxvar); last = idx+splitlen; jsvUnLock(idxvar); } jsvUnLock(part); } } return array; }
/*JSON{ "type" : "method", "class" : "Function", "name" : "replaceWith", "generate" : "jswrap_function_replaceWith", "params" : [ ["newFunc","JsVar","The new function to replace this function with"] ] } This replaces the function with the one in the argument - while keeping the old function's scope. This allows inner functions to be edited, and is used when edit() is called on an inner function. */ void jswrap_function_replaceWith(JsVar *oldFunc, JsVar *newFunc) { if (!jsvIsFunction(newFunc)) { jsWarn("First argument of replaceWith should be a function - ignoring"); return; } // If old was native or vice versa... if (jsvIsNativeFunction(oldFunc) != jsvIsNativeFunction(newFunc)) { if (jsvIsNativeFunction(newFunc)) oldFunc->flags |= JSV_NATIVE; else oldFunc->flags &= ~JSV_NATIVE; } // If old fn started with 'return' or vice versa... if (jsvIsFunctionReturn(oldFunc) != jsvIsFunctionReturn(newFunc)) { if (jsvIsFunctionReturn(newFunc)) oldFunc->flags = (oldFunc->flags&~JSV_VARTYPEMASK) |JSV_FUNCTION_RETURN; else oldFunc->flags = (oldFunc->flags&~JSV_VARTYPEMASK) |JSV_FUNCTION; } // Grab scope - the one thing we want to keep JsVar *scope = jsvFindChildFromString(oldFunc, JSPARSE_FUNCTION_SCOPE_NAME, false); // so now remove all existing entries jsvRemoveAllChildren(oldFunc); // now re-add scope if (scope) jsvAddName(oldFunc, scope); jsvUnLock(scope); // now re-add other entries JsvObjectIterator it; jsvObjectIteratorNew(&it, newFunc); while (jsvObjectIteratorHasValue(&it)) { JsVar *el = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); if (!jsvIsStringEqual(el, JSPARSE_FUNCTION_SCOPE_NAME)) { JsVar *copy = jsvCopy(el); if (copy) { jsvAddName(oldFunc, copy); jsvUnLock(copy); } } jsvUnLock(el); } jsvObjectIteratorFree(&it); }
JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) { const char *name = isMap ? "map":"forEach"; if (!jsvIsIterable(parent)) { jsError("Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsError("Array.%s's first argument should be a function", name); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsError("Array.%s's second argument should be undefined, or an object", name); return 0; } JsVar *array = 0; if (isMap) array = jsvNewWithFlags(JSV_ARRAY); if (array || !isMap) { JsvIterator it; jsvIteratorNew(&it, parent); while (jsvIteratorHasElement(&it)) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *mapped; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { if (isMap) { JsVar *name = jsvNewFromInteger(idxValue); if (name) { // out of memory? jsvMakeIntoVariableName(name, mapped); jsvAddName(array, name); jsvUnLock(name); } } jsvUnLock(mapped); } } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); } return array; }
JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) { if (!jsvIsFunction(funcVar)) { jsError("Array.map's first argument should be a function"); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsError("Arraymap's second argument should be undefined, or an object"); return 0; } JsVar *array = 0; if (isMap) array = jsvNewWithFlags(JSV_ARRAY); if (array || !isMap) { JsVarRef childRef = parent->firstChild; while (childRef) { JsVar *child = jsvLock(childRef); if (jsvIsInt(child)) { JsVar *args[3], *mapped; args[0] = jsvLock(child->firstChild); // child is a variable name, create a new variable for the index args[1] = jsvNewFromInteger(jsvGetInteger(child)); args[2] = parent; mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLock(args[0]); jsvUnLock(args[1]); if (mapped) { if (isMap) { JsVar *name = jsvCopyNameOnly(child, false/*linkChildren*/, true/*keepAsName*/); if (name) { // out of memory? name->firstChild = jsvGetRef(jsvRef(mapped)); jsvAddName(array, name); jsvUnLock(name); } } jsvUnLock(mapped); } } childRef = child->nextSibling; jsvUnLock(child); } } return array; }
/*JSON{ "type":"constructor", "class": "Array", "name": "Array", "description" : "Create an Array. Either give it one integer argument (>=0) which is the length of the array, or any number of arguments ", "generate" : "jswrap_array_constructor", "params" : [ [ "args", "JsVarArray", "The length of the array OR any number of items to add to the array" ] ], "return" : [ "JsVar", "An Array" ] }*/ JsVar *jswrap_array_constructor(JsVar *args) { assert(args); if (jsvGetArrayLength(args)==1) { JsVar *firstArg = jsvSkipNameAndUnLock(jsvArrayGetLast(args)); // also the first! if (jsvIsInt(firstArg) && jsvGetInteger(firstArg)>=0) { JsVarInt count = jsvGetInteger(firstArg); // we cheat - no need to fill the array - just the last element if (count>0) { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; // out of memory JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(count-1), 0); if (idx) { // could be out of memory jsvAddName(arr, idx); jsvUnLock(idx); } jsvUnLock(firstArg); return arr; } } jsvUnLock(firstArg); } // Otherwise, we just return the array! return jsvLockAgain(args); }
/*JSON{ "type" : "method", "class" : "Function", "name" : "bind", "generate" : "jswrap_function_bind", "params" : [ ["this","JsVar","The value to use as the 'this' argument when executing the function"], ["params","JsVarArray","Optional Default parameters that are prepended to the call"] ], "return" : ["JsVar","The 'bound' function"] } This executes the function with the supplied 'this' argument and parameters */ JsVar *jswrap_function_bind(JsVar *parent, JsVar *thisArg, JsVar *argsArray) { if (!jsvIsFunction(parent)) { jsExceptionHere(JSET_TYPEERROR, "Function.bind expects to be called on function, got %t", parent); return 0; } JsVar *fn; if (jsvIsNativeFunction(parent)) fn = jsvNewNativeFunction(parent->varData.native.ptr, parent->varData.native.argTypes); else fn = jsvNewWithFlags(jsvIsFunctionReturn(parent) ? JSV_FUNCTION_RETURN : JSV_FUNCTION); if (!fn) return 0; // Old function info JsvObjectIterator fnIt; jsvObjectIteratorNew(&fnIt, parent); // add previously bound arguments while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *defaultValue = jsvObjectIteratorGetValue(&fnIt); bool wasBound = jsvIsFunctionParameter(param) && defaultValue; if (wasBound) { JsVar *newParam = jsvCopy(param); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } } jsvUnLock2(param, defaultValue); if (!wasBound) break; jsvObjectIteratorNext(&fnIt); } // add bound arguments JsvObjectIterator argIt; jsvObjectIteratorNew(&argIt, argsArray); while (jsvObjectIteratorHasValue(&argIt)) { JsVar *defaultValue = jsvObjectIteratorGetValue(&argIt); bool addedParam = false; while (!addedParam && jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); if (!jsvIsFunctionParameter(param)) { jsvUnLock(param); break; } JsVar *newParam = jsvCopyNameOnly(param, false, true); jsvSetValueOfName(newParam, defaultValue); jsvAddName(fn, newParam); addedParam = true; jsvUnLock2(param, newParam); jsvObjectIteratorNext(&fnIt); } if (!addedParam) { JsVar *paramName = jsvNewFromEmptyString(); if (paramName) { jsvMakeFunctionParameter(paramName); // force this to be called a function parameter jsvSetValueOfName(paramName, defaultValue); jsvAddName(fn, paramName); jsvUnLock(paramName); } } jsvUnLock(defaultValue); jsvObjectIteratorNext(&argIt); } jsvObjectIteratorFree(&argIt); // Copy the rest of the old function's info while (jsvObjectIteratorHasValue(&fnIt)) { JsVar *param = jsvObjectIteratorGetKey(&fnIt); JsVar *newParam = jsvCopyNameOnly(param, true, true); if (newParam) { // could be out of memory jsvAddName(fn, newParam); jsvUnLock(newParam); } jsvUnLock(param); jsvObjectIteratorNext(&fnIt); } jsvObjectIteratorFree(&fnIt); // Add 'this' jsvObjectSetChild(fn, JSPARSE_FUNCTION_THIS_NAME, thisArg); // no unlock needed return fn; }
JsVar *jswrap_json_parse_internal() { switch (lex->tk) { case LEX_R_TRUE: jslGetNextToken(lex); return jsvNewFromBool(true); case LEX_R_FALSE: jslGetNextToken(lex); return jsvNewFromBool(false); case LEX_R_NULL: jslGetNextToken(lex); return jsvNewWithFlags(JSV_NULL); case '-': { jslGetNextToken(lex); if (lex->tk!=LEX_INT && lex->tk!=LEX_FLOAT) return 0; JsVar *v = jswrap_json_parse_internal(lex); JsVar *zero = jsvNewFromInteger(0); JsVar *r = jsvMathsOp(zero, v, '-'); jsvUnLock2(v, zero); return r; } case LEX_INT: { long long v = stringToInt(jslGetTokenValueAsString(lex)); jslGetNextToken(lex); return jsvNewFromLongInteger(v); } case LEX_FLOAT: { JsVarFloat v = stringToFloat(jslGetTokenValueAsString(lex)); jslGetNextToken(lex); return jsvNewFromFloat(v); } case LEX_STR: { JsVar *a = jslGetTokenValueAsVar(lex); jslGetNextToken(lex); return a; } case '[': { JsVar *arr = jsvNewEmptyArray(); if (!arr) return 0; jslGetNextToken(lex); // [ while (lex->tk != ']' && !jspHasError()) { JsVar *value = jswrap_json_parse_internal(lex); if (!value || (lex->tk!=']' && !jslMatch(','))) { jsvUnLock2(value, arr); return 0; } jsvArrayPush(arr, value); jsvUnLock(value); } if (!jslMatch(']')) { jsvUnLock(arr); return 0; } return arr; } case '{': { JsVar *obj = jsvNewObject(); if (!obj) return 0; jslGetNextToken(lex); // { while (lex->tk == LEX_STR && !jspHasError()) { JsVar *key = jsvAsArrayIndexAndUnLock(jslGetTokenValueAsVar(lex)); jslGetNextToken(lex); JsVar *value = 0; if (!jslMatch(':') || !(value=jswrap_json_parse_internal(lex)) || (lex->tk!='}' && !jslMatch(','))) { jsvUnLock3(key, value, obj); return 0; } jsvAddName(obj, jsvMakeIntoVariableName(key, value)); jsvUnLock2(value, key); } if (!jslMatch('}')) { jsvUnLock(obj); return 0; } return obj; } default: { char buf[32]; jslTokenAsString(lex->tk, buf, 32); jsExceptionHere(JSET_SYNTAXERROR, "Expecting a valid value, got %s", buf); return 0; // undefined = error } } }
JsVar *jswrap_json_parse_internal(JsLex *lex) { switch (lex->tk) { case LEX_R_TRUE: jslGetNextToken(lex); return jsvNewFromBool(true); case LEX_R_FALSE: jslGetNextToken(lex); return jsvNewFromBool(false); case LEX_R_NULL: jslGetNextToken(lex); return jsvNewWithFlags(JSV_NULL); case '-': { jslGetNextToken(lex); if (lex->tk!=LEX_INT && lex->tk!=LEX_FLOAT) return 0; JsVar *v = jswrap_json_parse_internal(lex); JsVar *zero = jsvNewFromInteger(0); JsVar *r = jsvMathsOp(zero, v, '-'); jsvUnLock(v); jsvUnLock(zero); return r; } case LEX_INT: { long long v = stringToInt(jslGetTokenValueAsString(lex)); jslGetNextToken(lex); return jsvNewFromLongInteger(v); } case LEX_FLOAT: { JsVarFloat v = stringToFloat(jslGetTokenValueAsString(lex)); jslGetNextToken(lex); return jsvNewFromFloat(v); } case LEX_STR: { JsVar *a = jslGetTokenValueAsVar(lex); jslGetNextToken(lex); return a; } case '[': { JsVar *arr = jsvNewWithFlags(JSV_ARRAY); if (!arr) return 0; jslGetNextToken(lex); // [ while (lex->tk != ']') { JsVar *value = jswrap_json_parse_internal(lex); if (!value || (lex->tk!=']' && !jslMatch(lex, ','))) { jsvUnLock(value); jsvUnLock(arr); return 0; } jsvArrayPush(arr, value); jsvUnLock(value); } if (!jslMatch(lex, ']')) { jsvUnLock(arr); return 0; } return arr; } case '{': { JsVar *obj = jsvNewWithFlags(JSV_OBJECT); if (!obj) return 0; jslGetNextToken(lex); // { while (lex->tk == LEX_STR) { JsVar *key = jsvAsArrayIndexAndUnLock(jslGetTokenValueAsVar(lex)); jslGetNextToken(lex); JsVar *value = 0; if (!jslMatch(lex, ':') || !(value=jswrap_json_parse_internal(lex)) || (lex->tk!='}' && !jslMatch(lex, ','))) { jsvUnLock(key); jsvUnLock(value); jsvUnLock(obj); return 0; } jsvAddName(obj, jsvMakeIntoVariableName(key, value)); jsvUnLock(value); jsvUnLock(key); } if (!jslMatch(lex, '}')) { jsvUnLock(obj); return 0; } return obj; } default: return 0; // undefined = error } }
JsVar *_jswrap_array_iterate_with_callback(const char *name, JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool wantArray, bool isBoolCallback, bool expectedValue) { if (!jsvIsIterable(parent)) { jsExceptionHere(JSET_ERROR, "Array.%s can only be called on something iterable", name); return 0; } if (!jsvIsFunction(funcVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's first argument should be a function", name); return 0; } if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) { jsExceptionHere(JSET_ERROR, "Array.%s's second argument should be undefined, or an object", name); return 0; } JsVar *result = 0; if (wantArray) result = jsvNewEmptyArray(); bool isDone = false; if (result || !wantArray) { JsvIterator it; jsvIteratorNew(&it, parent); while (jsvIteratorHasElement(&it) && !isDone) { JsVar *index = jsvIteratorGetKey(&it); if (jsvIsInt(index)) { JsVarInt idxValue = jsvGetInteger(index); JsVar *args[3], *cb_result; args[0] = jsvIteratorGetValue(&it); args[1] = jsvNewFromInteger(idxValue); // child is a variable name, create a new variable for the index args[2] = parent; cb_result = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args); jsvUnLockMany(2,args); if (cb_result) { bool matched; if (isBoolCallback) matched = (jsvGetBool(cb_result) == expectedValue); if (wantArray) { if (isBoolCallback) { // filter if (matched) { jsvArrayPushAndUnLock(result, jsvIteratorGetValue(&it)); } } else { // map JsVar *name = jsvNewFromInteger(idxValue); if (name) { // out of memory? jsvMakeIntoVariableName(name, cb_result); jsvAddName(result, name); jsvUnLock(name); } } } else { // break the loop early if expecting a particular value and didn't get it if (isBoolCallback && !matched) isDone = true; } jsvUnLock(cb_result); } } jsvUnLock(index); jsvIteratorNext(&it); } jsvIteratorFree(&it); } /* boolean result depends on whether the loop terminated early for 'some' or completed for 'every' */ if (!wantArray && isBoolCallback) { result = jsvNewFromBool(isDone != expectedValue); } return result; }
/*JSON{ "type" : "staticmethod", "class" : "url", "name" : "parse", "generate" : "jswrap_url_parse", "params" : [ ["urlStr","JsVar","A URL to be parsed"], ["parseQuery","bool","Whether to parse the query string into an object not (default = false)"] ], "return" : ["JsVar","An object containing options for ```http.request``` or ```http.get```. Contains `method`, `host`, `path`, `pathname`, `search`, `port` and `query`"] } A utility function to split a URL into parts This is useful in web servers for instance when handling a request. For instance `url.parse("/a?b=c&d=e",true)` returns `{"method":"GET","host":"","path":"/a?b=c&d=e","pathname":"/a","search":"?b=c&d=e","port":80,"query":{"b":"c","d":"e"}}` */ JsVar *jswrap_url_parse(JsVar *url, bool parseQuery) { if (!jsvIsString(url)) return 0; JsVar *obj = jsvNewWithFlags(JSV_OBJECT); if (!obj) return 0; // out of memory // scan string to try and pick stuff out JsvStringIterator it; jsvStringIteratorNew(&it, url, 0); int slashes = 0; int colons = 0; int addrStart = -1; int portStart = -1; int pathStart = -1; int searchStart = -1; int charIdx = 0; int portNumber = 0; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '/') { slashes++; if (pathStart<0) pathStart = charIdx; if (colons==1 && slashes==2 && addrStart<0) { addrStart = charIdx; pathStart = -1; searchStart = -1; } } if (ch == ':') { colons++; if (addrStart>=0 && pathStart<0) portStart = charIdx; } if (portStart>=0 && charIdx>portStart && pathStart<0 && ch >= '0' && ch <= '9') { portNumber = portNumber*10 + (ch-'0'); } if (ch == '?' && pathStart>=0) { searchStart = charIdx; } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); // try and sort stuff out if (pathStart<0) pathStart = charIdx; if (pathStart<0) pathStart = charIdx; int addrEnd = (portStart>=0) ? portStart : pathStart; // pull out details if (addrStart>0) jsvObjectSetChildAndUnLock(obj, "protocol", jsvNewFromStringVar(url, 0, (size_t)addrStart-1)); jsvObjectSetChildAndUnLock(obj, "method", jsvNewFromString("GET")); jsvObjectSetChildAndUnLock(obj, "host", jsvNewFromStringVar(url, (size_t)(addrStart+1), (size_t)(addrEnd-(addrStart+1)))); JsVar *v; v = jsvNewFromStringVar(url, (size_t)pathStart, JSVAPPENDSTRINGVAR_MAXLENGTH); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "path", v); v = jsvNewFromStringVar(url, (size_t)pathStart, (size_t)((searchStart>=0)?(searchStart-pathStart):JSVAPPENDSTRINGVAR_MAXLENGTH)); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvObjectSetChildAndUnLock(obj, "pathname", v); jsvObjectSetChildAndUnLock(obj, "search", (searchStart>=0)?jsvNewFromStringVar(url, (size_t)searchStart, JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull()); jsvObjectSetChildAndUnLock(obj, "port", (portNumber<=0 || portNumber>65535) ? jsvNewWithFlags(JSV_NULL) : jsvNewFromInteger(portNumber)); JsVar *query = (searchStart>=0)?jsvNewFromStringVar(url, (size_t)(searchStart+1), JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull(); if (parseQuery && !jsvIsNull(query)) { JsVar *queryStr = query; jsvStringIteratorNew(&it, query, 0); query = jsvNewWithFlags(JSV_OBJECT); JsVar *key = jsvNewFromEmptyString(); JsVar *val = jsvNewFromEmptyString(); bool hadEquals = false; while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch=='&') { if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); jsvUnLock2(key, val); key = jsvNewFromEmptyString(); val = jsvNewFromEmptyString(); hadEquals = false; } } else if (!hadEquals && ch=='=') { hadEquals = true; } else { // decode percent escape chars if (ch=='%') { jsvStringIteratorNext(&it); ch = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); ch = (char)((chtod(ch)<<4) | chtod(jsvStringIteratorGetChar(&it))); } if (hadEquals) jsvAppendCharacter(val, ch); else jsvAppendCharacter(key, ch); } jsvStringIteratorNext(&it); charIdx++; } jsvStringIteratorFree(&it); jsvUnLock(queryStr); if (jsvGetStringLength(key)>0 || jsvGetStringLength(val)>0) { key = jsvAsArrayIndexAndUnLock(key); // make sure "0" gets made into 0 jsvMakeIntoVariableName(key, val); jsvAddName(query, key); } jsvUnLock2(key, val); } jsvObjectSetChildAndUnLock(obj, "query", query); return obj; }
// httpParseHeaders(&receiveData, reqVar, true) // server // httpParseHeaders(&receiveData, resVar, false) // client bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) { // find /r/n/r/n int newlineIdx = 0; int strIdx = 0; int headerEnd = -1; JsvStringIterator it; jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\r') { if (newlineIdx==0) newlineIdx=1; else if (newlineIdx==2) newlineIdx=3; } else if (ch == '\n') { if (newlineIdx==1) newlineIdx=2; else if (newlineIdx==3) { headerEnd = strIdx+1; break; } } else newlineIdx=0; jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); // skip if we have no header if (headerEnd<0) return false; // Now parse the header JsVar *vHeaders = jsvNewObject(); if (!vHeaders) return true; jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, "headers")); strIdx = 0; int firstSpace = -1; int secondSpace = -1; int firstEOL = -1; int lineNumber = 0; int lastLineStart = 0; int colonPos = 0; //jsiConsolePrintStringVar(receiveData); jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch==' ' || ch=='\r') { if (firstSpace<0) firstSpace = strIdx; else if (secondSpace<0) secondSpace = strIdx; } if (ch == ':' && colonPos<0) colonPos = strIdx; if (ch == '\r') { if (firstEOL<0) firstEOL=strIdx; if (lineNumber>0 && colonPos>lastLineStart && lastLineStart<strIdx) { JsVar *hVal = jsvNewFromEmptyString(); if (hVal) jsvAppendStringVar(hVal, *receiveData, (size_t)colonPos+2, (size_t)(strIdx-(colonPos+2))); JsVar *hKey = jsvNewFromEmptyString(); if (hKey) { jsvMakeIntoVariableName(hKey, hVal); jsvAppendStringVar(hKey, *receiveData, (size_t)lastLineStart, (size_t)(colonPos-lastLineStart)); jsvAddName(vHeaders, hKey); jsvUnLock(hKey); } jsvUnLock(hVal); } lineNumber++; colonPos=-1; } if (ch == '\r' || ch == '\n') { lastLineStart = strIdx+1; } jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); jsvUnLock(vHeaders); // try and pull out methods/etc if (isServer) { jsvObjectSetChildAndUnLock(objectForData, "method", jsvNewFromStringVar(*receiveData, 0, (size_t)firstSpace)); jsvObjectSetChildAndUnLock(objectForData, "url", jsvNewFromStringVar(*receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1)))); } else { jsvObjectSetChildAndUnLock(objectForData, "httpVersion", jsvNewFromStringVar(*receiveData, 5, (size_t)firstSpace-5)); jsvObjectSetChildAndUnLock(objectForData, "statusCode", jsvNewFromStringVar(*receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1)))); jsvObjectSetChildAndUnLock(objectForData, "statusMessage", jsvNewFromStringVar(*receiveData, (size_t)(secondSpace+1), (size_t)(firstEOL-(secondSpace+1)))); } // strip out the header JsVar *afterHeaders = jsvNewFromStringVar(*receiveData, (size_t)headerEnd, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(*receiveData); *receiveData = afterHeaders; return true; }
bool httpParseHeaders(JsVar **receiveData, JsVar *objectForData, bool isServer) { // find /r/n/r/n int newlineIdx = 0; int strIdx = 0; int headerEnd = -1; JsvStringIterator it; jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\r') { if (newlineIdx==0) newlineIdx=1; else if (newlineIdx==2) newlineIdx=3; } else if (ch == '\n') { if (newlineIdx==1) newlineIdx=2; else if (newlineIdx==3) { headerEnd = strIdx+1; } } else newlineIdx=0; jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); // skip if we have no header if (headerEnd<0) return false; // Now parse the header JsVar *vHeaders = jsvNewWithFlags(JSV_OBJECT); if (!vHeaders) return true; jsvUnLock(jsvAddNamedChild(objectForData, vHeaders, "headers")); strIdx = 0; int firstSpace = -1; int secondSpace = -1; int lineNumber = 0; int lastLineStart = 0; int colonPos = 0; //jsiConsolePrintStringVar(receiveData); jsvStringIteratorNew(&it, *receiveData, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); if (ch==' ' || ch=='\r') { if (firstSpace<0) firstSpace = strIdx; else if (secondSpace<0) secondSpace = strIdx; } if (ch == ':' && colonPos<0) colonPos = strIdx; if (ch == '\r') { if (lineNumber>0 && colonPos>lastLineStart && lastLineStart<strIdx) { JsVar *hVal = jsvNewFromEmptyString(); if (hVal) jsvAppendStringVar(hVal, *receiveData, colonPos+2, strIdx-(colonPos+2)); JsVar *hKey = jsvNewFromEmptyString(); if (hKey) { jsvMakeIntoVariableName(hKey, hVal); jsvAppendStringVar(hKey, *receiveData, lastLineStart, colonPos-lastLineStart); jsvAddName(vHeaders, hKey); jsvUnLock(hKey); } jsvUnLock(hVal); } lineNumber++; colonPos=-1; } if (ch == '\r' || ch == '\n') { lastLineStart = strIdx+1; } jsvStringIteratorNext(&it); strIdx++; } jsvStringIteratorFree(&it); jsvUnLock(vHeaders); // try and pull out methods/etc if (isServer) { JsVar *vMethod = jsvNewFromEmptyString(); if (vMethod) { jsvAppendStringVar(vMethod, *receiveData, 0, firstSpace); jsvUnLock(jsvAddNamedChild(objectForData, vMethod, "method")); jsvUnLock(vMethod); } JsVar *vUrl = jsvNewFromEmptyString(); if (vUrl) { jsvAppendStringVar(vUrl, *receiveData, firstSpace+1, secondSpace-(firstSpace+1)); jsvUnLock(jsvAddNamedChild(objectForData, vUrl, "url")); jsvUnLock(vUrl); } } // strip out the header JsVar *afterHeaders = jsvNewFromEmptyString(); if (!afterHeaders) return true; jsvAppendStringVar(afterHeaders, *receiveData, headerEnd, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(*receiveData); *receiveData = afterHeaders; return true; }