Newer
Older
/* Synchronet single-key console functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
/****************************************************************************/
/* Waits for remote or local user to hit a key. Inactivity timer is checked */
/* and hangs up if inactive for 4 minutes. Returns key hit, or uppercase of */
/* key hit if mode&K_UPPER or key out of KEY BUFFER. Does not print key. */
/* Called from functions all over the place. */
/****************************************************************************/
time_t last_telnet_cmd=0;
char* cursor = text[SpinningCursor0 + sbbs_random(10)];
size_t cursors = strlen(cursor);
if(online==ON_REMOTE && !input_thread_running)
online=FALSE;
if(!online) {
YIELD(); // just in case someone is looping on getkey() when they shouldn't
}
if((sys_status&SS_USERON || action==NODE_DFLT) && !(mode&(K_GETSTR|K_NOSPIN)))
mode|=(useron.misc&SPIN);
lncntr=0;
getkey_last_activity=time(NULL);
#if !defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
if(mode&K_SPIN)
outchar(' ');
do {
if(sys_status&SS_ABORT) {
if(mode&K_SPIN) {
#if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
bputs(" \b");
#else
if(mode&K_SPIN) {
#if !defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
outchar('\b');
outchar(cursor[(c++) % cursors]);
#if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
outchar('\b');
#endif
ch=inkey(mode,mode&K_SPIN ? 200:1000);
if(sys_status&SS_ABORT)
return(0);
now=time(NULL);
if(ch) {
if(mode&K_NUMBER && IS_PRINTABLE(ch) && !IS_DIGIT(ch))
if(mode&K_ALPHA && IS_PRINTABLE(ch) && !IS_ALPHA(ch))
continue;
if(mode&K_NOEXASC && ch&0x80)
continue;
if(mode&K_SPIN) {
#if defined SPINNING_CURSOR_OVER_HARDWARE_CURSOR
bputs(" \b");
#else
if(mode&K_COLD && ch>' ' && useron.misc&COLDKEYS) {
if(mode&K_UPPER)
outchar(toupper(ch));
else
outchar(ch);
while((coldkey=inkey(mode,1000))==0 && online && !(sys_status&SS_ABORT))
;
if(coldkey==BS || coldkey==DEL)
if(mode&K_UPPER)
return(toupper(ch));
if(sys_status&SS_USERON && !(sys_status&SS_LCHAT)) gettimeleft();
else if(online && now-answertime>SEC_LOGON && !(sys_status&SS_LCHAT)) {
console&=~(CON_R_ECHOX|CON_L_ECHOX);
console|=(CON_R_ECHO|CON_L_ECHO);
bputs(text[TakenTooLongToLogon]);
if(sys_status&SS_USERON && online && (timeleft/60)<(5-timeleft_warn)
&& !SYSOP && !(sys_status&SS_LCHAT)) {
timeleft_warn=5-(timeleft/60);
attr(LIGHTGRAY);
bprintf(text[OnlyXminutesLeft]
,((ushort)timeleft/60)+1,(timeleft/60) ? "s" : nulstr);
restoreline();
if(!(startup->options&BBS_OPT_NO_TELNET_GA)
&& now!=last_telnet_cmd && now-getkey_last_activity>=60 && !((now-getkey_last_activity)%60)) {
// Let's make sure the socket is up
// Sending will trigger a socket d/c detection
send_telnet_cmd(TELNET_GA,0);
last_telnet_cmd=now;
}
time_t inactive = now - getkey_last_activity;
if(online==ON_REMOTE && !(console&CON_NO_INACT)
&& ((cfg.inactivity_warn && inactive >= cfg.max_getkey_inactivity * (cfg.inactivity_warn / 100.0))
|| inactive >= cfg.max_getkey_inactivity)) {
if(sys_status&SS_USERON && inactive < cfg.max_getkey_inactivity) {

rswindell
committed
bputs(text[AreYouThere]);
}
bputs(text[InactivityAlert]);
while(!inkey(K_NONE,100) && online && now-getkey_last_activity < cfg.max_getkey_inactivity) {
}
if(now-getkey_last_activity >= cfg.max_getkey_inactivity) {
if(online==ON_REMOTE) {
console|=CON_R_ECHO;
console&=~CON_R_ECHOX;

rswindell
committed
}
bputs(text[CallBackWhenYoureThere]);
logline(LOG_NOTICE,nulstr,"Maximum user input inactivity exceeded");
hangup();
return(0);

rswindell
committed
}
if(sys_status&SS_USERON) {
attr(LIGHTGRAY);
carriage_return();
cleartoeol();
restoreline();

rswindell
committed
}
getkey_last_activity=now;

rswindell
committed
}
return(0);
}
/****************************************************************************/
/* Outputs a string highlighting characters preceded by a tilde */
/****************************************************************************/

Rob Swindell
committed
void sbbs_t::mnemonics(const char *instr)

Rob Swindell
committed
if(!strchr(instr,'~')) {
mnestr= instr;
bputs(instr);

Rob Swindell
committed
ctrl_a_codes=strchr(instr,1);

Rob Swindell
committed
const char* last = lastchar(instr);
if(instr[0] == '@' && *last == '@' && strchr(instr + 1, '@') == last && strchr(instr, ' ') == NULL) {
mnestr= instr;
bputs(instr);
return;
}
attr(cfg.color[clr_mnelow]);
}

Rob Swindell
committed
char str[256];
expand_atcodes(instr, str, sizeof str);
if(str[l]=='~' && str[l+1] < ' ') {
add_hotspot('\r', /* hungry: */true);
l+=2;
}
else if(str[l]=='~') {
outchar('(');
l++;
if(!ctrl_a_codes)
attr(cfg.color[clr_mnehigh]);

rswindell
committed
add_hotspot(str[l], /* hungry: */true);
outchar(')');
if(!ctrl_a_codes)
attr(cfg.color[clr_mnelow]);
}

rswindell
committed
else if(str[l]=='`' && str[l+1]!=0) {
if(!(term&(ANSI|PETSCII)))
outchar('[');
l++;
if(!ctrl_a_codes)
attr(cfg.color[clr_mnehigh]);

rswindell
committed
add_hotspot(str[l], /* hungry: */false);

rswindell
committed
outchar(str[l]);
l++;
if(!(term&(ANSI|PETSCII)))
outchar(']');
if(!ctrl_a_codes)
attr(cfg.color[clr_mnelow]);
}
if(str[l]==CTRL_A && str[l+1]!=0) {
l++;
if(str[l] == 'Z') /* EOF (uppercase 'Z') */
break;
} else {
outchar(str[l++]);
}
if(!ctrl_a_codes)
attr(cfg.color[clr_mnecmd]);
}
/****************************************************************************/
/* Prompts user for Y or N (yes or no) and CR is interpreted as a Y */
/* Called from quite a few places */
/****************************************************************************/
bool sbbs_t::yesno(const char *str, int mode)
bprintf(mode, text[YesNoQuestion], str);
while(online) {
if(sys_status&SS_ABORT)
ch=no_key();
else
ch=getkey(K_UPPER|K_COLD);
if(ch==yes_key() || ch==CR) {
if(bputs(text[Yes], mode) && !(mode&P_NOCRLF))
if(!(mode&P_SAVEATR))
attr(LIGHTGRAY);
if(ch==no_key()) {
if(bputs(text[No], mode) && !(mode&P_NOCRLF))
if(!(mode&P_SAVEATR))
attr(LIGHTGRAY);
return(false);
}
}
return(true);
}
/****************************************************************************/
/* Prompts user for N or Y (no or yes) and CR is interpreted as a N */
/****************************************************************************/
bool sbbs_t::noyes(const char *str, int mode)
return true;
bprintf(mode, text[NoYesQuestion], str);
while(online) {
if(sys_status&SS_ABORT)
ch=no_key();
else
ch=getkey(K_UPPER|K_COLD);
if(ch==no_key() || ch==CR) {
if(bputs(text[No], mode) && !(mode&P_NOCRLF))
if(!(mode&P_SAVEATR))
attr(LIGHTGRAY);
if(ch==yes_key()) {
if(bputs(text[Yes], mode) && !(mode&P_NOCRLF))
if(!(mode&P_SAVEATR))
attr(LIGHTGRAY);
return(false);
}
}
return(true);
}
/****************************************************************************/
/* Waits for remote or local user to hit a key among 'keys'. */
/* If 'keys' is NULL, *any* non-numeric key is valid input. */
/* 'max' is non-zero, allow that a decimal number input up to that size */
/* and return the value OR'd with 0x80000000. */
/* default mode value is K_UPPER */
/****************************************************************************/
int sbbs_t::getkeys(const char *keys, uint max, int mode)
uchar ch,n=0,c=0;
if(keys != NULL) {
SAFECOPY(str,keys);
}
ch=getkey(mode);
if(max && ch>0x7f) /* extended ascii chars are digits to isdigit() */
continue;
if(sys_status&SS_ABORT) { /* return -1 if Ctrl-C hit */
if(!(mode&(K_NOECHO|K_NOCRLF))) {
attr(LIGHTGRAY);
CRLF;
}
if(ch && !n && ((keys == NULL && !IS_DIGIT(ch)) || (strchr(str,ch)))) { /* return character if in string */
if(ch > ' ') {
if(!(mode&K_NOECHO))
outchar(ch);
if(useron.misc&COLDKEYS) {
while(online && !(sys_status&SS_ABORT)) {
c=getkey(0);
if(c==CR || c==BS || c==DEL)
break;
}
if(sys_status&SS_ABORT) {
if(!(mode&(K_NOECHO|K_NOCRLF))) {
CRLF;
}
return(-1);
}
if(c==BS || c==DEL) {
if(!(mode&K_NOECHO))
backspace();
continue;
}
if(!(mode&(K_NOECHO|K_NOCRLF))) {
attr(LIGHTGRAY);
CRLF;
}
lncntr=0;
if(ch==CR && max) { /* return 0 if no number */
if(!(mode&(K_NOECHO|K_NOCRLF))) {
attr(LIGHTGRAY);
CRLF;
}
lncntr=0;
if(n)
return(i|0x80000000L); /* return number plus high bit */
return(0);
}
if((ch==BS || ch==DEL) && n) {
if(!(mode&K_NOECHO))
backspace();
else if(max && IS_DIGIT(ch) && (i*10)+(ch&0xf)<=max && (ch!='0' || n)) {
i*=10;
n++;
i+=ch&0xf;
if(!(mode&K_NOECHO))
outchar(ch);
if(i*10>max && !(useron.misc&COLDKEYS) && keybuf_level() < 1) {
if(!(mode&(K_NOECHO|K_NOCRLF))) {
attr(LIGHTGRAY);
CRLF;
}
return(i|0x80000000L);
}
}
}
return(-1);
}
/****************************************************************************/
/* Prints PAUSE message and waits for a key stoke */
/* Returns false if aborted by user */
/****************************************************************************/
bool sbbs_t::pause(bool set_abort)
uint tempattrs=curatr; /* was lclatr(-1) */
size_t len;
lncntr=0;
if(online==ON_REMOTE)
rioctl(IOFI);
pause_hotspot = add_hotspot('\r');
len = bstrlen(text[Pause]);
if(sys_status&SS_USERON && !(useron.misc&(HTML|WIP|NOPAUSESPIN))
&& cfg.spinning_pause_prompt)
if(pause_hotspot) {
pause_hotspot = NULL;
}
bool aborted = (ch==no_key() || ch==quit_key() || (sys_status & SS_ABORT));
if(set_abort && aborted)
sys_status|=SS_ABORT;
else if(ch==LF) // down arrow == display one more line
lncntr=rows-2;
if(text[Pause][0]!='@')
backspace(len);
getnodedat(cfg.node_num,&thisnode,0);
nodesync();
attr(tempattrs);
}
/****************************************************************************/
/* Puts a character into the input buffer */
/****************************************************************************/

rswindell
committed
void sbbs_t::ungetkey(char ch, bool insert)
#if 0 /* this way breaks ansi_getxy() */
RingBufWrite(&inbuf,(uchar*)&ch,sizeof(uchar));
#else
char* p = c_escape_char(ch);
if(p == NULL) {
p = dbg;
}
lprintf(LOG_DEBUG, "%s key into keybuf: %02X (%s)", insert ? "insert" : "append", ch, p);
if(insert) {
if(keybufbot == 0)
keybufbot = KEY_BUFSIZE - 1;
else
keybufbot--;
keybuf[keybufbot] = ch;
} else {
keybuf[keybuftop++]=ch;
if(keybuftop==KEY_BUFSIZE)
keybuftop=0;
}
} else
lprintf(LOG_WARNING, "No space in keyboard input buffer");
#endif
/****************************************************************************/
/* Puts a string into the input buffer */
/****************************************************************************/
void sbbs_t::ungetstr(const char* str, bool insert)
{
size_t i;
for(i = 0; str[i] != '\0'; i++)
ungetkey(str[i], insert);
}
size_t sbbs_t::keybuf_space(void)
{
return sizeof(keybuf) - (keybuf_level() + 1);
}
size_t sbbs_t::keybuf_level(void)
{
if(keybufbot > keybuftop)
return (sizeof(keybuf) - keybufbot) + keybuftop;
return keybuftop - keybufbot;
}