/*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", "ifndef" : "SAVE_ON_FLASH", "class" : "RegExp", "name" : "exec", "params" : [ ["str","JsVar","A string to match on"] ], "generate" : "jswrap_regexp_exec", "return" : ["JsVar","A result array, or null"] } Test this regex on a string - returns a result array on success, or `null` otherwise. `/Wo/.exec("Hello World")` will return: ``` [ "Wo", "index": 6, "input": "Hello World" ] ``` Or with groups `/W(o)rld/.exec("Hello World")` returns: ``` [ "World", "o", "index": 6, "input": "Hello World" ] ``` */ JsVar *jswrap_regexp_exec(JsVar *parent, JsVar *arg) { JsVar *str = jsvAsString(arg); JsVarInt lastIndex = jsvGetIntegerAndUnLock(jsvObjectGetChild(parent, "lastIndex", 0)); JsVar *regex = jsvObjectGetChild(parent, "source", 0); if (!jsvIsString(regex)) { jsvUnLock2(str,regex); return 0; } size_t regexLen = jsvGetStringLength(regex); char *regexPtr = (char *)alloca(regexLen+1); if (!regexPtr) { jsvUnLock2(str,regex); return 0; } jsvGetString(regex, regexPtr, regexLen+1); jsvUnLock(regex); JsVar *rmatch = match(regexPtr, str, (size_t)lastIndex, jswrap_regexp_hasFlag(parent,'i')); jsvUnLock(str); if (!rmatch) { rmatch = jsvNewWithFlags(JSV_NULL); lastIndex = 0; } else { // if it's global, set lastIndex if (jswrap_regexp_hasFlag(parent,'g')) { JsVar *matchStr = jsvGetArrayItem(rmatch,0); lastIndex = jsvGetIntegerAndUnLock(jsvObjectGetChild(rmatch, "index", 0)) + (JsVarInt)jsvGetStringLength(matchStr); jsvUnLock(matchStr); } else lastIndex = 0; } jsvObjectSetChildAndUnLock(parent, "lastIndex", jsvNewFromInteger(lastIndex)); return rmatch; }
/*JSON{ "type" : "method", "class" : "String", "name" : "split", "generate" : "jswrap_string_split", "params" : [ ["separator","JsVar","The start character index"] ], "return" : ["JsVar","Part of this string from start for len characters"] } Return an array made by splitting this string up by the separator. eg. ```'1,2,3'.split(',')==[1,2,3]``` */ JsVar *jswrap_string_split(JsVar *parent, JsVar *split) { JsVar *array = jsvNewWithFlags(JSV_ARRAY); if (!array) return 0; // out of memory if (jsvIsUndefined(split)) { jsvArrayPush(array, parent); return array; } split = jsvAsString(split, false); int idx, last = 0; int splitlen = jsvIsUndefined(split) ? 0 : (int)jsvGetStringLength(split); int l = (int)jsvGetStringLength(parent) + 1 - splitlen; for (idx=0;idx<=l;idx++) { if (splitlen==0 && idx==0) continue; // special case for where split string is "" if (idx==l || splitlen==0 || jsvCompareString(parent, split, (size_t)idx, 0, true)==0) { if (idx==l) { idx=l+splitlen; // if the last element, do to the end of the string if (splitlen==0) break; } JsVar *part = jsvNewFromStringVar(parent, (size_t)last, (size_t)(idx-last)); if (!part) break; // out of memory jsvArrayPush(array, part); jsvUnLock(part); last = idx+splitlen; } } jsvUnLock(split); return array; }
/** Push data into a stream. To be used by Espruino (not a user). * This either calls the on('data') handler if it exists, or it * puts the data in a buffer. This MAY CLAIM the string that is * passed in. * * This will return true on success, or false if the buffer is * full. Setting force=true will attempt to fill the buffer as * full as possible, and will raise an error flag if data is lost. */ bool jswrap_stream_pushData(JsVar *parent, JsVar *dataString, bool force) { assert(jsvIsObject(parent)); assert(jsvIsString(dataString)); bool ok = true; JsVar *callback = jsvFindChildFromString(parent, STREAM_CALLBACK_NAME, false); if (callback) { if (!jsiExecuteEventCallback(parent, callback, dataString, 0)) { jsError("Error processing Serial data handler - removing it."); jsErrorFlags |= JSERR_CALLBACK; jsvRemoveNamedChild(parent, STREAM_CALLBACK_NAME); } jsvUnLock(callback); } else { // No callback - try and add buffer JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); if (!jsvIsString(buf)) { // no buffer, just set this one up jsvObjectSetChild(parent, STREAM_BUFFER_NAME, dataString); } else { // append (if there is room!) size_t bufLen = jsvGetStringLength(buf); size_t dataLen = jsvGetStringLength(dataString); if (bufLen + dataLen > STREAM_MAX_BUFFER_SIZE) { if (force) jsErrorFlags |= JSERR_BUFFER_FULL; // jsWarn("String buffer overflowed maximum size (%d)", STREAM_MAX_BUFFER_SIZE); ok = false; } if ((ok || force) && (bufLen < STREAM_MAX_BUFFER_SIZE)) jsvAppendStringVar(buf, dataString, 0, STREAM_MAX_BUFFER_SIZE-bufLen); jsvUnLock(buf); } } return ok; }
/*JSON{ "type" : "method", "class" : "String", "name" : "startsWith", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_string_startsWith", "params" : [ ["searchString","JsVar","The string to search for"], ["position","int","The start character index (or 0 if not defined)"] ], "return" : ["bool","`true` if the given characters are found at the beginning of the string, otherwise, `false`."] } */ bool jswrap_string_startsWith(JsVar *parent, JsVar *search, int position) { if (!jsvIsString(parent)) return false; JsVar *searchStr = jsvAsString(search); bool match = false; if (position >= 0 && jsvGetStringLength(searchStr)+position <= jsvGetStringLength(parent)) match = jsvCompareString(parent, searchStr, position,0,true)==0; jsvUnLock(searchStr); return match; }
/*JSON{ "type" : "method", "class" : "String", "name" : "endsWith", "ifndef" : "SAVE_ON_FLASH", "generate" : "jswrap_string_endsWith", "params" : [ ["searchString","JsVar","The string to search for"], ["length","JsVar","The 'end' of the string - if left off the actual length of the string is used"] ], "return" : ["bool","`true` if the given characters are found at the end of the string, otherwise, `false`."] } */ bool jswrap_string_endsWith(JsVar *parent, JsVar *search, JsVar *length) { if (!jsvIsString(parent)) return false; int position = jsvIsNumeric(length) ? jsvGetInteger(length) : (int)jsvGetStringLength(parent); JsVar *searchStr = jsvAsString(search); position -= jsvGetStringLength(searchStr); bool match = false; if (position >= 0 && jsvGetStringLength(searchStr)+position <= jsvGetStringLength(parent)) match = jsvCompareString(parent, searchStr, position,0,true)==0; jsvUnLock(searchStr); return match; }
/*JSON{ "type" : "method", "class" : "String", "name" : "slice", "generate" : "jswrap_string_slice", "params" : [ ["start","int","The start character index, if negative it is from the end of the string"], ["end","JsVar","The end character index, if negative it is from the end of the string, and if omitted it is the end of the string"] ], "return" : ["JsVar","Part of this string from start for len characters"] }*/ JsVar *jswrap_string_slice(JsVar *parent, JsVarInt pStart, JsVar *vEnd) { JsVar *res; JsVarInt pEnd = jsvIsUndefined(vEnd) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vEnd); if (pStart<0) pStart += (JsVarInt)jsvGetStringLength(parent); if (pEnd<0) pEnd += (JsVarInt)jsvGetStringLength(parent); if (pStart<0) pStart = 0; if (pEnd<0) pEnd = 0; res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory if (pEnd>pStart) jsvAppendStringVar(res, parent, (size_t)pStart, (size_t)(pEnd-pStart)); return res; }
/*JSON{ "type":"method", "class": "String", "name" : "indexOf", "description" : "Return the index of substring in this string, or -1 if not found", "generate" : "jswrap_string_indexOf", "params" : [ [ "substring", "JsVar", "The string to search for"] ], "return" : ["int", "The index of the string, or -1 if not found"] }*/ JsVarInt jswrap_string_indexOf(JsVar *parent, JsVar *v) { // slow, but simple! v = jsvAsString(v, false); if (!v) return 0; // out of memory int idx = -1; int l = (int)jsvGetStringLength(parent) - (int)jsvGetStringLength(v); for (idx=0;idx<l;idx++) { if (jsvCompareString(parent, v, idx, 0, true)==0) { jsvUnLock(v); return idx; } } jsvUnLock(v); return -1; }
bool socketSendData(JsNetwork *net, JsVar *connection, int sckt, JsVar **sendData) { char buf[64]; int a=1; if (!jsvIsEmptyString(*sendData)) { size_t bufLen = httpStringGet(*sendData, buf, sizeof(buf)); a = net->send(net, sckt, buf, bufLen); // Now cut what we managed to send off the beginning of sendData if (a>0) { JsVar *newSendData = 0; if (a < (int)jsvGetStringLength(*sendData)) { // we didn't send all of it... cut out what we did send newSendData = jsvNewFromStringVar(*sendData, (size_t)a, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { // we sent all of it! Issue a drain event jsiQueueObjectCallbacks(connection, "#ondrain", &connection, 1); } jsvUnLock(*sendData); *sendData = newSendData; } } if (a<0) { // could just be busy which is ok jsError("Socket error %d while sending", a); return false; } return true; }
// returns 0 on success and a (negative) error number on failure int socketSendData(JsNetwork *net, JsVar *connection, int sckt, JsVar **sendData) { char *buf = alloca(net->chunkSize); // allocate on stack assert(!jsvIsEmptyString(*sendData)); size_t bufLen = httpStringGet(*sendData, buf, net->chunkSize); int num = netSend(net, sckt, buf, bufLen); if (num < 0) return num; // an error occurred // Now cut what we managed to send off the beginning of sendData if (num > 0) { JsVar *newSendData = 0; if (num < (int)jsvGetStringLength(*sendData)) { // we didn't send all of it... cut out what we did send newSendData = jsvNewFromStringVar(*sendData, (size_t)num, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { // we sent all of it! Issue a drain event, unless we want to close, then we shouldn't // callback for more data bool wantClose = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_CLOSE,0)); if (!wantClose) { jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); } newSendData = jsvNewFromEmptyString(); } jsvUnLock(*sendData); *sendData = newSendData; } return 0; }
/*JSON{ "type" : "method", "class" : "OneWire", "name" : "select", "generate" : "jswrap_onewire_select", "params" : [ ["rom","JsVar","The device to select (get this using `OneWire.search()`)"] ] } Select a ROM - always performs a reset first */ void jswrap_onewire_select(JsVar *parent, JsVar *rom) { Pin pin = onewire_getpin(parent); if (!jshIsPinValid(pin)) return; if (!jsvIsString(rom) || jsvGetStringLength(rom)!=16) { jsExceptionHere(JSET_TYPEERROR, "Invalid OneWire device address %q", rom); return; } // perform a reset OneWireReset(pin); // decode the address unsigned long long romdata = 0; JsvStringIterator it; jsvStringIteratorNew(&it, rom, 0); int i; for (i=0;i<8;i++) { char b[3]; b[0] = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); b[1] = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); b[2] = 0; romdata = romdata | (((unsigned long long)stringToIntWithRadix(b,16,NULL,NULL)) << (i*8)); } jsvStringIteratorFree(&it); // finally write data out OneWireWrite(pin, 8, 0x55); OneWireWrite(pin, 64, romdata); }
JsVar *jslNewStringFromLexer(JslCharPos *charFrom, size_t charTo) { // Original method - just copy it verbatim size_t maxLength = charTo + 1 - jsvStringIteratorGetIndex(&charFrom->it); assert(maxLength>0); // will fail if 0 // Try and create a flat string first JsVar *var = 0; if (maxLength > JSV_FLAT_STRING_BREAK_EVEN) { var = jsvNewFlatStringOfLength((unsigned int)maxLength); if (var) { // Flat string char *flatPtr = jsvGetFlatStringPointer(var); *(flatPtr++) = charFrom->currCh; JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (--maxLength>0)) { *(flatPtr++) = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); return var; } } // Non-flat string... var = jsvNewFromEmptyString(); if (!var) { // out of memory return 0; } //jsvAppendStringVar(var, lex->sourceVar, charFrom->it->index, (int)(charTo-charFrom)); JsVar *block = jsvLockAgain(var); block->varData.str[0] = charFrom->currCh; size_t blockChars = 1; size_t l = maxLength; // now start appending JsvStringIterator it = jsvStringIteratorClone(&charFrom->it); while (jsvStringIteratorHasChar(&it) && (--maxLength>0)) { char ch = jsvStringIteratorGetChar(&it); if (blockChars >= jsvGetMaxCharactersInVar(block)) { jsvSetCharactersInVar(block, blockChars); JsVar *next = jsvNewWithFlags(JSV_STRING_EXT_0); if (!next) break; // out of memory // we don't ref, because StringExts are never reffed as they only have one owner (and ALWAYS have an owner) jsvSetLastChild(block, jsvGetRef(next)); jsvUnLock(block); block = next; blockChars=0; // it's new, so empty } block->varData.str[blockChars++] = ch; jsvStringIteratorNext(&it); } jsvSetCharactersInVar(block, blockChars); jsvUnLock(block); // Just make sure we only assert if there's a bug here. If we just ran out of memory or at end of string it's ok assert((l == jsvGetStringLength(var)) || (jsErrorFlags&JSERR_MEMORY) || !jsvStringIteratorHasChar(&it)); jsvStringIteratorFree(&it); return var; }
// Return how many bytes are available to read JsVarInt jswrap_stream_available(JsVar *parent) { if (!jsvIsObject(parent)) return 0; JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); JsVarInt chars = 0; if (jsvIsString(buf)) chars = (JsVarInt)jsvGetStringLength(buf); jsvUnLock(buf); return chars; }
void socketClientPushReceiveData(JsVar *connection, JsVar *socket, JsVar **receiveData) { if (*receiveData && jsvGetStringLength(*receiveData)) { jswrap_stream_pushData(socket, *receiveData); // clear - because we have issued a callback jsvObjectSetChild(connection,HTTP_NAME_RECEIVE_DATA,0); jsvUnLock(*receiveData); *receiveData = 0; } }
/*JSON{ "type":"property", "class": "Object", "name" : "length", "description" : "Find the length of the object", "generate" : "jswrap_object_length", "return" : ["JsVar", "The value of the string"] }*/ JsVar *jswrap_object_length(JsVar *parent) { if (jsvIsArray(parent)) { return jsvNewFromInteger(jsvGetArrayLength(parent)); } else if (jsvIsArrayBuffer(parent)) { return jsvNewFromInteger((JsVarInt)jsvGetArrayBufferLength(parent)); } else if (jsvIsString(parent)) { return jsvNewFromInteger((JsVarInt)jsvGetStringLength(parent)); } return 0; }
/*JSON{ "type" : "method", "class" : "String", "name" : "lastIndexOf", "generate_full" : "jswrap_string_indexOf(parent, substring, fromIndex, true)", "params" : [ ["substring","JsVar","The string to search for"], ["fromIndex","JsVar","Index to search from"] ], "return" : ["int32","The index of the string, or -1 if not found"] } Return the last index of substring in this string, or -1 if not found */ int jswrap_string_indexOf(JsVar *parent, JsVar *substring, JsVar *fromIndex, bool lastIndexOf) { if (!jsvIsString(parent)) return 0; // slow, but simple! substring = jsvAsString(substring, false); if (!substring) return 0; // out of memory int parentLength = (int)jsvGetStringLength(parent); int subStringLength = (int)jsvGetStringLength(substring); if (subStringLength > parentLength) { jsvUnLock(substring); return -1; } int lastPossibleSearch = parentLength - subStringLength; int idx, dir, end; if (!lastIndexOf) { // normal indexOf dir = 1; end = lastPossibleSearch+1; idx = 0; if (jsvIsNumeric(fromIndex)) { idx = (int)jsvGetInteger(fromIndex); if (idx<0) idx=0; if (idx>end) idx=end; } } else { dir = -1; end = -1; idx = lastPossibleSearch; if (jsvIsNumeric(fromIndex)) { idx = (int)jsvGetInteger(fromIndex); if (idx<0) idx=0; if (idx>lastPossibleSearch) idx=lastPossibleSearch; } } for (;idx!=end;idx+=dir) { if (jsvCompareString(parent, substring, (size_t)idx, 0, true)==0) { jsvUnLock(substring); return idx; } } jsvUnLock(substring); return -1; }
/*JSON{ "type" : "method", "class" : "String", "name" : "substr", "generate" : "jswrap_string_substr", "params" : [ ["start","int","The start character index"], ["len","JsVar","The number of characters"] ], "return" : ["JsVar","Part of this string from start for len characters"] }*/ JsVar *jswrap_string_substr(JsVar *parent, JsVarInt pStart, JsVar *vLen) { JsVar *res; JsVarInt pLen = jsvIsUndefined(vLen) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vLen); if (pLen<0) pLen = 0; if (pStart<0) pStart += (JsVarInt)jsvGetStringLength(parent); if (pStart<0) pStart = 0; res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory jsvAppendStringVar(res, parent, (size_t)pStart, (size_t)pLen); return res; }
/*JSON{ "type" : "staticmethod", "class" : "ESP8266WiFi", "name" : "beAccessPoint", "generate" : "jswrap_ESP8266WiFi_beAccessPoint", "params" : [ ["jsv_ssid", "JsVar", "The network SSID"], ["jsv_password", "JsVar", "The password to allow stations to connect to the access point"] ] }*/ void jswrap_ESP8266WiFi_beAccessPoint( JsVar *jsv_ssid, //!< The network identity that the access point will advertize itself as. JsVar *jsv_password //!< The password a station will need to connect to the access point. ) { os_printf("> jswrap_ESP8266WiFi_beAccessPoint\n"); // Validate that the SSID and password are somewhat useful. if (jsv_ssid == NULL || !jsvIsString(jsv_ssid)) { jsExceptionHere(JSET_ERROR, "No SSID."); return; } // Build our SoftAP configuration details struct softap_config softApConfig; memset(&softApConfig, 0, sizeof(softApConfig)); // If no password has been supplied, then be open. Otherwise, use WPA2 and the // password supplied. Also check that the password is at least 8 characters long. if (jsv_password == NULL || !jsvIsString(jsv_password)) { softApConfig.authmode = AUTH_OPEN; } else { if (jsvGetStringLength(jsv_password) < 8) { jsExceptionHere(JSET_ERROR, "Password must be 8 characters or more in length."); return; } softApConfig.authmode = AUTH_WPA2_PSK; int len = jsvGetString(jsv_password, (char *)softApConfig.password, sizeof(softApConfig.password)-1); softApConfig.password[len]='\0'; } int len = jsvGetString(jsv_ssid, (char *)softApConfig.ssid, sizeof(softApConfig.ssid)-1); softApConfig.ssid[len]='\0'; // Define that we are in Soft AP mode. os_printf("Wifi: switching to soft-AP mode, authmode=%d\n", softApConfig.authmode); wifi_set_opmode_current(SOFTAP_MODE); softApConfig.ssid_len = 0; // Null terminated SSID softApConfig.ssid_hidden = 0; // Not hidden. softApConfig.max_connection = 4; // Maximum number of connections. // Set the WiFi configuration. int rc = wifi_softap_set_config_current(&softApConfig); // We should really check that becoming an access point works, however as of SDK 1.4, we // are finding that if we are currently connected to an access point and we switch to being // an access point, it works ... but returns 1 indicating an error. /* if (rc != 1) { os_printf(" - Error returned from wifi_softap_set_config_current=%d\n", rc); jsExceptionHere(JSET_ERROR, "Error setting ESP8266 softap config."); } */ os_printf("< jswrap_ESP8266WiFi_beAccessPoint\n"); }
/*JSON{ "type" : "method", "class" : "String", "name" : "match", "generate" : "jswrap_string_match", "params" : [ ["subStr","JsVar","Substring or RegExp to match"] ], "return" : ["JsVar","This match array"] } Matches `subStr` occurrence in the string. */ JsVar *jswrap_string_match(JsVar *parent, JsVar *subStr) { if (!jsvIsString(parent)) return 0; if (jsvIsUndefined(subStr)) return 0; #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); JsVar *match; match = jswrap_regexp_exec(subStr, parent); if (!jswrap_regexp_hasFlag(subStr,'g')) { return match; } // global JsVar *array = jsvNewEmptyArray(); if (!array) return 0; // out of memory while (match && !jsvIsNull(match)) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); int last = idx+len; jsvArrayPushAndUnLock(array, matchStr); // search again jsvUnLock(match); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(last)); match = jswrap_regexp_exec(subStr, parent); } jsvUnLock(match); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return array; } #endif subStr = jsvAsString(subStr); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *array = jsvNewEmptyArray(); if (!array) { jsvUnLock(subStr); return 0; // out of memory } jsvArrayPush(array, subStr); jsvObjectSetChildAndUnLock(array, "index", jsvNewFromInteger(idx)); jsvObjectSetChildAndUnLock(array, "input", subStr); return array; } jsvUnLock(subStr); return NULL; }
/*JSON{ "type" : "property", "class" : "Object", "name" : "length", "generate" : "jswrap_object_length", "return" : ["JsVar","The length of the object"] } Find the length of the object */ JsVar *jswrap_object_length(JsVar *parent) { JsVarInt l; if (jsvIsArray(parent)) { l = jsvGetArrayLength(parent); } else if (jsvIsArrayBuffer(parent)) { l = (JsVarInt)jsvGetArrayBufferLength(parent); } else if (jsvIsString(parent)) { l = (JsVarInt)jsvGetStringLength(parent); } else if (jsvIsFunction(parent)) { JsVar *args = jsvGetFunctionArgumentLength(parent); l = jsvGetArrayLength(args); jsvUnLock(args); } else return 0; return jsvNewFromInteger(l); }
/*JSON{ "type" : "method", "class" : "String", "name" : "replace", "generate" : "jswrap_string_replace", "params" : [ ["subStr","JsVar","The string to search for"], ["newSubStr","JsVar","The string to replace it with"] ], "return" : ["JsVar","This string with `subStr` replaced"] } Search and replace ONE occurrance of `subStr` with `newSubStr` and return the result. This doesn't alter the original string. Regular expressions not supported. */ JsVar *jswrap_string_replace(JsVar *parent, JsVar *subStr, JsVar *newSubStr) { JsVar *str = jsvAsString(parent, false); subStr = jsvAsString(subStr, false); newSubStr = jsvAsString(newSubStr, false); int idx = jswrap_string_indexOf(parent, subStr, 0, false); if (idx>=0) { JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); jsvAppendStringVar(newStr, newSubStr, 0, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvAppendStringVar(newStr, str, (size_t)idx+jsvGetStringLength(subStr), JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(str); str = newStr; } jsvUnLock2(subStr, newSubStr); return str; }
static bool jsfGetJSONForObjectItWithCallback(JsvObjectIterator *it, JSONFlags flags, const char *whitespace, JSONFlags nflags, vcbprintf_callback user_callback, void *user_data, bool first) { bool needNewLine = false; size_t sinceNewLine = 0; while (jsvObjectIteratorHasValue(it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(it); JsVar *item = jsvGetValueOfName(index); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)) || jsvIsGetterOrSetter(item); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_SOME_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (flags&JSON_ALL_NEWLINES) { needNewLine = true; newNeedsNewLine = true; } if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, whitespace, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } bool addQuotes = true; if (flags&JSON_DROP_QUOTES) { if (jsvIsIntegerish(index)) addQuotes = false; else if (jsvIsString(index) && jsvGetStringLength(index)<15) { char buf[16]; jsvGetString(index,buf,sizeof(buf)); if (isIDString(buf)) addQuotes=false; } } cbprintf(user_callback, user_data, addQuotes?"%q%s":"%v%s", index, (flags&JSON_PRETTY)?": ":":"); if (first) first = false; jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock2(index, item); jsvObjectIteratorNext(it); } return needNewLine; }
bool _http_send(SOCKET sckt, JsVar **sendData) { char buf[64]; int a=1; int len = (int)jsvGetStringLength(*sendData); if (len>0) { size_t bufLen = httpStringGet(*sendData, buf, sizeof(buf)); a = (int)send(sckt,buf,bufLen, MSG_NOSIGNAL); JsVar *newSendData = 0; if (a!=len) { newSendData = jsvNewFromEmptyString(); jsvAppendStringVar(newSendData, *sendData, a, JSVAPPENDSTRINGVAR_MAXLENGTH); } jsvUnLock(*sendData); *sendData = newSendData; } if (a<=0) { httpError("Socket error while sending"); return false; } return true; }
// Return a string containing 'chars' bytes. If chars<=0 the string will be all available data JsVar *jswrap_stream_read(JsVar *parent, JsVarInt chars) { if (!jsvIsObject(parent)) return 0; JsVar *buf = jsvObjectGetChild(parent, STREAM_BUFFER_NAME, 0); JsVar *data = 0; if (jsvIsString(buf)) { size_t len = jsvGetStringLength(buf); if (chars <= 0 || (size_t)chars>=len) { // return the whole buffer and remove it data = buf; buf = 0; jsvRemoveNamedChild(parent, STREAM_BUFFER_NAME); } else { // return just part of the buffer, and shorten it accordingly data = jsvNewFromStringVar(buf, 0, (size_t)chars); JsVar *newBuf = jsvNewFromStringVar(buf, (size_t)chars, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(jsvObjectSetChild(parent, STREAM_BUFFER_NAME, newBuf)); } } else data = jsvNewFromEmptyString(); jsvUnLock(buf); return data; }
bool socketClientConnectionsIdle(JsNetwork *net) { char *buf = alloca(net->chunkSize); // allocate on stack JsVar *arr = socketGetArray(HTTP_ARRAY_HTTP_CLIENT_CONNECTIONS,false); if (!arr) return false; bool hadSockets = false; JsvObjectIterator it; jsvObjectIteratorNew(&it, arr); while (jsvObjectIteratorHasValue(&it)) { hadSockets = true; // Get connection, socket, and socket type // For normal sockets, socket==connection, but for HTTP connection is httpCRq and socket is httpCRs JsVar *connection = jsvObjectIteratorGetValue(&it); SocketType socketType = socketGetType(connection); JsVar *socket = ((socketType&ST_TYPE_MASK)==ST_HTTP) ? jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0) : jsvLockAgain(connection); bool socketClosed = false; JsVar *receiveData = 0; bool hadHeaders = false; int error = 0; // error code received from netXxxx functions bool isHttp = (socketType&ST_TYPE_MASK) == ST_HTTP; bool closeConnectionNow = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSENOW, false)); bool alreadyConnected = jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CONNECTED, false)); int sckt = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_SOCKET,0))-1; // so -1 if undefined if (sckt>=0) { if (isHttp) hadHeaders = jsvGetBoolAndUnLock(jsvObjectGetChild(connection,HTTP_NAME_HAD_HEADERS,0)); else hadHeaders = true; receiveData = jsvObjectGetChild(connection,HTTP_NAME_RECEIVE_DATA,0); /* We do this up here because we want to wait until we have been once * around the idle loop (=callbacks have been executed) before we run this */ if (hadHeaders) socketClientPushReceiveData(connection, socket, &receiveData); JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (!closeConnectionNow) { // send data if possible if (sendData && !jsvIsEmptyString(sendData)) { // don't try to send if we're already in error state int num = 0; if (error == 0) num = socketSendData(net, connection, sckt, &sendData); if (num > 0 && !alreadyConnected && !isHttp) { // whoa, we sent something, must be connected! jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); alreadyConnected = true; } if (num < 0) { closeConnectionNow = true; error = num; } jsvObjectSetChild(connection, HTTP_NAME_SEND_DATA, sendData); // _http_send prob updated sendData } else { // no data to send, do we want to close? do so. if (jsvGetBoolAndUnLock(jsvObjectGetChild(connection, HTTP_NAME_CLOSE, false))) closeConnectionNow = true; } // Now read data if possible (and we have space for it) if (!receiveData || !hadHeaders) { int num = netRecv(net, sckt, buf, net->chunkSize); //if (num != 0) printf("recv returned %d\r\n", num); if (!alreadyConnected && num == SOCKET_ERR_NO_CONN) { ; // ignore... it's just telling us we're not connected yet } else if (num < 0) { closeConnectionNow = true; error = num; // disconnected without headers? error. if (!hadHeaders && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_NO_RESP; } else { // did we just get connected? if (!alreadyConnected && !isHttp) { jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &connection, 1); jsvObjectSetChildAndUnLock(connection, HTTP_NAME_CONNECTED, jsvNewFromBool(true)); alreadyConnected = true; // if we do not have any data to send, issue a drain event if (!sendData || (int)jsvGetStringLength(sendData) == 0) jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_DRAIN, &connection, 1); } // got data add it to our receive buffer if (num > 0) { if (!receiveData) { receiveData = jsvNewFromEmptyString(); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } if (receiveData) { // could be out of memory jsvAppendStringBuf(receiveData, buf, (size_t)num); if ((socketType&ST_TYPE_MASK)==ST_HTTP && !hadHeaders) { // for HTTP see whether we now have full response headers JsVar *resVar = jsvObjectGetChild(connection,HTTP_NAME_RESPONSE_VAR,0); if (httpParseHeaders(&receiveData, resVar, false)) { hadHeaders = true; jsvObjectSetChildAndUnLock(connection, HTTP_NAME_HAD_HEADERS, jsvNewFromBool(hadHeaders)); jsiQueueObjectCallbacks(connection, HTTP_NAME_ON_CONNECT, &resVar, 1); } jsvUnLock(resVar); jsvObjectSetChild(connection, HTTP_NAME_RECEIVE_DATA, receiveData); } } } } } jsvUnLock(sendData); } } if (closeConnectionNow) { socketClientPushReceiveData(connection, socket, &receiveData); if (!receiveData) { if ((socketType&ST_TYPE_MASK) != ST_HTTP) jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_END, &socket, 1); // If we had data to send but the socket closed, this is an error JsVar *sendData = jsvObjectGetChild(connection,HTTP_NAME_SEND_DATA,0); if (sendData && jsvGetStringLength(sendData) > 0 && error == SOCKET_ERR_CLOSED) error = SOCKET_ERR_UNSENT_DATA; jsvUnLock(sendData); _socketConnectionKill(net, connection); JsVar *connectionName = jsvObjectIteratorGetKey(&it); jsvObjectIteratorNext(&it); jsvRemoveChild(arr, connectionName); jsvUnLock(connectionName); socketClosed = true; // fire error event, if there is an error bool hadError = fireErrorEvent(error, connection, NULL); // close callback must happen after error callback JsVar *params[1] = { jsvNewFromBool(hadError) }; jsiQueueObjectCallbacks(socket, HTTP_NAME_ON_CLOSE, params, 1); jsvUnLock(params[0]); } } if (!socketClosed) { jsvObjectIteratorNext(&it); } jsvUnLock3(receiveData, connection, socket); } jsvUnLock(arr); return hadSockets; }
/*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; }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, const char *whitespace, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (!whitespace) whitespace=" "; if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { JsVarInt length = jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>(JsVarInt)JSON_LIMIT_AMOUNT); bool needNewLine = false; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); JsVarInt lastIndex = -1; bool numeric = true; bool first = true; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); while (lastIndex+1<length && numeric && !jspIsInterrupted()) { JsVar *key = jsvObjectIteratorGetKey(&it); if (!jsvObjectIteratorHasValue(&it) || jsvIsNumeric(key)) { JsVarInt index = jsvObjectIteratorHasValue(&it) ? jsvGetInteger(key) : length-1; JsVar *item = jsvObjectIteratorGetValue(&it); while (lastIndex < index) { lastIndex++; if (!limited || lastIndex<(JsVarInt)JSON_LIMITED_AMOUNT || lastIndex>=length-(JsVarInt)JSON_LIMITED_AMOUNT) { if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); first = false; if (limited && lastIndex==length-(JsVarInt)JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); bool newNeedsNewLine = ((flags&JSON_SOME_NEWLINES) && jsonNeedsNewLine(item)); if (flags&JSON_ALL_NEWLINES) { needNewLine = true; newNeedsNewLine = true; } if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, whitespace, user_callback, user_data); needNewLine = false; } if (lastIndex == index) jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); else cbprintf(user_callback, user_data, (flags&JSON_NO_UNDEFINED)?"null":"undefined"); needNewLine = newNeedsNewLine; } } jsvUnLock(item); jsvObjectIteratorNext(&it); } else { numeric = false; } jsvUnLock(key); } // non-numeric - but NOT for standard JSON if ((flags&JSON_PRETTY)) jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, first); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); bool asArray = flags&JSON_ARRAYBUFFER_AS_ARRAY; if (allZero && !asArray) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { const char *aname = jswGetBasicObjectName(var); /* You can't do `new ArrayBuffer([1,2,3])` so we have to output * `new Uint8Array([1,2,3]).buffer`! */ bool isBasicArrayBuffer = strcmp(aname,"ArrayBuffer")==0; if (isBasicArrayBuffer) { aname="Uint8Array"; } cbprintf(user_callback, user_data, asArray?"[":"new %s([", aname); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (flags&JSON_ALL_NEWLINES) jsonNewLine(nflags, whitespace, user_callback, user_data); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, whitespace, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } if (flags&JSON_ALL_NEWLINES) jsonNewLine(flags, whitespace, user_callback, user_data); jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, asArray?"]":"])"); if (isBasicArrayBuffer && !asArray) cbprintf(user_callback, user_data, ".buffer"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool showContents = true; if (flags & JSON_SHOW_OBJECT_NAMES) { JsVar *proto = jsvObjectGetChild(var, JSPARSE_INHERITS_VAR, 0); if (jsvHasChildren(proto)) { JsVar *constr = jsvObjectGetChild(proto, JSPARSE_CONSTRUCTOR_VAR, 0); if (constr) { JsVar *p = jsvGetIndexOf(execInfo.root, constr, true); if (p) cbprintf(user_callback, user_data, "%v: ", p); jsvUnLock2(p,constr); /* We had the constructor - now if there was a non-default toString function * we'll execute it and print the result */ JsVar *toStringFn = jspGetNamedField(var, "toString", false); if (toStringFn && toStringFn->varData.native.ptr != (void (*)(void))jswrap_object_toString) { // Function found and it's not the default one - execute it JsVar *result = jspExecuteFunction(toStringFn,var,0,0); cbprintf(user_callback, user_data, "%v", result); jsvUnLock(result); showContents = false; // we already printed something } jsvUnLock(toStringFn); } } jsvUnLock(proto); } if (showContents) { JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); bool needNewLine = jsfGetJSONForObjectItWithCallback(&it, flags, whitespace, nflags, user_callback, user_data, true); jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, whitespace, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock2(var1, var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
/*JSON{ "type":"staticmethod", "class" : "Integer", "name" : "valueOf", "generate" : "jswrap_integer_valueOf", "description" : "Given a string containing a single character, return the numeric value of it", "params" : [ [ "character" ,"JsVar", "A string containing a single character"] ], "return" : ["int", "The integer value of char"] }*/ JsVarInt jswrap_integer_valueOf(JsVar *v) { if (!jsvIsString(v) || jsvGetStringLength(v)!=1) return 0; return (int)v->varData.str[0]; }
void jsfGetJSONWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { JSONFlags nflags = flags + JSON_INDENT; // if we add a newline, make sure we indent any subsequent JSON more if (jsvIsUndefined(var)) { cbprintf(user_callback, user_data, "undefined"); } else { // Use IS_RECURSING flag to stop recursion if (var->flags & JSV_IS_RECURSING) { cbprintf(user_callback, user_data, " ... "); return; } var->flags |= JSV_IS_RECURSING; if (jsvIsArray(var)) { size_t length = (size_t)jsvGetArrayLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); bool needNewLine = false; size_t i; cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"[ ":"["); for (i=0;i<length && !jspIsInterrupted();i++) { if (!limited || i<JSON_LIMITED_AMOUNT || i>=length-JSON_LIMITED_AMOUNT) { if (i>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && i==length-JSON_LIMITED_AMOUNT) { if (needNewLine) jsonNewLine(nflags, user_callback, user_data); cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); } JsVar *item = jsvGetArrayItem(var, (JsVarInt)i); if (jsvIsUndefined(item) && (flags&JSON_NO_UNDEFINED)) item = jsvNewWithFlags(JSV_NULL); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; } jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; jsvUnLock(item); } } if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" ]":"]"); } else if (jsvIsArrayBuffer(var)) { JsvArrayBufferIterator it; bool allZero = true; jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it)) { if (jsvArrayBufferIteratorGetFloatValue(&it)!=0) allZero = false; jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); if (allZero) { cbprintf(user_callback, user_data, "new %s(%d)", jswGetBasicObjectName(var), jsvGetArrayBufferLength(var)); } else { cbprintf(user_callback, user_data, "new %s([", jswGetBasicObjectName(var)); size_t length = jsvGetArrayBufferLength(var); bool limited = (flags&JSON_LIMIT) && (length>JSON_LIMIT_AMOUNT); // no newlines needed for array buffers as they only contain simple stuff jsvArrayBufferIteratorNew(&it, var, 0); while (jsvArrayBufferIteratorHasElement(&it) && !jspIsInterrupted()) { if (!limited || it.index<JSON_LIMITED_AMOUNT || it.index>=length-JSON_LIMITED_AMOUNT) { if (it.index>0) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); if (limited && it.index==length-JSON_LIMITED_AMOUNT) cbprintf(user_callback, user_data, JSON_LIMIT_TEXT); JsVar *item = jsvArrayBufferIteratorGetValue(&it); jsfGetJSONWithCallback(item, nflags, user_callback, user_data); jsvUnLock(item); } jsvArrayBufferIteratorNext(&it); } jsvArrayBufferIteratorFree(&it); cbprintf(user_callback, user_data, "])"); } } else if (jsvIsObject(var)) { IOEventFlags device = (flags & JSON_SHOW_DEVICES) ? jsiGetDeviceFromClass(var) : EV_NONE; if (device!=EV_NONE) { cbprintf(user_callback, user_data, "%s", jshGetDeviceString(device)); } else { bool first = true; bool needNewLine = false; size_t sinceNewLine = 0; JsvObjectIterator it; jsvObjectIteratorNew(&it, var); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"{ ":"{"); while (jsvObjectIteratorHasValue(&it) && !jspIsInterrupted()) { JsVar *index = jsvObjectIteratorGetKey(&it); JsVar *item = jsvObjectIteratorGetValue(&it); bool hidden = jsvIsInternalObjectKey(index) || ((flags & JSON_IGNORE_FUNCTIONS) && jsvIsFunction(item)) || ((flags&JSON_NO_UNDEFINED) && jsvIsUndefined(item)); if (!hidden) { sinceNewLine++; if (!first) cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?", ":","); bool newNeedsNewLine = (flags&JSON_NEWLINES) && jsonNeedsNewLine(item); if ((flags&JSON_NEWLINES) && sinceNewLine>JSON_ITEMS_ON_LINE_OBJECT) needNewLine = true; if (needNewLine || newNeedsNewLine) { jsonNewLine(nflags, user_callback, user_data); needNewLine = false; sinceNewLine = 0; } cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?"%q: ":"%q:", index); if (first) first = false; jsfGetJSONWithCallback(item, nflags, user_callback, user_data); needNewLine = newNeedsNewLine; } jsvUnLock(index); jsvUnLock(item); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); if (needNewLine) jsonNewLine(flags, user_callback, user_data); cbprintf(user_callback, user_data, (flags&JSON_PRETTY)?" }":"}"); } } else if (jsvIsFunction(var)) { if (flags & JSON_IGNORE_FUNCTIONS) { cbprintf(user_callback, user_data, "undefined"); } else { cbprintf(user_callback, user_data, "function "); jsfGetJSONForFunctionWithCallback(var, nflags, user_callback, user_data); } } else if (jsvIsString(var) && !jsvIsName(var)) { if ((flags&JSON_LIMIT) && jsvGetStringLength(var)>JSON_LIMIT_STRING_AMOUNT) { // if the string is too big, split it and put dots in the middle JsVar *var1 = jsvNewFromStringVar(var, 0, JSON_LIMITED_STRING_AMOUNT); JsVar *var2 = jsvNewFromStringVar(var, jsvGetStringLength(var)-JSON_LIMITED_STRING_AMOUNT, JSON_LIMITED_STRING_AMOUNT); cbprintf(user_callback, user_data, "%q%s%q", var1, JSON_LIMIT_TEXT, var2); jsvUnLock(var1); jsvUnLock(var2); } else { cbprintf(user_callback, user_data, "%q", var); } } else { cbprintf(user_callback, user_data, "%v", var); } var->flags &= ~JSV_IS_RECURSING; } }
void clientRequestWrite(JsNetwork *net, JsVar *httpClientReqVar, JsVar *data) { SocketType socketType = socketGetType(httpClientReqVar); // Append data to sendData JsVar *sendData = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, 0); if (!sendData) { JsVar *options = 0; // Only append a header if we're doing HTTP AND we haven't already connected if ((socketType&ST_TYPE_MASK) == ST_HTTP) if (jsvGetIntegerAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_SOCKET, 0))==0) options = jsvObjectGetChild(httpClientReqVar, HTTP_NAME_OPTIONS_VAR, 0); if (options) { // We're an HTTP client - make a header JsVar *method = jsvObjectGetChild(options, "method", 0); JsVar *path = jsvObjectGetChild(options, "path", 0); sendData = jsvVarPrintf("%v %v HTTP/1.0\r\nUser-Agent: Espruino "JS_VERSION"\r\nConnection: close\r\n", method, path); jsvUnLock2(method, path); JsVar *headers = jsvObjectGetChild(options, "headers", 0); bool hasHostHeader = false; if (jsvIsObject(headers)) { JsVar *hostHeader = jsvObjectGetChild(headers, "Host", 0); hasHostHeader = hostHeader!=0; jsvUnLock(hostHeader); httpAppendHeaders(sendData, headers); // if Transfer-Encoding:chunked was set, subsequent writes need to 'chunk' the data that is sent if (jsvIsStringEqualAndUnLock(jsvObjectGetChild(headers, "Transfer-Encoding", 0), "chunked")) { jsvObjectSetChildAndUnLock(httpClientReqVar, HTTP_NAME_CHUNKED, jsvNewFromBool(true)); } } jsvUnLock(headers); if (!hasHostHeader) { JsVar *host = jsvObjectGetChild(options, "host", 0); int port = (int)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "port", 0)); if (port>0 && port!=80) jsvAppendPrintf(sendData, "Host: %v:%d\r\n", host, port); else jsvAppendPrintf(sendData, "Host: %v\r\n", host); jsvUnLock(host); } // finally add ending newline jsvAppendString(sendData, "\r\n"); } else { // !options // We're not HTTP (or were already connected), so don't send any header sendData = jsvNewFromString(""); } jsvObjectSetChild(httpClientReqVar, HTTP_NAME_SEND_DATA, sendData); jsvUnLock(options); } // We have data and aren't out of memory... if (data && sendData) { // append the data to what we want to send JsVar *s = jsvAsString(data, false); if (s) { if ((socketType&ST_TYPE_MASK) == ST_HTTP && jsvGetBoolAndUnLock(jsvObjectGetChild(httpClientReqVar, HTTP_NAME_CHUNKED, 0))) { // If we asked to send 'chunked' data, we need to wrap it up, // prefixed with the length jsvAppendPrintf(sendData, "%x\r\n%v\r\n", jsvGetStringLength(s), s); } else { jsvAppendStringVarComplete(sendData,s); } jsvUnLock(s); } } jsvUnLock(sendData); if ((socketType&ST_TYPE_MASK) == ST_HTTP) { // on HTTP we connect after the first write clientRequestConnect(net, httpClientReqVar); } }