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 *
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
* *
* 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++;
ctrl_a(str[l++]);
continue;
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 */
continue;
for(i=0;i<TOTAL_TEXT;i++)
if(str==text[i])
break;
if(i<TOTAL_TEXT) { /* Replacement text */
continue;
}
outchar(str[l++]);
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
/* Perform PETSCII terminal output translation */
static unsigned char petscii(unsigned char ch)
{
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;
}
/****************************************************************************/
/* 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(len==0)
len=strlen(str);
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) */
char ch = str[l];
if(term&PETSCII)
ch = petscii(ch);
if(outcom(ch)!=0)
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 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(void)
{
if(!(console&CON_ECHO_OFF)) {
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;
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 */
/* Performs column counting, line counting, and auto-pausing */
/* Performs saveline buffering (for restoreline) */
/****************************************************************************/
void 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
*/
if(console&CON_ECHO_OFF)
return;
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;
}
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) {
if(!(sys_status&SS_PAUSEOFF)) {
pause();
while(lncntr && online && !(sys_status&SS_ABORT))
pause();
if(online==ON_REMOTE && console&CON_R_ECHO) {
if(console&CON_R_ECHOX && (uchar)ch>=' ' && !outchar_esc) {
ch=text[YNQP][3];
if(text[YNQP][2]==0 || ch==0) ch='X';

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) */
if(term&PETSCII) {
char pet = petscii(ch);
if(pet == PETSCII_SOLID)
outcom(PETSCII_REVERSE_ON);
outcom(pet);
if(pet == PETSCII_SOLID)
outcom(PETSCII_REVERSE_OFF);
} else
outcom(ch);
if(!outchar_esc) {
if((uchar)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;
else if(ch=='\b') {
if(column)
column--;
}
}
if(lncntr || lastlinelen)
lncntr++;
column=0;
} else if(ch==FF) {
lncntr=0;
lbuflen=0;
tos=1;
column=0;
} else {
if(!lbuflen)
latr=curatr;
if(lbuflen<LINE_BUFSIZE)
lbuf[lbuflen++]=ch;
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();
}
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(' ');
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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');
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)
{
char tmp1[128],atr=curatr;
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;
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
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;
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': /* 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;
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
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 */
atr=(atr&0x8f);
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;
break;
}
/****************************************************************************/
/* Sends terminal control codes to change remote terminal colors/attributes */
/****************************************************************************/
void sbbs_t::attr(int atr)
{
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
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()
{

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)
int atr;
int save_atr = curatr;
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]));
}
938
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
struct savedline {
char buf[LINE_BUFSIZE+1]; /* Line buffer (i.e. ANSI-encoded) */
char beg_attr; /* Starting attribute of each line */
char end_attr; /* Ending attribute of each line */
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;
}