Newer
Older
/* Synchronet string input routines */
/****************************************************************************
* @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"
#include "utf8.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, int mode, const str_list_t history)
size_t i, l, x, z; /* i=current position, l=length, j=printed chars */
/* x&z=misc */
char str1[256], str2[256], undo[256];
uchar ch;
uint atr;
int hidx = -1;
int org_column = column;
int org_lbuflen = lbuflen;
int term = term_supports();
console &= ~(CON_UPARROW | CON_DOWNARROW | CON_LEFTARROW | CON_RIGHTARROW | CON_BACKSPACE | CON_DELETELINE);
if (!(mode & K_WORDWRAP))
console &= ~CON_INSERT;
sys_status &= ~SS_ABORT;
if (!(mode & K_LINEWRAP) && cols >= TERM_COLS_MIN && !(mode & K_NOECHO) && !(console & CON_R_ECHOX)
&& column + (int)maxlen >= cols) /* Don't allow the terminal to auto line-wrap */
maxlen = cols - column - 1;
if (mode & K_LINE && (term & (ANSI | PETSCII)) && !(mode & K_NOECHO)) {
attr(cfg.color[clr_inputline]);
for (i = 0; i < maxlen; i++)
outcom(' ');
cursor_left(maxlen);
column = org_column;
if (wordwrap[0]) {
SAFECOPY(str1, wordwrap);
wordwrap[0] = 0;
if (mode & K_EDIT)
SAFECAT(str1, strout);
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;

Rob Swindell
committed
attr(i);
bputs(str1, P_AUTO_UTF8);
if (mode & K_EDIT && !(mode & (K_LINE | K_AUTODEL)))

Rob Swindell
committed
cleartoeol(); /* destroy to eol */
SAFECOPY(undo, str1);
i = l = bstrlen(str1, P_AUTO_UTF8);
if (mode & K_AUTODEL && str1[0] && !(mode & K_NOECHO)) {
ch = getkey(mode | K_GETSTR);
if (IS_PRINTABLE(ch) || ch == DEL) {
for (i = 0; i < l; i++)
for (i = 0; i < l; i++)
column += bputs(str1, P_AUTO_UTF8);
i = l;
if (ch != ' ' && ch != TAB)

Rob Swindell
committed
ungetkey(ch);
}
if (mode & K_USEOFFSET) {
i = getstr_offset;
if (i > l)
i = l;
if (l - i) {
cursor_left(l - i);
}
}
if (console & CON_INSERT && !(mode & K_NOECHO))
insert_indicator();
while (!(sys_status & SS_ABORT) && online && input_thread_running) {
if (mode & K_LEFTEXIT
&& console & (CON_LEFTARROW | CON_BACKSPACE | CON_DELETELINE))
break;
if (console & CON_UPARROW)
break;
if ((ch = getkey(mode | K_GETSTR)) == CR)
if (sys_status & SS_ABORT || !online)
if (ch == LF && mode & K_MSG) { /* Down-arrow same as CR */
console |= CON_DOWNARROW;
}
if (ch == TERM_KEY_RIGHT && (mode & K_RIGHTEXIT) && i == l) {
console |= CON_RIGHTARROW;
break;
}
if (ch == TAB && (mode & K_TAB || (!(mode & K_WORDWRAP) && history == NULL))) /* TAB same as CR */
if (!i && (mode & (K_UPRLWR | K_TRIM)) && (ch == ' ' || 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");
}
getstr_offset = i;
switch (ch) {
if (!(mode & K_MSG) || useron.rest & FLAG('A') || i > maxlen - 3)
if (console & CON_INSERT) {
if (l < maxlen)
for (x = l; x > i; x--)
str1[x] = str1[x - 1];
column += rprintf("%.*s", (int)(l - i), str1 + i);
cursor_left(l - i);
if (i == maxlen - 1)
console &= ~CON_INSERT;
}
outchar(str1[i++] = 1);
case TERM_KEY_HOME: /* Ctrl-B Beginning of Line */
if (i && !(mode & K_NOECHO)) {
cursor_left(i);
}
case CTRL_D: /* Ctrl-D Delete word right */
if (i < l) {
x = i;
while (x < l && str1[x] != ' ') {

Rob Swindell
committed
x++;
}
while (x < l && str1[x] == ' ') {

Rob Swindell
committed
x++;
}
cursor_left(x - i); /* move cursor back */
z = i;
while (z < l - (x - i)) { /* move chars in string */
outchar(str1[z] = str1[z + (x - i)]);

Rob Swindell
committed
z++;
}
while (z < l) { /* write over extra chars */

Rob Swindell
committed
z++;
}
cursor_left(z - i);
l -= x - i; /* l=new length */
}
case TERM_KEY_END: /* Ctrl-E End of line */
if (term & (ANSI | PETSCII) && i < l) {
cursor_right(l - i); /* move cursor to eol */
i = l;
}
case TERM_KEY_RIGHT: /* Ctrl-F move cursor forward */
if (i < l && term & (ANSI | PETSCII)) {
cursor_right(); /* move cursor right one */

Rob Swindell
committed
i++;
}
if (useron.rest & FLAG('B')) {
if (i + 6 < maxlen) {
if (console & CON_INSERT) {
for (x = l + 6; x > i; x--)
str1[x] = str1[x - 6];
if (l + 5 < maxlen)
l += 6;
if (i == maxlen - 1)
console &= ~CON_INSERT;
}
str1[i++] = '(';
str1[i++] = 'b';
str1[i++] = 'e';
str1[i++] = 'e';
str1[i++] = 'p';
str1[i++] = ')';
if (!(mode & K_NOECHO))

Rob Swindell
committed
bputs("(beep)");
}
if (console & CON_INSERT)
redrwstr(str1, i, l, 0);

Rob Swindell
committed
break;
}
if (console & CON_INSERT) {
if (l < maxlen)
for (x = l; x > i; x--)
str1[x] = str1[x - 1];
if (i == maxlen - 1)
console &= ~CON_INSERT;
}
if (i < maxlen) {
str1[i++] = BEL;
if (!(mode & K_NOECHO))

Rob Swindell
committed
outchar(BEL);
}
break;
case CTRL_H: /* Ctrl-H/Backspace */
if (i == 0) {
console |= CON_BACKSPACE;
}
} while ((term & UTF8) && (i > 0) && (str1[i] & 0x80) && (str1[i - 1] & 0x80));
if (i != l) { /* Deleting char in middle of line */
z = i;
while (z < l) { /* move the characters in the line */
outchar(str1[z] = str1[z + 1]);

Rob Swindell
committed
z++;
}
outchar(' '); /* write over the last char */
cursor_left((l - i) + 1);
}
else if (!(mode & K_NOECHO))
case CTRL_I: /* Ctrl-I/TAB */
if (history != NULL) {
if (l < 1)
break;
int hi;
for (hi = 0; history[hi] != NULL; hi++)
if (strnicmp(history[hi], str1, l) == 0) {
hidx = hi;
SAFECOPY(str1, history[hi]);
backspace();
rputs(str1);
cleartoeol();
break;
}
break;
}
if (!(i % EDIT_TABSIZE)) {
if (console & CON_INSERT) {
if (l < maxlen)
for (x = l; x > i; x--)
str1[x] = str1[x - 1];
if (i == maxlen - 1)
console &= ~CON_INSERT;
}
str1[i++] = ' ';
if (!(mode & K_NOECHO))

Rob Swindell
committed
outchar(' ');
}
while (i < maxlen && i % EDIT_TABSIZE) {
if (console & CON_INSERT) {
if (l < maxlen)
for (x = l; x > i; x--)
str1[x] = str1[x - 1];
if (i == maxlen - 1)
console &= ~CON_INSERT;
}
str1[i++] = ' ';
if (!(mode & K_NOECHO))

Rob Swindell
committed
outchar(' ');
}
if (console & CON_INSERT && !(mode & K_NOECHO))
redrwstr(str1, i, l, 0);
case CTRL_L: /* Ctrl-L Center line (used to be Ctrl-V) */
str1[l] = 0;
l = bstrlen(str1);
for (x = 0; x < (maxlen - l) / 2; x++)
str2[x] = ' ';
str2[x] = 0;
SAFECAT(str2, str1);
strcpy(strout, str2);
l = strlen(strout);
if (mode & K_NOECHO)
if (mode & K_MSG)
redrwstr(strout, i, l, K_MSG);
bputs("\b");
bputs(strout);

Rob Swindell
committed
attr(LIGHTGRAY);
}
if (!(mode & K_NOCRLF))
if (i < l && term & (ANSI | PETSCII)) {
x = i;
while (str1[i] != ' ' && i < l)
while (str1[i] == ' ' && i < l)
}
if (!(mode & K_NOECHO))
redrwstr(str1, i, l, P_AUTO_UTF8);
case TERM_KEY_INSERT: /* Ctrl-V Toggles Insert/Overwrite */
if (mode & K_NOECHO)
console ^= CON_INSERT;
insert_indicator();
case CTRL_W: /* Ctrl-W Delete word left */
if (i < l) {
x = i; /* x=original offset */
while (i && str1[i - 1] == ' ') {

Rob Swindell
committed
i--;
}
while (i && str1[i - 1] != ' ') {

Rob Swindell
committed
i--;
}
z = i; /* i=z=new offset */
while (z < l - (x - i)) { /* move chars in string */
outchar(str1[z] = str1[z + (x - i)]);

Rob Swindell
committed
z++;
}
while (z < l) { /* write over extra chars */

Rob Swindell
committed
z++;
}
cursor_left(z - i); /* back to new x corridnant */
l -= x - i; /* l=new length */
} else {
while (i && str1[i - 1] == ' ') {
if (!(mode & K_NOECHO))

Rob Swindell
committed
backspace();
}
while (i && str1[i - 1] != ' ') {
if (!(mode & K_NOECHO))

Rob Swindell
committed
backspace();
}
}
case CTRL_Y: /* Ctrl-Y Delete to end of line */
if (i != l) { /* if not at EOL */
if (!(mode & K_NOECHO))
cleartoeol();
case CTRL_X: /* Ctrl-X Delete entire line */
if (mode & K_NOECHO)
l = 0;
cursor_left(i);
cleartoeol();
lbuflen = org_lbuflen;
}
i = 0;
console |= CON_DELETELINE;
case CTRL_Z: /* Undo */
if (!(mode & K_NOECHO)) {
while (i--)
backspace();
}
SAFECOPY(str1, undo);
i = l = strlen(str1);
rputs(str1);
cleartoeol();
case CTRL_BACKSLASH: /* Ctrl-\ Previous word */
if (i && !(mode & K_NOECHO)) {
x = i;
while (str1[i - 1] == ' ' && i)
while (str1[i - 1] != ' ' && i)
}
case TERM_KEY_LEFT: /* Ctrl-]/Left Arrow Reverse Cursor Movement */
if (i == 0) {
if (mode & K_LEFTEXIT)
console |= CON_LEFTARROW;
break;
}
if (!(mode & K_NOECHO)) {
cursor_left(); /* move cursor left one */

Rob Swindell
committed
i--;
}
case TERM_KEY_DOWN:
if (history != NULL) {
if (hidx < 0) {
outchar(BEL);
break;
}
hidx--;
SAFECOPY(str1, undo);
else
SAFECOPY(str1, history[hidx]);
backspace();
rputs(str1);
cleartoeol();
break;
}
break;
case TERM_KEY_UP: /* Ctrl-^/Up Arrow */
if (history != NULL) {
if (history[hidx + 1] == NULL) {
outchar(BEL);
break;
}
hidx++;
backspace();
SAFECOPY(str1, history[hidx]);
rputs(str1);
cleartoeol();
break;
}
#if 1
if (i > l)
l = i;
str1[l] = 0;
strcpy(strout, str1);
if ((strip_invalid_attr(strout) || console & CON_INSERT) && !(mode & K_NOECHO))
redrwstr(strout, i, l, K_MSG);
if (mode & K_LINE && !(mode & K_NOECHO))
console |= CON_UPARROW;
#else
console |= CON_UPARROW;
break;
#endif
case TERM_KEY_DELETE: /* Ctrl-BkSpc (DEL) Delete current char */
if (i == l) { /* Backspace if end of line */
if (i) {
if (!(mode & K_NOECHO))
} while ((term & UTF8) && (i > 0) && (str1[i] & 0x80) && (str1[i - 1] & 0x80));
z = i;
while (z < l) { /* move the characters in the line */
outchar(str1[z] = str1[z + 1]);

Rob Swindell
committed
z++;
}
outchar(' '); /* write over the last char */
cursor_left((l - i) + 1);
if (mode & K_WORDWRAP && i == maxlen && ch >= ' ' && !(console & CON_INSERT)) {
str1[i] = 0;
if (ch == ' ' && !(mode & K_CHAT)) { /* don't wrap a space */
strcpy(strout, str1); /* as last char */
if (strip_invalid_attr(strout) && !(mode & K_NOECHO))
redrwstr(strout, i, l, K_MSG);
if (!(mode & (K_NOECHO | K_NOCRLF)))
}
x = i - 1;
z = 1;
wordwrap[0] = ch;
while (str1[x] != ' ' && x > 0 && z < sizeof(wordwrap) - 1)
wordwrap[z++] = str1[x--];
if (x < (maxlen / 2)) {
wordwrap[1] = 0; /* only wrap one character */
strcpy(strout, str1);
if (strip_invalid_attr(strout) && !(mode & K_NOECHO))
redrwstr(strout, i, l, K_MSG);
if (!(mode & (K_NOECHO | K_NOCRLF)))
}
wordwrap[z] = 0;
if (!(mode & K_NOECHO))
while (z--) {

Rob Swindell
committed
i--;
}
str1[x] = 0;
strcpy(strout, str1);
if (strip_invalid_attr(strout) && !(mode & K_NOECHO))
redrwstr(strout, i, x, mode);
if (!(mode & (K_NOECHO | K_NOCRLF)))
}
if (i < maxlen && ch >= ' ') {
if (ch == ' ' && (mode & K_TRIM) && i && str1[i - 1] == ' ')
continue;
if ((mode & K_UPRLWR) && !(ch & 0x80)) {
if (!i || (i && (str1[i - 1] == ' ' || str1[i - 1] == '-'
|| str1[i - 1] == '.' || str1[i - 1] == '_')))
ch = toupper(ch);
if (console & CON_INSERT && i != l) {
if (l < maxlen) /* l<maxlen */
for (x = l; x > i; x--)
str1[x] = str1[x - 1];
column += rprintf("%.*s", (int)(l - i), str1 + i);
cursor_left(l - i);
if (i == maxlen - 1) {
console &= ~CON_INSERT;

Rob Swindell
committed
}
}
str1[i++] = ch;
if (!(mode & K_NOECHO)) {
if ((term & UTF8) && (ch & 0x80)) {
if (i > l)
l = i;
str1[l] = 0;
if (utf8_str_is_valid(str1))
redrwstr(str1, column - org_column, l, P_UTF8);
} else
outchar(BEL); /* Added at Angus McLeod's request */
}
if (i > l)
l = i;
if (mode & K_CHAT && !l)
}
getstr_offset = i;
if (!online)
if (i > l)
l = i;
str1[l] = 0;
if (!(sys_status & SS_ABORT)) {
strcpy(strout, str1);
if (mode & K_TRIM)
truncsp(strout);
if ((strip_invalid_attr(strout) || (console & CON_INSERT)) && !(mode & K_NOECHO))
redrwstr(strout, i, l, P_AUTO_UTF8);
}
if (mode & K_LINE && !(mode & K_NOECHO))
attr(LIGHTGRAY);
if (!(mode & (K_NOCRLF | K_NOECHO))) {
if (!(mode & K_MSG && sys_status & SS_ABORT)) {
}
}
/****************************************************************************/
/* Hot keyed number input routine. */
/* Returns a valid number between 1 and max, 0 if no number entered, or -1 */
/* if the user hit the quit key (e.g. 'Q') or ctrl-c */
/****************************************************************************/
int sbbs_t::getnum(uint max, uint dflt)
uchar ch, n = 0;
int i = 0;
while (online) {
ch = getkey(K_UPPER);
if (ch > 0x7f)
if (ch == quit_key()) {
outchar(quit_key());
if (useron.misc & COLDKEYS)
ch = getkey(K_UPPER);
if (ch == BS || ch == DEL) {

Rob Swindell
committed
continue;
}
}
else if (sys_status & SS_ABORT) {
}
lncntr = 0;
if (!n)
return dflt;
return i;
}
else if ((ch == BS || ch == DEL) && n) {

Rob Swindell
committed
n--;
}
else if (IS_DIGIT(ch) && (i * 10UL) + (ch & 0xf) <= max && (dflt || ch != '0' || n)) {
i *= 10L;
if (i * 10UL > max && !(useron.misc & COLDKEYS) && keybuf_level() < 1) {

Rob Swindell
committed
}
}
}
void sbbs_t::insert_indicator(void)
{
if (term_supports(ANSI)) {
char str[32];
ansi_save();
ansi_gotoxy(cols, 1);
int tmpatr;
if (console & CON_INSERT) {
putcom(ansi_attr(tmpatr = BLINK | BLACK | (LIGHTGRAY << 4), curatr, str, term_supports(COLOR)));
outcom('I');
} else {
putcom(ansi(tmpatr = ANSI_NORMAL));
outcom(' ');
}
putcom(ansi_attr(curatr, tmpatr, str, term_supports(COLOR)));
ansi_restore();
column = col;