getstr.cpp 13.86 KiB
/* getstr.cpp */
/* Synchronet string input routines */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2000 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 *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* 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 input a CR terminated string. 'length' */
/* is the maximum number of characters that getstr will allow the user to */
/* input into the string. 'mode' specifies upper case characters are echoed */
/* or wordwrap or if in message input (^A sequences allowed). ^W backspaces */
/* a word, ^X backspaces a line, ^Gs, BSs, TABs are processed, LFs ignored. */
/* ^N non-destructive BS, ^V center line. Valid keys are echoed. */
/****************************************************************************/
size_t sbbs_t::getstr(char *strout, size_t maxlen, long mode)
{
size_t i,l,x,z; /* i=current position, l=length, j=printed chars */
/* x&z=misc */
char str1[256],str2[256];
uchar ch;
uchar ins=0,atr;
console&=~CON_UPARROW;
sys_status&=~SS_ABORT;
if(mode&K_LINE && useron.misc&ANSI && !(mode&K_NOECHO)) {
attr(cfg.color[clr_inputline]);
for(i=0;i<maxlen;i++)
outchar(SP);
bprintf("\x1b[%dD",maxlen); }
if(wordwrap[0]) {
strcpy(str1,wordwrap);
wordwrap[0]=0; }
else str1[0]=0;
if(mode&K_EDIT)
strcat(str1,strout);
else
strout[0]=0;
if(strlen(str1)>maxlen)
str1[maxlen]=0;
atr=curatr;
if(!(mode&K_NOECHO)) {
if(mode&K_AUTODEL && str1[0]) {
i=(cfg.color[clr_inputline]&0x77)<<4;
i|=(cfg.color[clr_inputline]&0x77)>>4;
attr(i); }
rputs(str1);
if(mode&K_EDIT && !(mode&(K_LINE|K_AUTODEL)) && useron.misc&ANSI)
bputs("\x1b[K"); /* destroy to eol */ }
i=l=strlen(str1);
if(mode&K_AUTODEL && str1[0] && !(mode&K_NOECHO)) {
ch=getkey(mode|K_GETSTR);
attr(atr);
if(isprint(ch) || ch==DEL) {
for(i=0;i<l;i++)
bputs("\b \b");
i=l=0; }
else {
for(i=0;i<l;i++)
outchar(BS);
rputs(str1);
i=l; }
if(ch!=SP && ch!=TAB)
ungetkey(ch); }
while(!(sys_status&SS_ABORT) && (ch=getkey(mode|K_GETSTR))!=CR && online) {
if(!input_thread_running)
break;
if(sys_status&SS_ABORT)
break;
if(ch==LF && mode&K_MSG) /* Down-arrow same as CR */
break;
if(ch==TAB && (mode&K_TAB || !(mode&K_WRAP))) /* TAB same as CR */
break;
if(!i && mode&K_UPRLWR && (ch==SP || ch==TAB))
continue; /* ignore beginning white space if upper/lower */
if(mode&K_E71DETECT && (uchar)ch==(CR|0x80) && l>1) {
if(strstr(str1,"")) {
bputs("\r\n\r\nYou must set your terminal to NO PARITY, "
"8 DATA BITS, and 1 STOP BIT (N-8-1).\7\r\n");
return(0); } }
switch(ch) {
case CTRL_A: /* Ctrl-A for ANSI */
if(!(mode&K_MSG) || useron.rest&FLAG('A') || i>maxlen-3)
break;
if(ins) {
if(l<maxlen)
l++;
for(x=l;x>i;x--)
str1[x]=str1[x-1];
rprintf("%.*s",l-i,str1+i);
rprintf("\x1b[%dD",l-i);
if(i==maxlen-1)
ins=0; }
outchar(str1[i++]=1);
break;
case CTRL_B: /* Ctrl-B Beginning of Line */
if(useron.misc&ANSI && i && !(mode&K_NOECHO)) {
bprintf("\x1b[%dD",i);
i=0; }
break;
case CTRL_D: /* Ctrl-D Delete word right */
if(i<l) {
x=i;
while(x<l && str1[x]!=SP) {
outchar(SP);
x++; }
while(x<l && str1[x]==SP) {
outchar(SP);
x++; }
bprintf("\x1b[%dD",x-i); /* move cursor back */
z=i;
while(z<l-(x-i)) { /* move chars in string */
outchar(str1[z]=str1[z+(x-i)]);
z++; }
while(z<l) { /* write over extra chars */
outchar(SP);
z++; }
bprintf("\x1b[%dD",z-i);
l-=x-i; } /* l=new length */
break;
case CTRL_E: /* Ctrl-E End of line */
if(useron.misc&ANSI && i<l) {
bprintf("\x1b[%dC",l-i); /* move cursor to eol */
i=l; }
break;
case CTRL_F: /* Ctrl-F move cursor forewards */
if(i<l && (useron.misc&ANSI)) {
bputs("\x1b[C"); /* move cursor right one */
i++; }
break;
case CTRL_G: /* Bell */
if(!(mode&K_MSG))
break;
if(useron.rest&FLAG('B')) {
if (i+6<maxlen) {
if(ins) {
for(x=l+6;x>i;x--)
str1[x]=str1[x-6];
if(l+5<maxlen)
l+=6;
if(i==maxlen-1)
ins=0; }
str1[i++]='(';
str1[i++]='b';
str1[i++]='e';
str1[i++]='e';
str1[i++]='p';
str1[i++]=')';
if(!(mode&K_NOECHO))
bputs("(beep)"); }
if(ins)
redrwstr(str1,i,l,0);
break; }
if(ins) {
if(l<maxlen)
l++;
for(x=l;x>i;x--)
str1[x]=str1[x-1];
if(i==maxlen-1)
ins=0; }
if(i<maxlen) {
str1[i++]=BEL;
if(!(mode&K_NOECHO))
outchar(BEL); }
break;
case CTRL_H: /* Ctrl-H/Backspace */
if(!i)
break;
i--;
l--;
if(i!=l) { /* Deleting char in middle of line */
outchar(BS);
z=i;
while(z<l) { /* move the characters in the line */
outchar(str1[z]=str1[z+1]);
z++; }
outchar(SP); /* write over the last char */
bprintf("\x1b[%dD",(l-i)+1); }
else if(!(mode&K_NOECHO))
bputs("\b \b");
break;
case CTRL_I: /* Ctrl-I/TAB */
if(!(i%TABSIZE)) {
if(ins) {
if(l<maxlen)
l++;
for(x=l;x>i;x--)
str1[x]=str1[x-1];
if(i==maxlen-1)
ins=0; }
str1[i++]=SP;
if(!(mode&K_NOECHO))
outchar(SP); }
while(i<maxlen && i%TABSIZE) {
if(ins) {
if(l<maxlen)
l++;
for(x=l;x>i;x--)
str1[x]=str1[x-1];
if(i==maxlen-1)
ins=0; }
str1[i++]=SP;
if(!(mode&K_NOECHO))
outchar(SP); }
if(ins && !(mode&K_NOECHO))
redrwstr(str1,i,l,0);
break;
case CTRL_L: /* Ctrl-L Center line (used to be Ctrl-V) */
str1[l]=0;
l=bstrlen(str1);
if(!l) break;
for(x=0;x<(maxlen-l)/2;x++)
str2[x]=SP;
str2[x]=0;
strcat(str2,str1);
strcpy(strout,str2);
l=strlen(strout);
if(mode&K_NOECHO)
return(l);
if(mode&K_MSG)
redrwstr(strout,i,l,K_MSG);
else {
while(i--)
bputs("\b");
bputs(strout);
if(mode&K_LINE)
attr(LIGHTGRAY); }
CRLF;
return(l);
case CTRL_N: /* Ctrl-N Next word */
if(i<l && (useron.misc&ANSI)) {
x=i;
while(str1[i]!=SP && i<l)
i++;
while(str1[i]==SP && i<l)
i++;
bprintf("\x1b[%dC",i-x); }
break;
case CTRL_R: /* Ctrl-R Redraw Line */
if(!(mode&K_NOECHO))
redrwstr(str1,i,l,0);
break;
case CTRL_V: /* Ctrl-V Toggles Insert/Overwrite */
if(!(useron.misc&ANSI) || mode&K_NOECHO)
break;
if(ins) {
ins=0;
redrwstr(str1,i,l,0); }
else if(i<l) {
ins=1;
bprintf("\x1b[s\x1b[79C"); /* save pos */
z=curatr; /* and go to EOL */
attr(z|BLINK|HIGH);
outchar('');
attr(z);
bputs("\x1b[u"); } /* restore pos */
break;
case CTRL_W: /* Ctrl-W Delete word left */
if(i<l) {
x=i; /* x=original offset */
while(i && str1[i-1]==SP) {
outchar(BS);
i--; }
while(i && str1[i-1]!=SP) {
outchar(BS);
i--; }
z=i; /* i=z=new offset */
while(z<l-(x-i)) { /* move chars in string */
outchar(str1[z]=str1[z+(x-i)]);
z++; }
while(z<l) { /* write over extra chars */
outchar(SP);
z++; }
bprintf("\x1b[%dD",z-i); /* back to new x corridnant */
l-=x-i; } /* l=new length */
else {
while(i && str1[i-1]==SP) {
i--;
l--;
if(!(mode&K_NOECHO))
bputs("\b \b"); }
while(i && str1[i-1]!=SP) {
i--;
l--;
if(!(mode&K_NOECHO))
bputs("\b \b"); } }
break;
case CTRL_X: /* Ctrl-X Delete entire line */
if(mode&K_NOECHO)
l=0;
else {
while(i<l) {
outchar(SP);
i++; }
while(l) {
l--;
bputs("\b \b"); } }
i=0;
break;
case CTRL_Y: /* Ctrl-Y Delete to end of line */
if(useron.misc&ANSI && !(mode&K_NOECHO)) {
bputs("\x1b[K");
l=i; }
break;
case 28: /* Ctrl-\ Previous word */
if(i && (useron.misc&ANSI) && !(mode&K_NOECHO)) {
x=i;
while(str1[i-1]==SP && i)
i--;
while(str1[i-1]!=SP && i)
i--;
bprintf("\x1b[%dD",x-i); }
break;
case 29: /* Ctrl-]/Left Arrow Reverse Cursor Movement */
if(i && (useron.misc&ANSI) && !(mode&K_NOECHO)) {
bputs("\x1b[D"); /* move cursor left one */
i--; }
break;
case 30: /* Ctrl-^/Up Arrow */
if(!(mode&K_EDIT))
break;
if(i>l)
l=i;
str1[l]=0;
strcpy(strout,str1);
if((stripattr(strout) || ins) && !(mode&K_NOECHO))
redrwstr(strout,i,l,K_MSG);
if(mode&K_LINE && !(mode&K_NOECHO))
attr(LIGHTGRAY);
console|=CON_UPARROW;
return(l);
case DEL: /* Ctrl-BkSpc (DEL) Delete current char */
if(i==l) { /* Backspace if end of line */
if(i) {
i--;
l--;
if(!(mode&K_NOECHO))
bputs("\b \b");
}
break;
}
l--;
z=i;
while(z<l) { /* move the characters in the line */
outchar(str1[z]=str1[z+1]);
z++; }
outchar(SP); /* write over the last char */
bprintf("\x1b[%dD",(l-i)+1);
break;
default:
if(mode&K_WRAP && i==maxlen && ch>=SP && !ins) {
str1[i]=0;
if(ch==SP && !(mode&K_CHAT)) { /* don't wrap a space */
strcpy(strout,str1); /* as last char */
if(stripattr(strout) && !(mode&K_NOECHO))
redrwstr(strout,i,l,K_MSG);
if(!(mode&K_NOECHO))
CRLF;
return(i); }
x=i-1;
z=1;
wordwrap[0]=ch;
while(str1[x]!=SP && x)
wordwrap[z++]=str1[x--];
if(x<(maxlen/2)) {
wordwrap[1]=0; /* only wrap one character */
strcpy(strout,str1);
if(stripattr(strout) && !(mode&K_NOECHO))
redrwstr(strout,i,l,K_MSG);
if(!(mode&K_NOECHO))
CRLF;
return(i); }
wordwrap[z]=0;
if(!(mode&K_NOECHO))
while(z--) {
bputs("\b \b");
i--; }
strrev(wordwrap);
str1[x]=0;
strcpy(strout,str1);
if(stripattr(strout) && !(mode&K_NOECHO))
redrwstr(strout,i,x,(char)mode);
if(!(mode&K_NOECHO))
CRLF;
return(x); }
if(i<maxlen && ch>=SP) {
if(mode&K_UPRLWR)
if(!i || (i && (str1[i-1]==SP || str1[i-1]=='-'
|| str1[i-1]=='.' || str1[i-1]=='_')))
ch=toupper(ch);
else
ch=tolower(ch);
if(ins) {
if(l<maxlen) /* l<maxlen */
l++;
for(x=l;x>i;x--)
str1[x]=str1[x-1];
rprintf("%.*s",l-i,str1+i);
rprintf("\x1b[%dD",l-i);
if(i==maxlen-1) {
bputs(" \b\b");
ins=0; } }
str1[i++]=ch;
if(!(mode&K_NOECHO))
outchar(ch); } }
if(i>l)
l=i;
if(mode&K_CHAT && !l)
return(0); }
if(!online)
return(0);
if(i>l)
l=i;
str1[l]=0;
if(!(sys_status&SS_ABORT)) {
strcpy(strout,str1);
if((stripattr(strout) || ins) && !(mode&K_NOECHO))
redrwstr(strout,i,l,K_MSG); }
else
l=0;
if(mode&K_LINE && !(mode&K_NOECHO)) attr(LIGHTGRAY);
if(!(mode&(K_NOCRLF|K_NOECHO))) {
outchar(CR);
if(!(mode&K_MSG && sys_status&SS_ABORT))
outchar(LF);
lncntr=0; }
return(l);
}
/****************************************************************************/
/* Hot keyed number input routine. */
/* Returns a valid number between 1 and max, 0 if no number entered, or -1 */
/* if the user hit 'Q' or ctrl-c */
/****************************************************************************/
long sbbs_t::getnum(ulong max)
{
uchar ch,n=0;
long i=0;
while(online) {
ch=getkey(K_UPPER);
if(ch>0x7f)
continue;
if(ch=='Q') {
outchar('Q');
if(useron.misc&COLDKEYS)
ch=getkey(K_UPPER);
if(ch==BS) {
bputs("\b \b");
continue; }
CRLF;
lncntr=0;
return(-1); }
else if(sys_status&SS_ABORT) {
CRLF;
lncntr=0;
return(-1); }
else if(ch==CR) {
CRLF;
lncntr=0;
return(i); }
else if(ch==BS && n) {
bputs("\b \b");
i/=10;
n--; }
else if(isdigit(ch) && (i*10UL)+(ch&0xf)<=max && (ch!='0' || n)) {
i*=10L;
n++;
i+=ch&0xf;
outchar(ch);
if(i*10UL>max && !(useron.misc&COLDKEYS)) {
CRLF;
lncntr=0;
return(i); } } }
return(0);
}