[REACTOS]
[reactos.git] / reactos / base / applications / network / telnet / src / ansiprsr.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998-2000 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25
26 ///////////////////////////////////////////////////////////////////////////////
27 //
28 // Module: ansiprsr.cpp
29 //
30 // Contents: ANSI parser base class
31 //
32 // Product: telnet
33 //
34 // Revisions: August 30, 1998 Paul Brannan <pbranna@clemson.edu>
35 // July 29, 1998 pbranna@clemson.edu
36 // June 15, 1998 pbranna@clemson.edu
37 // May 19, 1998 pbranna@clemson.edu
38 // 24 Dec, 1997 Andrey.V.Smilianets
39 // 05. Sep.1997 roryt@hol.gr (I.Ioannou)
40 // 11.May.1997 roryt@hol.gr (I.Ioannou)
41 // 6.April.1997 roryt@hol.gr (I.Ioannou)
42 // 5.April.1997 jbj@nounname.com
43 // 30.M\84rz.1997 Titus_Boxberg@public.uni-hamburg.de
44 // 14.Sept.1996 jbj@nounname.com
45 // Version 2.0
46 //
47 // 13.Jul.1995 igor.milavec@uni-lj.si
48 // Original code
49 //
50 ///////////////////////////////////////////////////////////////////////////////
51
52 //#include <windows.h>
53 #include "precomp.h"
54
55 const int ANSIColors[] = {BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE};
56
57 // The constructor now takes different arguments and initializes different
58 // variables (Paul Brannan 6/15/98)
59 TANSIParser::TANSIParser(TConsole &RefConsole, KeyTranslator &RefKeyTrans,
60 TScroller &RefScroller, TNetwork &RefNetwork,
61 TCharmap &RefCharmap):
62 TParser(RefConsole, RefKeyTrans, RefScroller, RefNetwork, RefCharmap) {
63 Init();
64 iSavedAttributes = (unsigned char) 7;
65 // must also check to make sure the string is non-NULL
66 // (Paul Brannan 5/8/98)
67 if ((ini.get_dumpfile() != NULL) && (*ini.get_dumpfile() != '\0')){
68 dumpfile = fopen(ini.get_dumpfile(), "wb");
69 }else {
70 dumpfile = NULL;
71 }
72 InPrintMode = 0;
73 printfile = NULL;
74
75 fast_write = ini.get_fast_write(); // Paul Brannan 6/28/98
76 Scroller.init(&StripBuffer);
77 }
78
79 TANSIParser::~TANSIParser(){
80 if (dumpfile) fclose (dumpfile);
81 // Added I.Ioannou 06 April, 1997
82 if (printfile != NULL) fclose (printfile);
83 }
84
85 // Created Init() function to initialize the parser but not clear the screen
86 // (Paul Brannan 9/23/98)
87 void TANSIParser::Init() {
88 // Paul Brannan 6/25/98
89 map_G0 = 'B'; map_G1 = 'B';
90 Charmap.setmap(map_G0);
91 current_map = 'B';
92
93 ignore_margins = 0;
94 vt52_mode = 0;
95 print_ctrl = 0;
96 newline_mode = false;
97
98 KeyTrans.clear_ext_mode();
99
100 iSavedCurY = 0; // Reset Variables
101 iSavedCurX = 0;
102 inGraphMode = 0;
103 Console.SetScroll(-1, -1);
104 Console.Normal(); // Reset Attributes
105
106 // Set tabs stops
107 resetTabStops();
108 }
109
110 void TANSIParser::ResetTerminal() {
111 Init();
112 Console.ClearScreen(); // Clear Screen
113 Console.SetRawCursorPosition(0,0); // Home Cursor
114 }
115 void TANSIParser::SaveCurY(int iY){
116 iSavedCurY=iY;
117 }
118
119 void TANSIParser::SaveCurX(int iX){
120 iSavedCurX=iX;
121 }
122
123 void TANSIParser::resetTabStops() {
124 for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
125 tab_stops[j] = 8 + j - (j%8);
126 }
127 }
128
129 void TANSIParser::ConSetAttribute(unsigned char TextAttrib){
130 // Paul Brannan 5/8/98
131 // Made this go a little bit faster by changing from switch{} to an array
132 // for the colors
133 if(TextAttrib >= 30) {
134 if(TextAttrib <= 37) {
135 Console.SetForeground(ANSIColors[TextAttrib-30]);
136 return;
137 } else if((TextAttrib >= 40) && (TextAttrib <= 47)) {
138 Console.SetBackground(ANSIColors[TextAttrib-40]);
139 return;
140 }
141 }
142
143 switch (TextAttrib){
144 // Text Attributes
145 case 0: Console.Normal(); break; // Normal video
146 case 1: Console.HighVideo(); break; // High video
147 case 2: Console.LowVideo(); break; // Low video
148 case 4: Console.UnderlineOn(); break; // Underline on (I.Ioannou)
149 case 5: Console.BlinkOn(); break; // Blink video
150 // Corrected by I.Ioannou 11 May, 1997
151 case 7: Console.ReverseOn(); break; // Reverse video
152 case 8: break; // hidden
153 // All from 10 thru 27 are hacked from linux kernel
154 // I.Ioannou 06 April, 1997
155 case 10:
156 // I.Ioannou 04 Sep 1997 turn on/off high bit
157 inGraphMode = 0;
158 print_ctrl = 0;
159 Charmap.setmap(current_map ? map_G1:map_G0); // Paul Brannan 6/25/98
160 break; // ANSI X3.64-1979 (SCO-ish?)
161 // Select primary font,
162 // don't display control chars
163 // if defined, don't set
164 // bit 8 on output (normal)
165 case 11:
166 inGraphMode = 0;
167 print_ctrl = 1;
168 Charmap.setmap(0); // Paul Brannan 6/25/98
169 break; // ANSI X3.64-1979 (SCO-ish?)
170 // Select first alternate font,
171 // let chars < 32 be displayed
172 // as ROM chars
173 case 12:
174 inGraphMode = 1;
175 print_ctrl = 1;
176 Charmap.setmap(0); // Paul Brannan 6/25/98
177 break; // ANSI X3.64-1979 (SCO-ish?)
178 // Select second alternate font,
179 // toggle high bit before
180 // displaying as ROM char.
181
182 case 21: // not really Low video
183 case 22: Console.LowVideo(); break; // but this works good also
184 case 24: Console.UnderlineOff(); break; // Underline off
185 case 25: Console.BlinkOff(); break; // blink off
186 // Corrected by I.Ioannou 11 May, 1997
187 case 27: Console.ReverseOff(); break; //Reverse video off
188
189 // Mutt needs this (Paul Brannan, Peter Jordan 12/31/98)
190 // This is from the Linux kernel source
191 case 38: /* ANSI X3.64-1979 (SCO-ish?)
192 * Enables underscore, white foreground
193 * with white underscore (Linux - use
194 * default foreground).
195 */
196 Console.UnderlineOn();
197 Console.SetForeground(ini.get_normal_fg());
198 break;
199 case 39: /* ANSI X3.64-1979 (SCO-ish?)
200 * Disable underline option.
201 * Reset colour to default? It did this
202 * before...
203 */
204 Console.UnderlineOff();
205 Console.SetForeground(ini.get_normal_fg());
206 break;
207 case 49:
208 Console.SetBackground(ini.get_normal_bg());
209 break;
210
211 }
212 }
213
214 void TANSIParser::ConSetCursorPos(int x, int y) {
215 if(ignore_margins)
216 Console.SetRawCursorPosition(x, y);
217 else
218 Console.SetCursorPosition(x, y);
219 }
220
221 const char* TANSIParser::GetTerminalID()
222 {
223 return "\033[?1;2c";
224 }
225
226 // All of the Telnet protocol stuff has been moved to TTelHndl.cpp
227 // This is more consistent with what OO should be
228 // (Paul Brannan 6/15/98)
229
230 #ifdef __BORLANDC__
231 // argsused doesn't work on MSVC++
232 #pragma argsused
233 #endif
234
235 // Use this for the VT100 flags (Paul Brannan 12/2/98)
236 #define FLAG_DOLLAR 0x0001
237 #define FLAG_QMARK 0x0002
238 #define FLAG_GREATER 0x0004
239 #define FLAG_LESS 0x0008
240 #define FLAG_EXCLAM 0x0010
241 #define FLAG_AMPERSAND 0x0020
242 #define FLAG_SLASH 0x0040
243 #define FLAG_EQUAL 0x0080
244 #define FLAG_QUOTE 0x0100
245 #define FLAG_OTHER 0x8000
246
247 char* TANSIParser::ParseEscapeANSI(char* pszBuffer, char* pszBufferEnd)
248 {
249
250 // The buffer contains something like <ESC>[pA
251 // where p is an optional decimal number specifying the count by which the
252 // appropriate action should take place.
253 // The pointer pszBuffer points us to the p, <ESC> and [ are
254 // already 'consumed'
255
256 // TITUS: Simplification of the code: Assume default count of 1 in case
257 // there are no parameters.
258 char tmpc;
259 const int nParam = 10; // Maximum number of parameters
260 int iParam[nParam] = {1, 0, 0, 0, 0}; // Assume 1 Parameter, Default 1
261 int iCurrentParam = 0;
262 DWORD flag = 0;
263 int missing_param = 0;
264
265 // Get parameters from escape sequence.
266 while ((tmpc = *pszBuffer) <= '?') {
267
268 if(tmpc < '0' || tmpc > '9') {
269 // Check for parameter delimiter.
270 if(tmpc == ';') {
271 // This is a hack (Paul Brannan 6/27/98)
272 if(*(pszBuffer - 1) == '[') missing_param = iCurrentParam+1;
273 pszBuffer++;
274 continue;
275 }
276
277 // It is legal to have control characters inside ANSI sequences
278 // (Paul Brannan 6/26/98)
279 if(tmpc < ' ') {
280 Console.WriteCtrlChar(tmpc);
281 pszBuffer++;
282 continue;
283 }
284
285 // A new way of handling flags (Paul Brannan 12/2/98)
286 switch(tmpc) {
287 case '$': flag |= FLAG_DOLLAR; break;
288 case '?': flag |= FLAG_QMARK; break;
289 case '>': flag |= FLAG_GREATER; break;
290 case '<': flag |= FLAG_LESS; break;
291 case '!': flag |= FLAG_EXCLAM; break;
292 case '&': flag |= FLAG_AMPERSAND; break;
293 case '/': flag |= FLAG_SLASH; break;
294 case '=': flag |= FLAG_EQUAL; break;
295 case '\"': flag |= FLAG_QUOTE; break;
296 default: flag |= FLAG_OTHER; break;
297 }
298
299 pszBuffer++;
300 }
301
302 // Got Numerical Parameter.
303 iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
304 if (iCurrentParam < nParam)
305 iCurrentParam++;
306 }
307
308 //~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
309 // So: If there is no digit, assume a count of 1
310
311 switch ((unsigned char)*pszBuffer++) {
312 // Insert Character
313 case '@':
314 if(iParam[0] == 0) iParam[0] = 1; // Paul Brannan 9/1/98
315 Console.InsertCharacter(iParam[0]); break;
316 // Move cursor up.
317 case 'A':
318 if(iParam[0] == 0) iParam[0] = 1;
319 Console.MoveCursorPosition(0, -iParam[0]); break;
320 // Move cursor down.
321 // Added by I.Ioannou 06 April, 1997
322 case 'B':
323 case 'e':
324 if(iParam[0] == 0) iParam[0] = 1;
325 Console.MoveCursorPosition(0, iParam[0]);
326 break;
327 // Move cursor right.
328 // Added by I.Ioannou 06 April, 1997
329 case 'C':
330 case 'a':
331 // Handle cursor size sequences (Jose Cesar Otero Rodriquez and
332 // Paul Brannan, 3/27/1999)
333 if(flag & FLAG_EQUAL) {
334 switch(iParam[0]) {
335 case 7: Console.SetCursorSize(50); break;
336 case 11: Console.SetCursorSize(6); break;
337 case 32: Console.SetCursorSize(0); break;
338 default: Console.SetCursorSize(13);
339 }
340 } else {
341 if(iParam[0] == 0) iParam[0] = 1;
342 Console.MoveCursorPosition(iParam[0], 0);
343 break;
344 }
345 // Move cursor left.
346 case 'D':
347 if(iParam[0] == 0) iParam[0] = 1;
348 Console.MoveCursorPosition(-iParam[0], 0);
349 break;
350 // Move cursor to beginning of line, p lines down.
351 // Added by I.Ioannou 06 April, 1997
352 case 'E':
353 Console.MoveCursorPosition(-Console.GetCursorX(), iParam[0]);
354 break;
355 // Moves active position to beginning of line, p lines up
356 // Added by I.Ioannou 06 April, 1997
357 // With '=' this changes the default fg color (Paul Brannan 6/27/98)
358 case 'F':
359 if(flag & FLAG_EQUAL)
360 Console.setDefaultFg(iParam[0]);
361 else
362 Console.MoveCursorPosition(-Console.GetCursorX(), -iParam[0]);
363 break;
364 // Go to column p
365 // Added by I.Ioannou 06 April, 1997
366 // With '=' this changes the default bg color (Paul Brannan 6/27/98)
367 case '`':
368 case 'G': // 'G' is from Linux kernel sources
369 if(flag & FLAG_EQUAL) {
370 Console.setDefaultBg(iParam[0]);
371 } else {
372 if (iCurrentParam < 1) // Alter Default
373 iParam[0] = 0;
374 // this was backward, and we should subtract 1 from x
375 // (Paul Brannan 5/27/98)
376 ConSetCursorPos(iParam[0] - 1, Console.GetCursorY());
377 }
378 break;
379 // Set cursor position.
380 case 'f':
381 case 'H':
382 if (iCurrentParam < 2 || iParam[1] < 1)
383 iParam[1] = 1;
384 ConSetCursorPos(iParam[1] - 1, iParam[0] - 1);
385 break;
386 // Clear screen
387 case 'J':
388 if ( iCurrentParam < 1 ) iParam[0] = 0; // Alter Default
389 switch (iParam[0]) {
390 case 0: Console.ClearEOScreen(); break;
391 case 1: Console.ClearBOScreen(); break;
392 case 2:
393 Console.ClearScreen();
394 Console.SetRawCursorPosition(0, 0);
395 break;
396 }
397 break;
398 // Clear line
399 case 'K':
400 if (iCurrentParam < 1) // Alter Default
401 iParam[0] = 0;
402 switch (iParam[0]) {
403 case 0: Console.ClearEOLine(); break;
404 case 1: Console.ClearBOLine(); break;
405 case 2: Console.ClearLine(); break;
406 }
407 break;
408 // Insert p new, blank lines.
409 // Added by I.Ioannou 06 April, 1997
410 case 'L':
411 {
412 // for (int i = 1; i <= iParam[0]; i++)
413 // This should speed things up a bit (Paul Brannan 9/2/98)
414 Console.ScrollDown(Console.GetRawCursorY(), -1, iParam[0]);
415 break;
416 }
417 // Delete p lines.
418 // Added by I.Ioannou 06 April, 1997
419 case 'M':
420 {
421 for (int i = 1; i <= iParam[0]; i++)
422 // This should speed things up a bit (Paul Brannan 9/2/98)
423 Console.ScrollDown(Console.GetRawCursorY(), -1, -1);
424 break;
425 }
426 // DELETE CHAR
427 case 'P':
428 Console.DeleteCharacter(iParam[0]);
429 break;
430 // Scrolls screen up (down? -- PB) p lines,
431 // Added by I.Ioannou 06 April, 1997
432 // ANSI X3.64-1979 references this but I didn't
433 // found it in any telnet implementation
434 // note 05 Oct 97 : but SCO terminfo uses them, so uncomment them !!
435 case 'S':
436 {
437 //for (int i = 1; i <= iParam[0]; i++)
438 // This should speed things up a bit (Paul Brannan 9/2/98)
439 Console.ScrollDown(-1, -1, -iParam[0]);
440 break;
441 }
442 // Scrolls screen up p lines,
443 // Added by I.Ioannou 06 April, 1997
444 // ANSI X3.64-1979 references this but I didn't
445 // found it in any telnet implementation
446 // note 05 Oct 97 : but SCO terminfo uses them, so uncomment them !!
447 case 'T':
448 {
449 // for (int i = 1; i <= iParam[0]; i++)
450 // This should speed things up a bit (Paul Brannan 9/2/98)
451 Console.ScrollDown(-1, -1, iParam[0]);
452 break;
453 }
454 // Erases p characters up to the end of line
455 // Added by I.Ioannou 06 April, 1997
456 case 'X':
457 {
458 int iKeepX = Console.GetRawCursorX();
459 int iKeepY = Console.GetRawCursorY();
460 if (iParam[0] > Console.GetWidth())
461 iParam[0] = Console.GetWidth(); // up to the end of line
462 for ( int i = 1; i <= iParam[0]; i++ )
463 Console.WriteString(" ", 1);
464 Console.SetRawCursorPosition(iKeepX , iKeepY);
465 break;
466 }
467 // Go back p tab stops
468 // Added by I.Ioannou 06 April, 1997
469 // Implemented by Paul Brannan, 4/13/2000
470 case 'Z':
471 {
472 int x = Console.GetCursorX();
473 for(int j = 0; x > 0 && j < iParam[0]; j++)
474 while(x > 0 && tab_stops[j] == tab_stops[x]) x--;
475 Console.SetCursorPosition(x, Console.GetCursorY());
476 }
477 break;
478 // Get Terminal ID
479 case 'c':
480 {
481 const char* szTerminalId = GetTerminalID();
482 Network.WriteString(szTerminalId, strlen(szTerminalId));
483 break;
484 }
485 // TITUS++ 2. November 1998: Repeat Character.
486 case 'b':
487 // isprint may be causing problems (Paul Brannan 3/27/99)
488 // if ( isprint(last_char) ) {
489 char buf[150]; // at most 1 line (max 132 chars)
490
491 if ( iParam[0] > 149 ) iParam[0] = 149;
492 memset(buf, last_char, iParam[0]);
493 buf[iParam[0]] = 0;
494 if ( fast_write )
495 Console.WriteStringFast(buf, iParam[0]);
496 else
497 Console.WriteString(buf, iParam[0]);
498 // } /* IF */
499 break;
500 // Go to line p
501 // Added by I.Ioannou 06 April, 1997
502 case 'd':
503 if (iCurrentParam < 1) // Alter Default
504 iParam[0] = 0;
505 // this was backward, and we should subtract 1 from y
506 // (Paul Brannan 5/27/98)
507 ConSetCursorPos(Console.GetCursorX(), iParam[0] - 1);
508 break;
509 // iBCS2 tab erase
510 // Added by I.Ioannou 06 April, 1997
511 case 'g':
512 if (iCurrentParam < 1) // Alter Default
513 iParam[0] = 0;
514 switch (iParam[0]) {
515 case 0:
516 {
517 // Clear the horizontal tab stop at the current active position
518 for(int j = 0; j < MAX_TAB_POSITIONS; j++) {
519 int x = Console.GetCursorX();
520 if(tab_stops[j] == x) tab_stops[j] = tab_stops[x + 1];
521 }
522 }
523 break;
524 case 2:
525 // I think this might be "set as default?"
526 break;
527 case 3:
528 {
529 // Clear all tab stops
530 for(int j = 0; j < MAX_TAB_POSITIONS; j++)
531 tab_stops[j] = -1;
532 }
533 break;
534 }
535 break;
536 // Set extended mode
537 case 'h':
538 {
539 for (int i = 0; i < iCurrentParam; i++) {
540 // Changed to a switch statement (Paul Brannan 5/27/98)
541 if(flag & FLAG_QMARK) {
542 switch(iParam[i]) {
543 case 1: // App cursor keys
544 KeyTrans.set_ext_mode(APP_KEY);
545 break;
546 case 2: // VT102 mode
547 vt52_mode = 0;
548 KeyTrans.unset_ext_mode(APP2_KEY);
549 break;
550 case 3: // 132 columns
551 if(ini.get_wide_enable()) {
552 Console.SetWindowSize(132, -1);
553 }
554 break;
555 case 4: // smooth scrolling
556 break;
557 case 5: // Light background
558 Console.Lightbg();
559 break;
560 case 6: // Stay in margins
561 ignore_margins = 0;
562 break;
563 case 7:
564 Console.setLineWrap(true);
565 break;
566 case 8: // Auto-repeat keys
567 break;
568 case 18: // Send FF to printer
569 break;
570 case 19: // Entire screen legal for printer
571 break;
572 case 25: // Visible cursor
573 break;
574 case 66: // Application numeric keypad
575 break;
576 default:
577 #ifdef DEBUG
578 Console.Beep();
579 #endif
580 break;
581 }
582 } else {
583 switch(iParam[i]) {
584 case 2: // Lock keyboard
585 break;
586 case 3: // Act upon control codes (PB 12/5/98)
587 print_ctrl = 0;
588 break;
589 case 4: // Set insert mode
590 Console.InsertMode(1);
591 break;
592 case 12: // Local echo off
593 break;
594 case 20: // Newline sends cr/lf
595 KeyTrans.set_ext_mode(APP4_KEY);
596 newline_mode = true;
597 break;
598 default:
599 #ifdef DEBUG
600 Console.Beep();
601 #endif
602 break;
603 }
604 }
605 }
606 }
607 break;
608 // Print Screen
609 case 'i':
610 if (iCurrentParam < 1)
611 iParam[0]=0;
612 switch (iParam[0]){
613 case 0: break; // Print Screen
614 case 1: break; // Print Line
615 // Added I.Ioannou 06 April, 1997
616 case 4:
617 // Stop Print Log
618 InPrintMode = 0;
619 if ( printfile != NULL )
620 fclose(printfile);
621 break;
622 case 5:
623 // Start Print Log
624 printfile = fopen(ini.get_printer_name(), "ab");
625 if (printfile != NULL) InPrintMode = 1;
626 break;
627 }
628 break;
629 // Unset extended mode
630 case 'l':
631 {
632 for (int i = 0; i < iCurrentParam; i++) {
633 // Changed to a switch statement (Paul Brannan 5/27/98)
634 if(flag & FLAG_QMARK) {
635 switch(iParam[i]) {
636 case 1: // Numeric cursor keys
637 KeyTrans.unset_ext_mode(APP_KEY);
638 break;
639 case 2: // VT52 mode
640 vt52_mode = 1;
641 KeyTrans.set_ext_mode(APP2_KEY);
642 break;
643 case 3: // 80 columns
644 if(ini.get_wide_enable()) {
645 Console.SetWindowSize(80, -1);
646 }
647 break;
648 case 4: // jump scrolling
649 break;
650 case 5: // Dark background
651 Console.Darkbg();
652 break;
653 case 6: // Ignore margins
654 ignore_margins = 1;
655 break;
656 case 7:
657 Console.setLineWrap(false);
658 break;
659 case 8: // Auto-repeat keys
660 break;
661 case 19: // Only send scrolling region to printer
662 break;
663 case 25: // Invisible cursor
664 break;
665 case 66: // Numeric keypad
666 break;
667 default:
668 #ifdef DEBUG
669 Console.Beep();
670 #endif
671 break;
672 }
673 } else {
674 switch(iParam[i]) {
675 case 2: // Unlock keyboard
676 break;
677 case 3: // Display control codes (PB 12/5/98)
678 print_ctrl = 1;
679 break;
680 case 4: // Set overtype mode
681 Console.InsertMode(0);
682 break;
683 case 12: // Local echo on
684 break;
685 case 20: // sends lf only
686 KeyTrans.unset_ext_mode(APP4_KEY);
687 newline_mode = false;
688 break;
689 default:
690 #ifdef DEBUG
691 Console.Beep();
692 #endif
693 break;
694 }
695 }
696 }
697 }
698 break;
699 // Set color
700 case 'm':
701 if(missing_param) Console.Normal();
702 if(iCurrentParam == 0) {
703 Console.Normal();
704 } else {
705 for(int i = 0; i < iCurrentParam; i++)
706 ConSetAttribute(iParam[i]);
707 }
708 break;
709 // report cursor position Row X Col
710 case 'n':
711 if (iCurrentParam == 1 && iParam[0]==5) {
712 // report the cursor position
713 Network.WriteString("\x1B[0n", 6);
714 break;
715 }
716 if (iCurrentParam == 1 && iParam[0]==6){
717 // report the cursor position
718 // The cursor position needs to be sent as a single string
719 // (Paul Brannan 6/27/98)
720 char szCursorReport[40] = "\x1B[";
721
722 itoa(Console.GetCursorY() + 1,
723 &szCursorReport[strlen(szCursorReport)], 10);
724 strcat(szCursorReport, ";");
725 itoa(Console.GetCursorX() + 1,
726 &szCursorReport[strlen(szCursorReport)], 10);
727 strcat(szCursorReport, "R");
728
729 Network.WriteString(szCursorReport, strlen(szCursorReport));
730
731 }
732 break;
733 // Miscellaneous weird sequences (Paul Brannan 6/27/98)
734 case 'p':
735 // Set conformance level
736 if(flag & FLAG_QUOTE) {
737 break;
738 }
739 // Soft terminal reset
740 if(flag & FLAG_EXCLAM) {
741 break;
742 }
743 // Report mode settings
744 if(flag & FLAG_DOLLAR) {
745 break;
746 }
747 break;
748 // Scroll Screen
749 case 'r':
750 if (iCurrentParam < 1) {
751 // Enable scrolling for entire display
752 Console.SetScroll(-1, -1);
753 break;
754 }
755 if (iCurrentParam >1) {
756 // Enable scrolling from row1 to row2
757 Console.SetScroll(iParam[0] - 1, iParam[1] - 1);
758 // If the cursor is outside the scrolling range, fix it
759 // (Paul Brannan 6/26/98)
760 // if(Console.GetRawCursorY() < iParam[0] - 1) {
761 // Console.SetRawCursorPosition(Console.GetCursorX(),
762 // iParam[0] - 1);
763 // }
764 // if(Console.GetRawCursorY() > iParam[1] - 1) {
765 // Console.SetRawCursorPosition(Console.GetCursorX(),
766 // iParam[1] - 1);
767 // }
768 }
769 // Move the cursor to the home position (Paul Brannan 12/2/98)
770 Console.SetCursorPosition(0, 0);
771 break;
772 // Save cursor position
773 case 's':
774 SaveCurY(Console.GetRawCursorY());
775 SaveCurX(Console.GetRawCursorX());
776 break;
777 // Restore cursor position
778 case 'u':
779 Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
780 break;
781 // DEC terminal report (Paul Brannan 6/28/98)
782 case 'x':
783 if(iParam[0])
784 Network.WriteString("\033[3;1;1;128;128;1;0x", 20);
785 else
786 Network.WriteString("\033[2;1;1;128;128;1;0x", 20);
787 break;
788 default:
789 #ifdef DEBUG
790 Console.Beep();
791 #endif
792 break;
793 }
794
795 return pszBuffer;
796 }
797
798 #ifdef MTE_SUPPORT
799 // Added by Frediano Ziglio, 5/31/2000
800 // MTE extension
801 // initially copied from ParseEscapeANSI
802 char* TANSIParser::ParseEscapeMTE(char* pszBuffer, char* pszBufferEnd)
803 {
804 // The buffer contains something like <ESC>~pA
805 // where p is an optional decimal number specifying the count by which the
806 // appropriate action should take place.
807 // The pointer pszBuffer points us to the p, <ESC> and ~ are
808 // already 'consumed'
809 // TITUS: Simplification of the code: Assume default count of 1 in case
810 // there are no parameters.
811 char tmpc;
812 const int nParam = 10; // Maximum number of parameters
813 int iParam[nParam] = {1, 0, 0, 0, 0}; // Assume 1 parameter, Default 1
814 int iCurrentParam = 0;
815 char sRepeat[2];
816
817 // Get parameters from escape sequence.
818 while ((tmpc = *pszBuffer) <= '?') {
819 if(tmpc < '0' || tmpc > '9') {
820 // Check for parameter delimiter.
821 if(tmpc == ';') {
822 pszBuffer++;
823 continue;
824 }
825 pszBuffer++;
826 }
827
828 // Got Numerical Parameter.
829 iParam[iCurrentParam] = strtoul(pszBuffer, &pszBuffer, 10);
830 if (iCurrentParam < nParam)
831 iCurrentParam++;
832 }
833
834 //~~~ TITUS: Apparently the digit is optional (look at termcap or terminfo)
835 // So: If there is no digit, assume a count of 1
836
837 switch ((unsigned char)*pszBuffer++) {
838 case 'A':
839 // set colors
840 if (iCurrentParam < 2 )
841 break;
842 if (iParam[0] <= 15 && iParam[1] <= 15)
843 Console.SetAttrib( (iParam[1] << 4) | iParam[0] );
844 break;
845
846 case 'R':
847 // define region
848 mteRegionXF = -1;
849 if (iCurrentParam < 2 )
850 break;
851 mteRegionXF = iParam[1]-1;
852 mteRegionYF = iParam[0]-1;
853 break;
854
855 case 'F':
856 // fill with char
857 {
858 if (mteRegionXF == -1 || iCurrentParam < 1)
859 break;
860 sRepeat[0] = (char)iParam[0];
861 sRepeat[1] = '\0';
862 int xi = Console.GetCursorX(),yi = Console.GetCursorY();
863 int xf = mteRegionXF;
864 int yf = mteRegionYF;
865 mteRegionXF = -1;
866 for(int y=yi;y<=yf;++y)
867 {
868 Console.SetCursorPosition(xi,y);
869 for(int x=xi;x<=xf;++x)
870
871 Console.WriteStringFast(sRepeat,1);
872 }
873 }
874 break;
875
876 case 'S':
877 // Scroll region
878 {
879 if (mteRegionXF == -1 || iCurrentParam < 2)
880 break;
881 int /*x = Console.GetCursorX(),*/y = Console.GetCursorY();
882 // int xf = mteRegionXF;
883 int yf = mteRegionYF;
884 mteRegionXF = -1;
885 // !!! don't use x during scroll
886 int diff = (iParam[0]-1)-y;
887 if (diff<0)
888 Console.ScrollDown(y-1,yf,diff);
889 else
890 Console.ScrollDown(y,yf+1,diff);
891 }
892 break;
893 // Meridian main version ??
894 case 'x':
895 // disable echo and line mode
896 Network.set_local_echo(0);
897 Network.set_line_mode(0);
898 // Meridian Server handle cursor itself
899 Console.SetCursorSize(0);
900 break;
901 // query ??
902 case 'Q':
903 if (iParam[0] == 1)
904 Network.WriteString("\033vga.",5);
905 break;
906 default:
907 #ifdef DEBUG
908 Console.Beep();
909 #endif
910 break;
911 }
912
913 return pszBuffer;
914 }
915 #endif
916
917 char* TANSIParser::ParseEscape(char* pszBuffer, char* pszBufferEnd) {
918 char *pszChar;
919
920 // Check if we have enough characters in buffer.
921 if ((pszBufferEnd - pszBuffer) < 2)
922 return pszBuffer;
923
924 // I.Ioannou 04 Sep 1997
925 // there is no need for pszBuffer++; after each command
926
927 // Decode the command.
928 pszBuffer++;
929
930 switch (*pszBuffer++) {
931 case 'A': // Cursor up
932 Console.MoveCursorPosition(0, -1);
933 break;
934 // Cursor down
935 case 'B':
936 Console.MoveCursorPosition(0, 1);
937 break;
938 // Cursor right
939 case 'C':
940 Console.MoveCursorPosition(1, 0);
941 break;
942 // LF *or* cursor left (Paul Brannan 6/27/98)
943 case 'D':
944 if(vt52_mode)
945 Console.MoveCursorPosition(-1, 0);
946 else
947 Console.index();
948 break;
949 // CR/LF (Paul Brannan 6/26/98)
950 case 'E':
951 Console.WriteCtrlString("\r\n", 2);
952 break;
953 // Special graphics char set (Paul Brannan 6/27/98)
954 case 'F':
955 Charmap.setmap('0');
956 break;
957 // ASCII char set (Paul Brannan 6/27/98)
958 case 'G':
959 Charmap.setmap('B');
960 break;
961 // Home cursor/tab set
962 case 'H':
963 if(ini.get_vt100_mode()) {
964 int x = Console.GetCursorX();
965 if(x != 0) {
966 int t = tab_stops[x - 1];
967 for(int j = x - 1; j >= 0 && tab_stops[j] == t; j--)
968 tab_stops[j] = x;
969 }
970 } else {
971 // I.Ioannou 04 Sep 1997 (0,0) not (1,1)
972 ConSetCursorPos(0, 0);
973 }
974 break;
975 // Reverse line feed (Paul Brannan 6/27/98)
976 // FIX ME!!! reverse_index is wrong to be calling here
977 // (Paul Brannan 12/2/98)
978 case 'I':
979 Console.reverse_index();
980 break;
981 // Erase end of screen
982 case 'J':
983 Console.ClearEOScreen();
984 break;
985 // Erase EOL
986 case 'K':
987 Console.ClearEOLine();
988 break;
989 // Scroll Up one line //Reverse index
990 case 'M':
991 Console.reverse_index();
992 break;
993 // Direct cursor addressing
994 case 'Y':
995 if ((pszBufferEnd - pszBuffer) >= 2){
996 // if we subtract '\x1F', then we may end up with a negative
997 // cursor position! (Paul Brannan 6/26/98)
998 ConSetCursorPos(pszBuffer[1] - ' ', pszBuffer[0] - ' ');
999 pszBuffer+=2;
1000 } else {
1001 pszBuffer--; // Paul Brannan 6/26/98
1002 }
1003 break;
1004 // Terminal ID Request
1005 case 'Z':
1006 {
1007 const char* szTerminalId = GetTerminalID();
1008 Network.WriteString(szTerminalId, strlen(szTerminalId));
1009 break;
1010 }
1011 // reset terminal to defaults
1012 case 'c':
1013 ResetTerminal();
1014 break;
1015 // Enter alternate keypad mode
1016 case '=':
1017 KeyTrans.set_ext_mode(APP3_KEY);
1018 break;
1019 // Exit alternate keypad mode
1020 case '>':
1021 KeyTrans.unset_ext_mode(APP3_KEY);
1022 break;
1023 // Enter ANSI mode
1024 case '<':
1025 KeyTrans.unset_ext_mode(APP2_KEY); // exit vt52 mode
1026 break;
1027 // Graphics processor on (See note 3)
1028 case '1':
1029 break;
1030 // Line size commands
1031 case '#': //Line size commands
1032 // (Paul Brannan 6/26/98)
1033 if(pszBuffer < pszBufferEnd) {
1034 switch(*pszBuffer++) {
1035 case '3': break; // top half of a double-height line
1036 case '4': break; // bottom half of a double-height line
1037 case '6': break; // current line becomes double-width
1038 case '8': Console.ClearScreen('E'); break;
1039 }
1040 } else {
1041 pszBuffer--;
1042 }
1043 break;
1044 // Graphics processor off (See note 3)
1045 case '2':
1046 break;
1047 // Save cursor and attribs
1048 case '7':
1049 SaveCurY(Console.GetRawCursorY());
1050 SaveCurX(Console.GetRawCursorX());
1051 iSavedAttributes = Console.GetAttrib();
1052 break;
1053 // Restore cursor position and attribs
1054 case '8':
1055 Console.SetRawCursorPosition(iSavedCurX, iSavedCurY);
1056 Console.SetAttrib(iSavedAttributes);
1057 break;
1058 // Set G0 map (Paul Brannan 6/25/98)
1059 case '(':
1060 if (pszBuffer < pszBufferEnd) {
1061 map_G0 = *pszBuffer;
1062 if(current_map == 0) Charmap.setmap(map_G0);
1063 pszBuffer++;
1064 } else {
1065 pszBuffer--;
1066 }
1067 break;
1068 // Set G1 map (Paul Brannan 6/25/98)
1069 case ')':
1070 if (pszBuffer < pszBufferEnd) {
1071 map_G1 = *pszBuffer;
1072 if(current_map == 1) Charmap.setmap(map_G1);
1073 pszBuffer++;
1074 } else {
1075 pszBuffer--;
1076 }
1077 break;
1078 // This doesn't do anything, as far as I can tell, but it does take
1079 // a parameter (Paul Brannan 6/27/98)
1080 case '%':
1081 if (pszBuffer < pszBufferEnd) {
1082 pszBuffer++;
1083 } else {
1084 pszBuffer--;
1085 }
1086 break;
1087 // ANSI escape sequence
1088 case '[':
1089 // Check if we have whole escape sequence in buffer.
1090 // This should not be isalpha anymore (Paul Brannan 9/1/98)
1091 pszChar = pszBuffer;
1092 while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
1093 pszChar++;
1094 if (pszChar == pszBufferEnd)
1095 pszBuffer -= 2;
1096 else
1097 pszBuffer = ParseEscapeANSI(pszBuffer, pszBufferEnd);
1098 break;
1099 #ifdef MTE_SUPPORT
1100 case '~':
1101 // Frediano Ziglio, 5/31/2000
1102 // Meridian Terminal Emulator extension
1103 // !!! same as ANSI
1104 // !!! should put in MTE procedure
1105 pszChar = pszBuffer;
1106 while ((pszChar < pszBufferEnd) && (*pszChar <= '?'))
1107 pszChar++;
1108 if (pszChar == pszBufferEnd)
1109 pszBuffer -= 2;
1110 else
1111 pszBuffer = ParseEscapeMTE(pszBuffer, pszBufferEnd);
1112 break;
1113 #endif
1114 default:
1115 #ifdef DEBUG
1116 Console.Beep();
1117 #endif
1118 break;
1119 }
1120
1121 return pszBuffer;
1122 }
1123
1124 // This function now only parses the ANSI buffer and does not do anything
1125 // with IAC sequences. That code has been moved to TTelHndl.cpp.
1126 // The scroller update routines have been moved to TScroll.cpp.
1127 // (Paul Brannan 6/15/98)
1128 char* TANSIParser::ParseBuffer(char* pszHead, char* pszTail){
1129 // copy into ANSI buffer
1130 char * pszResult;
1131
1132 // Parse the buffer for ANSI or display
1133 while (pszHead < pszTail) {
1134 if(!ini.get_output_redir()) {
1135 pszResult = ParseANSIBuffer(pszHead, pszTail);
1136 } else {
1137 // Output is being redirected
1138 if(ini.get_strip_redir()) {
1139 // Skip the WriteFile() altogether and pass the buffer to a filter
1140 // Mark Miesfield 09/24/2000
1141 pszResult = PrintGoodChars(pszHead, pszTail);
1142 } else {
1143 DWORD Result;
1144 // Paul Brannan 7/29/98
1145 // Note that this has the unforunate effect of printing out
1146 // NULL (ascii 0) characters onto the screen
1147 if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pszHead,
1148 pszTail - pszHead, &Result, NULL)) pszResult = pszHead;
1149 pszResult = pszHead + Result;
1150 }
1151 }
1152 if (dumpfile)
1153 fwrite( pszHead, sizeof (char), pszResult-pszHead, dumpfile);
1154 if(ini.get_scroll_enable()) Scroller.update(pszHead, pszResult);
1155 if (pszResult == pszHead) break;
1156 pszHead = pszResult;
1157 }
1158 // return the new head to the buffer
1159 return pszHead;
1160 }
1161
1162 // A simple routine to strip ANSI sequences
1163 // This isn't perfect, but it does an okay job (Paul Brannan 7/5/98)
1164 // Fixed a line counting bug (Paul Brannan 12/4/98)
1165 int TANSIParser::StripBuffer(char* pszHead, char* pszTail, int width) {
1166 int lines = 0, c = 0;
1167 char *pszBuf = pszHead;
1168
1169 while(pszHead < pszTail) {
1170 if(iscntrl(*pszHead)) {
1171 switch(*(pszHead++)) {
1172 case 8:
1173 case 127:
1174 if(c>0) {
1175 if(!(c%width)) lines--;
1176 c--;
1177 pszBuf--;
1178 }
1179 break;
1180 case 10: lines++;
1181 case 13:
1182 *(pszBuf++) = *(pszHead - 1);
1183 c = 0;
1184 break;
1185 case 27:
1186 switch(*(pszHead++)) {
1187 case 'Y': pszHead += 2; break;
1188 case '#':
1189 case '(':
1190 case ')':
1191 case '%': pszHead++; break;
1192 case '[':
1193 while((pszHead < pszTail) && (*pszHead < '?'))
1194 pszHead++;
1195 pszHead++;
1196 break;
1197 }
1198 }
1199 } else {
1200 *(pszBuf++) = *(pszHead++);
1201 c++;
1202 }
1203 if(c != 0 && !(c%width))
1204 lines++;
1205 }
1206
1207 // Fill in the end of the buffer with blanks
1208 while(pszBuf <= pszTail) *pszBuf++ = ' ';
1209
1210 return lines;
1211 }
1212
1213 char* TANSIParser::ParseANSIBuffer(char* pszBuffer, char* pszBufferEnd)
1214 {
1215 if(InPrintMode) {
1216 return PrintBuffer(pszBuffer, pszBufferEnd);
1217 }
1218
1219 unsigned char tmpc = *(unsigned char *)pszBuffer;
1220
1221 if(tmpc == 27) {
1222 return ParseEscape(pszBuffer, pszBufferEnd);
1223 }
1224
1225 // if((fast_write && tmpc < 32) ||
1226 // !print_ctrl && (tmpc < 32 || (EightBit_Ansi &&
1227 // (tmpc > 128 && tmpc < 128 + ' ')))) {
1228
1229 // We shouldn't print ctrl characters when fast write is enabled
1230 // and ctrl chars are disabled (Paul Brannan 9/1/98)
1231 if(tmpc < 32) {
1232 // From the Linux kernel (Paul Brannan 12/5/98):
1233 /* A bitmap for codes <32. A bit of 1 indicates that the code
1234 * corresponding to that bit number invokes some special action
1235 * (such as cursor movement) and should not be displayed as a
1236 * glyph unless the disp_ctrl mode is explicitly enabled.
1237 */
1238 const long CTRL_ACTION = 0x0d00ff81;
1239 const long CTRL_ALWAYS = 0x0800f501;
1240 if(!(((print_ctrl?CTRL_ALWAYS:CTRL_ACTION)>>tmpc)&1)) {
1241
1242 Console.WriteString((char *)&tmpc, 1);
1243 pszBuffer++;
1244 return pszBuffer;
1245 }
1246
1247 switch (tmpc) {
1248 case 0:
1249 pszBuffer++;
1250 break;
1251
1252 // I.Ioannou 5/30/98
1253 case 7:
1254 Console.Beep();
1255 pszBuffer++;
1256 break;
1257
1258 // destructive backspace
1259 case 8:
1260 // Added option for destructive backspace (Paul Brannan 5/13/98)
1261 // Changed to ConWriteCtrlString so that the cursor position can be
1262 // updated (Paul Brannan 5/25/98)
1263 if(ini.get_dstrbksp()) {
1264 Console.WriteCtrlChar('\b');
1265 Console.WriteString(" ", 1);
1266 Console.WriteCtrlChar('\b');
1267 }
1268 else Console.WriteCtrlChar('\b');
1269 pszBuffer++;
1270 break;
1271
1272 // horizontal tab
1273 case 9:
1274 {
1275 pszBuffer++;
1276 int x = Console.GetCursorX();
1277 if(x != -1)
1278 Console.SetCursorPosition(tab_stops[x], Console.GetCursorY());
1279 }
1280 break;
1281
1282 // Line Feed Char
1283 case 10:
1284 // Test for local echo (Paul Brannan 8/25/98)
1285 if(Network.get_local_echo() || newline_mode) // &&
1286 Console.WriteCtrlChar('\x0d');
1287 Console.WriteCtrlChar('\x0a');
1288 pszBuffer++;
1289 break;
1290
1291 // form feed
1292 case 12:
1293 pszBuffer++;
1294 Console.ClearScreen();
1295 Console.SetRawCursorPosition(Console.GetCursorX(), 1); // changed fm 1
1296 break;
1297
1298 case 13:
1299 Console.WriteCtrlChar('\x0d');
1300 pszBuffer++;
1301
1302 break;
1303
1304 case 14: // shift out of alternate chararcter set
1305 pszBuffer++;
1306 Charmap.setmap(map_G1); // Paul Brannan 6/25/98
1307 current_map = 1;
1308 break;
1309
1310 case 15: // shift in
1311 pszBuffer++;
1312 Charmap.setmap(map_G0); // Paul Brannan 6/25/98
1313 current_map = 0;
1314 break;
1315
1316 // Paul Brannan 9/1/98 - Is this okay?
1317 default:
1318 pszBuffer++;
1319 }
1320
1321 return pszBuffer;
1322 }
1323
1324 // added by I.Ioannou 06 April, 1997
1325 // In 8 bit systems the server may send 0x9b instead of ESC[
1326 // Well, this will produce troubles in Greek 737 Code page
1327 // which uses 0x9b as the small "delta" - and I thing that there
1328 // is another European country with the same problem.
1329 // If we have to stay 8-bit clean we may have to
1330 // give the ability of ROM characters (ESC[11m),
1331 // for striped 8'th bit (ESC[12m) as SCO does,
1332 // or a parameter at compile (or run ?) time.
1333 // We now check for a flag in the ini file (Paul Brannan 5/13/98)
1334 // We also handle any 8-bit ESC sequence (Paul Brannan 6/28/98)
1335 if(ini.get_eightbit_ansi() && (tmpc > 128 && tmpc < 128 + ' ')) {
1336 // There's a chance the sequence might not parse. If this happens
1337 // then pszBuffer will be one character too far back, since
1338 // ParseEscape is expecting two characters, not one.
1339 // In that case we must handle it.
1340 char *pszCurrent = pszBuffer;
1341 pszBuffer = ParseEscape(pszBuffer, pszBufferEnd);
1342 if(pszBuffer < pszCurrent) pszBuffer = pszCurrent;
1343 }
1344
1345 char* pszCurrent = pszBuffer + 1;
1346 // I.Ioannou 04 Sep 1997 FIXME with ESC[11m must show chars < 32
1347 // Fixed (Paul Brannan 6/28/98)
1348 while ((pszCurrent < pszBufferEnd) && (!iscntrl(*pszCurrent))) {
1349 // I.Ioannou 04 Sep 1997 strip on high bit
1350 if ( (inGraphMode) && (*pszCurrent > (char)32) )
1351 *pszCurrent |= 0x80 ;
1352 pszCurrent++;
1353 }
1354
1355 // Note that this may break dumpfiles slightly.
1356 // If 'B' is set to anything other than ASCII, this will cause problems
1357 // (Paul Brannan 6/28/98)
1358 if(current_map != 'B' && Charmap.enabled)
1359 Charmap.translate_buffer(pszBuffer, pszCurrent);
1360
1361 last_char = *(pszCurrent-1); // TITUS++: Remember last char
1362
1363 if(fast_write) {
1364 pszBuffer += Console.WriteStringFast(pszBuffer,
1365 pszCurrent - pszBuffer);
1366 } else {
1367 pszBuffer += Console.WriteString(pszBuffer,
1368 pszCurrent - pszBuffer);
1369 }
1370
1371 return pszBuffer;
1372 }
1373
1374 // Added by I.Ioannou 06 April, 1997
1375 // Print the buffer until you reach ESC[4i
1376 char* TANSIParser::PrintBuffer(char* pszBuffer, char* pszBufferEnd) {
1377 // Check if we have enough characters in buffer.
1378 if ((pszBufferEnd - pszBuffer) < 4)
1379 return pszBuffer;
1380 char *tmpChar;
1381
1382 tmpChar = pszBuffer;
1383 if ( *tmpChar == 27 ) {
1384 tmpChar++;
1385 if ( *tmpChar == '[' ) {
1386 tmpChar++;
1387 if ( *tmpChar == '4' ) {
1388 tmpChar++;
1389 if ( *tmpChar == 'i' ) {
1390 InPrintMode = 0; // Stop Print Log
1391 if ( printfile != NULL )
1392 fclose(printfile);
1393 pszBuffer += 4;
1394 return pszBuffer;
1395 }
1396 }
1397 }
1398 }
1399
1400 if (printfile != NULL) {
1401 fputc( *pszBuffer, printfile);
1402 pszBuffer++;
1403 } else
1404 InPrintMode = 0;
1405
1406 return pszBuffer;
1407 }
1408
1409 /* - PrintGoodChars( pszHead, pszTail ) - - - - - - - - - - - - - - - - - - -
1410 -*
1411
1412 Mark Miesfield 09/24/2000
1413
1414 Prints the characters in a buffer, from the specified head to the specified
1415 tail, to standard out, skipping any control characters or ANSI escape
1416 sequences.
1417
1418 Parameters on entry:
1419 pszHead -> Starting point in buffer.
1420
1421 pszTail -> Ending point in buffer.
1422
1423 Returns:
1424 Pointer to the first character in the buffer that was not output to
1425 standard out. (Since no error checking is done, this is in effect
1426 pszTail.)
1427
1428 Side Effects:
1429 None.
1430 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1431 */
1432 char * TANSIParser::PrintGoodChars( char * pszHead, char * pszTail ) {
1433
1434 while ( pszHead < pszTail ) {
1435 if ( iscntrl( *pszHead ) ) {
1436 switch ( *(pszHead++) ) {
1437 case 10 :
1438 putc( 10, stdout );
1439 break;
1440
1441 case 13 :
1442 putc( 13, stdout );
1443 break;
1444
1445 case 27:
1446 switch ( *(pszHead++) ) {
1447 case 'Y':
1448 pszHead += 2;
1449 break;
1450
1451 case '#':
1452 case '(':
1453 case ')':
1454 case '%': pszHead++; break;
1455 case '[':
1456 while ( (pszHead < pszTail) && (*pszHead < '?') )
1457 pszHead++;
1458 pszHead++;
1459 break;
1460
1461 default :
1462 break;
1463 }
1464 break;
1465
1466 default :
1467 break;
1468 }
1469 }
1470 else
1471 putc( *(pszHead++), stdout );
1472 }
1473 return ( pszTail );
1474 }
1475 // End of function: PrintGoodChars( pszHead, pszTail )