/** 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" : "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" : "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; }
/*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; res = jsvNewWithFlags(JSV_STRING); if (!res) return 0; // out of memory jsvAppendStringVar(res, parent, (int)pStart, (int)pLen); return res; }
/*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":"function", "name" : "edit", "description" : ["Fill the console with the contents of the given function, so you can edit it.", "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."], "generate" : "jswrap_interface_edit", "params" : [ [ "funcName", "JsVar", "The name of the function to edit (either a string or just the unquoted name)"] ] }*/ void jswrap_interface_edit(JsVar *funcName) { JsVar *func = 0; if (jsvIsString(funcName)) { funcName = jsvLockAgain(funcName); func = jsvSkipNameAndUnLock(jsvFindChildFromVar(execInfo.root, funcName, 0)); } else { func = funcName; funcName = jsvGetPathTo(execInfo.root, func, 2); } if (jsvIsString(funcName)) { if (jsvIsFunction(func)) { JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false); JsVar *inRoot = jsvGetArrayIndexOf(execInfo.root, func, true); bool normalDecl = scopeVar==0 && inRoot!=0; jsvUnLock(inRoot); jsvUnLock(scopeVar); JsVar *newLine = jsvNewFromEmptyString(); if (newLine) { // could be out of memory /* normalDecl: * * function foo() { ... } * * NOT normalDecl: * * foo.replaceWith(function() { ... }); * */ JsVar *funcData = jsvAsString(func, false); if (normalDecl) { jsvAppendString(newLine, "function "); jsvAppendStringVarComplete(newLine, funcName); jsvAppendStringVar(newLine, funcData, 9, JSVAPPENDSTRINGVAR_MAXLENGTH); } else { jsvAppendStringVarComplete(newLine, funcName); jsvAppendString(newLine, ".replaceWith("); jsvAppendStringVarComplete(newLine, funcData); jsvAppendString(newLine, ");"); } jsvUnLock(funcData); jsiReplaceInputLine(newLine); jsvUnLock(newLine); } } else { jsError("Edit should be called with the name of a function"); } } else { jsError("Edit should be called with edit(funcName) or edit('funcName')"); } jsvUnLock(func); jsvUnLock(funcName); }
/*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" : "substring", "generate" : "jswrap_string_substring", "params" : [ ["start","int","The start character index"], ["end","JsVar","The end character index"] ], "return" : ["JsVar","The part of this string between start and end"] }*/ JsVar *jswrap_string_substring(JsVar *parent, JsVarInt pStart, JsVar *vEnd) { JsVar *res; JsVarInt pEnd = jsvIsUndefined(vEnd) ? JSVAPPENDSTRINGVAR_MAXLENGTH : (int)jsvGetInteger(vEnd); if (pStart<0) pStart=0; if (pEnd<0) pEnd=0; if (pEnd<pStart) { JsVarInt l = pStart; pStart = pEnd; pEnd = l; } res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory jsvAppendStringVar(res, parent, (size_t)pStart, (size_t)(pEnd-pStart)); return res; }
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; }
// 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; }
/*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); #ifndef SAVE_ON_FLASH // Use RegExp if one is passed in if (jsvIsInstanceOf(subStr, "RegExp")) { JsVar *replace; if (jsvIsFunction(newSubStr) || jsvIsString(newSubStr)) replace = jsvLockAgain(newSubStr); else replace = jsvAsString(newSubStr); jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); bool global = jswrap_regexp_hasFlag(subStr,'g'); JsVar *match; match = jswrap_regexp_exec(subStr, str); while (match && !jsvIsNull(match) && !jspIsInterrupted()) { // get info about match JsVar *matchStr = jsvGetArrayItem(match,0); JsVarInt idx = jsvGetIntegerAndUnLock(jsvObjectGetChild(match,"index",0)); JsVarInt len = (JsVarInt)jsvGetStringLength(matchStr); // do the replacement JsVar *newStr = jsvNewFromStringVar(str, 0, (size_t)idx); JsvStringIterator dst; jsvStringIteratorNew(&dst, newStr, 0); jsvStringIteratorGotoEnd(&dst); if (jsvIsFunction(replace)) { unsigned int argCount = 0; JsVar *args[13]; args[argCount++] = jsvLockAgain(matchStr); JsVar *v; while ((v = jsvGetArrayItem(match, (JsVarInt)argCount))) args[argCount++] = v; args[argCount++] = jsvObjectGetChild(match,"index",0); args[argCount++] = jsvObjectGetChild(match,"input",0); JsVar *result = jsvAsStringAndUnLock(jspeFunctionCall(replace, 0, 0, false, (JsVarInt)argCount, args)); jsvUnLockMany(argCount, args); jsvStringIteratorAppendString(&dst, result, 0); jsvUnLock(result); } else { JsvStringIterator src; jsvStringIteratorNew(&src, replace, 0); while (jsvStringIteratorHasChar(&src)) { char ch = jsvStringIteratorGetChar(&src); if (ch=='$') { jsvStringIteratorNext(&src); ch = jsvStringIteratorGetChar(&src); JsVar *group = 0; if (ch>'0' && ch<='9') group = jsvGetArrayItem(match, ch-'0'); if (group) { jsvStringIteratorAppendString(&dst, group, 0); jsvUnLock(group); } else { jsvStringIteratorAppend(&dst, '$'); jsvStringIteratorAppend(&dst, ch); } } else { jsvStringIteratorAppend(&dst, ch); } jsvStringIteratorNext(&src); } jsvStringIteratorFree(&src); } JsVarInt lastIndex = 1+(JsVarInt)jsvStringIteratorGetIndex(&dst); jsvStringIteratorAppendString(&dst, str, (size_t)(idx+len)); jsvStringIteratorFree(&dst); jsvUnLock2(str,matchStr); str = newStr; // search again if global jsvUnLock(match); match = 0; if (global) { jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(lastIndex)); match = jswrap_regexp_exec(subStr, str); } } jsvUnLock(match); jsvUnLock(replace); // reset lastIndex if global if (global) jsvObjectSetChildAndUnLock(subStr, "lastIndex", jsvNewFromInteger(0)); return str; } #endif newSubStr = jsvAsString(newSubStr); subStr = jsvAsString(subStr); 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; }
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; }