#endif
smallint editing; // >0 while we are editing a file
- // [code audit says "can be 0 or 1 only"]
+ // [code audit says "can be 0, 1 or 2 only"]
smallint cmd_mode; // 0=command 1=insert 2=replace
int file_modified; // buffer contents changed (counter, not flag!)
- int last_file_modified; // = -1;
+ int last_file_modified; // = -1;
int fn_start; // index of first cmd line file name
int save_argc; // how many file names on cmd line
int cmdcnt; // repetition count
char *initial_cmds[3]; // currently 2 entries, NULL terminated
#endif
// Should be just enough to hold a key sequence,
- // but CRASME mode uses it as generated command buffer too
+ // but CRASHME mode uses it as generated command buffer too
#if ENABLE_FEATURE_VI_CRASHME
- char readbuffer[128];
+ char readbuffer[128];
#else
- char readbuffer[32];
+ char readbuffer[8];
#endif
#define STATUS_BUFFER_LEN 200
char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
static void screen_erase(void);
static void clear_to_eol(void);
static void clear_to_eos(void);
+static void go_bottom_and_clear_to_eol(void);
static void standout_start(void); // send "start reverse video" sequence
static void standout_end(void); // send "end reverse video" sequence
static void flash(int); // flash the terminal screen
// These are commands that change text[].
// Remember the input for the "." command
if (!adding2q && ioq_start == NULL
- && strchr(modifying_cmds, c)
+ && c != '\0' && strchr(modifying_cmds, c)
) {
start_new_cmd_q(c);
}
}
//-------------------------------------------------------------------
- place_cursor(rows, 0, FALSE); // go to bottom of screen
- clear_to_eol(); // Erase to end of line
+ go_bottom_and_clear_to_eol();
cookmode();
#undef cur_line
}
else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
int retcode;
// :!ls run the <cmd>
- place_cursor(rows - 1, 0, FALSE); // go to Status line
- clear_to_eol(); // clear the line
+ go_bottom_and_clear_to_eol();
cookmode();
retcode = system(orig_buf + 1); // run the cmd
if (retcode)
}
} else if (strncasecmp(cmd, "features", i) == 0) { // what features are available
// print out values of all features
- place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
- clear_to_eol(); // clear the line
+ go_bottom_and_clear_to_eol();
cookmode();
show_help();
rawmode();
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
}
- place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
- clear_to_eol(); // clear the line
+ go_bottom_and_clear_to_eol();
puts("\r");
for (; q <= r; q++) {
int c_is_no_print;
// only blank is regarded as args delmiter. What about tab '\t' ?
if (!args[0] || strcasecmp(args, "all") == 0) {
// print out values of all options
- place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
- clear_to_eol(); // clear the line
+ go_bottom_and_clear_to_eol();
printf("----------------------------------------\r\n");
#if ENABLE_FEATURE_VI_SETOPTS
if (!autoindent)
{
// get buffer for new cmd
// if there is a current cmd count put it in the buffer first
- if (cmdcnt > 0)
+ if (cmdcnt > 0) {
lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
- else { // just save char c onto queue
+ } else { // just save char c onto queue
last_modifying_cmd[0] = c;
lmc_len = 1;
}
//----- Come here when we get a continue signal -------------------
static void cont_sig(int sig UNUSED_PARAM)
{
- rawmode(); // terminal to "raw"
- last_status_cksum = 0; // force status update
- redraw(TRUE); // re-draw the screen
+ rawmode(); // terminal to "raw"
+ last_status_cksum = 0; // force status update
+ redraw(TRUE); // re-draw the screen
signal(SIGTSTP, suspend_sig);
signal(SIGCONT, SIG_DFL);
- kill(my_pid, SIGCONT);
+ kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
}
//----- Come here when we get a Suspend signal -------------------
static void suspend_sig(int sig UNUSED_PARAM)
{
- place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
- clear_to_eol(); // Erase to end of line
- cookmode(); // terminal to "cooked"
+ go_bottom_and_clear_to_eol();
+ cookmode(); // terminal to "cooked"
signal(SIGCONT, cont_sig);
signal(SIGTSTP, SIG_DFL);
{
char c;
int n;
- struct esc_cmds {
+
+ // Known escape sequences for cursor and function keys.
+ static const struct esc_cmds {
const char seq[4];
char val;
- };
-
- static const struct esc_cmds esccmds[] = {
+ } esccmds[] = {
{"OA" , VI_K_UP }, // cursor key Up
{"OB" , VI_K_DOWN }, // cursor key Down
{"OC" , VI_K_RIGHT }, // Cursor Key Right
{"OD" , VI_K_LEFT }, // cursor key Left
{"OH" , VI_K_HOME }, // Cursor Key Home
{"OF" , VI_K_END }, // Cursor Key End
+ {"OP" , VI_K_FUN1 }, // Function Key F1
+ {"OQ" , VI_K_FUN2 }, // Function Key F2
+ {"OR" , VI_K_FUN3 }, // Function Key F3
+ {"OS" , VI_K_FUN4 }, // Function Key F4
+
{"[A" , VI_K_UP }, // cursor key Up
{"[B" , VI_K_DOWN }, // cursor key Down
{"[C" , VI_K_RIGHT }, // Cursor Key Right
{"[4~" , VI_K_END }, // Cursor Key End
{"[5~" , VI_K_PAGEUP }, // Cursor Key Page Up
{"[6~" , VI_K_PAGEDOWN}, // Cursor Key Page Down
- {"OP" , VI_K_FUN1 }, // Function Key F1
- {"OQ" , VI_K_FUN2 }, // Function Key F2
- {"OR" , VI_K_FUN3 }, // Function Key F3
- {"OS" , VI_K_FUN4 }, // Function Key F4
// careful: these have no terminating NUL!
{"[11~", VI_K_FUN1 }, // Function Key F1
{"[12~", VI_K_FUN2 }, // Function Key F2
{"[23~", VI_K_FUN11 }, // Function Key F11
{"[24~", VI_K_FUN12 }, // Function Key F12
};
- enum { ESCCMDS_COUNT = ARRAY_SIZE(esccmds) };
fflush(stdout);
+
n = chars_to_parse;
- // get input from User- are there already input chars in Q?
- if (n <= 0) {
- // the Q is empty, wait for a typed char
- n = safe_read(STDIN_FILENO, readbuffer, sizeof(readbuffer));
- if (n < 0) {
- if (errno == EBADF || errno == EFAULT || errno == EINVAL
- || errno == EIO)
- editing = 0; // want to exit
- errno = 0;
- }
- if (n <= 0)
- return 0; // error
- if (readbuffer[0] == 27) {
- // This is an ESC char. Is this Esc sequence?
- // Could be bare Esc key. See if there are any
- // more chars to read after the ESC. This would
- // be a Function or Cursor Key sequence.
- struct pollfd pfd[1];
- pfd[0].fd = 0;
- pfd[0].events = POLLIN;
- // keep reading while there are input chars, and room in buffer
- // for a complete ESC sequence (assuming 8 chars is enough)
- while ((safe_poll(pfd, 1, 0) > 0)
- && ((size_t)n <= (sizeof(readbuffer) - 8))
- ) {
- // read the rest of the ESC string
- int r = safe_read(STDIN_FILENO, readbuffer + n, sizeof(readbuffer) - n);
- if (r > 0)
- n += r;
- }
+ if (n == 0) {
+ // If no data, block waiting for input.
+ n = safe_read(0, readbuffer, 1);
+ if (n <= 0) {
+ error:
+ go_bottom_and_clear_to_eol();
+ cookmode(); // terminal to "cooked"
+ bb_error_msg_and_die("can't read user input");
}
- chars_to_parse = n;
}
+
+ // Grab character to return from buffer
c = readbuffer[0];
- if (c == 27 && n > 1) {
- // Maybe cursor or function key?
+ // Returning NUL from this routine would be bad.
+ if (c == '\0')
+ c = ' ';
+ n--;
+ if (n) memmove(readbuffer, readbuffer + 1, n);
+
+ // If it's an escape sequence, loop through known matches.
+ if (c == 27) {
const struct esc_cmds *eindex;
- for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
+ for (eindex = esccmds; eindex < esccmds + ARRAY_SIZE(esccmds); eindex++) {
+ // n - position in seq to read
+ int i = 0; // position in seq to compare
int cnt = strnlen(eindex->seq, 4);
- if (n <= cnt)
- continue;
- if (strncmp(eindex->seq, readbuffer + 1, cnt) != 0)
- continue;
- c = eindex->val; // magic char value
- n = cnt + 1; // squeeze out the ESC sequence
- goto found;
- }
- // defined ESC sequence not found
- }
- n = 1;
- found:
- // remove key sequence from Q
- chars_to_parse -= n;
- memmove(readbuffer, readbuffer + n, sizeof(readbuffer) - n);
+
+ // Loop through chars in this sequence.
+ for (;;) {
+ // We've matched this escape sequence up to [i-1]
+ if (n <= i) {
+ // Need more chars, read another one if it wouldn't block.
+ // (Note that escape sequences come in as a unit,
+ // so if we would block it's not really an escape sequence.)
+ struct pollfd pfd;
+ pfd.fd = 0;
+ pfd.events = POLLIN;
+ // Rob needed 300ms timeout on qemu
+ if (safe_poll(&pfd, 1, /*timeout:*/ 300)) {
+ if (safe_read(0, readbuffer + n, 1) <= 0)
+ goto error;
+ n++;
+ } else {
+ // No more data!
+ // Array is sorted from shortest to longest,
+ // we can't match anything later in array,
+ // break out of both loops.
+ goto loop_out;
+ }
+ }
+ if (readbuffer[i] != eindex->seq[i])
+ break; // try next seq
+ if (++i == cnt) { // entire seq matched
+ c = eindex->val;
+ n = 0;
+ goto loop_out;
+ }
+ }
+ }
+ // We did not find matching sequence, it was a bare ESC.
+ // We possibly read and stored more input in readbuffer by now.
+ }
+loop_out:
+
+ chars_to_parse = n;
return c;
}
strcpy(buf, prompt);
last_status_cksum = 0; // force status update
- place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
- clear_to_eol(); // clear the line
+ go_bottom_and_clear_to_eol();
write1(prompt); // write out the :, /, or ? prompt
i = strlen(buf);
static void place_cursor(int row, int col, int optimize)
{
char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
+#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
+ enum {
+ SZ_UP = sizeof(CMup),
+ SZ_DN = sizeof(CMdown),
+ SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
+ };
+ char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
+#endif
char *cm;
if (row < 0) row = 0;
#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
if (optimize && col < 16) {
- enum {
- SZ_UP = sizeof(CMup),
- SZ_DN = sizeof(CMdown),
- SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
- };
- char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
char *screenp;
int Rrow = last_row;
int diff = Rrow - row;
write1(Ceol); // Erase from cursor to end of line
}
+static void go_bottom_and_clear_to_eol(void)
+{
+ place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
+ clear_to_eol(); // erase to end of line
+}
+
//----- Erase from cursor to end of screen -----------------------
static void clear_to_eos(void)
{
}
if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
last_status_cksum = cksum; // remember if we have seen this line
- place_cursor(rows - 1, 0, FALSE); // put cursor on status line
+ go_bottom_and_clear_to_eol();
write1(status_buffer);
- clear_to_eol();
if (have_status_msg) {
if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
(columns - 1) ) {