add input data types (text/raw/hex), TODO: output types & fix \r bug

This commit is contained in:
Edward Emelianov 2023-11-02 17:01:14 +03:00
parent 934cbc04fd
commit 84bfe8b499
9 changed files with 700 additions and 125 deletions

4
main.c
View File

@ -29,7 +29,7 @@ static chardevice conndev = {.dev = NULL, .mutex = PTHREAD_MUTEX_INITIALIZER, .n
void signals(int signo){
signal(signo, SIG_IGN);
closedev(&conndev);
closedev();
deinit_ncurses();
deinit_readline();
DBG("Exit");
@ -84,7 +84,7 @@ int main(int argc, char **argv){
while(1){
if(0 == pthread_mutex_lock(&conndev.mutex)){
int l;
char *buf = ReadData(&conndev, &l);
char *buf = ReadData(&l);
if(buf && l > 0){
char *eol = NULL, *estr = buf + l;
do{

View File

@ -26,7 +26,6 @@
#include <readline/history.h>
#include <readline/readline.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -34,11 +33,14 @@
#include "dbg.h"
#include "ttysocket.h"
#include "ncurses_and_readline.h"
#include "popup_msg.h"
#include "string_functions.h"
enum { // using colors
BKG_NO = 1,
NORMAL_NO = 2,
MARKED_NO = 3
BKGMARKED_NO = 2,
NORMAL_NO = 3,
MARKED_NO = 4
};
#define COLOR(x) COLOR_PAIR(x ## _NO)
@ -48,6 +50,10 @@ static bool visual_mode = false;
static bool insert_mode = true;
static bool should_exit = false;
static disptype disp_type = DISP_TEXT; // type of displaying data
static disptype input_type = DISP_TEXT; // parsing type of input data
const char *dispnames[] = {"TEXT", "RAW", "HEX"};
static chardevice *dtty = NULL;
static void fail_exit(const char *msg){
@ -94,26 +100,54 @@ static void forward_to_readline(char c){
rl_callback_read_char();
}
// functions to modify output data
static char *text_putchar(char *next){
char c = *next++;
DBG("put 0x%02X (%c)", c, c);
if(c < 31 || c > 126){
wattron(msg_win, COLOR(MARKED));
//waddch(msg_win, c);
wprintw(msg_win, "%02X", (uint8_t)c);
wattroff(msg_win, COLOR(MARKED));
}else{
//wprintw(msg_win, "%c" COLOR_GREEN "green" COLOR_RED "red" COLOR_OLD, c);
waddch(msg_win, c);
}
return next;
}
static char *raw_putchar(char *next){
waddch(msg_win, *next);
return next+1;
}
static char *hex_putchar(char *next){
waddch(msg_win, *next);
return next+1;
}
static void msg_win_redisplay(bool for_resize){
werase(msg_win);
Line *l = firstline;
static char *buf = NULL;
//static char *buf = NULL;
int nlines = 0; // total amount of lines @ output
for(; l && (nlines < LINES - 2); l = l->next){
int linemax = LINES - 2;
char *(*putfn)(char *);
switch(disp_type){
case DISP_RAW:
putfn = raw_putchar;
break;
case DISP_HEX:
putfn = hex_putchar;
break;
default:
putfn = text_putchar;
}
for(; l && (nlines < linemax); l = l->next){
wmove(msg_win, nlines, 0);
size_t contlen = strlen(l->contents) + 128;
buf = realloc(buf, contlen);
int sz = strlen(l->contents);
//size_t contlen = strlen(l->contents) + 128;
//buf = realloc(buf, contlen);
char *ptr = l->contents;
for(int i = 0; i < sz; ++i, ++ptr){
char c = *ptr;
if(c <'a' || c > 'z'){
wattron(msg_win, COLOR(MARKED));
waddch(msg_win, c);
wattroff(msg_win, COLOR(MARKED));
}else{
waddch(msg_win, c);
}
while((ptr = putfn(ptr)) && *ptr && nlines < linemax){
nlines = msg_win->_cury;
}
++nlines;
}
@ -122,7 +156,7 @@ static void msg_win_redisplay(bool for_resize){
}
static void cmd_win_redisplay(bool for_resize){
int cursor_col = 2 + rl_point; // "> " width is 2
int cursor_col = 3 + strlen(dispnames[input_type]) + rl_point; // " > " width is 3
werase(cmd_win);
int x = 0, maxw = COLS-2;
if(cursor_col > maxw){
@ -130,7 +164,7 @@ static void cmd_win_redisplay(bool for_resize){
cursor_col = maxw;
}
char abuf[4096];
snprintf(abuf, 4096, "> %s", rl_line_buffer);
snprintf(abuf, 4096, "%s > %s", dispnames[input_type], rl_line_buffer);
waddstr(cmd_win, abuf+x);
wmove(cmd_win, 0, cursor_col);
if(for_resize) wnoutrefresh(cmd_win);
@ -145,26 +179,27 @@ static void readline_redisplay(){
}
static void show_mode(bool for_resize){
static const char *insmodetext = "INSERT (F1 - help)";
wclear(sep_win);
char buf[128];
if(insert_mode){
if(dtty){
switch(dtty->type){
case DEV_NETSOCKET:
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) HOST: %s, ENDLINE: %s, PORT: %s",
dtty->name, dtty->seol, dtty->port);
snprintf(buf, 127, "%s HOST: %s, ENDLINE: %s, PORT: %s",
insmodetext, dtty->name, dtty->seol, dtty->port);
break;
case DEV_UNIXSOCKET:
if(*dtty->name)
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) PATH: %s, ENDLINE: %s",
dtty->name, dtty->seol);
snprintf(buf, 127, "%s PATH: %s, ENDLINE: %s",
insmodetext, dtty->name, dtty->seol);
else // name starting from \0
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) PATH: \\0%s, ENDLINE: %s",
dtty->name+1, dtty->seol);
snprintf(buf, 127, "%s PATH: \\0%s, ENDLINE: %s",
insmodetext, dtty->name+1, dtty->seol);
break;
case DEV_TTY:
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) DEV: %s, ENDLINE: %s, SPEED: %d, FORMAT: %s",
dtty->name, dtty->seol, dtty->speed, dtty->port);
snprintf(buf, 127, "%s DEV: %s, ENDLINE: %s, SPEED: %d, FORMAT: %s",
insmodetext, dtty->name, dtty->seol, dtty->speed, dtty->port);
break;
default:
break;
@ -172,8 +207,11 @@ static void show_mode(bool for_resize){
snprintf(buf, 127, "INSERT (TAB to switch, ctrl+D to quit) NOT INITIALIZED");
}
}else{
snprintf(buf, 127, "SCROLL (TAB to switch, q to quit) ENDLINE: %s", dtty?dtty->seol:"n");
snprintf(buf, 127, "SCROLL (F1 - help) ENDLINE: %s", dtty?dtty->seol:"n");
}
wattron(sep_win, COLOR(BKGMARKED));
wprintw(sep_win, "%s ", dispnames[disp_type]);
wattroff(sep_win, COLOR(BKGMARKED));
wprintw(sep_win, "%s", buf);
if(for_resize) wnoutrefresh(sep_win);
else wrefresh(sep_win);
@ -253,6 +291,7 @@ void init_ncurses(){
fail_exit("Failed to allocate windows");
if(has_colors()){
init_pair(BKG_NO, COLOR_WHITE, COLOR_BLUE);
init_pair(BKGMARKED_NO, 1, COLOR_BLUE); // COLOR_RED used in my usefull_macros
init_pair(NORMAL_NO, COLOR_WHITE, COLOR_BLACK);
init_pair(MARKED_NO, COLOR_CYAN, COLOR_BLACK);
wbkgd(sep_win, COLOR(BKG));
@ -271,16 +310,18 @@ void deinit_ncurses(){
endwin();
}
static char *previous_line = NULL; // previous line in readline input
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(SendData(dtty, line) == -1){
if(!previous_line || strcmp(previous_line, line)) add_history(line); // omit repeats
FREE(previous_line);
if(convert_and_send(input_type, line) == -1){
ERRX("Device disconnected");
}
FREE(line);
previous_line = line;
}
}
@ -293,7 +334,22 @@ void init_readline(){
rl_getc_function = readline_getc;
rl_input_available_hook = readline_input_avail;
rl_redisplay_function = readline_redisplay;
rl_callback_handler_install("> ", got_command);
rl_callback_handler_install("", got_command);
}
/**
* @brief change_disp - change input or output data types (text/raw/hex)
* @param in, out - types for input and display
*/
static void change_disp(disptype in, disptype out){
if(in >= DISP_TEXT && in < DISP_UNCHANGED){
input_type = in;
DBG("input -> %s", dispnames[in]);
}
if(out >= DISP_TEXT && out < DISP_UNCHANGED){
disp_type = out;
}
show_mode(false);
}
void deinit_readline(){
@ -318,6 +374,33 @@ static void rollup(){
}
}
static const char *help[] = {
"Common commands:",
" F1 - show this help",
" F2 - text mode",
" F3 - raw mode (all symbols in hex codes)",
" F4 - hexdump mode (like hexdump output)",
" mouse scroll - scroll text output",
" q,^c,^d - quit",
" TAB - switch between scroll and edit modes",
" to change display/input (text/raw/hex) press Fx when scroll/edit",
" in scroll mode keys are almost the same like for this help"
" Text mode: in input and output all special symbols are like \\code",
" Raw mode: output only in hex, input in dec, 0xhex, 0bbin, 0oct (space separated)",
" Hexdump mode: output like hexdump, input only hex (with or without spaces)",
"",
"This help:",
" ^p,<Up> - scroll the viewport up by one row",
" ^n,<Down> - scroll the viewport down by one row",
" ^l,<Left> - scroll the viewport left by one column",
" ^r,<Right> - scroll the viewport right by one column",
" h,<Home> - scroll the viewport to top of file",
" ^F,<PageDn> - scroll to the next page",
" ^B,<PageUp> - scroll to the previous page",
" e,<End> - scroll the viewport to end of file",
0
};
/**
* @brief cmdline - console reading process; runs as separate thread
* @param arg - tty/socket device to write strings entered by user
@ -331,7 +414,24 @@ void *cmdline(void* arg){
int c = wgetch(cmd_win);
bool processed = true;
//DBG("wgetch got %d", c);
disptype dt = DISP_UNCHANGED;
switch(c){ // common keys for both modes
case KEY_F(1): // help
DBG("\n\nASK for help\n\n");
popup_msg(msg_win, help);
break;
case KEY_F(2): // TEXT mode
DBG("\n\nIN TEXT mode\n\n");
dt = DISP_TEXT;
break;
case KEY_F(3): // RAW mode
DBG("\n\nIN RAW mode\n\n");
dt = DISP_RAW;
break;
case KEY_F(4): // HEX mode
DBG("\n\nIN HEX mode\n\n");
dt = DISP_HEX;
break;
case KEY_MOUSE:
if(getmouse(&event) == OK){
if(event.bstate & (BUTTON4_PRESSED)) rolldown(); // wheel up
@ -348,6 +448,10 @@ void *cmdline(void* arg){
default:
processed = false;
}
if(dt != DISP_UNCHANGED){
if(insert_mode) change_disp(dt, DISP_UNCHANGED);
else change_disp(DISP_UNCHANGED, dt);
}
if(processed) continue;
if(insert_mode){
DBG("forward_to_readline(%d)", c);
@ -396,7 +500,7 @@ void *cmdline(void* arg){
while(*ptr) forward_to_readline(*ptr++);
}
}else{
switch(c){
switch(c){ // TODO: add home/end
case KEY_UP: // roll down for one item
rolldown();
break;

View File

@ -22,6 +22,13 @@
#include "dbg.h"
#include "ttysocket.h"
typedef enum{ // display/input data as
DISP_TEXT, // text (non-ASCII input and output as \xxx)
DISP_RAW, // hex output as xx xx xx, input in as numbers in bin (0bxx), oct(0xx), hex (0xxx||0Xxx) or dec and letters
DISP_HEX, // hexdump output, input in hex only (with or without spaces)
DISP_UNCHANGED // old
} disptype;
void init_readline();
void deinit_readline();
void init_ncurses();

187
popup_msg.c Normal file
View File

@ -0,0 +1,187 @@
/*
* This file is part of the ttyterm project.
* Copyright 2023 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/>.
*/
/****************************************************************************
* Copyright 2018-2020,2021 Thomas E. Dickey *
* Copyright 2017 Free Software Foundation, Inc. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, distribute with modifications, sublicense, and/or sell *
* copies of the Software, and to permit persons to whom the Software is *
* furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included *
* in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
* *
* Except as contained in this notice, the name(s) of the above copyright *
* holders shall not be used in advertising or otherwise to promote the *
* sale, use or other dealings in this Software without prior written *
* authorization. *
****************************************************************************/
/*
* $Id: popup_msg.c,v 1.11 2021/12/18 21:19:19 tom Exp $
*
* Show a multi-line message in a window which may extend beyond the screen.
*
* Thomas Dickey - 2017/4/15.
*/
#include <string.h>
#include "dbg.h"
#include "popup_msg.h"
static WINDOW *old_window;
static void begin_popup(void){
doupdate();
old_window = dupwin(curscr);
}
static void end_popup(void){
touchwin(old_window);
wnoutrefresh(old_window);
doupdate();
delwin(old_window);
}
/*
* Display a temporary window, e.g., to display a help-message.
*/
void popup_msg(WINDOW *parent, const char *const *msg){
int x0 = 4;
int y0 = 2;
int y1 = 0, x1 = 0;
int y2 = 0;
int wide = getmaxx(parent) - ((x0 + 1) * 2);
int high = getmaxy(parent) - ((y0 + 1) * 2);
WINDOW *help;
WINDOW *data;
int n;
int width = 0;
int length;
int last_y, last_x;
int ch = ERR;
for (n = 0; msg[n] != 0; ++n) {
int check = (int) strlen(msg[n]);
if (width < check) width = check;
}
length = n;
last_x = width - wide + 4;
if ((help = newwin(high, wide, y0, x0)) == 0)
return;
if ((data = newpad(length + 1, width + 1)) == 0) {
delwin(help);
return;
}
begin_popup();
keypad(data, TRUE);
for (n = 0; n < length; ++n){
waddstr(data, msg[n]);
if ((n + 1) < length) waddch(data, '\n');
}
y2 = getcury(data);
last_y = (y2 - (high - 3));
do{
switch (ch){
case KEY_HOME:
y1 = 0;
break;
case KEY_END:
y1 = last_y;
break;
case KEY_PREVIOUS:
case KEY_PPAGE:
if (y1 > 0) {
y1 -= high / 2;
if (y1 < 0)
y1 = 0;
} else {
beep();
}
break;
case KEY_NEXT:
case KEY_NPAGE:
if (y1 < last_y) {
y1 += high / 2;
if (y1 > last_y)
y1 = last_y;
} else {
beep();
}
break;
case CTRL('P'):
case KEY_UP:
if (y1 > 0)
--y1;
else
beep();
break;
case CTRL('N'):
case KEY_DOWN:
if (y1 < last_y)
++y1;
else
beep();
break;
case CTRL('L'):
case KEY_LEFT:
if(x1 > 0) --x1;
else beep();
break;
case CTRL('R'):
case KEY_RIGHT:
if(x1 < last_x) ++x1;
else beep();
break;
default:
beep();
break;
case ERR:
break;
}
werase(help);
box(help, 0, 0);
wnoutrefresh(help);
pnoutrefresh(data, y1, x1, y0 + 1, x0 + 1, high, wide);
doupdate();
} while ((ch = wgetch(data)) != ERR && ch != QUIT && ch != ESCAPE);
werase(help);
wrefresh(help);
delwin(help);
delwin(data);
end_popup();
}

25
popup_msg.h Normal file
View File

@ -0,0 +1,25 @@
/*
* This file is part of the ttyterm project.
* Copyright 2023 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
#include <curses.h>
#define QUIT CTRL('Q')
#define ESCAPE CTRL('[')
void popup_msg(WINDOW *parent, const char *const *msg);

220
string_functions.c Normal file
View File

@ -0,0 +1,220 @@
/*
* This file is part of the ttyterm project.
* Copyright 2023 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/>.
*/
#include <stdio.h>
#include "string_functions.h"
// read text string and throw out all < 31 and > 126
static inline const char *omit_nonletters(const char *line){
while(*line){
char c = *line;
if(c > 31 && c < 127) break;
++line;
}
return line;
}
// return hex of given sybol (0..9, a..f or A..F) or -1
static inline int hex2i(char c){
if(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;
return -1;
}
static inline const char *getbin(const char *line, int *ch){
char c = *line;
if(c < '0' || c > '1'){
*ch = -1;
return line+1;
}
int num = 0, ctr = 8;
do{
if(--ctr < 0) break; // nineth symbol
if(c < '0' || c > '1'){
break;
}
num <<= 1;
if(c == '1') num |= 1;
++line;
}while((c = *line));
*ch = num < 256 ? num : -1;
return line;
}
/**
* @brief getoct read octal number without first '0' (up to three symbols)
* @param line - pointer to string
* @param ch - data read (or -1 if error)
* @return pointer to next symbol after this (or first incorrect symbol)
*/
static inline const char *getoct(const char *line, int *ch){
char c = *line;
if(c < '0' || c > '7'){
*ch = -1;
return line+1;
}
int num = 0, ctr = 3;
do{
if(--ctr < 0) break; // fourth symbol
if(c < '0' || c > '7'){ // other symbol
break;
}
num <<= 3;
num += c - '0';
++line;
}while((c = *line));
*ch = num < 256 ? num : -1;
return line;
}
static inline const char *getdec(const char *line, int *ch){
char c = *line;
if(c < '0' || c > '9'){
*ch = -1;
return line+1;
}
int num = 0, ctr = 3;
do{
if(--ctr < 0) break; // fourth symbol
if(c < '0' || c > '9'){ // other symbol
break;
}
num *= 10;
num += c - '0';
++line;
}while((c = *line));
*ch = num < 256 ? num : -1;
return line;
}
/**
* @brief gethex - read hex number without first '\x'
* @param line - pointer to string
* @param ch - data read (or -1 if error)
* @return pointer to next symbol after hex number (x or xx)
*/
static inline const char *gethex(const char *line, int *ch){
int i = hex2i(*line++);
if(i > -1){
int j = hex2i(*line);
if(j > -1){
i = (i<<4) | j;
++line;
}
}
*ch = i;
return line;
}
/**
* @brief getspec - get special symbol (after '\') (without unicode support!)
* @param line - pointer to string
* @param ch - data read (or -1 if error)
* @return pointer to next symbol after this
*/
static inline const char *getspec(const char *line, int *ch){
if(!*line){ *ch = -1; return line; }
int got = -1, s = *line++; // next symbol after '\'
if(s >= '0' && s <= '7'){ // octal symbol
line = getoct(line-1, &got);
}else switch(s){
case 'a': got = '\a'; break;
case 'b': got = '\b'; break;
case 'e': got = '\e'; break;
case 'f': got = '\f'; break;
case 'n': got = '\n'; break;
case 'r': got = '\r'; break;
case 't': got = '\t'; break;
case 'v': got = '\v'; break;
case 'x': line = gethex(line, &got); break; // hex symbol
default:// return "as is" all other printable symbols
if(s > 31 && s < 127) got = s;
break;
}
*ch = got;
return line;
}
/**
* @brief convert_and_send - convert input line and send it (in text mode add `eol`)
* @param line - line with data
* @return amount of bytes sent, 0 if error or -1 if disconnect
*/
int convert_and_send(disptype input_type, const char *line){
static char *buf = NULL;
static size_t bufsiz = 0;
size_t curpos = 0; // position in `buf`
line = omit_nonletters(line);
while(*line){
if(curpos >= bufsiz){ // out ouptut buffer can't be larger than input
bufsiz += BUFSIZ;
buf = realloc(buf, bufsiz);
}
int ch = -1;
switch(input_type){
case DISP_TEXT: // only check for '\'
ch = *line++;
if(ch == '\\') line = getspec(line, &ch);
break;
case DISP_RAW: // read next uint8_t and put into buffer
ch = *line++;
if(ch == '0'){ // number: 0, 0xHH, 0OOO, 0bBBBBBBBB
ch = *line;
switch(ch){
case 'x': // hexadecimal
case 'X':
line = gethex(line + 1, &ch);
break;
case 'b':
case 'B':
line = getbin(line + 1, &ch);
break;
default: // zero or octal
if(ch >= '0' && ch <= '7') line = getoct(line, &ch);
else ch = 0;
break;
}
}else if(ch > '0' && ch <= '9'){ // decimal number
line = getdec(line-1, &ch);
} // else - letter (without escape-symbols!)
break;
case DISP_HEX: // read next 2 hex bytes and put into buffer
line = gethex(line, &ch);
break;
default:
return 0; // unknown display type
}
if(ch > -1) buf[curpos++] = ch;
line = omit_nonletters(line);
}
// now insert EOL in text mode
if(input_type == DISP_TEXT){
if(curpos+2 >= bufsiz){
bufsiz += BUFSIZ;
buf = realloc(buf, bufsiz);
}
int l; char *e = geteol(&l);
snprintf(buf+curpos, l, "%s", e);
curpos += l;
}
return SendData(buf, curpos);
}

23
string_functions.h Normal file
View File

@ -0,0 +1,23 @@
/*
* This file is part of the ttyterm project.
* Copyright 2023 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
#include "ncurses_and_readline.h"
int convert_and_send(disptype input_type, const char *line);

View File

@ -33,6 +33,8 @@
static int sec = 0, usec = 100; // timeout
static FILE *dupfile = NULL; // file for output
static chardevice *device = NULL; // current opened device
// TODO: if unix socket name starts with \0 translate it as \\0 to d->name!
// set Read_tty timeout in milliseconds
@ -76,31 +78,31 @@ static int waittoread(int fd){
}
// substitute all EOL's by '\n'
static size_t rmeols(chardevice *d){
if(!d){
static size_t rmeols(){
if(!device){
DBG("d is NULL");
return 0;
}
TTY_descr2 *D = d->dev;
TTY_descr2 *D = device->dev;
if(!D || D->comfd < 0){
DBG("D bad");
return 0;
}
if(0 == strcmp(d->eol, "\n")){
if(0 == strcmp(device->eol, "\n")){
DBG("No subs need");
return D->buflen; // don't need to do this
}
int L = strlen(D->buf);
char *newbuf = MALLOC(char, L), *ptr = D->buf, *eptr = D->buf + L;
while(ptr < eptr){
char *eol = strstr(ptr, d->eol);
char *eol = strstr(ptr, device->eol);
if(eol){
eol[0] = '\n';
eol[1] = 0;
}
strcat(newbuf, ptr);
if(!eol) break;
ptr = eol + d->eollen;
ptr = eol + device->eollen;
}
strcpy(D->buf, newbuf);
FREE(newbuf);
@ -108,10 +110,16 @@ static size_t rmeols(chardevice *d){
return D->buflen;
}
char *geteol(int *s){
if(!device) return NULL;
*s = device->eollen;
return device->eol;
}
// get data drom TTY
static char *getttydata(chardevice *d, int *len){
if(!d || !d->dev) return NULL;
TTY_descr2 *D = d->dev;
static char *getttydata(int *len){
if(!device || !device->dev) return NULL;
TTY_descr2 *D = device->dev;
if(D->comfd < 0) return NULL;
int L = 0;
int length = D->bufsz;
@ -130,7 +138,7 @@ static char *getttydata(chardevice *d, int *len){
}
ptr += l; L += l;
length -= l;
if(L >= d->eollen && 0 == strcmp(&ptr[-(d->eollen)], d->eol)){ // found end of line
if(L >= device->eollen && 0 == strcmp(&ptr[-(device->eollen)], device->eol)){ // found end of line
break;
}
}while(length);
@ -138,14 +146,14 @@ static char *getttydata(chardevice *d, int *len){
D->buf[L] = 0;
if(len) *len = L;
if(!L) return NULL;
rmeols(d);
rmeols(device);
DBG("buffer len: %zd, content: =%s=", D->buflen, D->buf);
return D->buf;
}
static char *getsockdata(chardevice *d, int *len){
if(!d || !d->dev) return NULL;
TTY_descr2 *D = d->dev;
static char *getsockdata(int *len){
if(!device || !device->dev) return NULL;
TTY_descr2 *D = device->dev;
if(D->comfd < 0) return NULL;
char *ptr = NULL;
int n = waittoread(D->comfd);
@ -155,7 +163,7 @@ static char *getsockdata(chardevice *d, int *len){
ptr = D->buf;
ptr[n] = 0;
D->buflen = n;
n = rmeols(d);
n = rmeols(device);
DBG("got %d: ..%s..", n, ptr);
}else{
DBG("Got nothing");
@ -172,23 +180,24 @@ static char *getsockdata(chardevice *d, int *len){
* @param len (o) - length of data read (-1 if device disconnected)
* @return NULL or string
*/
char *ReadData(chardevice *d, int *len){
if(!d || !d->dev) return NULL;
char *ReadData(int *len){
if(!device || !device->dev) return NULL;
if(len) *len = -1;
char *r = NULL;
switch(d->type){
switch(device->type){
case DEV_TTY:
r = getttydata(d, len);
r = getttydata(len);
break;
case DEV_NETSOCKET:
case DEV_UNIXSOCKET:
r = getsockdata(d, len);
r = getsockdata(len);
break;
default:
break;
}
if(r && dupfile){
fprintf(dupfile, "< %s", r);
fwrite("< ", 1, 2, dupfile);
fwrite(r, 1, *len, dupfile);
}
return r;
}
@ -196,40 +205,33 @@ char *ReadData(chardevice *d, int *len){
/**
* @brief SendData - send data to tty or socket
* @param d - device
* @param str - text string
* @return 0 if error, -1 if disconnected
* @param data - buffer with data
* @return 0 if error or empty string, -1 if disconnected
*/
int SendData(chardevice *d, char *str){
char buf[BUFSIZ];
if(!d) return -1;
DBG("send %s", str);
if(!str) return 0;
int SendData(const char *data, size_t len){
if(!device) return -1;
if(!data || len == 0) return 0;
int ret = 0;
if(0 == pthread_mutex_lock(&d->mutex)){
int l = strlen(str), lplus = l + d->eollen;
if(l < 1) return 0;
if(lplus > BUFSIZ-1) lplus = BUFSIZ-1;
snprintf(buf, lplus+1, "%s%s", str, d->eol);
DBG("SENDBUF (%d): _%s_", lplus, buf);
switch(d->type){
if(0 == pthread_mutex_lock(&device->mutex)){
switch(device->type){
case DEV_TTY:
if(write_tty(d->dev->comfd, buf, lplus)) ret = 0;
else ret = l;
if(write_tty(device->dev->comfd, data, len)) ret = 0;
else ret = len;
break;
case DEV_NETSOCKET:
case DEV_UNIXSOCKET:
if(lplus != send(d->dev->comfd, buf, lplus, MSG_NOSIGNAL)) ret = 0;
else ret = l;
pthread_mutex_unlock(&d->mutex);
if(len != (size_t)send(device->dev->comfd, data, len, MSG_NOSIGNAL)) ret = 0;
else ret = len;
break;
default:
str = NULL;
data = NULL;
break;
}
if(str && dupfile){
fprintf(dupfile, "> %s", buf);
if(data && dupfile){
fwrite("> ", 1, 2, dupfile);
fwrite(data, 1, len, dupfile);
}
pthread_mutex_unlock(&d->mutex);
pthread_mutex_unlock(&device->mutex);
}else ret = -1;
DBG("ret=%d", ret);
return ret;
@ -237,8 +239,8 @@ int SendData(chardevice *d, char *str){
static const int socktypes[] = {SOCK_STREAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_DCCP, SOCK_PACKET, SOCK_DGRAM, 0};
static TTY_descr2* opensocket(chardevice *d){
if(!d) return FALSE;
static TTY_descr2* opensocket(){
if(!device) return FALSE;
TTY_descr2 *descr = MALLOC(TTY_descr2, 1); // only for `buf` and bufsz/buflen
descr->buf = MALLOC(char, BUFSIZ);
descr->bufsz = BUFSIZ;
@ -250,11 +252,11 @@ static TTY_descr2* opensocket(chardevice *d){
struct sockaddr *sa = NULL;
socklen_t addrlen = 0;
int domain = -1;
if(d->type == DEV_NETSOCKET){
DBG("NETSOCK to %s", d->name);
if(device->type == DEV_NETSOCKET){
DBG("NETSOCK to %s", device->name);
sa = (struct sockaddr*) &addr;
addrlen = sizeof(addr);
if((host = gethostbyname(d->name)) == NULL ){
if((host = gethostbyname(device->name)) == NULL ){
WARN("gethostbyname()");
FREE(descr->buf);
FREE(descr);
@ -263,7 +265,7 @@ static TTY_descr2* opensocket(chardevice *d){
struct in_addr *ia = (struct in_addr*)host->h_addr_list[0];
DBG("addr: %s", inet_ntoa(*ia));
addr.sin_family = AF_INET;
int p = atoi(d->port); DBG("PORT: %s - %d", d->port, p);
int p = atoi(device->port); DBG("PORT: %s - %d", device->port, p);
addr.sin_port = htons(p);
//addr.sin_addr.s_addr = *(long*)(host->h_addr);
addr.sin_addr.s_addr = ia->s_addr;
@ -273,16 +275,16 @@ static TTY_descr2* opensocket(chardevice *d){
sa = (struct sockaddr*) &saddr;
addrlen = sizeof(saddr);
saddr.sun_family = AF_UNIX;
if(*(d->name) == 0){ // if sun_path[0] == 0 then don't create a file
if(*(device->name) == 0){ // if sun_path[0] == 0 then don't create a file
DBG("convert name");
saddr.sun_path[0] = 0;
strncpy(saddr.sun_path+1, d->name+1, 105);
strncpy(saddr.sun_path+1, device->name+1, 105);
}
else if(strncmp("\\0", d->name, 2) == 0){
else if(strncmp("\\0", device->name, 2) == 0){
DBG("convert name");
saddr.sun_path[0] = 0;
strncpy(saddr.sun_path+1, d->name+2, 105);
}else strncpy(saddr.sun_path, d->name, 106);
strncpy(saddr.sun_path+1, device->name+2, 105);
}else strncpy(saddr.sun_path, device->name, 106);
domain = AF_UNIX;
}
const int *type = socktypes;
@ -363,17 +365,17 @@ someerr:
return NULL;
}
static TTY_descr2* opentty(chardevice *d){
if(!d->name){
static TTY_descr2* opentty(){
if(!device->name){
/// ïÔÓÕÔÓÔ×ÕÅÔ ÉÍÑ ÐÏÒÔÁ
WARNX(_("Port name is missing"));
return NULL;
}
TTY_descr2 *descr = MALLOC(TTY_descr2, 1);
descr->portname = strdup(d->name);
descr->speed = d->speed;
descr->portname = strdup(device->name);
descr->speed = device->speed;
tcflag_t flags;
descr->format = parse_format(d->port, &flags);
descr->format = parse_format(device->port, &flags);
if(!descr->format) goto someerr;
descr->buf = MALLOC(char, BUFSIZ);
descr->bufsz = BUFSIZ-1;
@ -389,14 +391,14 @@ static TTY_descr2* opentty(chardevice *d){
descr->tty.c_lflag = 0; // ~(ICANON | ECHO | ECHOE | ISIG)
descr->tty.c_iflag = 0;
descr->tty.c_cflag = BOTHER | flags |CREAD|CLOCAL;
descr->tty.c_ispeed = d->speed;
descr->tty.c_ospeed = d->speed;
descr->tty.c_ispeed = device->speed;
descr->tty.c_ospeed = device->speed;
if(ioctl(descr->comfd, TCSETS2, &descr->tty)){
WARN(_("Can't set new port config"));
goto someerr;
}
ioctl(descr->comfd, TCGETS2, &descr->tty);
d->speed = descr->tty.c_ispeed;
device->speed = descr->tty.c_ispeed;
#ifdef EBUG
printf("ispeed: %d, ospeed: %d, cflag=%d (BOTHER=%d)\n", descr->tty.c_ispeed, descr->tty.c_ospeed, descr->tty.c_cflag&CBAUD, BOTHER);
if(system("stty -F /dev/ttyUSB0")) WARN("system()");
@ -417,20 +419,24 @@ someerr:
int opendev(chardevice *d, char *path){
if(!d) return FALSE;
DBG("Try to open device");
switch(d->type){
device = MALLOC(chardevice, 1);
memcpy(device, d, sizeof(chardevice));
device->name = strdup(d->name);
device->port = strdup(d->port);
switch(device->type){
case DEV_TTY:
DBG("Serial");
d->dev = opentty(d);
if(!d->dev){
WARN("Can't open device %s", d->name);
device->dev = opentty();
if(!device->dev){
WARN("Can't open device %s", device->name);
DBG("CANT OPEN");
return FALSE;
}
break;
case DEV_NETSOCKET:
case DEV_UNIXSOCKET:
d->dev = opensocket(d);
if(!d->dev){
device->dev = opensocket();
if(!device->dev){
WARNX("Can't open socket");
DBG("CANT OPEN");
return FALSE;
@ -443,44 +449,45 @@ int opendev(chardevice *d, char *path){
dupfile = fopen(path, "a");
if(!dupfile){
WARN("Can't open %s", path);
closedev(d);
closedev();
return FALSE;
}
}
return TRUE;
}
void closedev(chardevice *d){
if(!d) return;
pthread_mutex_unlock(&d->mutex);
pthread_mutex_trylock(&d->mutex);
void closedev(){
if(!device) return;
pthread_mutex_unlock(&device->mutex);
pthread_mutex_trylock(&device->mutex);
if(dupfile){
fclose(dupfile);
dupfile = NULL;
}
switch(d->type){
switch(device->type){
case DEV_TTY:
if(d->dev){
TTY_descr2 *t = d->dev;
if(device->dev){
TTY_descr2 *t = device->dev;
ioctl(t->comfd, TCSETS2, &t->oldtty); // return TTY to previous state
close(t->comfd);
}
break;
case DEV_NETSOCKET:
if(d->dev){
close(d->dev->comfd);
FREE(d->dev);
if(device->dev){
close(device->dev->comfd);
FREE(device->dev);
}
break;
default:
return;
}
if(d->dev){
FREE(d->dev->format);
FREE(d->dev->portname);
FREE(d->dev->buf);
FREE(d->dev);
if(device->dev){
FREE(device->dev->format);
FREE(device->dev->portname);
FREE(device->dev->buf);
FREE(device->dev);
}
FREE(d->name);
FREE(device->name);
FREE(device);
DBG("Device closed");
}

View File

@ -23,9 +23,10 @@
#include <pthread.h>
#include <asm-generic/termbits.h>
#include <stdbool.h>
#include <stdint.h>
//#include "dbg.h"
typedef enum{
typedef enum{ // device: tty terminal, network socket or UNIX socket
DEV_TTY,
DEV_NETSOCKET,
DEV_UNIXSOCKET,
@ -55,10 +56,11 @@ typedef struct{
int eollen; // length of `eol`
} chardevice;
char *ReadData(chardevice *d, int *l);
int SendData(chardevice *d, char *str);
char *geteol(int *s);
char *ReadData(int *l);
int SendData(const char *data, size_t len);
void settimeout(int tms);
int opendev(chardevice *d, char *path);
void closedev(chardevice *d);
void closedev();
#endif // TTY_H__