@ -70,14 +70,69 @@ typedef struct {
typedef struct { char data [ SCRL_W ] ; int len ; int base_attr ; } ScrlLine ;
typedef struct { char data [ SCRL_W ] ; int len ; int base_attr ; } ScrlLine ;
/* ── Session ──────────────────────────────────────────────────
* All per - connection state in one struct so the program can run
* multiple simultaneous sessions ( one per tab in the GUI , or one
* in the CLI ) . S always points to the currently active session . */
# define MAX_SESSIONS 4
# define SESSION_LABEL 32
typedef struct {
int active ; /* slot is in use */
int host_idx ; /* index into hosts[] */
char label [ SESSION_LABEL ] ; /* display name / tab title */
/* Connection */
int sock ;
int connected ;
time_t connect_time ;
/* Logging */
int logging ;
FILE * log_file ;
/* BPQ monitor frame accumulator */
int in_mon_frame ;
char mon_fbuf [ 4096 ] ;
int mon_flen ;
/* Port tracking */
int port_seen ;
char ports_line_buf [ 256 ] ;
int ports_line_len ;
int ports_parse_mode ;
int ports_loaded ;
/* Output scrollback */
ScrlLine out_sbuf [ SCRL_MAX ] ;
int out_sbuf_head , out_sbuf_count ;
int out_scroll ;
char out_part [ SCRL_W ] ;
int out_part_len , out_part_base , out_part_cur ;
/* Monitor scrollback */
ScrlLine mon_sbuf [ SCRL_MAX ] ;
int mon_sbuf_head , mon_sbuf_count ;
int mon_scroll ;
char mon_part [ SCRL_W ] ;
int mon_part_len , mon_part_base , mon_part_cur ;
/* Input */
char input_buf [ INPUT_MAX ] ;
int input_len ;
/* Input history */
char hist_buf [ HIST_MAX ] [ INPUT_MAX ] ;
int hist_head , hist_count , hist_pos ;
char hist_save [ INPUT_MAX ] ;
} Session ;
static Session sessions [ MAX_SESSIONS ] ;
static Session * S ; /* active (displayed) session */
HostEntry hosts [ MAX_HOSTS ] ;
HostEntry hosts [ MAX_HOSTS ] ;
int num_hosts = 0 ;
int num_hosts = 0 ;
int current_host = - 1 ;
/* ── Network ─────────────────────────────────────────────── */
int sock = - 1 ;
int connected = 0 ;
time_t connect_time = 0 ;
/* ── Signal handling ─────────────────────────────────────── *
/* ── Signal handling ─────────────────────────────────────── *
* SIGINT ( Ctrl + C ) is trapped rather than left at its default
* SIGINT ( Ctrl + C ) is trapped rather than left at its default
@ -94,11 +149,7 @@ static void on_sigint(int signum)
sigint_pending = 1 ;
sigint_pending = 1 ;
}
}
/* ── Logging ─────────────────────────────────────────────── */
/* ── BPQ / monitor state (shared across all sessions) ─────── */
int logging = 0 ;
FILE * log_file = NULL ;
/* ── BPQ / monitor state ─────────────────────────────────── */
int mon_visible = 1 ;
int mon_visible = 1 ;
int mon_tx = 1 ;
int mon_tx = 1 ;
int mon_com = 1 ;
int mon_com = 1 ;
@ -106,38 +157,8 @@ int mon_nodes = 0;
int mon_colour = 1 ;
int mon_colour = 1 ;
int mon_ui_only = 1 ;
int mon_ui_only = 1 ;
int port_mask = 0xFFFF ;
int port_mask = 0xFFFF ;
int in_mon_frame = 0 ;
static char mon_fbuf [ 4096 ] ; /* accumulates one complete 0xFF…0xFE monitor frame */
char port_names [ 32 ] [ 32 ] ; /* port 1-32 names, loaded from config */
static int mon_flen = 0 ;
int port_seen = 0 ; /* bitmask: bit N-1 set when port N seen in monitor data */
char port_names [ 32 ] [ 32 ] ; /* names for ports 1-32, loaded from config or defaulted */
/* ── Session line parser (for PORTS response) ────────────── */
static char ports_line_buf [ 256 ] ;
static int ports_line_len = 0 ;
static int ports_parse_mode = 0 ; /* 1 while inside "} Ports" response */
static int ports_loaded = 0 ; /* set when PORTS list fully received */
/* ── Scrollback buffers ──────────────────────────────────── */
static ScrlLine out_sbuf [ SCRL_MAX ] ;
static int out_sbuf_head = 0 , out_sbuf_count = 0 ;
static int out_scroll = 0 ; /* 0=live, >0=lines from bottom */
static char out_part [ SCRL_W ] ;
static int out_part_len = 0 , out_part_base = 0 , out_part_cur = 0 ;
static ScrlLine mon_sbuf [ SCRL_MAX ] ;
static int mon_sbuf_head = 0 , mon_sbuf_count = 0 ;
static int mon_scroll = 0 ;
static char mon_part [ SCRL_W ] ;
static int mon_part_len = 0 , mon_part_base = 0 , mon_part_cur = 0 ;
/* ── Input history ───────────────────────────────────────── */
static char hist_buf [ HIST_MAX ] [ INPUT_MAX ] ;
static int hist_head = 0 ; /* next write slot (ring) */
static int hist_count = 0 ; /* valid entries */
static int hist_pos = - 1 ; /* -1=live, 0=most recent ... */
static char hist_save [ INPUT_MAX ] ; /* saved input while browsing */
/* ── ncurses windows ─────────────────────────────────────── */
/* ── ncurses windows ─────────────────────────────────────── */
WINDOW * status_win = NULL ;
WINDOW * status_win = NULL ;
@ -148,10 +169,6 @@ WINDOW *sep2_win = NULL; /* separator above input area */
WINDOW * input_win = NULL ;
WINDOW * input_win = NULL ;
WINDOW * fkey_win = NULL ; /* function key hint bar at bottom */
WINDOW * fkey_win = NULL ; /* function key hint bar at bottom */
/* ── Input buffer ────────────────────────────────────────── */
char input_buf [ INPUT_MAX ] ;
int input_len = 0 ;
/* ── ICS-213 General Message ─────────────────────────────── */
/* ── ICS-213 General Message ─────────────────────────────── */
# define ICS_SBLINE_MAX 120 /* "SB <addressee>@<distribution>" line */
# define ICS_SBLINE_MAX 120 /* "SB <addressee>@<distribution>" line */
# define ICS_FIELD_MAX 80 /* To / From / Subject single-line fields */
# define ICS_FIELD_MAX 80 /* To / From / Subject single-line fields */
@ -385,13 +402,13 @@ void open_log(const char *prefix)
snprintf ( filename , PATH_MAX , " %s_%04d%02d%02d_%02d%02d%02d.log " ,
snprintf ( filename , PATH_MAX , " %s_%04d%02d%02d_%02d%02d%02d.log " ,
prefix , t - > tm_year + 1900 , t - > tm_mon + 1 , t - > tm_mday ,
prefix , t - > tm_year + 1900 , t - > tm_mon + 1 , t - > tm_mday ,
t - > tm_hour , t - > tm_min , t - > tm_sec ) ;
t - > tm_hour , t - > tm_min , t - > tm_sec ) ;
log_file = fopen ( filename , " w " ) ;
S- > log_file = fopen ( filename , " w " ) ;
if ( log_file) logging = 1 ;
if ( S- > log_file) S - > logging = 1 ;
}
}
void close_log ( void )
void close_log ( void )
{
{
if ( log_file) { fclose ( log_file ) ; log_file = NULL ; logging = 0 ; }
if ( S- > log_file) { fclose ( S - > log_file ) ; S- > log_file = NULL ; S - > logging = 0 ; }
}
}
/* ═══════════════════════════════════════════════════════════
/* ═══════════════════════════════════════════════════════════
@ -431,22 +448,22 @@ int tcp_connect(const char *host, const char *port)
int tcp_send ( const char * data , int len )
int tcp_send ( const char * data , int len )
{
{
if ( sock < 0 | | len < = 0 ) return - 1 ;
if ( S- > sock < 0 | | len < = 0 ) return - 1 ;
if ( logging & & log_file ) { fwrite ( data , 1 , len , log_file) ; fflush ( log_file ) ; }
if ( S- > logging & & S - > log_file ) { fwrite ( data , 1 , len , S- > log_file) ; fflush ( S - > log_file ) ; }
return send ( sock, data , len , 0 ) ;
return send ( S- > sock, data , len , 0 ) ;
}
}
void tcp_disconnect ( void )
void tcp_disconnect ( void )
{
{
if ( sock > = 0 ) { close ( sock) ; sock = - 1 ; }
if ( S- > sock > = 0 ) { close ( S- > sock) ; S - > sock = - 1 ; }
connected = 0 ;
S- > connected = 0 ;
connect_time = 0 ;
S- > connect_time = 0 ;
in_mon_frame = 0 ;
S- > in_mon_frame = 0 ;
mon_flen = 0 ;
S- > mon_flen = 0 ;
ports_parse_mode = 0 ;
S- > ports_parse_mode = 0 ;
ports_line_len = 0 ;
S- > ports_line_len = 0 ;
ports_loaded = 0 ;
S- > ports_loaded = 0 ;
port_seen = 0 ;
S- > port_seen = 0 ;
for ( int i = 0 ; i < 32 ; i + + ) snprintf ( port_names [ i ] , 32 , " Port %d " , i + 1 ) ;
for ( int i = 0 ; i < 32 ; i + + ) snprintf ( port_names [ i ] , 32 , " Port %d " , i + 1 ) ;
draw_status ( ) ;
draw_status ( ) ;
}
}
@ -457,26 +474,26 @@ void tcp_disconnect(void)
void send_trace_options ( void )
void send_trace_options ( void )
{
{
if ( sock < 0 ) return ;
if ( S- > sock < 0 ) return ;
char buf [ 80 ] ;
char buf [ 80 ] ;
int len = snprintf ( buf , sizeof ( buf ) ,
int len = snprintf ( buf , sizeof ( buf ) ,
" \\ \\ \\ \\ %x %x %x %x %x %x \r " ,
" \\ \\ \\ \\ %x %x %x %x %x %x \r " ,
port_mask , mon_tx , mon_com ,
port_mask , mon_tx , mon_com ,
mon_nodes , mon_colour , mon_ui_only ) ;
mon_nodes , mon_colour , mon_ui_only ) ;
send ( sock, buf , len , 0 ) ;
send ( S- > sock, buf , len , 0 ) ;
}
}
/* Same as send_trace_options but appends P8=1, which tells BPQ to call
/* Same as send_trace_options but appends P8=1, which tells BPQ to call
* SendPortsForMonitor ( ) — returns port list as 0xFF 0xFF N | num desc | . . . */
* SendPortsForMonitor ( ) — returns port list as 0xFF 0xFF N | num desc | . . . */
static void send_trace_options_with_ports ( void )
static void send_trace_options_with_ports ( void )
{
{
if ( sock < 0 ) return ;
if ( S- > sock < 0 ) return ;
char buf [ 80 ] ;
char buf [ 80 ] ;
int len = snprintf ( buf , sizeof ( buf ) ,
int len = snprintf ( buf , sizeof ( buf ) ,
" \\ \\ \\ \\ %x %x %x %x %x %x 0 1 \r " ,
" \\ \\ \\ \\ %x %x %x %x %x %x 0 1 \r " ,
port_mask , mon_tx , mon_com ,
port_mask , mon_tx , mon_com ,
mon_nodes , mon_colour , mon_ui_only ) ;
mon_nodes , mon_colour , mon_ui_only ) ;
send ( sock, buf , len , 0 ) ;
send ( S- > sock, buf , len , 0 ) ;
}
}
/* Parse the 0xFF 0xFF port-info packet from SendPortsForMonitor.
/* Parse the 0xFF 0xFF port-info packet from SendPortsForMonitor.
@ -501,11 +518,11 @@ static void parse_portinfo(const char *data, int len)
int copy = dlen < 31 ? dlen : 31 ;
int copy = dlen < 31 ? dlen : 31 ;
memcpy ( port_names [ n - 1 ] , desc , copy ) ;
memcpy ( port_names [ n - 1 ] , desc , copy ) ;
port_names [ n - 1 ] [ copy ] = ' \0 ' ;
port_names [ n - 1 ] [ copy ] = ' \0 ' ;
port_seen | = ( 1u < < ( n - 1 ) ) ;
S- > port_seen | = ( 1u < < ( n - 1 ) ) ;
}
}
if ( p < e & & * p = = ' | ' ) p + + ;
if ( p < e & & * p = = ' | ' ) p + + ;
}
}
ports_loaded = 1 ;
S- > ports_loaded = 1 ;
}
}
static int bpq_color_attr ( unsigned char color_byte )
static int bpq_color_attr ( unsigned char color_byte )
@ -522,7 +539,7 @@ static int bpq_color_attr(unsigned char color_byte)
return COLOR_PAIR ( CP_MAGENTA ) ;
return COLOR_PAIR ( CP_MAGENTA ) ;
}
}
/* Scan monitor frame text for "Port=N" and record N in port_seen bitmask.
/* Scan monitor frame text for "Port=N" and record N in S-> port_seen bitmask.
* BPQ monitor frames use " Port=N " format ( e . g . " KB8PMY>APRS Port=2 <UI> " ) .
* BPQ monitor frames use " Port=N " format ( e . g . " KB8PMY>APRS Port=2 <UI> " ) .
* Port names come from the PORTS session command , not from monitor frames . */
* Port names come from the PORTS session command , not from monitor frames . */
static void detect_port_number ( const char * text , int len )
static void detect_port_number ( const char * text , int len )
@ -539,7 +556,7 @@ static void detect_port_number(const char *text, int len)
while ( j < len & & p [ j ] > = ' 0 ' & & p [ j ] < = ' 9 ' )
while ( j < len & & p [ j ] > = ' 0 ' & & p [ j ] < = ' 9 ' )
n = n * 10 + ( p [ j + + ] - ' 0 ' ) ;
n = n * 10 + ( p [ j + + ] - ' 0 ' ) ;
if ( n > = 1 & & n < = 32 )
if ( n > = 1 & & n < = 32 )
port_seen | = ( 1 < < ( n - 1 ) ) ;
S- > port_seen | = ( 1 < < ( n - 1 ) ) ;
}
}
}
}
}
}
@ -553,7 +570,7 @@ static void parse_session_line(const char *line, int len)
while ( len > 0 & & ( unsigned char ) line [ len - 1 ] < = ' ' ) len - - ;
while ( len > 0 & & ( unsigned char ) line [ len - 1 ] < = ' ' ) len - - ;
if ( len = = 0 ) return ;
if ( len = = 0 ) return ;
if ( ! ports_parse_mode) {
if ( ! S- > ports_parse_mode) {
/* Detect "} Ports" header (case-insensitive) */
/* Detect "} Ports" header (case-insensitive) */
for ( int i = 0 ; i + 6 < len ; i + + ) {
for ( int i = 0 ; i + 6 < len ; i + + ) {
if ( line [ i ] = = ' } ' & & line [ i + 1 ] = = ' ' & &
if ( line [ i ] = = ' } ' & & line [ i + 1 ] = = ' ' & &
@ -562,7 +579,7 @@ static void parse_session_line(const char *line, int len)
( line [ i + 4 ] = = ' r ' | | line [ i + 4 ] = = ' R ' ) & &
( line [ i + 4 ] = = ' r ' | | line [ i + 4 ] = = ' R ' ) & &
( line [ i + 5 ] = = ' t ' | | line [ i + 5 ] = = ' T ' ) & &
( line [ i + 5 ] = = ' t ' | | line [ i + 5 ] = = ' T ' ) & &
( line [ i + 6 ] = = ' s ' | | line [ i + 6 ] = = ' S ' ) ) {
( line [ i + 6 ] = = ' s ' | | line [ i + 6 ] = = ' S ' ) ) {
ports_parse_mode = 1 ;
S- > ports_parse_mode = 1 ;
return ;
return ;
}
}
}
}
@ -583,11 +600,11 @@ static void parse_session_line(const char *line, int len)
int copy = nlen < 31 ? nlen : 31 ;
int copy = nlen < 31 ? nlen : 31 ;
memcpy ( port_names [ n - 1 ] , line + j , copy ) ;
memcpy ( port_names [ n - 1 ] , line + j , copy ) ;
port_names [ n - 1 ] [ copy ] = ' \0 ' ;
port_names [ n - 1 ] [ copy ] = ' \0 ' ;
port_seen | = ( 1 < < ( n - 1 ) ) ;
S- > port_seen | = ( 1 < < ( n - 1 ) ) ;
}
}
} else {
} else {
if ( ports_parse_mode) ports_loaded = 1 ; /* list ended cleanly */
if ( S- > ports_parse_mode) S - > ports_loaded = 1 ; /* list ended cleanly */
ports_parse_mode = 0 ;
S- > ports_parse_mode = 0 ;
}
}
}
}
@ -595,12 +612,12 @@ static void parse_session_line(const char *line, int len)
static void feed_session_byte ( unsigned char c )
static void feed_session_byte ( unsigned char c )
{
{
if ( c = = ' \r ' | | c = = ' \n ' ) {
if ( c = = ' \r ' | | c = = ' \n ' ) {
if ( ports_line_len > 0 ) {
if ( S- > ports_line_len > 0 ) {
parse_session_line ( ports_line_buf, ports_line_len ) ;
parse_session_line ( S- > ports_line_buf, S - > ports_line_len ) ;
ports_line_len = 0 ;
S- > ports_line_len = 0 ;
}
}
} else if ( c > = 32 & & c < 128 & & ports_line_len < 255 ) {
} else if ( c > = 32 & & c < 128 & & S- > ports_line_len < 255 ) {
ports_line_buf[ ports_line_len + + ] = ( char ) c ;
S- > ports_line_buf[ S - > ports_line_len + + ] = ( char ) c ;
}
}
}
}
@ -610,7 +627,7 @@ void bpq_process(const char *buf, int len)
const char * end = buf + len ;
const char * end = buf + len ;
while ( p < end ) {
while ( p < end ) {
if ( ! in_mon_frame) {
if ( ! S- > in_mon_frame) {
const char * ff = memchr ( p , 0xFF , end - p ) ;
const char * ff = memchr ( p , 0xFF , end - p ) ;
if ( ! ff ) {
if ( ! ff ) {
/* All remaining bytes are session output */
/* All remaining bytes are session output */
@ -632,8 +649,8 @@ void bpq_process(const char *buf, int len)
parse_portinfo ( p + 1 , ( int ) ( end - p - 1 ) ) ;
parse_portinfo ( p + 1 , ( int ) ( end - p - 1 ) ) ;
p = end ;
p = end ;
} else {
} else {
in_mon_frame = 1 ;
S- > in_mon_frame = 1 ;
mon_flen = 0 ;
S- > mon_flen = 0 ;
}
}
} else {
} else {
const char * fe = memchr ( p , 0xFE , end - p ) ;
const char * fe = memchr ( p , 0xFE , end - p ) ;
@ -641,26 +658,26 @@ void bpq_process(const char *buf, int len)
if ( seg_len > 0 ) {
if ( seg_len > 0 ) {
detect_port_number ( p , seg_len ) ;
detect_port_number ( p , seg_len ) ;
/* Accumulate into the per-frame buffer */
/* Accumulate into the per-frame buffer */
int room = ( int ) sizeof ( mon_fbuf) - 1 - mon_flen ;
int room = ( int ) sizeof ( S- > mon_fbuf) - 1 - S - > mon_flen ;
if ( room > 0 ) {
if ( room > 0 ) {
int add = seg_len < room ? seg_len : room ;
int add = seg_len < room ? seg_len : room ;
memcpy ( mon_fbuf + mon_flen , p , add ) ;
memcpy ( S- > mon_fbuf + S - > mon_flen , p , add ) ;
mon_flen + = add ;
S- > mon_flen + = add ;
}
}
}
}
if ( fe ) {
if ( fe ) {
/* Frame complete: apply client-side NODES filter then display */
/* Frame complete: apply client-side NODES filter then display */
if ( mon_flen > 0 ) {
if ( S- > mon_flen > 0 ) {
mon_fbuf[ mon_flen ] = ' \0 ' ;
S- > mon_fbuf[ S - > mon_flen ] = ' \0 ' ;
/* Detect NODES broadcast by AX.25 destination ">NODES" in header */
/* Detect NODES broadcast by AX.25 destination ">NODES" in header */
int is_nodes = 0 ;
int is_nodes = 0 ;
for ( int i = 0 ; i < = mon_flen - 6 & & ! is_nodes ; i + + )
for ( int i = 0 ; i < = S- > mon_flen - 6 & & ! is_nodes ; i + + )
if ( memcmp ( mon_fbuf + i , " >NODES " , 6 ) = = 0 ) is_nodes = 1 ;
if ( memcmp ( S- > mon_fbuf + i , " >NODES " , 6 ) = = 0 ) is_nodes = 1 ;
if ( mon_nodes | | ! is_nodes )
if ( mon_nodes | | ! is_nodes )
append_mon ( mon_fbuf, mon_flen , COLOR_PAIR ( CP_GREEN ) ) ;
append_mon ( S- > mon_fbuf, S - > mon_flen , COLOR_PAIR ( CP_GREEN ) ) ;
}
}
mon_flen = 0 ;
S- > mon_flen = 0 ;
in_mon_frame = 0 ;
S- > in_mon_frame = 0 ;
p = fe + 1 ;
p = fe + 1 ;
} else {
} else {
break ;
break ;
@ -668,7 +685,7 @@ void bpq_process(const char *buf, int len)
}
}
}
}
if ( logging & & log_file ) { fwrite ( buf , 1 , len , log_file) ; fflush ( log_file ) ; }
if ( S- > logging & & S - > log_file ) { fwrite ( buf , 1 , len , S- > log_file) ; fflush ( S - > log_file ) ; }
}
}
/* ═══════════════════════════════════════════════════════════
/* ═══════════════════════════════════════════════════════════
@ -813,10 +830,10 @@ void draw_status(void)
/* Center: connection info */
/* Center: connection info */
char center [ 80 ] ;
char center [ 80 ] ;
if ( connected & & current_host > = 0 ) {
if ( S- > connected & & S - > host_idx > = 0 ) {
int elapsed = ( int ) ( time ( NULL ) - connect_time) ;
int elapsed = ( int ) ( time ( NULL ) - S- > connect_time) ;
snprintf ( center , sizeof ( center ) , " %s [%02d:%02d] " ,
snprintf ( center , sizeof ( center ) , " %s [%02d:%02d] " ,
hosts [ current_host ] . name , elapsed / 60 , elapsed % 60 ) ;
hosts [ S- > host_idx ] . name , elapsed / 60 , elapsed % 60 ) ;
} else {
} else {
snprintf ( center , sizeof ( center ) , " Not Connected " ) ;
snprintf ( center , sizeof ( center ) , " Not Connected " ) ;
}
}
@ -828,7 +845,7 @@ void draw_status(void)
struct tm * t = gmtime ( & now ) ;
struct tm * t = gmtime ( & now ) ;
char right [ 32 ] ;
char right [ 32 ] ;
snprintf ( right , sizeof ( right ) , " %s%02d:%02d UTC " ,
snprintf ( right , sizeof ( right ) , " %s%02d:%02d UTC " ,
logging ? " LOG " : " " , t - > tm_hour , t - > tm_min ) ;
S- > logging ? " LOG " : " " , t - > tm_hour , t - > tm_min ) ;
int rx = COLS - ( int ) strlen ( right ) ;
int rx = COLS - ( int ) strlen ( right ) ;
if ( rx > 0 ) mvwprintw ( status_win , 0 , rx , " %s " , right ) ;
if ( rx > 0 ) mvwprintw ( status_win , 0 , rx , " %s " , right ) ;
@ -923,49 +940,49 @@ void append_mon(const char *text, int len, int attr)
const char * p = text ;
const char * p = text ;
const char * end = text + len ;
const char * end = text + len ;
mon_part_cur = attr ;
S- > mon_part_cur = attr ;
if ( mon_part_len = = 0 ) mon_part_base = attr ;
if ( S- > mon_part_len = = 0 ) S - > mon_part_base = attr ;
if ( mon_win & & mon_scroll = = 0 )
if ( mon_win & & S- > mon_scroll = = 0 )
wattron ( mon_win , mon_part_cur) ;
wattron ( mon_win , S- > mon_part_cur) ;
while ( p < end ) {
while ( p < end ) {
unsigned char c = ( unsigned char ) * p ;
unsigned char c = ( unsigned char ) * p ;
if ( c = = 0x1B & & ( p + 1 ) < end ) {
if ( c = = 0x1B & & ( p + 1 ) < end ) {
int na = bpq_color_attr ( ( unsigned char ) p [ 1 ] ) ;
int na = bpq_color_attr ( ( unsigned char ) p [ 1 ] ) ;
if ( mon_part_len + 2 < SCRL_W ) {
if ( S- > mon_part_len + 2 < SCRL_W ) {
mon_part[ mon_part_len + + ] = ( char ) 0x1B ;
S- > mon_part[ S - > mon_part_len + + ] = ( char ) 0x1B ;
mon_part[ mon_part_len + + ] = p [ 1 ] ;
S- > mon_part[ S - > mon_part_len + + ] = p [ 1 ] ;
}
}
if ( mon_win & & mon_scroll = = 0 ) {
if ( mon_win & & S- > mon_scroll = = 0 ) {
wattroff ( mon_win , mon_part_cur) ;
wattroff ( mon_win , S- > mon_part_cur) ;
wattron ( mon_win , na ) ;
wattron ( mon_win , na ) ;
}
}
mon_part_cur = na ;
S- > mon_part_cur = na ;
p + = 2 ;
p + = 2 ;
} else if ( c = = ' \r ' | | c = = ' \n ' ) {
} else if ( c = = ' \r ' | | c = = ' \n ' ) {
ScrlLine * sl = & mon_sbuf[ mon_sbuf_head ] ;
ScrlLine * sl = & S- > mon_sbuf[ S - > mon_sbuf_head ] ;
memcpy ( sl - > data , mon_part, mon_part_len ) ;
memcpy ( sl - > data , S- > mon_part, S - > mon_part_len ) ;
sl - > len = mon_part_len;
sl - > len = S- > mon_part_len;
sl - > base_attr = mon_part_base;
sl - > base_attr = S- > mon_part_base;
mon_sbuf_head = ( mon_sbuf_head + 1 ) % SCRL_MAX ;
S- > mon_sbuf_head = ( S - > mon_sbuf_head + 1 ) % SCRL_MAX ;
if ( mon_sbuf_count < SCRL_MAX ) mon_sbuf_count + + ;
if ( S- > mon_sbuf_count < SCRL_MAX ) S - > mon_sbuf_count + + ;
mon_part_len = 0 ;
S- > mon_part_len = 0 ;
mon_part_base = mon_part_cur ;
S- > mon_part_base = S - > mon_part_cur ;
if ( mon_win & & mon_scroll = = 0 )
if ( mon_win & & S- > mon_scroll = = 0 )
waddch ( mon_win , ' \n ' ) ;
waddch ( mon_win , ' \n ' ) ;
p + + ;
p + + ;
} else if ( c > = 32 & & c < 127 ) {
} else if ( c > = 32 & & c < 127 ) {
if ( mon_part_len < SCRL_W - 1 )
if ( S- > mon_part_len < SCRL_W - 1 )
mon_part[ mon_part_len + + ] = ( char ) c ;
S- > mon_part[ S - > mon_part_len + + ] = ( char ) c ;
if ( mon_win & & mon_scroll = = 0 )
if ( mon_win & & S- > mon_scroll = = 0 )
waddch ( mon_win , c ) ;
waddch ( mon_win , c ) ;
p + + ;
p + + ;
} else { p + + ; }
} else { p + + ; }
}
}
if ( mon_win & & mon_scroll = = 0 ) {
if ( mon_win & & S- > mon_scroll = = 0 ) {
wattroff ( mon_win , mon_part_cur) ;
wattroff ( mon_win , S- > mon_part_cur) ;
wrefresh ( mon_win ) ;
wrefresh ( mon_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
}
}
@ -978,49 +995,49 @@ void append_out(const char *text, int len, int attr)
const char * p = text ;
const char * p = text ;
const char * end = text + len ;
const char * end = text + len ;
out_part_cur = attr ;
S- > out_part_cur = attr ;
if ( out_part_len = = 0 ) out_part_base = attr ;
if ( S- > out_part_len = = 0 ) S - > out_part_base = attr ;
if ( out_win & & out_scroll = = 0 )
if ( out_win & & S- > out_scroll = = 0 )
wattron ( out_win , out_part_cur) ;
wattron ( out_win , S- > out_part_cur) ;
while ( p < end ) {
while ( p < end ) {
unsigned char c = ( unsigned char ) * p ;
unsigned char c = ( unsigned char ) * p ;
if ( c = = 0x1B & & ( p + 1 ) < end ) {
if ( c = = 0x1B & & ( p + 1 ) < end ) {
int na = bpq_color_attr ( ( unsigned char ) p [ 1 ] ) ;
int na = bpq_color_attr ( ( unsigned char ) p [ 1 ] ) ;
if ( out_part_len + 2 < SCRL_W ) {
if ( S- > out_part_len + 2 < SCRL_W ) {
out_part[ out_part_len + + ] = ( char ) 0x1B ;
S- > out_part[ S - > out_part_len + + ] = ( char ) 0x1B ;
out_part[ out_part_len + + ] = p [ 1 ] ;
S- > out_part[ S - > out_part_len + + ] = p [ 1 ] ;
}
}
if ( out_win & & out_scroll = = 0 ) {
if ( out_win & & S- > out_scroll = = 0 ) {
wattroff ( out_win , out_part_cur) ;
wattroff ( out_win , S- > out_part_cur) ;
wattron ( out_win , na ) ;
wattron ( out_win , na ) ;
}
}
out_part_cur = na ;
S- > out_part_cur = na ;
p + = 2 ;
p + = 2 ;
} else if ( c = = ' \r ' | | c = = ' \n ' ) {
} else if ( c = = ' \r ' | | c = = ' \n ' ) {
ScrlLine * sl = & out_sbuf[ out_sbuf_head ] ;
ScrlLine * sl = & S- > out_sbuf[ S - > out_sbuf_head ] ;
memcpy ( sl - > data , out_part, out_part_len ) ;
memcpy ( sl - > data , S- > out_part, S - > out_part_len ) ;
sl - > len = out_part_len;
sl - > len = S- > out_part_len;
sl - > base_attr = out_part_base;
sl - > base_attr = S- > out_part_base;
out_sbuf_head = ( out_sbuf_head + 1 ) % SCRL_MAX ;
S- > out_sbuf_head = ( S - > out_sbuf_head + 1 ) % SCRL_MAX ;
if ( out_sbuf_count < SCRL_MAX ) out_sbuf_count + + ;
if ( S- > out_sbuf_count < SCRL_MAX ) S - > out_sbuf_count + + ;
out_part_len = 0 ;
S- > out_part_len = 0 ;
out_part_base = out_part_cur ;
S- > out_part_base = S - > out_part_cur ;
if ( out_win & & out_scroll = = 0 )
if ( out_win & & S- > out_scroll = = 0 )
waddch ( out_win , ' \n ' ) ;
waddch ( out_win , ' \n ' ) ;
p + + ;
p + + ;
} else if ( c > = 32 & & c < 127 ) {
} else if ( c > = 32 & & c < 127 ) {
if ( out_part_len < SCRL_W - 1 )
if ( S- > out_part_len < SCRL_W - 1 )
out_part[ out_part_len + + ] = ( char ) c ;
S- > out_part[ S - > out_part_len + + ] = ( char ) c ;
if ( out_win & & out_scroll = = 0 )
if ( out_win & & S- > out_scroll = = 0 )
waddch ( out_win , c ) ;
waddch ( out_win , c ) ;
p + + ;
p + + ;
} else { p + + ; }
} else { p + + ; }
}
}
if ( out_win & & out_scroll = = 0 ) {
if ( out_win & & S- > out_scroll = = 0 ) {
wattroff ( out_win , out_part_cur) ;
wattroff ( out_win , S- > out_part_cur) ;
wrefresh ( out_win ) ;
wrefresh ( out_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
}
}
@ -1040,16 +1057,16 @@ void draw_input(void)
mvwaddstr ( input_win , 0 , 0 , " > " ) ;
mvwaddstr ( input_win , 0 , 0 , " > " ) ;
/* Buffer content — wraps at COLS, '> ' prefix is 2 chars */
/* Buffer content — wraps at COLS, '> ' prefix is 2 chars */
for ( int i = 0 ; i < input_len; i + + ) {
for ( int i = 0 ; i < S- > input_len; i + + ) {
int pos = 2 + i ;
int pos = 2 + i ;
int r = pos / COLS ;
int r = pos / COLS ;
int c = pos % COLS ;
int c = pos % COLS ;
if ( r > = INPUT_OUTER ) break ;
if ( r > = INPUT_OUTER ) break ;
mvwaddch ( input_win , r , c , ( unsigned char ) input_buf[ i ] ) ;
mvwaddch ( input_win , r , c , ( unsigned char ) S- > input_buf[ i ] ) ;
}
}
/* Cursor position */
/* Cursor position */
int cpos = 2 + input_len;
int cpos = 2 + S- > input_len;
int cr = cpos / COLS ;
int cr = cpos / COLS ;
int cc = cpos % COLS ;
int cc = cpos % COLS ;
if ( cr < INPUT_OUTER ) wmove ( input_win , cr , cc ) ;
if ( cr < INPUT_OUTER ) wmove ( input_win , cr , cc ) ;
@ -1064,19 +1081,19 @@ void draw_input(void)
static void scrl_redraw_out ( void )
static void scrl_redraw_out ( void )
{
{
scrl_draw_window ( out_win , out_sbuf, out_sbuf_count , out_sbuf_head, out_scroll ) ;
scrl_draw_window ( out_win , S- > out_sbuf, S - > out_sbuf_count , S- > out_sbuf_head, S - > out_scroll ) ;
}
}
static void scrl_redraw_mon ( void )
static void scrl_redraw_mon ( void )
{
{
if ( ! mon_visible ) return ;
if ( ! mon_visible ) return ;
scrl_draw_window ( mon_win , mon_sbuf, mon_sbuf_count , mon_sbuf_head, mon_scroll ) ;
scrl_draw_window ( mon_win , S- > mon_sbuf, S - > mon_sbuf_count , S- > mon_sbuf_head, S - > mon_scroll ) ;
}
}
static void update_sep_labels ( void )
static void update_sep_labels ( void )
{
{
if ( sep_win ) {
if ( sep_win ) {
if ( out_scroll > 0 )
if ( S- > out_scroll > 0 )
fill_sep ( sep_win , " Output [scrolled - PgDn=live] " ) ;
fill_sep ( sep_win , " Output [scrolled - PgDn=live] " ) ;
else
else
fill_sep ( sep_win , " Output " ) ;
fill_sep ( sep_win , " Output " ) ;
@ -1126,7 +1143,7 @@ static void config_menu(void)
const int max_len [ ] = { 99 , 9 , 79 , 79 , 49 } ;
const int max_len [ ] = { 99 , 9 , 79 , 79 , 49 } ;
char fields [ 5 ] [ 100 ] ;
char fields [ 5 ] [ 100 ] ;
int cur_host = ( current_host > = 0 & & current_host < num_hosts ) ? current_ host : 0 ;
int cur_host = ( S- > host_idx > = 0 & & S - > host_idx < num_hosts ) ? S - > host_idx : 0 ;
int cur_field = 0 ;
int cur_field = 0 ;
if ( num_hosts = = 0 ) { memset ( & hosts [ 0 ] , 0 , sizeof ( HostEntry ) ) ; num_hosts = 1 ; }
if ( num_hosts = = 0 ) { memset ( & hosts [ 0 ] , 0 , sizeof ( HostEntry ) ) ; num_hosts = 1 ; }
@ -1492,7 +1509,7 @@ static int mon_ports_page(void)
{
{
int port_list [ 32 ] , n_ports = 0 ;
int port_list [ 32 ] , n_ports = 0 ;
for ( int b = 0 ; b < 32 ; b + + )
for ( int b = 0 ; b < 32 ; b + + )
if ( port_seen & ( 1 < < b ) ) port_list [ n_ports + + ] = b + 1 ;
if ( S- > port_seen & ( 1 < < b ) ) port_list [ n_ports + + ] = b + 1 ;
if ( n_ports = = 0 )
if ( n_ports = = 0 )
for ( int b = 0 ; b < 8 ; b + + ) port_list [ n_ports + + ] = b + 1 ;
for ( int b = 0 ; b < 8 ; b + + ) port_list [ n_ports + + ] = b + 1 ;
@ -1513,7 +1530,7 @@ static int mon_ports_page(void)
werase ( w ) ;
werase ( w ) ;
draw_box ( w ) ;
draw_box ( w ) ;
mvwprintw ( w , 0 , ( mw - 16 ) / 2 , " Port Selection " ) ;
mvwprintw ( w , 0 , ( mw - 16 ) / 2 , " Port Selection " ) ;
if ( port_seen = = 0 )
if ( S- > port_seen = = 0 )
mvwprintw ( w , 2 , 3 , " (connect to auto-detect ports) " ) ;
mvwprintw ( w , 2 , 3 , " (connect to auto-detect ports) " ) ;
for ( int i = 0 ; i < n_ports ; i + + ) {
for ( int i = 0 ; i < n_ports ; i + + ) {
int pn = port_list [ i ] ;
int pn = port_list [ i ] ;
@ -1605,14 +1622,14 @@ static void show_help(void)
/* ── Background send connection ──────────────────────────────
/* ── Background send connection ──────────────────────────────
* F2 always opens its OWN socket for sending , completely separate
* F2 always opens its OWN socket for sending , completely separate
* from the main interactive session ' s global ` sock` / ` connected ` .
* from the main interactive session ' s global ` S- > sock` / ` S - > connected ` .
* This means a send never disturbs whatever you ' re doing in the
* This means a send never disturbs whatever you ' re doing in the
* main window - no shared connection state , no risk of interleaving
* main window - no shared connection state , no risk of interleaving
* a send with whatever you ' re typing or watching scroll by .
* a send with whatever you ' re typing or watching scroll by .
*
*
* Deliberately does NOT call bpq_process ( ) : that function ' s monitor
* Deliberately does NOT call bpq_process ( ) : that function ' s monitor
* frame parser keeps state in globals ( in_mon_frame, mon_flen ,
* frame parser keeps state in globals ( S- > in_mon_frame, S - > mon_flen ,
* ports_parse_mode, etc . ) shared with the main session . Running two
* S- > ports_parse_mode, etc . ) shared with the main session . Running two
* sockets ' data through one shared parser would corrupt both . The
* sockets ' data through one shared parser would corrupt both . The
* background connection instead does a much simpler raw drain : BPQ
* background connection instead does a much simpler raw drain : BPQ
* monitor frames are bounded by 0xFF / 0xFE bytes , so they ' re just
* monitor frames are bounded by 0xFF / 0xFE bytes , so they ' re just
@ -1733,9 +1750,9 @@ static void bg_send_block(int fd, char lines[][ICS_MSG_LINELEN], int n, int *abo
}
}
/* Opens a new socket to hosts[idx], logs in if credentials are saved,
/* Opens a new socket to hosts[idx], logs in if credentials are saved,
* and drains the login banner briefly . Returns the connected fd , or
* and drains the login banner briefly . Returns the S- > connected fd , or
* - 1 on failure ( with a message already echoed to out_win ) . This is
* - 1 on failure ( with a message already echoed to out_win ) . This is
* intentionally separate from do_connect ( ) / sock/ connected - it never
* intentionally separate from do_connect ( ) / S- > sock/ S - > connected - it never
* touches the main session ' s connection state . */
* touches the main session ' s connection state . */
static int bg_connect_and_login ( int idx )
static int bg_connect_and_login ( int idx )
{
{
@ -2393,8 +2410,8 @@ static void pktnet_checkin_form(void)
memset ( & f , 0 , sizeof ( f ) ) ;
memset ( & f , 0 , sizeof ( f ) ) ;
strncpy ( f . sb_line , " SB PKTNET@USA " , ICS_SBLINE_MAX - 1 ) ;
strncpy ( f . sb_line , " SB PKTNET@USA " , ICS_SBLINE_MAX - 1 ) ;
strncpy ( f . to , " PKTNET@USA " , CHKIN_FIELD_MAX - 1 ) ;
strncpy ( f . to , " PKTNET@USA " , CHKIN_FIELD_MAX - 1 ) ;
if ( current_host > = 0 & & hosts [ current_host ] . username [ 0 ] )
if ( S- > host_idx > = 0 & & hosts [ S - > host_idx ] . username [ 0 ] )
strncpy ( f . from , hosts [ current_host ] . username , CHKIN_FIELD_MAX - 1 ) ;
strncpy ( f . from , hosts [ S- > host_idx ] . username , CHKIN_FIELD_MAX - 1 ) ;
f . type_idx = 0 ; /* EXERCISE */
f . type_idx = 0 ; /* EXERCISE */
f . band_idx = 2 ; /* VHF */
f . band_idx = 2 ; /* VHF */
f . session_idx = 1 ; /* AX25 Packet */
f . session_idx = 1 ; /* AX25 Packet */
@ -2597,22 +2614,22 @@ static void pktnet_checkin_form(void)
static void do_connect ( int idx )
static void do_connect ( int idx )
{
{
if ( idx < 0 | | idx > = num_hosts ) return ;
if ( idx < 0 | | idx > = num_hosts ) return ;
if ( connected) tcp_disconnect ( ) ;
if ( S- > connected) tcp_disconnect ( ) ;
char msg [ 160 ] ;
char msg [ 160 ] ;
snprintf ( msg , sizeof ( msg ) , " Connecting to %s:%s... \n " ,
snprintf ( msg , sizeof ( msg ) , " Connecting to %s:%s... \n " ,
hosts [ idx ] . host , hosts [ idx ] . port ) ;
hosts [ idx ] . host , hosts [ idx ] . port ) ;
append_out ( msg , ( int ) strlen ( msg ) , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( msg , ( int ) strlen ( msg ) , COLOR_PAIR ( CP_YELLOW ) ) ;
sock = tcp_connect ( hosts [ idx ] . host , hosts [ idx ] . port ) ;
S- > sock = tcp_connect ( hosts [ idx ] . host , hosts [ idx ] . port ) ;
if ( sock < 0 ) {
if ( S- > sock < 0 ) {
append_out ( " Connection failed. \n " , 19 , COLOR_PAIR ( CP_RED ) ) ;
append_out ( " Connection failed. \n " , 19 , COLOR_PAIR ( CP_RED ) ) ;
return ;
return ;
}
}
connected = 1 ;
S- > connected = 1 ;
current_host = idx ;
S- > host_idx = idx ;
connect_time = time ( NULL ) ;
S- > connect_time = time ( NULL ) ;
snprintf ( msg , sizeof ( msg ) , " Connected to %s \n " , hosts [ idx ] . name ) ;
snprintf ( msg , sizeof ( msg ) , " Connected to %s \n " , hosts [ idx ] . name ) ;
append_out ( msg , ( int ) strlen ( msg ) , COLOR_PAIR ( CP_GREEN ) | A_BOLD ) ;
append_out ( msg , ( int ) strlen ( msg ) , COLOR_PAIR ( CP_GREEN ) | A_BOLD ) ;
@ -2636,7 +2653,7 @@ static void do_connect(int idx)
/* Drain incoming data for up to 2 seconds waiting for PORTS response.
/* Drain incoming data for up to 2 seconds waiting for PORTS response.
* A single recv ( ) often gets only the login banner ; loop until we have
* A single recv ( ) often gets only the login banner ; loop until we have
* the full port list ( ports_loaded) or the deadline passes . */
* the full port list ( S- > ports_loaded) or the deadline passes . */
{
{
char rbuf [ 4096 ] ;
char rbuf [ 4096 ] ;
struct timeval t0 , now ;
struct timeval t0 , now ;
@ -2646,13 +2663,13 @@ static void do_connect(int idx)
long elapsed = ( now . tv_sec - t0 . tv_sec ) * 1000000L
long elapsed = ( now . tv_sec - t0 . tv_sec ) * 1000000L
+ ( now . tv_usec - t0 . tv_usec ) ;
+ ( now . tv_usec - t0 . tv_usec ) ;
if ( elapsed > = 2000000L ) break ;
if ( elapsed > = 2000000L ) break ;
if ( ports_loaded) break ;
if ( S- > ports_loaded) break ;
long remain = 2000000L - elapsed ;
long remain = 2000000L - elapsed ;
fd_set rfds ;
fd_set rfds ;
struct timeval tv = { remain / 1000000L , remain % 1000000L } ;
struct timeval tv = { remain / 1000000L , remain % 1000000L } ;
FD_ZERO ( & rfds ) ; FD_SET ( sock, & rfds ) ;
FD_ZERO ( & rfds ) ; FD_SET ( S- > sock, & rfds ) ;
if ( select ( sock + 1 , & rfds , NULL , NULL , & tv ) < = 0 ) break ;
if ( select ( S- > sock + 1 , & rfds , NULL , NULL , & tv ) < = 0 ) break ;
int n = recv ( sock, rbuf , sizeof ( rbuf ) - 1 , 0 ) ;
int n = recv ( S- > sock, rbuf , sizeof ( rbuf ) - 1 , 0 ) ;
if ( n < = 0 ) break ;
if ( n < = 0 ) break ;
bpq_process ( rbuf , n ) ;
bpq_process ( rbuf , n ) ;
}
}
@ -2665,14 +2682,14 @@ static void do_connect(int idx)
* " port shows enabled here but no traffic flows until re-toggled " ) .
* " port shows enabled here but no traffic flows until re-toggled " ) .
* Re - sending once the initial drain has settled costs one extra
* Re - sending once the initial drain has settled costs one extra
* trace command and catches that race without needing to detect it . */
* trace command and catches that race without needing to detect it . */
if ( connected & & sock > = 0 ) {
if ( S- > connected & & S - > sock > = 0 ) {
send_trace_options_with_ports ( ) ;
send_trace_options_with_ports ( ) ;
char rbuf [ 4096 ] ;
char rbuf [ 4096 ] ;
struct timeval tv2 = { 1 , 0 } ;
struct timeval tv2 = { 1 , 0 } ;
fd_set rfds2 ;
fd_set rfds2 ;
FD_ZERO ( & rfds2 ) ; FD_SET ( sock, & rfds2 ) ;
FD_ZERO ( & rfds2 ) ; FD_SET ( S- > sock, & rfds2 ) ;
if ( select ( sock + 1 , & rfds2 , NULL , NULL , & tv2 ) > 0 ) {
if ( select ( S- > sock + 1 , & rfds2 , NULL , NULL , & tv2 ) > 0 ) {
int n = recv ( sock, rbuf , sizeof ( rbuf ) - 1 , 0 ) ;
int n = recv ( S- > sock, rbuf , sizeof ( rbuf ) - 1 , 0 ) ;
if ( n > 0 ) bpq_process ( rbuf , n ) ;
if ( n > 0 ) bpq_process ( rbuf , n ) ;
}
}
}
}
@ -2753,7 +2770,7 @@ static void ctrl_a_menu(void)
break ;
break ;
case ' d ' :
case ' d ' :
if ( connected) {
if ( S- > connected) {
tcp_disconnect ( ) ;
tcp_disconnect ( ) ;
append_out ( " *** Disconnected \n " , 17 , COLOR_PAIR ( CP_RED ) ) ;
append_out ( " *** Disconnected \n " , 17 , COLOR_PAIR ( CP_RED ) ) ;
}
}
@ -2768,17 +2785,17 @@ static void ctrl_a_menu(void)
break ;
break ;
case ' l ' :
case ' l ' :
if ( logging) {
if ( S- > logging) {
close_log ( ) ;
close_log ( ) ;
append_out ( " [Logging OFF] \n " , 14 , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( " [Logging OFF] \n " , 14 , COLOR_PAIR ( CP_YELLOW ) ) ;
} else if ( connected & & current_host > = 0 ) {
} else if ( S- > connected & & S - > host_idx > = 0 ) {
char prefix [ 120 ] ;
char prefix [ 120 ] ;
snprintf ( prefix , sizeof ( prefix ) , " %s_%s " ,
snprintf ( prefix , sizeof ( prefix ) , " %s_%s " ,
hosts [ current_host] . host , hosts [ current_host ] . port ) ;
hosts [ S- > host_idx ] . host , hosts [ S - > host_idx ] . port ) ;
open_log ( prefix ) ;
open_log ( prefix ) ;
append_out ( " [Logging ON] \n " , 13 , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( " [Logging ON] \n " , 13 , COLOR_PAIR ( CP_YELLOW ) ) ;
} else {
} else {
append_out ( " Connect first to start logging.\n " , 32 , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( " Connect first to start S-> logging.\n " , 32 , COLOR_PAIR ( CP_YELLOW ) ) ;
}
}
draw_status ( ) ;
draw_status ( ) ;
break ;
break ;
@ -2792,8 +2809,8 @@ static void ctrl_a_menu(void)
break ;
break ;
case ' q ' :
case ' q ' :
if ( connected) tcp_disconnect ( ) ;
if ( S- > connected) tcp_disconnect ( ) ;
if ( logging) close_log ( ) ;
if ( S- > logging) close_log ( ) ;
endwin ( ) ;
endwin ( ) ;
exit ( 0 ) ;
exit ( 0 ) ;
}
}
@ -2823,7 +2840,7 @@ static void handle_key(int ch)
}
}
case KEY_F ( 4 ) :
case KEY_F ( 4 ) :
if ( connected) {
if ( S- > connected) {
tcp_disconnect ( ) ;
tcp_disconnect ( ) ;
append_out ( " *** Disconnected \n " , 17 , COLOR_PAIR ( CP_RED ) ) ;
append_out ( " *** Disconnected \n " , 17 , COLOR_PAIR ( CP_RED ) ) ;
draw_status ( ) ;
draw_status ( ) ;
@ -2835,24 +2852,24 @@ static void handle_key(int ch)
return ;
return ;
case KEY_F ( 6 ) :
case KEY_F ( 6 ) :
if ( logging) {
if ( S- > logging) {
close_log ( ) ;
close_log ( ) ;
append_out ( " [Logging OFF] \n " , 14 , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( " [Logging OFF] \n " , 14 , COLOR_PAIR ( CP_YELLOW ) ) ;
} else if ( connected & & current_host > = 0 ) {
} else if ( S- > connected & & S - > host_idx > = 0 ) {
char prefix [ 120 ] ;
char prefix [ 120 ] ;
snprintf ( prefix , sizeof ( prefix ) , " %s_%s " ,
snprintf ( prefix , sizeof ( prefix ) , " %s_%s " ,
hosts [ current_host] . host , hosts [ current_host ] . port ) ;
hosts [ S- > host_idx ] . host , hosts [ S - > host_idx ] . port ) ;
open_log ( prefix ) ;
open_log ( prefix ) ;
append_out ( " [Logging ON] \n " , 13 , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( " [Logging ON] \n " , 13 , COLOR_PAIR ( CP_YELLOW ) ) ;
} else {
} else {
append_out ( " Connect first to start logging.\n " , 32 , COLOR_PAIR ( CP_YELLOW ) ) ;
append_out ( " Connect first to start S-> logging.\n " , 32 , COLOR_PAIR ( CP_YELLOW ) ) ;
}
}
draw_status ( ) ;
draw_status ( ) ;
return ;
return ;
case KEY_F ( 10 ) :
case KEY_F ( 10 ) :
if ( connected) tcp_disconnect ( ) ;
if ( S- > connected) tcp_disconnect ( ) ;
if ( logging) close_log ( ) ;
if ( S- > logging) close_log ( ) ;
endwin ( ) ;
endwin ( ) ;
exit ( 0 ) ;
exit ( 0 ) ;
@ -2865,9 +2882,9 @@ static void handle_key(int ch)
case KEY_PPAGE : /* PgUp — scroll output back */
case KEY_PPAGE : /* PgUp — scroll output back */
if ( out_win ) {
if ( out_win ) {
int page = getmaxy ( out_win ) ;
int page = getmaxy ( out_win ) ;
out_scroll + = page ;
S- > out_scroll + = page ;
int max_scroll = out_sbuf_count > 0 ? out_sbuf_count - 1 : 0 ;
int max_scroll = S- > out_sbuf_count > 0 ? S - > out_sbuf_count - 1 : 0 ;
if ( out_scroll > max_scroll ) out_scroll = max_scroll ;
if ( S- > out_scroll > max_scroll ) S - > out_scroll = max_scroll ;
scrl_redraw_out ( ) ;
scrl_redraw_out ( ) ;
update_sep_labels ( ) ;
update_sep_labels ( ) ;
if ( input_win ) wrefresh ( input_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
@ -2875,10 +2892,10 @@ static void handle_key(int ch)
return ;
return ;
case KEY_NPAGE : /* PgDn — scroll output forward / return to live */
case KEY_NPAGE : /* PgDn — scroll output forward / return to live */
if ( out_win & & out_scroll > 0 ) {
if ( out_win & & S- > out_scroll > 0 ) {
int page = getmaxy ( out_win ) ;
int page = getmaxy ( out_win ) ;
out_scroll - = page ;
S- > out_scroll - = page ;
if ( out_scroll < 0 ) out_scroll = 0 ;
if ( S- > out_scroll < 0 ) S - > out_scroll = 0 ;
scrl_redraw_out ( ) ;
scrl_redraw_out ( ) ;
update_sep_labels ( ) ;
update_sep_labels ( ) ;
if ( input_win ) wrefresh ( input_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
@ -2888,83 +2905,83 @@ static void handle_key(int ch)
case KEY_HOME : /* Home — scroll monitor back */
case KEY_HOME : /* Home — scroll monitor back */
if ( mon_win & & mon_visible ) {
if ( mon_win & & mon_visible ) {
int page = getmaxy ( mon_win ) ;
int page = getmaxy ( mon_win ) ;
mon_scroll + = page ;
S- > mon_scroll + = page ;
int max_scroll = mon_sbuf_count > 0 ? mon_sbuf_count - 1 : 0 ;
int max_scroll = S- > mon_sbuf_count > 0 ? S - > mon_sbuf_count - 1 : 0 ;
if ( mon_scroll > max_scroll ) mon_scroll = max_scroll ;
if ( S- > mon_scroll > max_scroll ) S - > mon_scroll = max_scroll ;
scrl_redraw_mon ( ) ;
scrl_redraw_mon ( ) ;
if ( input_win ) wrefresh ( input_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
}
}
return ;
return ;
case KEY_END : /* End — scroll monitor forward / return to live */
case KEY_END : /* End — scroll monitor forward / return to live */
if ( mon_win & & mon_visible & & mon_scroll > 0 ) {
if ( mon_win & & mon_visible & & S- > mon_scroll > 0 ) {
int page = getmaxy ( mon_win ) ;
int page = getmaxy ( mon_win ) ;
mon_scroll - = page ;
S- > mon_scroll - = page ;
if ( mon_scroll < 0 ) mon_scroll = 0 ;
if ( S- > mon_scroll < 0 ) S - > mon_scroll = 0 ;
scrl_redraw_mon ( ) ;
scrl_redraw_mon ( ) ;
if ( input_win ) wrefresh ( input_win ) ;
if ( input_win ) wrefresh ( input_win ) ;
}
}
return ;
return ;
case ' \r ' : case ' \n ' : case KEY_ENTER :
case ' \r ' : case ' \n ' : case KEY_ENTER :
if ( input_len = = 0 ) return ;
if ( S- > input_len = = 0 ) return ;
input_buf[ input_len ] = ' \0 ' ;
S- > input_buf[ S - > input_len ] = ' \0 ' ;
/* Save to history */
/* Save to history */
strncpy ( hist_buf[ hist_head] , input_buf , INPUT_MAX - 1 ) ;
strncpy ( S- > hist_buf[ S- > hist_head] , S - > input_buf , INPUT_MAX - 1 ) ;
hist_buf[ hist_head ] [ INPUT_MAX - 1 ] = ' \0 ' ;
S- > hist_buf[ S - > hist_head ] [ INPUT_MAX - 1 ] = ' \0 ' ;
hist_head = ( hist_head + 1 ) % HIST_MAX ;
S- > hist_head = ( S - > hist_head + 1 ) % HIST_MAX ;
if ( hist_count < HIST_MAX ) hist_count + + ;
if ( S- > hist_count < HIST_MAX ) S - > hist_count + + ;
hist_pos = - 1 ;
S- > hist_pos = - 1 ;
if ( connected) {
if ( S- > connected) {
tcp_send ( input_buf, input_len ) ;
tcp_send ( S- > input_buf, S - > input_len ) ;
tcp_send ( " \r " , 1 ) ;
tcp_send ( " \r " , 1 ) ;
char echo [ INPUT_MAX + 4 ] ;
char echo [ INPUT_MAX + 4 ] ;
int elen = snprintf ( echo , sizeof ( echo ) , " > %s \n " , input_buf) ;
int elen = snprintf ( echo , sizeof ( echo ) , " > %s \n " , S- > input_buf) ;
append_out ( echo , elen , COLOR_PAIR ( CP_CYAN ) | A_BOLD ) ;
append_out ( echo , elen , COLOR_PAIR ( CP_CYAN ) | A_BOLD ) ;
} else {
} else {
append_out ( " Not connected -- Ctrl+A to open menu.\n " , 38 ,
append_out ( " Not S-> connected -- Ctrl+A to open menu.\n " , 38 ,
COLOR_PAIR ( CP_YELLOW ) ) ;
COLOR_PAIR ( CP_YELLOW ) ) ;
}
}
input_len = 0 ;
S- > input_len = 0 ;
break ;
break ;
case KEY_UP : /* history: go back */
case KEY_UP : /* history: go back */
if ( hist_count = = 0 ) break ;
if ( S- > hist_count = = 0 ) break ;
if ( hist_pos = = - 1 ) {
if ( S- > hist_pos = = - 1 ) {
strncpy ( hist_save, input_buf , INPUT_MAX - 1 ) ;
strncpy ( S- > hist_save, S - > input_buf , INPUT_MAX - 1 ) ;
hist_save[ INPUT_MAX - 1 ] = ' \0 ' ;
S- > hist_save[ INPUT_MAX - 1 ] = ' \0 ' ;
hist_pos = 0 ;
S- > hist_pos = 0 ;
} else if ( hist_pos < hist_count - 1 ) {
} else if ( S- > hist_pos < S - > hist_count - 1 ) {
hist_pos+ + ;
S- > hist_pos+ + ;
}
}
{
{
int idx = ( hist_head - 1 - hist_pos + HIST_MAX * 2 ) % HIST_MAX ;
int idx = ( S- > hist_head - 1 - S - > hist_pos + HIST_MAX * 2 ) % HIST_MAX ;
strncpy ( input_buf, hist_buf [ idx ] , INPUT_MAX - 1 ) ;
strncpy ( S- > input_buf, S - > hist_buf [ idx ] , INPUT_MAX - 1 ) ;
input_buf[ INPUT_MAX - 1 ] = ' \0 ' ;
S- > input_buf[ INPUT_MAX - 1 ] = ' \0 ' ;
input_len = ( int ) strlen ( input_buf ) ;
S- > input_len = ( int ) strlen ( S - > input_buf ) ;
}
}
break ;
break ;
case KEY_DOWN : /* history: go forward */
case KEY_DOWN : /* history: go forward */
if ( hist_pos < 0 ) break ;
if ( S- > hist_pos < 0 ) break ;
hist_pos- - ;
S- > hist_pos- - ;
if ( hist_pos < 0 ) {
if ( S- > hist_pos < 0 ) {
input_buf[ 0 ] = ' \0 ' ; /* always end with blank line */
S- > input_buf[ 0 ] = ' \0 ' ; /* always end with blank line */
} else {
} else {
int idx = ( hist_head - 1 - hist_pos + HIST_MAX * 2 ) % HIST_MAX ;
int idx = ( S- > hist_head - 1 - S - > hist_pos + HIST_MAX * 2 ) % HIST_MAX ;
strncpy ( input_buf, hist_buf [ idx ] , INPUT_MAX - 1 ) ;
strncpy ( S- > input_buf, S - > hist_buf [ idx ] , INPUT_MAX - 1 ) ;
input_buf[ INPUT_MAX - 1 ] = ' \0 ' ;
S- > input_buf[ INPUT_MAX - 1 ] = ' \0 ' ;
}
}
input_len = ( int ) strlen ( input_buf ) ;
S- > input_len = ( int ) strlen ( S - > input_buf ) ;
break ;
break ;
case KEY_BACKSPACE : case 127 : case ' \b ' :
case KEY_BACKSPACE : case 127 : case ' \b ' :
if ( input_len > 0 ) input_len - - ;
if ( S- > input_len > 0 ) S - > input_len - - ;
break ;
break ;
default :
default :
if ( ch > = 32 & & ch < 256 & & input_len < INPUT_MAX - 1 )
if ( ch > = 32 & & ch < 256 & & S- > input_len < INPUT_MAX - 1 )
input_buf[ input_len + + ] = ( char ) ch ;
S- > input_buf[ S - > input_len + + ] = ( char ) ch ;
break ;
break ;
}
}
@ -2988,7 +3005,7 @@ static int confirm_quit_popup(void)
draw_box ( w ) ;
draw_box ( w ) ;
mvwprintw ( w , 0 , ( mw - 8 ) / 2 , " Quit? " ) ;
mvwprintw ( w , 0 , ( mw - 8 ) / 2 , " Quit? " ) ;
mvwprintw ( w , 2 , 3 , " Ctrl+C pressed. " ) ;
mvwprintw ( w , 2 , 3 , " Ctrl+C pressed. " ) ;
if ( connected) mvwprintw ( w , 3 , 3 , " This will close the current session. " ) ;
if ( S- > connected) mvwprintw ( w , 3 , 3 , " This will close the current session. " ) ;
mvwprintw ( w , mh - 2 , 3 , " Y = quit N / Esc = resume " ) ;
mvwprintw ( w , mh - 2 , 3 , " Y = quit N / Esc = resume " ) ;
wrefresh ( w ) ;
wrefresh ( w ) ;
@ -3013,8 +3030,8 @@ void interactive_loop(void)
if ( sigint_pending ) {
if ( sigint_pending ) {
sigint_pending = 0 ;
sigint_pending = 0 ;
if ( confirm_quit_popup ( ) ) {
if ( confirm_quit_popup ( ) ) {
if ( connected) tcp_disconnect ( ) ;
if ( S- > connected) tcp_disconnect ( ) ;
if ( logging) close_log ( ) ;
if ( S- > logging) close_log ( ) ;
endwin ( ) ;
endwin ( ) ;
exit ( 0 ) ;
exit ( 0 ) ;
}
}
@ -3025,17 +3042,17 @@ void interactive_loop(void)
FD_ZERO ( & readfds ) ;
FD_ZERO ( & readfds ) ;
FD_SET ( STDIN_FILENO , & readfds ) ;
FD_SET ( STDIN_FILENO , & readfds ) ;
if ( connected & & sock > = 0 ) FD_SET ( sock , & readfds ) ;
if ( S- > connected & & S- > sock > = 0 ) FD_SET ( S - > sock , & readfds ) ;
tv . tv_sec = 1 ;
tv . tv_sec = 1 ;
tv . tv_usec = 0 ;
tv . tv_usec = 0 ;
int maxfd = ( connected & & sock > STDIN_FILENO ) ? sock : STDIN_FILENO ;
int maxfd = ( S- > connected & & S- > sock > STDIN_FILENO ) ? S - > sock : STDIN_FILENO ;
int ready = select ( maxfd + 1 , & readfds , NULL , NULL , & tv ) ;
int ready = select ( maxfd + 1 , & readfds , NULL , NULL , & tv ) ;
if ( ready < 0 ) continue ;
if ( ready < 0 ) continue ;
/* Network data */
/* Network data */
if ( connected & & sock > = 0 & & FD_ISSET ( sock , & readfds ) ) {
if ( S- > connected & & S- > sock > = 0 & & FD_ISSET ( S - > sock , & readfds ) ) {
int n = recv ( sock, recv_buf , sizeof ( recv_buf ) - 1 , 0 ) ;
int n = recv ( S- > sock, recv_buf , sizeof ( recv_buf ) - 1 , 0 ) ;
if ( n > 0 ) {
if ( n > 0 ) {
bpq_process ( recv_buf , n ) ;
bpq_process ( recv_buf , n ) ;
draw_input ( ) ;
draw_input ( ) ;
@ -3073,6 +3090,15 @@ int main(int argc, char *argv[])
}
}
}
}
/* Initialise session 0 — the one and only session in CLI mode */
memset ( sessions , 0 , sizeof ( sessions ) ) ;
sessions [ 0 ] . active = 1 ;
sessions [ 0 ] . sock = - 1 ;
sessions [ 0 ] . host_idx = - 1 ;
sessions [ 0 ] . hist_pos = - 1 ;
snprintf ( sessions [ 0 ] . label , SESSION_LABEL , " Session 1 " ) ;
S = & sessions [ 0 ] ;
/* Set default port names before load_config so INI PortName keys override them */
/* Set default port names before load_config so INI PortName keys override them */
for ( int i = 0 ; i < 32 ; i + + )
for ( int i = 0 ; i < 32 ; i + + )
snprintf ( port_names [ i ] , 32 , " Port %d " , i + 1 ) ;
snprintf ( port_names [ i ] , 32 , " Port %d " , i + 1 ) ;
@ -3122,8 +3148,8 @@ int main(int argc, char *argv[])
interactive_loop ( ) ;
interactive_loop ( ) ;
if ( connected) tcp_disconnect ( ) ;
if ( S- > connected) tcp_disconnect ( ) ;
if ( logging) close_log ( ) ;
if ( S- > logging) close_log ( ) ;
endwin ( ) ;
endwin ( ) ;
return 0 ;
return 0 ;
}
}