diff options
Diffstat (limited to 'call')
-rw-r--r-- | call/call.c | 1010 | ||||
-rw-r--r-- | call/call.man | 37 |
2 files changed, 769 insertions, 278 deletions
diff --git a/call/call.c b/call/call.c index 89d3264..f9fe6eb 100644 --- a/call/call.c +++ b/call/call.c @@ -36,7 +36,14 @@ #include <sys/wait.h> #include <termios.h> #include <unistd.h> -#include <curses.h> +#define __USE_XOPEN +#include <wchar.h> +#include <wctype.h> +#define _XOPEN_SOURCE_EXTENDED 1 +#include <ncursesw/ncurses.h> +#include <locale.h> +#include <iconv.h> +#include <sys/ioctl.h> #include <netax25/ax25.h> #include <netrom/netrom.h> @@ -79,10 +86,13 @@ static char *port; static char *mycall; static int stdin_is_tty = 1; +static iconv_t ibm850toutf8 = 0, wcharttoibm850 = 0, wcharttoutf8 = 0, utf8towchart = 0; volatile int interrupted = FALSE; +static int sigwinchsignal = FALSE; int paclen; int fd; +int curson = 1; int wait_for_remote_disconnect = FALSE; static struct timespec inactivity_timeout; @@ -104,7 +114,7 @@ typedef struct { WINDOW *ptr; int max_y; int max_x; - char string[MAX_BUFLEN]; + wchar_t string[MAX_BUFLEN]; unsigned long bytes; int curs_pos; } t_win; @@ -113,10 +123,233 @@ typedef struct { #define SLAVEMODE 002 /* Menu mode */ #define RAWMODE 004 /* mode used by earlier versions */ +#define ORIGINALENCODING 0 +#define UTF8ENCODING 1 + +#define SCROLLBACKSIZE 5000 + +typedef struct { + char *str;int len; +}scrollbackstruct; + + +static scrollbackstruct scrollback[SCROLLBACKSIZE]; +static int topscroll=0, lastscroll = 0, scrolledup=0, eatchar = 0; +static char inbuf[MAX_BUFLEN];static int inbuflen = 0, inbufwid = 0; +static char incharbuf[6];static int incharbuflen = 0; +void statline(int mode, char *s); + +void addscrollline(char *s, int len) +{ + scrollback[lastscroll].str = malloc(len); + memcpy(scrollback[lastscroll].str, s, len); + scrollback[lastscroll].len = len; + if (++lastscroll >= SCROLLBACKSIZE)lastscroll = 0; + if (lastscroll == topscroll){ + free(scrollback[topscroll].str); + if (++topscroll >= SCROLLBACKSIZE)topscroll = 0; + } +} + +// Notes: Japanese, Chinese and Korean characters mostly take up two +// characters in width. These characters return 2 via +// wcwidth to let the code know that they require two +// spaces. Mono-space fonts/ncurses seems to work correctly with +// East Asian characters. +// +// There are also some characters that return a wcwidth of 0. I'm +// not sure about all of them, but some of the 0 width characters +// add a mark above or below a character. In order for these marks +// to appear correctly, the previous character and the overstrike +// must be drawn together. using wmove and drawing the accent doesn't +// work. +// +// There are some other characters that have functions, and these +// are not supported using a print to the screen. South Asian +// fonts are complicated. I don't believe south asian fonts work +// correctly from the Linux command line (as of late 2009). They +// print but don't combine. The result is distorted. +// +// Many characters, as of 12/09, are too wide for a single space +// in the font, although they return a wcwidth of 1. I suspect +// this is due to these characters not being part of the mono-spaced +// font and are instead pulled from an alternate font. These +// characters also glitch in xterm, vim and other ncurses +// software. I suspect this is actually a font bug, and any character +// that returns wcwidth=1 in a monospaced font should be +// monospaced. +// + + +int widthchar(char *s, size_t bytes, int xpos) +{ + wchar_t c;int width; + char *outbuf=(char *) &c; + size_t outsize = sizeof(wchar_t); + // Note: Actually need to check if bad UTF8 characters show as ? + if (iconv(utf8towchart, &s, &bytes, &outbuf, &outsize)< 0)return 0; + if (c == 9){ + return 8 - (xpos & 7); + } + width = wcwidth(c); + if (width < 0)return 0; + return width; +} + + +int completecharlen(char *s) +{ + unsigned ut = (unsigned char)s[0];int clen; + if (ut <= 0x80)clen = 1; + else if ((ut >= 192) && (ut < 192+32))clen = 2; + else if ((ut >= 224) && (ut < 224+16))clen = 3; + else if ((ut >= 240) && (ut < 240+8))clen = 4; + else if ((ut >= 248) && (ut < 248+4))clen = 5; + else if ((ut >= 252) && (ut < 252+2))clen = 6; + else clen = 1; // bad + return clen; +} + + +// Must check for COLS while redrawing from history. Or otherwise the text +// wraps around and does strange things. +int waddnstrcolcheck(WINDOW *win, char *s, int len, int twidth) +{ + int n; + for (twidth = 0,n=0;n<len;){ + int cwidth = completecharlen(&s[n]), width; + if (n + cwidth > len)return twidth; // Error condition + width = widthchar(&s[n], cwidth, twidth); + if (twidth+width > COLS)return twidth; // + waddnstr(win, &s[n], cwidth); + n += cwidth; + twidth += width; + } + return twidth; +} + +// Update a line on the screen from the backscroll buffer. +void updateline(int screeny, int yfrom, t_win *win_in, int mode, t_win *win_out) +{ + wmove(win_in->ptr, screeny, 0); + if (yfrom == lastscroll){ + int twidth = 0; + if (inbuflen > 0) + twidth = waddnstrcolcheck(win_in->ptr, inbuf, inbuflen, 0); + if (mode == SLAVEMODE){ + char obuf[MAX_BUFLEN]; + char *inbuf = (char *)win_out->string, *outbuf = obuf; + size_t insize = win_out->bytes * sizeof(wchar_t), outsize = MAX_BUFLEN; + iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); + waddnstrcolcheck(win_in->ptr, obuf, MAX_BUFLEN - outsize, twidth); + win_out->curs_pos = win_out->bytes; + } + } + else { + waddnstrcolcheck(win_in->ptr, scrollback[yfrom].str, scrollback[yfrom].len, 0); + } +} + +// Cursor in SLAVE mode while scrolling looks broken. +// Cursor in TALK mode is always good, because it's on the bottom window +void checkcursor(int mode) +{ + int newcursor; + if ((mode == SLAVEMODE) && scrolledup)newcursor = 0; + else newcursor = 1; + if (curson != newcursor){ + curs_set(newcursor); + curson = newcursor; + } +} + +/* For CJK, it's important to keep the cursor always on the input + * window. Otherwise the display is confused */ +static void restorecursor(int mode, t_win *win_out) +{ + checkcursor(mode); + if (mode != RAWMODE){ + int x,y; + getyx(win_out->ptr, y, x);// Must restore input cursor location. + wmove(win_out->ptr,y, x); + wrefresh(win_out->ptr); + } +} + +void redrawscreen(t_win *win_in, int mode, t_win *win_out) +{ + int y, storedlines; + if (lastscroll >= topscroll) storedlines = lastscroll - topscroll; + else storedlines = lastscroll + SCROLLBACKSIZE - topscroll; + // Note it's stored lines + 1 extra line for text input. + for (y=0;(y<=win_in->max_y) && (y <= storedlines);y++){ + int linefrom; + if (storedlines <= win_in->max_y){// This is a little confusing. + linefrom = topscroll + y;// The screen scrolls top down at start + } + else linefrom = lastscroll -scrolledup - (win_in->max_y) + y; + while (linefrom < 0)linefrom += SCROLLBACKSIZE; + while (linefrom >= SCROLLBACKSIZE)linefrom -= SCROLLBACKSIZE; + updateline(y,linefrom , win_in, mode, win_out); + } + checkcursor(mode); +} + +void scrolltext(t_win *win_in, int lines, int mode, t_win *win_out) +{ + int topline, storedlines; + int y; + int wasscrolledup; + if (scrolledup + lines < 0){ + lines = -scrolledup; + } + // storedlines = Lines stored in buffer. + if (lastscroll >= topscroll) storedlines = lastscroll - topscroll; + else storedlines = lastscroll + SCROLLBACKSIZE - topscroll; + // The max scrolling we can do is the # of lines stored - the + // screen size. + topline = storedlines - win_in->max_y; + if (topline < 0)topline = 0; + if (scrolledup + lines > topline){ + lines = topline - scrolledup; + } + if (!lines)return; + wasscrolledup = scrolledup; + scrolledup += lines; + wscrl(win_in->ptr, -lines); + scrollok(win_in->ptr, FALSE); + if (lines > 0){ + for (y=0;y<lines;y++){ + int linefrom = lastscroll -scrolledup - (win_in->max_y) + y; + while (linefrom < 0)linefrom += SCROLLBACKSIZE; + while (linefrom >= SCROLLBACKSIZE)linefrom -= SCROLLBACKSIZE; + updateline(y,linefrom , win_in, mode, win_out); + } + } + else { + for (y=-lines-1;y>=0;y--){ + int linefrom = lastscroll -scrolledup - y; + while (linefrom < 0)linefrom += SCROLLBACKSIZE; + while (linefrom >= SCROLLBACKSIZE)linefrom -= SCROLLBACKSIZE; + updateline(win_in->max_y - y,linefrom , win_in, mode, win_out); + } + + } + scrollok(win_in->ptr, TRUE); + checkcursor(mode); + wrefresh(win_in->ptr); + if (wasscrolledup && !scrolledup){ + statline(mode, ""); + } + else if (!wasscrolledup && scrolledup){ + statline(mode, "Viewing Scrollback"); + } +} + void usage(void) { - fprintf(stderr, "usage: call [-b l|e] [-d] [-h] [-m s|e] [-p paclen] [-r] [-R]\n"); - fprintf(stderr, " [-s mycall] [-t] [-T timeout] [-v] [-w window] [-W]\n"); + fprintf(stderr, "usage: call [-8] [-b l|e] [-d] [-h] [-i] [-m s|e] [-p paclen] [-r] [-R]\n"); + fprintf(stderr, " [-s mycall] [-S] [-t] [-T timeout] [-v] [-w window] [-W]\n"); fprintf(stderr, " port callsign [[via] digipeaters...]\n"); exit(1); } @@ -161,6 +394,73 @@ void convert_upper_lower(char *buf, int len) } + +/* Return the with of this character in character blocks. (Normal = 1, CJK=2) + * Also for control chracters, return the width of the replacement string. + * */ +static int wcwidthcontrol(wchar_t c) +{ + int width; + cchar_t cc = {0}; + wchar_t *str; + cc.chars[0] = c; + str = wunctrl(&cc); + if (!str)return 0; + width = wcswidth(str, wcslen(str)); + return width; +} + +/* Return a string to print for a wchar_t. Expand control characters. + * Strings returned by wunctrl don't like to be freed. It seems. */ +static wchar_t *wunctrlwchar(wchar_t c) +{ + cchar_t cc = {0}; + wchar_t *str; + cc.chars[0] = c; + str = wunctrl(&cc); + return str; +} + +// For some reason wins_nwstr fails on fedora 12 on some characters +// but waddnstr works. +// Draw the entire input buffer when adding text. +// The fonts that do overstrike fail when written one char at a time. +void drawinbuf(WINDOW *w, wchar_t *string, int bytes, int cur_pos) +{ + int n, x, cursorx, xpos, ypos, width; + getyx(w, ypos, xpos);// Assume cursor to be at position of current char to draw. + x = xpos;cursorx = xpos; + // cur_pos-1 = the chracter that was just added. + for (n=cur_pos-2;n>=0;n--){// Move x position to start of string or 0 + width = wcwidthcontrol(string[n]); + if (x >= width)x -= width; + else x = 0; + } + wmove(w, ypos, x); + for (n=0;n<bytes;n++){ + char obuf[MAX_BUFLEN]; + char *inbuf, *outbuf = obuf; + size_t insize, outsize = MAX_BUFLEN; + wchar_t *str;int len, width; + if (n == cur_pos){ + cursorx = x; + } + str = wunctrlwchar(string[n]); + if (!str)continue; + inbuf = (char *) str; + len = wcslen(str); + insize = len * sizeof(wchar_t); + width = wcswidth(str, len); + iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); + waddnstr(w, obuf, MAX_BUFLEN-outsize); + x += width; + } + if (cur_pos < bytes){ + wmove(w,ypos, cursorx); + } +} + + /* Convert linear UNIX date to a MS-DOS time/date pair. */ static char * unix_to_sfbin_date_string(time_t gmt) @@ -412,6 +712,13 @@ static int connect_to(char *address[]) return fd; } + +static void cmd_sigwinch(int sig) +{ + signal(SIGWINCH, cmd_sigwinch); + sigwinchsignal = TRUE; +} + void cmd_intr(int sig) { interrupted = TRUE; @@ -428,7 +735,7 @@ void statline(int mode, char *s) if (oldlen > 0) { move(0, STATW_STAT); attron(A_REVERSE); - for (cnt = 0; cnt < oldlen; cnt++) + for (cnt = STATW_STAT; cnt < COLS; cnt++) addch(' '); oldlen = 0; attroff(A_REVERSE); @@ -441,21 +748,18 @@ void statline(int mode, char *s) fflush(stdout); return; } - if (strlen(s) > 80 - STATW_STAT) - s[80 - STATW_STAT] = '\0'; + if (COLS <= STATW_STAT)return; + l = strlen(s); + if (l > COLS - STATW_STAT) + l = COLS-STATW_STAT; move(0, STATW_STAT); attron(A_REVERSE); - addstr(s); - - if (oldlen > strlen(s)) { - l = oldlen - strlen(s); - for (cnt = 0; cnt < l; cnt++) - addch(' '); - } + addnstr(s, l); + for (cnt = STATW_STAT+l;cnt < COLS;cnt++)addch(' '); attroff(A_REVERSE); - oldlen = strlen(s); + oldlen = l; refresh(); } @@ -800,6 +1104,10 @@ int start_screen(char *call[]) { int cnt; char idString[12]; + struct winsize winsz = {0}; + + if ((ioctl(0, TIOCGWINSZ, &winsz) >= 0) && winsz.ws_row && winsz.ws_col) + resizeterm(winsz.ws_row, winsz.ws_col); sprintf(idString, " %9.9s ", call[0]); if ((win = initscr()) == NULL) @@ -811,7 +1119,8 @@ int start_screen(char *call[]) addch(ACS_VLINE); addstr("--------"); addch(ACS_VLINE); - for (cnt = STATW_STAT; cnt <= 80; cnt++) + move(0, STATW_STAT); + for (cnt = STATW_STAT; cnt < COLS; cnt++) addch(' '); attroff(A_REVERSE); @@ -819,8 +1128,8 @@ int start_screen(char *call[]) raw(); nodelay(win, TRUE); keypad(win, TRUE); + curson = 1; refresh(); - return 0; } @@ -841,7 +1150,8 @@ int start_slave_mode(wint * wintab, t_win * win_in, t_win * win_out) win_out->curs_pos = 0; win_in->bytes = 0; win_in->curs_pos = 0; - + redrawscreen(win_in, SLAVEMODE, win_out); + wrefresh(win_in->ptr); return 0; } @@ -872,15 +1182,16 @@ int start_talk_mode(wint * wintab, t_win * win_in, t_win * win_out) scrollok(win_out->ptr, TRUE); wclear(win_out->ptr); - wrefresh(win_out->ptr); wclear(win_in->ptr); - wrefresh(win_in->ptr); win_out->bytes = 0; win_out->curs_pos = 0; win_in->bytes = 0; win_out->curs_pos = 0; - + redrawscreen(win_in, TALKMODE, win_out); + restorecursor(TALKMODE, win_out); + wrefresh(win_in->ptr); + wrefresh(win_out->ptr); return 0; } @@ -931,168 +1242,208 @@ int change_mode(int oldmode, int newmode, wint * wintab, t_win * win_in, break; } + scrolledup = 0; return newmode; } -void writeincom(int mode, t_win * win_in, unsigned char buf[], int bytes) +static void reinit_mode(int mode, wint * wintab, t_win * win_in, t_win * win_out, char *call[]) { - int cnt; - - if (mode & RAWMODE) { - while (write(STDOUT_FILENO, buf, bytes) == -1) { - if (errno == EWOULDBLOCK || errno == EAGAIN) { - usleep(100000); - continue; - } - exit(1); - } - return; - } - for (cnt = 0; cnt < bytes; cnt++) { - switch (buf[cnt]) { - case 201: - case 218: - waddch(win_in->ptr, ACS_ULCORNER); - break; - case 187: - case 191: - waddch(win_in->ptr, ACS_URCORNER); - break; - case 200: - case 192: - waddch(win_in->ptr, ACS_LLCORNER); - break; - case 188: - case 217: - waddch(win_in->ptr, ACS_LRCORNER); - break; - case 204: - case 195: - waddch(win_in->ptr, ACS_LTEE); - break; - case 185: - case 180: - waddch(win_in->ptr, ACS_RTEE); - break; - case 203: - case 194: - waddch(win_in->ptr, ACS_TTEE); - break; - case 202: - case 193: - waddch(win_in->ptr, ACS_BTEE); - break; - case 205: - case 196: - waddch(win_in->ptr, ACS_HLINE); - break; - case 186: - case 179: - waddch(win_in->ptr, ACS_VLINE); - break; - case 129: - waddch(win_in->ptr, 252); /*u umlaut */ - break; - case 132: - waddch(win_in->ptr, 228); /*a umlaut */ - break; - case 142: - waddch(win_in->ptr, 196); /*A umlaut */ - break; - case 148: - waddch(win_in->ptr, 246); /*o umlaut */ - break; - case 153: - waddch(win_in->ptr, 214); /*O umlaut */ - break; - case 154: - waddch(win_in->ptr, 220); /*U umlaut */ - break; - case 225: - waddch(win_in->ptr, 223); /*sz */ - break; - default: - { - if (buf[cnt] > 127) - waddch(win_in->ptr, '.'); - else - waddch(win_in->ptr, buf[cnt]); - } - } + switch (mode) { + case RAWMODE: + break; + case TALKMODE:// Clear the screen and re-init. Which looks awful. + wclear(win_out->ptr); + wrefresh(win_out->ptr); + wclear(win_in->ptr); + //wrefresh(win_in->ptr); + wintab->next = 0; + endwin(); + start_screen(call); + start_talk_mode(wintab, win_in, win_out); + restorecursor(mode, win_out); + break; + case SLAVEMODE:// Also fix me. + wclear(win_out->ptr); + //wrefresh(win_out->ptr); + wintab->next = 0; + endwin(); + start_screen(call); + start_slave_mode(wintab, win_in, win_out); + restorecursor(mode, win_out); + break; } - -/* waddnstr(win_in->ptr, buf, bytes); */ - wrefresh(win_in->ptr); } -int getstring(wint * wintab, char text[], char buf[]) +void waddnstrcrcheck(t_win *win_in, char *buf, int bytes, int draw, int mode, t_win *win_out) { - int c; - int ypos = 0, xpos = 0; - int bytes = 0; + int n; + for (n=0;n<bytes;n++){ + int width; + incharbuf[incharbuflen++] = buf[n]; + if (completecharlen(incharbuf) > incharbuflen)continue; + if (eatchar && (incharbuf[0] == '\n')){ + eatchar = 0; + incharbuflen = 0; + continue; + } + width = widthchar(incharbuf, incharbuflen, inbufwid); + eatchar = 0; + if (draw) { + if (win_in) waddnstr(win_in->ptr, incharbuf, incharbuflen); + else write(STDOUT_FILENO, incharbuf, incharbuflen); + } + if (incharbuf[0] == '\n')incharbuflen = 0; + else if (width + inbufwid <= COLS){ + if (inbuflen + incharbuflen <= MAX_BUFLEN){ + memcpy(&inbuf[inbuflen], incharbuf, incharbuflen); + inbuflen += incharbuflen; + } + incharbuflen = 0; + inbufwid += width; + if (inbufwid >= COLS)eatchar = 1; + continue;// Skip to next line when width goes over. + } + addscrollline(inbuf, inbuflen); + if (incharbuflen){ + memcpy(&inbuf[0], incharbuf, incharbuflen); + inbuflen = incharbuflen; + incharbuflen = 0; + inbufwid = width; + } + else { + inbuflen = 0; + inbufwid = 0; + } + if (scrolledup && win_in && win_out){ + scrolledup++; // scrolledup is relative to bottom line + scrolltext(win_in, 0, mode, win_out); + } + } + if (draw && win_in) wrefresh(win_in->ptr); + +} - WINDOW *win = winopen(wintab, 3, COLS, 10, 0, TRUE); - wmove(win, 1, 2); - waddstr(win, text); - wrefresh(win); +void writeincom(int mode, int encoding, t_win * win_in, unsigned char buf[], int bytes, t_win *win_out) +{ + if (mode & RAWMODE) { + while (write(STDOUT_FILENO, buf, bytes) == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + usleep(100000); + continue; + } + exit(1); + } + return; + } else if (encoding == UTF8ENCODING) + waddnstrcrcheck(win_in, (char *)buf, bytes,scrolledup == 0, mode, win_out); + else { + char *inbuf = (char *) buf, out[MAX_BUFLEN], *outbuf=out; + size_t insize = bytes, outsize = MAX_BUFLEN; + iconv(ibm850toutf8, &inbuf, &insize, &outbuf, &outsize); + waddnstrcrcheck(win_in, out, MAX_BUFLEN-outsize,scrolledup == 0, mode, win_out); + } + return; +} +static void writeincomstr(int mode, int encoding, t_win * win_in, char buf[], t_win *win_out) +{ + int len; + len = strlen(buf); + writeincom(mode, encoding, win_in, (unsigned char *)buf, len, win_out); +} - do { - c = getch(); - if (c != ERR) { - switch (c) { - case KEY_BACKSPACE: - case 127: - { - getyx(win, ypos, xpos); - if (xpos > 0 && bytes > 0) { - wmove(win, ypos, --xpos); - waddch(win, ' '); - wmove(win, ypos, xpos); - bytes--; - } - } - break; - case (int) '\n': - case (int) '\r': - case KEY_ENTER: - { - waddch(win, '\n'); - buf[bytes++] = (char) '\n'; - wrefresh(win); - buf[bytes] = 0; - } - break; - default: - { - waddch(win, (char) c); - buf[bytes++] = (char) c; - } - } - wrefresh(win); - } - } - while (c != '\n' && c != '\r' && c != KEY_ENTER); - delwin(win); - winclose(wintab); - return 0; +int outstring(char *buf, wchar_t *string, int bytes, int encoding) +{ + char *inbuf = (char *) string, *outbuf = buf; + size_t insize = bytes * sizeof(wchar_t), outsize = MAX_BUFLEN-1; + if (encoding == UTF8ENCODING){ + iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); + } + else { + iconv(wcharttoibm850, &inbuf, &insize, &outbuf, &outsize); + + } + buf[(MAX_BUFLEN-1)-outsize] = '\0'; + return (MAX_BUFLEN-1)-outsize; +} + +int getstring(wint * wintab, char text[], char buf[]) +{ + wchar_t c; + int ypos = 0, xpos = 0; + int bytes = 0; + wchar_t wbuf[MAX_BUFLEN]; + WINDOW *win = winopen(wintab, 3, COLS, 10, 0, TRUE); + int done = 0; + + wmove(win, 1, 2); + waddstr(win, text); + wrefresh(win); + + do { + int r; + wint_t ci; + r = get_wch(&ci); + if (r != ERR) { + c = (wchar_t) ci; + if (((r == KEY_CODE_YES) && (c == KEY_BACKSPACE))|| + ((r == OK) && ((c==127)|| (c==8)))){ + getyx(win, ypos, xpos); + if (bytes > 0) { + int width, j; + width = wcwidthcontrol(wbuf[bytes-1]); + for (j=0;j<width;j++) mvwdelch(win, ypos, xpos-width); + xpos -= width; + wmove(win, ypos, xpos); + bytes--; + } + } + else if (( (r==KEY_CODE_YES) && (c == KEY_ENTER))|| + ( (r == OK) && ((c=='\n') || (c=='\r')))){ + wbuf[bytes++] = (wchar_t) '\n'; + outstring(buf, wbuf, bytes, UTF8ENCODING); + done = 1; + } + else if (r == KEY_CODE_YES); // Put in code for other KEYCODES here + else if (bytes+2 < MAX_BUFLEN){ + //int width; + //width = wins_nwchrmy(win, c); + //getyx(win, ypos, xpos); + //wmove(win, ypos, xpos+width); + wbuf[bytes++] = c; + drawinbuf(win, wbuf, bytes, bytes); + } + wrefresh(win); + } + } + while (!done); + delwin(win); + winclose(wintab); + return 0; } + int readoutg(t_win * win_out, wint * wintab, menuitem * top, char buf[], - int keyesc) + int keyesc, int mode, int encoding, t_win *win_in) { int out_cnt; - int c; + int r; + wint_t ci; + wchar_t c; int ypos = 0, xpos = 0; int value; - c = getch(); - if (c == ERR) + r = get_wch(&ci); + if (r == ERR) return 0; + c = (wchar_t) ci; if (c == keyesc) { - if ((value = top_menu(wintab, top, 1)) == 0) + if ((value = top_menu(wintab, top, 1)) == 0) { + restorecursor(mode, win_out); return 0; + } + restorecursor(mode, win_out); buf[0] = '~'; switch (value) { case 0x01: @@ -1111,6 +1462,7 @@ int readoutg(t_win * win_out, wint * wintab, menuitem * top, char buf[], getstring(wintab, "Please enter filename: ", &buf[2]); + restorecursor(mode, win_out); return strlen(buf); } case 0x12: @@ -1135,6 +1487,7 @@ int readoutg(t_win * win_out, wint * wintab, menuitem * top, char buf[], getstring(wintab, "Please enter filename: ", buf + 2); + restorecursor(mode, win_out); return strlen(buf); } case 0x21: @@ -1158,97 +1511,164 @@ int readoutg(t_win * win_out, wint * wintab, menuitem * top, char buf[], wrefresh(win_out->ptr); return 2; } - switch (c) { - case KEY_BACKSPACE: - case 127: - { + if (((r == KEY_CODE_YES) && (c == KEY_BACKSPACE))|| + ((r == OK) && ((c==127)|| (c==8)))){ + if ((mode == SLAVEMODE) && scrolledup) return 0; + while(win_out->curs_pos > 0){ + int width;int j; getyx(win_out->ptr, ypos, xpos); - if (win_out->bytes > 0) { - if (win_out->curs_pos < win_out->bytes) { - mvwaddnstr(win_out->ptr, ypos, - --xpos, - &win_out-> - string[win_out-> - curs_pos], - win_out->bytes - - win_out->curs_pos); - waddch(win_out->ptr, ' '); - memmove(&win_out-> - string[win_out->curs_pos - - 1], - &win_out->string[win_out-> - curs_pos], - win_out->bytes - - win_out->curs_pos); - } else - mvwaddch(win_out->ptr, ypos, - --xpos, ' '); - - wmove(win_out->ptr, ypos, xpos); - win_out->bytes--; - win_out->curs_pos--; - } - } - break; - case KEY_LEFT: - if (win_out->curs_pos > 0) { + width = wcwidthcontrol(win_out->string[win_out->curs_pos-1]); + for (j=0;j<width;j++) mvwdelch(win_out->ptr, ypos, xpos-width); + xpos -= width; + wmove(win_out->ptr, ypos, xpos); + if (win_out->curs_pos < win_out->bytes) { + memmove(&win_out-> + string[win_out->curs_pos - 1], + &win_out->string[win_out-> curs_pos], + (win_out->bytes - + win_out->curs_pos) * sizeof(wchar_t)); + } + win_out->bytes--; win_out->curs_pos--; - getyx(win_out->ptr, ypos, xpos); - wmove(win_out->ptr, ypos, xpos - 1); + if (width)break; } - break; - case KEY_RIGHT: - if (win_out->curs_pos < win_out->bytes) { + } + else if (( (r==KEY_CODE_YES) && (c == KEY_ENTER))|| + ( (r == OK) && ((c=='\n') || (c=='\r')))){ + if ((mode == SLAVEMODE) && scrolledup) return 0; + while (win_out->curs_pos < win_out->bytes) { // Move to end of the line + int width; + width = wcwidthcontrol(win_out->string[win_out->curs_pos]); win_out->curs_pos++; getyx(win_out->ptr, ypos, xpos); - wmove(win_out->ptr, ypos, xpos + 1); + wmove(win_out->ptr, ypos, xpos + width); + } + waddch(win_out->ptr, '\n'); + win_out->string[win_out->bytes++] = (wchar_t) '\n'; + wrefresh(win_out->ptr); + out_cnt = outstring(buf, win_out->string, win_out->bytes, encoding); + if (mode == SLAVEMODE){ + char obuf[MAX_BUFLEN]; + char *inbuf = (char *)win_out->string, *outbuf = obuf; + size_t insize = win_out->bytes * sizeof(wchar_t), outsize = MAX_BUFLEN; + iconv(wcharttoutf8, &inbuf, &insize, &outbuf, &outsize); + waddnstrcrcheck(win_in, obuf, MAX_BUFLEN-outsize, 0, mode, win_out); } - break; - case KEY_ENTER: - case (int) '\n': - case (int) '\r': - { - if (win_out->curs_pos < win_out->bytes) { - getyx(win_out->ptr, ypos, xpos); - wmove(win_out->ptr, ypos, - xpos + win_out->bytes - - win_out->curs_pos); + win_out->bytes = 0; + win_out->curs_pos = 0; + return out_cnt; + } + else if (r == KEY_CODE_YES){ + switch(c){ + case KEY_LEFT:// Character of 0 width + while (win_out->curs_pos > 0) { + int width; + win_out->curs_pos--; + width = wcwidthcontrol(win_out->string[win_out->curs_pos]); + getyx(win_out->ptr, ypos, xpos); + wmove(win_out->ptr, ypos, xpos - width); + if (width)break; // Skip to non-width + } + break; + case KEY_RIGHT: + { + int skipped = 0;// Skip over 0 length characters + while (win_out->curs_pos < win_out->bytes) { + int width; + width = wcwidthcontrol(win_out->string[win_out->curs_pos]); + if (width){ + if (skipped)break; + skipped = 1; + } + win_out->curs_pos++; + getyx(win_out->ptr, ypos, xpos); + wmove(win_out->ptr, ypos, xpos + width); + } + break; } - waddch(win_out->ptr, '\n'); - win_out->string[win_out->bytes++] = (char) '\n'; - wrefresh(win_out->ptr); - strncpy(buf, win_out->string, win_out->bytes); - wrefresh(win_out->ptr); - out_cnt = win_out->bytes; - win_out->bytes = 0; - win_out->curs_pos = 0; - return out_cnt; + case KEY_UP: + scrolltext(win_in, 1, mode, win_out); + break; + case KEY_DOWN: + scrolltext(win_in, -1, mode, win_out); + break; + case KEY_NPAGE: + scrolltext(win_in, -win_in->max_y, mode, win_out); + break; + case KEY_PPAGE: + scrolltext(win_in, win_in->max_y, mode, win_out); + break; + case KEY_HOME: + while (win_out->curs_pos > 0) { + int width; + win_out->curs_pos--; + width = wcwidthcontrol(win_out->string[win_out->curs_pos]); + getyx(win_out->ptr, ypos, xpos); + wmove(win_out->ptr, ypos, xpos - width); + } + break; + case KEY_END: + while (win_out->curs_pos < win_out->bytes) { // Move to end of the line + int width; + width = wcwidthcontrol(win_out->string[win_out->curs_pos]); + win_out->curs_pos++; + getyx(win_out->ptr, ypos, xpos); + wmove(win_out->ptr, ypos, xpos + width); + } + break; + case KEY_DC:{ + int skipped = 0; + if ((mode == SLAVEMODE) && scrolledup) return 0; + while (win_out->curs_pos < win_out->bytes){ + int width;int j; + getyx(win_out->ptr, ypos, xpos); + width = wcwidthcontrol(win_out->string[win_out->curs_pos]); + if (width){ + if (skipped)break; + skipped = 1; + } + for (j=0;j<width;j++) mvwdelch(win_out->ptr, ypos, xpos); + if (win_out->curs_pos + 1 < win_out->bytes) { + memmove(&win_out-> string[win_out->curs_pos], + &win_out->string[win_out-> curs_pos+1], + (win_out->bytes - (win_out->curs_pos+1)) * sizeof(wchar_t)); + } + win_out->bytes--; + } + break; + } + case KEY_RESIZE: + break; + case KEY_BACKSPACE: + break; + case KEY_ENTER: + break; + default: + break; } + } + else switch (c) { + case 8: + case 127: + case '\r': + case '\n': break; default: - { - waddch(win_out->ptr, (char) c); + if ((mode == SLAVEMODE) && scrolledup) return 0; // Don't try to edit while scrolled up in SLAVEmode. + // It's just not possible because the cursor is off screen + if (win_out->bytes < MAX_BUFLEN ) { if (win_out->curs_pos < win_out->bytes) { - getyx(win_out->ptr, ypos, xpos); - waddnstr(win_out->ptr, - &win_out->string[win_out-> - curs_pos], - win_out->bytes - - win_out->curs_pos); memmove(&win_out-> string[win_out->curs_pos + 1], - &win_out->string[win_out-> - curs_pos], - win_out->bytes - - win_out->curs_pos); - win_out->string[win_out->curs_pos] = - (char) c; - wmove(win_out->ptr, ypos, xpos); - } else - win_out->string[win_out->bytes] = (char) c; - + &win_out->string[win_out-> curs_pos], + (win_out->bytes - + win_out->curs_pos) * sizeof(wchar_t)); + } + win_out->string[win_out->curs_pos] = c; win_out->bytes++; win_out->curs_pos++; + drawinbuf(win_out->ptr, win_out->string, win_out->bytes, win_out->curs_pos); + break; } } wrefresh(win_out->ptr); @@ -1521,8 +1941,8 @@ void statbits(int mode, char stat, int m) refresh(); } - -int cmd_call(char *call[], int mode) + +int cmd_call(char *call[], int mode, int encoding) { menuitem con[] = { {"~Reconnect", 'R', M_ITEM, (void *) 0x01}, @@ -1585,8 +2005,8 @@ int cmd_call(char *call[], int mode) int upllen = 0; char *c, *t; t_gp gp; - t_win win_in; - t_win win_out; + t_win win_in = {0}; + t_win win_out = {0}; WINDOW *swin = 0; int cnt; int crc = 0; @@ -1607,9 +2027,11 @@ int cmd_call(char *call[], int mode) return FALSE; interrupted = FALSE; + sigwinchsignal = FALSE; signal(SIGQUIT, cmd_intr); signal(SIGINT, SIG_IGN); signal(SIGTSTP, SIG_IGN); + signal(SIGWINCH, cmd_sigwinch); sigemptyset(&oursigs); sigaddset(&oursigs, SIGQUIT); @@ -1668,6 +2090,12 @@ int cmd_call(char *call[], int mode) usleep(100000); continue; } + if ((errno == EINTR) && sigwinchsignal) { + // Just process screen resize here. + reinit_mode(mode, &wintab, &win_in, &win_out, call); + sigwinchsignal = 0; + continue; + } perror("select"); } break; @@ -1715,8 +2143,9 @@ int cmd_call(char *call[], int mode) convert_cr_lf(buf, bytes); if (!sevenplus) { - writeincom(mode, &win_in, - (unsigned char * ) buf, bytes); + writeincom(mode, encoding, &win_in, + (unsigned char * ) buf, bytes, &win_out); + restorecursor(mode, &win_out); } else { for (cnt = 0; cnt < bytes; cnt++) @@ -1843,7 +2272,7 @@ int cmd_call(char *call[], int mode) } else { bytes = readoutg(&win_out, &wintab, top, buf, - 0x1d); + 0x1d, mode, encoding, &win_in); if (bytes == -1) { wclear(win_in.ptr); wrefresh(win_in.ptr); @@ -1908,24 +2337,26 @@ int cmd_call(char *call[], int mode) case '?': case 'h': case 'H': - printf("\nTilde escapes:\n"); - printf(". close\n"); - printf("~ send ~\n"); - printf("r reconnect\n"); - printf("! shell\n"); - printf("Z suspend program. Resume with \"fg\"\n"); - printf("s Stop upload\n"); - printf("o Open log\n"); - printf("c Close log\n"); - printf("0 Switch GUI to \"RAW\" (line) mode - already here ;)\n"); - printf("1 Switch GUI to \"Slave\" mode\n"); - printf("2 Switch GUI to \"Talk\" (split) mode\n"); - printf("u Upload\n"); - printf("a Upload (autobin protocol)\n"); - printf("b Upload binary data (crlf conversion)\n"); - printf("yd YAPP Download\n"); - printf("yu YAPP Upload\n"); - fflush(stdout); + writeincomstr(mode, encoding, &win_in,"\nTilde escapes:\n", &win_out); + writeincomstr(mode, encoding, &win_in,". close\n", &win_out); + writeincomstr(mode, encoding, &win_in,"~ send ~\n", &win_out); + writeincomstr(mode, encoding, &win_in,"r reconnect\n", &win_out); + writeincomstr(mode, encoding, &win_in,"! shell\n", &win_out); + writeincomstr(mode, encoding, &win_in,"Z suspend program. Resume with \"fg\"\n", &win_out); + writeincomstr(mode, encoding, &win_in,"s Stop upload\n", &win_out); + writeincomstr(mode, encoding, &win_in,"o Open log\n", &win_out); + writeincomstr(mode, encoding, &win_in,"c Close log\n", &win_out); + writeincomstr(mode, encoding, &win_in,"0 Switch GUI to \"RAW\" (line) mode - already here ;)\n", &win_out); + writeincomstr(mode, encoding, &win_in,"1 Switch GUI to \"Slave\" mode\n", &win_out); + writeincomstr(mode, encoding, &win_in,"2 Switch GUI to \"Talk\" (split) mode\n", &win_out); + writeincomstr(mode, encoding, &win_in,"u Upload\n", &win_out); + writeincomstr(mode, encoding, &win_in,"a Upload (autobin protocol)\n", &win_out); + writeincomstr(mode, encoding, &win_in,"b Upload binary data (crlf conversion)\n", &win_out); + writeincomstr(mode, encoding, &win_in,"yd YAPP Download\n", &win_out); + writeincomstr(mode, encoding, &win_in,"yu YAPP Upload\n", &win_out); + writeincomstr(mode, encoding, &win_in,"i IBM850 encoding\n", &win_out); + writeincomstr(mode, encoding, &win_in,"8 UTF-8 encoding\n", &win_out); + restorecursor(mode, &win_out); continue; case 'S': case 's': @@ -1938,6 +2369,7 @@ int cmd_call(char *call[], int mode) statline(mode, "No upload in progress"); } + restorecursor(mode, &win_out); continue; case 'A': case 'a': @@ -1948,6 +2380,7 @@ int cmd_call(char *call[], int mode) if (uploadfile != -1) { statline(mode, "Already uploading"); + restorecursor(mode, &win_out); continue; } if ((t = @@ -1959,12 +2392,14 @@ int cmd_call(char *call[], int mode) if (*t == '\0') { statline(mode, "Upload requires a filename"); + restorecursor(mode, &win_out); continue; } uploadfile = open(t, O_RDONLY); if (uploadfile == -1) { statline(mode, "Unable to open upload file"); + restorecursor(mode, &win_out); continue; } if (lseek(uploadfile, 0L, SEEK_END) @@ -2063,8 +2498,9 @@ int cmd_call(char *call[], int mode) uplpos = 0; upldp = -1; upllen = 0; - } - break; + } + restorecursor(mode, &win_out); + break; case 'b': case 'B': binup = TRUE; @@ -2102,6 +2538,7 @@ int cmd_call(char *call[], int mode) statline(mode, s); } else statbits(mode, 'L', 1); + restorecursor(mode, &win_out); continue; case 'C': case 'c': @@ -2113,10 +2550,12 @@ int cmd_call(char *call[], int mode) statline(mode, "Log file not open"); } + restorecursor(mode, &win_out); continue; case 'Y': case 'y': cmd_yapp(buf + 2, bytes - 2); + restorecursor(mode, &win_out); continue; case '~': bytes--; @@ -2146,9 +2585,21 @@ int cmd_call(char *call[], int mode) &wintab, &win_in, &win_out, call); continue; + case '8': + encoding = UTF8ENCODING; + statline(mode, "UTF-8 encoding"); + restorecursor(mode, &win_out); + continue; + case 'i': + case 'I': + encoding = ORIGINALENCODING; + statline(mode, "IBM850 encoding"); + restorecursor(mode, &win_out); + continue; default: statline(mode, "Unknown '~' escape. Type ~h for a list"); + restorecursor(mode, &win_out); continue; } } @@ -2164,8 +2615,10 @@ int cmd_call(char *call[], int mode) if (uploadfile != -1) { statline(mode, "Ignored. Type ~s to stop upload"); + restorecursor(mode, &win_out); continue; } + convert_lf_cr(buf, bytes); while (offset != bytes) { @@ -2199,6 +2652,7 @@ int cmd_call(char *call[], int mode) winclose(&wintab); statline(mode, "Upload complete: 0 bytes"); + restorecursor(mode, &win_out); continue; } if (upldp == -1) { @@ -2214,6 +2668,7 @@ int cmd_call(char *call[], int mode) "Upload complete: %ld bytes", uplpos); statline(mode, s); + restorecursor(mode, &win_out); continue; } if (upllen == -1) { @@ -2225,6 +2680,7 @@ int cmd_call(char *call[], int mode) "Error reading upload file: upload aborted at %ld bytes", uplpos); statline(mode, s); + restorecursor(mode, &win_out); continue; } if (!binup) @@ -2244,6 +2700,7 @@ int cmd_call(char *call[], int mode) if (errno != EWOULDBLOCK && errno != EAGAIN) { sprintf(s, "Write error during upload. Connection lost"); statline(mode, s); + restorecursor(mode, &win_out); perror("write"); break; } @@ -2302,18 +2759,27 @@ int cmd_call(char *call[], int mode) } } +void iconvclose(void) +{ + iconv_close(ibm850toutf8); + iconv_close(wcharttoibm850); + iconv_close(wcharttoutf8); + iconv_close(utf8towchart); +} int main(int argc, char **argv) { int p; int mode = TALKMODE; + int encoding = UTF8ENCODING;// Maybe controversial? + setlocale(LC_ALL, ""); if (!isatty(STDIN_FILENO)) stdin_is_tty = 0; setlinebuf(stdin); - while ((p = getopt(argc, argv, "b:dhm:p:rs:RStT:vw:W")) != -1) { + while ((p = getopt(argc, argv, "b:dhm:p:rs:RStT:vw:Wi8")) != -1) { switch (p) { case 'b': if (*optarg != 'e' && *optarg != 'l') { @@ -2350,6 +2816,7 @@ int main(int argc, char **argv) } break; case 'r': + COLS = 80; // This is used to format the scrollback buffer, which is stored in raw mode = RAWMODE; break; case 's': @@ -2402,6 +2869,12 @@ int main(int argc, char **argv) case 'W': wait_for_remote_disconnect = TRUE; break; + case 'i': + encoding = ORIGINALENCODING; + break; + case '8': + encoding = UTF8ENCODING; + break; case '?': case ':': usage(); @@ -2411,6 +2884,13 @@ int main(int argc, char **argv) if (optind == argc || optind == argc - 1) { usage(); } + + ibm850toutf8 = iconv_open("UTF8", "IBM850"); + wcharttoibm850 = iconv_open("IBM850", "WCHAR_T"); + wcharttoutf8 = iconv_open("UTF8", "WCHAR_T"); + utf8towchart = iconv_open("WCHAR_T", "UTF8"); + atexit(iconvclose); + port = argv[optind]; if (ax25_config_load_ports() == 0) { @@ -2459,7 +2939,7 @@ int main(int argc, char **argv) printf("GW4PTS AX.25 Connect v1.11\n"); fflush(stdout); } - while (cmd_call(argv + optind + 1, mode)) { + while (cmd_call(argv + optind + 1, mode, encoding)) { if (!be_silent) { printf("Wait 60 sec before reconnect\n"); fflush(stdout); diff --git a/call/call.man b/call/call.man index a0ed052..f49de0d 100644 --- a/call/call.man +++ b/call/call.man @@ -42,6 +42,9 @@ Rose node tables. For Rose connects to work must have been used to set a callsign/uid mapping for the userid in use. .SH OPTIONS .TP 10 +.BI "\-8" +UTF-8 encoding (default) +.TP 10 .BI "\-b l|e" Sets the method of backoff to use with AX.25 connections. The default is taken from the port specification, but can be overridden by this option. @@ -57,6 +60,8 @@ Turn on socket level debugging. .BI \-h Selects Slave mode. .TP 10 +.BI \-i +Use IBM850 encoding .BI "\-m s|e" Sets the AX.25 mode to use. The default is taken from the port specification, but can be overridden by this option. Valid values are @@ -68,22 +73,12 @@ for extended (modulus 128) AX.25 operation. .BI "\-p paclen" Specify a specific maximum amount of data to be sent in each AX.25 packet. .TP 10 -.BI "\-s mycall" -Connect using the specified source @@@call@@@ (you may need to be root). -.TP 10 -.BI \-R -Disable all remote commands like autobin (#BIN#), 7plus downloads, //echo, etc. -This is useful for scripting where you just don't like the remote site to trigger uncaught conditions here. -.TP 10 -.BI \-S -Be silent. Useful for using @@@call@@@ in shellscripts (together with option \-r) in order to be really transparent. -.TP 10 -.BI "\-T timeout" -Set idle timeout seconds after a connection will be closed automatically when there's no data being transferred (in- and outbound). If timeout is set to 3600, the connection will close after 1h inactivity. A value of 0.5 is 500ms. -.TP 10 .BI \-r Selects Raw mode. .TP 10 +.BI "\-s mycall" +Connect using the specified source @@@call@@@ (you may need to be root). +.TP 10 .BI \-t Selects Talk mode. .TP 10 @@ -93,6 +88,16 @@ Display the version. .BI "\-w window" Specify a specific AX.25 window for this connection. Only valid in AX.25 mode. .TP 10 +.BI \-S +Be silent. Useful for using @@@call@@@ in shellscripts (together with option \-r) in order to be really transparent. +.TP 10 +.BI \-R +Disable all remote commands like autobin (#BIN#), 7plus downloads, //echo, etc. +This is useful for scripting where you just don't like the remote site to trigger uncaught conditions here. +.TP 10 +.BI "\-T timeout" +Set idle timeout seconds after a connection will be closed automatically when there's no data being transferred (in- and outbound). If timeout is set to 3600, the connection will close after 1h inactivity. A value of 0.5 is 500ms. +.TP 10 .BI "\-W" Wait for remote disconnect even if stdin is closed. Normaly, "echo q | @@@call@@@ ax0 db0fhn" closes immediately after "echo q" terminates; thus we never see the greeting from db0fhn. With the -W option, @@@call@@@ waits until the ax25-connection is disconnected (i.e. db0fhn disconnects after "q" command). Very useful for scripting. You might like to combine it with the -T option. .LP @@ -120,6 +125,9 @@ Change to Slave mode .BI ~2 Change to Talk mode .TP 16 +.BI ~8 +UTF-8 encoding +.TP 16 .BI ~a Start a file transfer (Autobin) .TP 16 @@ -132,6 +140,9 @@ Close the logfile .BI ~h List escapes .TP 16 +.BI ~i +IBM850 encoding +.TP 16 .BI "~o [filename]" Open a logfile (default 'logfile.txt') .TP 16 |