editable serial input line buffer
Dependents: MAX5715BOB_Tester MAX11131BOB_Tester MAX5171BOB_Tester MAX11410BOB_Tester ... more
CmdLine.cpp
- Committer:
- whismanoid
- Date:
- 2021-10-19
- Revision:
- 17:371f0c5956da
- Parent:
- 16:592df067fe14
- Child:
- 18:2c3c44a34d81
File content as of revision 17:371f0c5956da:
// /******************************************************************************* // * Copyright (C) 2019 Maxim Integrated Products, Inc., All Rights Reserved. // * // * Permission is hereby granted, free of charge, to any person obtaining a // * copy of this software and associated documentation files (the "Software"), // * to deal in the Software without restriction, including without limitation // * the rights to use, copy, modify, merge, publish, distribute, sublicense, // * and/or sell copies of the Software, and to permit persons to whom the // * Software is furnished to do so, subject to the following conditions: // * // * The above copyright notice and this permission notice shall be included // * in all copies or substantial portions of the Software. // * // * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES // * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // * OTHER DEALINGS IN THE SOFTWARE. // * // * Except as contained in this notice, the name of Maxim Integrated // * Products, Inc. shall not be used except as stated in the Maxim Integrated // * Products, Inc. Branding Policy. // * // * The mere transfer of this software does not imply any licenses // * of trade secrets, proprietary technology, copyrights, patents, // * trademarks, maskwork rights, or any other form of intellectual // * property whatsoever. Maxim Integrated Products, Inc. retains all // * ownership rights. // ******************************************************************************* // */ // ********************************************************************* // @file CmdLine.cpp // ********************************************************************* #include "CmdLine.h" // toupper requires include <ctype.h> #include <ctype.h> CmdLine::CmdLine(Stream& AssociatedSerialPort, const char *Name) : associatedSerialPort(AssociatedSerialPort) , name(Name) , onEOLcommandParser() { indexOfNextEmptyCell = 0; memset(buf, 0, COMMAND_BUFFER_LENGTH); chSeparator = 0; match_is_case_sensitive = false; quiet = false; } /** CmdLine::clear empties the command-line buffer */ void CmdLine::clear(void) { indexOfNextEmptyCell = 0; memset(buf, 0, COMMAND_BUFFER_LENGTH); } //void CmdLine::idleAppendIfReadable() //{ // // append ch to buf, unless BS or EOL or other editing character // // Polymorphism fail: associatedSerialPort.readable() // if (associatedSerialPort.readable()) { // append(associatedSerialPort.getc()); // // // // TODO1: set EOL timeout, so that we don't get lingering buffer cruft // // // } //} /** CmdLine::append handles an input character by appending the buffer, * or handling an immediate function like backspace/delete * or other custom immediate motor control functions. */ void CmdLine::append(char ch) { // void diagnostic_led_EOF(); void main_menu_status(CmdLine & cmdLine); // append ch to buf, unless BS or EOL or other editing character switch (ch) { case '\b': // Unicode (U+0008) BS BACKSPACE as destructive backspace case '\x7f': // Unicode (U+007F) DEL DELETE as destructive backspace if (indexOfNextEmptyCell > 0) { buf[--indexOfNextEmptyCell] = '\0'; // pre-decrement index, overwrite with null if (!quiet) associatedSerialPort.printf("\b \b"); // tty: backspace, overwrite with space, backspace } break; case '\r': // Unicode (U+000D) CR CARRIAGE RETURN(CR) as EOL end of line case '\n': // Unicode (U+000A) LF LINE FEED(LF) as EOL end of line //associatedSerialPort.printf("%c", ch); // echo line end if (!quiet) associatedSerialPort.printf("\r\n"); // echo line end //~ associatedSerialPort.printf("\r\n~%s~\r\n", buf); // DIAGNOSTIC: print line buffer // parse and handle the command by invoking onEOLcommandParser callback if (onEOLcommandParser) { onEOLcommandParser(*this); } clear(); break; #define ECHO_EOF_IMMEDIATELY 1 #if ECHO_EOF_IMMEDIATELY case '\x04': // Unicode (U+0004) EOT END OF TRANSMISSION = CTRL+D as EOF end of file if (diagnostic_led_EOF) { diagnostic_led_EOF(); } //~ main_menu_status(*this); if (!quiet) associatedSerialPort.printf("** U+0004 EOT = EOF **"); // immediately echo EOF for test scripting if (diagnostic_led_EOF) { diagnostic_led_EOF(); } if (!quiet) associatedSerialPort.printf("\r\n\x04\r\n"); // immediately echo EOF for test scripting //~ associatedSerialPort.printf("\x1a"); // immediately echo EOF for test scripting //~ associatedSerialPort.printf("\x04"); // immediately echo EOF for test scripting //~ associatedSerialPort.printf("\x1a"); // immediately echo EOF for test scripting clear(); // EOF discard any pending commands, to avoid surprise break; case '\x1a': // Unicode (U+001A) SUB SUBSTITUTE = CTRL+Z as EOF end of file if (diagnostic_led_EOF) { diagnostic_led_EOF(); } //~ main_menu_status(*this); if (!quiet) associatedSerialPort.printf("** U+001A SUB = EOF **"); // immediately echo EOF for test scripting if (diagnostic_led_EOF) { diagnostic_led_EOF(); } if (!quiet) associatedSerialPort.printf("\x1a"); // immediately echo EOF for test scripting if (!quiet) associatedSerialPort.printf("\x04"); // immediately echo EOF for test scripting if (!quiet) associatedSerialPort.printf("\x1a"); // immediately echo EOF for test scripting if (!quiet) associatedSerialPort.printf("\x04"); // immediately echo EOF for test scripting clear(); // EOF discard any pending commands, to avoid surprise break; #endif // // Support commands that get handled immediately w/o waiting for EOL // Avoid using characters that may appear in other commands, // such as 0-9 A-Z a-z and some punctuation %*+-./= // so these 25 characters are available: !"#$&'(),:;<>?@[\]^_`{|}~ //case '!': // immediate command !) example // do_some_immediate_action(); // clear(); // break; //case ' ': // on_immediate_0x20(); // Unicode (U+0020) SPACE // break; // // Note: these on_immediate case may fall through, by design. // #if __GNUC__ // #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" // #endif #if 1 case '!': //~ on_immediate_0x21(); // Unicode (U+0021) ! EXCLAMATION MARK //~ break; // handled as immediate command, do not append to buffer if (on_immediate_0x21) { on_immediate_0x21(); // Unicode (U+0021) ! EXCLAMATION MARK break; // handled as immediate command, do not append to buffer } case '{': //~ on_immediate_0x7b(); // Unicode (U+007B) { LEFT CURLY BRACKET //~ break; // handled as immediate command, do not append to buffer if (on_immediate_0x7b) { on_immediate_0x7b(); // Unicode (U+007B) { LEFT CURLY BRACKET break; // handled as immediate command, do not append to buffer } case '}': //~ on_immediate_0x7d(); // Unicode (U+007D) } RIGHT CURLY BRACKET //~ break; // handled as immediate command, do not append to buffer if (on_immediate_0x7d) { on_immediate_0x7d(); // Unicode (U+007D) } RIGHT CURLY BRACKET break; // handled as immediate command, do not append to buffer } // // default: case '"': if (on_immediate_0x22) { on_immediate_0x22(); // Unicode (U+0022) " QUOTATION MARK break; // handled as immediate command, do not append to buffer } case '#': if (on_immediate_0x23) { on_immediate_0x23(); // Unicode (U+0023) # NUMBER SIGN = pound sign, hash, crosshatch break; // handled as immediate command, do not append to buffer } case '$': if (on_immediate_0x24) { on_immediate_0x24(); // Unicode (U+0024) $ DOLLAR SIGN break; // handled as immediate command, do not append to buffer } //~ on_immediate_0x24(); // Unicode (U+0024) $ DOLLAR SIGN //~ break; // handled as immediate command, do not append to buffer //case '%': // on_immediate_0x25(); // Unicode (U+0025) % PERCENT SIGN // break; case '&': if (on_immediate_0x26) { on_immediate_0x26(); // Unicode (U+0026) & AMPERSAND break; // handled as immediate command, do not append to buffer } case '\'': if (on_immediate_0x27) { on_immediate_0x27(); // Unicode (U+0027) ' APOSTROPHE break; // handled as immediate command, do not append to buffer } case '(': if (on_immediate_0x28) { on_immediate_0x28(); // Unicode (U+0028) ( LEFT PARENTHESIS break; // handled as immediate command, do not append to buffer } case ')': if (on_immediate_0x29) { on_immediate_0x29(); // Unicode (U+0029) ) RIGHT PARENTHESIS break; // handled as immediate command, do not append to buffer } //case '*': // on_immediate_0x2a(); // Unicode (U+002A) * ASTERISK // break; //case '+': // on_immediate_0x2b(); // Unicode (U+002B) + PLUS SIGN // break; case ',': if (on_immediate_0x2c) { on_immediate_0x2c(); // Unicode (U+002C) , COMMA break; // handled as immediate command, do not append to buffer } //case '-': // on_immediate_0x2d(); // Unicode (U+002D) - HYPHEN-MINUS // break; //case '.': // on_immediate_0x2e(); // Unicode (U+002E) . FULL STOP // break; //case '/': // on_immediate_0x2f(); // Unicode (U+002F) / SOLIDUS =SLASH // break; case ':': if (on_immediate_0x3a) { on_immediate_0x3a(); // Unicode (U+003A) : COLON break; // handled as immediate command, do not append to buffer } case ';': if (on_immediate_0x3b) { on_immediate_0x3b(); // Unicode (U+003B) ; SEMICOLON break; // handled as immediate command, do not append to buffer } case '<': if (on_immediate_0x3c) { on_immediate_0x3c(); // Unicode (U+003C) < LESS-THAN SIGN break; // handled as immediate command, do not append to buffer } //case '=': // on_immediate_0x3d(); // Unicode (U+003D) = EQUALS SIGN // break; case '>': if (on_immediate_0x3e) { on_immediate_0x3e(); // Unicode (U+003E) > GREATER-THAN SIGN break; // handled as immediate command, do not append to buffer } case '?': if (on_immediate_0x3f) { on_immediate_0x3f(); // Unicode (U+003F) ? QUESTION MARK break; // handled as immediate command, do not append to buffer } case '@': if (on_immediate_0x40) { on_immediate_0x40(); // Unicode (U+0040) @ COMMERCIAL AT = at sign break; // handled as immediate command, do not append to buffer } case '[': if (on_immediate_0x5b) { on_immediate_0x5b(); // Unicode (U+005B) [ LEFT SQUARE BRACKET break; // handled as immediate command, do not append to buffer } case '\\': if (on_immediate_0x5c) { on_immediate_0x5c(); // Unicode (U+005C) \ REVERSE SOLIDUS break; // handled as immediate command, do not append to buffer } case ']': if (on_immediate_0x5d) { on_immediate_0x5d(); // Unicode (U+005D) ] RIGHT SQUARE BRACKET break; // handled as immediate command, do not append to buffer } case '^': if (on_immediate_0x5e) { on_immediate_0x5e(); // Unicode (U+005E) ^ CIRCUMFLEX ACCENT break; // handled as immediate command, do not append to buffer } case '_': if (on_immediate_0x5f) { on_immediate_0x5f(); // Unicode (U+005F) _ LOW LINE =SPACING UNDERSCORE break; // handled as immediate command, do not append to buffer } case '`': if (on_immediate_0x60) { on_immediate_0x60(); // Unicode (U+0060) ` GRAVE ACCENT (also called backtick) break; // handled as immediate command, do not append to buffer } case '|': if (on_immediate_0x7c) { on_immediate_0x7c(); // Unicode (U+007C) | VERTICAL LINE break; // handled as immediate command, do not append to buffer } case '~': if (on_immediate_0x7e) { on_immediate_0x7e(); // Unicode (U+007E) ~ TILDE break; // handled as immediate command, do not append to buffer } #endif // #if __GNUC__ // #pragma GCC diagnostic pop // #endif // Note: these on_immediate case may fall through, by design. // default: MBED_ASSERT(indexOfNextEmptyCell <= COMMAND_BUFFER_LENGTH - 2); buf[indexOfNextEmptyCell++] = ch; // append character, post-increment index buf[indexOfNextEmptyCell] = '\0'; // null-terminate the buffer MBED_ASSERT(indexOfNextEmptyCell <= COMMAND_BUFFER_LENGTH - 1); if (!quiet) associatedSerialPort.printf("%c", ch); // echo if (indexOfNextEmptyCell == COMMAND_BUFFER_LENGTH - 1) { // buffer is full, parse what we've got if (onEOLcommandParser) { onEOLcommandParser(*this); } clear(); } break; } } /** CmdLine::parse_and_remove_key matches "key" * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] valueBuf buffer is populated with the substring between key= and the first space delimiter (or end of string) * @param[in] valueBufLen limits the size of valueBuf * * @post on successful match, the key=value substring is deleted from cmdbuf * @post chSeparator is populated with the separator character '=' or '?' following key=, or null character if no separator * */ bool CmdLine::parse_and_remove_key(const char *key, char *valueBuf, size_t valueBufLen) { bool verbose = false; if (verbose) { serial().printf("\r\n parse_and_remove_key(\"%s\")...", key); } // match key inside buf[]? for (unsigned int idxSearch = 0; idxSearch < indexOfNextEmptyCell; idxSearch++) { if (buf[idxSearch] == '\0') { if (verbose) { serial().printf("\r\n parse_and_remove_key(\"%s\") no match", key); } return false; /* no match */ } if (match_is_case_sensitive) { // case-sensitive string comparison if (buf[idxSearch] != key[0]) { continue; } } else { // case-insensitive string comparison using toupper() if (toupper(buf[idxSearch]) != toupper(key[0])) { continue; } } // possible match; compare buf[idxSearch..] to key[0..] unsigned int idxKey = idxSearch; // test whether buf[idxKey..] == key[0..] unsigned int idxSeparator = idxSearch; // test whether key=value pair unsigned int idxSpace = idxSearch; // end of key=value word for (unsigned int offset = 0; offset < strlen(key); offset++) { if (match_is_case_sensitive) { // case-sensitive string comparison if (buf[idxKey + offset] != key[offset]) { idxKey = 0; break; } } else { // case-insensitive string comparison using toupper() if (toupper(buf[idxKey + offset]) != toupper(key[offset])) { idxKey = 0; break; } } idxSpace = idxKey + offset + 1; // assume next char is a word break idxSeparator = idxKey + offset + 1; // assume next char is a separator if ((buf[idxSeparator] != '=') && (buf[idxSeparator] != '?')) { idxSeparator = 0; } } if (idxKey == 0) continue; // no match at idxSearch but keep searching // ASSERT buf[idxKey..] == key[0..] while ((buf[idxSpace] != ' ') && idxSpace < indexOfNextEmptyCell) { idxSpace++; } if (verbose) { serial().printf("\r\n parse_and_remove_key(\"%s\") match at index %d length %d, '=' index %d, ' ' index %d", key, idxKey, strlen(key), idxSeparator, idxSpace); } if (idxSeparator != 0) { // found key=value: copy buf[idxSeparator+1..' '-1] into valueBuf[0..valueBufLen-1] chSeparator = buf[idxSeparator]; for (unsigned int offset = 0; offset < valueBufLen - 1; offset++) { if (buf[idxSeparator + 1 + offset] == ' ') break; valueBuf[offset] = buf[idxSeparator + 1 + offset]; valueBuf[offset + 1] = '\0'; } } else { // found key but no =value: valueBuf[] = "" chSeparator = '\0'; valueBuf[0] = '\0'; } // on successful match, the key=value should be deleted from cmdbuf if (verbose) { serial().printf("\r\n parse_and_remove_key(\"%s\") buf=\"%s\" valueBuf=\"%s\" before deleting key", key, buf, valueBuf); } for (unsigned int offset = 0; offset < indexOfNextEmptyCell; offset++) { unsigned int idxCopyDst = idxKey + offset; unsigned int idxCopySrc = idxSpace + 1 + offset; if (idxCopyDst > indexOfNextEmptyCell) break; if (idxCopySrc > indexOfNextEmptyCell) break; buf[idxCopyDst] = buf[idxCopySrc]; } if (verbose) { serial().printf("\r\n parse_and_remove_key(\"%s\") buf=\"%s\" valueBuf=\"%s\" after deleting key", key, buf, valueBuf); serial().printf("\r\n parse_and_remove_key(\"%s\") returning true: valueBuf=\"%s\"", key, valueBuf); } return true; } if (verbose) { serial().printf("\r\n parse_and_remove_key(\"%s\") no match", key); } return false; // no match } /** CmdLine::parse_and_remove_key_and_arrayIndex matches "key" with array indexing * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] valueBuf buffer is populated with the substring between key= and the first space delimiter (or end of string) * @param[in] valueBufLen limits the size of valueBuf * * @post on successful match, the key=value substring is deleted from cmdbuf * @post chSeparator is populated with the separator character '=' or '?' following key=, or null character if no separator * @post *pindex is populated with the array index value * */ bool CmdLine::parse_and_remove_key_and_arrayIndex(const char *key, size_t *pindex, char *valueBuf, size_t valueBufLen) { // WIP #347 - parse_and_remove_key_and_arrayIndex(const char *key, size_t *pindex, char *valueBuf, int valueBufLen) based on parse_and_remove_key // Work in Progress // match (key) "[" (index:[0-9]+) "]" // bool verbose = false; if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\")...", key); } // match key inside buf[]? for (unsigned int idxSearch = 0; idxSearch < indexOfNextEmptyCell; idxSearch++) { if (buf[idxSearch] == '\0') { if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") no match", key); } return false; /* no match */ } if (match_is_case_sensitive) { // case-sensitive string comparison if (buf[idxSearch] != key[0]) { continue; } } else { // case-insensitive string comparison using toupper() if (toupper(buf[idxSearch]) != toupper(key[0])) { continue; } } // possible match; compare buf[idxSearch..] to key[0..] unsigned int idxKey = idxSearch; // test whether buf[idxKey..] == key[0..] unsigned int idxBL = 0; // find such that buf[idxBL] == '[' // bracket left unsigned int idxBR = 0; // find such that buf[idxBR] == ']' // bracket right unsigned int idxSeparator = idxSearch; // test whether key=value pair unsigned int idxSpace = idxSearch; // end of key=value word for (unsigned int offset = 0; offset < strlen(key); offset++) { if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") searching idxKey=%d + offset=%d buf[%d]='%c'", key, idxKey, offset, (idxKey + offset), buf[idxKey + offset]); } // // WIP #347 - handle brackets idxBL '[' idxBR ']' if (buf[idxKey + offset] == '[') { idxBL = idxKey + offset; if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") found '[' at index idxBL=%d", key, idxBL); } } // // WIP #347 - handle brackets idxBL '[' idxBR ']' //if (buf[idxKey + offset] == ']') { // idxBR = idxKey + offset; // if (verbose) { // serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") found ']' at index idxBR=%d", key, idxBR); // } //} // if (match_is_case_sensitive) { // case-sensitive string comparison if (buf[idxKey + offset] != key[offset]) { idxKey = 0; break; } } else { // case-insensitive string comparison using toupper() if (toupper(buf[idxKey + offset]) != toupper(key[offset])) { idxKey = 0; break; } } idxSpace = idxKey + offset + 1; // assume next char is a word break idxSeparator = idxKey + offset + 1; // assume next char is a separator // if ((buf[idxSeparator] != '=') && (buf[idxSeparator] != '?') && (buf[idxSeparator] != '[') && (buf[idxSeparator] != ']')) { idxSeparator = 0; } } if (idxKey == 0) continue; // no match at idxSearch but keep searching // ASSERT buf[idxKey..] == key[0..] while ((buf[idxSpace] != ' ') && idxSpace < indexOfNextEmptyCell) { idxSpace++; } if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") match at index %d length %d, '=' index %d, ' ' index %d", key, idxKey, strlen(key), idxSeparator, idxSpace); serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") buf[idxSeparator=%d]='%c'", key, idxSeparator, buf[idxSeparator]); //~ serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") idxBL=%d idxBR=%d", //~ key, idxBL, idxBR); //~ serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") [ index= %s ]", key, buf[idxBracket+1] ); } // WIP #347 - handle brackets idxBL '[' idxBR ']' if (buf[idxSeparator] == '[') { idxBL = idxSeparator; if (verbose) { if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") key[... continuing search from idxKey=%d + offset=%d buf[%d]='%c'", key, idxKey, idxSeparator + 1, (idxKey + idxSeparator + 1), buf[idxKey + idxSeparator + 1]); } //~ serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") idxBL=%d idxBR=%d", //~ key, idxBL, idxBR); //~ serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") [ index= %s ]", key, buf[idxBracket+1] ); } for (unsigned int idxBRsearch = idxBL + 1; (buf[idxBRsearch] != '\0'); idxBRsearch++) { if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") key[... searching buf[idxBRsearch=%d]='%c'", key, idxBRsearch, buf[idxBRsearch]); } if (buf[idxBRsearch] == '=') { idxSeparator = idxBRsearch; idxSpace = idxBRsearch; break; } else if (buf[idxBRsearch] == ']') { idxBR = idxBRsearch; idxSeparator = idxBRsearch; idxSpace = idxBRsearch; //~ break; } else if (buf[idxBRsearch] == '?') { idxSeparator = idxBRsearch; idxSpace = idxBRsearch; break; } else { idxSeparator = 0; //~ break; } } if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") idxBL=%d idxBR=%d", key, idxBL, idxBR); serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") buf[idxSeparator=%d]='%c'", key, idxSeparator, buf[idxSeparator]); //~ serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") [ index= %s ]", key, buf[idxBracket+1] ); } int arrayIndexValue = strtoul(&(buf[idxBL + 1]), NULL, 10); // default radix decimal if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") arrayIndexValue=%d", key, arrayIndexValue); } if (pindex != NULL) { *pindex = arrayIndexValue; } } if (idxSeparator != 0) { // found key=value: copy buf[idxSeparator+1..' '-1] into valueBuf[0..valueBufLen-1] chSeparator = buf[idxSeparator]; for (unsigned int offset = 0; offset < valueBufLen - 1; offset++) { if (buf[idxSeparator + 1 + offset] == ' ') break; valueBuf[offset] = buf[idxSeparator + 1 + offset]; valueBuf[offset + 1] = '\0'; } } else { // found key but no =value: valueBuf[] = "" chSeparator = '\0'; valueBuf[0] = '\0'; } // on successful match, the key=value should be deleted from cmdbuf if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") *pindex=%d buf=\"%s\" valueBuf=\"%s\" before deleting key", key, *pindex, buf, valueBuf); } for (unsigned int offset = 0; offset < indexOfNextEmptyCell; offset++) { unsigned int idxCopyDst = idxKey + offset; unsigned int idxCopySrc = idxSpace + 1 + offset; if (idxCopyDst > indexOfNextEmptyCell) break; if (idxCopySrc > indexOfNextEmptyCell) break; buf[idxCopyDst] = buf[idxCopySrc]; } if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") *pindex=%d buf=\"%s\" valueBuf=\"%s\" after deleting key", key, *pindex, buf, valueBuf); serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") returning true: valueBuf=\"%s\"", key, valueBuf); } return true; } if (verbose) { serial().printf("\r\n parse_and_remove_key_and_arrayIndex(\"%s\") no match", key); } return false; // no match } /** CmdLine::parse_frequency_Hz matches "key"=digits * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] frequency_Hz updated from value string if match "key"=value, * optional suffix kHz or MHz scales the value by 1000 or 10000000 * * @post on successful match, the key=value substring is deleted from cmdbuf * */ bool CmdLine::parse_frequency_Hz(const char *key, uint32_t& frequency_Hz) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (SCLK=\d+(kHZ|MHZ)?)? --> g_SPI_SCLK_Hz frequency_Hz = strtoul(valueBuf, NULL, 10); if (strstr(valueBuf, "M")) { frequency_Hz = frequency_Hz * 1000000; } if (strstr(valueBuf, "k")) { frequency_Hz = frequency_Hz * 1000; } return true; } return false; // no match } /** CmdLine::parse_interval_usec matches "key"=digits * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] interval_usec updated from value string if match "key"=value, * optional suffix Hz kHz or MHz 1/x inverts and scales the value * optional suffix s or ms or msec or us or usec scales the value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ bool CmdLine::parse_interval_usec(const char *key, us_timestamp_t& interval_usec) { char valueBuf[32]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value interval_usec = strtoul(valueBuf, NULL, 10); if (strstr(valueBuf, "ms")) { interval_usec = interval_usec * 1000; } else if (strstr(valueBuf, "us")) { // avoid matching "s" which is a subset of "us" // interval_usec = interval_usec * 1; } else if (strstr(valueBuf, "s")) { interval_usec = interval_usec * 1000000; } else if (strstr(valueBuf, "MHz")) { interval_usec = 1. / interval_usec; } else if (strstr(valueBuf, "kHz")) { interval_usec = 1000. / interval_usec; } else if (strstr(valueBuf, "Hz")) { interval_usec = 1000000. / interval_usec; } return true; } return false; // no match } /** CmdLine::parse_flag matches "key"=0 or 1 * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] nFlagVar variable to be updated by setting or clearing bits specified by nFlagBitMask. * If match "key"=0 then the nFlagBitMask bits in nFlagVar are cleared. * If match "key"=1 then the nFlagBitMask bits in nFlagVar are set. * @param[in] nFlagBitMask bit mask contains binary 1 in the bit to be controlled by the key=value setting * * @post on successful match, the key=value substring is deleted from cmdbuf * */ bool CmdLine::parse_flag(const char *key, uint8_t& nFlagVar, const uint8_t nFlagBitMask) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CPHA=\d)? --> g_SPI_dataMode | SPI_MODE1 int x = strtoul(valueBuf, NULL, 10); if (x) { nFlagVar |= nFlagBitMask; } else { nFlagVar &= ~nFlagBitMask; } return true; } return false; // no match } /** CmdLine::parse_byte_hex matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] nByteVar updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-16 hexadecimal. */ bool CmdLine::parse_byte_hex(const char *key, uint8_t& nByteVar) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { nByteVar = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { nByteVar = strtoul(valueBuf + 2, NULL, 16); return true; } nByteVar = strtoul(valueBuf, NULL, 16); // default radix hex return true; } return false; // no match } /** CmdLine::parse_byte_dec matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] nByteVar updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_byte_dec(const char *key, uint8_t& nByteVar) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_dec take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { nByteVar = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { nByteVar = strtoul(valueBuf + 2, NULL, 16); return true; } nByteVar = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_byte_dec matches "key"=value with array indexing * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] nByteVar array base pointer * @param[in] arrayIndexLimit array index limit * * @post on successful match, array[index] is updated from value string, and key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_uint8_dec_arrayUpdate(const char *key, uint8_t* nBytearray, size_t arrayIndexLimit) { // WIP #347 - parse_uint8_dec_arrayUpdate(const char *key, /*array=*/&AINcode[0], /*arrayIndexLimit=*/10) based on parse_uint8_dec(key, pValue) // Work in progress // if (parse_and_remove_key_and_arrayIndex(key, &index, valueBuf, sizeof(valueBuf))) // match (key) "[" (index:[0-9]+) "]" // nBytearray[index] = strtoul(valueBuf + 0, NULL, _radix_); // char valueBuf[16]; size_t index; // bool parse_and_remove_key_and_arrayIndex(const char *key, size_t *pindex, char *valueBuf, size_t valueBufLen); if (parse_and_remove_key_and_arrayIndex(key, &index, valueBuf, sizeof(valueBuf))) { if (index >= arrayIndexLimit) { index = arrayIndexLimit-1; } // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_dec take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { nBytearray[index] = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { nBytearray[index] = strtoul(valueBuf + 2, NULL, 16); return true; } nBytearray[index] = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_uint16_hex matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] uint16Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-16 hexadecimal. */ bool CmdLine::parse_uint16_hex(const char *key, uint16_t& uint16Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { uint16Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint16Var = strtoul(valueBuf + 2, NULL, 16); return true; } uint16Var = strtoul(valueBuf, NULL, 16); // default radix hex return true; } return false; // no match } /** CmdLine::parse_uint16_dec matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] uint16Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_uint16_dec(const char *key, uint16_t& uint16Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { uint16Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint16Var = strtoul(valueBuf + 2, NULL, 16); return true; } uint16Var = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_uint16_dec_arrayUpdate matches "key"=value with array indexing * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] uint16Var array base pointer * @param[in] arrayIndexLimit array index limit * * @post on successful match, array[index] is updated from value string, and key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_uint16_dec_arrayUpdate(const char *key, uint16_t* uint16array, size_t arrayIndexLimit) { // WIP #347 - parse_uint16_dec_arrayUpdate(/*name=*/"AINcode", /*array=*/&AINcode[0], /*arrayIndexLimit=*/10) based on parse_uint16_dec(key, pValue) // Work in progress // char valueBuf[16]; size_t index; // bool parse_and_remove_key_and_arrayIndex(const char *key, size_t *pindex, char *valueBuf, size_t valueBufLen); if (parse_and_remove_key_and_arrayIndex(key, &index, valueBuf, sizeof(valueBuf))) { if (index >= arrayIndexLimit) { index = arrayIndexLimit-1; } // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { uint16array[index] = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint16array[index] = strtoul(valueBuf + 2, NULL, 16); return true; } uint16array[index] = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_int_dec matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] intVar updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_int_dec(const char *key, int& intVar) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { intVar = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { intVar = strtoul(valueBuf + 2, NULL, 16); return true; } intVar = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_int16_hex matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] int16Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-16 hexadecimal. */ bool CmdLine::parse_int16_hex(const char *key, int16_t& int16Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { int16Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { int16Var = strtoul(valueBuf + 2, NULL, 16); return true; } int16Var = strtoul(valueBuf, NULL, 16); // default radix hex return true; } return false; // no match } /** CmdLine::parse_int16_dec matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] int16Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_int16_dec(const char *key, int16_t& int16Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { int16Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { int16Var = strtoul(valueBuf + 2, NULL, 16); return true; } int16Var = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_uint32_hex matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] uint32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-16 hexadecimal. */ bool CmdLine::parse_uint32_hex(const char *key, uint32_t& uint32Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { uint32Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint32Var = strtoul(valueBuf + 2, NULL, 16); return true; } uint32Var = strtoul(valueBuf, NULL, 16); // default radix hex return true; } return false; // no match } /** CmdLine::parse_uint32_dec matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] uint32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_uint32_dec(const char *key, uint32_t& uint32Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { uint32Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint32Var = strtoul(valueBuf + 2, NULL, 16); return true; } uint32Var = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_int32_hex matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] int32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-16 hexadecimal. */ bool CmdLine::parse_int32_hex(const char *key, int32_t& int32Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { int32Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { int32Var = strtoul(valueBuf + 2, NULL, 16); return true; } int32Var = strtoul(valueBuf, NULL, 16); // default radix hex return true; } return false; // no match } /** CmdLine::parse_int32_dec matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] int32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * * Prefix '$' or '0x' or '0X' selects radix base-16 hexadecimal. * Default number conversion radix is base-10 decimal. */ bool CmdLine::parse_int32_dec(const char *key, int32_t& int32Var) { char valueBuf[16]; // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { // ASSERT: buf[matched_index] contains '=' followed by value // parse cmdLine arg (CMD=\d)? --> g_I2C_command_regAddress // TODO1: parse_byte_hex take hex prefix 0x 0X or suffix $ h H if (valueBuf[0] == '$') { int32Var = strtoul(valueBuf + 1, NULL, 16); return true; } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { int32Var = strtoul(valueBuf + 2, NULL, 16); return true; } int32Var = strtoul(valueBuf, NULL, 10); // default radix decimal return true; } return false; // no match } /** CmdLine::parse_double_or_int16 matches "key"=value * * Value will be parsed as double if it contains "." or "V", * Otherwise, * Value will be parsed as hexadecimal if it begins with "$" or "0x", * Otherwise, * Value will be parsed as decimal * * @return 0 if keyword not found in buffer; 1: parsed as integer; 2: parsed as double * @param[in] key string value to match * @param[out] doubleVar updated from value string if match "key"=xx.xxx * @param[out] uint32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ uint8_t CmdLine::parse_double_or_int16(const char *key, double& doubleVar, int16_t& int16Var) { char valueBuf[16]; char *end; // buffer required by strtof // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { for (size_t index = 0; valueBuf[index] != '\0' && index < sizeof(valueBuf); index++) { switch(valueBuf[index]) { case '.': case 'V': case 'v': // valueBuf contains decimal "." or letter "V" doubleVar = strtof(valueBuf, &end); return 2; // 2: parsed as double } } if (valueBuf[0] == '$') { int16Var = strtoul(valueBuf + 1, NULL, 16); return 1; // 1: parsed as integer } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { int16Var = strtoul(valueBuf + 2, NULL, 16); return 1; // 1: parsed as integer } int16Var = strtoul(valueBuf, NULL, 10); // default radix decimal return 1; // 1: parsed as integer } return 0; // no match } /** CmdLine::parse_double_or_uint16 matches "key"=value * * Value will be parsed as double if it contains "." or "V", * Otherwise, * Value will be parsed as hexadecimal if it begins with "$" or "0x", * Otherwise, * Value will be parsed as decimal * * @return 0 if keyword not found in buffer; 1: parsed as integer; 2: parsed as double * @param[in] key string value to match * @param[out] doubleVar updated from value string if match "key"=xx.xxx * @param[out] uint32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ uint8_t CmdLine::parse_double_or_uint16(const char *key, double& doubleVar, uint16_t& uint16Var) { char valueBuf[16]; char *end; // buffer required by strtof // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { for (size_t index = 0; valueBuf[index] != '\0' && index < sizeof(valueBuf); index++) { switch(valueBuf[index]) { case '.': case 'V': case 'v': // valueBuf contains decimal "." or letter "V" doubleVar = strtof(valueBuf, &end); return 2; // 2: parsed as double } } if (valueBuf[0] == '$') { uint16Var = strtoul(valueBuf + 1, NULL, 16); return 1; // 1: parsed as integer } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint16Var = strtoul(valueBuf + 2, NULL, 16); return 1; // 1: parsed as integer } uint16Var = strtoul(valueBuf, NULL, 10); // default radix decimal return 1; // 1: parsed as integer } return 0; // no match } /** CmdLine::parse_double_or_int32 matches "key"=value * * Value will be parsed as double if it contains "." or "V", * Otherwise, * Value will be parsed as hexadecimal if it begins with "$" or "0x", * Otherwise, * Value will be parsed as decimal * * @return 0 if keyword not found in buffer; 1: parsed as integer; 2: parsed as double * @param[in] key string value to match * @param[out] doubleVar updated from value string if match "key"=xx.xxx * @param[out] uint32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ uint8_t CmdLine::parse_double_or_int32(const char *key, double& doubleVar, int32_t& int32Var) { char valueBuf[16]; char *end; // buffer required by strtof // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { for (size_t index = 0; valueBuf[index] != '\0' && index < sizeof(valueBuf); index++) { switch(valueBuf[index]) { case '.': case 'V': case 'v': // valueBuf contains decimal "." or letter "V" doubleVar = strtof(valueBuf, &end); return 2; // 2: parsed as double } } if (valueBuf[0] == '$') { int32Var = strtoul(valueBuf + 1, NULL, 16); return 1; // 1: parsed as integer } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { int32Var = strtoul(valueBuf + 2, NULL, 16); return 1; // 1: parsed as integer } int32Var = strtoul(valueBuf, NULL, 10); // default radix decimal return 1; // 1: parsed as integer } return 0; // no match } /** CmdLine::parse_double_or_uint32 matches "key"=value * * Value will be parsed as double if it contains "." or "V", * Otherwise, * Value will be parsed as hexadecimal if it begins with "$" or "0x", * Otherwise, * Value will be parsed as decimal * * @return 0 if keyword not found in buffer; 1: parsed as integer; 2: parsed as double * @param[in] key string value to match * @param[out] doubleVar updated from value string if match "key"=xx.xxx * @param[out] uint32Var updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ uint8_t CmdLine::parse_double_or_uint32(const char *key, double& doubleVar, uint32_t& uint32Var) { char valueBuf[16]; char *end; // buffer required by strtof // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { for (size_t index = 0; valueBuf[index] != '\0' && index < sizeof(valueBuf); index++) { switch(valueBuf[index]) { case '.': case 'V': case 'v': // valueBuf contains decimal "." or letter "V" doubleVar = strtof(valueBuf, &end); return 2; // 2: parsed as double } } if (valueBuf[0] == '$') { uint32Var = strtoul(valueBuf + 1, NULL, 16); return 1; // 1: parsed as integer } if (valueBuf[0] == '0' && (valueBuf[1] == 'X' || valueBuf[1] == 'x')) { uint32Var = strtoul(valueBuf + 2, NULL, 16); return 1; // 1: parsed as integer } uint32Var = strtoul(valueBuf, NULL, 10); // default radix decimal return 1; // 1: parsed as integer } return 0; // no match } /** CmdLine::parse_double matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] doubleVar updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ bool CmdLine::parse_double(const char *key, double& doubleVar) { char valueBuf[16]; char *end; // buffer required by strtof // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { doubleVar = strtof(valueBuf, &end); return true; } return false; // no match } /** CmdLine::parse_float matches "key"=value * * @return true if keyword was found in buffer * @param[in] key string value to match * @param[out] floatVar updated from value string if match "key"=value * * @post on successful match, the key=value substring is deleted from cmdbuf * */ bool CmdLine::parse_float(const char *key, float& floatVar) { char valueBuf[16]; char *end; // buffer required by strtof // bool parse_and_remove_key(const char *key, char *valueBuf, int valueBufLen); if (parse_and_remove_key(key, valueBuf, sizeof(valueBuf))) { floatVar = strtof(valueBuf, &end); return true; } return false; // no match } /** CmdLine::parse_byteCount_byteList_hex matches hexadecimal digits separated by spaces. * The 0x / 0X prefix is optional. The numbers must be hexadecimal. * * @return true if more than one hex byte found in buffer * @param[out] byteCount is populated with the number of hex bytes found * @param[out] mosiDataBuf buffer mosiDataBuf[0..byteCount-1] is populated with the hex bytes found * @param[in] mosiDataBufSize limits the number of bytes that will be used * */ bool CmdLine::parse_byteCount_byteList_hex(size_t& byteCount, char *mosiDataBuf, size_t mosiDataBufSize) { //serial().printf("\r\n parse_byteCount_byteList_hex (buf=\"%s\")...", buf); // parse cmdLine hex byte list --> int byteCount; int mosiData[MAX_SPI_BYTE_COUNT]; byteCount = 0; bool got_value = false; uint8_t nybbleValue; uint8_t temp_value = 0; for (unsigned int idxSearch = 0; idxSearch < indexOfNextEmptyCell; idxSearch++) { if (buf[idxSearch] == '\0') { break; // end of buffer } switch (buf[idxSearch]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': nybbleValue = buf[idxSearch] - '0'; temp_value = temp_value * 0x10; temp_value = temp_value + nybbleValue; got_value = true; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': nybbleValue = buf[idxSearch] - 'a' + 0x0a; temp_value = temp_value * 0x10; temp_value = temp_value + nybbleValue; got_value = true; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': nybbleValue = buf[idxSearch] - 'A' + 0x0a; temp_value = temp_value * 0x10; temp_value = temp_value + nybbleValue; got_value = true; break; case 'x': case 'X': temp_value = 0; break; case ' ': case ',': if ((got_value) && (byteCount < mosiDataBufSize)) { //serial().printf("\r\n parse_byteCount_byteList_hex mosiDataBuf[%d] = 0x%2.2x", byteCount, temp_value); mosiDataBuf[byteCount++] = temp_value; temp_value = 0; got_value = false; } break; } } // for idxSearch if ((got_value) && (byteCount < mosiDataBufSize)) { //serial().printf("\r\n parse_byteCount_byteList_hex mosiDataBuf[%d] = 0x%2.2x", byteCount, temp_value); mosiDataBuf[byteCount++] = temp_value; temp_value = 0; got_value = false; } //serial().printf("\r\n parse_byteCount_byteList_hex (buf=\"%s\") returning: byteCount=%d", buf, byteCount); return (byteCount > 0); } /** CmdLine::parse_byteCount_byteList_dec matches a list of numbers, separated by spaces. * The 0x / 0X prefix may be used to select hexadecimal instead of decimal. * * @return true if more than one number found in buffer * @param[out] byteCount is populated with the number of numbers found * @param[out] mosiDataBuf buffer mosiDataBuf[0..byteCount-1] is populated with the numbers found * @param[in] mosiDataBufSize limits the number of bytes that will be used * */ bool CmdLine::parse_byteCount_byteList_dec(size_t& byteCount, char *mosiDataBuf, size_t mosiDataBufSize) { //serial().printf("\r\n parse_byteCount_byteList_dec (buf=\"%s\")...", buf); // parse cmdLine hex byte list --> int byteCount; int mosiData[MAX_SPI_BYTE_COUNT]; byteCount = 0; bool got_value = false; uint8_t nybbleValue; uint8_t temp_value = 0; uint8_t radix = 10; for (unsigned int idxSearch = 0; idxSearch < indexOfNextEmptyCell; idxSearch++) { if (buf[idxSearch] == '\0') { break; // end of buffer } switch (buf[idxSearch]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': nybbleValue = buf[idxSearch] - '0'; temp_value = temp_value * radix; temp_value = temp_value + nybbleValue; got_value = true; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': nybbleValue = buf[idxSearch] - 'a' + 0x0a; radix = 0x10; temp_value = temp_value * radix; temp_value = temp_value + nybbleValue; got_value = true; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': nybbleValue = buf[idxSearch] - 'A' + 0x0a; radix = 0x10; temp_value = temp_value * radix; temp_value = temp_value + nybbleValue; got_value = true; break; case 'x': case 'X': temp_value = 0; radix = 0x10; break; case ' ': case ',': if ((got_value) && (byteCount < mosiDataBufSize)) { //serial().printf("\r\n parse_byteCount_byteList_dec mosiDataBuf[%d] = 0x%2.2x", byteCount, temp_value); mosiDataBuf[byteCount++] = temp_value; temp_value = 0; radix = 10; got_value = false; } break; } } // for idxSearch if ((got_value) && (byteCount < mosiDataBufSize)) { //serial().printf("\r\n parse_byteCount_byteList_dec mosiDataBuf[%d] = 0x%2.2x", byteCount, temp_value); mosiDataBuf[byteCount++] = temp_value; temp_value = 0; got_value = false; } //serial().printf("\r\n parse_byteCount_byteList_dec (buf=\"%s\") returning: byteCount=%d", buf, byteCount); return (byteCount > 0); } // End of file