void jslSeekTo(JsLex *lex, size_t seekToChar) { jsvStringIteratorFree(&lex->it); jsvStringIteratorNew(&lex->it, lex->sourceVar, seekToChar); lex->tokenStart.it.var = 0; lex->tokenStart.currCh = 0; jslPreload(lex); }
/*JSON{ "type" : "method", "class" : "String", "name" : "trim", "generate" : "jswrap_string_trim", "return" : ["JsVar","A String with Whitespace removed from the beginning and end"], "return_object" : "String" } Return a new string with any whitespace (tabs, space, form feed, newline, carriage return, etc) removed from the beginning and end. */ JsVar *jswrap_string_trim(JsVar *parent) { JsVar *s = jsvAsString(parent); if (!s) return s; unsigned int start = 0; int end = -1; // work out beginning and end JsvStringIterator it; jsvStringIteratorNew(&it, s, 0); while (jsvStringIteratorHasChar(&it)) { bool ws = isWhitespace(jsvStringIteratorGetChar(&it)); if (!ws) { if (end<0) start = (unsigned int)jsvStringIteratorGetIndex(&it); end = (int)jsvStringIteratorGetIndex(&it); // last } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); // work out length unsigned int len = 0; if (end>=(int)start) len = 1+(unsigned int)end-start; JsVar *res = jsvNewFromStringVar(s, start, len); jsvUnLock(s); return res; }
/*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); }
/* match: search for regexp anywhere in text */ static JsVar *match(char *regexp, JsVar *str, size_t startIndex, bool ignoreCase) { matchInfo info; info.sourceStr = str; info.startIndex = startIndex; info.ignoreCase = ignoreCase; info.rangeMatch = false; info.rangeFirstChar = NO_RANGE; info.groups = 0; JsVar *rmatch; JsvStringIterator txtIt, txtIt2; jsvStringIteratorNew(&txtIt, str, startIndex); /* must look even if string is empty */ txtIt2 = jsvStringIteratorClone(&txtIt); rmatch = matchhere(regexp, &txtIt2, info); jsvStringIteratorFree(&txtIt2); jsvStringIteratorNext(&txtIt); while (!rmatch && jsvStringIteratorHasChar(&txtIt)) { info.startIndex++; txtIt2 = jsvStringIteratorClone(&txtIt); rmatch = matchhere(regexp, &txtIt2, info); jsvStringIteratorFree(&txtIt2); jsvStringIteratorNext(&txtIt); } jsvStringIteratorFree(&txtIt); return rmatch; }
void jslSeekTo(size_t seekToChar) { if (lex->it.var) jsvLockAgain(lex->it.var); // see jslGetNextCh jsvStringIteratorFree(&lex->it); jsvStringIteratorNew(&lex->it, lex->sourceVar, seekToChar); jsvUnLock(lex->it.var); // see jslGetNextCh lex->tokenStart.it.var = 0; lex->tokenStart.currCh = 0; jslPreload(); }
void jsvStringIteratorAppendString(JsvStringIterator *it, JsVar *str, size_t startIdx) { JsvStringIterator sit; jsvStringIteratorNew(&sit, str, startIdx); while (jsvStringIteratorHasChar(&sit)) { jsvStringIteratorAppend(it, jsvStringIteratorGetChar(&sit)); jsvStringIteratorNext(&sit); } jsvStringIteratorFree(&sit); }
void jsfGetJSONWhitespace(JsVar *var, JsVar *result, JSONFlags flags, const char *whitespace) { assert(jsvIsString(result)); JsvStringIterator it; jsvStringIteratorNew(&it, result, 0); jsvStringIteratorGotoEnd(&it); jsfGetJSONWithCallback(var, flags, whitespace, (vcbprintf_callback)&jsvStringIteratorPrintfCallback, &it); jsvStringIteratorFree(&it); }
void jsvIteratorNew(JsvIterator *it, JsVar *obj) { if (jsvIsArray(obj) || jsvIsObject(obj) || jsvIsFunction(obj)) { it->type = JSVI_OBJECT; jsvObjectIteratorNew(&it->it.obj, obj); } else if (jsvIsArrayBuffer(obj)) { it->type = JSVI_ARRAYBUFFER; jsvArrayBufferIteratorNew(&it->it.buf, obj, 0); } else if (jsvHasCharacterData(obj)) { it->type = JSVI_STRING; jsvStringIteratorNew(&it->it.str, obj, 0); } else assert(0); }
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, size_t tokenPos, char *prefix) { size_t line = 1,col = 1; jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col); size_t startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1); size_t lineLength = jsvGetCharsOnLine(lex->sourceVar, line); size_t prefixLength = 0; if (prefix) { user_callback(prefix, user_data); prefixLength = strlen(prefix); } if (lineLength>60 && tokenPos-startOfLine>30) { cbprintf(user_callback, user_data, "..."); size_t skipChars = tokenPos-30 - startOfLine; startOfLine += 3+skipChars; if (skipChars<=col) col -= skipChars; else col = 0; lineLength -= skipChars; } // print the string until the end of the line, or 60 chars (whichever is less) int chars = 0; JsvStringIterator it; jsvStringIteratorNew(&it, lex->sourceVar, startOfLine); unsigned char lastch = 0; while (jsvStringIteratorHasChar(&it) && chars<60) { unsigned char ch = (unsigned char)jsvStringIteratorGetChar(&it); if (ch == '\n') break; if (jslNeedSpaceBetween(lastch, ch)) { col++; user_callback(" ", user_data); } char buf[32]; jslFunctionCharAsString(ch, buf, sizeof(buf)); size_t len = strlen(buf); col += len-1; user_callback(buf, user_data); chars++; lastch = ch; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); if (lineLength > 60) user_callback("...", user_data); user_callback("\n", user_data); col += prefixLength; while (col-- > 1) user_callback(" ", user_data); user_callback("^\n", user_data); }
void jsfGetEscapedString(JsVar *var, vcbprintf_callback user_callback, void *user_data) { user_callback("\"",user_data); JsvStringIterator it; jsvStringIteratorNew(&it, var, 0); while (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); user_callback(escapeCharacter(ch), user_data); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); user_callback("\"",user_data); }
void jslInit(JsLex *lex, JsVar *var) { lex->sourceVar = jsvLockAgain(var); // reset stuff lex->tk = 0; lex->tokenStart.it.var = 0; lex->tokenStart.currCh = 0; lex->tokenLastStart = 0; lex->tokenl = 0; lex->tokenValue = 0; // set up iterator jsvStringIteratorNew(&lex->it, lex->sourceVar, 0); jslPreload(lex); }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsVar *parentStr = jsvAsString(parent); JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parentStr, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); ch = upper ? jsvStringCharToUpper(ch) : jsvStringCharToLower(ch); jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); jsvUnLock(parentStr); return res; }
/*JSON{ "type" : "method", "class" : "File", "name" : "read", "generate" : "jswrap_file_read", "description" : [ "Read data in a file in byte size chunks"], "params" : [ ["length", "int32", "is an integer specifying the number of bytes to read."] ], "return" : [ "JsVar", "A string containing the characters that were read" ] }*/ JsVar *jswrap_file_read(JsVar* parent, int length) { JsVar *buffer = 0; JsvStringIterator it; FRESULT res = 0; size_t bytesRead = 0; if (jsfsInit()) { JsFile file; if (fileGetFromVar(&file, parent)) { if(file.data.mode == FM_READ || file.data.mode == FM_READ_WRITE) { char buf[32]; size_t actual = 0; while (bytesRead < (size_t)length) { size_t requested = (size_t)length - bytesRead; if (requested > sizeof( buf )) requested = sizeof( buf ); actual = 0; #ifndef LINUX res = f_read(&file.data.handle, buf, requested, &actual); if(res) break; #else actual = fread(buf, 1, requested, file.data.handle); #endif if (actual>0) { if (!buffer) { buffer = jsvNewFromEmptyString(); if (!buffer) return 0; // out of memory jsvStringIteratorNew(&it, buffer, 0); } size_t i; for (i=0;i<actual;i++) jsvStringIteratorAppend(&it, buf[i]); } bytesRead += actual; if(actual != requested) break; } fileSetVar(&file); } } } if (res) jsfsReportError("Unable to read file", res); if (buffer) jsvStringIteratorFree(&it); // automatically close this file if we're at the end of it if (bytesRead!=(size_t)length) jswrap_file_close(parent); return buffer; }
/*JSON{ "type" : "method", "class" : "String", "name" : "charAt", "generate" : "jswrap_string_charAt", "params" : [ ["pos","int","The character number in the string. Negative values return characters from end of string (-1 = last char)"] ], "return" : ["JsVar","The character in the string"] } Return a single character at the given position in the String. */ JsVar *jswrap_string_charAt(JsVar *parent, JsVarInt idx) { // We do this so we can handle '/0' in a string JsVar *r = jsvNewFromEmptyString(); if (r && jsvIsString(parent) && idx>=0) { JsvStringIterator it; jsvStringIteratorNew(&it, parent, (size_t)idx); if (jsvStringIteratorHasChar(&it)) { char ch = jsvStringIteratorGetChar(&it); jsvAppendStringBuf(r, &ch, 1); } jsvStringIteratorFree(&it); } return r; }
/*JSON{ "type" : "method", "class" : "String", "name" : "toUpperCase", "generate_full" : "jswrap_string_toUpperLowerCase(parent, true)", "params" : [ ], "return" : ["JsVar","The uppercase version of this string"] }*/ JsVar *jswrap_string_toUpperLowerCase(JsVar *parent, bool upper) { JsVar *res = jsvNewFromEmptyString(); if (!res) return 0; // out of memory JsvStringIterator itsrc, itdst; jsvStringIteratorNew(&itsrc, parent, 0); jsvStringIteratorNew(&itdst, res, 0); while (jsvStringIteratorHasChar(&itsrc)) { char ch = jsvStringIteratorGetChar(&itsrc); if (upper) { if (ch >= 97 && ch <= 122) ch = (char)(ch - 32); } else { if (ch >= 65 && ch <= 90) ch = (char)(ch + 32); // A-Z } jsvStringIteratorAppend(&itdst, ch); jsvStringIteratorNext(&itsrc); } jsvStringIteratorFree(&itsrc); jsvStringIteratorFree(&itdst); return res; }
/// Does this regex have the given flag? bool jswrap_regexp_hasFlag(JsVar *parent, char flag) { JsVar *flags = jsvObjectGetChild(parent, "flags", 0); bool has = false; if (jsvIsString(flags)) { JsvStringIterator it; jsvStringIteratorNew(&it, flags, 0); while (jsvStringIteratorHasChar(&it)) { has |= jsvStringIteratorGetChar(&it)==flag; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); } jsvUnLock(flags); return has; }
void jslInit(JsVar *var) { lex->sourceVar = jsvLockAgain(var); // reset stuff lex->tk = 0; lex->tokenStart.it.var = 0; lex->tokenStart.currCh = 0; lex->tokenLastStart = 0; lex->tokenl = 0; lex->tokenValue = 0; lex->lineNumberOffset = 0; // set up iterator jsvStringIteratorNew(&lex->it, lex->sourceVar, 0); jsvUnLock(lex->it.var); // see jslGetNextCh jslPreload(); }
size_t httpStringGet(JsVar *v, char *str, size_t len) { size_t l = len; JsvStringIterator it; jsvStringIteratorNew(&it, v, 0); while (jsvStringIteratorHasChar(&it)) { if (l--==0) { jsvStringIteratorFree(&it); return len; } *(str++) = jsvStringIteratorGetChar(&it); jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); return len-l; }
void jsvIteratorNew(JsvIterator *it, JsVar *obj, JsvIteratorFlags flags) { if (jsvIsArray(obj) || jsvIsObject(obj) || jsvIsFunction(obj) || jsvIsGetterOrSetter(obj)) { it->type = JSVI_OBJECT; if (jsvIsArray(obj) && (flags&JSIF_EVERY_ARRAY_ELEMENT)) { it->type = JSVI_FULLARRAY; it->it.obj.index = 0; it->it.obj.var = jsvLockAgain(obj); } jsvObjectIteratorNew(&it->it.obj.it, obj); } else if (jsvIsArrayBuffer(obj)) { it->type = JSVI_ARRAYBUFFER; jsvArrayBufferIteratorNew(&it->it.buf, obj, 0); } else if (jsvHasCharacterData(obj)) { it->type = JSVI_STRING; jsvStringIteratorNew(&it->it.str, obj, 0); } else assert(0); }
// -------------------------------------------------------------------------------------------- void jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, size_t index) { assert(jsvIsArrayBuffer(arrayBuffer)); it->index = index; it->type = arrayBuffer->varData.arraybuffer.type; it->byteLength = arrayBuffer->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(it->type); it->byteOffset = arrayBuffer->varData.arraybuffer.byteOffset; JsVar *arrayBufferData = jsvGetArrayBufferBackingString(arrayBuffer); it->byteLength += it->byteOffset; // because we'll check if we have more bytes using this it->byteOffset = it->byteOffset + index*JSV_ARRAYBUFFER_GET_SIZE(it->type); if (it->byteOffset>=(it->byteLength+1-JSV_ARRAYBUFFER_GET_SIZE(it->type))) { jsvUnLock(arrayBufferData); it->type = ARRAYBUFFERVIEW_UNDEFINED; return; } jsvStringIteratorNew(&it->it, arrayBufferData, (size_t)it->byteOffset); jsvUnLock(arrayBufferData); it->hasAccessedElement = false; }
// No match found, so search to end of regex to see if there's an '|' static JsVar *nomatchfound(char *regexp, matchInfo info) { if (!jspCheckStackPosition()) return 0; // TODO: | inside a group? while (*regexp && *regexp!='|') { if (*regexp=='\\') { regexp++; // end of regex after escape char! if (!*regexp) return 0; } regexp++; } if (*regexp != '|') return 0; regexp++; JsvStringIterator txtIt; jsvStringIteratorNew(&txtIt, info.sourceStr, info.startIndex); JsVar *rmatch = matchhere(regexp, &txtIt, info); jsvStringIteratorFree(&txtIt); return rmatch; }
void jslPrintTokenLineMarker(vcbprintf_callback user_callback, void *user_data, size_t tokenPos, char *prefix) { size_t line = 1,col = 1; jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col); size_t startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1); size_t lineLength = jsvGetCharsOnLine(lex->sourceVar, line); size_t prefixLength = 0; if (prefix) { user_callback(prefix, user_data); prefixLength = strlen(prefix); } if (lineLength>60 && tokenPos-startOfLine>30) { cbprintf(user_callback, user_data, "..."); size_t skipChars = tokenPos-30 - startOfLine; startOfLine += 3+skipChars; col -= skipChars; lineLength -= skipChars; } // print the string until the end of the line, or 60 chars (whichever is lesS) int chars = 0; JsvStringIterator it; jsvStringIteratorNew(&it, lex->sourceVar, startOfLine); while (jsvStringIteratorHasChar(&it) && chars<60) { char ch = jsvStringIteratorGetChar(&it); if (ch == '\n') break; char buf[2]; buf[0] = ch; buf[1] = 0; user_callback(buf, user_data); chars++; jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); if (lineLength > 60) user_callback("...", user_data); user_callback("\n", user_data); col += prefixLength; while (col-- > 1) user_callback(" ", user_data); user_callback("^\n", user_data); }
/** Iterate over the contents of var, calling callback for each. Contents may be: * * numeric -> output * * a string -> output each character * * array/arraybuffer -> call itself on each element * * object -> call itself object.count times, on object.data */ bool jsvIterateCallback(JsVar *data, void (*callback)(int item, void *callbackData), void *callbackData) { bool ok = true; if (jsvIsNumeric(data)) { callback((int)jsvGetInteger(data), callbackData); } else if (jsvIsObject(data)) { 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 { jsWarn("If specifying an object, it must be of the form {data : ..., count : N}"); } jsvUnLock(countVar); jsvUnLock(dataVar); } 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); } else if (jsvIsIterable(data)) { JsvIterator it; jsvIteratorNew(&it, data); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsWarn("Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
static void jslLexRegex() { lex->tokenValue = jsvNewFromEmptyString(); if (!lex->tokenValue) { lex->tk = LEX_EOF; return; } JsvStringIterator it; jsvStringIteratorNew(&it, lex->tokenValue, 0); jsvStringIteratorAppend(&it, '/'); // strings... jslGetNextCh(); while (lex->currCh && lex->currCh!='/') { if (lex->currCh == '\\') { jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } else if (lex->currCh=='\n') { /* Was a newline - this is now allowed * unless we're a template string */ break; } jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } lex->tk = LEX_REGEX; if (lex->currCh!='/') { lex->tk++; // +1 gets you to 'unfinished X' } else { jsvStringIteratorAppend(&it, '/'); jslGetNextCh(); // regex modifiers while (lex->currCh=='g' || lex->currCh=='i' || lex->currCh=='m' || lex->currCh=='y' || lex->currCh=='u') { jslTokenAppendChar(lex->currCh); jsvStringIteratorAppend(&it, lex->currCh); jslGetNextCh(); } } jsvStringIteratorFree(&it); }
/*JSON{ "type":"function", "name" : "isNaN", "description" : "Whether the x is NaN (Not a Number) or not", "generate" : "jswrap_isNaN", "params" : [ [ "x", "JsVar", ""] ], "return" : ["bool", "True is the value is NaN, false if not."] }*/ bool jswrap_isNaN(JsVar *v) { if (jsvIsUndefined(v) || jsvIsObject(v) || (jsvIsFloat(v) && isnan(jsvGetFloat(v)))) return true; if (jsvIsString(v)) { // this is where is can get a bit crazy bool allWhiteSpace = true; JsvStringIterator it; jsvStringIteratorNew(&it,v,0); while (jsvStringIteratorHasChar(&it)) { if (!isWhitespace(jsvStringIteratorGetChar(&it))) { allWhiteSpace = false; break; } jsvStringIteratorNext(&it); } jsvStringIteratorFree(&it); if (allWhiteSpace) return false; return isnan(jsvGetFloat(v)); } return false; }
/*JSON{ "type" : "method", "class" : "File", "name" : "read", "generate" : "jswrap_file_read", "params" : [ ["length","int32","is an integer specifying the number of bytes to read."] ], "return" : ["JsVar","A string containing the characters that were read"] } Read data in a file in byte size chunks */ JsVar *jswrap_file_read(JsVar* parent, int length) { if (length<0) length=0; JsVar *buffer = 0; JsvStringIterator it; FRESULT res = 0; size_t bytesRead = 0; if (jsfsInit()) { JsFile file; if (fileGetFromVar(&file, parent)) { if(file.data->mode == FM_READ || file.data->mode == FM_READ_WRITE) { size_t actual = 0; #ifndef LINUX // if we're able to load this into a flat string, do it! size_t len = f_size(&file.data->handle)-f_tell(&file.data->handle); if ( len == 0 ) { // file all read return 0; // if called from a pipe signal end callback } if (len > (size_t)length) len = (size_t)length; buffer = jsvNewFlatStringOfLength(len); if (buffer) { res = f_read(&file.data->handle, jsvGetFlatStringPointer(buffer), len, &actual); if (res) jsfsReportError("Unable to read file", res); return buffer; } #endif char buf[32]; while (bytesRead < (size_t)length) { size_t requested = (size_t)length - bytesRead; if (requested > sizeof( buf )) requested = sizeof( buf ); actual = 0; #ifndef LINUX res = f_read(&file.data->handle, buf, requested, &actual); if(res) break; #else actual = fread(buf, 1, requested, file.data->handle); #endif if (actual>0) { if (!buffer) { buffer = jsvNewFromEmptyString(); if (!buffer) return 0; // out of memory jsvStringIteratorNew(&it, buffer, 0); } size_t i; for (i=0;i<actual;i++) jsvStringIteratorAppend(&it, buf[i]); } bytesRead += actual; if(actual != requested) break; } } } } if (res) jsfsReportError("Unable to read file", res); if (buffer) jsvStringIteratorFree(&it); return buffer; }
/** * \brief 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 * object -> call itself object.count times, on object.data */ bool jsvIterateCallback( JsVar *data, // The data to iterate over. void (*callback)(int item, void *callbackData), // The callback function invoke. void *callbackData // Data to be passed to the callback function ) { 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 *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 { jsWarn("If specifying an object, it must be of the form {data : ..., count : N}"); } 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)) { // faster for single byte arrays. while (jsvArrayBufferIteratorHasElement(&it)) { callback((int)(unsigned char)jsvStringIteratorGetChar(&it.it), callbackData); jsvArrayBufferIteratorNext(&it); } } 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); while (jsvIteratorHasElement(&it) && ok) { JsVar *el = jsvIteratorGetValue(&it); ok = jsvIterateCallback(el, callback, callbackData); jsvUnLock(el); jsvIteratorNext(&it); } jsvIteratorFree(&it); } else { jsWarn("Expecting a number or something iterable, got %t", data); ok = false; } return ok; }
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); } } #ifdef LINUX closedir(dir); #endif } } if (res) jsfsReportError("Unable to list files", res); return arr; } /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "writeFile", "generate" : "wrap_fat_writeFile", "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" ] ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "writeFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_writeFile", "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" ] ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "appendFile", "generate" : "wrap_fat_appendFile", "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" ] ] }*/ /*JSON{ "type" : "staticmethod", "class" : "fs", "name" : "appendFileSync", "ifndef" : "SAVE_ON_FLASH", "generate" : "wrap_fat_appendFile", "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" ] ] }*/ void 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); } void wrap_fat_writeFile(JsVar *path, JsVar *data) { wrap_fat_writeOrAppendFile(path, data, false); } void wrap_fat_appendFile(JsVar *path, JsVar *data) { wrap_fat_writeOrAppendFile(path, data, true); }
/*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; }
/* This is like jsfGetJSONWithCallback, but handles ONLY functions (and does not print the initial 'function' text) */ void jsfGetJSONForFunctionWithCallback(JsVar *var, JSONFlags flags, vcbprintf_callback user_callback, void *user_data) { assert(jsvIsFunction(var)); JsVar *codeVar = 0; // TODO: this should really be in jsvAsString JsvObjectIterator it; jsvObjectIteratorNew(&it, var); bool firstParm = true; cbprintf(user_callback, user_data, "("); while (jsvObjectIteratorHasValue(&it)) { JsVar *child = jsvObjectIteratorGetKey(&it); if (jsvIsFunctionParameter(child)) { if (firstParm) firstParm=false; else cbprintf(user_callback, user_data, ","); JsVar *name = jsvNewFromStringVar(child, 1, JSVAPPENDSTRINGVAR_MAXLENGTH); cbprintf(user_callback, user_data, "%v", name); jsvUnLock(name); } else if (jsvIsString(child) && jsvIsStringEqual(child, JSPARSE_FUNCTION_CODE_NAME)) { codeVar = jsvObjectIteratorGetValue(&it); } jsvUnLock(child); jsvObjectIteratorNext(&it); } jsvObjectIteratorFree(&it); cbprintf(user_callback, user_data, ") "); if (jsvIsNative(var)) { cbprintf(user_callback, user_data, "{ [native code] }"); } else { if (codeVar) { if (flags & JSON_LIMIT) { cbprintf(user_callback, user_data, "{%s}", JSON_LIMIT_TEXT); } else { bool hasNewLine = jsvGetStringIndexOf(codeVar,'\n')>=0; user_callback(hasNewLine?"{\n ":"{", user_data); if (jsvIsFunctionReturn(var)) user_callback("return ", user_data); // reconstruct the tokenised output into something more readable char buf[32]; unsigned char lastch = 0; JsvStringIterator it; jsvStringIteratorNew(&it, codeVar, 0); while (jsvStringIteratorHasChar(&it)) { unsigned char ch = (unsigned char)jsvStringIteratorGetChar(&it); if (jslNeedSpaceBetween(lastch, ch)) user_callback(" ", user_data); jslFunctionCharAsString(ch, buf, sizeof(buf)); user_callback(buf, user_data); jsvStringIteratorNext(&it); lastch = ch; } jsvStringIteratorFree(&it); user_callback(hasNewLine?"\n}":"}", user_data); } } else cbprintf(user_callback, user_data, "{}"); } jsvUnLock(codeVar); }