Line editing.

The program can be started in line editing mode. In this mode, the
current line can be edited by inserting/deleting characters. Escape
values can be used for bytes.

Controls:
    printable   - adds character to the position of the cursor
    RIGHT, LEFT - moves cursor in the line
    UP, DOWN    - gets prevoiusly sent lines from the history
    BACKSPACE   - deletes character before the cursor
    ENTER       - sends line

Commands:
    :?          - list available commands
    :q          - quit
    :v          - show version
    ::          - send ':'

Escapes:
    \dNNN       - decimal NNN       (e.g: \d045      = 45)
    \xNN        - hexadecimal NN    (e.g: \xff       = 255)
    \bNNNNNNNN  - binary NNNNNNNN   (e.g: \b00000001 = 1)

Added option --line-edit, to start the program in line editing mode.
Added option --no-newline-in-line-edit to prevent sending newline
characters.
This commit is contained in:
g0mb4 2021-07-29 15:59:27 +02:00
parent 7fc8e278ed
commit aa9f6435db
10 changed files with 730 additions and 195 deletions

View file

@ -99,7 +99,17 @@ Start in hexadecimal mode.
.TP .TP
.BR \-\-newline-in-hex .BR \-\-newline-in-hex
Interpret new line characters ('\\r', '\\n') in hexadecimal mode. Interpret new line characters ('\er', '\en') in hexadecimal mode.
.TP
.BR \-\-line-edit
Start in line editing mode.
.TP
.BR \-\-no-newline-in-line-edit
Does not append "\er\en" after a line in line editing mode.
.TP .TP
.BR \-v ", " \-\-version .BR \-v ", " \-\-version
@ -150,6 +160,62 @@ By default there is \fBno new line\fR in this mode, but it can be turned on usin
.TP .TP
Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR. Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR.
.SH "LINE EDIT"
The program can be started in line editing mode using the \fB--line-edot\fR option. In this mode, the current line can be edited by inserting/deleting characters. Escape values can be used for bytes.
.PP
.TP 16n
The following keys can be used:
.IP "\fB<ASCII>"
Add character to the position of the cursor.
.IP "\fBRIGHT\fR, \fBLEFT\fR"
Move cursor in the line.
.IP "\fBUP\fR, \fBDOWN\fR"
Get prevoiusly sent lines from the history.
.IP "\fBBACKSPACE\fR"
Delete character before the cursor.
.IP "\fBENTER\fR"
Send line.
.PP
.TP 16n
The following commands can be used:
.IP "\fB:?\fR"
List available commands.
.IP "\fB:q\fR"
Quit.
.IP "\fB:v\fR"
Show version.
.IP "\fB::\fR"
Send ':'.
.PP
.TP 16n
Escape bytes:
.IP \fB\edNNN\fR
Send NNN as a decimal value. NNN must be 3 characters and less than or equal to 255. For example: \ed023 = 23.
.IP \fB\exNN\fR
Send NN as a hexadecimal value. NN must be 2 characters. For example: \exff = 255.
.IP \fB\ebNNNNNNNN\fR
Send NNNNNNNN as a binary value. NNNNNNNN must be 8 characters. For example: \eb0000001 = 1.
.TP 7n
The following line sends the ASCII string \fI"Hello"\fR:
H\ed101\ex6c\eb01101100o
.SH "EXAMPLES" .SH "EXAMPLES"
.TP .TP
Typical use is without options. For example: Typical use is without options. For example:

View file

@ -12,7 +12,9 @@ tio_SOURCES = tty.c \
include/tio/time.h \ include/tio/time.h \
include/tio/print.h \ include/tio/print.h \
include/tio/log.h \ include/tio/log.h \
include/tio/error.h include/tio/error.h \
lineedit.c \
include/tio/lineedit.h
if ADD_SETSPEED2 if ADD_SETSPEED2
tio_SOURCES += setspeed2.c tio_SOURCES += setspeed2.c

View file

@ -17,8 +17,14 @@ _tio()
-p --parity \ -p --parity \
-o --output-delay \ -o --output-delay \
-n --no-autoconnect \ -n --no-autoconnect \
-e --local-echo \
-t --timestamp \
-l --log \ -l --log \
-m --map \ -m --map \
-x --hex \
--newline-in-hex \
--line-edit \
--no-newline-in-line-edit \
-v --version \ -v --version \
-t --timestamp \ -t --timestamp \
-h --help" -h --help"
@ -70,6 +76,22 @@ _tio()
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0
;; ;;
-x | --hex)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--newline-in-hex)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--line-edit)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
--no-newline-in-line-edit)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
-v | --version) -v | --version)
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0 return 0

View file

@ -0,0 +1,40 @@
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <stdbool.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#define LINE_SIZE 81 /* 80 chars + terminating */
#define GOTOXY(x,y) fprintf(stdout, "\033[%d;%dH", (y), (x))
#define ACTION_PREFIX_1 0x1b
#define ACTION_PREFIX_2 0x5b
#define ARROW_LEFT 0x44
#define ARROW_RIGHT 0x43
#define ARROW_UP 0x41
#define ARROW_DOWN 0x42
#define BACKSPACE 0x7f
void lineedit_configure(const char * prefix, size_t max_len);
void add_to_history(const char * line);
char * get_line(char c);
void free_line(char * line);
#endif

View file

@ -28,6 +28,8 @@
#include <sys/param.h> #include <sys/param.h>
#define OPT_NEWLINE_IN_HEX 1000 // "short" option for --newline-in-hex #define OPT_NEWLINE_IN_HEX 1000 // "short" option for --newline-in-hex
#define OPT_LINE_EDIT 1001 // "short" option for --line-edit
#define OPT_NO_NEWLINE_LINE_EDIT 1002 // "short" option for --no-newline-in-line-edit
/* Options */ /* Options */
struct option_t struct option_t
@ -45,6 +47,8 @@ struct option_t
bool timestamp; bool timestamp;
bool hex_mode; bool hex_mode;
bool newline_in_hex; bool newline_in_hex;
bool line_edit;
bool no_newline_in_line_edit;
const char *log_filename; const char *log_filename;
const char *map; const char *map;
}; };

192
src/lineedit.c Normal file
View file

@ -0,0 +1,192 @@
#include "tio/lineedit.h"
#include "tio/options.h"
#include "tio/print.h"
static struct winsize winsize;
static char edited_line[LINE_SIZE];
static unsigned char edited_line_ctr = 0;
static int edited_line_cursor_pos = 0;
static const char * line_edit_prefix;
static int line_edit_prefix_length;
static char prev_char = 0, prevprev_char = 0;
static char ** history = NULL;
static unsigned int history_max_len;
static unsigned int history_len = 0;
static unsigned int history_pointer = 0;
static void free_history(void){
for(unsigned int i = 0; i < history_len; i++){
if(history[i]){
free(history[i]);
}
}
free(history);
}
static char * get_from_history(int dir){
char * ret = NULL;
if(dir == 1){
ret = history[history_pointer];
if(history_pointer + 1 < history_len){
history_pointer++;
}
} else if(dir == -1){
if(history_pointer > 0){
history_pointer--;
}
ret = history[history_pointer];
}
return ret;
}
void lineedit_configure(const char * prefix, size_t max_len){
if(ioctl(0, TIOCGWINSZ, &winsize) < 0){
warning_printf("Could not get the dimensions of the terminal");
exit(EXIT_FAILURE);
}
history_max_len = max_len;
history = (char **) calloc(history_max_len, sizeof(char *));
if(history == NULL){
warning_printf("Could not allocate the history");
exit(EXIT_FAILURE);
}
atexit(free_history);
line_edit_prefix = prefix;
line_edit_prefix_length = strlen(prefix);
}
char * get_line(char c){
char * finished_line = NULL;
char * history_line = NULL;
if(prev_char == ACTION_PREFIX_1 && c == ACTION_PREFIX_2){
/* do nothing */
} else if(prevprev_char == ACTION_PREFIX_1 && prev_char == ACTION_PREFIX_2){
switch(c){
case ARROW_LEFT:
if(edited_line_cursor_pos > 0){
edited_line_cursor_pos--;
}
break;
case ARROW_RIGHT:
if(edited_line_cursor_pos < edited_line_ctr){
edited_line_cursor_pos++;
}
break;
case ARROW_UP:
if((history_line = get_from_history(1)) != NULL){
strcpy(edited_line, history_line);
edited_line_ctr = strlen(history_line);
edited_line_cursor_pos = edited_line_ctr;
}
break;
case ARROW_DOWN:
if((history_line = get_from_history(-1)) != NULL){
strcpy(edited_line, history_line);
edited_line_ctr = strlen(history_line);
edited_line_cursor_pos = edited_line_ctr;
}
break;
}
} else {
if(c == '\r' || c == '\n'){
edited_line[edited_line_ctr] = '\0';
finished_line = strdup(edited_line);
} else {
if(isprint(c)){
if(edited_line_ctr < LINE_SIZE - 1){
memmove(&edited_line[edited_line_cursor_pos + 1],
&edited_line[edited_line_cursor_pos],
edited_line_ctr - edited_line_cursor_pos);
edited_line[edited_line_cursor_pos] = c;
edited_line_ctr++;
edited_line_cursor_pos++;
}
} else if(c == BACKSPACE){
char * rest = &edited_line[edited_line_cursor_pos];
memcpy(&edited_line[edited_line_cursor_pos - 1], rest, edited_line_ctr - edited_line_cursor_pos);
if(edited_line_ctr > 0){
edited_line_ctr--;
}
if(edited_line_cursor_pos > 0){
edited_line_cursor_pos--;
}
}
}
}
/* clear line with spaces */
fprintf(stdout, "\r%*c", winsize.ws_col, ' ');
/* output the contents of the buffer */
fprintf(stdout, "\r%s%.*s", line_edit_prefix, edited_line_ctr, edited_line);
/* set the position of the cursor */
GOTOXY(edited_line_cursor_pos + line_edit_prefix_length + 1, winsize.ws_row);
fflush(stdout);
/* clear buffer */
if(finished_line != NULL){
memset(edited_line, 0, LINE_SIZE);
edited_line_ctr = 0;
edited_line_cursor_pos = 0;
}
prevprev_char = prev_char;
prev_char = c;
return finished_line;
}
void free_line(char * line){
free(line);
fprintf(stdout, "\r%s", line_edit_prefix);
}
void add_to_history(const char * line){
/* dont add duplicate duplicate */
if(history_len > 0 && strcmp(line, history[0]) == 0){
return;
}
/* history is full, make room */
if(history_len == history_max_len){
free(history[history_len - 1]);
}
/* shift history to the right by 1 */
size_t count = history_len == history_max_len ? history_len - 1 : history_len;
memmove(&history[1], &history[0], count * sizeof(char *));
/* add to history */
history[0] = strdup(line);
if(history[0] == NULL){
return;
}
if(history_len < history_max_len){
history_len++;
}
history_pointer = 0;
}

1
src/linenoise Submodule

@ -0,0 +1 @@
Subproject commit 97d2850af13c339369093b78abe5265845d78220

View file

@ -28,6 +28,7 @@
#include "tio/log.h" #include "tio/log.h"
#include "tio/error.h" #include "tio/error.h"
#include "tio/print.h" #include "tio/print.h"
#include "tio/lineedit.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -51,6 +52,9 @@ int main(int argc, char *argv[])
/* Install log exit handler */ /* Install log exit handler */
atexit(&log_exit); atexit(&log_exit);
if(option.line_edit)
lineedit_configure(">", 100);
/* Create log file */ /* Create log file */
if (option.log) if (option.log)
log_open(option.log_filename); log_open(option.log_filename);

View file

@ -49,6 +49,8 @@ struct option_t option =
false, // No timestamp false, // No timestamp
false, // Not starting in hex mode false, // Not starting in hex mode
false, // No newlines in hex mode false, // No newlines in hex mode
false, // Not starting in line edit mode
false, // Send new line in line edit mode
"", // Log filename "", // Log filename
"" // Map string "" // Map string
}; };
@ -71,12 +73,15 @@ void print_help(char *argv[])
printf(" -m, --map <flags> Map special characters\n"); printf(" -m, --map <flags> Map special characters\n");
printf(" -x, --hex Start in hexadecimal mode\n"); printf(" -x, --hex Start in hexadecimal mode\n");
printf(" --newline-in-hex Interpret new line characters in hex mode\n"); printf(" --newline-in-hex Interpret new line characters in hex mode\n");
printf(" --line-edit Start in line edit mode\n");
printf(" --no-newline-in-line-edit Don't send newline after a line in line edit mode\n");
printf(" -v, --version Display version\n"); printf(" -v, --version Display version\n");
printf(" -h, --help Display help\n"); printf(" -h, --help Display help\n");
printf("\n"); printf("\n");
printf("See the man page for list of supported mapping flags.\n"); printf("See the man page for list of supported mapping flags.\n");
printf("\n"); printf("\n");
printf("In session, press ctrl-t q to quit.\n"); printf("In session, press ctrl-t q to quit.\n");
printf("In line edit mode, type :q to quit.\n");
printf("\n"); printf("\n");
} }
@ -123,11 +128,15 @@ void parse_options(int argc, char *argv[])
{"map", required_argument, 0, 'm'}, {"map", required_argument, 0, 'm'},
{"hex", no_argument, 0, 'x'}, {"hex", no_argument, 0, 'x'},
{"newline-in-hex", no_argument, 0, OPT_NEWLINE_IN_HEX }, {"newline-in-hex", no_argument, 0, OPT_NEWLINE_IN_HEX },
{"line-edit", no_argument, 0, OPT_LINE_EDIT },
{"no-newline-in-line-edit", no_argument, 0, OPT_NO_NEWLINE_LINE_EDIT },
{"version", no_argument, 0, 'v'}, {"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{0, 0, 0, 0 } {0, 0, 0, 0 }
}; };
#define OPT_NO_NEWLINE_LINE_EDIT 1002 // "short" option for --no-newline-in-line-edit
/* getopt_long stores the option index here */ /* getopt_long stores the option index here */
int option_index = 0; int option_index = 0;
@ -203,6 +212,14 @@ void parse_options(int argc, char *argv[])
option.newline_in_hex = true; option.newline_in_hex = true;
break; break;
case OPT_LINE_EDIT:
option.line_edit = true;
break;
case OPT_NO_NEWLINE_LINE_EDIT:
option.no_newline_in_line_edit = true;
break;
case 'v': case 'v':
printf("tio v%s\n", VERSION); printf("tio v%s\n", VERSION);
printf("Copyright (c) 2014-2018 Martin Lund\n"); printf("Copyright (c) 2014-2018 Martin Lund\n");

213
src/tty.c
View file

@ -43,11 +43,14 @@
#include "tio/time.h" #include "tio/time.h"
#include "tio/log.h" #include "tio/log.h"
#include "tio/error.h" #include "tio/error.h"
#include "tio/lineedit.h"
#ifdef HAVE_TERMIOS2 #ifdef HAVE_TERMIOS2
extern int setspeed2(int fd, int baudrate); extern int setspeed2(int fd, int baudrate);
#endif #endif
#define ESCAPED_BUFFER_SIZE 9 // max 8 binary digit + terminating 0
static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old; static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old;
static unsigned long rx_total = 0, tx_total = 0; static unsigned long rx_total = 0, tx_total = 0;
static bool connected = false; static bool connected = false;
@ -60,9 +63,14 @@ static bool map_i_nl_crnl = false;
static bool map_o_cr_nl = false; static bool map_o_cr_nl = false;
static bool map_o_nl_crnl = false; static bool map_o_nl_crnl = false;
static bool map_o_del_bs = false; static bool map_o_del_bs = false;
static char hex_chars[2]; static char hex_chars[3] = { 0 };
static unsigned char hex_char_index = 0; static unsigned char hex_char_index = 0;
static char parsed_line[LINE_SIZE];
static unsigned int parsed_line_ctr;
void handle_command_sequence(char input_char, char previous_char, char *output_char, bool *forward);
#define tio_printf(format, args...) \ #define tio_printf(format, args...) \
{ \ { \
if (tainted) putchar('\n'); \ if (tainted) putchar('\n'); \
@ -80,22 +88,20 @@ static void optional_local_echo(char c)
log_write(c); log_write(c);
} }
inline static bool is_valid_bin(char c)
{
return (c >= '0' && c <= '1');
}
inline static bool is_valid_hex(char c) inline static bool is_valid_hex(char c)
{ {
return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
} }
inline static unsigned char char_to_nibble(char c) inline static bool is_valid_dec(char c)
{ {
if(c >= '0' && c <= '9'){ return (c >= '0' && c <= '9');
return c - '0';
} else if(c >= 'a' && c <= 'f'){
return c - 'a' + 10;
} else if(c >= 'A' && c <= 'F'){
return c - 'A' + 10;
} else {
return 0;
}
} }
static void output_hex(char c) static void output_hex(char c)
@ -103,7 +109,7 @@ static void output_hex(char c)
hex_chars[hex_char_index++] = c; hex_chars[hex_char_index++] = c;
if(hex_char_index == 2){ if(hex_char_index == 2){
unsigned char hex_value = char_to_nibble(hex_chars[0]) << 4 | (char_to_nibble(hex_chars[1]) & 0x0F); unsigned char hex_value = (unsigned char)strtol(hex_chars, NULL, 16);
hex_char_index = 0; hex_char_index = 0;
optional_local_echo(hex_value); optional_local_echo(hex_value);
@ -135,6 +141,162 @@ static void print_normal(char c)
fflush(stdout); fflush(stdout);
} }
static void send_line(const char * line)
{
int len = strlen(line);
if(option.local_echo){
fprintf(stdout, "\r\n");
for(int i = 0; i < len; i++){
optional_local_echo(line[i]);
}
if(option.no_newline_in_line_edit){
fprintf(stdout, "\r\n");
}
} else {
get_line(0);
}
ssize_t status = write(fd, line, len);
if (status < 0){
warning_printf("Could not write to tty device");
} else {
tx_total += len;
}
}
static void parse_line(const char * line)
{
int len = strlen(line);
if(len == 0){
return;
}
/* escaped characters will collapse */
if(len > LINE_SIZE - 3){
warning_printf("Line is too long (%d): %s", len, line);
return;
}
parsed_line_ctr = 0;
memset(parsed_line, 0, LINE_SIZE);
/* handle commands */
if(len == 2 && line[0] == ':'){
switch(line[1]){
case '?':
tio_printf("Commands:");
tio_printf(":? List available commands");
tio_printf(":q Quit");
tio_printf(":v Show version");
tio_printf(":: Send ':'");
break;
case 'q':
handle_command_sequence(KEY_Q, KEY_CTRL_T, NULL, NULL);
break;
case 'v':
handle_command_sequence(KEY_V, KEY_CTRL_T, NULL, NULL);
break;
case ':':
parsed_line[parsed_line_ctr++] = ':';
break;
default:
warning_printf("Unknown command: %c", line[1]);
return;
}
/* interpret line */
} else {
int characters_to_read = 0;
char buffer[ESCAPED_BUFFER_SIZE];
unsigned int buffer_ctr = 0;
int base = 10;
memset(buffer, 0, ESCAPED_BUFFER_SIZE);
unsigned long value;
/* handle escape characters */
for(unsigned int i = 0; i < len; i++){
if(characters_to_read > 0){
if(base == 16 && !is_valid_hex(line[i])){
warning_printf("Invalid hexadecimal character: %c", line[i]);
return;
} else if(base == 10 && !is_valid_dec(line[i])){
warning_printf("Invalid decimal character: %c", line[i]);
return;
} else if(base == 2 && !is_valid_bin(line[i])){
warning_printf("Invalid binary character: %c", line[i]);
return;
} else {
buffer[buffer_ctr++] = line[i];
if(buffer_ctr == characters_to_read){
value = (unsigned long)strtol(buffer, NULL, base);
if(value > 255){
warning_printf("Value is too large for a byte: %lu", value);
return;
}
parsed_line[parsed_line_ctr++] = (unsigned char)value;
memset(buffer, 0, ESCAPED_BUFFER_SIZE);
buffer_ctr = 0;
characters_to_read = 0;
}
}
} else {
if(line[i] == '\\'){
if(i + 1 < len && line[i+1] == '\\'){
parsed_line[parsed_line_ctr++] = '\\';
i++;
} else if(i + 1 < len && line[i+1] == 'x'){
base = 16;
characters_to_read = 2;
i++;
} else if(i + 1 < len && line[i+1] == 'd'){
base = 10;
characters_to_read = 3;
i++;
} else if(i + 1 < len && line[i+1] == 'b'){
base = 2;
characters_to_read = 8;
i++;
} else if(i + 1 < len){
warning_printf("Unknown base: %c", line[i+1]);
return;
} else {
warning_printf("Invalid escape: %s", line);
return;
}
} else {
parsed_line[parsed_line_ctr++] = line[i];
}
}
}
if(characters_to_read != 0){
warning_printf("Unescaped line: %s", line);
return;
}
if(option.no_newline_in_line_edit == false){
parsed_line[parsed_line_ctr++] = '\r';
parsed_line[parsed_line_ctr++] = '\n';
}
parsed_line[parsed_line_ctr++] = '\0';
}
send_line(parsed_line);
}
static void toggle_line(const char *line_name, int mask) static void toggle_line(const char *line_name, int mask)
{ {
int state; int state;
@ -375,7 +537,12 @@ void stdout_configure(void)
/* Print launch hints */ /* Print launch hints */
tio_printf("tio v%s", VERSION); tio_printf("tio v%s", VERSION);
tio_printf("Press ctrl-t q to quit"); if(option.line_edit)
{
tio_printf("Type :q to quit, :? for help.");
} else {
tio_printf("Press ctrl-t q to quit, ctrl-t ? for help.");
}
/* At start use normal print function */ /* At start use normal print function */
print = print_normal; print = print_normal;
@ -661,6 +828,7 @@ int tty_connect(void)
int status; int status;
time_t next_timestamp = 0; time_t next_timestamp = 0;
char* now = NULL; char* now = NULL;
char* raw_line = NULL;
/* Open tty device */ /* Open tty device */
#ifdef __APPLE__ #ifdef __APPLE__
@ -745,6 +913,12 @@ int tty_connect(void)
maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */ maxfd = MAX(fd, STDIN_FILENO) + 1; /* Maximum bit entry (fd) to test */
/* display initial prompt */
if(option.line_edit)
{
get_line(0);
}
/* Input loop */ /* Input loop */
while (true) while (true)
{ {
@ -783,6 +957,7 @@ int tty_connect(void)
next_timestamp = 0; next_timestamp = 0;
} }
// FIXME: make it better in line edit mode
/* Map input character */ /* Map input character */
if ((input_char == '\n') && (map_i_nl_crnl)) if ((input_char == '\n') && (map_i_nl_crnl))
{ {
@ -825,6 +1000,15 @@ int tty_connect(void)
goto error_read; goto error_read;
} }
if(option.line_edit){
if((raw_line = get_line(input_char)) != NULL){
add_to_history(raw_line);
parse_line(raw_line);
free_line(raw_line);
}
} else {
/* Forward input to output except ctrl-t key */ /* Forward input to output except ctrl-t key */
output_char = input_char; output_char = input_char;
if (input_char == KEY_CTRL_T) if (input_char == KEY_CTRL_T)
@ -838,6 +1022,7 @@ int tty_connect(void)
if(print_mode == HEX){ if(print_mode == HEX){
if(!is_valid_hex(input_char)){ if(!is_valid_hex(input_char)){
warning_printf("Invalid hex character: '%c' (0x%02x)", input_char, input_char); warning_printf("Invalid hex character: '%c' (0x%02x)", input_char, input_char);
hex_char_index = 0;
continue; continue;
} }
} }
@ -861,6 +1046,7 @@ int tty_connect(void)
delay(option.output_delay); delay(option.output_delay);
} }
if(print_mode == HEX){ if(print_mode == HEX){
output_hex(output_char); output_hex(output_char);
} else { } else {
@ -884,6 +1070,7 @@ int tty_connect(void)
previous_char = input_char; previous_char = input_char;
} }
}
} else if (status == -1) } else if (status == -1)
{ {
error_printf("Error: select() failed (%s)", strerror(errno)); error_printf("Error: select() failed (%s)", strerror(errno));