Newer
Older
/* Synchronet console output routines */
// vi: tabstop=4
/* $Id$ */
/****************************************************************************
* @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 *
* *
* 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"
#include "utf8.h"
#include "unicode.h"
#include "petdefs.h"
char* sbbs_t::auto_utf8(const char* str, long* mode)
{
if(strncmp(str, "\xEF\xBB\xBF", 3) == 0) {
*mode |= P_UTF8;
return (char*)(str + 3);
}
if((*mode)&P_AUTO_UTF8) {
if(!str_is_ascii(str) && utf8_str_is_valid(str))
*mode |= P_UTF8;
}
return (char*)str;
}
/****************************************************************************/
/* Outputs a NULL terminated string locally and remotely (if applicable) */
/* Handles ctrl-a codes, Telnet-escaping, column & line count, auto-pausing */
/* Supported P_* mode flags:
P_PETSCII
P_UTF8
P_AUTO_UTF8
P_NOATCODES
P_TRUNCATE
****************************************************************************/
if(online==ON_LOCAL && console&CON_L_ECHO) /* script running as event */
return(lputs(LOG_INFO, str));
str = auto_utf8(str, &mode);
size_t len = strlen(str);
while(l < len && online) {
switch(str[l]) {
case '\b':
case '\r':
case '\n':
case FF:
case CTRL_A:
break;
default: // printing char
if((mode&P_TRUNCATE) && column >= (cols - 1)) {
l++;
continue;
}
}
if(str[l]==CTRL_A && str[l+1]!=0) {
l++;
if(str[l] == 'Z') /* EOF (uppercase 'Z' only) */
break;
ctrl_a(str[l++]);
continue;
if(!(mode&P_NOATCODES) && str[l]=='@') {
if(str==mnestr /* Mnemonic string or */
|| (str>=text[0] /* Straight out of TEXT.DAT */
&& str<=text[TOTAL_TEXT-1])) {
i=show_atcode(str+l); /* return 0 if not valid @ code */
l+=i; /* i is length of code string */
if(i) /* if valid string, go to top */
continue;
for(i=0;i<TOTAL_TEXT;i++)
if(str==text[i])
break;
if(i<TOTAL_TEXT) { /* Replacement text */
continue;
}
if(mode&P_PETSCII) {
if(term&PETSCII)
outcom(str[l++]);
else
petscii_to_ansibbs(str[l++]);
} else if((str[l]&0x80) && (mode&P_UTF8)) {
if(term&UTF8)
outcom(str[l++]);
else
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/****************************************************************************/
/* Returns the printed columns from 'str' accounting for Ctrl-A codes */
/****************************************************************************/
size_t sbbs_t::bstrlen(const char *str, long mode)
{
str = auto_utf8(str, &mode);
size_t count = 0;
const char* end = str + strlen(str);
while (str < end) {
int len = 1;
if(*str == CTRL_A) {
str++;
if(*str == 0 || *str == 'Z') // EOF
break;
if(*str == '[') // CR
count = 0;
else if(*str == '<' && count) // ND-Backspace
count--;
} else if(((*str) & 0x80) && (mode&P_UTF8)) {
enum unicode_codepoint codepoint = UNICODE_UNDEFINED;
len = utf8_getc(str, end - str, &codepoint);
if(len < 1)
break;
count += unicode_width(codepoint);;
} else
count++;
str += len;
}
return count;
}
/* Perform PETSCII terminal output translation (from ASCII/CP437) */
unsigned char cp437_to_petscii(unsigned char ch)
{
if(isalpha(ch))
return ch ^ 0x20; /* swap upper/lower case */
switch(ch) {
case '\1': return '@';
case '\x10': return '>';
case '\x11': return '<';
case '\x18':
case '\x1e': return PETSCII_UPARROW;
case '\x19':
case '\x1f': return 'V';
case '\x1a': return '>';
case '|': return PETSCII_VERTLINE;
case '\\': return PETSCII_BACKSLASH;
case '`': return PETSCII_BACKTICK;
case '~': return PETSCII_TILDE;
case '_': return PETSCII_UNDERSCORE;
case '{': return '(';
case '}': return ')';
case '\b': return PETSCII_LEFT;
case 156: return PETSCII_BRITPOUND;
case 251: return PETSCII_CHECKMARK;
case 176: return PETSCII_LIGHTHASH;
case 177: return PETSCII_MEDIUMHASH;
case 178: return PETSCII_HEAVYHASH;
case 219: return PETSCII_SOLID;
case 220: return PETSCII_BOTTOMHALF;
case 221: return PETSCII_LEFTHALF;
case 222: return PETSCII_RIGHTHALF;
case 223: return PETSCII_TOPHALF;
case 254: return PETSCII_UPRLFTBOX;
/* Line drawing chars */
case 186:
case 179: return PETSCII_VERTLINE;
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
case 205:
case 196: return PETSCII_HORZLINE;
case 206:
case 215:
case 216:
case 197: return PETSCII_CROSS;
case 188:
case 189:
case 190:
case 217: return '\xBD';
case 201:
case 213:
case 214:
case 218: return '\xB0';
case 183:
case 184:
case 187:
case 191: return '\xAE';
case 200:
case 211:
case 212:
case 192: return '\xAD';
case 198:
case 199:
case 204:
case 195: return '\xAB';
case 180:
case 181:
case 182:
case 185: return '\xB3';
case 203:
case 209:
case 210:
case 194: return '\xB2';
case 202:
case 207:
case 208:
case 193: return '\xB1';
}
if(ch&0x80)
return exascii_to_ascii_char(ch);
return ch;
}
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/* Perform PETSCII conversion to ANSI-BBS/CP437 */
int sbbs_t::petscii_to_ansibbs(unsigned char ch)
{
if((ch&0xe0) == 0xc0) /* "Codes $60-$7F are, actually, copies of codes $C0-$DF" */
ch = 0x60 | (ch&0x1f);
if(isalpha(ch))
return outchar(ch ^ 0x20); /* swap upper/lower case */
switch(ch) {
case '\r': newline(); break;
case PETSCII_HOME: cursor_home(); break;
case PETSCII_CLEAR: return CLS;
case PETSCII_DELETE: backspace(); break;
case PETSCII_LEFT: cursor_left(); break;
case PETSCII_RIGHT: cursor_right(); break;
case PETSCII_UP: cursor_up(); break;
case PETSCII_DOWN: cursor_down(); break;
case PETSCII_BRITPOUND: return outchar((char)156);
case PETSCII_CHECKMARK: return outchar((char)251);
case PETSCII_CHECKERBRD:
case PETSCII_LIGHTHASH: return outchar((char)176);
case 0x7e:
case PETSCII_MEDIUMHASH: return outchar((char)177);
case PETSCII_HEAVYHASH: return outchar((char)178);
case PETSCII_SOLID: return outchar((char)219);
case PETSCII_BOTTOMHALF: return outchar((char)220);
case PETSCII_LEFTHALF: return outchar((char)221);
case PETSCII_RIGHTHALF: return outchar((char)222);
case PETSCII_TOPHALF: return outchar((char)223);
case PETSCII_LWRLFTBOX:
case PETSCII_LWRRHTBOX:
case PETSCII_UPRRHTBOX:
case PETSCII_UPRLFTBOX: return outchar((char)254);
/* Line drawing chars */
case 0x7D:
case PETSCII_VERTLINE: return outchar((char)179);
case PETSCII_HORZLINE: return outchar((char)196);
case 0x7B:
case PETSCII_CROSS: return outchar((char)197);
case (uchar)'\xBD': return outchar((char)217);
case (uchar)'\xB0': return outchar((char)218);
case (uchar)'\xAE': return outchar((char)191);
case (uchar)'\xAD': return outchar((char)192);
case (uchar)'\xAB': return outchar((char)195);
case (uchar)'\xB3': return outchar((char)180);
case (uchar)'\xB2': return outchar((char)194);
case (uchar)'\xB1': return outchar((char)193);
case PETSCII_BLACK: return attr(BLACK);
case PETSCII_WHITE: return attr(WHITE);
case PETSCII_RED: return attr(RED);
case PETSCII_GREEN: return attr(GREEN);
case PETSCII_BLUE: return attr(BLUE);
case PETSCII_ORANGE: return attr(MAGENTA);
case PETSCII_BROWN: return attr(BROWN);
case PETSCII_YELLOW: return attr(YELLOW);
case PETSCII_CYAN: return attr(LIGHTCYAN);
case PETSCII_LIGHTRED: return attr(LIGHTRED);
case PETSCII_DARKGRAY: return attr(DARKGRAY);
case PETSCII_MEDIUMGRAY: return attr(CYAN);
case PETSCII_LIGHTGREEN: return attr(LIGHTGREEN);
case PETSCII_LIGHTBLUE: return attr(LIGHTBLUE);
case PETSCII_LIGHTGRAY: return attr(LIGHTGRAY);
case PETSCII_PURPLE: return attr(LIGHTMAGENTA);
case PETSCII_REVERSE_ON: return attr((curatr&0x07) << 4);
case PETSCII_REVERSE_OFF: return attr(curatr >> 4);
case PETSCII_FLASH_ON: return attr(curatr | BLINK);
case PETSCII_FLASH_OFF: return attr(curatr & ~BLINK);
default:
if(ch&0x80) return bprintf("#%3d", ch);
return outchar(ch);
case PETSCII_UPPERLOWER:
case PETSCII_UPPERGRFX:
/* Do nothing */
return 0;
}
return 0;
}
// Return length of sequence
size_t sbbs_t::print_utf8_as_cp437(const char* str, size_t len)
{
if(((*str)&0x80) == 0) {
outchar(*str);
return sizeof(char);
}
enum unicode_codepoint codepoint = UNICODE_UNDEFINED;
len = utf8_getc(str, len, &codepoint);
if((int)len < 2) {
lprintf(LOG_NOTICE, "Invalid UTF-8 sequence: %02X (error = %d)", (uchar)*str, (int)len);
return 1;
}
for(int i = 1; i < 0x100; i++) {
if(cp437_unicode_tbl[i]
&& cp437_unicode_tbl[i] == codepoint) {
outchar(i);
return len;
}
}
char ch = unicode_to_cp437(codepoint);
if(ch)
outchar(ch);
else if(unicode_width(codepoint) > 0) {
outchar(CP437_INVERTED_QUESTION_MARK);
char seq[32] = "";
for(size_t i = 0; i < len; i++)
sprintf(seq + strlen(seq), "%02X ", (uchar)*(str + i));
lprintf(LOG_DEBUG, "Unsupported UTF-8 sequence: %s (U+%X)", seq, codepoint);
}
return len;
}
/****************************************************************************/
/* Raw put string (remotely) */
/* Performs Telnet IAC escaping */
/* Performs charset translations */
/* Performs saveline buffering (for restoreline) */
/* DOES NOT expand ctrl-A codes, track columns, lines, auto-pause, etc. */
/****************************************************************************/
int sbbs_t::rputs(const char *str, size_t len)
if(len==0)
len=strlen(str);
char utf8[UTF8_MAX_LEN + 1] = "";
for(l=0;l<len && online;l++) {
uchar ch = str[l];
utf8[0] = 0;
ch = cp437_to_petscii(ch);
else if((term&NO_EXASCII) && (ch&0x80))
ch = exascii_to_ascii_char(ch); /* seven bit table */
else if(term&UTF8) {
enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch];
if(codepoint != 0)
utf8_putc(utf8, sizeof(utf8) - 1, codepoint);
}
if(utf8[0])
putcom(utf8);
else {
if(outcom(ch)!=0)
break;
if((char)ch == (char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */
}
if(lbuflen<LINE_BUFSIZE)
}
return(l);
}
/****************************************************************************/
/* Performs printf() using bbs bputs function */
/****************************************************************************/
int sbbs_t::bprintf(const char *fmt, ...)
char sbuf[4096];
return(bputs(fmt));
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0; /* force termination */
va_end(argptr);
return(bputs(sbuf));
}
/****************************************************************************/
/* Performs printf() using bbs bputs function (with mode) */
/****************************************************************************/
int sbbs_t::bprintf(long mode, const char *fmt, ...)
{
va_list argptr;
char sbuf[4096];
if(strchr(fmt,'%')==NULL)
return(bputs(fmt, mode));
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0; /* force termination */
va_end(argptr);
return(bputs(sbuf, mode));
}
/****************************************************************************/
/* Performs printf() using bbs rputs function */
/****************************************************************************/
int sbbs_t::rprintf(const char *fmt, ...)
char sbuf[4096];
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0; /* force termination */
va_end(argptr);
return(rputs(sbuf));
}
/****************************************************************************/
/****************************************************************************/
void sbbs_t::backspace(int count)
if(count < 1)
return;
if(!(console&CON_ECHO_OFF)) {
for(int i = 0; i < count; i++) {
if(term_supports(PETSCII))
outcom(PETSCII_DELETE);
else {
outcom('\b');
outcom(' ');
outcom('\b');
}
if(column)
column--;
/****************************************************************************/
/* Returns true if the user (or the yet-to-be-logged-in client) supports */
/* all of the specified terminal 'cmp_flags' (e.g. ANSI, COLOR, RIP). */
/* If no flags specified, returns all terminal flag bits supported */
/****************************************************************************/
long sbbs_t::term_supports(long cmp_flags)
{
long flags = ((sys_status&SS_USERON) && !(useron.misc&AUTOTERM)) ? useron.misc : autoterm;
if((sys_status&SS_USERON) && (useron.misc&AUTOTERM))
flags |= useron.misc & (NO_EXASCII | SWAP_DELETE | COLOR | ICE_COLOR);
return(cmp_flags ? ((flags&cmp_flags)==cmp_flags) : (flags&TERM_FLAGS));
}
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
/****************************************************************************/
/* Returns description of the terminal type */
/****************************************************************************/
const char* sbbs_t::term_type(long term)
{
if(term == -1)
term = term_supports();
if(term&PETSCII)
return "PETSCII";
if(term&RIP)
return "RIP";
if(term&ANSI)
return "ANSI";
return "DUMB";
}
/****************************************************************************/
/* Returns description of the terminal supported character set (charset) */
/****************************************************************************/
const char* sbbs_t::term_charset(long term)
{
if(term == -1)
term = term_supports();
if(term&PETSCII)
return "CBM-ASCII";
if(term&UTF8)
return "UTF-8";
if(term&NO_EXASCII)
return "US-ASCII";
return "CP437";
}
/****************************************************************************/
/* Outputs character */
/* Performs terminal translations (e.g. EXASCII-to-ASCII, FF->ESC[2J) */
/* Performs Telnet IAC escaping */
/* Performs column counting, line counting, and auto-pausing */
/* Performs saveline buffering (for restoreline) */
/****************************************************************************/
int sbbs_t::outchar(char ch)
/*
* outchar_esc values:
* 0: No sequence
* 1: ESC
* 2: CSI
* 3: Final byte
* 4: APS, DCS, PM, or OSC
* 5: SOS
* 6: ESC inside of SOS
*/
return 0;
if(ch==ESC && outchar_esc < 4)
outchar_esc=1;
else if(outchar_esc==1) {
else if(ch=='_' || ch=='P' || ch == '^' || ch == ']')
outchar_esc=4;
else if(ch=='X')
outchar_esc=5;
else if(ch >= 0x40 && ch <= 0x5f)
outchar_esc=3;
else
outchar_esc=0;
}
else if(outchar_esc==2) {
if(ch>='@' && ch<='~')
outchar_esc++;
else if(outchar_esc==4) { // APS, DCS, PM, or OSC
if (ch == ESC)
outchar_esc = 1;
if (!((ch >= 0x08 && ch <= 0x0d) || (ch >= 0x20 && ch <= 0x7e)))
outchar_esc = 0;
}
else if(outchar_esc==5) { // SOS
if (ch == ESC)
outchar_esc++;
}
else if(outchar_esc==6) { // ESC inside SOS
if (ch == '\\')
outchar_esc = 1;
else if (ch == 'X')
outchar_esc = 0;
else
outchar_esc = 5;
}
char utf8[UTF8_MAX_LEN + 1] = "";
if(!(term&PETSCII)) {
if((term&NO_EXASCII) && (ch&0x80))
ch = exascii_to_ascii_char(ch); /* seven bit table */
else if(term&UTF8) {
enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch];
if(codepoint != 0)
utf8_putc(utf8, sizeof(utf8) - 1, codepoint);
}
if(ch==FF && lncntr > 0 && !tos) {
if(!(sys_status&SS_PAUSEOFF)) {
pause();
while(lncntr && online && !(sys_status&SS_ABORT))
pause();
if(!(console&CON_R_ECHO))
return 0;
if((console&CON_R_ECHOX) && (uchar)ch>=' ' && !outchar_esc) {
ch=text[YNQP][3];
if(text[YNQP][2]==0 || ch==0) ch='X';
}
if(ch==FF) {
if(term&ANSI)
putcom("\x1b[2J\x1b[H"); /* clear screen, home cursor */
else if(term&PETSCII)
outcom(PETSCII_CLEAR);
else
outcom(FF);
}
else if(ch == '\t') {
outcom(' ');
column++;
while(column%tabstop) {
else {
if(ch==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */
if(ch == '\r' && (console&CON_CR_CLREOL))
cleartoeol();
if(term&PETSCII) {
uchar pet = cp437_to_petscii(ch);
if(pet == PETSCII_SOLID)
outcom(PETSCII_REVERSE_ON);
outcom(pet);
if(pet == PETSCII_SOLID)
outcom(PETSCII_REVERSE_OFF);
if(ch == '\r' && (curatr&0xf0) != 0) // reverse video is disabled upon CR
curatr >>= 4;
} else {
if(utf8[0] != 0)
putcom(utf8);
else
outcom(ch);
}
if(!outchar_esc) {
/* Track cursor position locally */
switch(ch) {
case '\a': // 7
case '\t': // 9
/* Non-printing or handled elsewhere */
break;
case '\b': // 8
if(column > 0)
column--;
if(lbuflen > 0)
lbuflen--;
break;
case '\n': // 10
if(lncntr || lastlinelen)
lncntr++;
lbuflen=0;
tos=0;
column=0;
break;
case FF: // 12
lncntr=0;
lbuflen=0;
tos=1;
column=0;
case '\r': // 13
lastlinelen = column;
inc_column(1);
if(!lbuflen)
latr=curatr;
if(lbuflen<LINE_BUFSIZE)
lbuf[lbuflen++]=ch;
break;
if(outchar_esc==3)
outchar_esc=0;
if(lncntr==rows-1 && ((useron.misc&UPAUSE) || sys_status&SS_PAUSEON)
&& !(sys_status&(SS_PAUSEOFF|SS_ABORT))) {
pause();
return 0;
int sbbs_t::outchar(enum unicode_codepoint codepoint, const char* cp437_fallback)
{
if(term_supports(UTF8)) {
char str[UTF8_MAX_LEN];
int len = utf8_putc(str, sizeof(str), codepoint);
if(len < 1)
return len;
putcom(str, len);
inc_column(unicode_width(codepoint));
return 0;
}
if(cp437_fallback == NULL)
return bputs(cp437_fallback);
}
int sbbs_t::outchar(enum unicode_codepoint codepoint, char cp437_fallback)
{
char str[2] = { cp437_fallback, '\0' };
return outchar(codepoint, str);
}
void sbbs_t::inc_column(int count)
{
column += count;
if(column >= cols) { // assume terminal has/will auto-line-wrap
lncntr++;
lbuflen = 0;
tos = 0;
lastlinelen = column;
column = 0;
}
}
void sbbs_t::center(char *instr)
{
char str[256];
int i,j;
SAFECOPY(str,instr);
truncsp(str);
j=bstrlen(str);
if(j < cols)
for(i=0;i<(cols-j)/2;i++)
outchar(' ');
void sbbs_t::wide(const char* str)
{
long term = term_supports();
while(*str != '\0') {
if((term&UTF8) && *str >= '!' && *str <= '~')
outchar((enum unicode_codepoint)(UNICODE_FULLWIDTH_EXCLAMATION_MARK + (*str - '!')));
else {
outchar(*str);
outchar(' ');
}
str++;
}
}
// Send a bare carriage return, hopefully moving the cursor to the far left, current row
void sbbs_t::carriage_return(int count)
if(count < 1)
return;
for(int i = 0; i < count; i++) {
if(term_supports(PETSCII))
cursor_left(column);
else
outcom('\r');
column = 0;
}
}
// Send a bare line_feed, hopefully moving the cursor down one row, current column
void sbbs_t::line_feed(int count)
if(count < 1)
return;
for(int i = 0; i < count; i++) {
if(term_supports(PETSCII))
outcom(PETSCII_DOWN);
else
outcom('\n');
}
void sbbs_t::newline(int count)
if(count < 1)
return;
for(int i = 0; i < count; i++) {
outchar('\r');
outchar('\n');
}
void sbbs_t::clearline(void)
{
cleartoeol();
}
void sbbs_t::cursor_home(void)
{
rputs("\x1b[H");
else
outchar(FF); /* this will clear some terminals, do nothing with others */
tos=1;
column=0;
}
void sbbs_t::cursor_up(int count)
{
if(count<1)
return;
long term = term_supports();
if(term&ANSI) {
if(count>1)
rprintf("\x1b[%dA",count);
else
rputs("\x1b[A");
} else {
if(term&PETSCII) {
for(int i=0;i<count;i++)
outcom(PETSCII_UP);
}
}
}
void sbbs_t::cursor_down(int count)
{
if(count<1)
return;
if(term_supports(ANSI)) {
if(count>1)
rprintf("\x1b[%dB",count);
else
rputs("\x1b[B");
} else {
for(int i=0;i<count;i++)
line_feed();
}
}
void sbbs_t::cursor_right(int count)
{
if(count<1)
return;
if(count>1)
rprintf("\x1b[%dC",count);
else
rputs("\x1b[C");
} else {
for(int i=0;i<count;i++) {
if(term&PETSCII)
outcom(PETSCII_RIGHT);
else
outcom(' ');
}
column+=count;
}
void sbbs_t::cursor_left(int count)
{
if(count<1)
return;
if(count>1)
rprintf("\x1b[%dD",count);
else
rputs("\x1b[D");
} else {
for(int i=0;i<count;i++) {
if(term&PETSCII)
outcom(PETSCII_LEFT);
else
outcom('\b');
}
if(column > count)
column-=count;
else
column=0;
}
void sbbs_t::cleartoeol(void)
{
int i,j;
rputs("\x1b[K");
else {
i=j=column;
while(++i<cols)
outcom(' ');
while(++j<cols) {
if(term&PETSCII)
outcom(PETSCII_LEFT);
else
outcom('\b');
}
}
}
void sbbs_t::cleartoeos(void)
{
if(term_supports(ANSI))
rputs("\x1b[J");
}
/****************************************************************************/
/* performs the correct attribute modifications for the Ctrl-A code */
/****************************************************************************/
void sbbs_t::ctrl_a(char x)
{
struct tm tm;
if(x && (uchar)x<=CTRL_Z) { /* Ctrl-A through Ctrl-Z for users with MF only */
if(!(useron.flags1&FLAG(x+64)))
console^=(CON_ECHO_OFF);
return;
}
if((uchar)x>0x7f) {
cursor_right((uchar)x-0x7f);
return;
if(isdigit(x)) { /* background color */
atr &= (BG_BRIGHT|BLINK|0x0f);
}
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
switch(toupper(x)) {
case '!': /* level 10 or higher */
if(useron.level<10)
console^=CON_ECHO_OFF;
break;
case '@': /* level 20 or higher */
if(useron.level<20)
console^=CON_ECHO_OFF;
break;
case '#': /* level 30 or higher */
if(useron.level<30)
console^=CON_ECHO_OFF;
break;
case '$': /* level 40 or higher */
if(useron.level<40)
console^=CON_ECHO_OFF;
break;
case '%': /* level 50 or higher */
if(useron.level<50)
console^=CON_ECHO_OFF;
break;
case '^': /* level 60 or higher */
if(useron.level<60)
console^=CON_ECHO_OFF;
break;
case '&': /* level 70 or higher */
if(useron.level<70)
console^=CON_ECHO_OFF;
break;
case '*': /* level 80 or higher */
if(useron.level<80)
console^=CON_ECHO_OFF;
break;
case '(': /* level 90 or higher */
if(useron.level<90)
console^=CON_ECHO_OFF;
break;
case ')': /* turn echo back on */
console&=~CON_ECHO_OFF;
break;
case '+': /* push current attribute */
attr_stack[attr_sp++]=curatr;
break;
case '-': /* pop current attribute OR optimized "normal" */
if(attr_sp>0)
attr(attr_stack[--attr_sp]);
else /* turn off all attributes if */
if(atr&(HIGH|BLINK|BG_LIGHTGRAY)) /* high intensity, blink or */
attr(LIGHTGRAY); /* background bits are set */
break;
case '_': /* turn off all attributes if */
if(atr&(BLINK|BG_LIGHTGRAY)) /* blink or background is set */
attr(LIGHTGRAY);
break;
case 'P': /* Pause */
pause();
break;
case 'Q': /* Pause reset */
lncntr=0;
break;
case 'T': /* Time */
now=time(NULL);
localtime_r(&now,&tm);
if(cfg.sys_misc&SM_MILITARY)
bprintf("%02u:%02u:%02u"
,tm.tm_hour, tm.tm_min, tm.tm_sec);
else
bprintf("%02d:%02d %s"
,tm.tm_hour==0 ? 12
: tm.tm_hour>12 ? tm.tm_hour-12
: tm.tm_hour, tm.tm_min, tm.tm_hour>11 ? "pm":"am");
break;
case 'D': /* Date */
now=time(NULL);
bputs(unixtodstr(&cfg,(time32_t)now,tmp1));
break;
case ',': /* Delay 1/10 sec */
mswait(100);
break;
case ';': /* Delay 1/2 sec */
mswait(500);
break;
case '.': /* Delay 2 secs */
mswait(2000);
break;
case 'S': /* Synchronize */
ASYNC;
break;
case 'J': /* clear to end-of-screen */
cleartoeos();
break;
case 'L': /* CLS (form feed) */
CLS;
break;
case '`': /* Home cursor */
cursor_home();
break;
cleartoeol();
break;
case '<': /* Non-destructive backspace */
case '/': /* Conditional new-line */
if(column > 0)
newline();
break;
case '\\': /* Conditional New-line / Continuation prefix (if cols < 80) */
if(column > 0 && cols < TERM_COLS_DEFAULT)
bputs(text[LongLineContinuationPrefix]);
case '?': /* Conditional blank-line */
if(column > 0)
case '[': /* Carriage return */
break;
case ']': /* Line feed */
break;
case 'A': /* Ctrl-A */
case 'Z': /* Ctrl-Z */
outchar(CTRL_Z);
break;
case 'H': /* High intensity */
atr|=HIGH;
attr(atr);
break;
case 'I':
if((term_supports()&(ICE_COLOR|PETSCII)) != ICE_COLOR)
attr(atr|BLINK);
break;
case 'E': /* Bright Background */
if(term_supports()&(ICE_COLOR|PETSCII))
attr(atr|BG_BRIGHT);
case 'F': /* Blink, only if alt Blink Font is loaded */
if(((atr&HIGH) && (console&CON_HBLINK_FONT)) || (!(atr&HIGH) && (console&CON_BLINK_FONT)))
attr(atr|BLINK);
else if(x == 'F' && !(atr&HIGH)) /* otherwise, set HIGH attribute (only if capital 'F') */
attr(atr|HIGH);
break;
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
case 'N': /* Normal */
attr(LIGHTGRAY);
break;
case 'R':
atr=(atr&0xf8)|RED;
attr(atr);
break;
case 'G':
atr=(atr&0xf8)|GREEN;
attr(atr);
break;
case 'B':
atr=(atr&0xf8)|BLUE;
attr(atr);
break;
case 'W': /* White */
atr=(atr&0xf8)|LIGHTGRAY;
attr(atr);
break;
case 'C':
atr=(atr&0xf8)|CYAN;
attr(atr);
break;
case 'M':
atr=(atr&0xf8)|MAGENTA;
attr(atr);
break;
case 'Y': /* Yellow */
atr=(atr&0xf8)|BROWN;
attr(atr);
break;
case 'K': /* Black */
atr=(atr&0xf8)|BLACK;
attr(atr);
break;
case '0': /* Black Background */
attr(atr);
break;
case '1': /* Red Background */
break;
case '2': /* Green Background */
break;
case '3': /* Yellow Background */
break;
case '4': /* Blue Background */
break;
case '5': /* Magenta Background */
break;
case '6': /* Cyan Background */
break;
case '7': /* White Background */
break;
}
/****************************************************************************/
/* Sends terminal control codes to change remote terminal colors/attributes */
/****************************************************************************/
int sbbs_t::attr(int atr)
int newatr = atr;
long term = term_supports();
if(term&PETSCII) {
if(atr&(0x70|BG_BRIGHT)) { // background color (reverse video for PETSCII)
if(atr&BG_BRIGHT)
atr |= HIGH;
else
atr &= ~HIGH;
atr = (atr&(BLINK|HIGH)) | ((atr&0x70)>>4);
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
outcom(PETSCII_REVERSE_ON);
} else
outcom(PETSCII_REVERSE_OFF);
if(atr&BLINK)
outcom(PETSCII_FLASH_ON);
else
outcom(PETSCII_FLASH_OFF);
switch(atr&0x0f) {
case BLACK:
outcom(PETSCII_BLACK);
break;
case WHITE:
outcom(PETSCII_WHITE);
break;
case DARKGRAY:
outcom(PETSCII_DARKGRAY);
break;
case LIGHTGRAY:
outcom(PETSCII_LIGHTGRAY);
break;
case BLUE:
outcom(PETSCII_BLUE);
break;
case LIGHTBLUE:
outcom(PETSCII_LIGHTBLUE);
break;
case CYAN:
outcom(PETSCII_MEDIUMGRAY);
break;
case LIGHTCYAN:
outcom(PETSCII_CYAN);
break;
case YELLOW:
outcom(PETSCII_YELLOW);
break;
case BROWN:
outcom(PETSCII_BROWN);
break;
case RED:
outcom(PETSCII_RED);
break;
case LIGHTRED:
outcom(PETSCII_LIGHTRED);
break;
case GREEN:
outcom(PETSCII_GREEN);
break;
case LIGHTGREEN:
outcom(PETSCII_LIGHTGREEN);
break;
case MAGENTA:
outcom(PETSCII_ORANGE);
break;
case LIGHTMAGENTA:
outcom(PETSCII_PURPLE);
break;
}
}
else if(term&ANSI)
rputs(ansi(newatr,curatr,str));
curatr=newatr;
return 0;
}
/****************************************************************************/
/* Checks to see if user has hit Pause or Abort. Returns 1 if user aborted. */
/* If the user hit Pause, waits for a key to be hit. */
/* Emulates remote XON/XOFF flow control on local console */
/* Preserves SS_ABORT flag state, if already set. */
/* Called from various listing procedures that wish to check for abort */
/****************************************************************************/
bool sbbs_t::msgabort()
{

rswindell
committed
static ulong counter;
if(sys_status&SS_SYSPAGE && !(++counter%100))
sbbs_beep(sbbs_random(800),1);

rswindell
committed
checkline();
if(sys_status&SS_ABORT)
return(true);
if(!online)
return(true);
return(false);
}
int sbbs_t::backfill(const char* instr, float pct, int full_attr, int empty_attr)
char* str = strip_ctrl(instr, NULL);
if(!term_supports(ANSI))
bputs(str);
else {
for(int i=0; i<len; i++) {
if(((float)(i+1) / len)*100.0 <= pct)
atr = full_attr;
else
atr = empty_attr;
if(curatr != atr) attr(atr);
outchar(str[i]);
}
attr(save_atr);
free(str);
void sbbs_t::progress(const char* text, int count, int total, int interval)
{
char str[128];
if(cfg.node_num == 0)
return; // Don't output this for events
if((count%interval) != 0)
return;
if(text == NULL) text = "";
float pct = ((float)count/total)*100.0F;
SAFEPRINTF2(str, "[ %-8s %4.1f%% ]", text, pct);
cursor_left(backfill(str, pct, cfg.color[clr_progress_full], cfg.color[clr_progress_empty]));
}
struct savedline {
char buf[LINE_BUFSIZE+1]; /* Line buffer (i.e. ANSI-encoded) */
uint beg_attr; /* Starting attribute of each line */
uint end_attr; /* Ending attribute of each line */
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
long column; /* Current column number */
};
bool sbbs_t::saveline(void)
{
struct savedline line;
line.beg_attr = latr;
line.end_attr = curatr;
line.column = column;
snprintf(line.buf, sizeof(line.buf), "%.*s", lbuflen, lbuf);
TERMINATE(line.buf);
lbuflen=0;
return listPushNodeData(&savedlines, &line, sizeof(line)) != NULL;
}
bool sbbs_t::restoreline(void)
{
struct savedline* line = (struct savedline*)listPopNode(&savedlines);
if(line == NULL)
return false;
lbuflen=0;
attr(line->beg_attr);
rputs(line->buf);
curatr = line->end_attr;
column = line->column;
free(line);
return true;
}