Skip to content
Snippets Groups Projects
con_out.cpp 25.9 KiB
Newer Older
/* Synchronet console output 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 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.	*
 ****************************************************************************/


/**********************************************************************/
/* Functions that pertain to console i/o - color, strings, chars etc. */
/* Called from functions everywhere                                   */
/**********************************************************************/

#include "sbbs.h"

/****************************************************************************/
/* Outputs a NULL terminated string locally and remotely (if applicable)    */
/* Handles ctrl-a codes, Telnet-escaping, column & line count, auto-pausing */
/****************************************************************************/
int sbbs_t::bputs(const char *str)
	if(online==ON_LOCAL && console&CON_L_ECHO) 	/* script running as event */
		return(lputs(LOG_INFO, str));
	while(str[l] && online) {
		if(str[l]==CTRL_A && str[l+1]!=0) {
			l++;
			if(str[l] == 'Z')	/* EOF (uppercase 'Z' only) */
				break;
		if(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 */
			for(i=0;i<TOTAL_TEXT;i++)
				if(str==text[i])
					break;
			if(i<TOTAL_TEXT) {		/* Replacement text */
				i=show_atcode(str+l);
/* Perform PETSCII terminal output translation (from ASCII/CP437) */
unsigned char cp437_to_petscii(unsigned char ch)
rswindell's avatar
rswindell committed
{
	if(isalpha(ch))
		return ch ^ 0x20;	/* swap upper/lower case */
	switch(ch) {
		case '\1':		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;
		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;
}

/* 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;
}


/****************************************************************************/
/* Raw put string (remotely)												*/
/* Performs Telnet IAC escaping												*/
/* 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(console&CON_ECHO_OFF)
		return 0;
	if(len==0)
		len=strlen(str);
rswindell's avatar
rswindell committed
	long term = term_supports();
	for(l=0;l<len && online;l++) {
		if(str[l]==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
			outcom(TELNET_IAC);	/* Must escape Telnet IAC char (255) */
rswindell's avatar
rswindell committed
		if(term&PETSCII)
rswindell's avatar
rswindell committed
		if(outcom(ch)!=0)
rswindell's avatar
rswindell committed
			lbuf[lbuflen++] = ch;
	return(l);
}

/****************************************************************************/
/* Performs printf() using bbs bputs function								*/
/****************************************************************************/
int sbbs_t::bprintf(const char *fmt, ...)
deuce's avatar
deuce committed
	if(strchr(fmt,'%')==NULL)
		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 rputs function								*/
/****************************************************************************/
int sbbs_t::rprintf(const char *fmt, ...)

	va_start(argptr,fmt);
	vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;	/* force termination */
	va_end(argptr);
	return(rputs(sbuf));
}

/****************************************************************************/
rswindell's avatar
rswindell committed
/* Outputs destructive backspace 											*/
/****************************************************************************/
void sbbs_t::backspace(void)
{
	if(!(console&CON_ECHO_OFF)) {
rswindell's avatar
rswindell committed
		if(term_supports(PETSCII))
			outcom(PETSCII_DELETE);
		else {
			outcom('\b');
			outcom(' ');
			outcom('\b');
		}
/****************************************************************************/
/* 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;

	return(cmp_flags ? ((flags&cmp_flags)==cmp_flags) : (flags&TERM_FLAGS));
}

/****************************************************************************/
/* Outputs character														*/
/* Performs terminal translations (e.g. EXASCII-to-ASCII, FF->ESC[2J)		*/
/* Performs Telnet IAC escaping												*/
rswindell's avatar
rswindell committed
/* Performs tab expansion													*/
/* Performs column counting, line counting, and auto-pausing				*/
/* Performs saveline buffering (for restoreline)							*/
/****************************************************************************/
	/*
	 * 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
     */

	if(console&CON_ECHO_OFF)
		outchar_esc=1;
	else if(outchar_esc==1) {
deuce's avatar
deuce committed
		if(ch=='[')
			outchar_esc++;
		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;
deuce's avatar
deuce committed
		else
			outchar_esc=0;
	}
	else if(outchar_esc==2) {
	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;
	}
	else
		outchar_esc=0;
rswindell's avatar
rswindell committed
	long term = term_supports();
	if((term&(PETSCII|NO_EXASCII)) == NO_EXASCII && ch&0x80)
		ch=exascii_to_ascii_char(ch);  /* seven bit table */
	if(ch==FF && lncntr > 0 && !tos) {
rswindell's avatar
rswindell committed
		newline();
		if(!(sys_status&SS_PAUSEOFF)) {
			pause();
			while(lncntr && online && !(sys_status&SS_ABORT))

	if(online==ON_REMOTE && console&CON_R_ECHO) {
deuce's avatar
deuce committed
		if(console&CON_R_ECHOX && (uchar)ch>=' ' && !outchar_esc) {
			ch=text[YNQP][3];
			if(text[YNQP][2]==0 || ch==0) ch='X';
rswindell's avatar
rswindell committed
		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) {
				outcom(' ');
				column++;
			}
			if(ch==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
				outcom(TELNET_IAC);	/* Must escape Telnet IAC char (255) */
rswindell's avatar
rswindell committed
			if(term&PETSCII) {
rswindell's avatar
rswindell committed
				if(pet == PETSCII_SOLID)
					outcom(PETSCII_REVERSE_ON);
				outcom(pet);
				if(pet == PETSCII_SOLID)
					outcom(PETSCII_REVERSE_OFF);
			} else
				outcom(ch);
			if(column >= cols) {	// assume terminal has/will auto-line-wrap
				lncntr++;
				lbuflen = 0;
				tos = 0;
				lastlinelen = column;
				column = 0;
			}
		}
		else if(ch=='\r') {
			lastlinelen = column;
		if(lncntr || lastlinelen)
			lncntr++;
	} else if(ch==FF) {
		lncntr=0;
		lbuflen=0;
		tos=1;
	} else {
		if(!lbuflen)
			latr=curatr;
		if(lbuflen<LINE_BUFSIZE)
	if(outchar_esc==3)
		outchar_esc=0;
	if(lncntr==rows-1 && ((useron.misc&UPAUSE) || sys_status&SS_PAUSEON)
		&& !(sys_status&(SS_PAUSEOFF|SS_ABORT))) {
}

void sbbs_t::center(char *instr)
{
	char str[256];
	int i,j;

	truncsp(str);
	j=bstrlen(str);
rswindell's avatar
rswindell committed
	if(j < cols)
		for(i=0;i<(cols-j)/2;i++)
			outchar(' ');
rswindell's avatar
rswindell committed
	newline();
}

// Send a bare carriage return, hopefully moving the cursor to the far left, current row
void sbbs_t::carriage_return(void)
{
	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(void)
{
	if(term_supports(PETSCII))
		outcom(PETSCII_DOWN);
	else 
		outcom('\n');
}

void sbbs_t::newline(void)
{
	outchar('\r');
	outchar('\n');
rswindell's avatar
rswindell committed
	carriage_return();
rswindell's avatar
rswindell committed
	long term = term_supports();
	if(term&ANSI)
rswindell's avatar
rswindell committed
	else if(term&PETSCII)
		outcom(PETSCII_HOME);
		outchar(FF);	/* this will clear some terminals, do nothing with others */
rswindell's avatar
rswindell committed
	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);
		}
	}
rswindell's avatar
rswindell committed
	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();
	}
rswindell's avatar
rswindell committed
	long term = term_supports();
	if(term&ANSI) {
		if(count>1)
			rprintf("\x1b[%dC",count);
		else
			rputs("\x1b[C");
	} else {
rswindell's avatar
rswindell committed
		for(int i=0;i<count;i++) {
			if(term&PETSCII)
				outcom(PETSCII_RIGHT);
			else
				outcom(' ');
		}
rswindell's avatar
rswindell committed
	long term = term_supports();
	if(term&ANSI) {
		if(count>1)
			rprintf("\x1b[%dD",count);
		else
			rputs("\x1b[D");
	} else {
rswindell's avatar
rswindell committed
		for(int i=0;i<count;i++) {
			if(term&PETSCII)
				outcom(PETSCII_LEFT);
			else
				outcom('\b');
		}
rswindell's avatar
rswindell committed
	long term = term_supports();
	if(term&ANSI)
rswindell's avatar
rswindell committed
		while(++j<cols) {
			if(term&PETSCII)
				outcom(PETSCII_LEFT);
			else
				outcom('\b');
		}
rswindell's avatar
rswindell committed
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)
{
	char	tmp1[128],atr=curatr;
	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);
	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 */
rswindell's avatar
rswindell committed
			if(attr_sp<(int)sizeof(attr_stack))
				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);
			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;
rswindell's avatar
rswindell committed
		case 'J':	/* clear to end-of-screen */
			cleartoeos();
			break;
		case 'L':	/* CLS (form feed) */
			CLS;
			break;
rswindell's avatar
rswindell committed
		case '`':	/* Home cursor */
			cursor_home();
			break;
		case '>':   /* CLREOL */
			break;
		case '<':   /* Non-destructive backspace */
rswindell's avatar
rswindell committed
			cursor_left();
		case '/':	/* Conditional new-line */
			if(column > 0)
rswindell's avatar
rswindell committed
				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)
rswindell's avatar
rswindell committed
				newline();
rswindell's avatar
rswindell committed
				newline();
		case '[':   /* Carriage return */
rswindell's avatar
rswindell committed
			carriage_return();
			break;
		case ']':   /* Line feed */
rswindell's avatar
rswindell committed
			line_feed();
			break;
		case 'A':   /* Ctrl-A */
			outchar(CTRL_A);
		case 'Z':	/* Ctrl-Z */
			outchar(CTRL_Z);
			break;
		case 'H': 	/* High intensity */
			atr|=HIGH;
			attr(atr);
			break;
		case 'I':	/* Blink */
			atr|=BLINK;
			attr(atr);
			break;
		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;
		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 */
			atr=(atr&0x8f)|(uchar)BG_RED;
			attr(atr);
			break;
		case '2':	/* Green Background */
			atr=(atr&0x8f)|(uchar)BG_GREEN;
			attr(atr);
			break;
		case '3':	/* Yellow Background */
			atr=(atr&0x8f)|(uchar)BG_BROWN;
			attr(atr);
			break;
		case '4':	/* Blue Background */
			atr=(atr&0x8f)|(uchar)BG_BLUE;
			attr(atr);
			break;
		case '5':	/* Magenta Background */
			atr=(atr&0x8f)|(uchar)BG_MAGENTA;
			attr(atr);
			break;
		case '6':	/* Cyan Background */
			atr=(atr&0x8f)|(uchar)BG_CYAN;
			attr(atr);
			break;
		case '7':	/* White Background */
			atr=(atr&0x8f)|(uchar)BG_LIGHTGRAY;
}

/****************************************************************************/
rswindell's avatar
rswindell committed
/* Sends terminal control codes to change remote terminal colors/attributes */
/****************************************************************************/
rswindell's avatar
rswindell committed
	int		newatr = atr;

	long term = term_supports();
	if(term&PETSCII) {
		if(atr&0x70) {
			atr >>= 4;
			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;
}

/****************************************************************************/
/* 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()
{
	if(sys_status&SS_SYSPAGE && !(++counter%100))
		sbbs_beep(sbbs_random(800),1);
	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)
	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]);