Guest User

Untitled

a guest
Jul 16th, 2018
264
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 40.22 KB | None | 0 0
  1. /*
  2. * ssterm - simple serial-port terminal.
  3. * Version 1.0 - October 2009
  4. * Written by Vanya A. Sergeev - <vsergeev@gmail.com>
  5. *
  6. * Copyright (C) 2009 Vanya A. Sergeev
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. */
  21.  
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/types.h>
  26. #include <sys/stat.h>
  27. #include <sys/ioctl.h>
  28. #include <fcntl.h>
  29. #include <unistd.h>
  30. #include <termios.h>
  31. #include <signal.h>
  32. #include <pthread.h>
  33. #include <curses.h>
  34. #include <errno.h>
  35. #include <getopt.h>
  36.  
  37. /* Much of the dirty work is keeping the circular receive buffer printing
  38. * cleanly with the fixed size and non-wrapping curses pad, which displays the
  39. * actual data. */
  40.  
  41. /* Todo:
  42. * write file configuration backend
  43. * custom color coded characters?
  44.  
  45. * fixed pad, redraw buffer window each time?
  46.  
  47. * sending control characters
  48. * setting control lines
  49. */
  50.  
  51. /******************************************************************************
  52. ******************************************************************************
  53. ******************************************************************************/
  54.  
  55. /* Possible error codes for tty configure, read, write functions */
  56. #define ERROR_ERRNO -1
  57. #define ERROR_BAUDRATE -2
  58. #define ERROR_DATABITS -3
  59. #define ERROR_PARITY -4
  60. #define ERROR_STOPBITS -5
  61. #define ERROR_FLOWCONTROL -6
  62.  
  63. /* Options for the serial port */
  64. #define PARITY_NONE 0
  65. #define PARITY_ODD 1
  66. #define PARITY_EVEN 2
  67. #define FLOW_CONTROL_NONE 0
  68. #define FLOW_CONTROL_RTSCTS 1
  69. #define FLOW_CONTROL_XONXOFF 2
  70.  
  71. #define PARITY_NONE_STR "none"
  72. #define PARITY_ODD_STR "odd"
  73. #define PARITY_EVEN_STR "even"
  74. #define FLOW_CONTROL_NONE_STR "none"
  75. #define FLOW_CONTROL_RTSCTS_STR "rtscts"
  76. #define FLOW_CONTROL_XONXOFF_STR "xonxoff"
  77. #define NEWLINE_NONE_STR "none"
  78. #define NEWLINE_CR_STR "cr"
  79. #define NEWLINE_LF_STR "lf"
  80. #define NEWLINE_CRLF_STR "crlf"
  81. #define NEWLINE_CRORLF_STR "crorlf"
  82. #define NEWLINE_RAW_STR "raw"
  83.  
  84. /* Special curses key definitions */
  85. #define CTRL_C 0x03
  86. #define CTRL_D 0x04
  87. #define CTRL_H 0x08
  88. #define CTRL_L 0x0C
  89. #define CTRL_N 0x0E
  90. #define CTRL_O 0x0F
  91. #define CTRL_R 0x12
  92.  
  93. /* Signals to the read thread */
  94. #define SIGNAL_RTH_EXIT (1<<0)
  95. #define SIGNAL_RTH_SCREEN_REFRESH (1<<1)
  96. #define SIGNAL_RTH_BUFFER_CLEAR (1<<2)
  97. #define SIGNAL_RTH_BUFFER_DUMP (1<<3)
  98.  
  99. /* Screen modes */
  100. #define UI_OPTION_STDIN_STDOUT (1<<0)
  101. #define UI_OPTION_ECHO (1<<1)
  102. #define UI_OPTION_HEX (1<<2)
  103. #define UI_OPTION_HEX_NEWLINE (1<<3)
  104. #define UI_OPTION_COLOR_CODED (1<<4)
  105.  
  106. /* CR and LF mapping bits */
  107. #define OPTION_NEWLINE_NONE 0
  108. #define OPTION_NEWLINE_CR (1<<0)
  109. #define OPTION_NEWLINE_LF (1<<1)
  110. #define OPTION_NEWLINE_CRLF (1<<2)
  111. #define OPTION_NEWLINE_CRORLF (OPTION_NEWLINE_CR|OPTION_NEWLINE_LF)
  112. #define OPTION_NEWLINE_RAW (1<<3)
  113.  
  114. /* Important options */
  115. #define DUMP_FILENAME_PREFIX "ssterm-dump-"
  116. #define DUMP_MAX_FILES 100
  117. #define DEFAULT_BUFFER_SIZE 4096
  118.  
  119. /******************************************************************************
  120. ******************************************************************************
  121. ******************************************************************************/
  122.  
  123. /* default serial port settings */
  124. int tty_baudrate = 9600;
  125. int tty_parity = PARITY_NONE;
  126. int tty_databits = 8;
  127. int tty_stopbits = 1;
  128. int tty_flowcontrol = FLOW_CONTROL_NONE;
  129. int tty_output_newline = OPTION_NEWLINE_RAW;
  130. int tty_input_newline = OPTION_NEWLINE_LF;
  131. int tty_buffer_size = DEFAULT_BUFFER_SIZE;
  132.  
  133. /******************************************************************************
  134. ******************************************************************************
  135. ******************************************************************************/
  136.  
  137. /* serial port file descriptor */
  138. int tty_fd;
  139.  
  140. /* circular buffer data structure to hold tty data */
  141. unsigned char *tty_buffer;
  142. int tty_buffer_index_1;
  143. int tty_buffer_index_2;
  144. int tty_buffer_wrap;
  145.  
  146. /* read thread */
  147. pthread_t read_thread;
  148. /* signaling variable to read thread */
  149. int read_thread_signal;
  150.  
  151. /* curses window and coordinates */
  152. WINDOW *screen_pad;
  153. /* screen viewport index of the curses pad */
  154. int screen_pad_y;
  155. /* curses max lines and cols saved from original measurement */
  156. int screen_max_lines, screen_max_cols;
  157. int stdout_cursor_x;
  158. /* default color coded characters and colors */
  159. char screen_color_coded_chars[] = {'\r', '\n'};
  160. short screen_color_coded_colors[] = {COLOR_MAGENTA, COLOR_CYAN};
  161.  
  162. /* options variable for different UI functionality */
  163. int ui_options;
  164.  
  165. /******************************************************************************
  166. ******************************************************************************
  167. ******************************************************************************/
  168.  
  169. void handler_sigint(int signal);
  170.  
  171. int tty_open(const char *device, int options);
  172. int tty_set_options(void);
  173. int tty_read_circular(void);
  174. int tty_read_regular(void);
  175. int tty_write(unsigned char *data, int data_len);
  176. void tty_buffer_clear(void);
  177. int tty_buffer_dump(void);
  178.  
  179. int screen_init(void);
  180. void screen_cleanup(void);
  181. void screen_scroll_home(void);
  182. void screen_scroll_end(void);
  183. void screen_scroll_up(int lines);
  184. void screen_scroll_down(int lines);
  185. void screen_update(int index_1, int index_2);
  186.  
  187. void stdout_print(void);
  188.  
  189. void *read_curses_loop(void *id);
  190. void write_curses_loop(void);
  191.  
  192. int read_write_stdin_loop(void);
  193.  
  194. static void printVersion(FILE *stream);
  195. static void printCommands(FILE *stream);
  196. static void printUsage(FILE *stream, const char *programName);
  197. int main(int argc, char *argv[]);
  198.  
  199. /******************************************************************************
  200. *** SIGINT Handler / Clean Up ***
  201. ******************************************************************************/
  202.  
  203. void handler_sigint(int signal) {
  204. /* Clean up curses */
  205. screen_cleanup();
  206. /* Tell our read thread to exit */
  207. read_thread_signal |= SIGNAL_RTH_EXIT;
  208. /* Join our read thread to this thread */
  209. pthread_join(read_thread, NULL);
  210. /* Make sure all of our pthreads have terminaed */
  211. pthread_exit(NULL);
  212. /* Close the serial port */
  213. close(tty_fd);
  214. /* Free our tty buffer */
  215. free(tty_buffer);
  216. /* Exit */
  217. exit(EXIT_SUCCESS);
  218. }
  219.  
  220. /******************************************************************************
  221. *** Serial port options, read, write ***
  222. ******************************************************************************/
  223.  
  224. int tty_open(const char *device, int options) {
  225.  
  226. /* Open the serial port */
  227. tty_fd = open(device, options);
  228. if (tty_fd < 0)
  229. return ERROR_ERRNO;
  230.  
  231. return 0;
  232. }
  233.  
  234. int tty_set_options(void) {
  235. int retVal;
  236. struct termios options;
  237. speed_t new_baudrate;
  238.  
  239. /* Grab the current options */
  240. retVal = tcgetattr(tty_fd, &options);
  241. if (retVal < 0)
  242. return ERROR_ERRNO;
  243.  
  244. switch (tty_baudrate) {
  245. case 50: new_baudrate = B50; break;
  246. case 75: new_baudrate = B75; break;
  247. case 110: new_baudrate = B110; break;
  248. case 134: new_baudrate = B134; break;
  249. case 150: new_baudrate = B150; break;
  250. case 200: new_baudrate = B200; break;
  251. case 300: new_baudrate = B300; break;
  252. case 600: new_baudrate = B600; break;
  253. case 1200: new_baudrate = B1200; break;
  254. case 1800: new_baudrate = B1800; break;
  255. case 2400: new_baudrate = B2400; break;
  256. case 4800: new_baudrate = B4800; break;
  257. case 9600: new_baudrate = B9600; break;
  258. case 19200: new_baudrate = B19200; break;
  259. case 38400: new_baudrate = B38400; break;
  260. case 57600: new_baudrate = B57600; break;
  261. case 115200: new_baudrate = B115200; break;
  262. case 230400: new_baudrate = B230400; break;
  263. /* Baudrates B460800 and up removed due to the lack of these
  264. * definitions in some *nix platforms. */
  265. default: return ERROR_BAUDRATE;
  266. }
  267.  
  268. /* Clear cflag and set it from scratch */
  269. options.c_cflag = 0;
  270.  
  271. /* Set the input and output baudrates */
  272. retVal = cfsetispeed(&options, new_baudrate);
  273. if (retVal < 0)
  274. return ERROR_BAUDRATE;
  275. retVal = cfsetospeed(&options, new_baudrate);
  276. if (retVal < 0)
  277. return ERROR_BAUDRATE;
  278.  
  279. switch (tty_databits) {
  280. case 5: options.c_cflag |= CS5; break;
  281. case 6: options.c_cflag |= CS6; break;
  282. case 7: options.c_cflag |= CS7; break;
  283. case 8: options.c_cflag |= CS8; break;
  284. default: return ERROR_DATABITS;
  285. }
  286.  
  287. switch (tty_parity) {
  288. case PARITY_NONE: break;
  289. case PARITY_EVEN: options.c_cflag |= PARENB; break;
  290. case PARITY_ODD: options.c_cflag |= (PARENB | PARODD); break;
  291. default: return ERROR_PARITY;
  292. }
  293.  
  294. switch (tty_stopbits) {
  295. case 1: break;
  296. case 2: options.c_cflag |= CSTOPB; break;
  297. default: return ERROR_STOPBITS;
  298. }
  299.  
  300. switch (tty_flowcontrol) {
  301. case FLOW_CONTROL_NONE: break;
  302. case FLOW_CONTROL_RTSCTS: options.c_cflag |= CRTSCTS; break; /* We'll handle this later, in c_iflag */
  303. case FLOW_CONTROL_XONXOFF: break;
  304. default: return ERROR_FLOWCONTROL;
  305. }
  306.  
  307. /* Enable the receiver */
  308. options.c_cflag |= (CREAD | CLOCAL);
  309.  
  310. /* Turn off signals generated from special characters, turn of canonical
  311. * mode so we can have raw input */
  312. //options.c_lflag &= ~(ISIG | ECHO | ICANON);
  313. options.c_lflag = 0;
  314.  
  315. /* Turn off any POSIX defined output processing and character
  316. * mapping/delays for pure raw output */
  317. options.c_oflag = 0;
  318.  
  319. /* Ignore break characters */
  320. options.c_iflag = IGNBRK;
  321. /* Enable parity checking and parity bit stripping if we are using
  322. * parity */
  323. if (tty_parity != PARITY_NONE)
  324. options.c_iflag |= (INPCK | ISTRIP);
  325.  
  326. /* Turn on XON/XOFF if we want software flow control */
  327. if (tty_flowcontrol == FLOW_CONTROL_XONXOFF)
  328. options.c_iflag |= (IXON | IXOFF | IXANY);
  329.  
  330. /* Set the new options of the serial port */
  331. retVal = tcsetattr(tty_fd, TCSANOW, &options);
  332. if (retVal < 0)
  333. return ERROR_ERRNO;
  334.  
  335. return 0;
  336. }
  337.  
  338. int tty_read_circular(void) {
  339. int retVal;
  340.  
  341. /* Wrap around if we reach the end of the circular buffer */
  342. if (tty_buffer_index_2 == tty_buffer_size) {
  343. tty_buffer_index_2 = 0;
  344. tty_buffer_wrap = 1;
  345. }
  346.  
  347. /* Catch up index 1 to index 2, now that we've processed the
  348. * previously new data between index 1 and index 2 */
  349. tty_buffer_index_1 = tty_buffer_index_2;
  350.  
  351. /* Read as much data as we can */
  352. retVal = read(tty_fd, tty_buffer+tty_buffer_index_1, tty_buffer_size-tty_buffer_index_2);
  353. if (retVal < 0 && errno != EWOULDBLOCK)
  354. return ERROR_ERRNO;
  355.  
  356. /* If no data was returned because none was available */
  357. if (retVal < 0 && errno == EWOULDBLOCK)
  358. tty_buffer_index_2 += 0;
  359. else
  360. /* Otherwise increment our index 2 by the number of read characters */
  361. tty_buffer_index_2 += retVal;
  362.  
  363. return 0;
  364. }
  365.  
  366. int tty_read_regular(void) {
  367. int retVal;
  368.  
  369. /* Read as much data as we can */
  370. retVal = read(tty_fd, tty_buffer, tty_buffer_size);
  371. if (retVal < 0 && errno != EWOULDBLOCK)
  372. return ERROR_ERRNO;
  373.  
  374. /* If no data was returned because none was available */
  375. if (retVal < 0 && errno == EWOULDBLOCK)
  376. tty_buffer_index_1 = 0;
  377. else
  378. /* Otherwise set our index 1 by the number of read characters */
  379. tty_buffer_index_1 = retVal;
  380.  
  381. return 0;
  382. }
  383.  
  384. int tty_write(unsigned char *data, int data_len) {
  385. int retVal;
  386.  
  387. retVal = write(tty_fd, data, data_len);
  388. if (retVal < 0)
  389. return ERROR_ERRNO;
  390.  
  391. return 0;
  392. }
  393.  
  394. void tty_buffer_clear(void) {
  395. int i;
  396.  
  397. /* Zero out the buffer */
  398. for (i = 0; i < tty_buffer_size; i++)
  399. tty_buffer[i] = 0;
  400.  
  401. /* Reset our circular buffer indexes and wrap indicator */
  402. tty_buffer_index_1 = tty_buffer_index_2 = 0;
  403. tty_buffer_wrap = 0;
  404. }
  405.  
  406. int tty_buffer_dump(void) {
  407. char filename[sizeof(DUMP_FILENAME_PREFIX)+4];
  408. FILE *fp;
  409. int i;
  410.  
  411. /* First find a non-existent suitable file to place the dump */
  412. for (i = 0; i < DUMP_MAX_FILES; i++) {
  413. sprintf(filename, "%s%02d", DUMP_FILENAME_PREFIX, i);
  414. if (access(filename, F_OK) != 0)
  415. breaki;
  416. }
  417.  
  418. /* Error out if we've reached our max number of file dumps */
  419. if (i == DUMP_MAX_FILES)
  420. return -1;
  421.  
  422. /* Open the file, error out if there was an error */
  423. fp = fopen(filename, "w");
  424. if (fp == NULL)
  425. return -1;
  426.  
  427. /* Write the tty buffer to the file */
  428.  
  429. /* If we've already wrapped around */
  430. if (tty_buffer_wrap) {
  431. /* Write from index 2 to the end of the buffer */
  432. if (fwrite(tty_buffer+tty_buffer_index_2, 1, tty_buffer_size-tty_buffer_index_2, fp) < (tty_buffer_size-tty_buffer_index_2))
  433. return -1;
  434. /* Write from the beginning of the buffer to index 1 */
  435. if (fwrite(tty_buffer, 1, tty_buffer_index_2, fp) < tty_buffer_index_2)
  436. return -1;
  437.  
  438. } else {
  439. /* We haven't wrapped around yet, so just write from beginning
  440. * of the buffer to index 2 */
  441. if (fwrite(tty_buffer, 1, tty_buffer_index_2, fp) < tty_buffer_index_2)
  442. return -1;
  443. }
  444.  
  445. fclose(fp);
  446.  
  447. return 0;
  448. }
  449.  
  450. /******************************************************************************
  451. *** Curses Window Initialization and Printing ***
  452. ******************************************************************************/
  453.  
  454. int screen_init(void) {
  455. int i;
  456.  
  457. /* Initialize the curses screen */
  458. initscr();
  459. noecho();
  460. raw();
  461. keypad(stdscr, TRUE);
  462.  
  463. /* Enable echo if it is specified */
  464. if (ui_options & UI_OPTION_ECHO)
  465. echo();
  466.  
  467. /* Create a new pad the user can scroll through the tty data with */
  468. getmaxyx(stdscr, screen_max_lines, screen_max_cols);
  469. screen_pad = newpad(tty_buffer_size, screen_max_cols);
  470. if (newpad == NULL)
  471. return ERROR_ERRNO;
  472.  
  473. /* Initialize colors and the color pairs for the color coded characters,
  474. * if they exist. */
  475. start_color();
  476. for (i = 0; i < sizeof(screen_color_coded_chars); i++) {
  477. init_pair(i+1, COLOR_BLACK, screen_color_coded_colors[i]);
  478. }
  479.  
  480. /* Reset our pad window of view */
  481. screen_pad_y = 0;
  482.  
  483. return 0;
  484. }
  485.  
  486. void screen_cleanup(void) {
  487. delwin(screen_pad);
  488. endwin();
  489. }
  490.  
  491. void screen_scroll_home(void) {
  492. screen_pad_y = 0;
  493. }
  494.  
  495. void screen_scroll_end(void) {
  496. int cursor_y, cursor_x;
  497.  
  498. /* Bound the scroll down with at the end of the current cursor */
  499. getyx(screen_pad, cursor_y, cursor_x);
  500. screen_pad_y = cursor_y-screen_max_lines+1;
  501. }
  502.  
  503. void screen_scroll_up(int lines) {
  504. /* Scroll our pad down, bottom out at 0 */
  505. screen_pad_y -= lines;
  506. if (screen_pad_y < 0)
  507. screen_pad_y = 0;
  508. }
  509.  
  510. void screen_scroll_down(int lines) {
  511. int cursor_y, cursor_x;
  512.  
  513. /* Scroll our pad up, bottom out at the current cursor position */
  514. screen_pad_y += lines;
  515.  
  516. /* Bound the scroll down with at the end of the current cursor */
  517. getyx(screen_pad, cursor_y, cursor_x);
  518. if (screen_pad_y > cursor_y-screen_max_lines)
  519. screen_pad_y = cursor_y-screen_max_lines+1;
  520. }
  521.  
  522. void screen_update(int index_1, int index_2) {
  523. int screen_index;
  524. int i = 0;
  525. int cursor_y, cursor_x;
  526. int found_cr = 0;
  527.  
  528. /* Print the characters from specified index_1 to index_2 of the tty
  529. * buffer */
  530. for (screen_index = index_1; screen_index < index_2; screen_index++) {
  531. if (ui_options & UI_OPTION_HEX) {
  532. if (ui_options & UI_OPTION_COLOR_CODED) {
  533. /* Check if this character matches a color
  534. * coded character */
  535. for (i = 0; i < sizeof(screen_color_coded_chars); i++) {
  536. /* If so, turn on its color pair */
  537. if (tty_buffer[screen_index] == (unsigned char)screen_color_coded_chars[i]) {
  538. wattron(screen_pad, COLOR_PAIR(i+1));
  539. break;
  540. }
  541. }
  542. }
  543. /* Write the character in hex */
  544. wprintw(screen_pad, "%02X", tty_buffer[screen_index]);
  545.  
  546. /* Turn off the color pair if we turned it on for a
  547. * color coded character. */
  548. if (i != sizeof(screen_color_coded_chars)) {
  549. wattroff(screen_pad, COLOR_PAIR(i+1));
  550. wattron(screen_pad, COLOR_PAIR(0));
  551. }
  552.  
  553. if (ui_options & UI_OPTION_HEX_NEWLINE) {
  554. /* Print a newline if we encountered one, otherwise
  555. * just a space to separate hex characters */
  556. if (tty_buffer[screen_index] == '\r') {
  557. /* If we need both CRLF, set a flag reminding
  558. * us that we are still looking for LF */
  559. if (tty_input_newline & OPTION_NEWLINE_CRLF)
  560. found_cr = 1;
  561. /* If we just need CR, print a newline now */
  562. else if (tty_input_newline & OPTION_NEWLINE_CR)
  563. waddch(screen_pad, '\n');
  564. } else if (tty_buffer[screen_index] == '\n') {
  565. /* If we need both CRLF, and we already found
  566. * CR, go ahead and print the newline */
  567. if (tty_input_newline & OPTION_NEWLINE_CRLF) {
  568. if (found_cr)
  569. waddch(screen_pad, '\n');
  570. /* If we just need LF, print a newline now */
  571. } else if (tty_input_newline & OPTION_NEWLINE_LF) {
  572. waddch(screen_pad, '\n');
  573. }
  574. } else {
  575. /* If we were looking for the LF to a CR,
  576. * and we didn't come across it, clear our
  577. * reminder. */
  578. found_cr = 0;
  579. }
  580. }
  581.  
  582. /* Pretty print for hex characters */
  583.  
  584. getyx(screen_pad, cursor_y, cursor_x);
  585. /* If we're getting near the edge of the screen, move
  586. * over to the next line so all of our hex characters
  587. * are aligned */
  588. if (cursor_x >= (screen_max_cols-2))
  589. waddch(screen_pad, '\n');
  590. /* Otherwise, just add a space between each hex */
  591. /* Make sure we didn't have a newline above carry us
  592. * over before adding a space */
  593. else if (cursor_x != 0)
  594. waddch(screen_pad, ' ');
  595.  
  596. } else {
  597. /* Special handling for newlines */
  598. if (tty_buffer[screen_index] == '\r' && !(tty_input_newline & OPTION_NEWLINE_RAW)) {
  599. /* If we need both CRLF, set a flag reminding
  600. * us that we are still looking for LF */
  601. if (tty_input_newline & OPTION_NEWLINE_CRLF)
  602. found_cr = 1;
  603. /* If we just need CR, print a newline now */
  604. else if (tty_input_newline & OPTION_NEWLINE_CR)
  605. waddch(screen_pad, '\n');
  606. } else if (tty_buffer[screen_index] == '\n' && !(tty_input_newline & OPTION_NEWLINE_RAW)) {
  607. /* If we need both CRLF, and we already found
  608. * CR, go ahead and print the newline */
  609. if (tty_input_newline & OPTION_NEWLINE_CRLF) {
  610. if (found_cr)
  611. waddch(screen_pad, '\n');
  612. /* If we just need LF, print a newline now */
  613. } else if (tty_input_newline & OPTION_NEWLINE_LF) {
  614. waddch(screen_pad, '\n');
  615. }
  616. } else {
  617. /* Print the normal character as usual */
  618. waddch(screen_pad, tty_buffer[screen_index]);
  619. /* If we were looking for the LF to a CR,
  620. * and we didn't come across it, clear our
  621. * reminder. */
  622. found_cr = 0;
  623. }
  624. }
  625.  
  626. getyx(screen_pad, cursor_y, cursor_x);
  627. /* If we've reached the end of our pad, refresh the screen */
  628. if (cursor_y == tty_buffer_size-1) {
  629. read_thread_signal |= SIGNAL_RTH_SCREEN_REFRESH;
  630. } else {
  631. /* Only scroll down if we're looking at the end of the
  632. * buffer */
  633. if (screen_pad_y >= (cursor_y-screen_max_lines-1)) {
  634. cursor_y -= screen_pad_y;
  635. /* Automatically scroll down if we get off of the pad */
  636. if (cursor_y >= (screen_max_lines-1))
  637. screen_pad_y += (cursor_y-screen_max_lines+1);
  638. }
  639. }
  640. }
  641.  
  642. prefresh(screen_pad, screen_pad_y, 0, 0, 0, screen_max_lines-1, screen_max_cols);
  643. }
  644.  
  645. /******************************************************************************
  646. *** stdout Printing ***
  647. ******************************************************************************/
  648.  
  649. void stdout_print(void) {
  650. int i;
  651. int found_cr = 0;
  652.  
  653. for (i = 0; i < tty_buffer_index_1; i++) {
  654. if (ui_options & UI_OPTION_HEX) {
  655. /* Write the character in hex */
  656. printf("%02X", tty_buffer[i]);
  657. stdout_cursor_x += 3;
  658.  
  659. if (ui_options & UI_OPTION_HEX_NEWLINE) {
  660. /* Print a newline if we encountered one, otherwise
  661. * just a space to separate hex characters */
  662. if (tty_buffer[i] == '\r') {
  663. /* If we need both CRLF, set a flag reminding
  664. * us that we are still looking for LF */
  665. if (tty_input_newline & OPTION_NEWLINE_CRLF)
  666. found_cr = 1;
  667. /* If we just need CR, print a newline now */
  668. else if (tty_input_newline & OPTION_NEWLINE_CR) {
  669. fputc('\n', stdout);
  670. stdout_cursor_x = 0;
  671. }
  672. } else if (tty_buffer[i] == '\n') {
  673. /* If we need both CRLF, and we already found
  674. * CR, go ahead and print the newline */
  675. if (tty_input_newline & OPTION_NEWLINE_CRLF) {
  676. if (found_cr) {
  677. fputc('\n', stdout);
  678. stdout_cursor_x = 0;
  679. }
  680. /* If we just need LF, print a newline now */
  681. } else if (tty_input_newline & OPTION_NEWLINE_LF) {
  682. fputc('\n', stdout);
  683. stdout_cursor_x = 0;
  684. }
  685. } else {
  686. /* If we were looking for the LF to a CR,
  687. * and we didn't come across it, clear our
  688. * reminder. */
  689. found_cr = 0;
  690. }
  691. }
  692.  
  693. /* Pretty print for hex characters */
  694.  
  695. /* If we're at the end of the screen, print a
  696. * newline, otherwise just print a space between hex
  697. * characters. */
  698. if (stdout_cursor_x >= (screen_max_cols-1)) {
  699. fputc('\n', stdout);
  700. stdout_cursor_x = 0;
  701. } else if (stdout_cursor_x != 0) {
  702. fputc(' ', stdout);
  703. }
  704. } else {
  705. /* Special handling for newlines */
  706. if (tty_buffer[i] == '\r' && !(tty_input_newline & OPTION_NEWLINE_RAW)) {
  707. /* If we need both CRLF, set a flag reminding
  708. * us that we are still looking for LF */
  709. if (tty_input_newline & OPTION_NEWLINE_CRLF)
  710. found_cr = 1;
  711. /* If we just need CR, print a newline now */
  712. else if (tty_input_newline & OPTION_NEWLINE_CR)
  713. fputc('\n', stdout);
  714. } else if (tty_buffer[i] == '\n' && !(tty_input_newline & OPTION_NEWLINE_RAW)) {
  715. /* If we need both CRLF, and we already found
  716. * CR, go ahead and print the newline */
  717. if (tty_input_newline & OPTION_NEWLINE_CRLF) {
  718. if (found_cr)
  719. fputc('\n', stdout);
  720. /* If we just need LF, print a newline now */
  721. } else if (tty_input_newline & OPTION_NEWLINE_LF) {
  722. fputc('\n', stdout);
  723. }
  724. } else {
  725. /* Print the normal character as usual */
  726. fputc(tty_buffer[i], stdout);
  727. /* If we were looking for the LF to a CR,
  728. * and we didn't come across it, clear our
  729. * reminder. */
  730. found_cr = 0;
  731. }
  732. }
  733. }
  734.  
  735. fflush(stdout);
  736. }
  737.  
  738.  
  739. /******************************************************************************
  740. *** Curses Read and Write Threads ***
  741. ******************************************************************************/
  742.  
  743. void *read_curses_loop(void *id) {
  744. int retVal;
  745. fd_set rfds;
  746. struct timeval tv;
  747.  
  748. read_thread_signal = 0;
  749. tty_buffer_clear();
  750. while (1) {
  751. /* If we need to exit */
  752. if (read_thread_signal & SIGNAL_RTH_EXIT)
  753. break;
  754.  
  755. /* If the write thread wants us to clear the tty buffer */
  756. if (read_thread_signal & SIGNAL_RTH_BUFFER_CLEAR) {
  757. read_thread_signal &= ~SIGNAL_RTH_BUFFER_CLEAR;
  758. /* Clear the tty buffer */
  759. tty_buffer_clear();
  760. /* Clear the screen pad and reset our screen pad
  761. * view port */
  762. wclear(screen_pad);
  763. screen_pad_y = 0;
  764. }
  765.  
  766. /* If the write thread wants us to refresh the screen buffer */
  767. if (read_thread_signal & SIGNAL_RTH_SCREEN_REFRESH) {
  768. int cursor_y, cursor_x;
  769.  
  770. read_thread_signal &= ~SIGNAL_RTH_SCREEN_REFRESH;
  771. /* Clear the screen pad and update our screen pad
  772. * view port to the end of the printed tty buffer */
  773. wclear(screen_pad);
  774. /* If we've already wrapped around in the circular
  775. * buffer */
  776. if (tty_buffer_wrap) {
  777. /* Redraw the old data: index 2 to the end
  778. * of the buffer */
  779. screen_update(tty_buffer_index_2, tty_buffer_size);
  780. /* Redraw the new data: beginning of buffer
  781. * up to index 2 */
  782. screen_update(0, tty_buffer_index_2);
  783. } else {
  784. /* Otherwise redraw the data we have in the
  785. * buffer so far, beginning of buffer to index 2
  786. */
  787. screen_update(0, tty_buffer_index_2);
  788. }
  789. /* Adjust screen pad y to the "new end"
  790. * This has to do with our screen pad actually
  791. * being greater in size than the tty buffer
  792. * itself, so the new end of our data has
  793. * shortened. */
  794. getyx(screen_pad, cursor_y, cursor_x);
  795. if (screen_pad_y > cursor_y-screen_max_lines)
  796. screen_pad_y = cursor_y-screen_max_lines+1;
  797.  
  798. continue;
  799. }
  800.  
  801. /* If the write thread wants us to dump the tty buffer */
  802. if (read_thread_signal & SIGNAL_RTH_BUFFER_DUMP) {
  803. read_thread_signal &= ~SIGNAL_RTH_BUFFER_DUMP;
  804. /* Dump the file */
  805. retVal = tty_buffer_dump();
  806. /* If we had an error, complain, and refresh the screen
  807. * buffer. */
  808. if (retVal < 0) {
  809. /* Highlight the error message */
  810. attron(A_STANDOUT);
  811. printw("Error dumping tty buffer to file!\n");
  812. attroff(A_STANDOUT);
  813. refresh();
  814. sleep(1);
  815. /* Refresh the screen buffer to get rid of the
  816. * above error message */
  817. read_thread_signal |= SIGNAL_RTH_SCREEN_REFRESH;
  818. continue;
  819. }
  820. }
  821.  
  822. /* Just update the screen with the new data */
  823. screen_update(tty_buffer_index_1, tty_buffer_index_2);
  824.  
  825. /* Catch up the tty buffer index 1 to index 2 now that we have
  826. * updated the screen with the new data */
  827. tty_buffer_index_1 = tty_buffer_index_2;
  828.  
  829. /* Clear our file descriptor list and add our tty fd */
  830. FD_ZERO(&rfds);
  831. FD_SET(tty_fd, &rfds);
  832.  
  833. /* Set a 10000 microsecond timeout */
  834. tv.tv_sec = 0;
  835. tv.tv_usec = 10000;
  836.  
  837. /* Check if tty fd has data */
  838. retVal = select(tty_fd+1, &rfds, NULL, NULL, &tv);
  839. if (retVal == -1) {
  840. perror("Error select() with serial port");
  841. handler_sigint(SIGINT);
  842. }
  843.  
  844. if (retVal > 0) {
  845. /* Read in new data from the serial port */
  846. if (tty_read_circular() < 0) {
  847. perror("Error reading");
  848. handler_sigint(SIGINT);
  849. }
  850. }
  851. }
  852.  
  853. return NULL;
  854. }
  855.  
  856. void write_curses_loop(void) {
  857. int ch;
  858. unsigned char crlf[2] = {'\r', '\n'};
  859.  
  860. while (1) {
  861. ch = wgetch(stdscr);
  862. /* Check for special control keys */
  863. if (ch == KEY_UP) {
  864. screen_scroll_up(1);
  865. } else if (ch == KEY_HOME) {
  866. screen_scroll_home();
  867. } else if (ch == KEY_END) {
  868. screen_scroll_end();
  869. } else if (ch == KEY_DOWN) {
  870. screen_scroll_down(1);
  871. } else if (ch == KEY_PPAGE) {
  872. screen_scroll_up(5);
  873. } else if (ch == KEY_NPAGE) {
  874. screen_scroll_down(5);
  875. } else if (ch == CTRL_C) {
  876. handler_sigint(SIGINT);
  877. } else if (ch == CTRL_H) {
  878. /* Toggle the screen hex mode */
  879. if (ui_options & UI_OPTION_HEX)
  880. ui_options &= ~UI_OPTION_HEX;
  881. else
  882. ui_options |= UI_OPTION_HEX;
  883. } else if (ch == CTRL_N) {
  884. /* Toggle interpretation of newlines in hex mode */
  885. if (ui_options & UI_OPTION_HEX_NEWLINE)
  886. ui_options &= ~UI_OPTION_HEX_NEWLINE;
  887. else
  888. ui_options |= UI_OPTION_HEX_NEWLINE;
  889. } else if (ch == CTRL_O) {
  890. /* Toggle the screen color coding mode */
  891. if (ui_options & UI_OPTION_COLOR_CODED)
  892. ui_options &= ~UI_OPTION_COLOR_CODED;
  893. else
  894. ui_options |= UI_OPTION_COLOR_CODED;
  895. } else if (ch == CTRL_L) {
  896. /* Signal to our read thread to clear the tty buffer */
  897. read_thread_signal |= SIGNAL_RTH_BUFFER_CLEAR;
  898. } else if (ch == CTRL_R) {
  899. /* Signal to our read thread to refresh the screen
  900. * buffer. */
  901. read_thread_signal |= SIGNAL_RTH_SCREEN_REFRESH;
  902. } else if (ch == CTRL_D) {
  903. /* Signal to our read thread to dump the tty buffer */
  904. read_thread_signal |= SIGNAL_RTH_BUFFER_DUMP;
  905. } else {
  906. /* Otherwise, write out the terminal */
  907.  
  908. /* If we encountered a newline character */
  909. if (ch == '\n') {
  910. /* Send CRLF if we are outputting CRLF and
  911. * start back up top. */
  912. if (tty_output_newline & OPTION_NEWLINE_CRLF) {
  913. tty_write(crlf, sizeof(crlf));
  914. continue;
  915. /* Change to CR if we are just outputting CR */
  916. } else if (tty_output_newline & OPTION_NEWLINE_CR) {
  917. ch = '\r';
  918. /* Ignore this character if we are not
  919. * transmitting newlines. */
  920. } else if (tty_output_newline & OPTION_NEWLINE_NONE) {
  921. continue;
  922. }
  923. }
  924. tty_write((unsigned char *)&ch, 1);
  925. }
  926. }
  927. }
  928.  
  929. /******************************************************************************
  930. *** stdin/stdout Read/Write select() based loop ***
  931. ******************************************************************************/
  932.  
  933. int read_write_stdin_loop(void) {
  934. fd_set rfds;
  935. int retVal;
  936. int stdin_ch;
  937. unsigned char crlf[2] = {'\r', '\n'};
  938. struct termios options;
  939. struct winsize win;
  940.  
  941. /* Get the console window size */
  942. if (ioctl(0, TIOCGWINSZ, &win) < 0) {
  943. perror("Error getting console window size");
  944. return ERROR_ERRNO;
  945. }
  946. screen_max_cols = win.ws_col;
  947.  
  948. /* Get the stdin tty options */
  949. if (tcgetattr(0, &options) < 0) {
  950. perror("Error getting stdin tty options");
  951. return ERROR_ERRNO;
  952. }
  953. /* Disable canonical input and echo, so we can send characters without
  954. * the need of a line feed */
  955. options.c_lflag &= ~(ICANON | ECHO | ECHOE);
  956. /* Turn off support for XON & XOFF characters so they pass through */
  957. options.c_iflag &= ~(IXON | IXOFF | IXANY);
  958. /* Enable echo if it is specified */
  959. if (ui_options & UI_OPTION_ECHO)
  960. options.c_lflag |= ECHO;
  961. /* Set the stdin tty options */
  962. if (tcsetattr(0, TCSANOW, &options) < 0) {
  963. perror("Error setting stdin tty options");
  964. return ERROR_ERRNO;
  965. }
  966.  
  967. while (1) {
  968. /* Clear our file descriptor list */
  969. FD_ZERO(&rfds);
  970. /* Add stdin and our tty fd to our file descriptor list */
  971. FD_SET(0, &rfds);
  972. FD_SET(tty_fd, &rfds);
  973.  
  974. /* Check if stdin or our tty fd has data */
  975. retVal = select(tty_fd+1, &rfds, NULL, NULL, NULL);
  976. if (retVal == -1) {
  977. perror("Error select() with stdin and serial port");
  978. return ERROR_ERRNO;
  979. }
  980.  
  981. if (FD_ISSET(0, &rfds)) {
  982. stdin_ch = fgetc(stdin);
  983. if (stdin_ch == '\n') {
  984. /* Write CRLF if we are outputting CRLF */
  985. if (tty_output_newline & OPTION_NEWLINE_CRLF) {
  986. tty_write(crlf, sizeof(crlf));
  987. /* Change to CR if we are just outputting CR */
  988. } else if (tty_output_newline & OPTION_NEWLINE_CR) {
  989. stdin_ch = '\r';
  990. tty_write((unsigned char *)&stdin_ch, 1);
  991. /* Ignore this character if we are not
  992. * transmitting newlines */
  993. } else if (tty_output_newline & OPTION_NEWLINE_NONE) {
  994. ;
  995. /* Otherwise, just print the \n as usual */
  996. } else {
  997. tty_write((unsigned char *)&stdin_ch, 1);
  998. }
  999. } else {
  1000. tty_write((unsigned char *)&stdin_ch, 1);
  1001. }
  1002. }
  1003.  
  1004. if (FD_ISSET(tty_fd, &rfds)) {
  1005. /* Read from the serial port */
  1006. retVal = tty_read_regular();
  1007. if (retVal < 0) {
  1008. perror("Error reading");
  1009. return ERROR_ERRNO;
  1010. }
  1011.  
  1012. /* Print if we received data */
  1013. if (tty_buffer_index_1 > 0) {
  1014. stdout_print();
  1015. }
  1016. }
  1017. }
  1018.  
  1019. return 0;
  1020. }
  1021.  
  1022. /******************************************************************************
  1023. *** Command-Line Options Parsing ***
  1024. ******************************************************************************/
  1025.  
  1026. static struct option long_options[] = {
  1027. /* Interface Options */
  1028. {"stdin", no_argument, NULL, 's'},
  1029. /* Serial Port Options */
  1030. {"baudrate", required_argument, NULL, 'b'},
  1031. {"databits", required_argument, NULL, 'd'},
  1032. {"parity", required_argument, NULL, 'p'},
  1033. {"stopbits", required_argument, NULL, 't'},
  1034. {"flowcontrol", required_argument, NULL, 'f'},
  1035. {"tx-nl", required_argument, NULL, 0},
  1036. {"rx-nl", required_argument, NULL, 0},
  1037. {"buffer-size", required_argument, NULL, 0},
  1038. /* Formatting Options */
  1039. {"echo", no_argument, NULL, 'e'},
  1040. {"hex", no_argument, NULL, 'x'},
  1041. {"hex-nl", no_argument, NULL, 0},
  1042. /* Curses Formatting Options */
  1043. {"rx-nl-color", no_argument, NULL, 'c'},
  1044. /* Misc. Options */
  1045. {"help", no_argument, NULL, 'h'},
  1046. {"commands", no_argument, NULL, 'k'},
  1047. {"version", no_argument, NULL, 'v'},
  1048. {NULL, 0, NULL, 0}
  1049. };
  1050.  
  1051. static void printVersion(FILE *stream) {
  1052. fprintf(stream, "ssterm version 1.0 - 2009/10/23\n");
  1053. fprintf(stream, "Written by Vanya Sergeev - <vsergeev@gmail.com>\n");
  1054. }
  1055.  
  1056. static void printCommands(FILE *stream) {
  1057. fprintf(stream, "\n\
  1058. Curses Commands for ssterm:\n\
  1059. Page Up/Page Down Scroll buffer up/down by 5 lines\n\
  1060. Home/End Scroll to the top/bottom of the buffer\n\
  1061. Up/Down Scroll buffer up/down by 1 line\n\
  1062. \n\
  1063. Ctrl-l Clear buffer\n\
  1064. Ctrl-r Reprint buffer\n\
  1065. Ctrl-d Dump buffer to file\n\
  1066. \n\
  1067. Ctrl-h Hexadecimal representation mode\n\
  1068. Ctrl-n Interpret newlines in hexadecimal mode\n\
  1069. Ctrl-o Color-code newline characters in hexadecimal\n\
  1070. mode\n\
  1071. \n\
  1072. Ctrl-q Send XON\n\
  1073. Ctrl-s Send XOFF\n\
  1074. \n");
  1075. }
  1076.  
  1077. static void printUsage(FILE *stream, const char *programName) {
  1078. fprintf(stream, "Usage: %s <option(s)> <serial port>\n", programName);
  1079. fprintf(stream, " ssterm - simple serial-port terminal\n");
  1080. fprintf(stream, " Written by Vanya A. Sergeev - <vsergeev@gmail.com>.\n\n");
  1081. fprintf(stream, "\
  1082. Interface Options:\n\
  1083. -s, --stdin Use an stdin/stdout interface as opposed to \n\
  1084. curses \n\
  1085. \n\
  1086. Serial Port Options:\n\
  1087. -b, --baudrate <rate> Specify the baudrate\n\
  1088. -d, --databits <number> Specify the number of data bits [5,6,7,8]\n\
  1089. -p, --parity <type> Specify the parity [none, odd, even]\n\
  1090. -t, --stopbits <number> Specify number of stop bits [1,2]\n\
  1091. -f, --flow-control <type> Specify the flow-control [none, rtscts, xonxoff]\n\
  1092. \n\
  1093. Formatting Options:\n\
  1094. --tx-nl <combination> Specify the transmit newline combination\n\
  1095. [raw, none, cr, lf, crlf, crorlf]\n\
  1096. --rx-nl <combination> Specify the receive newline combination\n\
  1097. [raw, none, cr, lf, crlf, crorlf]\n\
  1098. -e, --echo Turn on local character echo\n\
  1099. -x, --hex Turn on hexadecimal representation mode\n\
  1100. --hex-nl Turn on newlines in hexadecimal mode\n\
  1101. \n\
  1102. Curses Formatting Options:\n\
  1103. -c, --rx-nl-color Color-code all receive newline combinations\n\
  1104. \n\
  1105. Misc. Options:\n\
  1106. --buffer-size <bytes> Specify the size of ssterm's receive buffer\n\
  1107. -h, --help Display this usage/help\n\
  1108. -k, --commands Display curses commands\n\
  1109. -v, --version Display the program's version\n\n");
  1110. fprintf(stream, "\
  1111. Default options: curses, 9600 8N1, flow control: none, transmit newline: raw,\n\
  1112. receive newline: LF, echo: off, hexadecimal: off, receive color-code: off,\n\
  1113. buffer size: 4096\n");
  1114. fprintf(stream, "\n");
  1115. }
  1116.  
  1117. /******************************************************************************
  1118. ******************************************************************************
  1119. ******************************************************************************/
  1120.  
  1121. int main(int argc, char *argv[]) {
  1122. int optc, long_index;
  1123. int retVal;
  1124.  
  1125. ui_options = 0;
  1126.  
  1127. while (1) {
  1128. int *var;
  1129. optc = getopt_long(argc, (char * const *)argv, "b:d:p:t:f:scxehkv", long_options, &long_index);
  1130. if (optc == -1)
  1131. break;
  1132.  
  1133. #define str_option_cmp(str) (strcasecmp(optarg, str) == 0)
  1134. switch (optc) {
  1135. /* Long option */
  1136. case 0:
  1137. /* --tx-nl */
  1138. if (strcmp(long_options[long_index].name, "buffer-size") == 0) {
  1139. tty_buffer_size = atoi(optarg);
  1140. break;
  1141. } else if (strcmp(long_options[long_index].name, "tx-nl") == 0) {
  1142. var = &tty_output_newline;
  1143. /* --rx-nl */
  1144. } else if (strcmp(long_options[long_index].name, "rx-nl") == 0) {
  1145. var = &tty_input_newline;
  1146. /* --hex-nl */
  1147. } else if (strcmp(long_options[long_index].name, "hex-nl") == 0) {
  1148. ui_options |= UI_OPTION_HEX_NEWLINE;
  1149. break;
  1150. } else {
  1151. break;
  1152. }
  1153.  
  1154. if (str_option_cmp(NEWLINE_NONE_STR))
  1155. (*var) = OPTION_NEWLINE_NONE;
  1156. else if (str_option_cmp(NEWLINE_CR_STR))
  1157. (*var) = OPTION_NEWLINE_CR;
  1158. else if (str_option_cmp(NEWLINE_LF_STR))
  1159. (*var) = OPTION_NEWLINE_LF;
  1160. else if (str_option_cmp(NEWLINE_CRLF_STR))
  1161. (*var) = OPTION_NEWLINE_CRLF;
  1162. else if (str_option_cmp(NEWLINE_CRORLF_STR))
  1163. (*var) = OPTION_NEWLINE_CRORLF;
  1164. else if (str_option_cmp(NEWLINE_RAW_STR))
  1165. (*var) = OPTION_NEWLINE_RAW;
  1166. break;
  1167. case 's':
  1168. ui_options |= UI_OPTION_STDIN_STDOUT;
  1169. break;
  1170. case 'b':
  1171. tty_baudrate = atoi(optarg);
  1172. break;
  1173. case 'd':
  1174. tty_databits = atoi(optarg);
  1175. break;
  1176. case 't':
  1177. tty_stopbits = atoi(optarg);
  1178. break;
  1179. case 'p':
  1180. if (str_option_cmp(PARITY_NONE_STR))
  1181. tty_parity = PARITY_NONE;
  1182. else if (str_option_cmp(PARITY_ODD_STR))
  1183. tty_parity = PARITY_ODD;
  1184. else if (str_option_cmp(PARITY_EVEN_STR))
  1185. tty_parity = PARITY_EVEN;
  1186. else
  1187. tty_parity = -1;
  1188. break;
  1189. case 'f':
  1190. if (str_option_cmp(FLOW_CONTROL_NONE_STR))
  1191. tty_flowcontrol = FLOW_CONTROL_NONE;
  1192. else if (str_option_cmp(FLOW_CONTROL_RTSCTS_STR))
  1193. tty_flowcontrol = FLOW_CONTROL_RTSCTS;
  1194. else if (str_option_cmp(FLOW_CONTROL_XONXOFF_STR))
  1195. tty_flowcontrol = FLOW_CONTROL_XONXOFF;
  1196. else
  1197. tty_flowcontrol = -1;
  1198. break;
  1199. case 'e':
  1200. ui_options |= UI_OPTION_ECHO;
  1201. break;
  1202. case 'x':
  1203. ui_options |= UI_OPTION_HEX;
  1204. break;
  1205. case 'c':
  1206. ui_options |= UI_OPTION_COLOR_CODED;
  1207. break;
  1208. case 'h':
  1209. printUsage(stderr, argv[0]);
  1210. exit(EXIT_SUCCESS);
  1211. case 'k':
  1212. printCommands(stderr);
  1213. exit(EXIT_SUCCESS);
  1214. case 'v':
  1215. printVersion(stderr);
  1216. exit(EXIT_SUCCESS);
  1217. default:
  1218. printUsage(stderr, argv[0]);
  1219. exit(EXIT_SUCCESS);
  1220. }
  1221. }
  1222.  
  1223. /* Check arguments */
  1224. if (optind == argc) {
  1225. printUsage(stderr, argv[0]);
  1226. exit(EXIT_FAILURE);
  1227. }
  1228.  
  1229. if (!(ui_options & UI_OPTION_STDIN_STDOUT) && (tty_input_newline & OPTION_NEWLINE_RAW)) {
  1230. fprintf(stderr, "Error: receive newline character option 'raw' unsupported in curses mode (CR characters will delete lines).\n");
  1231. exit(EXIT_FAILURE);
  1232. }
  1233.  
  1234. /* Check supplied buffer size */
  1235. if (tty_buffer_size < 0) {
  1236. fprintf(stderr, "Invalid buffer size!\n");
  1237. exit(EXIT_FAILURE);
  1238. }
  1239.  
  1240. /* Open the serial port */
  1241. /* If we're interacting with stdin/stdout, and subsequently going
  1242. * to use select(), open the tty fd in blocking mode */
  1243. if (ui_options & UI_OPTION_STDIN_STDOUT)
  1244. retVal = tty_open(argv[optind], O_RDWR | O_NOCTTY);
  1245. /* Otherwise, open the tty in non-blocking mode */
  1246. else
  1247. retVal = tty_open(argv[optind], O_RDWR | O_NOCTTY);
  1248. if (retVal < 0) {
  1249. perror("Error opening serial port");
  1250. exit(EXIT_FAILURE);
  1251. }
  1252.  
  1253. /* Set the serial port options */
  1254. retVal = tty_set_options();
  1255. switch (retVal) {
  1256. case ERROR_ERRNO:
  1257. perror("Error setting serial port options");
  1258. break;
  1259. case ERROR_BAUDRATE:
  1260. fprintf(stderr, "Invalid baudrate setting!\n");
  1261. break;
  1262. case ERROR_DATABITS:
  1263. fprintf(stderr, "Invalid data bits setting!\n");
  1264. break;
  1265. case ERROR_STOPBITS:
  1266. fprintf(stderr, "Invalid stop bits setting!\n");
  1267. break;
  1268. case ERROR_PARITY:
  1269. fprintf(stderr, "Invalid parity setting!\n");
  1270. break;
  1271. case ERROR_FLOWCONTROL:
  1272. fprintf(stderr, "Invalid flow control setting!\n");
  1273. break;
  1274. }
  1275.  
  1276. /* Check for setting errors, and quit if there were any */
  1277. if (retVal < 0) {
  1278. close(tty_fd);
  1279. exit(EXIT_FAILURE);
  1280. }
  1281.  
  1282. /* Allocate memory for the tty_buffer */
  1283. tty_buffer = malloc(tty_buffer_size * sizeof(char));
  1284. if (tty_buffer == NULL) {
  1285. perror("Error allocating memory for receive buffer");
  1286. exit(EXIT_FAILURE);
  1287. }
  1288.  
  1289. if (!(ui_options & UI_OPTION_STDIN_STDOUT)) {
  1290. /* Establish a handler for SIGINT since we're going into curses mode */
  1291. signal(SIGINT, handler_sigint);
  1292.  
  1293. /* Initialize our curses screen */
  1294. retVal = screen_init();
  1295. if (retVal < 0) {
  1296. screen_cleanup();
  1297. free(tty_buffer);
  1298. close(tty_fd);
  1299. perror("Error creating curses screen");
  1300. exit(EXIT_FAILURE);
  1301. }
  1302.  
  1303. /* Thread our read and write loops */
  1304. retVal = pthread_create(&read_thread, NULL, read_curses_loop, NULL);
  1305. if (retVal < 0) {
  1306. screen_cleanup();
  1307. free(tty_buffer);
  1308. close(tty_fd);
  1309. perror("Error creating read pthread:");
  1310. exit(EXIT_FAILURE);
  1311. }
  1312. /* This thread will take the write / UI loop */
  1313. write_curses_loop();
  1314. } else {
  1315. /* Otherwise, enter the select() based read/write loop
  1316. * for stdin/stdout */
  1317. retVal = read_write_stdin_loop();
  1318. }
  1319.  
  1320. return retVal;
  1321. }
Add Comment
Please, Sign In to add comment