/* * This file is part of the ttyterm project. * Copyright 2023 Edward V. Emelianov . * * 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 . */ #include #include #include "string_functions.h" // end of line - for text mode static char *eol = "\n"; static int eollen = 1; // read text string and throw out all < 31 and > 126 static inline const char *omit_nonletters(disptype input_type, const char *line){ int start = (input_type == DISP_TEXT) ? 31 : 32; // remove spaces for non-TEXT modes while(*line){ char c = *line; if(c > start && 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; } /* static inline const char* getu32(const char *str, uint32_t *val){ char *eptr; if(!str) return NULL; long ll = strtol(str, &eptr, 0); if(ll < 0 || ll > UINT32_MAX){ // wrong number return NULL; } if(val) *val = (uint32_t)ll; return omit_nonletters(DISP_RTU, eptr); }*/ /** * @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 uint8_t *buf = NULL; static size_t bufsiz = 0; size_t curpos = 0; // position in `buf` line = omit_nonletters(input_type, line); DBG("got: '%s' to send", line); void CHKbufsiz(size_t sz){ if(curpos + sz >= bufsiz){ // out ouptut buffer can't be larger than input bufsiz += BUFSIZ; buf = realloc(buf, bufsiz); } } while(*line){ CHKbufsiz(1); 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 case DISP_RTURAW: // the same (but calculate CRC at the end) 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 case DISP_RTUHEX: // the same (but calculate CRC at the end) line = gethex(line, &ch); break; default: return 0; // unknown display type } if(ch > -1) buf[curpos++] = ch; line = omit_nonletters(input_type, line); } // now insert EOL in text mode if(input_type == DISP_TEXT){ if(curpos+eollen >= bufsiz){ bufsiz += BUFSIZ; buf = realloc(buf, bufsiz); } memcpy(buf+curpos, eol, eollen); curpos += eollen; DBG("Add EOL"); }else if(input_type == DISP_RTURAW || input_type == DISP_RTUHEX){ // calculate CRC CHKbufsiz(2); uint16_t crc = 0xFFFF; for(size_t pos = 0; pos < curpos; ++pos){ crc ^= (uint16_t)buf[pos]; for(int i = 8; i; --i){ if((crc & 1)){ crc >>= 1; crc ^= 0xA001; }else crc >>= 1; } } buf[curpos++] = crc & 0xff; // Lo buf[curpos++] = crc >> 8; // Hi } return SendData(buf, curpos); } /** * @brief changeeol - set EOL to given * @param e - new end of line for text mode * WARNING! This function doesn't call free(), so don't call it more than once */ void changeeol(const char *e){ eollen = strlen(e); eol = strdup(e); }