put the asterix and colon the right way round
[reactos.git] / posix / apps / posixw32 / vt100.c
1 /* vt100.c
2 *
3 * AUTHOR: John L. Miller, johnmil@cs.cmu.edu / johnmil@jprc.com
4 * DATE: 8/4/96
5 *
6 * Copyright (c) 1996 John L. Miller
7 *
8 * Full permission is granted to use, modify and distribute
9 * this code, provided:
10 * 1) This comment field is included in its entirity
11 * 2) No money is charged for any work including or based on
12 * portions of this code.
13 *
14 * If you're a nice person and find this useful, I'd appreciate a
15 * note letting me know about it. e-mail is usually what spurs me
16 * on to improve and support software I've written.
17 *
18 */
19
20 /* This is the main code body for my generic vt-100 emulator. This code
21 * body provides parsing for most of the vt-100 escape sequences, but it
22 * doesn't actually do anything with some of them. The idea behind this
23 * parser is to provide a generic front-end that you can initialize, then
24 * send all of your output to. The output is parsed by the routines in this
25 * program, then spit out to a back-end.
26 *
27 * What back-end you say? Well, the one you have to supply yourself. There's a
28 * dozen or so routines you have to provide for character-based I/O, cursor
29 * movement, erasing and deleting text, and setting text and terminal attributes.
30 *
31 * For a list of the routines your back end must supply, read vt100.h closely.
32 *
33 * In case it's not obvious, these routines were written for a system running win32.
34 * for vt100.c and vt100.h, most of the code should be easily portable to other
35 * operating systems. Yeah, like they NEED a vt-100 emulator :p
36 */
37
38 #include <windows.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include "vt100.h"
42
43
44 /* NOTE - many of the functions look like they should
45 * take X-Y pairs. Bear in mind this is a text display,
46 * so for this we're talking ROWS and COLUMNS. So parm
47 * lists go (row, column), which is the opposite of (x, y).
48 */
49
50 char cBuffer[MAXVTBUFLEN+1]; /* terminal output buffer for unprocessed characters */
51 int BufLen; /* Number of characters in cBuffer waiting for output */
52
53 /* List of all device-independant colors. These colors will be transmitted to a
54 * back-end routine responsible for setting the foreground and background text
55 * colors appropriately. Note that the color combinations are ordered to correspond
56 * with combinations of red (0x1), green (0x2) and blue (0x4) bit flags.
57 */
58
59 int ScColorTrans[8]= { 0,
60 SC_RED,
61 SC_GREEN,
62 SC_RED|SC_GREEN,
63 SC_BLUE,
64 SC_RED|SC_BLUE,
65 SC_RED|SC_GREEN,
66 SC_RED|SC_GREEN|SC_BLUE
67 };
68
69
70 /* List of terminal attributes which we track (used by <esc>[?#h and <esc>[?#l) */
71
72 int termAttrMode[NUM_TERM_ATTR_MODES] = { 0,
73 CURSORAPPL_MODE,
74 ANSI_MODE,
75 COL132_MODE,
76 SMOOTHSCROLL_MODE,
77 REVSCREEN_MODE,
78 ORIGINREL_MODE,
79 WRAP_MODE,
80 REPEAT_MODE,
81 };
82
83 /* FORWARD FUNCTION DECLARATIONS -
84 * these functions are intended for use only in this module */
85
86 static int ProcessBracket(int Start);
87 static int ProcessEscape(int Start);
88 static int ProcessControl(int Start);
89 static int ProcessBuffer(void);
90
91 /* END FORWARD FUNCTION DECLARATIONS */
92
93
94
95 /* vtputs() -
96 *
97 * front-end 'puts()' substitute. Call this routine instead
98 * of 'puts()', and it'll pass the output through the vt100 emulator.
99 */
100
101 vtputs(char *f)
102 {
103 char cbuf[1024];
104
105 strcpy(cbuf,f);
106 strcat(cbuf,"\n");
107 vtProcessedTextOut(cbuf, strlen(cbuf));
108
109 return(0);
110 }
111
112
113 /* vtprintf -
114 *
115 * This routine is a substitute for printf(). Call this routine with the
116 * same parameters you would use for printf, and output will be channelled
117 * through the vt-100 emulator.
118 */
119
120 vtprintf(char *format, ...)
121 {
122 char cbuf[1024];
123 va_list va;
124
125 va_start(va, format);
126 vsprintf(cbuf,format, va);
127 va_end(va);
128
129 vtProcessedTextOut(cbuf, strlen(cbuf));
130
131 return(0);
132 }
133
134 /* vtInitVT100 -
135 *
136 * Set the initial state of the VT-100 emulator, and call the initialization
137 * routine for the back end. This routine MUST be invoked before any other, or
138 * the VT-100 emulator will most likely roll over and die.
139 */
140
141 vtInitVT100(void)
142 {
143 int i=0;
144
145 cBuffer[0]='\0';
146 BufLen=0;
147
148 beInitVT100Terminal(); /* call the back-end initialization. */
149
150 return(0);
151 }
152
153
154 /* ProcessBracket -
155 *
156 * Helper routine for processing escape sequences. By the time this
157 * routine is invoked, '<esc>[' has already been read in the input
158 * stream. 'Start' is an index in cBuffer to the '<esc>'.
159 *
160 * If an escape sequence is successfully parsed, return the index of the
161 * first character AFTER the escape sequence. Otherwise, return 'Start'.
162 *
163 */
164
165 static int ProcessBracket(int Start)
166 {
167 int End; /* Current character being examined in cBuffer */
168 int args[10], numargs=0; /* numerical args after <esc>[ */
169 int iMode;
170 int i;
171 int itmp=0;
172 int left;
173 int iForeground, iBackground;
174
175 /* If there's no valid escape sequence, return as we were called. */
176
177 if ((cBuffer[Start+1] != '[')||(Start+2 >= BufLen))
178 return(Start);
179
180 End = Start + 2;
181
182 /* Loop through the buffer, hacking out all numeric
183 * arguments (consecutive string of digits and
184 * semicolons
185 */
186
187 do {
188 itmp = 0; /* itmp will hold the current arguments integer value */
189
190 /* the semicolon is a delimiter */
191 if (cBuffer[End] == ';')
192 {
193 End++;
194
195 if (End >= BufLen)
196 return(Start);
197 }
198
199 /* Parse this argument into a number. */
200
201 while (isdigit(cBuffer[End]))
202 {
203 itmp = itmp*10 + (cBuffer[End]-'0');
204 End++;
205 if (End >= BufLen)
206 return(Start);
207 }
208
209 /* Save the numeric argument if we actually
210 * parsed a number.
211 */
212
213 if (End != Start + 2)
214 args[numargs++] = itmp;
215
216 } while (cBuffer[End] == ';');
217
218 /* At this point, we've come across a character that isn't a number
219 * and isn't a semicolon. This means it is the command specifier.
220 */
221
222 /* Number of characters left in the input stream. I don't use
223 * this as rigorously as I should here.
224 */
225
226 left = BufLen - End;
227
228 /* Return if there's definitely not enough characters for a
229 * full escape sequence.
230 */
231
232 if (left <= 0)
233 return(Start);
234
235 /* Giant switch statement, parsing the command specifier that followed
236 * up <esc>[arg;arg;...arg
237 */
238
239 switch (cBuffer[End])
240 {
241 /* Cursor Up: Esc [ Pn A */
242 case 'A':
243 beOffsetCursor(numargs ? -args[0] : -1, 0);
244 End += 1;
245 break;
246
247 /* Cursor Down: Esc [ Pn B */
248 case 'B':
249 beOffsetCursor(numargs ? args[0] : 1, 0);
250 End += 1;
251 break;
252
253 /* Cursor Right: Esc [ Pn C */
254 case 'C':
255 beOffsetCursor(0, numargs ? args[0] : 1);
256 End += 1;
257 break;
258
259 /* Cursor Left: Esc [ Pn D */
260 case 'D':
261 beOffsetCursor(0, numargs ? -args[0] : -1);
262 End += 1;
263 break;
264
265 /* Direct Addressing : Esc [ Pn(row);Pn(col)H or
266 * Esc [ Pn(row);Pn(col)f
267 */
268 case 'H':
269 case 'f':
270 if (numargs == 0)
271 beAbsoluteCursor(1,1);
272 else if (numargs == 1)
273 beAbsoluteCursor(args[0] > 0 ? args[0] : 1,1);
274 else if (numargs == 2)
275 beAbsoluteCursor(args[0] > 0 ? args[0] : 1, args[1] > 0 ? args[1] : 1);
276
277 End += 1;
278 break;
279
280 /* Erase from Cursor to end of screen Esc [ J
281 * Erase from Beginning of screen to cursor Esc [ 1 J
282 * Erase Entire screen Esc [ 2 J
283 */
284 case 'J':
285 if (numargs == 0)
286 beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE);
287 else if (args[0] == 1)
288 beEraseText(TOP_EDGE, LEFT_EDGE, CUR_ROW, CUR_COL);
289 else
290 beEraseText(TOP_EDGE, LEFT_EDGE, BOTTOM_EDGE, RIGHT_EDGE);
291
292 End += 1;
293 break;
294
295 /* Erase from Cursor to end of line Esc [ K
296 * Erase from Beginning of line to cursor Esc [ 1 K
297 * Erase Entire line Esc [ 2 K
298 */
299 case 'K':
300 if (numargs == 0)
301 beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE);
302 else if (args[0] == 1)
303 beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, CUR_COL);
304 else
305 beEraseText(CUR_ROW, LEFT_EDGE, CUR_ROW, RIGHT_EDGE);
306
307 End += 1;
308 break;
309
310
311 /* Set Graphics Rendition:
312 * ESC[#;#;....;#m
313 * The graphics rendition is basically foreground and background
314 * color and intensity.
315 */
316 case 'm':
317 beGetTextAttributes(&iForeground, &iBackground);
318
319 if (numargs < 1)
320 {
321 /* If we just get ESC[m, treat it as though
322 * we should shut off all extra text
323 * attributes
324 */
325
326 iForeground &= ~(SC_BOLD|SC_UL|SC_BL|SC_RV|SC_GRAPHICS|SC_G0|SC_G1);
327 iForeground |= SC_ASCII;
328
329 beSetTextAttributes(iForeground, iBackground);
330 End += 1;
331 break;
332 }
333
334 /* Loop through all the color specs we got, and combine them
335 * together. Note that things like 'reverse video', 'bold',
336 * and 'blink' are only applied to the foreground. The back end
337 * is responsible for applying these properties to all text.
338 */
339 for (i=0; i < numargs; i++)
340 {
341 switch(args[i])
342 {
343 /* 0 for normal display */
344 case 0:
345 iForeground &= ~SC_BOLD;
346 break;
347
348 /* 1 for bold on */
349 case 1:
350 iForeground |= SC_BOLD;
351 break;
352
353 /* 4 underline (mono only) */
354 case 4:
355 iForeground |= SC_UL;
356 break;
357
358 /* 5 blink on */
359 case 5:
360 iForeground |= SC_BL;
361 break;
362
363 /* 7 reverse video on */
364 case 7:
365 iForeground |= SC_RV;
366 break;
367
368 /* 8 nondisplayed (invisible) BUGBUG - not doing this. */
369
370
371 /* 30-37 is bit combination of 30+ red(1) green(2) blue(4)
372 * 30 black foreground
373 */
374 case 30:
375 case 31:
376 case 32:
377 case 33:
378 case 34:
379 case 35:
380 case 36:
381 case 37:
382 iForeground &= ~(SC_RED|SC_GREEN|SC_BLUE);
383 iForeground |= ScColorTrans[args[i]-30];
384 break;
385
386 /* 40-47 is bit combo similar to 30-37, but for background. */
387 case 40:
388 case 41:
389 case 42:
390 case 43:
391 case 44:
392 case 45:
393 case 46:
394 case 47:
395 iBackground &= ~(SC_RED|SC_GREEN|SC_BLUE);
396 iBackground |= ScColorTrans[args[i]-30];
397 break;
398 }
399 }
400
401 beSetTextAttributes(iForeground, iBackground);
402
403 End += 1;
404 break;
405
406
407 /*
408 * Set with Esc [ Ps h
409 * Reset with Esc [ Ps l
410 * Mode name Ps Set Reset
411 * -------------------------------------------------------------------
412 * Keyboard action 2 Locked Unlocked
413 * Insertion 4 Insert Overwrite
414 * Send - Receive 12 Full Echo
415 * Line feed/New line 20 New line Line feed
416 */
417 case 'h':
418 case 'l':
419 /* BUGBUG - many of the terminal modes are set with '?' as the
420 * first character rather than a number sign. These are dealt with
421 * later in this switch statement because they must be. Most other
422 * settings are just ignored, however.
423 */
424 End += 1;
425 break;
426
427 /* Insert line Esc [ Pn L */
428 case 'L':
429 beInsertRow(CUR_ROW);
430 End += 1;
431 break;
432
433 /* Delete line Esc [ Pn M */
434 case 'M':
435 do {
436 beDeleteText(CUR_ROW,LEFT_EDGE, CUR_ROW, RIGHT_EDGE);
437 } while (--args[0] > 0);
438
439 End += 1;
440 break;
441
442 /* Delete character Esc [ Pn P */
443 case 'P':
444 do {
445 beDeleteText(CUR_ROW, CUR_COL, CUR_ROW, CUR_COL);
446 } while (--args[0] > 0);
447 End += 1;
448 break;
449
450 /* Set the Scrolling region Esc [ Pn(top);Pn(bot) r */
451 case 'r':
452 if (numargs == 0)
453 beSetScrollingRows(TOP_EDGE,BOTTOM_EDGE);
454 else if (numargs == 2)
455 beSetScrollingRows(args[0],args[1]);
456 End += 1;
457 break;
458
459 /* Print screen or region Esc [ i
460 * Print cursor line Esc [ ? 1 i
461 * Enter auto print Esc [ ? 5 i
462 * Exit auto print Esc [ ? 4 i
463 * Enter print controller Esc [ 5 i
464 * Exit print controller Esc [ 4 i
465 */
466 case 'i':
467 /* BUGBUG - print commands are not acted upon. */
468 End += 1;
469 break;
470
471
472 /* Clear tab at current column Esc [ g
473 * Clear all tabs Esc [ 3 g
474 */
475 case 'g':
476 if (numargs == 0)
477 beClearTab(CUR_COL);
478 else
479 if ((numargs == 1) && (args[0] == 3))
480 beClearTab(ALL_TABS);
481
482 End += 1;
483 break;
484
485 /* BUGBUG - queries which require responses are ignored. */
486
487 /* Esc [ c DA:Device Attributes
488 * or
489 *
490 * Esc [ <sol> x DECREQTPARM: Request Terminal Parameters
491 * * <sol> values other than 1 are ignored. Upon
492 * receipt of a <sol> value of 1, the following
493 * response is sent:
494 * Esc[3;<par>;<nbits>;<xspeed>;<rspeed>;1;0x
495 *
496 * * Where <par>, <nbits>, <xspeed>, and <rspeed>
497 * are as for VT100s with the following
498 * exceptions:
499 * <nbits> Values of 5 and 6 bits per
500 * character are sent as 7 bits.
501 * <xspeed>,<rspeed>
502 * These two numbers will always
503 * be the same. 9600 baud is
504 * sent for 7200 baud.
505 *
506 * Esc [ Ps n DSR: Device Status Report
507 * * Parameter values other than 5, 6, are ignored.
508 * If the parameter value is 5, the sequence
509 * Esc [ O n is returned. If the parameter value is
510 * 6, the CPR: Cursor Position Report sequence
511 * Esc [ Pn ; Pn R is returned with the Pn set to
512 * cursor row and column numbers.
513 *
514 * Cursor Controls:
515 * ESC[#;#R Reports current cursor line & column
516 */
517
518 /* spec <esc>[<spec>h <esc>[<spec>l
519 * Cursor key ?1 Application Cursor
520 * ANSI/VT52 ?2 ANSI VT52
521 * Column ?3 132 80
522 * Scrolling ?4 Smooth Jump
523 * Screen ?5 Reverse Normal
524 * Origin ?6 Relative Absolute
525 * Wraparound ?7 Wrap Truncate
526 * Auto key repeat ?8 Repeating No repeat
527 */
528 case '?':
529 /* We didn't catch the numeric argument because the '?' stopped
530 * it before. Parse it now.
531 */
532 args[0]=0;
533 while (isdigit(cBuffer[++End]))
534 {
535 if (End >= BufLen)
536 return(Start);
537 args[0] = 10*args[0] + (cBuffer[End]-'0');
538 }
539
540 /* If we don't handle this particular '?' command (and
541 * there are plenty we don't) then just ignore it.
542 */
543 if ( (args[0] > 8)
544 ||( (cBuffer[End] != 'l') && (cBuffer[End] != 'h'))
545 )
546 {
547 End++;
548 if (End >= BufLen)
549 return(Start);
550 break;
551 }
552
553 /* This command sets terminal status. Get the current status,
554 * determine what needs to be changed, and send it back.
555 */
556
557 iMode = beGetTermMode();
558
559 /* If we need a given mode and it's not already set, set it. */
560
561 if ((cBuffer[End] == 'h') && (!(iMode & termAttrMode[args[0]])))
562 {
563 beSetTermMode(iMode | termAttrMode[args[0]]);
564 }
565
566 /* likewise, clear it as appropriate */
567 if ((cBuffer[End] == 'l') && (iMode & termAttrMode[args[0]]))
568 {
569 beSetTermMode(iMode & ~termAttrMode[args[0]]);
570 }
571
572 End++;
573 break;
574
575 /* If it's an escape sequence we don't treat, pretend as though we never saw
576 * it.
577 */
578 default:
579 End += 1;
580 break;
581 }
582
583 return(End);
584
585 }
586
587
588 /* ProcessEscape -
589 *
590 * At this point, <esc> has been seen. Start points to the escape
591 * itself, and then this routine is responsible for parsing the
592 * rest of the escape sequence, and either pawning off further parsing,
593 * or acting upon it as appropriate.
594 *
595 * Note that the escape sequences being parsed are still contained in cBuffer.
596 */
597
598 static int ProcessEscape(int Start)
599 {
600 int End;
601 int left;
602 int fore, back;
603 int i;
604
605 /* if it's definitely not a full escape sequence, return that we haven't
606 * seen it.
607 */
608 if ((cBuffer[Start] != 27)||(Start+1 >= BufLen))
609 return(Start);
610
611 End = Start + 1;
612
613 /* At this point, if the sequence is <esc> x, 'End' points at
614 * x
615 */
616
617 /* left = number of characters left unparsed in the buffer. */
618 left = BufLen - End -1;
619
620 /* Main switch statement - parse the escape sequence according to the
621 * next character we see.
622 */
623
624 switch (cBuffer[End])
625 {
626 /* Hard Reset Esc c BUGBUG - not imp'd. */
627 case 'c':
628 End += 1;
629 break;
630
631 /* Cursor up Esc A */
632 case 'A':
633 beOffsetCursor(-1,0);
634 End += 1;
635 break;
636
637 /* Cursor down Esc B */
638 case 'B':
639 beOffsetCursor(1,0);
640 End += 1;
641 break;
642
643 /* Cursor right Esc C */
644 case 'C':
645 beOffsetCursor(0,1);
646 End += 1;
647 break;
648
649 /* Cursor left Esc D */
650 case 'D':
651 beOffsetCursor(0,-1);
652 End += 1;
653 break;
654
655 /* Newline command: Esc E */
656 case 'E':
657 beRawTextOut("\n",strlen("\n"));
658 End += 1;
659 break;
660
661 /* Invoke the Graphics character set Esc F */
662 case 'F':
663 beGetTextAttributes(&fore, &back);
664 if (! (fore & SC_GRAPHICS))
665 {
666 fore &= ~(SC_ASCII|SC_G0|SC_G1);
667 fore |= SC_GRAPHICS;
668 beSetTextAttributes(fore, back);
669 }
670 End += 1;
671 break;
672
673 /* Invoke the ASCII character set Esc G */
674 case 'G':
675 beGetTextAttributes(&fore, &back);
676 if (! (fore & SC_ASCII))
677 {
678 fore &= ~(SC_G0|SC_G1|SC_GRAPHICS);
679 fore |= SC_ASCII;
680 beSetTextAttributes(fore, back);
681 }
682 End += 1;
683 break;
684
685 /* Move the cursor to (1,1): Home cursor Esc H */
686 case 'H':
687 beAbsoluteCursor(TOP_EDGE,LEFT_EDGE);
688 End += 1;
689 break;
690
691 /* Reverse line feed Esc I */
692 case 'I':
693 beOffsetCursor(-1,0);
694 End += 1;
695 break;
696
697 /* Erase to end of screen Esc J */
698 case 'J':
699 beEraseText(CUR_ROW, CUR_COL, BOTTOM_EDGE, RIGHT_EDGE);
700 End += 1;
701 break;
702
703 /* Erase to end of line Esc K */
704 case 'K':
705 beEraseText(CUR_ROW, CUR_COL, CUR_ROW, RIGHT_EDGE);
706 End += 1;
707 break;
708
709 /* Reverse Line: Esc M */
710 case 'M':
711 beAbsoluteCursor(CUR_ROW, LEFT_EDGE);
712 beOffsetCursor(-1,0);
713 End += 1;
714 break;
715
716 /* Switch to G1 graphics character set. Esc N */
717 case 'N':
718 beGetTextAttributes(&fore, &back);
719 if (! (fore & SC_G1))
720 {
721 fore &= ~(SC_G0|SC_ASCII|SC_GRAPHICS);
722 fore |= SC_G1;
723 beSetTextAttributes(fore, back);
724 }
725 End += 1;
726 break;
727
728 /* Switch to G0 graphics character set Esc O */
729 case 'O':
730 beGetTextAttributes(&fore, &back);
731 if (! (fore & SC_G0))
732 {
733 fore &= ~(SC_G1|SC_ASCII|SC_GRAPHICS);
734 fore |= SC_G0;
735 beSetTextAttributes(fore, back);
736 }
737 End += 1;
738 break;
739
740 /* Print cursor line Esc V BUGBUG - unimp'd */
741 case 'V':
742 End += 1;
743 break;
744
745 /* Enter print controller Esc W BUGBUG - unimp'd */
746 case 'W':
747 End += 1;
748 break;
749
750 /* Exit print controller Esc X BUGBUG - unimp'd */
751 case 'X':
752 End += 1;
753 break;
754
755 /* Cursor address Esc Y row col BUGBUG - unimp'd and needed */
756 case 'Y':
757 End += 1;
758 break;
759
760 /* Identify terminal type Esc Z */
761 case 'Z':
762 beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE));
763 End += 1;
764 break;
765
766 /* One of dozens of escape sequences starting <esc>[. Parse further */
767 case '[':
768 /* pass in the escape as the starting point */
769 End = ProcessBracket(End-1);
770 break;
771
772 /* Print screen Esc ] BUGBUG - unimp'd */
773 case ']':
774 End += 1;
775 break;
776
777 /* Enter auto print Esc ^ BUGBUG - unimpd' */
778 case '^':
779 End += 1;
780 break;
781
782 /* Exit auto print Esc - BUGBUG - unimpd' */
783 case '-':
784 End += 1;
785 break;
786
787 /* Alternate keypad Esc = BUGBUG - unimpd' */
788 case '=':
789 End += 1;
790 break;
791
792 /* Numeric keypad Esc > BUGBUG - unimpd' */
793 case '>':
794 End += 1;
795 break;
796
797 /* Enter ANSI mode Esc < BUGBUG - unimpd' */
798 case '<':
799 End += 1;
800 break;
801
802 /* Save cursor position & Attributes: Esc 7 */
803 case '7':
804 beSaveCursor();
805 End += 1;
806 break;
807
808 /* Restore cursor position & attributes: Esc 8 */
809 case '8':
810 beRestoreCursor();
811 End += 1;
812 break;
813
814 /* Set character size - BUGBUG - unimp'd.
815 * # 1 Double ht, single width top half chars
816 * # 2 Double ht, single width lower half chars
817 * # 3 Double ht, double width top half chars
818 * # 4 Double ht, double width lower half chars
819 * # 5 Single ht, single width chars
820 * # 6 Single ht, double width chars
821 */
822 case '#':
823 End += 1;
824 break;
825
826 /* Select character set
827 * ESC ( A British
828 * ESC ( C Finnish
829 * ESC ( E Danish or Norwegian
830 * ESC ( H Swedish
831 * ESC ( K German
832 * ESC ( Q French Canadian
833 * ESC ( R Flemish or French/Belgian
834 * ESC ( Y Italian
835 * ESC ( Z Spanish
836 * ESC ( 1 Alternative Character
837 * ESC ( 4 Dutch
838 * ESC ( 5 Finnish
839 * ESC ( 6 Danish or Norwegian
840 * ESC ( 7 Swedish
841 * ESC ( = Swiss (French or German)
842 */
843 case '(':
844 case ')':
845 /* BUGBUG - most character sets aren't implemented. */
846 beGetTextAttributes(&fore, &back);
847 switch (cBuffer[++End])
848 {
849 case 'B': /* ESC ( B North American ASCII set */
850 i=SC_ASCII;
851 break;
852
853 case '0': /* ESC ( 0 Line Drawing */
854 i=SC_G1;
855 break;
856
857 case '2': /* ESC ( 2 Alternative Line drawing */
858 i=SC_G0;
859 break;
860
861 default:
862 /* Make sure the screen mode isn't set. */
863 i = 0xFFFFFFFF;
864 break;
865 }
866
867 if (! (fore & i))
868 {
869 fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
870 fore |= i;
871 beSetTextAttributes(fore, back);
872 }
873
874 End += 1;
875 break;
876
877 /* Unknown escape sequence */
878 default:
879 {
880 char cbuf[80];
881 sprintf(cbuf,"<esc>%c", cBuffer[End]);
882 beRawTextOut(cbuf+Start,6);
883 End += 1;
884 }
885 }
886
887 return(End);
888 }
889
890
891 /* ProcessControl -
892 *
893 * Process a probable escape or control sequence
894 * starting at the supplied index. Return the index
895 * of the first character *after* the escape sequence we
896 * process.
897 * In the case of an incomplete sequence, ie one
898 * that isn't completed by the end of the buffer, return
899 * 'Start'.
900 * In the case of an invalid sequence,
901 * note the invalid escape sequence, and return Start+1
902 */
903
904 static int ProcessControl(int Start)
905 {
906 int fore, back;
907 int End = Start;
908
909 /* Check to make sure we at least have enough characters
910 * for a control character
911 */
912
913 if (Start >= BufLen)
914 return(Start);
915
916 switch (cBuffer[Start])
917 {
918 /* NUL 0 Fill character; ignored on input.
919 * DEL 127 Fill character; ignored on input.
920 */
921 case 0:
922 case 127:
923 End += 1;
924 break;
925
926 /* ENQ 5 Transmit answerback message. */
927 case 5:
928 beTransmitText(ANSWERBACK_MESSAGE,strlen(ANSWERBACK_MESSAGE));
929 End += 1;
930 break;
931
932 /* BEL 7 Ring the bell. */
933 case 7:
934 beRingBell();
935 End += 1;
936 break;
937
938 /* BS 8 Move cursor left. */
939 case 8:
940 beOffsetCursor(0,-1);
941 End += 1;
942 break;
943
944 /* HT 9 Move cursor to next tab stop. */
945 case 9:
946 beAdvanceToTab();
947 End += 1;
948 break;
949
950 /* LF 10 Line feed; causes print if in autoprint. */
951 case 10:
952 beOffsetCursor(1,0);
953 End += 1;
954 break;
955
956 /* VT 11 Same as LF.
957 * FF 12 Same as LF.
958 */
959 case 11:
960 case 12:
961 beOffsetCursor(1,0);
962 End += 1;
963 break;
964
965 /* CR 13 Move cursor to left margin or newline. */
966 case 13:
967 beOffsetCursor(1,0);
968 beAbsoluteCursor(CUR_ROW,LEFT_EDGE);
969 End += 1;
970 break;
971
972 /* SO 14 Invoke G1 character set. */
973 case 14:
974 beGetTextAttributes(&fore, &back);
975 if (! (fore & SC_G1))
976 {
977 fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
978 fore |= SC_G1;
979 beSetTextAttributes(fore, back);
980 }
981 End += 1;
982 break;
983
984 /* SI 15 Invoke G0 character set. */
985 case 15:
986 beGetTextAttributes(&fore, &back);
987 if (! (fore & SC_G0))
988 {
989 fore &= ~(SC_ASCII|SC_G0|SC_G1|SC_GRAPHICS);
990 fore |= SC_G0;
991 beSetTextAttributes(fore, back);
992 }
993 End += 1;
994 break;
995
996 /* CAN 24 Cancel escape sequence and display checkerboard. BUGBUG - not imp'd.
997 * SUB 26 Same as CAN.
998 */
999 case 24:
1000 End += 1;
1001 break;
1002
1003 /* ESC 27 Introduce a control sequence. */
1004 case 27:
1005 End = ProcessEscape(Start);
1006 break;
1007
1008 /* Print any other control character received. */
1009 default:
1010 {
1011 char buf[4];
1012 sprintf(buf,"^%c",'A' + cBuffer[Start] - 1);
1013 beRawTextOut(buf, 2);
1014 End += 1;
1015 }
1016 break;
1017 }
1018
1019 return(End);
1020 }
1021
1022
1023 /* ProcessBuffer -
1024 *
1025 * Process the current contents of the terminal character buffer.
1026 */
1027 static int ProcessBuffer(void)
1028 {
1029 int Start=0,End=0;
1030
1031 /* Null-terminate the buffer. Why? Heck, why not? */
1032
1033 cBuffer[BufLen] = '\0';
1034
1035 /* Loop through the entire buffer. Start will be incremented
1036 * to point at the start of unprocessed text in the buffer as
1037 * we go.
1038 */
1039 while (Start < BufLen)
1040 {
1041 End = Start;
1042
1043 /* Since we null-terminated, null < 27 and we have a termination condition */
1044 while ((cBuffer[End] > 27)||(cBuffer[End] == 10)||(cBuffer[End] == 13))
1045 End++;
1046
1047 /* At this point, if Start < End, we have a string of characters which
1048 * doesn't have any control sequences, and should be printed raw.
1049 */
1050
1051 if (End > Start)
1052 beRawTextOut(cBuffer+Start, End-Start);
1053
1054 if (End >= BufLen)
1055 {
1056 break;
1057 }
1058
1059 /* At this point, 'End' points to the beginning of an escape
1060 * sequence. We'll reset 'start' to be AFTER parsing the
1061 * escape sequence. Note that if the escape sequence
1062 * is incomplete, ProcessControl should return the
1063 * same value passed in. Otherwise, it'll return the
1064 * index of the first character after the valid
1065 * escape sequence.
1066 */
1067
1068 Start = ProcessControl(End);
1069
1070 if (Start == End)
1071 {
1072 /* The escape sequence was incomplete.
1073 * Move the unprocessed portion of the input buffer
1074 * to start at the beginning of the buffer, then
1075 * return.
1076 */
1077
1078 while (End < BufLen)
1079 {
1080 cBuffer[End-Start] = cBuffer[End];
1081 End += 1;
1082 }
1083
1084 BufLen = End-Start;
1085 return(0);
1086 }
1087 }
1088
1089 /* If we made it this far, Start == Buflen, and so we've finished
1090 * processing the buffer completely.
1091 */
1092 BufLen = 0;
1093 cBuffer[BufLen] = '\0';
1094
1095 return(0);
1096 }
1097
1098
1099 /* vtProcessedTextOut -
1100 *
1101 * Output characters to terminal, passing them through the vt100 emulator.
1102 * Return -1 on error
1103 */
1104 int
1105 vtProcessedTextOut(char *cbuf, int count)
1106 {
1107 /* If we have a buffer overflow, error out if we haven't already crashed. */
1108
1109 if ((count + BufLen) > MAXVTBUFLEN)
1110 {
1111 beRawTextOut("ERROR: VT-100 internal buffer overflow!",39);
1112 return(-1);
1113 }
1114
1115 /* Otherwise, add our requested information to the
1116 * output buffer, and attempt to parse it.
1117 */
1118
1119 memcpy(cBuffer + BufLen, cbuf, count);
1120 BufLen += count;
1121
1122 return(ProcessBuffer());
1123 }
1124