mirror of
https://github.com/tio/tio.git
synced 2026-05-01 23:07:58 +02:00
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:
parent
7fc8e278ed
commit
aa9f6435db
10 changed files with 730 additions and 195 deletions
68
man/tio.1
68
man/tio.1
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
40
src/include/tio/lineedit.h
Normal file
40
src/include/tio/lineedit.h
Normal 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
|
||||||
|
|
@ -27,7 +27,9 @@
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#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
192
src/lineedit.c
Normal 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
1
src/linenoise
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 97d2850af13c339369093b78abe5265845d78220
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
@ -70,13 +72,16 @@ void print_help(char *argv[])
|
||||||
printf(" -l, --log <filename> Log to file\n");
|
printf(" -l, --log <filename> Log to file\n");
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,24 +115,28 @@ void parse_options(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
static struct option long_options[] =
|
static struct option long_options[] =
|
||||||
{
|
{
|
||||||
{"baudrate", required_argument, 0, 'b'},
|
{"baudrate", required_argument, 0, 'b'},
|
||||||
{"databits", required_argument, 0, 'd'},
|
{"databits", required_argument, 0, 'd'},
|
||||||
{"flow", required_argument, 0, 'f'},
|
{"flow", required_argument, 0, 'f'},
|
||||||
{"stopbits", required_argument, 0, 's'},
|
{"stopbits", required_argument, 0, 's'},
|
||||||
{"parity", required_argument, 0, 'p'},
|
{"parity", required_argument, 0, 'p'},
|
||||||
{"output-delay", required_argument, 0, 'o'},
|
{"output-delay", required_argument, 0, 'o'},
|
||||||
{"no-autoconnect", no_argument, 0, 'n'},
|
{"no-autoconnect", no_argument, 0, 'n'},
|
||||||
{"local-echo", no_argument, 0, 'e'},
|
{"local-echo", no_argument, 0, 'e'},
|
||||||
{"timestamp", no_argument, 0, 't'},
|
{"timestamp", no_argument, 0, 't'},
|
||||||
{"log", required_argument, 0, 'l'},
|
{"log", required_argument, 0, 'l'},
|
||||||
{"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 },
|
||||||
{"version", no_argument, 0, 'v'},
|
{"line-edit", no_argument, 0, OPT_LINE_EDIT },
|
||||||
{"help", no_argument, 0, 'h'},
|
{"no-newline-in-line-edit", no_argument, 0, OPT_NO_NEWLINE_LINE_EDIT },
|
||||||
{0, 0, 0, 0 }
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{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");
|
||||||
|
|
|
||||||
525
src/tty.c
525
src/tty.c
|
|
@ -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,30 +88,28 @@ 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)
|
||||||
{
|
{
|
||||||
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__
|
||||||
|
|
@ -701,17 +869,17 @@ int tty_connect(void)
|
||||||
next_timestamp = time(NULL);
|
next_timestamp = time(NULL);
|
||||||
|
|
||||||
if (option.hex_mode)
|
if (option.hex_mode)
|
||||||
{
|
{
|
||||||
print = print_hex;
|
print = print_hex;
|
||||||
print_mode = HEX;
|
print_mode = HEX;
|
||||||
tio_printf("Switched to hexadecimal mode");
|
tio_printf("Switched to hexadecimal mode");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
print = print_normal;
|
print = print_normal;
|
||||||
print_mode = NORMAL;
|
print_mode = NORMAL;
|
||||||
tio_printf("Switched to normal mode");
|
tio_printf("Switched to normal mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save current port settings */
|
/* Save current port settings */
|
||||||
if (tcgetattr(fd, &tio_old) < 0)
|
if (tcgetattr(fd, &tio_old) < 0)
|
||||||
|
|
@ -745,152 +913,171 @@ 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 */
|
||||||
|
|
||||||
/* Input loop */
|
/* display initial prompt */
|
||||||
while (true)
|
if(option.line_edit)
|
||||||
{
|
{
|
||||||
FD_ZERO(&rdfs);
|
get_line(0);
|
||||||
FD_SET(fd, &rdfs);
|
|
||||||
FD_SET(STDIN_FILENO, &rdfs);
|
|
||||||
|
|
||||||
/* Block until input becomes available */
|
|
||||||
status = select(maxfd, &rdfs, NULL, NULL, NULL);
|
|
||||||
if (status > 0)
|
|
||||||
{
|
|
||||||
if (FD_ISSET(fd, &rdfs))
|
|
||||||
{
|
|
||||||
/* Input from tty device ready */
|
|
||||||
if (read(fd, &input_char, 1) > 0)
|
|
||||||
{
|
|
||||||
/* Update receive statistics */
|
|
||||||
rx_total++;
|
|
||||||
|
|
||||||
/* Print timestamp on new line, if desired. */
|
|
||||||
if (next_timestamp && input_char != '\n' && input_char != '\r')
|
|
||||||
{
|
|
||||||
now = current_time();
|
|
||||||
fprintf(stdout, ANSI_COLOR_GRAY "[%s] " ANSI_COLOR_RESET, now);
|
|
||||||
if (option.log)
|
|
||||||
{
|
|
||||||
log_write('[');
|
|
||||||
while (*now != '\0')
|
|
||||||
{
|
|
||||||
log_write(*now);
|
|
||||||
++now;
|
|
||||||
}
|
|
||||||
log_write(']');
|
|
||||||
log_write(' ');
|
|
||||||
}
|
|
||||||
next_timestamp = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map input character */
|
|
||||||
if ((input_char == '\n') && (map_i_nl_crnl))
|
|
||||||
{
|
|
||||||
print('\r');
|
|
||||||
print('\n');
|
|
||||||
if (option.timestamp)
|
|
||||||
next_timestamp = time(NULL);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* Print received tty character to stdout */
|
|
||||||
print(input_char);
|
|
||||||
}
|
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
/* Write to log */
|
|
||||||
if (option.log)
|
|
||||||
log_write(input_char);
|
|
||||||
|
|
||||||
tainted = true;
|
|
||||||
|
|
||||||
if (input_char == '\n' && option.timestamp)
|
|
||||||
next_timestamp = time(NULL);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* Error reading - device is likely unplugged */
|
|
||||||
error_printf_silent("Could not read from tty device");
|
|
||||||
goto error_read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FD_ISSET(STDIN_FILENO, &rdfs))
|
|
||||||
{
|
|
||||||
bool forward = true;
|
|
||||||
|
|
||||||
/* Input from stdin ready */
|
|
||||||
status = read(STDIN_FILENO, &input_char, 1);
|
|
||||||
if (status <= 0)
|
|
||||||
{
|
|
||||||
error_printf_silent("Could not read from stdin");
|
|
||||||
goto error_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Forward input to output except ctrl-t key */
|
|
||||||
output_char = input_char;
|
|
||||||
if (input_char == KEY_CTRL_T)
|
|
||||||
forward = false;
|
|
||||||
|
|
||||||
/* Handle commands */
|
|
||||||
handle_command_sequence(input_char, previous_char, &output_char, &forward);
|
|
||||||
|
|
||||||
if (forward)
|
|
||||||
{
|
|
||||||
if(print_mode == HEX){
|
|
||||||
if(!is_valid_hex(input_char)){
|
|
||||||
warning_printf("Invalid hex character: '%c' (0x%02x)", input_char, input_char);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Map output character */
|
|
||||||
if ((output_char == 127) && (map_o_del_bs))
|
|
||||||
output_char = '\b';
|
|
||||||
if ((output_char == '\r') && (map_o_cr_nl))
|
|
||||||
output_char = '\n';
|
|
||||||
|
|
||||||
/* Map newline character */
|
|
||||||
if ((output_char == '\n') && (map_o_nl_crnl)) {
|
|
||||||
char r = '\r';
|
|
||||||
|
|
||||||
optional_local_echo(r);
|
|
||||||
status = write(fd, &r, 1);
|
|
||||||
if (status < 0)
|
|
||||||
warning_printf("Could not write to tty device");
|
|
||||||
|
|
||||||
tx_total++;
|
|
||||||
delay(option.output_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(print_mode == HEX){
|
|
||||||
output_hex(output_char);
|
|
||||||
} else {
|
|
||||||
/* Send output to tty device */
|
|
||||||
optional_local_echo(output_char);
|
|
||||||
|
|
||||||
status = write(fd, &output_char, 1);
|
|
||||||
if (status < 0)
|
|
||||||
warning_printf("Could not write to tty device");
|
|
||||||
fsync(fd);
|
|
||||||
|
|
||||||
/* Update transmit statistics */
|
|
||||||
tx_total++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert output delay */
|
|
||||||
delay(option.output_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Save previous key */
|
|
||||||
previous_char = input_char;
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if (status == -1)
|
|
||||||
{
|
|
||||||
error_printf("Error: select() failed (%s)", strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Input loop */
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
FD_ZERO(&rdfs);
|
||||||
|
FD_SET(fd, &rdfs);
|
||||||
|
FD_SET(STDIN_FILENO, &rdfs);
|
||||||
|
|
||||||
|
/* Block until input becomes available */
|
||||||
|
status = select(maxfd, &rdfs, NULL, NULL, NULL);
|
||||||
|
if (status > 0)
|
||||||
|
{
|
||||||
|
if (FD_ISSET(fd, &rdfs))
|
||||||
|
{
|
||||||
|
/* Input from tty device ready */
|
||||||
|
if (read(fd, &input_char, 1) > 0)
|
||||||
|
{
|
||||||
|
/* Update receive statistics */
|
||||||
|
rx_total++;
|
||||||
|
|
||||||
|
/* Print timestamp on new line, if desired. */
|
||||||
|
if (next_timestamp && input_char != '\n' && input_char != '\r')
|
||||||
|
{
|
||||||
|
now = current_time();
|
||||||
|
fprintf(stdout, ANSI_COLOR_GRAY "[%s] " ANSI_COLOR_RESET, now);
|
||||||
|
if (option.log)
|
||||||
|
{
|
||||||
|
log_write('[');
|
||||||
|
while (*now != '\0')
|
||||||
|
{
|
||||||
|
log_write(*now);
|
||||||
|
++now;
|
||||||
|
}
|
||||||
|
log_write(']');
|
||||||
|
log_write(' ');
|
||||||
|
}
|
||||||
|
next_timestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: make it better in line edit mode
|
||||||
|
/* Map input character */
|
||||||
|
if ((input_char == '\n') && (map_i_nl_crnl))
|
||||||
|
{
|
||||||
|
print('\r');
|
||||||
|
print('\n');
|
||||||
|
if (option.timestamp)
|
||||||
|
next_timestamp = time(NULL);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
/* Print received tty character to stdout */
|
||||||
|
print(input_char);
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
/* Write to log */
|
||||||
|
if (option.log)
|
||||||
|
log_write(input_char);
|
||||||
|
|
||||||
|
tainted = true;
|
||||||
|
|
||||||
|
if (input_char == '\n' && option.timestamp)
|
||||||
|
next_timestamp = time(NULL);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
/* Error reading - device is likely unplugged */
|
||||||
|
error_printf_silent("Could not read from tty device");
|
||||||
|
goto error_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(STDIN_FILENO, &rdfs))
|
||||||
|
{
|
||||||
|
bool forward = true;
|
||||||
|
|
||||||
|
/* Input from stdin ready */
|
||||||
|
status = read(STDIN_FILENO, &input_char, 1);
|
||||||
|
if (status <= 0)
|
||||||
|
{
|
||||||
|
error_printf_silent("Could not read from stdin");
|
||||||
|
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 */
|
||||||
|
output_char = input_char;
|
||||||
|
if (input_char == KEY_CTRL_T)
|
||||||
|
forward = false;
|
||||||
|
|
||||||
|
/* Handle commands */
|
||||||
|
handle_command_sequence(input_char, previous_char, &output_char, &forward);
|
||||||
|
|
||||||
|
if (forward)
|
||||||
|
{
|
||||||
|
if(print_mode == HEX){
|
||||||
|
if(!is_valid_hex(input_char)){
|
||||||
|
warning_printf("Invalid hex character: '%c' (0x%02x)", input_char, input_char);
|
||||||
|
hex_char_index = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Map output character */
|
||||||
|
if ((output_char == 127) && (map_o_del_bs))
|
||||||
|
output_char = '\b';
|
||||||
|
if ((output_char == '\r') && (map_o_cr_nl))
|
||||||
|
output_char = '\n';
|
||||||
|
|
||||||
|
/* Map newline character */
|
||||||
|
if ((output_char == '\n') && (map_o_nl_crnl)) {
|
||||||
|
char r = '\r';
|
||||||
|
|
||||||
|
optional_local_echo(r);
|
||||||
|
status = write(fd, &r, 1);
|
||||||
|
if (status < 0)
|
||||||
|
warning_printf("Could not write to tty device");
|
||||||
|
|
||||||
|
tx_total++;
|
||||||
|
delay(option.output_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(print_mode == HEX){
|
||||||
|
output_hex(output_char);
|
||||||
|
} else {
|
||||||
|
/* Send output to tty device */
|
||||||
|
optional_local_echo(output_char);
|
||||||
|
|
||||||
|
status = write(fd, &output_char, 1);
|
||||||
|
if (status < 0)
|
||||||
|
warning_printf("Could not write to tty device");
|
||||||
|
fsync(fd);
|
||||||
|
|
||||||
|
/* Update transmit statistics */
|
||||||
|
tx_total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert output delay */
|
||||||
|
delay(option.output_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save previous key */
|
||||||
|
previous_char = input_char;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (status == -1)
|
||||||
|
{
|
||||||
|
error_printf("Error: select() failed (%s)", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return TIO_SUCCESS;
|
return TIO_SUCCESS;
|
||||||
|
|
||||||
#ifdef HAVE_TERMIOS2
|
#ifdef HAVE_TERMIOS2
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue