kolibrios/programs/emulator/dgen-sdl-1.33/sdl/prompt.c

662 lines
17 KiB
C
Raw Normal View History

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "prompt.h"
enum state_type {
TYPE_SPACE, /* between words */
TYPE_WORD, /* on a word */
TYPE_SQUOTE, /* on a word, between simple quotes */
TYPE_DQUOTE, /* on a word, between double quotes */
TYPE_BSLASH, /* after a backslash */
TYPE_BSLHEX1, /* \x, on the 1st hex digit */
TYPE_BSLHEX2, /* \x, on the 2nd hex digit */
TYPE_BSLOCT2, /* \[0-7], on the 2nd octal digit */
TYPE_BSLOCT3, /* \[0-7], on the 3rd octal digit */
TYPE_DQBSLASH, /* between ", after a backslash */
TYPE_DQBSLHEX1, /* between ", \x, on the 1st hex digit */
TYPE_DQBSLHEX2, /* between ", \x, on the 2nd hex digit */
TYPE_DQBSLOCT2, /* between ", \[0-7], on the 2nd octal digit */
TYPE_DQBSLOCT3, /* between ", \[0-7], on the 3rd octal digit */
TYPE_MAX /* number of elements in this enum */
};
enum state_match {
MATCH_SPACE, /* a space */
MATCH_SQUOTE, /* a simple quote */
MATCH_DQUOTE, /* a double quote */
MATCH_BSLASH, /* a backslash */
MATCH_HEX, /* a hex digit */
MATCH_OCT, /* an octal digit */
MATCH_x, /* an 'x' */
MATCH_ANY, /* anything else */
MATCH_MAX /* number of elements in this enum */
};
enum state_action {
ACTION_NONE, /* ignore input */
ACTION_CHAR, /* store character */
ACTION_DIGIT /* store hex or octal digit */
};
struct state {
enum state_type type; /* current state type */
struct {
enum state_action action; /* before going into next state */
enum state_type type; /* next state */
} next[MATCH_MAX];
};
static const struct state prompt_parse_state[TYPE_MAX] = {
{
TYPE_SPACE, /* between words */
{
{ ACTION_NONE, TYPE_SPACE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_SQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_BSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_HEX */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_WORD, /* on a word */
{
{ ACTION_NONE, TYPE_SPACE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_SQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_BSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_HEX */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_SQUOTE, /* on a word, between simple quotes */
{
{ ACTION_CHAR, TYPE_SQUOTE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_WORD }, /* MATCH_SQUOTE */
{ ACTION_CHAR, TYPE_SQUOTE }, /* MATCH_DQUOTE */
{ ACTION_CHAR, TYPE_SQUOTE }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_SQUOTE }, /* MATCH_HEX */
{ ACTION_CHAR, TYPE_SQUOTE }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_SQUOTE }, /* MATCH_x */
{ ACTION_CHAR, TYPE_SQUOTE } /* MATCH_ANY */
}
},
{
TYPE_DQUOTE, /* on a word, between double quotes */
{
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_WORD }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_DQBSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_HEX */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_x */
{ ACTION_CHAR, TYPE_DQUOTE } /* MATCH_ANY */
}
},
{
TYPE_BSLASH, /* after a backslash */
{
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_SQUOTE */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_DQUOTE */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_BSLOCT2 }, /* MATCH_OCT */
{ ACTION_NONE, TYPE_BSLHEX1 }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_BSLHEX1, /* \x, on the first hex digit */
{
{ ACTION_NONE, TYPE_SPACE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_SQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_BSLASH }, /* MATCH_BSLASH */
{ ACTION_DIGIT, TYPE_BSLHEX2 }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_BSLHEX2 }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_BSLHEX2, /* \x, on the second hex digit */
{
{ ACTION_NONE, TYPE_SPACE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_SQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_BSLASH }, /* MATCH_BSLASH */
{ ACTION_DIGIT, TYPE_WORD }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_WORD }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_BSLOCT2, /* \, on the second octal digit */
{
{ ACTION_NONE, TYPE_SPACE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_SQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_BSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_BSLOCT3 }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_BSLOCT3, /* \, on the third octal digit */
{
{ ACTION_NONE, TYPE_SPACE }, /* MATCH_SPACE */
{ ACTION_NONE, TYPE_SQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_BSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_WORD }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_WORD }, /* MATCH_x */
{ ACTION_CHAR, TYPE_WORD } /* MATCH_ANY */
}
},
{
TYPE_DQBSLASH, /* between ", after a backslash */
{
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SQUOTE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_DQUOTE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_DQBSLOCT2 }, /* MATCH_OCT */
{ ACTION_NONE, TYPE_DQBSLHEX1 }, /* MATCH_x */
{ ACTION_CHAR, TYPE_DQUOTE } /* MATCH_ANY */
}
},
{
TYPE_DQBSLHEX1, /* between ", \x, on the 1st hex digit */
{
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_WORD }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_DQBSLASH }, /* MATCH_BSLASH */
{ ACTION_DIGIT, TYPE_DQBSLHEX2 }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_DQBSLHEX2 }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_x */
{ ACTION_CHAR, TYPE_DQUOTE } /* MATCH_ANY */
}
},
{
TYPE_DQBSLHEX2, /* between ", \x, on the 2nd hex digit */
{
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_WORD }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_DQBSLASH }, /* MATCH_BSLASH */
{ ACTION_DIGIT, TYPE_DQUOTE }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_DQUOTE }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_x */
{ ACTION_CHAR, TYPE_DQUOTE } /* MATCH_ANY */
}
},
{
TYPE_DQBSLOCT2, /* between ", \[0-7], on the 2nd octal digit */
{
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_WORD }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_DQBSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_BSLOCT3 }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_x */
{ ACTION_CHAR, TYPE_DQUOTE } /* MATCH_ANY */
}
},
{
TYPE_DQBSLOCT3, /* between ", \[0-7], on the 3rd octal digit */
{
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SPACE */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_SQUOTE */
{ ACTION_NONE, TYPE_WORD }, /* MATCH_DQUOTE */
{ ACTION_NONE, TYPE_DQBSLASH }, /* MATCH_BSLASH */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_HEX */
{ ACTION_DIGIT, TYPE_DQUOTE }, /* MATCH_OCT */
{ ACTION_CHAR, TYPE_DQUOTE }, /* MATCH_x */
{ ACTION_CHAR, TYPE_DQUOTE } /* MATCH_ANY */
}
}
};
void prompt_parse_clean(struct prompt_parse *pp)
{
if (pp->argv != NULL) {
unsigned int i;
for (i = 0; (pp->argv[i] != NULL); ++i)
free(pp->argv[i]);
free(pp->argv);
}
free(pp->args);
free(pp->argo);
memset(pp, 0, sizeof(*pp));
}
struct prompt_parse *prompt_parse(struct prompt *p, struct prompt_parse *pp)
{
struct prompt_history *ph = &p->history[p->current];
enum state_type type;
struct {
unsigned int length;
uint8_t tmp;
unsigned int word: 1;
unsigned int num: 1;
unsigned int last: 1;
} parsing;
uint8_t *s;
unsigned int i;
unsigned int j;
unsigned int k;
unsigned int argc;
unsigned int index;
unsigned int cursor;
enum state_action act;
uint8_t **argv = NULL;
unsigned int *args = NULL;
struct prompt_parse_argo *argo = NULL;
uint8_t *temp = NULL;
size_t size_max = 0;
uint8_t c = '\0';
enum state_type cur = TYPE_SPACE;
enum state_match ma = MATCH_SPACE;
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
argv_ok:
argc = 0;
index = 0;
cursor = ~0u;
memset(&parsing, 0, sizeof(parsing));
type = TYPE_SPACE;
for (s = ph->line, i = 0, j = 0, k = 0; (i != ph->length); ++i) {
cur = type;
c = s[i];
if ((cur == TYPE_BSLASH) || (cur == TYPE_DQBSLASH)) {
switch (c) {
case 'a':
c = '\a';
break;
case 'b':
c = '\b';
break;
case 'f':
c = '\f';
break;
case 'n':
c = '\n';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\v';
break;
}
}
if (c == '\'')
ma = MATCH_SQUOTE;
else if (c == '"')
ma = MATCH_DQUOTE;
else if (c == '\\')
ma = MATCH_BSLASH;
else if (c == 'x')
ma = MATCH_x;
else if (isspace(c))
ma = MATCH_SPACE;
else if ((c >= '0') && (c <= '7') &&
((cur == TYPE_BSLASH) ||
(cur == TYPE_BSLOCT2) ||
(cur == TYPE_BSLOCT3) ||
(cur == TYPE_DQBSLASH) ||
(cur == TYPE_DQBSLOCT2) ||
(cur == TYPE_DQBSLOCT3))) {
ma = MATCH_OCT;
parsing.tmp *= 8;
parsing.tmp += (c - '0');
}
else if ((isxdigit(c)) &&
((cur == TYPE_BSLHEX1) ||
(cur == TYPE_BSLHEX2) ||
(cur == TYPE_DQBSLHEX1) ||
(cur == TYPE_DQBSLHEX2))) {
uint8_t x = c;
ma = MATCH_HEX;
parsing.tmp *= 16;
x &= 0xcf;
x -= (((c >> 6) & 0x01) * 7);
x &= 0x0f;
parsing.tmp += x;
}
else
ma = MATCH_ANY;
if (type == TYPE_SPACE) {
last:
if (i == p->cursor) {
index = argc;
if (type == TYPE_SPACE)
cursor = ~0u;
else
cursor = parsing.length;
}
if (parsing.num) {
if (temp != NULL)
temp[parsing.length] = parsing.tmp;
++(parsing.length);
parsing.num = 0;
parsing.tmp = 0;
}
if (parsing.word) {
if (parsing.length > size_max)
size_max = parsing.length;
if (temp != NULL) {
argv[argc] = malloc
(sizeof(*argv[argc]) *
(parsing.length + 1));
if (argv[argc] == NULL)
goto fail;
memcpy(argv[argc], temp,
parsing.length);
argv[argc][parsing.length] = '\0';
args[argc] = parsing.length;
argo[argc].pos = k;
if (type == TYPE_SPACE)
argo[argc].len = ((i - 1) - k);
else
argo[argc].len = (i - k);
}
++argc;
parsing.length = 0;
parsing.word = 0;
parsing.num = 0;
parsing.tmp = 0;
}
if ((i == p->cursor) && (type == TYPE_SPACE))
index = argc;
if (parsing.last) {
if (temp != NULL) {
argo[argc].pos = p->cursor;
argo[argc].len = 0;
}
goto end;
}
}
act = prompt_parse_state[cur].next[ma].action;
type = prompt_parse_state[cur].next[ma].type;
if (i == p->cursor) {
index = argc;
if ((cur == TYPE_SPACE) && (type == TYPE_SPACE))
cursor = ~0u;
else
cursor = parsing.length;
}
switch (act) {
case ACTION_NONE:
if (parsing.num) {
if (temp != NULL)
temp[parsing.length] = parsing.tmp;
++(parsing.length);
++j;
parsing.num = 0;
parsing.tmp = 0;
}
break;
case ACTION_CHAR:
if (parsing.num) {
if (temp != NULL)
temp[parsing.length] = parsing.tmp;
++(parsing.length);
++j;
parsing.num = 0;
parsing.tmp = 0;
}
if (temp != NULL)
temp[parsing.length] = c;
++(parsing.length);
++j;
break;
case ACTION_DIGIT:
parsing.num = 1;
break;
}
if ((cur == TYPE_SPACE) && (type != TYPE_SPACE)) {
k = i;
parsing.word = 1;
}
}
parsing.last = 1;
goto last;
end:
if (argv == NULL) {
if (((argv = malloc(sizeof(*argv) * (argc + 1))) == NULL) ||
((args = malloc(sizeof(*args) * (argc + 1))) == NULL) ||
((argo = malloc(sizeof(*argo) * (argc + 1))) == NULL) ||
((size_max != 0) &&
((temp = malloc(sizeof(*temp) * size_max)) == NULL))) {
argc = 0;
goto fail;
}
argv[argc] = NULL;
args[argc] = 0;
argo[argc].pos = 0;
argo[argc].len = 0;
goto argv_ok;
}
free(temp);
pp->index = index;
pp->cursor = cursor;
pp->argc = argc;
pp->argv = argv;
pp->args = args;
pp->argo = argo;
return pp;
fail:
free(temp);
if (argv != NULL) {
for (i = 0; (i < argc); ++i)
free(argv[i]);
free(argv);
}
free(args);
free(argo);
return NULL;
}
void prompt_init(struct prompt *p)
{
memset(p, 0, sizeof(*p));
p->entries = 1;
}
void prompt_push(struct prompt *p)
{
struct prompt_history *ph = &p->history[p->current];
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
assert(p->entries <= (sizeof(p->history) / sizeof(p->history[0])));
assert(p->current < p->entries);
/* don't keep empty strings */
if (ph->length == 0)
return;
if (p->entries != (sizeof(p->history) / sizeof(p->history[0])))
++(p->entries);
if (p->current != 0)
p->history[0] = p->history[p->current];
memmove(&p->history[1], &p->history[0],
(sizeof(p->history[0]) * (p->entries - 1)));
memset(&p->history[0], 0, sizeof(p->history[0]));
p->cursor = 0;
p->current = 0;
}
void prompt_older(struct prompt *p)
{
assert(p->entries <= (sizeof(p->history) / sizeof(p->history[0])));
assert(p->current < p->entries);
if (p->current == (p->entries - 1))
return;
++(p->current);
p->cursor = p->history[p->current].length;
assert(p->cursor <= sizeof(p->history[p->current].line));
}
void prompt_newer(struct prompt *p)
{
assert(p->entries <= (sizeof(p->history) / sizeof(p->history[0])));
assert(p->current < p->entries);
if (p->current == 0)
return;
--(p->current);
p->cursor = p->history[p->current].length;
assert(p->cursor <= sizeof(p->history[p->current].line));
}
void prompt_put(struct prompt *p, uint8_t c)
{
struct prompt_history *ph = &p->history[p->current];
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
if (p->cursor == ph->length) {
/* append character */
if (ph->length == sizeof(ph->line))
return;
ph->line[(ph->length++)] = c;
++(p->cursor);
return;
}
/* insert character */
if (ph->length == sizeof(ph->line))
--(ph->length);
memmove(&ph->line[(p->cursor + 1)], &ph->line[p->cursor],
(ph->length - p->cursor));
++(ph->length);
ph->line[(p->cursor++)] = c;
}
void prompt_delete(struct prompt *p)
{
struct prompt_history *ph = &p->history[p->current];
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
if (p->cursor == ph->length)
return;
/* delete character at cursor */
memmove(&ph->line[p->cursor], &ph->line[(p->cursor + 1)],
(ph->length - p->cursor));
--(ph->length);
}
void prompt_replace(struct prompt *p, unsigned int pos,
unsigned int len, const uint8_t *with,
unsigned int with_len)
{
struct prompt_history *ph = &p->history[p->current];
uint8_t *dest;
unsigned int dest_len;
unsigned int dest_maxlen;
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
if (pos > ph->length)
return;
dest = &ph->line[pos];
dest_len = (ph->length - pos);
dest_maxlen = (sizeof(ph->line) - pos);
if (len > dest_len)
len = dest_len;
if (with_len > dest_maxlen)
with_len = dest_maxlen;
if (with_len > len) {
unsigned int inc = (with_len - len);
if ((dest_len + inc) > dest_maxlen)
dest_len = (dest_maxlen - inc);
ph->length += inc;
memmove(&dest[with_len], &dest[len], dest_len);
}
else if (with_len < len) {
unsigned int dec = (len - with_len);
ph->length -= dec;
memmove(&dest[with_len], &dest[len], dest_len);
}
memcpy(dest, with, with_len);
if (p->cursor > ph->length)
p->cursor = ph->length;
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
}
void prompt_left(struct prompt *p)
{
/* move left */
if (p->cursor != 0)
--(p->cursor);
}
void prompt_right(struct prompt *p)
{
struct prompt_history *ph = &p->history[p->current];
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
/* move right */
if (p->cursor != ph->length)
++(p->cursor);
}
void prompt_begin(struct prompt *p)
{
p->cursor = 0;
}
void prompt_end(struct prompt *p)
{
struct prompt_history *ph = &p->history[p->current];
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
p->cursor = ph->length;
}
void prompt_clear(struct prompt *p)
{
struct prompt_history *ph = &p->history[p->current];
assert(ph->length <= sizeof(ph->line));
assert(p->cursor <= ph->length);
ph->length = 0;
p->cursor = 0;
}
void prompt_backspace(struct prompt *p)
{
/* delete previous character */
if (p->cursor != 0) {
prompt_left(p);
prompt_delete(p);
}
}