xref: /reactos/drivers/sac/driver/vtutf8chan.c (revision 34593d93)
1 /*
2  * PROJECT:     ReactOS Drivers
3  * LICENSE:     BSD - See COPYING.ARM in the top level directory
4  * FILE:        drivers/sac/driver/vtutf8chan.c
5  * PURPOSE:     Driver for the Server Administration Console (SAC) for EMS
6  * PROGRAMMERS: ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "sacdrv.h"
12 
13 /* GLOBALS ********************************************************************/
14 
15 CHAR IncomingUtf8ConversionBuffer[4];
16 WCHAR IncomingUnicodeValue;
17 
18 SAC_STATIC_ESCAPE_STRING SacStaticEscapeStrings [] =
19 {
20     { VT_ANSI_CURSOR_UP, 2, SacCursorUp },
21     { VT_ANSI_CURSOR_DOWN, 2, SacCursorDown },
22     { VT_ANSI_CURSOR_RIGHT, 2, SacCursorRight },
23     { VT_ANSI_CURSOR_LEFT, 2, SacCursorLeft },
24     { VT_220_BACKTAB, 3, SacBackTab },
25     { VT_ANSI_ERASE_END_LINE, 2, SacEraseEndOfLine },
26     { VT_ANSI_ERASE_START_LINE, 3, SacEraseStartOfLine },
27     { VT_ANSI_ERASE_ENTIRE_LINE, 3, SacEraseLine },
28     { VT_ANSI_ERASE_DOWN_SCREEN, 2, SacEraseEndOfScreen },
29     { VT_ANSI_ERASE_UP_SCREEN, 3, SacEraseStartOfScreen },
30     { VT_ANSI_ERASE_ENTIRE_SCREEN, 3, SacEraseScreen },
31 };
32 
33 /* FUNCTIONS ******************************************************************/
34 
35 FORCEINLINE
36 VOID
VTUTF8ChannelAssertCursor(IN PSAC_CHANNEL Channel)37 VTUTF8ChannelAssertCursor(IN PSAC_CHANNEL Channel)
38 {
39     ASSERT(Channel->CursorRow < SAC_VTUTF8_ROW_HEIGHT);
40     ASSERT(Channel->CursorCol < SAC_VTUTF8_COL_WIDTH);
41 }
42 
43 BOOLEAN
44 NTAPI
VTUTF8ChannelScanForNumber(IN PWCHAR String,OUT PULONG Number)45 VTUTF8ChannelScanForNumber(IN PWCHAR String,
46                            OUT PULONG Number)
47 {
48     /* If the first character is invalid, fail early */
49     if ((*String < L'0') || (*String > L'9')) return FALSE;
50 
51     /* Otherwise, initialize the output and loop the string */
52     *Number = 0;
53     while ((*String >= L'0') && (*String <= L'9'))
54     {
55         /* Save the first decimal */
56         *Number = 10 * *Number;
57 
58         /* Compute and add the second one */
59         *Number += *++String - L'0';
60     }
61 
62     /* All done */
63     return TRUE;
64 }
65 
66 NTSTATUS
67 NTAPI
VTUTF8ChannelAnsiDispatch(IN PSAC_CHANNEL Channel,IN SAC_ANSI_DISPATCH AnsiCode,IN INT * Data,IN ULONG Length)68 VTUTF8ChannelAnsiDispatch(IN PSAC_CHANNEL Channel,
69                           IN SAC_ANSI_DISPATCH AnsiCode,
70                           IN INT* Data,
71                           IN ULONG Length)
72 {
73     NTSTATUS Status = STATUS_SUCCESS;
74     PCHAR LocalBuffer = NULL, Tmp;
75     INT l;
76     CHECK_PARAMETER1(Channel);
77 
78     /* Check which ANSI sequence we should output */
79     switch (AnsiCode)
80     {
81         /* Send the [2J (Clear Screen and Reset Cursor) */
82         case SacAnsiClearScreen:
83             Tmp = "\x1B[2J";
84             break;
85 
86         /* Send the [0J (Clear From Position Till End Of Screen) */
87         case SacAnsiClearEndOfScreen:
88             Tmp = "\x1B[0J";
89             break;
90 
91         /* Send the [0K (Clear from Position Till End Of Line) */
92         case SacAnsiClearEndOfLine:
93             Tmp = "\x1B[0K";
94             break;
95 
96         /* Send a combination of two [#m attribute changes */
97         case SacAnsiSetColors:
98 
99             /* Allocate a small local buffer for it */
100             LocalBuffer = SacAllocatePool(SAC_VTUTF8_COL_WIDTH, GLOBAL_BLOCK_TAG);
101             CHECK_ALLOCATION(LocalBuffer);
102 
103             /* Caller should have sent two colors as two integers */
104             if (!(Data) || (Length != 8))
105             {
106                 Status = STATUS_INVALID_PARAMETER;
107                 break;
108             }
109 
110             /* Create the escape sequence string */
111             l = sprintf(LocalBuffer, "\x1B[%dm\x1B[%dm", Data[1], Data[0]);
112             ASSERT((l + 1)*sizeof(UCHAR) < SAC_VTUTF8_COL_WIDTH);
113             ASSERT(LocalBuffer);
114             Tmp = LocalBuffer;
115             break;
116 
117         /* Send the [#;#H (Cursor Position) sequence */
118         case SacAnsiSetPosition:
119 
120             /* Allocate a small local buffer for it */
121             LocalBuffer = SacAllocatePool(SAC_VTUTF8_COL_WIDTH, GLOBAL_BLOCK_TAG);
122             CHECK_ALLOCATION(LocalBuffer);
123 
124             /* Caller should have sent the position as two integers */
125             if (!(Data) || (Length != 8))
126             {
127                 Status = STATUS_INVALID_PARAMETER;
128                 break;
129             }
130 
131             /* Create the escape sequence string */
132             l = sprintf(LocalBuffer, "\x1B[%d;%dH", Data[1] + 1, Data[0] + 1);
133             ASSERT((l + 1)*sizeof(UCHAR) < SAC_VTUTF8_COL_WIDTH);
134             ASSERT(LocalBuffer);
135             Tmp = LocalBuffer;
136             break;
137 
138         /* Send the [0m sequence (Set Attribute 0) */
139         case SacAnsiClearAttributes:
140             Tmp = "\x1B[0m";
141             break;
142 
143         /* Send the [7m sequence (Set Attribute 7) */
144         case SacAnsiSetInverseAttribute:
145             Tmp = "\x1B[7m";
146             break;
147 
148         /* Send the [27m sequence (Set Attribute 27) */
149         case SacAnsiClearInverseAttribute:
150             Tmp = "\x1B[27m";
151             break;
152 
153         /* Send the [5m sequence (Set Attribute 5) */
154         case SacAnsiSetBlinkAttribute:
155             Tmp = "\x1B[5m";
156             break;
157 
158         /* Send the [25m sequence (Set Attribute 25) */
159         case SacAnsiClearBlinkAttribute:
160             Tmp = "\x1B[25m";
161             break;
162 
163         /* Send the [1m sequence (Set Attribute 1) */
164         case SacAnsiSetBoldAttribute:
165             Tmp = "\x1B[1m";
166             break;
167 
168         /* Send the [22m sequence (Set Attribute 22) */
169         case SacAnsiClearBoldAttribute:
170             Tmp = "\x1B[22m";
171             break;
172 
173         /* We don't recognize it */
174         default:
175             Status = STATUS_INVALID_PARAMETER;
176             break;
177     }
178 
179     /* Did everything work above? */
180     if (NT_SUCCESS(Status))
181     {
182         /* Go write out the sequence */
183         Status = ConMgrWriteData(Channel, Tmp, strlen(Tmp));
184         if (NT_SUCCESS(Status))
185         {
186             /* Now flush it */
187             Status = ConMgrFlushData(Channel);
188         }
189     }
190 
191     /* Free the temporary buffer, if any, and return the status */
192     if (LocalBuffer) SacFreePool(LocalBuffer);
193     return Status;
194 }
195 
196 NTSTATUS
197 NTAPI
VTUTF8ChannelProcessAttributes(IN PSAC_CHANNEL Channel,IN UCHAR Attribute)198 VTUTF8ChannelProcessAttributes(IN PSAC_CHANNEL Channel,
199                                IN UCHAR Attribute)
200 {
201     NTSTATUS Status;
202     CHECK_PARAMETER(Channel);
203 
204     /* Set bold if needed */
205     Status = VTUTF8ChannelAnsiDispatch(Channel,
206                                        Attribute & SAC_CELL_FLAG_BOLD ?
207                                        SacAnsiSetBoldAttribute :
208                                        SacAnsiClearBoldAttribute,
209                                        NULL,
210                                        0);
211     if (!NT_SUCCESS(Status)) return Status;
212 
213     /* Set blink if needed */
214     Status = VTUTF8ChannelAnsiDispatch(Channel,
215                                        Attribute & SAC_CELL_FLAG_BLINK ?
216                                        SacAnsiSetBlinkAttribute :
217                                        SacAnsiClearBlinkAttribute,
218                                        NULL,
219                                        0);
220     if (!NT_SUCCESS(Status)) return Status;
221 
222     /* Set inverse if needed */
223     return VTUTF8ChannelAnsiDispatch(Channel,
224                                      Attribute & SAC_CELL_FLAG_INVERTED ?
225                                      SacAnsiSetInverseAttribute :
226                                      SacAnsiClearInverseAttribute,
227                                      NULL,
228                                      0);
229 }
230 
231 //
232 // This function is the guts of the sequences that SAC supports.
233 //
234 // It is written to conform to the way that Microsoft's SAC driver interprets
235 // the ANSI standard. If you want to extend and/or "fix" it, please use a flag
236 // that can be set in the Registry to enable "extended" ANSI support or etc...
237 //
238 // Hermes, I'm looking at you, buddy.
239 //
240 ULONG
241 NTAPI
VTUTF8ChannelConsumeEscapeSequence(IN PSAC_CHANNEL Channel,IN PWCHAR String)242 VTUTF8ChannelConsumeEscapeSequence(IN PSAC_CHANNEL Channel,
243                                    IN PWCHAR String)
244 {
245     ULONG Number, Number2, Number3, i, Action, Result;
246     PWCHAR Sequence;
247     PSAC_VTUTF8_SCREEN Screen;
248     ASSERT(String[0] == VT_ANSI_ESCAPE);
249 
250     /* Microsoft's driver does this after the O(n) check below. Be smarter. */
251     if (String[1] != VT_ANSI_COMMAND) return 0;
252 
253     /* Now that we know it's a valid command, look through the common cases */
254     for (i = 0; i < RTL_NUMBER_OF(SacStaticEscapeStrings); i++)
255     {
256         /* Check if an optimized sequence was detected */
257         if (!wcsncmp(String + 1,
258                      SacStaticEscapeStrings[i].Sequence,
259                      SacStaticEscapeStrings[i].Size))
260         {
261             /* Yep, return the right action, length, and set optionals to 1 */
262             Action = SacStaticEscapeStrings[i].Action;
263             Result = SacStaticEscapeStrings[i].Size + 1;
264             Number = Number2 = Number3 = 1;
265             goto ProcessString;
266         }
267     }
268 
269     /* It's a more complex sequence, start parsing it */
270     Result = 0;
271     Sequence = String + 2;
272 
273     /* First, check for the cursor sequences. This is useless due to above. */
274     switch (*Sequence)
275     {
276         case VT_ANSI_CURSOR_UP_CHAR:
277             Action = SacCursorUp;
278             goto ProcessString;
279 
280         case VT_ANSI_CURSOR_DOWN_CHAR:
281             Action = SacCursorDown;
282             goto ProcessString;
283 
284         case VT_ANSI_CURSOR_RIGHT_CHAR:
285             Action = SacCursorLeft; //bug
286             goto ProcessString;
287 
288         case VT_ANSI_CURSOR_LEFT_CHAR:
289             Action = SacCursorRight; //bug
290             goto ProcessString;
291 
292         case VT_ANSI_ERASE_LINE_CHAR:
293             Action = SacEraseEndOfLine;
294             goto ProcessString;
295 
296         default:
297             break;
298     }
299 
300     /* This must be a sequence starting with ESC[# */
301     if (!VTUTF8ChannelScanForNumber(Sequence, &Number)) return 0;
302     while ((*Sequence >= L'0') && (*Sequence <= L'9')) Sequence++;
303 
304     /* Check if this is ESC[#m */
305     if (*Sequence == VT_ANSI_SET_ATTRIBUTE_CHAR)
306     {
307         /* Some attribute is being set, go over the ones we support */
308         switch (Number)
309         {
310             /* Make the font standard */
311             case Normal:
312                 Action = SacFontNormal;
313                 break;
314 
315             /* Make the font bold */
316             case Bold:
317                 Action = SacFontBold;
318                 break;
319 
320             /* Make the font blink */
321             case SlowBlink:
322                 Action = SacFontBlink;
323                 break;
324 
325             /* Make the font colors inverted */
326             case Inverse:
327                 Action = SacFontInverse;
328                 break;
329 
330             /* Make the font standard intensity */
331             case BoldOff:
332                 Action = SacFontBoldOff;
333                 break;
334 
335             /* Turn off blinking */
336             case BlinkOff:
337                 Action = SacFontBlinkOff;
338                 break;
339 
340             /* Turn off inverted colors */
341             case InverseOff:
342                 Action = SacFontInverseOff;
343                 break;
344 
345             /* Something else... */
346             default:
347 
348                 /* Is a background color being set? */
349                 if ((Number < SetBackColorStart) || (Number > SetBackColorMax))
350                 {
351                     /* Nope... is it the foreground color? */
352                     if ((Number < SetColorStart) || (Number > SetColorMax))
353                     {
354                         /* Nope. SAC expects no other attributes so bail out */
355                         ASSERT(FALSE);
356                         return 0;
357                     }
358 
359                     /* Yep -- the number will tell us which */
360                     Action = SacSetFontColor;
361                 }
362                 else
363                 {
364                     /* Yep -- the number will tell us which */
365                     Action = SacSetBackgroundColor;
366                 }
367                 break;
368         }
369 
370         /* In all cases, we're done here */
371         goto ProcessString;
372     }
373 
374     /* The only allowed possibility now is ESC[#;# */
375     if (*Sequence != VT_ANSI_SEPARATOR_CHAR) return 0;
376     Sequence++;
377     if (!VTUTF8ChannelScanForNumber(Sequence, &Number2)) return 0;
378     while ((*Sequence >= L'0') && (*Sequence <= L'9')) Sequence++;
379 
380     /* There's two valid forms accepted at this point: ESC[#;#m and ESC[#;#H */
381     switch (*Sequence)
382     {
383         /* This is ESC[#;#m -- used to set both colors at once */
384         case VT_ANSI_SET_ATTRIBUTE_CHAR:
385             Action = SacSetColors;
386             goto ProcessString;
387 
388         /* This is ESC[#;#H -- used to set the cursor position */
389         case VT_ANSI_CUP_CURSOR_CHAR:
390             Action = SacSetCursorPosition;
391             goto ProcessString;
392 
393         /* Finally, this *could* be ESC[#;#; -- we'll keep parsing */
394         case VT_ANSI_SEPARATOR_CHAR:
395             Sequence++;
396             break;
397 
398         /* Abandon anything else */
399         default:
400             return 0;
401     }
402 
403     /* The SAC seems to accept a few more possibilities if a ';' follows... */
404     switch (*Sequence)
405     {
406         /* Both ESC[#;#;H and ESC[#;#;f are really the same command */
407         case VT_ANSI_CUP_CURSOR_CHAR:
408         case VT_ANSI_HVP_CURSOR_CHAR:
409             /* It's unclear why MS doesn't allow the HVP sequence on its own */
410             Action = SacSetCursorPosition;
411             goto ProcessString;
412 
413         /* And this is the ESC[#;#;r command to set the scroll region... */
414         case VT_ANSI_SCROLL_CHAR:
415             /* Again, not clear why ESC[#;#r isn't supported */
416             Action = SacSetScrollRegion;
417             goto ProcessString;
418 
419         /* Anything else must be ESC[#;#;# */
420         default:
421             break;
422     }
423 
424     /* Get the last "#" */
425     if (!VTUTF8ChannelScanForNumber(Sequence, &Number3)) return 0;
426     while ((*Sequence >= L'0') && (*Sequence <= L'9')) Sequence++;
427 
428     /* And now the only remaining possibility is ESC[#;#;#;m */
429     if (*Sequence == VT_ANSI_SET_ATTRIBUTE_CHAR)
430     {
431         /* Which sets both color and attributes in one command */
432         Action = SacSetColorsAndAttributes;
433         goto ProcessString;
434     }
435 
436     /* No other sequences supported */
437     return 0;
438 
439 ProcessString:
440     /* Unless we got here from the optimized path, calculate the length */
441     if (!Result) Result = Sequence - String + 1;
442 
443     /* Get the current cell buffer */
444     Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
445     VTUTF8ChannelAssertCursor(Channel);
446 
447     /* Handle all the supported SAC ANSI commands */
448     switch (Action)
449     {
450         case SacCursorUp:
451             /* Check if we are scrolling too high */
452             if (Channel->CursorRow < Number)
453             {
454                 /* Reset the row to the top */
455                 Channel->CursorRow = 0;
456             }
457             else
458             {
459                 /* We're fine -- scroll up by that much */
460                 Channel->CursorRow -= Number;
461             }
462 
463             /* All done */
464             VTUTF8ChannelAssertCursor(Channel);
465             break;
466 
467         case SacCursorDown:
468             /* Check if we are scrolling too low */
469             if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT)
470             {
471                 /* Reset the row to the bottom */
472                 Channel->CursorRow = SAC_VTUTF8_ROW_HEIGHT;
473             }
474             else
475             {
476                 /* We're fine -- scroll down by that much */
477                 Channel->CursorRow += Number;
478             }
479 
480             /* All done */
481             VTUTF8ChannelAssertCursor(Channel);
482             break;
483 
484         case SacCursorLeft:
485             /* Check if we're scrolling too much to the left */
486             if (Channel->CursorCol < Number)
487             {
488                 /* Reset the column to the left-most margin */
489                 Channel->CursorCol = 0;
490             }
491             else
492             {
493                 /* We're fine -- scroll left by that much */
494                 Channel->CursorCol -= Number;
495             }
496 
497             /* All done */
498             VTUTF8ChannelAssertCursor(Channel);
499             break;
500 
501         case SacCursorRight:
502             /* Check if we're scrolling too much to the right */
503             if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH)
504             {
505                 /* Reset the column to the right-most margin */
506                 Channel->CursorCol = SAC_VTUTF8_COL_WIDTH;
507             }
508             else
509             {
510                 /* We're fine -- scroll right by that much */
511                 Channel->CursorCol += Number;
512             }
513 
514             /* All done */
515             VTUTF8ChannelAssertCursor(Channel);
516             break;
517 
518         case SacFontNormal:
519             /* Reset the cell attributes */
520             Channel->CellFlags = 0;
521             Channel->CellBackColor = SetBackColorBlack;
522             Channel->CellForeColor = SetColorWhite;
523             break;
524 
525         case SacFontBlink:
526             /* Set the appropriate flag */
527             Channel->CellFlags |= SAC_CELL_FLAG_BLINK;
528             break;
529 
530         case SacFontBlinkOff:
531             /* Clear the appropriate flag */
532             Channel->CellFlags &= ~SAC_CELL_FLAG_BLINK;
533             break;
534 
535         case SacFontBold:
536             /* Set the appropriate flag */
537             Channel->CellFlags |= SAC_CELL_FLAG_BOLD;
538             break;
539 
540         case SacFontBoldOff:
541             /* Clear the appropriate flag */
542             Channel->CellFlags &= ~SAC_CELL_FLAG_BOLD;
543             break;
544 
545         case SacFontInverse:
546             /* Set the appropriate flag */
547             Channel->CellFlags |= SAC_CELL_FLAG_INVERTED;
548             break;
549 
550         case SacFontInverseOff:
551             /* Clear the appropriate flag */
552             Channel->CellFlags &= ~SAC_CELL_FLAG_INVERTED;
553             break;
554 
555         case SacEraseEndOfLine:
556             /* Loop all columns in this line past the current position */
557             for (i = Channel->CursorCol; i < SAC_VTUTF8_COL_WIDTH; i++)
558             {
559                 /* Replace everything after the current position with blanks */
560                 Screen->Cell[Channel->CursorRow][i].CellFlags = Channel->CellFlags;
561                 Screen->Cell[Channel->CursorRow][i].CellBackColor = Channel->CellForeColor;
562                 Screen->Cell[Channel->CursorRow][i].CellForeColor = Channel->CellBackColor;
563                 Screen->Cell[Channel->CursorRow][i].Char = L' ';
564             }
565             break;
566 
567         case SacEraseStartOfLine:
568             /* Loop all columns in this line, before the current position */
569             for (i = 0; i < (Channel->CursorCol + 1); i++)
570             {
571                 /* Replace everything after the current position with blanks */
572                 Screen->Cell[Channel->CursorRow][i].CellFlags = Channel->CellFlags;
573                 Screen->Cell[Channel->CursorRow][i].CellBackColor = Channel->CellForeColor;
574                 Screen->Cell[Channel->CursorRow][i].CellForeColor = Channel->CellBackColor;
575                 Screen->Cell[Channel->CursorRow][i].Char = L' ';
576             }
577             break;
578 
579         case SacEraseLine:
580             /* Loop all the columns in this line */
581             for (i = 0; i < SAC_VTUTF8_COL_WIDTH; i++)
582             {
583                 /* Replace them all with blanks */
584                 Screen->Cell[Channel->CursorRow][i].CellFlags = Channel->CellFlags;
585                 Screen->Cell[Channel->CursorRow][i].CellBackColor = Channel->CellForeColor;
586                 Screen->Cell[Channel->CursorRow][i].CellForeColor = Channel->CellBackColor;
587                 Screen->Cell[Channel->CursorRow][i].Char = L' ';
588             }
589             break;
590 
591         case SacEraseEndOfScreen:
592             ASSERT(FALSE); // todo
593             break;
594 
595         case SacEraseStartOfScreen:
596             ASSERT(FALSE); // todo
597             break;
598 
599         case SacEraseScreen:
600             ASSERT(FALSE); // todo
601             break;
602 
603         case SacSetCursorPosition:
604             ASSERT(FALSE); // todo
605             break;
606 
607         case SacSetScrollRegion:
608             ASSERT(FALSE); // todo
609             break;
610 
611         case SacSetColors:
612             /* Set the cell colors */
613             Channel->CellForeColor = Number;
614             Channel->CellBackColor = Number2;
615             break;
616 
617         case SacSetBackgroundColor:
618             /* Set the cell back color */
619             Channel->CellBackColor = Number;
620             break;
621 
622         case SacSetFontColor:
623             /* Set the cell text color */
624             Channel->CellForeColor = Number;
625             break;
626 
627         case SacSetColorsAndAttributes:
628             /* Set the cell flag and colors */
629             Channel->CellFlags = Number;
630             Channel->CellForeColor = Number2;
631             Channel->CellBackColor = Number3;
632             break;
633 
634         default:
635             /* Unknown, do nothing */
636             break;
637     }
638 
639     /* Return the length of the sequence */
640     return Result;
641 }
642 
643 NTSTATUS
644 NTAPI
VTUTF8ChannelOInit(IN PSAC_CHANNEL Channel)645 VTUTF8ChannelOInit(IN PSAC_CHANNEL Channel)
646 {
647     PSAC_VTUTF8_SCREEN Screen;
648     ULONG R, C;
649     CHECK_PARAMETER(Channel);
650 
651     /* Set the current channel cell parameters */
652     Channel->CellFlags = 0;
653     Channel->CellBackColor = SetBackColorBlack;
654     Channel->CellForeColor = SetColorWhite;
655 
656     /* Set the cell buffer position */
657     Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
658 
659     /* Loop the output buffer height by width */
660     for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++)
661     {
662         for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++)
663         {
664             /* For every character, set the defaults */
665             Screen->Cell[R][C].Char = L' ';
666             Screen->Cell[R][C].CellBackColor = SetBackColorBlack;
667             Screen->Cell[R][C].CellForeColor = SetColorWhite;
668         }
669     }
670 
671     /* All done */
672     return STATUS_SUCCESS;
673 }
674 
675 NTSTATUS
676 NTAPI
VTUTF8ChannelCreate(IN PSAC_CHANNEL Channel)677 VTUTF8ChannelCreate(IN PSAC_CHANNEL Channel)
678 {
679     NTSTATUS Status;
680     CHECK_PARAMETER(Channel);
681 
682     /* Allocate the output buffer */
683     Channel->OBuffer = SacAllocatePool(SAC_VTUTF8_OBUFFER_SIZE, GLOBAL_BLOCK_TAG);
684     CHECK_ALLOCATION(Channel->OBuffer);
685 
686     /* Allocate the input buffer */
687     Channel->IBuffer = SacAllocatePool(SAC_VTUTF8_IBUFFER_SIZE, GLOBAL_BLOCK_TAG);
688     CHECK_ALLOCATION(Channel->IBuffer);
689 
690     /* Initialize the output stream */
691     Status = VTUTF8ChannelOInit(Channel);
692     if (NT_SUCCESS(Status)) return Status;
693 
694     /* Reset all flags and return success */
695     _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 0);
696     _InterlockedExchange(&Channel->ChannelHasNewIBufferData, 0);
697     return STATUS_SUCCESS;
698 }
699 
700 NTSTATUS
701 NTAPI
VTUTF8ChannelDestroy(IN PSAC_CHANNEL Channel)702 VTUTF8ChannelDestroy(IN PSAC_CHANNEL Channel)
703 {
704     CHECK_PARAMETER(Channel);
705 
706     /* Free the buffer and then destroy the channel */
707     if (Channel->OBuffer) SacFreePool(Channel->OBuffer);
708     if (Channel->IBuffer) SacFreePool(Channel->IBuffer);
709     return ChannelDestroy(Channel);
710 }
711 
712 NTSTATUS
713 NTAPI
VTUTF8ChannelORead(IN PSAC_CHANNEL Channel,IN PCHAR Buffer,IN ULONG BufferSize,OUT PULONG ByteCount)714 VTUTF8ChannelORead(IN PSAC_CHANNEL Channel,
715                    IN PCHAR Buffer,
716                    IN ULONG BufferSize,
717                    OUT PULONG ByteCount)
718 {
719     ASSERT(FALSE);
720     return STATUS_NOT_IMPLEMENTED;
721 }
722 
723 NTSTATUS
724 NTAPI
VTUTF8ChannelOFlush(IN PSAC_CHANNEL Channel)725 VTUTF8ChannelOFlush(IN PSAC_CHANNEL Channel)
726 {
727     NTSTATUS Status;
728     PSAC_VTUTF8_SCREEN Screen;
729     INT Color[2], Position[2];
730     ULONG Utf8ProcessedCount, Utf8Count, R, C, ForeColor, BackColor, Attribute;
731     PWCHAR TmpBuffer;
732     BOOLEAN Overflow = FALSE;
733     CHECK_PARAMETER(Channel);
734 
735     /* Set the cell buffer position */
736     Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
737 
738     /* Allocate a temporary buffer */
739     TmpBuffer = SacAllocatePool(40, GLOBAL_BLOCK_TAG);
740     if (!TmpBuffer)
741     {
742         Status = STATUS_NO_MEMORY;
743         goto Quickie;
744     }
745 
746     /* First, clear the screen */
747     Status = VTUTF8ChannelAnsiDispatch(Channel,
748                                        SacAnsiClearScreen,
749                                        NULL,
750                                        0);
751     if (!NT_SUCCESS(Status)) goto Quickie;
752 
753     /* Next, reset the cursor position */
754     Position[1] = 0;
755     Position[0] = 0;
756     Status = VTUTF8ChannelAnsiDispatch(Channel,
757                                        SacAnsiSetPosition,
758                                        Position,
759                                        sizeof(Position));
760     if (!NT_SUCCESS(Status)) goto Quickie;
761 
762     /* Finally, reset the attributes */
763     Status = VTUTF8ChannelAnsiDispatch(Channel,
764                                        SacAnsiClearAttributes,
765                                        NULL,
766                                        0);
767     if (!NT_SUCCESS(Status)) goto Quickie;
768 
769     /* Now set the current cell attributes */
770     Attribute = Channel->CellFlags;
771     Status = VTUTF8ChannelProcessAttributes(Channel, Attribute);
772     if (!NT_SUCCESS(Status)) goto Quickie;
773 
774     /* And set the current cell colors */
775     ForeColor = Channel->CellForeColor;
776     BackColor = Channel->CellBackColor;
777     Color[1] = BackColor;
778     Color[0] = ForeColor;
779     Status = VTUTF8ChannelAnsiDispatch(Channel,
780                                        SacAnsiSetColors,
781                                        Color,
782                                        sizeof(Color));
783     if (!NT_SUCCESS(Status)) goto Quickie;
784 
785     /* Now loop all the characters in the cell buffer */
786     for (R = 0; R < SAC_VTUTF8_ROW_HEIGHT; R++)
787     {
788         /* Across every row */
789         for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++)
790         {
791             /* Check if there's been a change in colors */
792             if ((Screen->Cell[R][C].CellBackColor != BackColor) ||
793                 (Screen->Cell[R][C].CellForeColor != ForeColor))
794             {
795                 /* New colors are being drawn -- are we also on a new row now? */
796                 if (Overflow)
797                 {
798                     /* Reposition the cursor correctly */
799                     Position[1] = R;
800                     Position[0] = C;
801                     Status = VTUTF8ChannelAnsiDispatch(Channel,
802                                                        SacAnsiSetPosition,
803                                                        Position,
804                                                        sizeof(Position));
805                     if (!NT_SUCCESS(Status)) goto Quickie;
806                     Overflow = FALSE;
807                 }
808 
809                 /* Cache the new colors */
810                 ForeColor = Screen->Cell[R][C].CellForeColor;
811                 BackColor = Screen->Cell[R][C].CellBackColor;
812 
813                 /* Set them on the screen */
814                 Color[1] = BackColor;
815                 Color[0] = ForeColor;
816                 Status = VTUTF8ChannelAnsiDispatch(Channel,
817                                                    SacAnsiSetColors,
818                                                    Color,
819                                                    sizeof(Color));
820                 if (!NT_SUCCESS(Status)) goto Quickie;
821             }
822 
823             /* Check if there's been a change in attributes */
824             if (Screen->Cell[R][C].CellFlags != Attribute)
825             {
826                 /* Yep! Are we also on a new row now? */
827                 if (Overflow)
828                 {
829                     /* Reposition the cursor correctly */
830                     Position[1] = R;
831                     Position[0] = C;
832                     Status = VTUTF8ChannelAnsiDispatch(Channel,
833                                                        SacAnsiSetPosition,
834                                                        Position,
835                                                        sizeof(Position));
836                     if (!NT_SUCCESS(Status)) goto Quickie;
837                     Overflow = FALSE;
838                 }
839 
840                 /* Set the new attributes on screen */
841                 Attribute = Screen->Cell[R][C].CellFlags;
842                 Status = VTUTF8ChannelProcessAttributes(Channel, Attribute);
843                 if (!NT_SUCCESS(Status)) goto Quickie;
844             }
845 
846             /* Time to write the character -- are we on a new row now? */
847             if (Overflow)
848             {
849                 /* Reposition the cursor correctly */
850                 Position[1] = R;
851                 Position[0] = C;
852                 Status = VTUTF8ChannelAnsiDispatch(Channel,
853                                                    SacAnsiSetPosition,
854                                                    Position,
855                                                    sizeof(Position));
856                 if (!NT_SUCCESS(Status)) goto Quickie;
857                 Overflow = FALSE;
858             }
859 
860             /* Write the character into our temporary buffer */
861             *TmpBuffer = Screen->Cell[R][C].Char;
862             TmpBuffer[1] = UNICODE_NULL;
863 
864             /* Convert it to UTF-8 */
865             if (!SacTranslateUnicodeToUtf8(TmpBuffer,
866                                            1,
867                                            Utf8ConversionBuffer,
868                                            Utf8ConversionBufferSize,
869                                            &Utf8Count,
870                                            &Utf8ProcessedCount))
871             {
872                 /* Bail out if this failed */
873                 Status = STATUS_UNSUCCESSFUL;
874                 goto Quickie;
875             }
876 
877             /* Make sure we have a remaining valid character */
878             if (Utf8Count)
879             {
880                 /* Write it out on the wire */
881                 Status = ConMgrWriteData(Channel, Utf8ConversionBuffer, Utf8Count);
882                 if (!NT_SUCCESS(Status)) goto Quickie;
883             }
884         }
885 
886         /* All the characters on the row are done, indicate we need a reset */
887         Overflow = TRUE;
888     }
889 
890     /* Everything is done, set the position one last time */
891     Position[1] = Channel->CursorRow;
892     Position[0] = Channel->CursorCol;
893     Status = VTUTF8ChannelAnsiDispatch(Channel,
894                                        SacAnsiSetPosition,
895                                        Position,
896                                        sizeof(Position));
897     if (!NT_SUCCESS(Status)) goto Quickie;
898 
899     /* Set the current attribute one last time */
900     Status = VTUTF8ChannelProcessAttributes(Channel, Channel->CellFlags);
901     if (!NT_SUCCESS(Status)) goto Quickie;
902 
903     /* Set the current colors one last time */
904     Color[1] = Channel->CellBackColor;
905     Color[0] = Channel->CellForeColor;
906     Status = VTUTF8ChannelAnsiDispatch(Channel,
907                                        SacAnsiSetColors,
908                                        Color,
909                                        sizeof(Color));
910     if (!NT_SUCCESS(Status)) goto Quickie;
911 
912     /* Flush all the data out on the wire */
913     Status = ConMgrFlushData(Channel);
914 
915 Quickie:
916     /* We're done, free the temporary buffer */
917     if (TmpBuffer) SacFreePool(TmpBuffer);
918 
919     /* Indicate that all new data has been flushed now */
920     if (NT_SUCCESS(Status))
921     {
922         _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 0);
923     }
924 
925     /* Return the result */
926     return Status;
927 }
928 
929 NTSTATUS
930 NTAPI
VTUTF8ChannelOWrite2(IN PSAC_CHANNEL Channel,IN PWCHAR String,IN ULONG Size)931 VTUTF8ChannelOWrite2(IN PSAC_CHANNEL Channel,
932                      IN PWCHAR String,
933                      IN ULONG Size)
934 {
935     PSAC_VTUTF8_SCREEN Screen;
936     ULONG i, EscapeSize, R, C;
937     PWSTR pwch;
938     CHECK_PARAMETER1(Channel);
939     CHECK_PARAMETER2(String);
940     VTUTF8ChannelAssertCursor(Channel);
941 
942     /* Loop every character */
943     Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer;
944     for (i = 0; i < Size; i++)
945     {
946         /* Check what the character is */
947         pwch = &String[i];
948         switch (*pwch)
949         {
950             /* It's an escape sequence... */
951             case L'\x1B':
952 
953                 /* Send it to the parser, see how long the sequence was */
954                 EscapeSize = VTUTF8ChannelConsumeEscapeSequence(Channel, pwch);
955                 if (EscapeSize)
956                 {
957                     /* Consume that many characters for next time*/
958                     i += EscapeSize - 1;
959                 }
960                 else
961                 {
962                     /* Invalid escape sequence, skip just the ESC character */
963                     i++;
964                 }
965 
966                 /* Keep going*/
967                 break;
968 
969             /* It's a line feed */
970             case L'\n':
971 
972                 /* Simply reset the column to zero on the current line */
973                 Channel->CursorCol = 0;
974                 break;
975 
976             /* It's a carriage feed */
977             case L'\r':
978 
979                 /* Move to the next row */
980                 Channel->CursorRow++;
981 
982                 /* Check if we hit the last row on the screen */
983                 if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT)
984                 {
985                     /* Go over every row before the last one */
986                     for (R = 0; R < (SAC_VTUTF8_ROW_HEIGHT - 1); R++)
987                     {
988                         /* Sanity check, since we always copy one row below */
989                         ASSERT((R + 1) < SAC_VTUTF8_ROW_HEIGHT);
990 
991                         /* Loop every character on the row */
992                         for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++)
993                         {
994                             /* And replace it with one from the row below */
995                             Screen->Cell[R][C] = Screen->Cell[R + 1][C];
996                         }
997                     }
998 
999                     /* Now we're left with the before-last row, zero it out */
1000                     ASSERT(R == (SAC_VTUTF8_ROW_HEIGHT - 1));
1001                     RtlZeroMemory(&Screen->Cell[R], sizeof(Screen->Cell[R]));
1002 
1003                     /* Reset the row back by one */
1004                     Channel->CursorRow--;
1005                     VTUTF8ChannelAssertCursor(Channel);
1006                 }
1007                 break;
1008 
1009             /* It's a TAB character */
1010             case L'\t':
1011 
1012                 /* Loop the remaining characters until a multiple of 4 */
1013                 VTUTF8ChannelAssertCursor(Channel);
1014                 for (C = (4 - Channel->CursorCol % 4); C; C--)
1015                 {
1016                     /* Fill each remaining character with a space */
1017                     VTUTF8ChannelAssertCursor(Channel);
1018                     Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellFlags = Channel->CellFlags;
1019                     Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellBackColor = Channel->CellBackColor;
1020                     Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellForeColor = Channel->CellForeColor;
1021                     Screen->Cell[Channel->CursorRow][Channel->CursorCol].Char = L' ';
1022 
1023                     /* Move to the next character position, but don't overflow */
1024                     Channel->CursorCol++;
1025                     if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH)
1026                     {
1027                         Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
1028                     }
1029                 }
1030 
1031                 /* All done, move to the next one */
1032                 VTUTF8ChannelAssertCursor(Channel);
1033                 break;
1034 
1035             /* It's a backspace or delete character */
1036             case L'\b':
1037             case L'\x7F':
1038 
1039                 /* Move back one character, unless we had nothing typed */
1040                 if (Channel->CursorCol) Channel->CursorCol--;
1041                 VTUTF8ChannelAssertCursor(Channel);
1042                 break;
1043 
1044             /* It's some other character */
1045             default:
1046 
1047                 /* Is it non-printable? Ignore it and keep parsing */
1048                 if (*pwch < L' ') continue;
1049 
1050                 /* Otherwise, print it out with the current attributes */
1051                 VTUTF8ChannelAssertCursor(Channel);
1052                 Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellFlags = Channel->CellFlags;
1053                 Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellBackColor = Channel->CellBackColor;
1054                 Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellForeColor = Channel->CellForeColor;
1055                 Screen->Cell[Channel->CursorRow][Channel->CursorCol].Char = *pwch;
1056 
1057                 /* Move forward one character, but make sure not to overflow */
1058                 Channel->CursorCol++;
1059                 if (Channel->CursorCol == SAC_VTUTF8_COL_WIDTH)
1060                 {
1061                     Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1;
1062                 }
1063 
1064                 /* All done, move to the next one */
1065                 VTUTF8ChannelAssertCursor(Channel);
1066                 break;
1067             }
1068     }
1069 
1070     /* Parsing of the input string completed -- string was written */
1071     VTUTF8ChannelAssertCursor(Channel);
1072     return STATUS_SUCCESS;
1073 }
1074 
1075 NTSTATUS
1076 NTAPI
VTUTF8ChannelOEcho(IN PSAC_CHANNEL Channel,IN PCHAR String,IN ULONG Size)1077 VTUTF8ChannelOEcho(IN PSAC_CHANNEL Channel,
1078                    IN PCHAR String,
1079                    IN ULONG Size)
1080 {
1081     NTSTATUS Status = STATUS_SUCCESS;
1082     PWSTR pwch;
1083     ULONG i, k, TranslatedCount, UTF8TranslationSize;
1084     BOOLEAN Result;
1085     CHECK_PARAMETER1(Channel);
1086     CHECK_PARAMETER2(String);
1087 
1088     /* Return success if there's nothing to echo */
1089     if (!(Size / sizeof(WCHAR))) return Status;
1090 
1091     /* Start with the input string */
1092     pwch = (PWCHAR)String;
1093 
1094     /* First, figure out how much is outside of the block length alignment */
1095     k = (Size / sizeof(WCHAR)) % MAX_UTF8_ENCODE_BLOCK_LENGTH;
1096     if (k)
1097     {
1098         /* Translate the misaligned portion */
1099         Result = SacTranslateUnicodeToUtf8(pwch,
1100                                            k,
1101                                            Utf8ConversionBuffer,
1102                                            Utf8ConversionBufferSize,
1103                                            &UTF8TranslationSize,
1104                                            &TranslatedCount);
1105         ASSERT(k == TranslatedCount);
1106         if (!Result)
1107         {
1108             /* If we couldn't translate, write failure to break out below */
1109             Status = STATUS_UNSUCCESSFUL;
1110         }
1111         else
1112         {
1113             /* Write the misaligned portion into the buffer */
1114             Status = ConMgrWriteData(Channel,
1115                                      Utf8ConversionBuffer,
1116                                      UTF8TranslationSize);
1117         }
1118 
1119         /* If translation or write failed, bail out */
1120         if (!NT_SUCCESS(Status)) goto Return;
1121     }
1122 
1123     /* Push the string to its new location (this could be a noop if aligned) */
1124     pwch += k;
1125 
1126     /* Now figure out how many aligned blocks we have, and loop each one */
1127     k = (Size / sizeof(WCHAR)) / MAX_UTF8_ENCODE_BLOCK_LENGTH;
1128     for (i = 0; i < k; i++)
1129     {
1130         /* Translate the aligned block */
1131         Result = SacTranslateUnicodeToUtf8(pwch,
1132                                            MAX_UTF8_ENCODE_BLOCK_LENGTH,
1133                                            Utf8ConversionBuffer,
1134                                            Utf8ConversionBufferSize,
1135                                            &UTF8TranslationSize,
1136                                            &TranslatedCount);
1137         ASSERT(MAX_UTF8_ENCODE_BLOCK_LENGTH == TranslatedCount);
1138         ASSERT(UTF8TranslationSize > 0);
1139         if (!Result)
1140         {
1141             /* Set failure here, we'll break out below */
1142             Status = STATUS_UNSUCCESSFUL;
1143         }
1144         else
1145         {
1146             /* Move the string location to the next aligned block */
1147             pwch += MAX_UTF8_ENCODE_BLOCK_LENGTH;
1148 
1149             /* Write the aligned block into the buffer */
1150             Status = ConMgrWriteData(Channel,
1151                                      Utf8ConversionBuffer,
1152                                      UTF8TranslationSize);
1153         }
1154 
1155         /* If translation or write failed, bail out */
1156         if (!NT_SUCCESS(Status)) break;
1157     }
1158 
1159 Return:
1160     ASSERT(pwch == (PWSTR)(String + Size));
1161     if (NT_SUCCESS(Status)) Status = ConMgrFlushData(Channel);
1162     return Status;
1163 }
1164 
1165 NTSTATUS
1166 NTAPI
VTUTF8ChannelOWrite(IN PSAC_CHANNEL Channel,IN PCHAR String,IN ULONG Length)1167 VTUTF8ChannelOWrite(IN PSAC_CHANNEL Channel,
1168                     IN PCHAR String,
1169                     IN ULONG Length)
1170 {
1171     NTSTATUS Status;
1172     CHECK_PARAMETER1(Channel);
1173     CHECK_PARAMETER2(String);
1174 
1175     /* Call the lower level function */
1176     Status = VTUTF8ChannelOWrite2(Channel, (PWCHAR)String, Length / sizeof(WCHAR));
1177     if (NT_SUCCESS(Status))
1178     {
1179         /* Is the channel enabled for output? */
1180         if ((ConMgrIsWriteEnabled(Channel)) && (Channel->WriteEnabled))
1181         {
1182             /* Go ahead and output it */
1183             Status = VTUTF8ChannelOEcho(Channel, String, Length);
1184         }
1185         else
1186         {
1187             /* Otherwise, just remember that we have new data */
1188             _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 1);
1189         }
1190     }
1191 
1192     /* We're done */
1193     return Status;
1194 }
1195 
1196 ULONG
1197 NTAPI
VTUTF8ChannelGetIBufferIndex(IN PSAC_CHANNEL Channel)1198 VTUTF8ChannelGetIBufferIndex(IN PSAC_CHANNEL Channel)
1199 {
1200     ASSERT(Channel);
1201     ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
1202     ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
1203 
1204     /* Return the current buffer index */
1205     return Channel->IBufferIndex;
1206 }
1207 
1208 VOID
1209 NTAPI
VTUTF8ChannelSetIBufferIndex(IN PSAC_CHANNEL Channel,IN ULONG BufferIndex)1210 VTUTF8ChannelSetIBufferIndex(IN PSAC_CHANNEL Channel,
1211                              IN ULONG BufferIndex)
1212 {
1213     NTSTATUS Status;
1214     ASSERT(Channel);
1215     ASSERT((Channel->IBufferIndex % sizeof(WCHAR)) == 0);
1216     ASSERT(Channel->IBufferIndex < SAC_VTUTF8_IBUFFER_SIZE);
1217 
1218     /* Set the new index, and if it's not zero, it means we have data */
1219     Channel->IBufferIndex = BufferIndex;
1220     _InterlockedExchange(&Channel->ChannelHasNewIBufferData, BufferIndex != 0);
1221 
1222     /* If we have new data, and an event has been registered... */
1223     if (!(Channel->IBufferIndex) &&
1224         (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT))
1225     {
1226         /* Go ahead and signal it */
1227         ChannelClearEvent(Channel, HasNewDataEvent);
1228         UNREFERENCED_PARAMETER(Status);
1229     }
1230 }
1231 
1232 NTSTATUS
1233 NTAPI
VTUTF8ChannelIRead(IN PSAC_CHANNEL Channel,IN PCHAR Buffer,IN ULONG BufferSize,IN PULONG ReturnBufferSize)1234 VTUTF8ChannelIRead(IN PSAC_CHANNEL Channel,
1235                    IN PCHAR Buffer,
1236                    IN ULONG BufferSize,
1237                    IN PULONG ReturnBufferSize)
1238 {
1239     ULONG CopyChars, ReadLength;
1240     CHECK_PARAMETER1(Channel);
1241     CHECK_PARAMETER2(Buffer);
1242     CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
1243 
1244     /* Assume failure */
1245     *ReturnBufferSize = 0;
1246 
1247     /* Check how many bytes are in the buffer */
1248     if (Channel->ChannelInputBufferLength(Channel) == 0)
1249     {
1250         /* Apparently nothing. Make sure the flag indicates so too */
1251         ASSERT(ChannelHasNewIBufferData(Channel) == FALSE);
1252     }
1253     else
1254     {
1255         /* Use the smallest number of bytes either in the buffer or requested */
1256         ReadLength = min(Channel->ChannelInputBufferLength(Channel) * sizeof(WCHAR),
1257                          BufferSize);
1258 
1259         /* Do some cheezy buffer alignment */
1260         CopyChars = ReadLength / sizeof(WCHAR);
1261         ReadLength = CopyChars * sizeof(WCHAR);
1262         ASSERT(CopyChars <= Channel->ChannelInputBufferLength(Channel));
1263 
1264         /* Copy them into the caller's buffer */
1265         RtlCopyMemory(Buffer, Channel->IBuffer, ReadLength);
1266 
1267         /* Update the channel's index past the copied (read) bytes */
1268         VTUTF8ChannelSetIBufferIndex(Channel,
1269             VTUTF8ChannelGetIBufferIndex(Channel) - ReadLength);
1270 
1271         /* Are there still bytes that haven't been read yet? */
1272         if (Channel->ChannelInputBufferLength(Channel))
1273         {
1274             /* Shift them up in the buffer */
1275             RtlMoveMemory(Channel->IBuffer,
1276                           &Channel->IBuffer[ReadLength],
1277                           Channel->ChannelInputBufferLength(Channel) *
1278                           sizeof(WCHAR));
1279         }
1280 
1281         /* Return the number of bytes we actually copied */
1282         *ReturnBufferSize = ReadLength;
1283     }
1284 
1285     /* Return success */
1286     return STATUS_SUCCESS;
1287 }
1288 
1289 NTSTATUS
1290 NTAPI
VTUTF8ChannelIBufferIsFull(IN PSAC_CHANNEL Channel,OUT PBOOLEAN BufferStatus)1291 VTUTF8ChannelIBufferIsFull(IN PSAC_CHANNEL Channel,
1292                            OUT PBOOLEAN BufferStatus)
1293 {
1294     CHECK_PARAMETER1(Channel);
1295 
1296     /* If the index is beyond the length, the buffer must be full */
1297     *BufferStatus = VTUTF8ChannelGetIBufferIndex(Channel) > SAC_VTUTF8_IBUFFER_SIZE;
1298     return STATUS_SUCCESS;
1299 }
1300 
1301 ULONG
1302 NTAPI
VTUTF8ChannelIBufferLength(IN PSAC_CHANNEL Channel)1303 VTUTF8ChannelIBufferLength(IN PSAC_CHANNEL Channel)
1304 {
1305     ASSERT(Channel);
1306 
1307     /* The index is the length, so divide by two to get character count */
1308     return VTUTF8ChannelGetIBufferIndex(Channel) / sizeof(WCHAR);
1309 }
1310 
1311 WCHAR
1312 NTAPI
VTUTF8ChannelIReadLast(IN PSAC_CHANNEL Channel)1313 VTUTF8ChannelIReadLast(IN PSAC_CHANNEL Channel)
1314 {
1315     PWCHAR LastCharLocation;
1316     WCHAR LastChar = 0;
1317     ASSERT(Channel);
1318 
1319     /* Check if there's anything to read in the buffer */
1320     if (Channel->ChannelInputBufferLength(Channel))
1321     {
1322         /* Go back one character */
1323         VTUTF8ChannelSetIBufferIndex(Channel,
1324                                      VTUTF8ChannelGetIBufferIndex(Channel) -
1325                                      sizeof(WCHAR));
1326 
1327         /* Read it, and clear its current value */
1328         LastCharLocation = (PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)];
1329         LastChar = *LastCharLocation;
1330         *LastCharLocation = UNICODE_NULL;
1331     }
1332 
1333     /* Return the last character */
1334     return LastChar;
1335 }
1336 
1337 NTSTATUS
1338 NTAPI
VTUTF8ChannelIWrite(IN PSAC_CHANNEL Channel,IN PCHAR Buffer,IN ULONG BufferSize)1339 VTUTF8ChannelIWrite(IN PSAC_CHANNEL Channel,
1340                     IN PCHAR Buffer,
1341                     IN ULONG BufferSize)
1342 {
1343     NTSTATUS Status;
1344     BOOLEAN IsFull;
1345     ULONG Index, i;
1346     CHECK_PARAMETER1(Channel);
1347     CHECK_PARAMETER2(Buffer);
1348     CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE);
1349 
1350     /* First, check if the input buffer still has space */
1351     Status = VTUTF8ChannelIBufferIsFull(Channel, &IsFull);
1352     if (!NT_SUCCESS(Status)) return Status;
1353     if (IsFull) return STATUS_UNSUCCESSFUL;
1354 
1355     /* Get the current buffer index */
1356     Index = VTUTF8ChannelGetIBufferIndex(Channel);
1357     if ((SAC_VTUTF8_IBUFFER_SIZE - Index) < BufferSize)
1358     {
1359         return STATUS_INSUFFICIENT_RESOURCES;
1360     }
1361 
1362     /* Copy the new data */
1363     for (i = 0; i < BufferSize; i++)
1364     {
1365         /* Convert the character */
1366         if (SacTranslateUtf8ToUnicode(Buffer[i],
1367                                       IncomingUtf8ConversionBuffer,
1368                                       &IncomingUnicodeValue))
1369         {
1370             /* Write it into the buffer */
1371             *(PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)] =
1372                 IncomingUnicodeValue;
1373 
1374             /* Update the index */
1375             Index = VTUTF8ChannelGetIBufferIndex(Channel);
1376             VTUTF8ChannelSetIBufferIndex(Channel, Index + sizeof(WCHAR));
1377         }
1378     }
1379 
1380     /* Signal the event, if one was set */
1381     if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT)
1382     {
1383         ChannelSetEvent(Channel, HasNewDataEvent);
1384     }
1385 
1386     /* All done */
1387     return STATUS_SUCCESS;
1388 }
1389