|
|
1.1 root 1: /* con_out.cpp */
2:
3: /* Synchronet console output routines */
4:
1.1.1.2 ! root 5: /* $Id: con_out.cpp,v 1.67 2009/02/27 06:15:24 rswindell Exp $ */
1.1 root 6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
1.1.1.2 ! root 11: * Copyright 2009 Rob Swindell - http://www.synchro.net/copyright.html *
1.1 root 12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38:
39: /**********************************************************************/
40: /* Functions that pertain to console i/o - color, strings, chars etc. */
41: /* Called from functions everywhere */
42: /**********************************************************************/
43:
44: #include "sbbs.h"
45:
46: /****************************************************************************/
47: /* Outputs a NULL terminated string locally and remotely (if applicable) */
1.1.1.2 ! root 48: /* Handles ctrl-a codes, Telnet-escaping, column & line count, auto-pausing */
1.1 root 49: /****************************************************************************/
1.1.1.2 ! root 50: int sbbs_t::bputs(const char *str)
1.1 root 51: {
52: int i;
53: ulong l=0;
54:
55: if(online==ON_LOCAL && console&CON_L_ECHO) /* script running as event */
56: return(eprintf(LOG_INFO,"%s",str));
57:
1.1.1.2 ! root 58: while(str[l] && online) {
! 59: if(str[l]==CTRL_A && str[l+1]!=0) {
! 60: l++;
! 61: if(toupper(str[l])=='Z') /* EOF */
! 62: break;
! 63: ctrl_a(str[l++]);
1.1 root 64: continue;
65: }
66: if(str[l]=='@') { /* '@' */
67: if(str==mnestr /* Mnemonic string or */
68: || (str>=text[0] /* Straight out of TEXT.DAT */
69: && str<=text[TOTAL_TEXT-1])) {
70: i=show_atcode(str+l); /* return 0 if not valid @ code */
71: l+=i; /* i is length of code string */
72: if(i) /* if valid string, go to top */
73: continue;
74: }
75: for(i=0;i<TOTAL_TEXT;i++)
76: if(str==text[i])
77: break;
78: if(i<TOTAL_TEXT) { /* Replacement text */
79: i=show_atcode(str+l);
80: l+=i;
81: if(i)
82: continue;
83: }
84: }
85: outchar(str[l++]);
86: }
87: return(l);
88: }
89:
90: /****************************************************************************/
1.1.1.2 ! root 91: /* Raw put string (remotely) */
! 92: /* Performs Telnet IAC escaping */
! 93: /* Performs saveline buffering (for restoreline) */
! 94: /* DOES NOT expand ctrl-A codes, track colunms, lines, auto-pause, etc. */
1.1 root 95: /****************************************************************************/
1.1.1.2 ! root 96: int sbbs_t::rputs(const char *str, size_t len)
1.1 root 97: {
1.1.1.2 ! root 98: size_t l;
1.1 root 99:
1.1.1.2 ! root 100: if(console&CON_ECHO_OFF)
! 101: return 0;
! 102: if(len==0)
! 103: len=strlen(str);
! 104: for(l=0;l<len && online;l++) {
! 105: if(str[l]==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
! 106: outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */
! 107: if(outcom(str[l])!=0)
! 108: break;
! 109: if(lbuflen<LINE_BUFSIZE)
! 110: lbuf[lbuflen++]=str[l];
! 111: }
1.1 root 112: return(l);
113: }
114:
115: /****************************************************************************/
116: /* Performs printf() using bbs bputs function */
117: /****************************************************************************/
1.1.1.2 ! root 118: int sbbs_t::bprintf(const char *fmt, ...)
1.1 root 119: {
120: va_list argptr;
121: char sbuf[4096];
122:
123: if(strchr(fmt,'%')==NULL)
124: return(bputs(fmt));
125: va_start(argptr,fmt);
126: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
127: sbuf[sizeof(sbuf)-1]=0; /* force termination */
128: va_end(argptr);
129: return(bputs(sbuf));
130: }
131:
132: /****************************************************************************/
133: /* Performs printf() using bbs rputs function */
134: /****************************************************************************/
1.1.1.2 ! root 135: int sbbs_t::rprintf(const char *fmt, ...)
1.1 root 136: {
137: va_list argptr;
138: char sbuf[4096];
139:
140: va_start(argptr,fmt);
141: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
142: sbuf[sizeof(sbuf)-1]=0; /* force termination */
143: va_end(argptr);
144: return(rputs(sbuf));
145: }
146:
147: /****************************************************************************/
148: /* Outputs destructive backspace locally and remotely (if applicable), */
149: /****************************************************************************/
150: void sbbs_t::backspace(void)
151: {
1.1.1.2 ! root 152: if(!(console&CON_ECHO_OFF)) {
! 153: outcom('\b');
! 154: outcom(' ');
! 155: outcom('\b');
! 156: if(column)
! 157: column--;
! 158: }
1.1 root 159: }
160:
161: /****************************************************************************/
162: /* Returns true if the user (or the yet-to-be-logged-in client) supports */
163: /* all of the specified terminal 'cmp_flags' (e.g. ANSI, COLOR, RIP). */
164: /* If no flags specified, returns all terminal flag bits supported */
165: /****************************************************************************/
166: long sbbs_t::term_supports(long cmp_flags)
167: {
168: long flags = sys_status&SS_USERON ? useron.misc : autoterm;
169:
170: return(cmp_flags ? ((flags&cmp_flags)==cmp_flags) : (flags&TERM_FLAGS));
171: }
172:
173: /****************************************************************************/
1.1.1.2 ! root 174: /* Outputs character */
! 175: /* Performs terminal translations (e.g. EXASCII-to-ASCII, FF->ESC[2J) */
! 176: /* Performs Telnet IAC escaping */
! 177: /* Performs column counting, line counting, and auto-pausing */
! 178: /* Performs saveline buffering (for restoreline) */
1.1 root 179: /****************************************************************************/
180: void sbbs_t::outchar(char ch)
181: {
182: int i;
183:
184: if(console&CON_ECHO_OFF)
185: return;
186: if(ch==ESC)
187: outchar_esc=1;
188: else if(outchar_esc==1) {
189: if(ch=='[')
190: outchar_esc++;
191: else
192: outchar_esc=0;
193: }
194: else if(outchar_esc==2) {
1.1.1.2 ! root 195: if(ch>='@' && ch<='~')
1.1 root 196: outchar_esc++;
197: }
198: else
199: outchar_esc=0;
200: if(term_supports(NO_EXASCII) && ch&0x80)
1.1.1.2 ! root 201: ch=exascii_to_ascii_char(ch); /* seven bit table */
1.1 root 202: if(ch==FF && lncntr>1 && !tos) {
203: lncntr=0;
204: CRLF;
205: if(!(sys_status&SS_PAUSEOFF)) {
206: pause();
207: while(lncntr && online && !(sys_status&SS_ABORT))
208: pause();
209: }
210: }
211:
212: if(online==ON_REMOTE && console&CON_R_ECHO) {
213: if(console&CON_R_ECHOX && (uchar)ch>=' ' && !outchar_esc) {
214: ch=text[YN][3];
215: if(text[YN][2]==0 || ch==0) ch='X';
216: }
217: if(ch==FF && term_supports(ANSI)) {
218: putcom("\x1b[2J\x1b[H"); /* clear screen, home cursor */
219: }
220: else {
221: if(ch==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
222: outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */
223: i=0;
224: while(outcom(ch)&TXBOF && i<1440) { /* 3 minute pause delay */
225: if(!online)
226: break;
227: i++;
228: if(sys_status&SS_SYSPAGE)
229: sbbs_beep(i,80);
230: else
231: mswait(80);
232: }
233: if(i==1440) { /* timeout - beep flush outbuf */
234: i=rioctl(TXBC);
235: lprintf(LOG_NOTICE,"timeout(outchar) %04X %04X\r\n",i,rioctl(IOFO));
236: outcom(BEL);
237: rioctl(IOCS|PAUSE);
238: }
239: }
240: }
1.1.1.2 ! root 241: if(!outchar_esc) {
! 242: if((uchar)ch>=' ')
! 243: column++;
! 244: else if(ch=='\r')
! 245: column=0;
! 246: else if(ch=='\b') {
! 247: if(column)
! 248: column--;
! 249: }
! 250: }
! 251: if(ch==LF || column>=cols) {
1.1 root 252: lncntr++;
253: lbuflen=0;
254: tos=0;
1.1.1.2 ! root 255: column=0;
1.1 root 256: } else if(ch==FF) {
257: lncntr=0;
258: lbuflen=0;
259: tos=1;
1.1.1.2 ! root 260: column=0;
1.1 root 261: } else {
262: if(!lbuflen)
263: latr=curatr;
264: if(lbuflen<LINE_BUFSIZE)
265: lbuf[lbuflen++]=ch;
266: }
267: if(outchar_esc==3)
268: outchar_esc=0;
269:
270: if(lncntr==rows-1 && ((useron.misc&UPAUSE) || sys_status&SS_PAUSEON)
271: && !(sys_status&SS_PAUSEOFF)) {
272: lncntr=0;
273: pause();
274: }
275: }
276:
277: void sbbs_t::center(char *instr)
278: {
279: char str[256];
280: int i,j;
281:
282: SAFECOPY(str,instr);
283: truncsp(str);
284: j=bstrlen(str);
285: for(i=0;i<(cols-j)/2;i++)
286: outchar(' ');
287: bputs(str);
288: CRLF;
289: }
290:
291: void sbbs_t::clearline(void)
292: {
1.1.1.2 ! root 293: outcom(CR);
! 294: column=0;
! 295: cleartoeol();
1.1 root 296: }
297:
298: void sbbs_t::cursor_home(void)
299: {
300: if(term_supports(ANSI))
301: rputs("\x1b[H");
302: else
1.1.1.2 ! root 303: outchar(FF); /* this will clear some terminals, do nothing with others */
1.1 root 304: }
305:
306: void sbbs_t::cursor_up(int count)
307: {
308: if(count<1)
309: return;
310: if(!term_supports(ANSI))
311: return;
312: if(count>1)
313: rprintf("\x1b[%dA",count);
314: else
315: rputs("\x1b[A");
316: }
317:
318: void sbbs_t::cursor_down(int count)
319: {
320: if(count<1)
321: return;
322: if(!term_supports(ANSI))
323: return;
324: if(count>1)
325: rprintf("\x1b[%dB",count);
326: else
327: rputs("\x1b[B");
328: }
329:
330: void sbbs_t::cursor_right(int count)
331: {
332: if(count<1)
333: return;
334: if(term_supports(ANSI)) {
335: if(count>1)
336: rprintf("\x1b[%dC",count);
337: else
338: rputs("\x1b[C");
339: } else {
340: for(int i=0;i<count;i++)
1.1.1.2 ! root 341: outcom(' ');
1.1 root 342: }
1.1.1.2 ! root 343: column+=count;
1.1 root 344: }
345:
346: void sbbs_t::cursor_left(int count)
347: {
348: if(count<1)
349: return;
350: if(term_supports(ANSI)) {
351: if(count>1)
352: rprintf("\x1b[%dD",count);
353: else
354: rputs("\x1b[D");
355: } else {
356: for(int i=0;i<count;i++)
1.1.1.2 ! root 357: outcom('\b');
1.1 root 358: }
1.1.1.2 ! root 359: if(column > count)
! 360: column-=count;
! 361: else
! 362: column=0;
1.1 root 363: }
364:
365: void sbbs_t::cleartoeol(void)
366: {
1.1.1.2 ! root 367: int i,j;
! 368:
1.1 root 369: if(term_supports(ANSI))
370: rputs("\x1b[K");
371: else {
1.1.1.2 ! root 372: i=j=column;
! 373: while(++i<cols)
! 374: outcom(' ');
! 375: while(++j<cols)
! 376: outcom(BS);
1.1 root 377: }
378: }
379:
380: /****************************************************************************/
381: /* performs the correct attribute modifications for the Ctrl-A code */
382: /****************************************************************************/
383: void sbbs_t::ctrl_a(char x)
384: {
385: char tmp1[128],atr=curatr;
386: struct tm tm;
387:
388: if(x && (uchar)x<=CTRL_Z) { /* Ctrl-A through Ctrl-Z for users with MF only */
389: if(!(useron.flags1&FLAG(x+64)))
390: console^=(CON_ECHO_OFF);
391: return;
392: }
393: if((uchar)x>0x7f) {
394: cursor_right((uchar)x-0x7f);
395: return;
396: }
397: switch(toupper(x)) {
398: case '!': /* level 10 or higher */
399: if(useron.level<10)
400: console^=CON_ECHO_OFF;
401: break;
402: case '@': /* level 20 or higher */
403: if(useron.level<20)
404: console^=CON_ECHO_OFF;
405: break;
406: case '#': /* level 30 or higher */
407: if(useron.level<30)
408: console^=CON_ECHO_OFF;
409: break;
410: case '$': /* level 40 or higher */
411: if(useron.level<40)
412: console^=CON_ECHO_OFF;
413: break;
414: case '%': /* level 50 or higher */
415: if(useron.level<50)
416: console^=CON_ECHO_OFF;
417: break;
418: case '^': /* level 60 or higher */
419: if(useron.level<60)
420: console^=CON_ECHO_OFF;
421: break;
422: case '&': /* level 70 or higher */
423: if(useron.level<70)
424: console^=CON_ECHO_OFF;
425: break;
426: case '*': /* level 80 or higher */
427: if(useron.level<80)
428: console^=CON_ECHO_OFF;
429: break;
430: case '(': /* level 90 or higher */
431: if(useron.level<90)
432: console^=CON_ECHO_OFF;
433: break;
434: case ')': /* turn echo back on */
435: console&=~CON_ECHO_OFF;
436: break;
437: case '+': /* push current attribte */
438: if(attr_sp<(int)sizeof(attr_stack))
439: attr_stack[attr_sp++]=curatr;
440: break;
441: case '-': /* pop current attribute OR optimized "normal" */
442: if(attr_sp>0)
443: attr(attr_stack[--attr_sp]);
444: else /* turn off all attributes if */
445: if(atr&(HIGH|BLINK|BG_LIGHTGRAY)) /* high intensity, blink or */
446: attr(LIGHTGRAY); /* background bits are set */
447: break;
448: case '_': /* turn off all attributes if */
449: if(atr&(BLINK|BG_LIGHTGRAY)) /* blink or background is set */
450: attr(LIGHTGRAY);
451: break;
452: case 'P': /* Pause */
453: pause();
454: break;
455: case 'Q': /* Pause reset */
456: lncntr=0;
457: break;
458: case 'T': /* Time */
459: now=time(NULL);
460: localtime_r(&now,&tm);
461: bprintf("%02d:%02d %s"
462: ,tm.tm_hour==0 ? 12
463: : tm.tm_hour>12 ? tm.tm_hour-12
464: : tm.tm_hour, tm.tm_min, tm.tm_hour>11 ? "pm":"am");
465: break;
466: case 'D': /* Date */
467: now=time(NULL);
468: bputs(unixtodstr(&cfg,now,tmp1));
469: break;
470: case ',': /* Delay 1/10 sec */
471: mswait(100);
472: break;
473: case ';': /* Delay 1/2 sec */
474: mswait(500);
475: break;
476: case '.': /* Delay 2 secs */
477: mswait(2000);
478: break;
479: case 'S': /* Synchronize */
480: ASYNC;
481: break;
482: case 'L': /* CLS (form feed) */
483: CLS;
484: break;
485: case '>': /* CLREOL */
486: cleartoeol();
487: break;
488: case '<': /* Non-destructive backspace */
489: outchar(BS);
490: break;
491: case '[': /* Carriage return */
492: outchar(CR);
493: break;
494: case ']': /* Line feed */
495: outchar(LF);
496: break;
497: case 'A': /* Ctrl-A */
498: outchar(CTRL_A);
499: break;
500: case 'H': /* High intensity */
501: atr|=HIGH;
502: attr(atr);
503: break;
504: case 'I': /* Blink */
505: atr|=BLINK;
506: attr(atr);
507: break;
508: case 'N': /* Normal */
509: attr(LIGHTGRAY);
510: break;
511: case 'R':
512: atr=(atr&0xf8)|RED;
513: attr(atr);
514: break;
515: case 'G':
516: atr=(atr&0xf8)|GREEN;
517: attr(atr);
518: break;
519: case 'B':
520: atr=(atr&0xf8)|BLUE;
521: attr(atr);
522: break;
523: case 'W': /* White */
524: atr=(atr&0xf8)|LIGHTGRAY;
525: attr(atr);
526: break;
527: case 'C':
528: atr=(atr&0xf8)|CYAN;
529: attr(atr);
530: break;
531: case 'M':
532: atr=(atr&0xf8)|MAGENTA;
533: attr(atr);
534: break;
535: case 'Y': /* Yellow */
536: atr=(atr&0xf8)|BROWN;
537: attr(atr);
538: break;
539: case 'K': /* Black */
540: atr=(atr&0xf8)|BLACK;
541: attr(atr);
542: break;
543: case '0': /* Black Background */
544: atr=(atr&0x8f);
545: attr(atr);
546: break;
547: case '1': /* Red Background */
548: atr=(atr&0x8f)|(uchar)BG_RED;
549: attr(atr);
550: break;
551: case '2': /* Green Background */
552: atr=(atr&0x8f)|(uchar)BG_GREEN;
553: attr(atr);
554: break;
555: case '3': /* Yellow Background */
556: atr=(atr&0x8f)|(uchar)BG_BROWN;
557: attr(atr);
558: break;
559: case '4': /* Blue Background */
560: atr=(atr&0x8f)|(uchar)BG_BLUE;
561: attr(atr);
562: break;
563: case '5': /* Magenta Background */
564: atr=(atr&0x8f)|(uchar)BG_MAGENTA;
565: attr(atr);
566: break;
567: case '6': /* Cyan Background */
568: atr=(atr&0x8f)|(uchar)BG_CYAN;
569: attr(atr);
570: break;
571: case '7': /* White Background */
572: atr=(atr&0x8f)|(uchar)BG_LIGHTGRAY;
573: attr(atr);
574: break;
575: }
576: }
577:
578: /***************************************************************************/
579: /* Changes local and remote text attributes accounting for monochrome */
580: /***************************************************************************/
581: /****************************************************************************/
582: /* Sends ansi codes to change remote ansi terminal's colors */
583: /* Only sends necessary codes - tracks remote terminal's current attributes */
584: /* through the 'curatr' variable */
585: /****************************************************************************/
586: void sbbs_t::attr(int atr)
587: {
588: char str[16];
589:
590: if(!term_supports(ANSI))
591: return;
1.1.1.2 ! root 592: rputs(ansi(atr,curatr,str));
1.1 root 593: curatr=atr;
594: }
595:
596: /****************************************************************************/
597: /* Checks to see if user has hit Pause or Abort. Returns 1 if user aborted. */
598: /* If the user hit Pause, waits for a key to be hit. */
599: /* Emulates remote XON/XOFF flow control on local console */
600: /* Preserves SS_ABORT flag state, if already set. */
601: /* Called from various listing procedures that wish to check for abort */
602: /****************************************************************************/
603: bool sbbs_t::msgabort()
604: {
605: static ulong counter;
606:
607: if(sys_status&SS_SYSPAGE && !(++counter%100))
608: sbbs_beep(sbbs_random(800),1);
609:
610: checkline();
611: if(sys_status&SS_ABORT)
612: return(true);
613: if(!online)
614: return(true);
615: return(false);
616: }
617:
618:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.