con_out.cpp 26.9 KB
Newer Older
1
/* Synchronet console output routines */
2
// vi: tabstop=4
3
4
5
6
7
8
9

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * 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
 *																			*
 * 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"
44
#include "cp437_utf8_tbl.h"
45
46
47

/****************************************************************************/
/* Outputs a NULL terminated string locally and remotely (if applicable)    */
48
/* Handles ctrl-a codes, Telnet-escaping, column & line count, auto-pausing */
49
/****************************************************************************/
50
int sbbs_t::bputs(const char *str)
51
52
53
54
{
	int i;
    ulong l=0;

55
	if(online==ON_LOCAL && console&CON_L_ECHO) 	/* script running as event */
56
		return(lputs(LOG_INFO, str));
57

58
	while(str[l] && online) {
59
60
		if(str[l]==CTRL_A && str[l+1]!=0) {
			l++;
61
62
			if(str[l] == 'Z')	/* EOF (uppercase 'Z' only) */
				break;
63
			ctrl_a(str[l++]);
64
			continue;
65
		}
66
67
68
69
		if(str[l]=='@') {           /* '@' */
			if(str==mnestr			/* Mnemonic string or */
				|| (str>=text[0]	/* Straight out of TEXT.DAT */
					&& str<=text[TOTAL_TEXT-1])) {
70
				i=show_atcode(str+l);	/* return 0 if not valid @ code */
71
72
				l+=i;					/* i is length of code string */
				if(i)					/* if valid string, go to top */
73
					continue;
74
			}
75
76
77
78
			for(i=0;i<TOTAL_TEXT;i++)
				if(str==text[i])
					break;
			if(i<TOTAL_TEXT) {		/* Replacement text */
79
				i=show_atcode(str+l);
80
81
				l+=i;
				if(i)
82
83
					continue;
			}
84
		}
85
		outchar(str[l++]);
86
	}
87
88
89
	return(l);
}

90
91
/* Perform PETSCII terminal output translation (from ASCII/CP437) */
unsigned char cp437_to_petscii(unsigned char ch)
rswindell's avatar
rswindell committed
92
93
94
95
96
{
	if(isalpha(ch))
		return ch ^ 0x20;	/* swap upper/lower case */
	switch(ch) {
		case '\1':		return '@';
97
98
99
100
101
102
103
		case '\x10':	return '>';
		case '\x11':	return '<';
		case '\x18':	
		case '\x1e':	return PETSCII_UPARROW;
		case '\x19':
		case '\x1f':	return 'V';
		case '\x1a':	return '>';
rswindell's avatar
rswindell committed
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
161
162
163
164
165
166
167
168
169
		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;
}

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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
245
246
247
248
249
/* 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;
}


250
/****************************************************************************/
251
252
253
/* Raw put string (remotely)												*/
/* Performs Telnet IAC escaping												*/
/* Performs saveline buffering (for restoreline)							*/
254
/* DOES NOT expand ctrl-A codes, track columns, lines, auto-pause, etc.     */
255
/****************************************************************************/
256
int sbbs_t::rputs(const char *str, size_t len)
257
{
258
259
    size_t	l;

260
261
	if(console&CON_ECHO_OFF)
		return 0;
262
263
	if(len==0)
		len=strlen(str);
rswindell's avatar
rswindell committed
264
	long term = term_supports();
265
266
267
	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) */
268
		uchar ch = str[l];
rswindell's avatar
rswindell committed
269
		if(term&PETSCII)
270
			ch = cp437_to_petscii(ch);
rswindell's avatar
rswindell committed
271
		if(outcom(ch)!=0)
272
			break;
273
		if(lbuflen<LINE_BUFSIZE)
rswindell's avatar
rswindell committed
274
			lbuf[lbuflen++] = ch;
275
	}
276
277
278
279
280
281
	return(l);
}

/****************************************************************************/
/* Performs printf() using bbs bputs function								*/
/****************************************************************************/
282
int sbbs_t::bprintf(const char *fmt, ...)
283
284
{
	va_list argptr;
285
	char sbuf[4096];
286

deuce's avatar
deuce committed
287
	if(strchr(fmt,'%')==NULL)
288
289
		return(bputs(fmt));
	va_start(argptr,fmt);
290
291
	vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;	/* force termination */
292
293
294
295
296
297
298
	va_end(argptr);
	return(bputs(sbuf));
}

/****************************************************************************/
/* Performs printf() using bbs rputs function								*/
/****************************************************************************/
299
int sbbs_t::rprintf(const char *fmt, ...)
300
301
{
	va_list argptr;
302
	char sbuf[4096];
303
304

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

311
/****************************************************************************/
rswindell's avatar
rswindell committed
312
/* Outputs destructive backspace 											*/
313
314
315
/****************************************************************************/
void sbbs_t::backspace(void)
{
316
	if(!(console&CON_ECHO_OFF)) {
rswindell's avatar
rswindell committed
317
318
319
320
321
322
323
		if(term_supports(PETSCII))
			outcom(PETSCII_DELETE);
		else {
			outcom('\b');
			outcom(' ');
			outcom('\b');
		}
324
325
326
		if(column)
			column--;
	}
327
328
}

329
330
331
332
333
334
335
/****************************************************************************/
/* 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)
{
336
	long flags = ((sys_status&SS_USERON) && !(useron.misc&AUTOTERM)) ? useron.misc : autoterm;
337

338
	if((sys_status&SS_USERON) && (useron.misc&AUTOTERM))
rswindell's avatar
rswindell committed
339
		flags |= useron.misc & (NO_EXASCII | SWAP_DELETE | COLOR | ICE_COLOR);
340

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

344
/****************************************************************************/
345
346
347
/* Outputs character														*/
/* Performs terminal translations (e.g. EXASCII-to-ASCII, FF->ESC[2J)		*/
/* Performs Telnet IAC escaping												*/
rswindell's avatar
rswindell committed
348
/* Performs tab expansion													*/
349
350
/* Performs column counting, line counting, and auto-pausing				*/
/* Performs saveline buffering (for restoreline)							*/
351
/****************************************************************************/
352
int sbbs_t::outchar(char ch)
353
{
354
355
356
357
358
359
360
361
362
363
364
	/*
	 * 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
     */

365
	if(console&CON_ECHO_OFF)
366
		return 0;
367
	if(ch==ESC && outchar_esc < 4)
368
369
		outchar_esc=1;
	else if(outchar_esc==1) {
deuce's avatar
deuce committed
370
371
		if(ch=='[')
			outchar_esc++;
372
373
374
375
		else if(ch=='_' || ch=='P' || ch == '^' || ch == ']')
			outchar_esc=4;
		else if(ch=='X')
			outchar_esc=5;
376
377
		else if(ch >= 0x40 && ch <= 0x5f)
			outchar_esc=3;
deuce's avatar
deuce committed
378
379
380
381
		else
			outchar_esc=0;
	}
	else if(outchar_esc==2) {
382
		if(ch>='@' && ch<='~')
383
			outchar_esc++;
384
	}
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
	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;
	}
403
404
	else
		outchar_esc=0;
rswindell's avatar
rswindell committed
405
	long term = term_supports();
406
407
408
409
410
411
412
	const char* utf8 = NULL;
	if(!(term&PETSCII)) {
		if((term&NO_EXASCII) && (ch&0x80))
			ch = exascii_to_ascii_char(ch);  /* seven bit table */
		else if(term&UTF8)
			utf8 = cp437_utf8_tbl[(uchar)ch];
	}
rswindell's avatar
rswindell committed
413

414
	if(ch==FF && lncntr > 0 && !tos) {
415
		lncntr=0;
rswindell's avatar
rswindell committed
416
		newline();
417
418
419
		if(!(sys_status&SS_PAUSEOFF)) {
			pause();
			while(lncntr && online && !(sys_status&SS_ABORT))
420
				pause();
421
422
		}
	}
423

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
	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) {
rswindell's avatar
rswindell committed
443
444
			outcom(' ');
			column++;
445
		}
446
	}
447
448
449
	else {
		if(ch==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
			outcom(TELNET_IAC);	/* Must escape Telnet IAC char (255) */
450
451
		if(ch == '\r' && (console&CON_CR_CLREOL))
			cleartoeol();
452
453
454
455
456
457
458
		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);
rswindell's avatar
rswindell committed
459
460
			if(ch == '\r' && (curatr&0xf0) != 0) // reverse video is disabled upon CR
				curatr >>= 4;
461
462
463
464
465
466
		} else {
			if(utf8 != NULL)
				putcom(utf8);
			else
				outcom(ch);
		}
467
	}
468
	if(!outchar_esc) {
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
		/* 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
494
				lastlinelen = column;
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
				column=0;
				break;
			default:
				column++;
				if(column >= cols) {	// assume terminal has/will auto-line-wrap
					lncntr++;
					lbuflen = 0;
					tos = 0;
					lastlinelen = column;
					column = 0;
				}
				if(!lbuflen)
					latr=curatr;
				if(lbuflen<LINE_BUFSIZE)
					lbuf[lbuflen++]=ch;
				break;
511
512
		}
	}
513
514
	if(outchar_esc==3)
		outchar_esc=0;
515

516
	if(lncntr==rows-1 && ((useron.misc&UPAUSE) || sys_status&SS_PAUSEON)
517
		&& !(sys_status&(SS_PAUSEOFF|SS_ABORT))) {
518
		lncntr=0;
519
		pause();
rswindell's avatar
rswindell committed
520
	}
521
	return 0;
522
523
524
525
526
527
528
}

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

529
	SAFECOPY(str,instr);
530
531
	truncsp(str);
	j=bstrlen(str);
rswindell's avatar
rswindell committed
532
533
534
	if(j < cols)
		for(i=0;i<(cols-j)/2;i++)
			outchar(' ');
535
	bputs(str);
rswindell's avatar
rswindell committed
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
	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');
562
563
}

564
565
void sbbs_t::clearline(void)
{
rswindell's avatar
rswindell committed
566
	carriage_return();
567
	cleartoeol();
568
569
570
571
}

void sbbs_t::cursor_home(void)
{
rswindell's avatar
rswindell committed
572
573
	long term = term_supports();
	if(term&ANSI)
574
		rputs("\x1b[H");
rswindell's avatar
rswindell committed
575
576
	else if(term&PETSCII)
		outcom(PETSCII_HOME);
577
	else
578
		outchar(FF);	/* this will clear some terminals, do nothing with others */
579
580
	tos=1;
	column=0;
581
582
583
584
}

void sbbs_t::cursor_up(int count)
{
585
586
	if(count<1)
		return;
rswindell's avatar
rswindell committed
587
588
589
590
591
592
593
594
595
596
597
598
	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);
		}
	}
599
600
601
602
}

void sbbs_t::cursor_down(int count)
{
603
604
	if(count<1)
		return;
rswindell's avatar
rswindell committed
605
606
607
608
609
610
611
612
613
	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();
	}
614
615
616
617
}

void sbbs_t::cursor_right(int count)
{
618
619
	if(count<1)
		return;
rswindell's avatar
rswindell committed
620
621
	long term = term_supports();
	if(term&ANSI) {
622
623
624
625
626
		if(count>1)
			rprintf("\x1b[%dC",count);
		else
			rputs("\x1b[C");
	} else {
rswindell's avatar
rswindell committed
627
628
629
630
631
632
		for(int i=0;i<count;i++) {
			if(term&PETSCII)
				outcom(PETSCII_RIGHT);
			else
				outcom(' ');
		}
633
	}
634
	column+=count;
635
636
637
638
}

void sbbs_t::cursor_left(int count)
{
639
640
	if(count<1)
		return;
rswindell's avatar
rswindell committed
641
642
	long term = term_supports();
	if(term&ANSI) {
643
644
645
646
647
		if(count>1)
			rprintf("\x1b[%dD",count);
		else
			rputs("\x1b[D");
	} else {
rswindell's avatar
rswindell committed
648
649
650
651
652
653
		for(int i=0;i<count;i++) {
			if(term&PETSCII)
				outcom(PETSCII_LEFT);
			else
				outcom('\b');
		}
654
	}
655
656
657
658
	if(column > count)
		column-=count;
	else
		column=0;
659
660
661
662
}

void sbbs_t::cleartoeol(void)
{
663
664
	int i,j;

rswindell's avatar
rswindell committed
665
666
	long term = term_supports();
	if(term&ANSI)
667
668
		rputs("\x1b[K");
	else {
669
		i=j=column;
670
		while(++i<cols)
671
			outcom(' ');
rswindell's avatar
rswindell committed
672
673
674
675
676
677
		while(++j<cols) {
			if(term&PETSCII)
				outcom(PETSCII_LEFT);
			else
				outcom('\b');
		}
678
679
	}
}
680

rswindell's avatar
rswindell committed
681
682
683
684
685
686
void sbbs_t::cleartoeos(void)
{
	if(term_supports(ANSI))
		rputs("\x1b[J");
}

687
688
689
690
691
/****************************************************************************/
/* performs the correct attribute modifications for the Ctrl-A code			*/
/****************************************************************************/
void sbbs_t::ctrl_a(char x)
{
rswindell's avatar
rswindell committed
692
693
	char	tmp1[128];
	uint	atr = curatr;
694
	struct	tm tm;
695

rswindell's avatar
rswindell committed
696
	if(x && (uchar)x<=CTRL_Z) {    /* Ctrl-A through Ctrl-Z for users with MF only */
697
698
		if(!(useron.flags1&FLAG(x+64)))
			console^=(CON_ECHO_OFF);
699
		return;
700
701
	}
	if((uchar)x>0x7f) {
702
		cursor_right((uchar)x-0x7f);
703
		return;
704
	}
rswindell's avatar
rswindell committed
705
706
707
	if(isdigit(x)) {	/* background color */
		atr &= (BG_BRIGHT|BLINK|0x0f);
	}
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
	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;
748
		case '+':	/* push current attribute */
rswindell's avatar
rswindell committed
749
			if(attr_sp<(int)sizeof(attr_stack))
750
751
752
753
754
755
756
757
				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 */
758
759
			break;
		case '_':								/* turn off all attributes if */
760
			if(atr&(BLINK|BG_LIGHTGRAY))		/* blink or background is set */
761
762
763
764
765
766
767
768
769
770
				attr(LIGHTGRAY);
			break;
		case 'P':	/* Pause */
			pause();
			break;
		case 'Q':   /* Pause reset */
			lncntr=0;
			break;
		case 'T':   /* Time */
			now=time(NULL);
771
			localtime_r(&now,&tm);
772
773
774
775
776
777
778
779
			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");
780
781
782
			break;
		case 'D':   /* Date */
			now=time(NULL);
783
			bputs(unixtodstr(&cfg,(time32_t)now,tmp1));
784
785
786
787
788
789
790
791
792
793
794
795
796
			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
797
798
799
		case 'J':	/* clear to end-of-screen */
			cleartoeos();
			break;
800
801
802
		case 'L':	/* CLS (form feed) */
			CLS;
			break;
rswindell's avatar
rswindell committed
803
804
805
		case '`':	/* Home cursor */
			cursor_home();
			break;
806
		case '>':   /* CLREOL */
807
			cleartoeol();
808
809
			break;
		case '<':   /* Non-destructive backspace */
rswindell's avatar
rswindell committed
810
			cursor_left();
811
			break;
812
813
		case '/':	/* Conditional new-line */
			if(column > 0)
rswindell's avatar
rswindell committed
814
815
816
817
818
				newline();
			break;
		case '\\':	/* Conditional New-line / Continuation prefix (if cols < 80) */
			if(column > 0 && cols < TERM_COLS_DEFAULT)
				bputs(text[LongLineContinuationPrefix]);
819
			break;
820
821
		case '?':	/* Conditional blank-line */
			if(column > 0)
rswindell's avatar
rswindell committed
822
				newline();
823
			if(lastlinelen)
rswindell's avatar
rswindell committed
824
				newline();
825
			break;
826
		case '[':   /* Carriage return */
rswindell's avatar
rswindell committed
827
			carriage_return();
828
829
			break;
		case ']':   /* Line feed */
rswindell's avatar
rswindell committed
830
			line_feed();
831
832
			break;
		case 'A':   /* Ctrl-A */
833
			outchar(CTRL_A);
834
			break;
835
836
837
		case 'Z':	/* Ctrl-Z */
			outchar(CTRL_Z);
			break;
838
839
840
841
		case 'H': 	/* High intensity */
			atr|=HIGH;
			attr(atr);
			break;
rswindell's avatar
rswindell committed
842
843
844
845
846
847
848
		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);
849
			break;
850
851
852
853
854
855
		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;
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
		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 */
rswindell's avatar
rswindell committed
895
			attr(atr | BG_RED);
896
897
			break;
		case '2':	/* Green Background */
rswindell's avatar
rswindell committed
898
			attr(atr | BG_GREEN);
899
900
			break;
		case '3':	/* Yellow Background */
rswindell's avatar
rswindell committed
901
			attr(atr | BG_BROWN);
902
903
			break;
		case '4':	/* Blue Background */
rswindell's avatar
rswindell committed
904
			attr(atr | BG_BLUE);
905
906
			break;
		case '5':	/* Magenta Background */
rswindell's avatar
rswindell committed
907
			attr(atr |BG_MAGENTA);
908
909
			break;
		case '6':	/* Cyan Background */
rswindell's avatar
rswindell committed
910
			attr(atr | BG_CYAN);
911
912
			break;
		case '7':	/* White Background */
rswindell's avatar
rswindell committed
913
			attr(atr | BG_LIGHTGRAY);
914
			break;
915
	}
916
917
918
}

/****************************************************************************/
rswindell's avatar
rswindell committed
919
/* Sends terminal control codes to change remote terminal colors/attributes */
920
/****************************************************************************/
921
int sbbs_t::attr(int atr)
922
{
923
	char	str[16];
rswindell's avatar
rswindell committed
924
925
926
927
	int		newatr = atr;

	long term = term_supports();
	if(term&PETSCII) {
rswindell's avatar
rswindell committed
928
929
930
931
932
933
		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);
rswindell's avatar
rswindell committed
934
935
936
937
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
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
			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;
995
	return 0;
996
997
998
999
1000
}

/****************************************************************************/
/* 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.                        */
For faster browsing, not all history is shown. View entire blame