mirror of
https://github.com/eddyem/tty_term.git
synced 2025-12-06 02:25:11 +03:00
remake: do it @ncurses & readline
This commit is contained in:
parent
d4e0ae967d
commit
b87fb2e22a
@ -33,12 +33,23 @@ endif()
|
||||
|
||||
###### pkgconfig ######
|
||||
# pkg-config modules (for pkg-check-modules)
|
||||
set(MODULES usefull_macros)
|
||||
set(MODULES ncurses readline usefull_macros)
|
||||
|
||||
# find packages:
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
if(THREADS_HAVE_PTHREAD_ARG)
|
||||
set_property(TARGET ${PROJ} PROPERTY COMPILE_OPTIONS "-pthread")
|
||||
set_property(TARGET ${PROJ} PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread")
|
||||
endif()
|
||||
if(CMAKE_THREAD_LIBS_INIT)
|
||||
list(APPEND ${PROJ}_LIBRARIES "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endif()
|
||||
|
||||
|
||||
###### additional flags ######
|
||||
#list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads")
|
||||
|
||||
|
||||
18
Readme.md
18
Readme.md
@ -1,11 +1,13 @@
|
||||
Very simple terminal client
|
||||
===========================
|
||||
|
||||
Usage: tty_term [args]
|
||||
|
||||
Where args are:
|
||||
|
||||
-d, --devname=arg serial device name
|
||||
-e, --eol=arg end of line: n, r or rn
|
||||
-h, --help show this help
|
||||
-s, --speed=arg baudrate (default: 9600)
|
||||
|
||||
Usage: tty_term [args]
|
||||
|
||||
Where args are:
|
||||
|
||||
-d, --devname=arg serial device name
|
||||
-e, --eol=arg end of line: n (default), r, nr or rn
|
||||
-h, --help show this help
|
||||
-s, --speed=arg baudrate (default: 9600)
|
||||
-t, --timeout=arg timeout for select() in ms (default: 100)
|
||||
|
||||
@ -35,6 +35,7 @@ glob_pars const Gdefault = {
|
||||
.speed = 9600,
|
||||
.ttyname = "/dev/ttyUSB0",
|
||||
.eol = "n",
|
||||
.tmoutms = 100,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -46,7 +47,8 @@ static myoption cmdlnopts[] = {
|
||||
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
|
||||
{"speed", NEED_ARG, NULL, 's', arg_int, APTR(&G.speed), _("baudrate (default: 9600)")},
|
||||
{"devname", NEED_ARG, NULL, 'd', arg_string, APTR(&G.ttyname), _("serial device name")},
|
||||
{"eol", NEED_ARG, NULL, 'e', arg_string, APTR(&G.eol), _("end of line: n, r or rn")},
|
||||
{"eol", NEED_ARG, NULL, 'e', arg_string, APTR(&G.eol), _("end of line: n (default), r, nr or rn")},
|
||||
{"timeout", NEED_ARG, NULL, 't', arg_int, APTR(&G.tmoutms), _("timeout for select() in ms (default: 100)")},
|
||||
end_option
|
||||
};
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
*/
|
||||
typedef struct{
|
||||
int speed; // baudrate
|
||||
int tmoutms; // timeout for select() in ms
|
||||
char *ttyname; // device name
|
||||
char *eol; // end of line: \r (CR), \rn (CR+LF) or \n (LF): "r", "rn", "n"
|
||||
} glob_pars;
|
||||
|
||||
83
main.c
83
main.c
@ -21,61 +21,72 @@
|
||||
#include <string.h> // strcmp
|
||||
#include <usefull_macros.h>
|
||||
#include "cmdlnopts.h"
|
||||
#include "ncurses_and_readline.h"
|
||||
#include "tty.h"
|
||||
|
||||
// we don't need more than 64 bytes for TTY input buffer - USB CDC can't transmit more in one packet
|
||||
#define BUFLEN 65
|
||||
#define BUFLEN 4096
|
||||
|
||||
static TTY_descr *dev = NULL;
|
||||
static ttyd dtty = {.dev = NULL, .mutex = PTHREAD_MUTEX_INITIALIZER};
|
||||
|
||||
void signals(int signo){
|
||||
restore_console();
|
||||
if(dev) close_tty(&dev);
|
||||
if(dtty.dev){
|
||||
pthread_mutex_lock(&dtty.mutex);
|
||||
close_tty(&dtty.dev);
|
||||
}
|
||||
deinit_ncurses();
|
||||
deinit_readline();
|
||||
exit(signo);
|
||||
}
|
||||
|
||||
typedef enum{
|
||||
EOL_N = 0,
|
||||
EOL_R,
|
||||
EOL_RN
|
||||
} eol_t;
|
||||
|
||||
int main(int argc, char **argv){
|
||||
glob_pars *G = NULL; // default parameters see in cmdlnopts.c
|
||||
initial_setup();
|
||||
G = parse_args(argc, argv);
|
||||
dev = new_tty(G->ttyname, G->speed, BUFLEN);
|
||||
if(!dev || !(dev = tty_open(dev, 1))) return 1; // open exclusively
|
||||
eol_t eol = EOL_N;
|
||||
if(strcmp(G->eol, "n")){ // change eol
|
||||
if(strcmp(G->eol, "r") == 0) eol = EOL_R;
|
||||
else if(strcmp(G->eol, "rn") == 0) eol = EOL_RN;
|
||||
else ERRX("End of line should be \"r\", \"n\" or \"rn\"");
|
||||
if(G->tmoutms < 0) ERRX("Timeout should be >= 0");
|
||||
dtty.dev = new_tty(G->ttyname, G->speed, BUFLEN);
|
||||
if(!dtty.dev || !(dtty.dev = tty_open(dtty.dev, 1))){
|
||||
WARN("Can't open device %s", G->ttyname);
|
||||
signals(1);
|
||||
}
|
||||
init_ncurses();
|
||||
init_readline();
|
||||
const char *EOL = "\n";
|
||||
if(strcasecmp(G->eol, "n")){
|
||||
if(strcasecmp(G->eol, "r") == 0) EOL = "\r";
|
||||
else if(strcasecmp(G->eol, "rn") == 0) EOL = "\r\n";
|
||||
else if(strcasecmp(G->eol, "nr") == 0) EOL = "\n\r";
|
||||
else ERRX("End of line should be \"r\", \"n\" or \"rn\" or \"nr\"");
|
||||
}
|
||||
strcpy(dtty.eol, EOL);
|
||||
int eollen = strlen(EOL);
|
||||
dtty.eollen = eollen;
|
||||
signal(SIGTERM, signals); // kill (-15) - quit
|
||||
signal(SIGHUP, signals); // hup - quit
|
||||
signal(SIGINT, signals); // ctrl+C - quit
|
||||
signal(SIGQUIT, signals); // ctrl+\ - quit
|
||||
signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z
|
||||
setup_con();
|
||||
const char r = '\r';
|
||||
pthread_t writer;
|
||||
if(pthread_create(&writer, NULL, cmdline, (void*)&dtty)) ERR("pthread_create()");
|
||||
settimeout(G->tmoutms);
|
||||
while(1){
|
||||
int b;
|
||||
int l = read_ttyX(dev, &b);
|
||||
if(l < 0) signals(9);
|
||||
if(b > -1){
|
||||
char c = (char)b;
|
||||
if(c == '\n' && eol != EOL_N){ // !\n
|
||||
if(eol == EOL_R) c = '\r'; // \r
|
||||
else if(write_tty(dev->comfd, &r, 1)) WARN("write_tty()"); // \r\n
|
||||
}
|
||||
if(write_tty(dev->comfd, &c, 1)) WARN("write_tty()");
|
||||
}
|
||||
if(l){
|
||||
printf("%s", dev->buf);
|
||||
fflush(stdout);
|
||||
// if(fout) copy_buf_to_file(buff, &oldcmd);
|
||||
}
|
||||
//if(0 == pthread_mutex_lock(&dtty.mutex)){
|
||||
int l = Read_tty(dtty.dev);
|
||||
if(l){
|
||||
char *buf = dtty.dev->buf;
|
||||
char *eol = NULL, *estr = buf + l;
|
||||
do{
|
||||
eol = strstr(buf, EOL);
|
||||
if(eol){
|
||||
*eol = 0;
|
||||
add_ttydata(buf);
|
||||
buf = eol + eollen;
|
||||
}else{
|
||||
add_ttydata(buf);
|
||||
}
|
||||
}while(eol && buf < estr);
|
||||
}else if(l < 0) signals(9);
|
||||
// pthread_mutex_unlock(&dtty.mutex);
|
||||
//}
|
||||
}
|
||||
// never reached
|
||||
return 0;
|
||||
|
||||
331
ncurses_and_readline.c
Normal file
331
ncurses_and_readline.c
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* This file is part of the ttyterm project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
// based on https://stackoverflow.com/a/28709979/1965803 ->
|
||||
// https://github.com/ulfalizer/readline-and-ncurses
|
||||
// Copyright (c) 2015-2019, Ulf Magnusson
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
#include <curses.h>
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ncurses_and_readline.h"
|
||||
|
||||
// Keeps track of the terminal mode so we can reset the terminal if needed on errors
|
||||
static bool visual_mode = false;
|
||||
// insert commands when true; roll upper screen when false
|
||||
static bool insert_mode = true;
|
||||
static bool should_exit = false;
|
||||
|
||||
static ttyd *dtty = NULL;
|
||||
|
||||
static void fail_exit(const char *msg){
|
||||
// Make sure endwin() is only called in visual mode. As a note, calling it
|
||||
// twice does not seem to be supported and messed with the cursor position.
|
||||
if(visual_mode) endwin();
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static WINDOW *msg_win; // Message window
|
||||
static WINDOW *sep_win; // Separator line above the command (readline) window
|
||||
static WINDOW *cmd_win; // Command (readline) window
|
||||
|
||||
// string list
|
||||
typedef struct _Line{
|
||||
int Nline;
|
||||
char *contents;
|
||||
struct _Line *prev, *next;
|
||||
} Line;
|
||||
// head of list, current item and first line on screen
|
||||
Line *head = NULL, *curr = NULL, *firstline = NULL;
|
||||
int nr_lines = 0; // total anount of data portions @ input
|
||||
|
||||
static unsigned char input; // Input character for readline
|
||||
|
||||
// Used to signal "no more input" after feeding a character to readline
|
||||
static bool input_avail = false;
|
||||
|
||||
// Not bothering with 'input_avail' and just returning 0 here seems to do the
|
||||
// right thing too, but this might be safer across readline versions
|
||||
static int readline_input_avail(){
|
||||
return input_avail;
|
||||
}
|
||||
|
||||
static int readline_getc(__attribute__((__unused__)) FILE *dummy){
|
||||
input_avail = false;
|
||||
return input;
|
||||
}
|
||||
|
||||
static void forward_to_readline(char c){
|
||||
input = c;
|
||||
input_avail = true;
|
||||
rl_callback_read_char();
|
||||
}
|
||||
|
||||
static void msg_win_redisplay(bool for_resize){
|
||||
werase(msg_win);
|
||||
Line *l = firstline;
|
||||
int nlines = 0; // total amount of lines @ output
|
||||
for(; l && (nlines < LINES - 2); l = l->next){
|
||||
size_t contlen = strlen(l->contents) + 128;
|
||||
char *buf = malloc(contlen);
|
||||
// don't add trailing '\n' (or last line will be empty with cursor)
|
||||
contlen = snprintf(buf, contlen, "%s", l->contents);
|
||||
int nlnext = (contlen - 1) / COLS + 1;
|
||||
wmove(msg_win, nlines, 0);
|
||||
if(nlines + nlnext < LINES-2){ // can put out the full line
|
||||
waddstr(msg_win, buf);
|
||||
//wprintw(msg_win, "%d (%d): %s -> %d", l->Nline, firstline->Nline, l->contents, nlnext);
|
||||
nlines += nlnext;
|
||||
}else{ // put only first part
|
||||
int rest = LINES-2 - nlines;
|
||||
waddnstr(msg_win, buf, rest *COLS);
|
||||
break;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
curs_set(0);
|
||||
if(for_resize) wnoutrefresh(msg_win);
|
||||
else wrefresh(msg_win);
|
||||
}
|
||||
|
||||
void add_ttydata(const char *text){
|
||||
if(!text) return;
|
||||
if(!*text) text = " "; // empty string
|
||||
Line *lp = malloc(sizeof(Line));
|
||||
lp->contents = strdup(text);
|
||||
lp->prev = curr;
|
||||
lp->next = NULL;
|
||||
lp->Nline = nr_lines++;
|
||||
if(!curr || !head){
|
||||
head = curr = firstline = lp;
|
||||
}else
|
||||
curr->next = lp;
|
||||
curr = lp;
|
||||
// roll back to show last input
|
||||
if(curr->prev){
|
||||
firstline = curr;
|
||||
int totalln = (strlen(firstline->contents) - 1)/COLS + 1;
|
||||
while(firstline->prev){
|
||||
totalln += (strlen(firstline->prev->contents) - 1)/COLS + 1;
|
||||
if(totalln > LINES-2) break;
|
||||
firstline = firstline->prev;
|
||||
}
|
||||
}
|
||||
msg_win_redisplay(false);
|
||||
}
|
||||
|
||||
static void cmd_win_redisplay(bool for_resize){
|
||||
int cursor_col = 2 + rl_point; // "> " width is 2
|
||||
werase(cmd_win);
|
||||
int x = 0, maxw = COLS-2;
|
||||
if(cursor_col > maxw){
|
||||
x = cursor_col - maxw;
|
||||
cursor_col = maxw;
|
||||
}
|
||||
char abuf[4096];
|
||||
snprintf(abuf, 4096, "> %s", rl_line_buffer);
|
||||
waddstr(cmd_win, abuf+x);
|
||||
wmove(cmd_win, 0, cursor_col);
|
||||
curs_set(2);
|
||||
if(for_resize) wnoutrefresh(cmd_win);
|
||||
else wrefresh(cmd_win);
|
||||
}
|
||||
|
||||
static void readline_redisplay(){
|
||||
cmd_win_redisplay(false);
|
||||
}
|
||||
|
||||
static void show_mode(bool for_resize){
|
||||
wclear(sep_win);
|
||||
if(insert_mode) waddstr(sep_win, "INSERT (TAB to switch, ctrl+D to quit)");
|
||||
else waddstr(sep_win, "SCROLL (TAB to switch, q to quit)");
|
||||
if(for_resize) wnoutrefresh(sep_win);
|
||||
else wrefresh(sep_win);
|
||||
cmd_win_redisplay(for_resize);
|
||||
}
|
||||
|
||||
static void resize(){
|
||||
if(LINES > 2){
|
||||
wresize(msg_win, LINES - 2, COLS);
|
||||
wresize(sep_win, 1, COLS);
|
||||
wresize(cmd_win, 1, COLS);
|
||||
mvwin(sep_win, LINES - 2, 0);
|
||||
mvwin(cmd_win, LINES - 1, 0);
|
||||
}
|
||||
msg_win_redisplay(true);
|
||||
show_mode(true);
|
||||
doupdate();
|
||||
}
|
||||
|
||||
void init_ncurses(){
|
||||
if (!initscr())
|
||||
fail_exit("Failed to initialize ncurses");
|
||||
visual_mode = true;
|
||||
if(has_colors()){
|
||||
start_color();
|
||||
use_default_colors();
|
||||
}
|
||||
cbreak();
|
||||
noecho();
|
||||
nonl();
|
||||
intrflush(NULL, FALSE);
|
||||
keypad(cmd_win, 0);
|
||||
curs_set(2);
|
||||
if(LINES > 2){
|
||||
msg_win = newwin(LINES - 2, COLS, 0, 0);
|
||||
sep_win = newwin(1, COLS, LINES - 2, 0);
|
||||
cmd_win = newwin(1, COLS, LINES - 1, 0);
|
||||
}
|
||||
else{
|
||||
msg_win = newwin(1, COLS, 0, 0);
|
||||
sep_win = newwin(1, COLS, 0, 0);
|
||||
cmd_win = newwin(1, COLS, 0, 0);
|
||||
}
|
||||
if(!msg_win || !sep_win || !cmd_win)
|
||||
fail_exit("Failed to allocate windows");
|
||||
if(has_colors()){
|
||||
init_pair(1, COLOR_WHITE, COLOR_BLUE);
|
||||
wbkgd(sep_win, COLOR_PAIR(1));
|
||||
}else
|
||||
wbkgd(sep_win, A_STANDOUT);
|
||||
show_mode(false);
|
||||
mousemask(BUTTON4_PRESSED|BUTTON5_PRESSED, NULL);
|
||||
}
|
||||
|
||||
void deinit_ncurses(){
|
||||
delwin(msg_win);
|
||||
delwin(sep_win);
|
||||
delwin(cmd_win);
|
||||
endwin();
|
||||
visual_mode = false;
|
||||
}
|
||||
|
||||
static void got_command(char *line){
|
||||
if(!line) // Ctrl-D pressed on empty line
|
||||
should_exit = true;
|
||||
else{
|
||||
if(!*line) return; // zero length
|
||||
add_history(line);
|
||||
if(dtty && dtty->dev){
|
||||
|
||||
//if(0 == pthread_mutex_lock(&dtty->mutex)){
|
||||
if(write_tty(dtty->dev->comfd, line, strlen(line))) signals(9);
|
||||
if(write_tty(dtty->dev->comfd, dtty->eol, dtty->eollen)) signals(9);
|
||||
// pthread_mutex_unlock(&dtty->mutex);
|
||||
//}
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
|
||||
void init_readline(){
|
||||
rl_catch_signals = 0;
|
||||
rl_catch_sigwinch = 0;
|
||||
rl_deprep_term_function = NULL;
|
||||
rl_prep_term_function = NULL;
|
||||
rl_change_environment = 0;
|
||||
rl_getc_function = readline_getc;
|
||||
rl_input_available_hook = readline_input_avail;
|
||||
rl_redisplay_function = readline_redisplay;
|
||||
rl_callback_handler_install("> ", got_command);
|
||||
}
|
||||
|
||||
void deinit_readline(){
|
||||
rl_callback_handler_remove();
|
||||
}
|
||||
|
||||
static void rolldown(){
|
||||
if(firstline && firstline->prev){
|
||||
firstline = firstline->prev;
|
||||
msg_win_redisplay(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void rollup(){
|
||||
if(firstline && firstline->next){
|
||||
firstline = firstline->next;
|
||||
msg_win_redisplay(false);
|
||||
}
|
||||
}
|
||||
|
||||
void *cmdline(void* arg){
|
||||
MEVENT event;
|
||||
dtty = (ttyd*)arg;
|
||||
do{
|
||||
int c = wgetch(cmd_win);
|
||||
bool processed = true;
|
||||
switch(c){
|
||||
case KEY_MOUSE:
|
||||
if(getmouse(&event) == OK){
|
||||
if(event.bstate & (BUTTON4_PRESSED)) rolldown(); // wheel up
|
||||
else if(event.bstate & (BUTTON5_PRESSED)) rollup(); // wheel down
|
||||
}
|
||||
break;
|
||||
case '\t': // tab switch between scroll and edit mode
|
||||
keypad(cmd_win, insert_mode); // enable/disable reaction @ special characters
|
||||
insert_mode = !insert_mode;
|
||||
show_mode(false);
|
||||
break;
|
||||
case KEY_RESIZE:
|
||||
resize();
|
||||
break;
|
||||
default:
|
||||
processed = false;
|
||||
}
|
||||
if(processed) continue;
|
||||
if(insert_mode){
|
||||
forward_to_readline(c);
|
||||
}else{
|
||||
switch(c){
|
||||
case KEY_UP: // roll down for one item
|
||||
rolldown();
|
||||
break;
|
||||
case KEY_DOWN: // roll up for one item
|
||||
rollup();
|
||||
break;
|
||||
case KEY_PPAGE: // PageUp: roll down for 10 items
|
||||
for(int i = 0; i < 10; ++i){
|
||||
if(firstline && firstline->prev) firstline = firstline->prev;
|
||||
else break;
|
||||
}
|
||||
msg_win_redisplay(false);
|
||||
break;
|
||||
case KEY_NPAGE: // PageUp: roll up for 10 items
|
||||
for(int i = 0; i < 10; ++i){
|
||||
if(firstline && firstline->next) firstline = firstline->next;
|
||||
else break;
|
||||
}
|
||||
msg_win_redisplay(false);
|
||||
break;
|
||||
default:
|
||||
if(c == 'q' || c == 'Q') should_exit = true; // quit
|
||||
}
|
||||
}
|
||||
}while(!should_exit);
|
||||
signals(0);
|
||||
return NULL;
|
||||
}
|
||||
39
ncurses_and_readline.h
Normal file
39
ncurses_and_readline.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the ttyterm project.
|
||||
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef NCURSES_AND_READLINE_H__
|
||||
#define NCURSES_AND_READLINE_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <usefull_macros.h>
|
||||
|
||||
typedef struct{
|
||||
TTY_descr *dev;
|
||||
pthread_mutex_t mutex;
|
||||
char eol[3];
|
||||
int eollen;
|
||||
} ttyd;
|
||||
|
||||
void init_readline();
|
||||
void deinit_readline();
|
||||
void init_ncurses();
|
||||
void deinit_ncurses();
|
||||
void *cmdline(void* arg);
|
||||
void add_ttydata(const char *text);
|
||||
|
||||
#endif // NCURSES_AND_READLINE_H__
|
||||
99
tty.c
99
tty.c
@ -20,78 +20,43 @@
|
||||
#include <sys/select.h>
|
||||
#include "tty.h"
|
||||
|
||||
/**
|
||||
* @brief read_ttyX- read data from TTY & console with 1ms timeout WITH disconnect detection
|
||||
* @param buff (o) - buffer for data read
|
||||
* @param length - buffer len
|
||||
* @param rb (o) - byte read from console or -1
|
||||
* @return amount of bytes read on tty or -1 if disconnected
|
||||
*/
|
||||
int read_ttyX(TTY_descr *d, int *rb){
|
||||
if(!d || d->comfd < 0) return -1;
|
||||
ssize_t L = 0;
|
||||
static int sec = 0, usec = 100; // timeout
|
||||
|
||||
void settimeout(int tmout){
|
||||
sec = 0;
|
||||
if(tmout > 999){
|
||||
sec = tmout / 1000;
|
||||
tmout -= sec * 1000;
|
||||
}
|
||||
usec = tmout * 1000L;
|
||||
}
|
||||
|
||||
int Read_tty(TTY_descr *d){
|
||||
if(d->comfd < 0) return 0;
|
||||
size_t L = 0;
|
||||
ssize_t l;
|
||||
size_t length = d->bufsz;
|
||||
char *ptr = d->buf;
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int retval;
|
||||
if(rb) *rb = -1;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(STDIN_FILENO, &rfds);
|
||||
FD_SET(d->comfd, &rfds);
|
||||
// wait for 1ms
|
||||
tv.tv_sec = 0; tv.tv_usec = 1000;
|
||||
retval = select(d->comfd + 1, &rfds, NULL, NULL, &tv);
|
||||
if(!retval) return 0;
|
||||
if(retval < 0){
|
||||
WARN("select()");
|
||||
return -1;
|
||||
}
|
||||
if(FD_ISSET(STDIN_FILENO, &rfds) && rb){
|
||||
*rb = getchar();
|
||||
}
|
||||
if(FD_ISSET(d->comfd, &rfds)){
|
||||
if((L = read(d->comfd, d->buf, d->bufsz)) < 1){
|
||||
WARN("read()");
|
||||
return -1; // disconnect or other error - close TTY & die
|
||||
do{
|
||||
l = 0;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(d->comfd, &rfds);
|
||||
tv.tv_sec = sec; tv.tv_usec = usec;
|
||||
retval = select(d->comfd + 1, &rfds, NULL, NULL, &tv);
|
||||
if (!retval) break;
|
||||
if(FD_ISSET(d->comfd, &rfds)){
|
||||
l = read(d->comfd, ptr, length);
|
||||
if(l < 0) return -1;
|
||||
if(l == 0) break;
|
||||
ptr += l; L += l;
|
||||
length -= l;
|
||||
}
|
||||
}
|
||||
d->buflen = (size_t)L;
|
||||
}while(l && length);
|
||||
d->buflen = L;
|
||||
d->buf[L] = 0;
|
||||
return (int)L;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Copy line by line buffer buff to file removing cmd starting from newline
|
||||
* @param buffer - data to put into file
|
||||
* @param cmd - symbol to remove from line startint (if found, change *cmd to (-1)
|
||||
* or NULL, (-1) if no command to remove
|
||||
*/
|
||||
void copy_buf_to_file(char *buffer, int *cmd){
|
||||
char *buff, *line, *ptr;
|
||||
if(!cmd || *cmd < 0){
|
||||
fprintf(fout, "%s", buffer);
|
||||
return;
|
||||
}
|
||||
buff = strdup(buffer), ptr = buff;
|
||||
do{
|
||||
if(!*ptr) break;
|
||||
if(ptr[0] == (char)*cmd){
|
||||
*cmd = -1;
|
||||
ptr++;
|
||||
if(ptr[0] == '\n') ptr++;
|
||||
if(!*ptr) break;
|
||||
}
|
||||
line = ptr;
|
||||
ptr = strchr(buff, '\n');
|
||||
if(ptr){
|
||||
*ptr++ = 0;
|
||||
fprintf(fout, "%s\n", line);
|
||||
}else
|
||||
fprintf(fout, "%s", line); // no newline found in buffer
|
||||
// fprintf(fout, "%s\n", line);
|
||||
}while(ptr);
|
||||
free(buff);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user