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