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