/*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; }
JsVar *wrap_fat_readdir(JsVar *path) { JsVar *arr = 0; // undefined unless we can open card char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); #ifdef LINUX if (!pathStr[0]) strcpy(pathStr, "."); // deal with empty readdir #endif FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX DIR dirs; if ((res=f_opendir(&dirs, pathStr)) == FR_OK) { char lfnBuf[_MAX_LFN+1]; FILINFO Finfo; Finfo.lfname = lfnBuf; Finfo.lfsize = sizeof(lfnBuf); #else DIR *dir = opendir(pathStr); if(dir) { #endif arr = jsvNewWithFlags(JSV_ARRAY); if (arr) { // could be out of memory #ifndef LINUX while (((res=f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0]) { char *fn = GET_FILENAME(Finfo); #else struct dirent *pDir=NULL; while((pDir = readdir(dir)) != NULL) { char *fn = (*pDir).d_name; #endif JsVar *fnVar = jsvNewFromString(fn); if (fnVar) {// out of memory? jsvArrayPush(arr, fnVar); jsvUnLock(fnVar); } } } #ifdef LINUX closedir(dir); #endif } } if (res) jsfsReportError("Unable to list files", res); return arr; } /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "writeFile", "generate_full" : " wrap_fat_writeOrAppendFile(path, data, false)", "description" : [ "Write the data to the given file", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ], "return" : [ "bool", "True on success, false on failure" ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "writeFileSync", "ifndef" : "SAVE_ON_FLASH", "generate_full" : " wrap_fat_writeOrAppendFile(path, data, false)", "description" : [ "Write the data to the given file" ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ], "return" : [ "bool", "True on success, false on failure" ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "appendFile", "generate_full" : " wrap_fat_writeOrAppendFile(path, data, true)", "description" : [ "Append the data to the given file, created a new file if it doesn't exist", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ], "return" : [ "bool", "True on success, false on failure" ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "appendFileSync", "ifndef" : "SAVE_ON_FLASH", "generate_full" : "wrap_fat_writeOrAppendFile(path, data, true)", "description" : [ "Append the data to the given file, created a new file if it doesn't exist" ], "params" : [ [ "path", "JsVar", "The path of the file to write" ], [ "data", "JsVar", "The data to write to the file" ] ], "return" : [ "bool", "True on success, false on failure" ] }*/ bool wrap_fat_writeOrAppendFile(JsVar *path, JsVar *data, bool append) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX FIL file; if ((res=f_open(&file, pathStr, FA_WRITE|(append ? FA_OPEN_ALWAYS : FA_CREATE_ALWAYS))) == FR_OK) { if (append) { // move to end of file to append data f_lseek(&file, file.fsize); // if (res != FR_OK) jsfsReportError("Unable to move to end of file", res); } #else FILE *file = fopen(pathStr, append?"a":"w"); if (file) { #endif JsvStringIterator it; JsVar *dataString = jsvAsString(data, false); jsvStringIteratorNew(&it, dataString, 0); size_t toWrite = 0; size_t written = 0; while (jsvStringIteratorHasChar(&it) && res==FR_OK && written==toWrite) { // re-use pathStr buffer toWrite = 0; while (jsvStringIteratorHasChar(&it) && toWrite < JS_DIR_BUF_SIZE) { pathStr[toWrite++] = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } #ifndef LINUX res = f_write(&file, pathStr, toWrite, &written); #else written = fwrite(pathStr, 1, toWrite, file); #endif } jsvStringIteratorFree(&it); jsvUnLock(dataString); #ifndef LINUX f_close(&file); #else fclose(file); #endif } } if (res) { jsfsReportError("Unable to write file", res); return false; } return true; } /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "readFile", "generate" : "wrap_fat_readFile", "description" : [ "Read all data from a file and return as a string", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to read" ] ], "return" : [ "JsVar", "A string containing the contents of the file (or undefined if the file doesn't exist)" ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "readFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_readFile", "description" : [ "Read all data from a file and return as a string" ], "params" : [ [ "path", "JsVar", "The path of the file to read" ] ], "return" : [ "JsVar", "A string containing the contents of the file (or undefined if the file doesn't exist)" ] }*/ JsVar *wrap_fat_readFile(JsVar *path) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); JsVar *result = 0; // undefined unless we read the file FRESULT res = 0; if (jsfsInit()) { #ifndef LINUX FIL file; if ((res=f_open(&file, pathStr, FA_READ)) == FR_OK) { #else FILE *file = fopen(pathStr, "r"); if (file) { #endif result = jsvNewFromEmptyString(); if (result) { // out of memory? // re-use pathStr buffer size_t bytesRead = JS_DIR_BUF_SIZE; while (res==FR_OK && bytesRead==JS_DIR_BUF_SIZE) { #ifndef LINUX res = f_read (&file, pathStr, JS_DIR_BUF_SIZE, &bytesRead); #else bytesRead = fread(pathStr,1,JS_DIR_BUF_SIZE,file); #endif if (!jsvAppendStringBuf(result, pathStr, (int)bytesRead)) break; // was out of memory } } #ifndef LINUX f_close(&file); #else fclose(file); #endif } } if (res) jsfsReportError("Unable to read file", res); return result; } /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "unlink", "generate" : "wrap_fat_unlink", "description" : [ "Delete the given file", "NOTE: Espruino does not yet support Async file IO, so this function behaves like the 'Sync' version." ], "params" : [ [ "path", "JsVar", "The path of the file to delete" ] ], "return" : [ "bool", "True on success, or false on failure" ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "unlinkSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_unlink", "description" : [ "Delete the given file" ], "params" : [ [ "path", "JsVar", "The path of the file to delete" ] ], "return" : [ "bool", "True on success, or false on failure" ] }*/ bool wrap_fat_unlink(JsVar *path) { char pathStr[JS_DIR_BUF_SIZE] = ""; if (!jsvIsUndefined(path)) jsvGetString(path, pathStr, JS_DIR_BUF_SIZE); #ifndef LINUX FRESULT res = 0; if (jsfsInit()) { res = f_unlink(pathStr); } #else FRESULT res = remove(pathStr); #endif if (res) { jsfsReportError("Unable to delete file", res); return false; } return true; }
/*JSON{ "type":"staticmethod", "class" : "url", "name" : "parse", "generate" : "jswrap_url_parse", "description" : ["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\"}}`"], "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`" ] }*/ 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 jsvUnLock(jsvObjectSetChild(obj, "method", jsvNewFromString("GET"))); jsvUnLock(jsvObjectSetChild(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, "/"); jsvUnLock(jsvObjectSetChild(obj, "path", v)); v = jsvNewFromStringVar(url, (size_t)pathStart, (size_t)((searchStart>=0)?(searchStart-pathStart):JSVAPPENDSTRINGVAR_MAXLENGTH)); if (jsvGetStringLength(v)==0) jsvAppendString(v, "/"); jsvUnLock(jsvObjectSetChild(obj, "pathname", v)); jsvUnLock(jsvObjectSetChild(obj, "search", (searchStart>=0)?jsvNewFromStringVar(url, (size_t)searchStart, JSVAPPENDSTRINGVAR_MAXLENGTH):jsvNewNull())); if (portNumber<=0 || portNumber>65535) portNumber=80; jsvUnLock(jsvObjectSetChild(obj, "port", 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); jsvUnLock(key); jsvUnLock(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); } jsvUnLock(key); jsvUnLock(val); } jsvUnLock(jsvObjectSetChild(obj, "query", query)); return obj; }
/** Iterate over the contents of the content of a variable, calling callback for each. Contents may be: * numeric -> output * a string -> output each character * array/arraybuffer -> call itself on each element * {data:..., count:...} -> call itself object.count times, on object.data * {callback:...} -> call the given function, call itself on return value */ bool jsvIterateCallback( JsVar *data, jsvIterateCallbackFn callback, void *callbackData ) { bool ok = true; // Handle the data being a single numeric. if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } // Handle the data being an object. else if (jsvIsObject(data)) { JsVar *callbackVar = jsvObjectGetChild(data, "callback", 0); if (jsvIsFunction(callbackVar)) { JsVar *result = jspExecuteFunction(callbackVar,0,0,NULL); jsvUnLock(callbackVar); if (result) { bool r = jsvIterateCallback(result, callback, callbackData); jsvUnLock(result); return r; } return true; } jsvUnLock(callbackVar); JsVar *countVar = jsvObjectGetChild(data, "count", 0); JsVar *dataVar = jsvObjectGetChild(data, "data", 0); if (countVar && dataVar && jsvIsNumeric(countVar)) { int n = (int)jsvGetInteger(countVar); while (ok && n-- > 0) { ok = jsvIterateCallback(dataVar, callback, callbackData); } } else { jsExceptionHere(JSET_TYPEERROR, "If specifying an object, it must be of the form {data : ..., count : N} or {callback : fn} - got %j", data); ok = false; } jsvUnLock2(countVar, dataVar); } // Handle the data being a string else if (jsvIsString(data)) { JsvStringIterator it; jsvStringIteratorNew(&it, data, 0); while (jsvStringIteratorHasChar(&it) && ok) { char ch = jsvStringIteratorGetChar(&it); callback(ch, callbackData); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } // Handle the data being an array buffer else if (jsvIsArrayBuffer(data)) { JsvArrayBufferIterator it; jsvArrayBufferIteratorNew(&it, data, 0); if (JSV_ARRAYBUFFER_GET_SIZE(it.type) == 1 && !JSV_ARRAYBUFFER_IS_SIGNED(it.type)) { JsvStringIterator *sit = &it.it; // faster for single byte arrays - read using the string iterator. while (jsvStringIteratorHasChar(sit)) { callback((int)(unsigned char)jsvStringIteratorGetChar(sit), callbackData); jsvStringIteratorNextInline(sit); } } else { while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)jsvArrayBufferIteratorGetIntegerValue(&it), callbackData); jsvArrayBufferIteratorNext(&it); } } jsvArrayBufferIteratorFree(&it); } // Handle the data being iterable else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data, JSIF_EVERY_ARRAY_ELEMENT); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsExceptionHere(JSET_TYPEERROR, "Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
// 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; } } 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, (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) { JsVar *vMethod = jsvNewFromEmptyString(); if (vMethod) { jsvAppendStringVar(vMethod, *receiveData, 0, (size_t)firstSpace); jsvUnLock(jsvAddNamedChild(objectForData, vMethod, "method")); jsvUnLock(vMethod); } JsVar *vUrl = jsvNewFromEmptyString(); if (vUrl) { jsvAppendStringVar(vUrl, *receiveData, (size_t)(firstSpace+1), (size_t)(secondSpace-(firstSpace+1))); jsvUnLock(jsvAddNamedChild(objectForData, vUrl, "url")); jsvUnLock(vUrl); } } // strip out the header JsVar *afterHeaders = jsvNewFromStringVar(*receiveData, (size_t)headerEnd, JSVAPPENDSTRINGVAR_MAXLENGTH); jsvUnLock(*receiveData); *receiveData = afterHeaders; return true; }
JsVar *jswrap_spi_send(JsVar *parent, JsVar *srcdata, Pin nss_pin) { NOT_USED(parent); IOEventFlags device = jsiGetDeviceFromClass(parent); jswrap_spi_send_data data; if (!jsspiGetSendFunction(parent, &data.spiSend, &data.spiSendData)) return 0; JsVar *dst = 0; // we're sending and receiving if (DEVICE_IS_SPI(device)) jshSPISetReceive(device, true); // assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, false); // send data if (jsvIsNumeric(srcdata)) { int r = data.spiSend((unsigned char)jsvGetInteger(srcdata), &data.spiSendData); if (r<0) r = data.spiSend(-1, &data.spiSendData); dst = jsvNewFromInteger(r); // retrieve the byte (no send!) } else if (jsvIsString(srcdata)) { dst = jsvNewFromEmptyString(); JsvStringIterator it; jsvStringIteratorNew(&it, srcdata, 0); int incount = 0, outcount = 0; while (jsvStringIteratorHasChar(&it) && !jspIsInterrupted()) { unsigned char in = (unsigned char)jsvStringIteratorGetChar(&it); incount++; int out = data.spiSend(in, &data.spiSendData); if (out>=0) { outcount++; char outc = (char)out; jsvAppendStringBuf(dst, (char*)&outc, 1); } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); // finally add the remaining bytes (no send!) while (outcount < incount && !jspIsInterrupted()) { outcount++; unsigned char out = (unsigned char)data.spiSend(-1, &data.spiSendData); jsvAppendStringBuf(dst, (char*)&out, 1); } } else { int nBytes = jsvIterateCallbackCount(srcdata); dst = jsvNewTypedArray(ARRAYBUFFERVIEW_UINT8, nBytes); if (dst) { data.rxAmt = data.txAmt = 0; jsvArrayBufferIteratorNew(&data.it, dst, 0); // Write data jsvIterateCallback(srcdata, (void (*)(int, void *))jswrap_spi_send_cb, &data); // Wait until SPI send is finished, and flush data while (data.rxAmt < data.txAmt && !jspIsInterrupted()) jswrap_spi_send_cb(-1, &data); jsvArrayBufferIteratorFree(&data.it); } } // de-assert NSS if (nss_pin!=PIN_UNDEFINED) jshPinOutput(nss_pin, true); return dst; }
JsVar *jslNewTokenisedStringFromLexer(JslCharPos *charFrom, size_t charTo) { // New method - tokenise functions // save old lex JsLex *oldLex = lex; JsLex newLex; lex = &newLex; // work out length size_t length = 0; jslInit(oldLex->sourceVar); jslSeekToP(charFrom); int lastTk = LEX_EOF; while (lex->tk!=LEX_EOF && jsvStringIteratorGetIndex(&lex->it)<=charTo+1) { if ((lex->tk==LEX_ID || lex->tk==LEX_FLOAT || lex->tk==LEX_INT) && ( lastTk==LEX_ID || lastTk==LEX_FLOAT || lastTk==LEX_INT)) { jsExceptionHere(JSET_SYNTAXERROR, "ID/number following ID/number isn't valid JS"); length = 0; break; } if (lex->tk==LEX_ID || lex->tk==LEX_INT || lex->tk==LEX_FLOAT || lex->tk==LEX_STR || lex->tk==LEX_TEMPLATE_LITERAL) { length += jsvStringIteratorGetIndex(&lex->it)-jsvStringIteratorGetIndex(&lex->tokenStart.it); } else { length++; } lastTk = lex->tk; jslGetNextToken(); } // Try and create a flat string first JsVar *var = jsvNewStringOfLength((unsigned int)length, NULL); if (var) { // out of memory JsvStringIterator dstit; jsvStringIteratorNew(&dstit, var, 0); // now start appending jslSeekToP(charFrom); while (lex->tk!=LEX_EOF && jsvStringIteratorGetIndex(&lex->it)<=charTo+1) { if (lex->tk==LEX_ID || lex->tk==LEX_INT || lex->tk==LEX_FLOAT || lex->tk==LEX_STR || lex->tk==LEX_TEMPLATE_LITERAL) { jsvStringIteratorSetCharAndNext(&dstit, lex->tokenStart.currCh); JsvStringIterator it = jsvStringIteratorClone(&lex->tokenStart.it); while (jsvStringIteratorGetIndex(&it)+1 < jsvStringIteratorGetIndex(&lex->it)) { jsvStringIteratorSetCharAndNext(&dstit, jsvStringIteratorGetChar(&it)); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } else { jsvStringIteratorSetCharAndNext(&dstit, (char)lex->tk); } lastTk = lex->tk; jslGetNextToken(); } jsvStringIteratorFree(&dstit); } // restore lex jslKill(); lex = oldLex; return var; }