mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Shell and Terminal API
This commit is contained in:
parent
b078357392
commit
ef532b3eef
@ -666,6 +666,7 @@ INPUT = ../libraries/LCD \
|
|||||||
../libraries/Crypto \
|
../libraries/Crypto \
|
||||||
../libraries/RingOscillatorNoiseSource \
|
../libraries/RingOscillatorNoiseSource \
|
||||||
../libraries/TransistorNoiseSource \
|
../libraries/TransistorNoiseSource \
|
||||||
|
../libraries/Terminal \
|
||||||
.
|
.
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
|
304
gen/genkeymap.cpp
Normal file
304
gen/genkeymap.cpp
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* 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 THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Generates the keymap state machine table for the Terminal class.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "../libraries/Terminal/USBKeysExtra.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *sequence;
|
||||||
|
int code;
|
||||||
|
} EscKey;
|
||||||
|
static EscKey const escKeys[] = {
|
||||||
|
// Based on the key sequence tables from:
|
||||||
|
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
|
||||||
|
// http://aperiodic.net/phil/archives/Geekery/term-function-keys.html
|
||||||
|
// Also some miscellaneous codes from other sources like the Linux console.
|
||||||
|
|
||||||
|
// Cursor control keys.
|
||||||
|
{"[A", KEY_UP_ARROW},
|
||||||
|
{"OA", KEY_UP_ARROW},
|
||||||
|
{"A", KEY_UP_ARROW},
|
||||||
|
{"[B", KEY_DOWN_ARROW},
|
||||||
|
{"OB", KEY_DOWN_ARROW},
|
||||||
|
{"B", KEY_DOWN_ARROW},
|
||||||
|
{"[C", KEY_RIGHT_ARROW},
|
||||||
|
{"OC", KEY_RIGHT_ARROW},
|
||||||
|
{"C", KEY_RIGHT_ARROW},
|
||||||
|
{"[D", KEY_LEFT_ARROW},
|
||||||
|
{"OD", KEY_LEFT_ARROW},
|
||||||
|
{"D", KEY_LEFT_ARROW},
|
||||||
|
{"[H", KEY_HOME},
|
||||||
|
{"OH", KEY_HOME},
|
||||||
|
{"[1~", KEY_HOME},
|
||||||
|
{"[F", KEY_END},
|
||||||
|
{"OF", KEY_END},
|
||||||
|
{"[4~", KEY_END},
|
||||||
|
{"[2~", KEY_INSERT},
|
||||||
|
{"[3~", KEY_DELETE},
|
||||||
|
{"[5~", KEY_PAGE_UP},
|
||||||
|
{"[6~", KEY_PAGE_DOWN},
|
||||||
|
|
||||||
|
// Numeric keypad. Mostly mapped back to ASCII.
|
||||||
|
{"O ", ' '},
|
||||||
|
{"? ", ' '},
|
||||||
|
{"OI", KEY_TAB},
|
||||||
|
{"?I", KEY_TAB},
|
||||||
|
{"OM", KEY_RETURN},
|
||||||
|
{"?M", KEY_RETURN},
|
||||||
|
{"Oj", '*'},
|
||||||
|
{"?j", '*'},
|
||||||
|
{"Ok", '+'},
|
||||||
|
{"?k", '+'},
|
||||||
|
{"Ol", ','},
|
||||||
|
{"?l", ','},
|
||||||
|
{"Om", '-'},
|
||||||
|
{"?m", '-'},
|
||||||
|
{"On", '.'},
|
||||||
|
{"?n", '.'},
|
||||||
|
{"Oo", '/'},
|
||||||
|
{"?o", '/'},
|
||||||
|
{"Op", '0'},
|
||||||
|
{"?p", '0'},
|
||||||
|
{"Oq", '1'},
|
||||||
|
{"?q", '1'},
|
||||||
|
{"Or", '2'},
|
||||||
|
{"?r", '2'},
|
||||||
|
{"Os", '3'},
|
||||||
|
{"?s", '3'},
|
||||||
|
{"Ot", '4'},
|
||||||
|
{"?t", '4'},
|
||||||
|
{"Ou", '5'},
|
||||||
|
{"?u", '5'},
|
||||||
|
{"Ov", '6'},
|
||||||
|
{"?v", '6'},
|
||||||
|
{"Ow", '7'},
|
||||||
|
{"?w", '7'},
|
||||||
|
{"Ox", '8'},
|
||||||
|
{"?x", '8'},
|
||||||
|
{"Oy", '9'},
|
||||||
|
{"?y", '9'},
|
||||||
|
{"OX", '='},
|
||||||
|
{"?X", '='},
|
||||||
|
|
||||||
|
// Function keys.
|
||||||
|
{"[11~", KEY_F1},
|
||||||
|
{"P", KEY_F1},
|
||||||
|
{"OP", KEY_F1},
|
||||||
|
{"[[A", KEY_F1},
|
||||||
|
{"[12~", KEY_F2},
|
||||||
|
{"Q", KEY_F2},
|
||||||
|
{"OQ", KEY_F2},
|
||||||
|
{"[[B", KEY_F2},
|
||||||
|
{"[13~", KEY_F3},
|
||||||
|
{"R", KEY_F3},
|
||||||
|
{"OR", KEY_F3},
|
||||||
|
{"[[C", KEY_F3},
|
||||||
|
{"[14~", KEY_F4},
|
||||||
|
{"S", KEY_F4},
|
||||||
|
{"OS", KEY_F4},
|
||||||
|
{"[[D", KEY_F4},
|
||||||
|
{"[15~", KEY_F5},
|
||||||
|
{"[[E", KEY_F5},
|
||||||
|
{"[17~", KEY_F6},
|
||||||
|
{"[18~", KEY_F7},
|
||||||
|
{"[19~", KEY_F8},
|
||||||
|
{"[20~", KEY_F9},
|
||||||
|
{"[21~", KEY_F10},
|
||||||
|
{"[23~", KEY_F11},
|
||||||
|
{"[24~", KEY_F12},
|
||||||
|
{"[25~", KEY_F13},
|
||||||
|
{"[11;2~", KEY_F13},
|
||||||
|
{"O2P", KEY_F13},
|
||||||
|
{"[26~", KEY_F14},
|
||||||
|
{"[12;2~", KEY_F14},
|
||||||
|
{"O2Q", KEY_F14},
|
||||||
|
{"[28~", KEY_F15},
|
||||||
|
{"[13;2~", KEY_F15},
|
||||||
|
{"O2R", KEY_F15},
|
||||||
|
{"[29~", KEY_F16},
|
||||||
|
{"[14;2~", KEY_F16},
|
||||||
|
{"O2S", KEY_F16},
|
||||||
|
{"[31~", KEY_F17},
|
||||||
|
{"[15;2~", KEY_F17},
|
||||||
|
{"[32~", KEY_F18},
|
||||||
|
{"[17;2~", KEY_F18},
|
||||||
|
{"[33~", KEY_F19},
|
||||||
|
{"[18;2~", KEY_F19},
|
||||||
|
{"[34~", KEY_F20},
|
||||||
|
{"[19;2~", KEY_F20},
|
||||||
|
{"[20;2~", KEY_F21},
|
||||||
|
{"[23$", KEY_F21},
|
||||||
|
{"[21;2~", KEY_F22},
|
||||||
|
{"[24$", KEY_F22},
|
||||||
|
{"[23;2~", KEY_F23},
|
||||||
|
{"[11^", KEY_F23},
|
||||||
|
{"[24;2~", KEY_F24},
|
||||||
|
{"[12^", KEY_F24},
|
||||||
|
|
||||||
|
// Other keys.
|
||||||
|
{"[Z", KEY_BACK_TAB},
|
||||||
|
{"OZ", KEY_BACK_TAB},
|
||||||
|
{"[P", KEY_PAUSE},
|
||||||
|
{"[G", KEY_NUMPAD_5},
|
||||||
|
};
|
||||||
|
#define numEscKeys (sizeof(escKeys) / sizeof(escKeys[0]))
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Node(Node *parent = 0);
|
||||||
|
~Node();
|
||||||
|
|
||||||
|
void add(const char *str, int code);
|
||||||
|
void dumpRules(std::vector<uint8_t> *vec);
|
||||||
|
|
||||||
|
int ch;
|
||||||
|
int code;
|
||||||
|
int offset;
|
||||||
|
Node *firstChild;
|
||||||
|
Node *lastChild;
|
||||||
|
Node *nextChild;
|
||||||
|
};
|
||||||
|
|
||||||
|
Node::Node(Node *parent)
|
||||||
|
: ch(0)
|
||||||
|
, code(-1)
|
||||||
|
, offset(0)
|
||||||
|
, firstChild(0)
|
||||||
|
, lastChild(0)
|
||||||
|
, nextChild(0)
|
||||||
|
{
|
||||||
|
if (parent) {
|
||||||
|
if (parent->lastChild)
|
||||||
|
parent->lastChild->nextChild = this;
|
||||||
|
else
|
||||||
|
parent->firstChild = this;
|
||||||
|
parent->lastChild = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::~Node()
|
||||||
|
{
|
||||||
|
Node *child = firstChild;
|
||||||
|
Node *next;
|
||||||
|
while (child != 0) {
|
||||||
|
next = child->nextChild;
|
||||||
|
delete child;
|
||||||
|
child = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::add(const char *str, int code)
|
||||||
|
{
|
||||||
|
int ch = str[0] & 0xFF;
|
||||||
|
Node *child = firstChild;
|
||||||
|
while (child != 0) {
|
||||||
|
if (child->ch == ch)
|
||||||
|
break;
|
||||||
|
child = child->nextChild;
|
||||||
|
}
|
||||||
|
if (!child) {
|
||||||
|
child = new Node(this);
|
||||||
|
child->ch = ch;
|
||||||
|
}
|
||||||
|
if (str[1] == '\0') {
|
||||||
|
// Leaf node at the end of a string.
|
||||||
|
child->code = code;
|
||||||
|
} else {
|
||||||
|
// Interior node with more children.
|
||||||
|
child->add(str + 1, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::dumpRules(std::vector<uint8_t> *vec)
|
||||||
|
{
|
||||||
|
Node *child;
|
||||||
|
|
||||||
|
// First pass: Output the recognizers for this level.
|
||||||
|
offset = vec->size();
|
||||||
|
child = firstChild;
|
||||||
|
while (child != 0) {
|
||||||
|
if (child->firstChild) {
|
||||||
|
// Interior nodes need 3 bytes for the character and offset.
|
||||||
|
vec->push_back((uint8_t)(child->ch | 0x80));
|
||||||
|
vec->push_back(0);
|
||||||
|
vec->push_back(0);
|
||||||
|
} else {
|
||||||
|
// Leaf nodes need 2 bytes for the character and code.
|
||||||
|
vec->push_back((uint8_t)(child->ch));
|
||||||
|
vec->push_back((uint8_t)(child->code));
|
||||||
|
if (child->code > 255)
|
||||||
|
printf("Code 0x%X exceeds 255 - need to change table format\n", child->code);
|
||||||
|
}
|
||||||
|
child = child->nextChild;
|
||||||
|
}
|
||||||
|
vec->push_back(0); // Terminate this level.
|
||||||
|
|
||||||
|
// Second pass: Output the recognizers for the child levels.
|
||||||
|
child = firstChild;
|
||||||
|
while (child != 0) {
|
||||||
|
if (child->firstChild)
|
||||||
|
child->dumpRules(vec);
|
||||||
|
child = child->nextChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third pass: Back-patch the links to the child recognizers.
|
||||||
|
int posn = offset;
|
||||||
|
child = firstChild;
|
||||||
|
while (child != 0) {
|
||||||
|
if (child->firstChild) {
|
||||||
|
int value = child->offset;
|
||||||
|
(*vec)[posn + 1] = (uint8_t)value;
|
||||||
|
(*vec)[posn + 2] = (uint8_t)(value >> 8);
|
||||||
|
posn += 3;
|
||||||
|
} else {
|
||||||
|
posn += 2;
|
||||||
|
}
|
||||||
|
child = child->nextChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
Node *root = new Node();
|
||||||
|
for (unsigned index = 0; index < numEscKeys; ++index)
|
||||||
|
root->add(escKeys[index].sequence, escKeys[index].code);
|
||||||
|
std::vector<uint8_t> vec;
|
||||||
|
root->dumpRules(&vec);
|
||||||
|
printf("static uint8_t const keymap[%d] PROGMEM = {\n", (int)vec.size());
|
||||||
|
for (unsigned index = 0; index < vec.size(); ++index) {
|
||||||
|
if ((index % 12) == 0)
|
||||||
|
printf(" ");
|
||||||
|
printf("0x%02X", (int)(vec[index]));
|
||||||
|
if ((index % 12) == 11)
|
||||||
|
printf(",\n");
|
||||||
|
else
|
||||||
|
printf(", ");
|
||||||
|
}
|
||||||
|
printf("};\n");
|
||||||
|
delete root;
|
||||||
|
return 0;
|
||||||
|
}
|
235
gen/genwcwidth.c
Normal file
235
gen/genwcwidth.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* 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 THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Generates the Terminal::isWideCharacter() function from the data at:
|
||||||
|
// http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
|
||||||
|
// http://www.unicode.org/reports/tr11/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_UNICODE 0x10FFFF
|
||||||
|
#define NUM_UNICODE (MAX_UNICODE + 1)
|
||||||
|
|
||||||
|
static unsigned char *masks;
|
||||||
|
|
||||||
|
static void mark_range(long code1, long code2)
|
||||||
|
{
|
||||||
|
while (code1 <= code2) {
|
||||||
|
if (code1 > MAX_UNICODE)
|
||||||
|
break;
|
||||||
|
masks[code1 / 8] |= (1 << (code1 % 8));
|
||||||
|
++code1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unmark_range(long code1, long code2)
|
||||||
|
{
|
||||||
|
while (code1 <= code2) {
|
||||||
|
if (code1 > MAX_UNICODE)
|
||||||
|
break;
|
||||||
|
masks[code1 / 8] &= ~(1 << (code1 % 8));
|
||||||
|
++code1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_ranges(void)
|
||||||
|
{
|
||||||
|
long code;
|
||||||
|
int index, sum;
|
||||||
|
unsigned char *prevptr = 0;
|
||||||
|
unsigned char *ptr;
|
||||||
|
int dotdot = 0;
|
||||||
|
for (code = 0; code <= MAX_UNICODE; code += 0x100) {
|
||||||
|
ptr = masks + (code / 8);
|
||||||
|
sum = 0;
|
||||||
|
for (index = 0; index < 32; ++index)
|
||||||
|
sum += ptr[index];
|
||||||
|
if (sum == 0 || sum == (0xFF * 32)) {
|
||||||
|
if (prevptr && !memcmp(ptr, prevptr, 32)) {
|
||||||
|
if (!dotdot) {
|
||||||
|
dotdot = 1;
|
||||||
|
printf("..\n");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dotdot = 0;
|
||||||
|
printf("%06lX: ", code);
|
||||||
|
for (index = 0; index < 32; ++index)
|
||||||
|
printf("%02X", ptr[index]);
|
||||||
|
printf("\n");
|
||||||
|
prevptr = ptr;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_lookup_table(const char *name, long first, long last)
|
||||||
|
{
|
||||||
|
long index, size;
|
||||||
|
unsigned char *ptr = masks + first / 8;
|
||||||
|
size = (last - first + 1) / 8;
|
||||||
|
printf(" static unsigned char const %s[%ld] PROGMEM = {\n", name, size);
|
||||||
|
for (index = 0; index < size; ++index) {
|
||||||
|
if ((index % 8) == 0)
|
||||||
|
printf(" ");
|
||||||
|
printf("0x%02X", ptr[index]);
|
||||||
|
if (index < (size - 1)) {
|
||||||
|
if ((index % 8) == 7)
|
||||||
|
printf(",\n");
|
||||||
|
else
|
||||||
|
printf(", ");
|
||||||
|
} else {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(" };\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recognizer(void)
|
||||||
|
{
|
||||||
|
long code;
|
||||||
|
int first = 1;
|
||||||
|
|
||||||
|
printf("bool Terminal::isWideCharacter(long code)\n{\n");
|
||||||
|
printf(" // This function was automatically generated by genwcwidth.c\n");
|
||||||
|
print_lookup_table("range3000", 0x3000, 0x30FF);
|
||||||
|
print_lookup_table("rangeFE00", 0xFE00, 0xFFFF);
|
||||||
|
printf(" unsigned c;\n");
|
||||||
|
|
||||||
|
// Bail out early for Latin character sets.
|
||||||
|
printf(" if (code < 0x2300) {\n");
|
||||||
|
printf(" return false;\n");
|
||||||
|
|
||||||
|
// Densely populated ranges.
|
||||||
|
printf(" } else if (code >= 0x3000 && code <= 0x30FF) {\n");
|
||||||
|
printf(" c = (unsigned)(code - 0x3000);\n");
|
||||||
|
printf(" return (pgm_read_byte(range3000 + (c / 8)) & (1 << (c %% 8))) != 0;\n");
|
||||||
|
printf(" } else if (code >= 0xFE00 && code <= 0xFFFF) {\n");
|
||||||
|
printf(" c = (unsigned)(code - 0xFE00);\n");
|
||||||
|
printf(" return (pgm_read_byte(rangeFE00 + (c / 8)) & (1 << (c %% 8))) != 0;\n");
|
||||||
|
|
||||||
|
// Deal with the main wide character ranges.
|
||||||
|
printf(" } else if (code >= 0x3400 && code <= 0x4DBF) {\n");
|
||||||
|
printf(" return true;\n");
|
||||||
|
printf(" } else if (code >= 0x4E00 && code <= 0x9FFF) {\n");
|
||||||
|
printf(" return true;\n");
|
||||||
|
printf(" } else if (code >= 0xF900 && code <= 0xFAFF) {\n");
|
||||||
|
printf(" return true;\n");
|
||||||
|
printf(" } else if (code >= 0x20000 && code <= 0x2FFFD) {\n");
|
||||||
|
printf(" return true;\n");
|
||||||
|
printf(" } else if (code >= 0x30000 && code <= 0x3FFFD) {\n");
|
||||||
|
printf(" return true;\n");
|
||||||
|
printf(" } else if (");
|
||||||
|
|
||||||
|
// Deal with the left-overs.
|
||||||
|
unmark_range(0x3000, 0x30FF);
|
||||||
|
unmark_range(0xFE00, 0xFFFF);
|
||||||
|
for (code = 0; code <= MAX_UNICODE; ++code) {
|
||||||
|
if (masks[code / 8] & (1 << (code % 8))) {
|
||||||
|
if (!first)
|
||||||
|
printf(" ||\n ");
|
||||||
|
else
|
||||||
|
first = 0;
|
||||||
|
printf("code == 0x%04lX", code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf(") {\n");
|
||||||
|
printf(" return true;\n");
|
||||||
|
printf(" }\n");
|
||||||
|
|
||||||
|
printf(" return false;\n");
|
||||||
|
printf("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
FILE *file;
|
||||||
|
char buffer[BUFSIZ];
|
||||||
|
|
||||||
|
// Allocate memory for the "is this a wide character?" mask array.
|
||||||
|
masks = calloc(NUM_UNICODE / 8, sizeof(unsigned char));
|
||||||
|
if (!masks) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the contents of "EastAsianWidth.txt".
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: %s EastAsianWidth.txt\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((file = fopen(argv[1], "r")) == NULL) {
|
||||||
|
perror(argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (fgets(buffer, sizeof(buffer), file)) {
|
||||||
|
if ((buffer[0] >= '0' && buffer[0] <= '9') ||
|
||||||
|
(buffer[0] >= 'A' && buffer[0] <= 'F')) {
|
||||||
|
long code1 = 0;
|
||||||
|
long code2 = 0;
|
||||||
|
char *endptr = NULL;
|
||||||
|
code1 = strtol(buffer, &endptr, 16);
|
||||||
|
if (endptr[0] == '.' && endptr[1] == '.') {
|
||||||
|
endptr += 2;
|
||||||
|
code2 = strtol(buffer, &endptr, 16);
|
||||||
|
} else {
|
||||||
|
code2 = code1;
|
||||||
|
}
|
||||||
|
if (endptr[0] == ';') {
|
||||||
|
// Recognise 'W' and 'F' as wide characters. It is possible
|
||||||
|
// that 'A' (ambiguous) characters may also be wide but only
|
||||||
|
// in East Asian contexts, which we assume we're not for now.
|
||||||
|
if (endptr[1] == 'W' || endptr[1] == 'F') {
|
||||||
|
mark_range(code1, code2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// Some special ranges that are implicitly all-wide even if the
|
||||||
|
// code points aren't currently allocated by the Unicode standard.
|
||||||
|
mark_range(0x3400, 0x4DBF);
|
||||||
|
mark_range(0x4E00, 0x9FFF);
|
||||||
|
mark_range(0xF900, 0xFAFF);
|
||||||
|
mark_range(0x20000, 0x2FFFD);
|
||||||
|
mark_range(0x30000, 0x3FFFD);
|
||||||
|
|
||||||
|
// Dump the ranges.
|
||||||
|
dump_ranges();
|
||||||
|
|
||||||
|
// Unmark the special ranges to make it easier to find the left-overs.
|
||||||
|
unmark_range(0x3400, 0x4DBF);
|
||||||
|
unmark_range(0x4E00, 0x9FFF);
|
||||||
|
unmark_range(0xF900, 0xFAFF);
|
||||||
|
unmark_range(0x20000, 0x2FFFD);
|
||||||
|
unmark_range(0x30000, 0x3FFFD);
|
||||||
|
|
||||||
|
// Create the recognition tree.
|
||||||
|
recognizer();
|
||||||
|
|
||||||
|
// Clean up and exit.
|
||||||
|
free(masks);
|
||||||
|
return 0;
|
||||||
|
}
|
675
libraries/Terminal/Shell.cpp
Normal file
675
libraries/Terminal/Shell.cpp
Normal file
@ -0,0 +1,675 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* 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 THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Shell.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class Shell Shell.h <Shell.h>
|
||||||
|
* \brief Command-line shell access.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \def SHELL_MAX_CMD_LEN
|
||||||
|
* \brief Maximum command length for the shell, including the terminating NUL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \typedef ShellCommandFunc
|
||||||
|
* \brief Type of functions that provide shell command handlers.
|
||||||
|
*
|
||||||
|
* \param shell Points to the shell instance that executed the command,
|
||||||
|
* which can be used to print command results or read more input.
|
||||||
|
* \param argc Number of arguments to the command, including the
|
||||||
|
* command's name.
|
||||||
|
* \param argv The arguments to the command.
|
||||||
|
*
|
||||||
|
* \sa ShellCommand()
|
||||||
|
* \relates Shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \typedef ShellPasswordCheckFunc
|
||||||
|
* \brief Password checking function for login shells.
|
||||||
|
*
|
||||||
|
* \param userid Points to the user identifier that was supplied at login.
|
||||||
|
* \param password Points to the password that was supplied at login.
|
||||||
|
*
|
||||||
|
* \return Returns true if the user identifier and password combination
|
||||||
|
* is correct, false if incorrect.
|
||||||
|
*
|
||||||
|
* Timing can be very important: the check should take the same amount of
|
||||||
|
* time for valid and invalid user identifiers or passwords so that an
|
||||||
|
* attacker cannot gain knowledge about the valid users on the system
|
||||||
|
* based on failed login attempts.
|
||||||
|
*
|
||||||
|
* \relates Shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructs a new Shell instance.
|
||||||
|
*
|
||||||
|
* This constructor must be followed by a call to begin() to specify
|
||||||
|
* the underlying I/O stream.
|
||||||
|
*/
|
||||||
|
Shell::Shell()
|
||||||
|
: maxHistory(0)
|
||||||
|
, curLen(0)
|
||||||
|
, history(0)
|
||||||
|
, historyWrite(0)
|
||||||
|
, historyPosn(0)
|
||||||
|
, prom("> ")
|
||||||
|
, hideChars(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroys this Shell object.
|
||||||
|
*/
|
||||||
|
Shell::~Shell()
|
||||||
|
{
|
||||||
|
delete [] history;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Begin shell handling on an underlying character stream.
|
||||||
|
*
|
||||||
|
* \param stream The stream to apply the shell to. Usually this is a
|
||||||
|
* serial port or TCP network connection.
|
||||||
|
* \param maxHistory The number of commands to allocate in the history
|
||||||
|
* stack for scrolling back through using Up/Down arrow keys.
|
||||||
|
* \return Returns true if the shell was initialized, or false if there
|
||||||
|
* is insufficient memory for the history stack.
|
||||||
|
*
|
||||||
|
* This function will print the prompt() in preparation for entry of
|
||||||
|
* the first command. The default prompt is "> "; call setPrompt()
|
||||||
|
* before begin() to change this:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* Serial.begin(9600);
|
||||||
|
* shell.setPrompt("Command: ");
|
||||||
|
* shell.begin(Serial);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* \sa end(), setPrompt()
|
||||||
|
*/
|
||||||
|
bool Shell::begin(Stream &stream, size_t maxHistory)
|
||||||
|
{
|
||||||
|
// Initialize the Terminal base class with the underlying stream.
|
||||||
|
Terminal::begin(stream);
|
||||||
|
|
||||||
|
// Create the history buffer.
|
||||||
|
this->maxHistory = maxHistory;
|
||||||
|
delete [] history;
|
||||||
|
if (maxHistory) {
|
||||||
|
history = new char [sizeof(buffer) * maxHistory];
|
||||||
|
if (!history) {
|
||||||
|
Terminal::end();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memset(history, 0, sizeof(buffer) * maxHistory);
|
||||||
|
} else {
|
||||||
|
history = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear other variables.
|
||||||
|
curLen = 0;
|
||||||
|
historyWrite = 0;
|
||||||
|
historyPosn = 0;
|
||||||
|
hideChars = false;
|
||||||
|
|
||||||
|
// Print the initial prompt.
|
||||||
|
if (prom)
|
||||||
|
print(prom);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Ends shell processing on the underlying stream.
|
||||||
|
*
|
||||||
|
* This function is intended to be called when a TCP network connection
|
||||||
|
* is closed to clean up the shell state that was in use by the connection.
|
||||||
|
*
|
||||||
|
* \sa begin()
|
||||||
|
*/
|
||||||
|
void Shell::end()
|
||||||
|
{
|
||||||
|
Terminal::end();
|
||||||
|
delete [] history;
|
||||||
|
maxHistory = 0;
|
||||||
|
curLen = 0;
|
||||||
|
history = 0;
|
||||||
|
historyWrite = 0;
|
||||||
|
historyPosn = 0;
|
||||||
|
hideChars = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
|
// Standard command names for use with executeBuiltin().
|
||||||
|
static char const builtin_cmd_exit[] PROGMEM = "exit";
|
||||||
|
static char const builtin_cmd_help[] PROGMEM = "help";
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Performs regular activities on the shell.
|
||||||
|
*
|
||||||
|
* This function must be called regularly from the application's main loop
|
||||||
|
* to process input for the shell.
|
||||||
|
*/
|
||||||
|
void Shell::loop()
|
||||||
|
{
|
||||||
|
// Read the next key and bail out if none. We only process a single
|
||||||
|
// key each time we enter this function to prevent other tasks in the
|
||||||
|
// system from becoming starved of time resources if the bytes are
|
||||||
|
// arriving rapidly from the underyling stream.
|
||||||
|
int key = readKey();
|
||||||
|
if (key == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Process the key.
|
||||||
|
switch (key) {
|
||||||
|
case KEY_BACKSPACE:
|
||||||
|
// Backspace over the last character.
|
||||||
|
clearCharacters(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_RETURN:
|
||||||
|
// CR, LF, or CRLF pressed, so execute the current command.
|
||||||
|
execute();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x15:
|
||||||
|
// CTRL-U - clear the entire command.
|
||||||
|
clearCharacters(curLen);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x04: case 0x1A:
|
||||||
|
// CTRL-D or CTRL-Z - equivalent to the "exit" command.
|
||||||
|
executeBuiltin(builtin_cmd_exit);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_UP_ARROW:
|
||||||
|
// Go back one item in the command history.
|
||||||
|
if (history && historyPosn < maxHistory) {
|
||||||
|
++historyPosn;
|
||||||
|
changeHistory();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_DOWN_ARROW:
|
||||||
|
// Go forward one item in the command history.
|
||||||
|
if (history && historyPosn > 0) {
|
||||||
|
--historyPosn;
|
||||||
|
changeHistory();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_F1:
|
||||||
|
// F1 is equivalent to the "help" command.
|
||||||
|
executeBuiltin(builtin_cmd_help);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_UNICODE: {
|
||||||
|
// Add the Unicode code point to the buffer if it will fit.
|
||||||
|
long code = unicodeKey();
|
||||||
|
size_t size = Terminal::utf8Length(code);
|
||||||
|
if (size && (curLen + size) < (sizeof(buffer) - 1)) {
|
||||||
|
Terminal::utf8Format((uint8_t *)(buffer + curLen), code);
|
||||||
|
if (!hideChars)
|
||||||
|
write((uint8_t *)(buffer + curLen), size);
|
||||||
|
curLen += size;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (key >= 0x20 && key <= 0x7E) {
|
||||||
|
// Printable ASCII character - echo and add it to the buffer.
|
||||||
|
if (curLen < (sizeof(buffer) - 1)) {
|
||||||
|
if (!hideChars)
|
||||||
|
write((uint8_t)key);
|
||||||
|
buffer[curLen++] = (char)key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__AVR__)
|
||||||
|
|
||||||
|
// String compare of two strings in program memory.
|
||||||
|
static int progmem_strcmp(const char *str1, const char *str2)
|
||||||
|
{
|
||||||
|
uint8_t ch1, ch2;
|
||||||
|
for (;;) {
|
||||||
|
ch1 = pgm_read_byte((const uint8_t *)str1);
|
||||||
|
ch2 = pgm_read_byte((const uint8_t *)str2);
|
||||||
|
if (!ch1) {
|
||||||
|
if (ch2)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
} else if (!ch2) {
|
||||||
|
return 1;
|
||||||
|
} else if (ch1 != ch2) {
|
||||||
|
return ((int)ch1) - ((int)ch2);
|
||||||
|
}
|
||||||
|
++str1;
|
||||||
|
++str2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define progmem_strcmp(str1,str2) (strcmp((str1), (str2)))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Reads the "name" field from a command information block in program memory.
|
||||||
|
static const char *readInfoName(const ShellCommandInfo *info)
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
return (const char *)pgm_read_word
|
||||||
|
(((const uint8_t *)info) + offsetof(ShellCommandInfo, name));
|
||||||
|
#else
|
||||||
|
return info->name;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the "help" field from a command information block in program memory.
|
||||||
|
static const char *readInfoHelp(const ShellCommandInfo *info)
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
return (const char *)pgm_read_word
|
||||||
|
(((const uint8_t *)info) + offsetof(ShellCommandInfo, help));
|
||||||
|
#else
|
||||||
|
return info->help;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the "func" field from a command information block in program memory.
|
||||||
|
static ShellCommandFunc readInfoFunc(const ShellCommandInfo *info)
|
||||||
|
{
|
||||||
|
#if defined(__AVR__)
|
||||||
|
if (sizeof(ShellCommandFunc) == 2) {
|
||||||
|
return (ShellCommandFunc)pgm_read_word
|
||||||
|
(((const uint8_t *)info) + offsetof(ShellCommandInfo, func));
|
||||||
|
} else {
|
||||||
|
return (ShellCommandFunc)pgm_read_dword
|
||||||
|
(((const uint8_t *)info) + offsetof(ShellCommandInfo, func));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return info->func;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShellCommandRegister *firstCmd = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Registers a command with the shell.
|
||||||
|
*
|
||||||
|
* \note This function is internal. The ShellCommand() macro should be
|
||||||
|
* used instead.
|
||||||
|
*/
|
||||||
|
void Shell::registerCommand(ShellCommandRegister *cmd)
|
||||||
|
{
|
||||||
|
// Insert the command into the list in alphanumeric order.
|
||||||
|
// We cannot rely upon the construction order to sort the list for us.
|
||||||
|
ShellCommandRegister *prev = 0;
|
||||||
|
ShellCommandRegister *current = firstCmd;
|
||||||
|
while (current != 0) {
|
||||||
|
if (progmem_strcmp(readInfoName(cmd->info), readInfoName(current->info)) < 0)
|
||||||
|
break;
|
||||||
|
prev = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
if (prev)
|
||||||
|
prev->next = cmd;
|
||||||
|
else
|
||||||
|
firstCmd = cmd;
|
||||||
|
cmd->next = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn const char *Shell::prompt() const
|
||||||
|
* \brief Gets the prompt string to display in the shell.
|
||||||
|
*
|
||||||
|
* \return The current prompt. The default is "> ".
|
||||||
|
*
|
||||||
|
* \sa setPrompt()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn void Shell::setPrompt(const char *prompt)
|
||||||
|
* \brief Sets the prompt string to display in the shell.
|
||||||
|
*
|
||||||
|
* \param prompt The new prompt string. The caller is responsible to ensure
|
||||||
|
* that the string persists after this call returns. The Shell class does
|
||||||
|
* not make a copy of the string.
|
||||||
|
*
|
||||||
|
* This function must be called before begin() or the first line will be
|
||||||
|
* prompted with the default of "> ". Afterwards, calling this function
|
||||||
|
* will change the prompt for the following line of input.
|
||||||
|
*
|
||||||
|
* \sa prompt()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn bool Shell::hideCharacters() const
|
||||||
|
* \brief Determine if character echo is hidden.
|
||||||
|
*
|
||||||
|
* \sa setHideCharacters()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Enables or disables character hiding.
|
||||||
|
*
|
||||||
|
* \param hide Set to true to enable character hiding, or false to disable.
|
||||||
|
*
|
||||||
|
* When character hiding is enabled, all characters on a command-line are
|
||||||
|
* hidden and suppressed from being echoed. This is useful for the entry
|
||||||
|
* of passwords and other sensitive information.
|
||||||
|
*
|
||||||
|
* \sa hideCharacters()
|
||||||
|
*/
|
||||||
|
void Shell::setHideCharacters(bool hide)
|
||||||
|
{
|
||||||
|
if (hideChars == hide)
|
||||||
|
return;
|
||||||
|
hideChars = hide;
|
||||||
|
if (hide) {
|
||||||
|
// Hide the current command if something has already been echoed.
|
||||||
|
size_t len = curLen;
|
||||||
|
clearCharacters(len);
|
||||||
|
curLen = len;
|
||||||
|
} else {
|
||||||
|
// Print the current in-progress command to un-hide it.
|
||||||
|
if (curLen)
|
||||||
|
write((const uint8_t *)buffer, curLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Displays help for all supported commands.
|
||||||
|
*/
|
||||||
|
void Shell::help()
|
||||||
|
{
|
||||||
|
// Find the command with the maximum length.
|
||||||
|
ShellCommandRegister *current = firstCmd;
|
||||||
|
size_t maxLen = 0;
|
||||||
|
size_t len;
|
||||||
|
while (current != 0) {
|
||||||
|
len = strlen_P(readInfoName(current->info));
|
||||||
|
if (len > maxLen)
|
||||||
|
maxLen = len;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
maxLen += 2;
|
||||||
|
|
||||||
|
// Print the commands with the help strings aligned on the right.
|
||||||
|
current = firstCmd;
|
||||||
|
while (current != 0) {
|
||||||
|
writeProgMem(readInfoName(current->info));
|
||||||
|
len = maxLen - strlen_P(readInfoName(current->info));
|
||||||
|
while (len > 0) {
|
||||||
|
write(' ');
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
writeProgMem(readInfoHelp(current->info));
|
||||||
|
println();
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Executes the command in the buffer.
|
||||||
|
*/
|
||||||
|
void Shell::execute()
|
||||||
|
{
|
||||||
|
size_t posn = 0;
|
||||||
|
|
||||||
|
// Terminate the current line.
|
||||||
|
println();
|
||||||
|
|
||||||
|
// Make sure the command is properly NUL-terminated.
|
||||||
|
buffer[curLen] = '\0';
|
||||||
|
|
||||||
|
// If we have a history stack and the new command is different from
|
||||||
|
// the previous command, then copy the command into the stack.
|
||||||
|
if (history && curLen > 0) {
|
||||||
|
char *hist = history + sizeof(buffer) * historyWrite;
|
||||||
|
if (strcmp(hist, buffer) != 0) {
|
||||||
|
historyWrite = (historyWrite + 1) % maxHistory;
|
||||||
|
hist = history + sizeof(buffer) * historyWrite;
|
||||||
|
strcpy(hist, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the history read position to the top of the stack.
|
||||||
|
historyPosn = 0;
|
||||||
|
|
||||||
|
// Skip white space at the start of the line.
|
||||||
|
while (posn < curLen && buffer[posn] == ' ')
|
||||||
|
++posn;
|
||||||
|
|
||||||
|
// Break the command up into arguments and populate the argv array.
|
||||||
|
int argc = 0;
|
||||||
|
size_t outposn = 0;
|
||||||
|
char quote = 0;
|
||||||
|
while (posn < curLen) {
|
||||||
|
char ch = buffer[posn];
|
||||||
|
if (ch == ' ') {
|
||||||
|
++posn;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
argv[argc++] = buffer + outposn;
|
||||||
|
do {
|
||||||
|
ch = buffer[posn];
|
||||||
|
if (ch == '"' || ch == '\'') {
|
||||||
|
if (quote == ch) {
|
||||||
|
quote = 0;
|
||||||
|
++posn;
|
||||||
|
continue;
|
||||||
|
} else if (!quote) {
|
||||||
|
quote = ch;
|
||||||
|
++posn;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (!quote && ch == ' ') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[outposn++] = ch;
|
||||||
|
++posn;
|
||||||
|
} while (posn < curLen);
|
||||||
|
buffer[outposn++] = '\0';
|
||||||
|
if (posn < curLen)
|
||||||
|
++posn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the line buffer.
|
||||||
|
curLen = 0;
|
||||||
|
|
||||||
|
// Execute the command.
|
||||||
|
if (argc > 0) {
|
||||||
|
if (!execute(argc, argv)) {
|
||||||
|
// Could not find a matching command, try the builtin "help".
|
||||||
|
if (!strcmp_P(argv[0], builtin_cmd_help)) {
|
||||||
|
help();
|
||||||
|
} else {
|
||||||
|
static char const unknown_cmd[] PROGMEM = "Unknown command: ";
|
||||||
|
writeProgMem(unknown_cmd);
|
||||||
|
print(argv[0]);
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare for the next command.
|
||||||
|
if (prom)
|
||||||
|
print(prom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Executes a command that has been parsed into arguments.
|
||||||
|
*
|
||||||
|
* \param argc The number of elements in \a argv.
|
||||||
|
* \param argv The arguments.
|
||||||
|
*
|
||||||
|
* \return Returns true if the command was found; false if not found.
|
||||||
|
*/
|
||||||
|
bool Shell::execute(int argc, char **argv)
|
||||||
|
{
|
||||||
|
ShellCommandRegister *current = firstCmd;
|
||||||
|
while (current != 0) {
|
||||||
|
if (!strcmp_P(argv[0], readInfoName(current->info))) {
|
||||||
|
ShellCommandFunc func = readInfoFunc(current->info);
|
||||||
|
(*func)(*this, argc, argv);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Executes a builtin command like "exit" or "help".
|
||||||
|
*
|
||||||
|
* \param cmd The command to execute, which must point to program memory.
|
||||||
|
*/
|
||||||
|
void Shell::executeBuiltin(const char *cmd)
|
||||||
|
{
|
||||||
|
clearCharacters(curLen);
|
||||||
|
curLen = strlen_P(cmd);
|
||||||
|
strncpy_P(buffer, cmd, curLen);
|
||||||
|
write((const uint8_t *)buffer, curLen);
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Clears characters from the input line by backspacing over them.
|
||||||
|
*
|
||||||
|
* \param len The number of characters to clear.
|
||||||
|
*/
|
||||||
|
void Shell::clearCharacters(size_t len)
|
||||||
|
{
|
||||||
|
// If the characters are hidden, then there's nothing to backspace over.
|
||||||
|
if (hideChars)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Backspace over all characters in the buffer.
|
||||||
|
while (len > 0 && curLen > 0) {
|
||||||
|
uint8_t ch = (uint8_t)(buffer[curLen - 1]);
|
||||||
|
if (ch < 0x80) {
|
||||||
|
backspace();
|
||||||
|
} else {
|
||||||
|
// UTF-8 character sequence. Back up some more and
|
||||||
|
// determine the value of the Unicode code point.
|
||||||
|
long code = (ch & 0x3F);
|
||||||
|
uint8_t shift = 6;
|
||||||
|
while (curLen > 1) {
|
||||||
|
--curLen;
|
||||||
|
ch = (uint8_t)(buffer[curLen - 1]);
|
||||||
|
if ((ch & 0xC0) != 0x80)
|
||||||
|
break;
|
||||||
|
code |= ((long)(ch & 0x3F)) << shift;
|
||||||
|
shift += 6;
|
||||||
|
}
|
||||||
|
if ((ch & 0xE0) == 0xC0)
|
||||||
|
ch &= 0x1F;
|
||||||
|
else if ((ch & 0xF0) == 0xE0)
|
||||||
|
ch &= 0x0F;
|
||||||
|
else
|
||||||
|
ch &= 0x07;
|
||||||
|
code |= ((long)ch) << shift;
|
||||||
|
|
||||||
|
// If the character is wide, we need to emit two backspaces.
|
||||||
|
if (isWideCharacter(code))
|
||||||
|
backspace();
|
||||||
|
backspace();
|
||||||
|
}
|
||||||
|
--len;
|
||||||
|
--curLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Changes the current command to reflect a different position
|
||||||
|
* in the history stack.
|
||||||
|
*/
|
||||||
|
void Shell::changeHistory()
|
||||||
|
{
|
||||||
|
// Replace the command with the historyPosn item from the stack.
|
||||||
|
// A historyPosn of 1 is the top of the history stack and a
|
||||||
|
// historyPosn of maxHistory is the bottom of the history stack.
|
||||||
|
// A historyPosn of 0 means that the down arrow has navigated
|
||||||
|
// off the history stack, so clear the command only.
|
||||||
|
if (historyPosn) {
|
||||||
|
size_t posn = (historyWrite + maxHistory - (historyPosn - 1)) % maxHistory;
|
||||||
|
char *hist = history + sizeof(buffer) * posn;
|
||||||
|
if (*hist != '\0') {
|
||||||
|
// Copy the line from the history into the command buffer.
|
||||||
|
clearCharacters(curLen);
|
||||||
|
curLen = strlen(hist);
|
||||||
|
memcpy(buffer, hist, curLen);
|
||||||
|
if (!hideChars)
|
||||||
|
write((uint8_t *)hist, curLen);
|
||||||
|
} else {
|
||||||
|
// We've gone too far - the history is still smaller
|
||||||
|
// than maxHistory in size. So reset the position and
|
||||||
|
// don't go any further.
|
||||||
|
--historyPosn;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We've navigated off the history stack.
|
||||||
|
clearCharacters(curLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ShellCommand(name,help,function)
|
||||||
|
* \brief Registers a command with the shell.
|
||||||
|
*
|
||||||
|
* \param name The name of the command.
|
||||||
|
* \param help Help string to display that describes the command.
|
||||||
|
* \param function The function to call to handle the command.
|
||||||
|
*
|
||||||
|
* The \a name and \a help parameters must be constant strings that can
|
||||||
|
* be placed into program memory.
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* void cmdMotor(Shell &shell, int argc, char *argv[])
|
||||||
|
* {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ShellCommand(motor, "Turn the motor on or off", cmdMotor);
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* If there are multiple Shell instances active in the system, then the
|
||||||
|
* command will be registered with all of them.
|
||||||
|
*
|
||||||
|
* \relates Shell
|
||||||
|
*/
|
124
libraries/Terminal/Shell.h
Normal file
124
libraries/Terminal/Shell.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* 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 THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHELL_h
|
||||||
|
#define SHELL_h
|
||||||
|
|
||||||
|
#include "Terminal.h"
|
||||||
|
|
||||||
|
class Shell;
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
#define SHELL_MAX_CMD_LEN 256
|
||||||
|
#else
|
||||||
|
#define SHELL_MAX_CMD_LEN 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*ShellCommandFunc)(Shell &shell, int argc, char *argv[]);
|
||||||
|
typedef bool (*ShellPasswordCheckFunc)(const char *userid, const char *password);
|
||||||
|
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
const char *help;
|
||||||
|
ShellCommandFunc func;
|
||||||
|
|
||||||
|
} ShellCommandInfo;
|
||||||
|
|
||||||
|
class ShellCommandRegister
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline ShellCommandRegister(const ShellCommandInfo *_info);
|
||||||
|
|
||||||
|
const ShellCommandInfo *info;
|
||||||
|
ShellCommandRegister *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
class Shell : public Terminal
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Shell();
|
||||||
|
virtual ~Shell();
|
||||||
|
|
||||||
|
bool begin(Stream &stream, size_t maxHistory = 0);
|
||||||
|
void end();
|
||||||
|
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
static void registerCommand(ShellCommandRegister *cmd);
|
||||||
|
|
||||||
|
const char *prompt() const { return prom; }
|
||||||
|
void setPrompt(const char *prompt) { prom = prompt; }
|
||||||
|
|
||||||
|
bool hideCharacters() const { return hideChars; }
|
||||||
|
void setHideCharacters(bool hide);
|
||||||
|
|
||||||
|
void help();
|
||||||
|
|
||||||
|
private:
|
||||||
|
char buffer[SHELL_MAX_CMD_LEN];
|
||||||
|
char *argv[SHELL_MAX_CMD_LEN / 2];
|
||||||
|
size_t maxHistory;
|
||||||
|
size_t curLen;
|
||||||
|
char *history;
|
||||||
|
size_t historyWrite;
|
||||||
|
size_t historyPosn;
|
||||||
|
const char *prom;
|
||||||
|
bool hideChars;
|
||||||
|
|
||||||
|
// Disable copy constructor and operator=().
|
||||||
|
Shell(const Shell &other) {}
|
||||||
|
Shell &operator=(const Shell &) { return *this; }
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
bool execute(int argc, char **argv);
|
||||||
|
void executeBuiltin(const char *cmd);
|
||||||
|
void clearCharacters(size_t len);
|
||||||
|
void changeHistory();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
|
inline ShellCommandRegister::ShellCommandRegister(const ShellCommandInfo *_info)
|
||||||
|
: info(_info)
|
||||||
|
, next(0)
|
||||||
|
{
|
||||||
|
Shell::registerCommand(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
#define ShellCommand(name,help,function) \
|
||||||
|
static char const shell_id_##name[] PROGMEM = #name; \
|
||||||
|
static char const shell_help_##name[] PROGMEM = help; \
|
||||||
|
static ShellCommandInfo const shell_info_##name PROGMEM = { \
|
||||||
|
shell_id_##name, \
|
||||||
|
shell_help_##name, \
|
||||||
|
(function) \
|
||||||
|
}; \
|
||||||
|
static ShellCommandRegister shell_cmd_##name(&shell_info_##name)
|
||||||
|
|
||||||
|
#endif
|
1179
libraries/Terminal/Terminal.cpp
Normal file
1179
libraries/Terminal/Terminal.cpp
Normal file
File diff suppressed because it is too large
Load Diff
130
libraries/Terminal/Terminal.h
Normal file
130
libraries/Terminal/Terminal.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* 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 THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TERMINAL_h
|
||||||
|
#define TERMINAL_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <USBAPI.h>
|
||||||
|
#include "USBKeysExtra.h"
|
||||||
|
|
||||||
|
// Special key code that indicates that unicodeKey() contains the actual code.
|
||||||
|
#define KEY_UNICODE 0x1000
|
||||||
|
|
||||||
|
class Terminal : public Stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Terminal();
|
||||||
|
virtual ~Terminal();
|
||||||
|
|
||||||
|
void begin(Stream &stream);
|
||||||
|
void end();
|
||||||
|
|
||||||
|
virtual int available();
|
||||||
|
virtual int peek();
|
||||||
|
virtual int read();
|
||||||
|
|
||||||
|
virtual void flush();
|
||||||
|
|
||||||
|
virtual size_t write(uint8_t c);
|
||||||
|
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||||
|
using Stream::write;
|
||||||
|
|
||||||
|
void writeProgMem(const char *str);
|
||||||
|
|
||||||
|
int readKey();
|
||||||
|
|
||||||
|
long unicodeKey() const { return ucode; }
|
||||||
|
|
||||||
|
size_t writeUnicode(long code);
|
||||||
|
|
||||||
|
int columns() const { return ncols; }
|
||||||
|
int rows() const { return nrows; }
|
||||||
|
|
||||||
|
void setWindowSize(int columns, int rows);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void clearToEOL();
|
||||||
|
|
||||||
|
void cursorMove(int x, int y);
|
||||||
|
void cursorLeft();
|
||||||
|
void cursorRight();
|
||||||
|
void cursorUp();
|
||||||
|
void cursorDown();
|
||||||
|
|
||||||
|
void backspace();
|
||||||
|
|
||||||
|
void insertLine();
|
||||||
|
void insertChar();
|
||||||
|
void deleteLine();
|
||||||
|
void deleteChar();
|
||||||
|
|
||||||
|
void scrollUp();
|
||||||
|
void scrollDown();
|
||||||
|
|
||||||
|
void normal();
|
||||||
|
void bold();
|
||||||
|
void underline();
|
||||||
|
void blink();
|
||||||
|
void reverse();
|
||||||
|
|
||||||
|
enum Color
|
||||||
|
{
|
||||||
|
Black = 0x00,
|
||||||
|
DarkRed = 0x01,
|
||||||
|
DarkGreen = 0x02,
|
||||||
|
DarkYellow = 0x03,
|
||||||
|
DarkBlue = 0x04,
|
||||||
|
DarkMagenta = 0x05,
|
||||||
|
DarkCyan = 0x06,
|
||||||
|
LightGray = 0x07,
|
||||||
|
DarkGray = 0x08,
|
||||||
|
Red = 0x09,
|
||||||
|
Green = 0x0A,
|
||||||
|
Yellow = 0x0B,
|
||||||
|
Blue = 0x0C,
|
||||||
|
Magenta = 0x0D,
|
||||||
|
Cyan = 0x0E,
|
||||||
|
White = 0x0F
|
||||||
|
};
|
||||||
|
|
||||||
|
void color(Color fg);
|
||||||
|
void color(Color fg, Color bg);
|
||||||
|
|
||||||
|
static bool isWideCharacter(long code);
|
||||||
|
|
||||||
|
static size_t utf8Length(long code);
|
||||||
|
static size_t utf8Format(uint8_t *buffer, long code);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stream *_stream;
|
||||||
|
long ucode;
|
||||||
|
int ncols, nrows;
|
||||||
|
unsigned long timer;
|
||||||
|
uint16_t offset;
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t utf8len;
|
||||||
|
|
||||||
|
int matchEscape(int ch);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
124
libraries/Terminal/USBKeysExtra.h
Normal file
124
libraries/Terminal/USBKeysExtra.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
|
*
|
||||||
|
* 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 THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef USBKEYSEXTRA_h
|
||||||
|
#define USBKEYSEXTRA_h
|
||||||
|
|
||||||
|
// Extra key codes that are not included in the standard USBAPI.h header.
|
||||||
|
// Reference: http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
|
||||||
|
// Note: USBAPI.h shifts the Hut codes by adding 136 (0x88) so that
|
||||||
|
// they don't intersect with ASCII. We do that here as well so
|
||||||
|
// that these codes can be used with Keyboard.press(). We use #ifndef
|
||||||
|
// here in case the core Arduino libraries add these in the future.
|
||||||
|
|
||||||
|
#ifndef KEY_PRINT_SCREEN
|
||||||
|
#define KEY_PRINT_SCREEN (0x46 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_SCROLL_LOCK
|
||||||
|
#define KEY_SCROLL_LOCK (0x47 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_PAUSE
|
||||||
|
#define KEY_PAUSE (0x48 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_NUM_LOCK
|
||||||
|
#define KEY_NUM_LOCK (0x53 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_NUMPAD_5
|
||||||
|
#define KEY_NUMPAD_5 (0x5D + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F13
|
||||||
|
#define KEY_F13 (0x68 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F14
|
||||||
|
#define KEY_F14 (0x69 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F15
|
||||||
|
#define KEY_F15 (0x6A + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F16
|
||||||
|
#define KEY_F16 (0x6B + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F17
|
||||||
|
#define KEY_F17 (0x6C + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F18
|
||||||
|
#define KEY_F18 (0x6D + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F19
|
||||||
|
#define KEY_F19 (0x6E + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F20
|
||||||
|
#define KEY_F20 (0x6F + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F21
|
||||||
|
#define KEY_F21 (0x70 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F22
|
||||||
|
#define KEY_F22 (0x71 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F23
|
||||||
|
#define KEY_F23 (0x72 + 0x88)
|
||||||
|
#endif
|
||||||
|
#ifndef KEY_F24
|
||||||
|
#define KEY_F24 (0x73 + 0x88)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// USB does not have a code for "Back Tab" as it is usually Shift-TAB.
|
||||||
|
// For convenience, we map it to the ASCII vertical tab character (0x0B).
|
||||||
|
#define KEY_BACK_TAB 0x0B
|
||||||
|
|
||||||
|
#ifndef KEY_RETURN
|
||||||
|
|
||||||
|
// If the Arduino variant does not support USB, then USBAPI.h will not
|
||||||
|
// define the key codes that we need. So we define them here instead.
|
||||||
|
|
||||||
|
#define KEY_RETURN (0x28 + 0x88)
|
||||||
|
#define KEY_ESC (0x29 + 0x88)
|
||||||
|
#define KEY_BACKSPACE (0x2A + 0x88)
|
||||||
|
#define KEY_TAB (0x2B + 0x88)
|
||||||
|
#define KEY_CAPS_LOCK (0x39 + 0x88)
|
||||||
|
#define KEY_F1 (0x3A + 0x88)
|
||||||
|
#define KEY_F2 (0x3B + 0x88)
|
||||||
|
#define KEY_F3 (0x3C + 0x88)
|
||||||
|
#define KEY_F4 (0x3D + 0x88)
|
||||||
|
#define KEY_F5 (0x3E + 0x88)
|
||||||
|
#define KEY_F6 (0x3F + 0x88)
|
||||||
|
#define KEY_F7 (0x40 + 0x88)
|
||||||
|
#define KEY_F8 (0x41 + 0x88)
|
||||||
|
#define KEY_F9 (0x42 + 0x88)
|
||||||
|
#define KEY_F10 (0x43 + 0x88)
|
||||||
|
#define KEY_F11 (0x44 + 0x88)
|
||||||
|
#define KEY_F12 (0x45 + 0x88)
|
||||||
|
#define KEY_INSERT (0x49 + 0x88)
|
||||||
|
#define KEY_HOME (0x4A + 0x88)
|
||||||
|
#define KEY_PAGE_UP (0x4B + 0x88)
|
||||||
|
#define KEY_DELETE (0x4C + 0x88)
|
||||||
|
#define KEY_END (0x4D + 0x88)
|
||||||
|
#define KEY_PAGE_DOWN (0x4E + 0x88)
|
||||||
|
#define KEY_RIGHT_ARROW (0x4F + 0x88)
|
||||||
|
#define KEY_LEFT_ARROW (0x50 + 0x88)
|
||||||
|
#define KEY_DOWN_ARROW (0x51 + 0x88)
|
||||||
|
#define KEY_UP_ARROW (0x52 + 0x88)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
121
libraries/Terminal/examples/Keys/Keys.ino
Normal file
121
libraries/Terminal/examples/Keys/Keys.ino
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
This example tests the Terminal class by displaying the VT100 keys that
|
||||||
|
are pressed. A real terminal program like PuTTY will be needed.
|
||||||
|
|
||||||
|
This example is placed into the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Terminal.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
|
||||||
|
Terminal term;
|
||||||
|
|
||||||
|
#define K(name) {name, #name}
|
||||||
|
struct KeyInfo
|
||||||
|
{
|
||||||
|
uint8_t code;
|
||||||
|
char name[19];
|
||||||
|
};
|
||||||
|
struct KeyInfo const keys[] PROGMEM = {
|
||||||
|
K(KEY_RETURN),
|
||||||
|
K(KEY_ESC),
|
||||||
|
K(KEY_BACKSPACE),
|
||||||
|
K(KEY_TAB),
|
||||||
|
K(KEY_BACK_TAB),
|
||||||
|
K(KEY_CAPS_LOCK),
|
||||||
|
K(KEY_F1),
|
||||||
|
K(KEY_F2),
|
||||||
|
K(KEY_F3),
|
||||||
|
K(KEY_F4),
|
||||||
|
K(KEY_F5),
|
||||||
|
K(KEY_F6),
|
||||||
|
K(KEY_F7),
|
||||||
|
K(KEY_F8),
|
||||||
|
K(KEY_F9),
|
||||||
|
K(KEY_F10),
|
||||||
|
K(KEY_F11),
|
||||||
|
K(KEY_F12),
|
||||||
|
K(KEY_F13),
|
||||||
|
K(KEY_F14),
|
||||||
|
K(KEY_F15),
|
||||||
|
K(KEY_F16),
|
||||||
|
K(KEY_F17),
|
||||||
|
K(KEY_F18),
|
||||||
|
K(KEY_F19),
|
||||||
|
K(KEY_F20),
|
||||||
|
K(KEY_F21),
|
||||||
|
K(KEY_F22),
|
||||||
|
K(KEY_F23),
|
||||||
|
K(KEY_F24),
|
||||||
|
K(KEY_INSERT),
|
||||||
|
K(KEY_HOME),
|
||||||
|
K(KEY_PAGE_UP),
|
||||||
|
K(KEY_DELETE),
|
||||||
|
K(KEY_END),
|
||||||
|
K(KEY_PAGE_DOWN),
|
||||||
|
K(KEY_RIGHT_ARROW),
|
||||||
|
K(KEY_LEFT_ARROW),
|
||||||
|
K(KEY_DOWN_ARROW),
|
||||||
|
K(KEY_UP_ARROW),
|
||||||
|
K(KEY_PRINT_SCREEN),
|
||||||
|
K(KEY_SCROLL_LOCK),
|
||||||
|
K(KEY_PAUSE),
|
||||||
|
K(KEY_NUM_LOCK),
|
||||||
|
K(KEY_NUMPAD_5),
|
||||||
|
{0, ""}
|
||||||
|
};
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
term.begin(Serial);
|
||||||
|
|
||||||
|
term.println("Press keys to see their codes ...");
|
||||||
|
term.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
int key = term.readKey();
|
||||||
|
if (key >= 0x21 && key <= 0x7E) {
|
||||||
|
// Printable ASCII character.
|
||||||
|
term.print("ASCII: ");
|
||||||
|
term.write((uint8_t)key);
|
||||||
|
term.println();
|
||||||
|
} else if (key == 0x20) {
|
||||||
|
// Space.
|
||||||
|
term.println("ASCII: SPACE");
|
||||||
|
} else if (key == KEY_UNICODE) {
|
||||||
|
// Extended Unicode character.
|
||||||
|
term.print("Unicode: U+");
|
||||||
|
term.print(term.unicodeKey(), 16);
|
||||||
|
term.println();
|
||||||
|
} else if (key >= 0 && key <= 0xFF) {
|
||||||
|
// Special arrow or function key.
|
||||||
|
const uint8_t *table = (const uint8_t *)keys;
|
||||||
|
int code;
|
||||||
|
while ((code = pgm_read_byte(table)) != 0) {
|
||||||
|
if (code == key) {
|
||||||
|
term.print("Special: ");
|
||||||
|
term.writeProgMem((const char *)(table + 1));
|
||||||
|
term.println();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
table += sizeof(struct KeyInfo);
|
||||||
|
}
|
||||||
|
if (!code) {
|
||||||
|
// Non-printable ASCII or unknown key.
|
||||||
|
if (key < 0x20)
|
||||||
|
term.print("ASCII: 0x");
|
||||||
|
else
|
||||||
|
term.print("Unknown: 0x");
|
||||||
|
term.print(key, 16);
|
||||||
|
term.println();
|
||||||
|
}
|
||||||
|
} else if (key >= 0) {
|
||||||
|
// Unknown keycode. Print in hex.
|
||||||
|
term.print("Unknown: 0x");
|
||||||
|
term.print(key, 16);
|
||||||
|
term.println();
|
||||||
|
}
|
||||||
|
}
|
35
libraries/Terminal/examples/SerialShell/SerialShell.ino
Normal file
35
libraries/Terminal/examples/SerialShell/SerialShell.ino
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
This example demonstrates how to create a simple shell on the serial port.
|
||||||
|
|
||||||
|
This example is placed into the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Shell.h>
|
||||||
|
|
||||||
|
int ledPin = 13;
|
||||||
|
|
||||||
|
Shell shell;
|
||||||
|
|
||||||
|
void cmdLed(Shell &shell, int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc > 1 && !strcmp(argv[1], "on"))
|
||||||
|
digitalWrite(ledPin, HIGH);
|
||||||
|
else
|
||||||
|
digitalWrite(ledPin, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShellCommand(led, "Turns the status LED on or off", cmdLed);
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
pinMode(ledPin, OUTPUT);
|
||||||
|
|
||||||
|
Serial.begin(9600);
|
||||||
|
shell.setPrompt("Command: ");
|
||||||
|
shell.begin(Serial, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
shell.loop();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user