NTSTATUS NTAPI VTUTF8ChannelOWrite(IN PSAC_CHANNEL Channel, IN PCHAR String, IN ULONG Length) { NTSTATUS Status; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(String); /* Call the lower level function */ Status = VTUTF8ChannelOWrite2(Channel, (PWCHAR)String, Length / sizeof(WCHAR)); if (NT_SUCCESS(Status)) { /* Is the channel enabled for output? */ if ((ConMgrIsWriteEnabled(Channel)) && (Channel->WriteEnabled)) { /* Go ahead and output it */ Status = VTUTF8ChannelOEcho(Channel, String, Length); } else { /* Otherwise, just remember that we have new data */ _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 1); } } /* We're done */ return Status; }
NTSTATUS NTAPI VTUTF8ChannelIRead(IN PSAC_CHANNEL Channel, IN PCHAR Buffer, IN ULONG BufferSize, IN PULONG ReturnBufferSize) { ULONG CopyChars, ReadLength; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Buffer); CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE); /* Assume failure */ *ReturnBufferSize = 0; /* Check how many bytes are in the buffer */ if (Channel->ChannelInputBufferLength(Channel) == 0) { /* Apparently nothing. Make sure the flag indicates so too */ ASSERT(ChannelHasNewIBufferData(Channel) == FALSE); } else { /* Use the smallest number of bytes either in the buffer or requested */ ReadLength = min(Channel->ChannelInputBufferLength(Channel) * sizeof(WCHAR), BufferSize); /* Do some cheezy buffer alignment */ CopyChars = ReadLength / sizeof(WCHAR); ReadLength = CopyChars * sizeof(WCHAR); ASSERT(CopyChars <= Channel->ChannelInputBufferLength(Channel)); /* Copy them into the caller's buffer */ RtlCopyMemory(Buffer, Channel->IBuffer, ReadLength); /* Update the channel's index past the copied (read) bytes */ VTUTF8ChannelSetIBufferIndex(Channel, VTUTF8ChannelGetIBufferIndex(Channel) - ReadLength); /* Are there still bytes that haven't been read yet? */ if (Channel->ChannelInputBufferLength(Channel)) { /* Shift them up in the buffer */ RtlMoveMemory(Channel->IBuffer, &Channel->IBuffer[ReadLength], Channel->ChannelInputBufferLength(Channel) * sizeof(WCHAR)); } /* Return the number of bytes we actually copied */ *ReturnBufferSize = ReadLength; } /* Return success */ return STATUS_SUCCESS; }
NTSTATUS NTAPI RawChannelORead(IN PSAC_CHANNEL Channel, IN PCHAR Buffer, IN ULONG BufferSize, OUT PULONG ByteCount) { NTSTATUS Status; ULONG NextIndex; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Buffer); CHECK_PARAMETER3(BufferSize > 0); CHECK_PARAMETER4(ByteCount); *ByteCount = 0; if (ChannelHasNewOBufferData(Channel)) { Status = STATUS_SUCCESS; while (TRUE) { Buffer[(*ByteCount)++] = Channel->OBuffer[Channel->OBufferFirstGoodIndex]; NextIndex = (Channel->OBufferFirstGoodIndex + 1) & (SAC_OBUFFER_SIZE - 1); Channel->OBufferFirstGoodIndex = NextIndex; if (NextIndex == Channel->OBufferIndex) { _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 0); break; } ASSERT(*ByteCount > 0); if (*ByteCount >= BufferSize) break; } } else { Status = STATUS_NO_DATA_DETECTED; } if (Channel->OBufferFirstGoodIndex == Channel->OBufferIndex) { ASSERT(ChannelHasNewOBufferData(Channel) == FALSE); } if (ChannelHasNewOBufferData(Channel) == FALSE) { ASSERT(Channel->OBufferFirstGoodIndex == Channel->OBufferIndex); } return Status; }
NTSTATUS NTAPI ChannelHasRedrawEvent(IN PSAC_CHANNEL Channel, OUT PBOOLEAN Present) { CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Present); /* Return if the flag is set */ *Present = Channel->Flags & SAC_CHANNEL_FLAG_REDRAW_EVENT; return STATUS_SUCCESS; }
NTSTATUS NTAPI RawChannelIBufferIsFull(IN PSAC_CHANNEL Channel, OUT PBOOLEAN BufferStatus) { CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(BufferStatus); /* If the index is beyond the length, the buffer must be full */ *BufferStatus = RawChannelGetIBufferIndex(Channel) > SAC_RAW_IBUFFER_SIZE; return STATUS_SUCCESS; }
NTSTATUS NTAPI VTUTF8ChannelIWrite(IN PSAC_CHANNEL Channel, IN PCHAR Buffer, IN ULONG BufferSize) { NTSTATUS Status; BOOLEAN IsFull; ULONG Index, i; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Buffer); CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE); /* First, check if the input buffer still has space */ Status = VTUTF8ChannelIBufferIsFull(Channel, &IsFull); if (!NT_SUCCESS(Status)) return Status; if (IsFull) return STATUS_UNSUCCESSFUL; /* Get the current buffer index */ Index = VTUTF8ChannelGetIBufferIndex(Channel); if ((SAC_VTUTF8_IBUFFER_SIZE - Index) < BufferSize) { return STATUS_INSUFFICIENT_RESOURCES; } /* Copy the new data */ for (i = 0; i < BufferSize; i++) { /* Convert the character */ if (SacTranslateUtf8ToUnicode(Buffer[i], IncomingUtf8ConversionBuffer, &IncomingUnicodeValue)) { /* Write it into the buffer */ *(PWCHAR)&Channel->IBuffer[VTUTF8ChannelGetIBufferIndex(Channel)] = IncomingUnicodeValue; /* Update the index */ Index = VTUTF8ChannelGetIBufferIndex(Channel); VTUTF8ChannelSetIBufferIndex(Channel, Index + sizeof(WCHAR)); } } /* Signal the event, if one was set */ if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) { ChannelSetEvent(Channel, HasNewDataEvent); } /* All done */ return STATUS_SUCCESS; }
NTSTATUS NTAPI RawChannelOWrite(IN PSAC_CHANNEL Channel, IN PCHAR String, IN ULONG Length) { CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(String); if ((ConMgrIsWriteEnabled(Channel)) && (Channel->WriteEnabled)) { return RawChannelOEcho(Channel, String, Length); } return RawChannelOWrite2(Channel, String, Length); }
NTSTATUS NTAPI RawChannelOEcho(IN PSAC_CHANNEL Channel, IN PCHAR String, IN ULONG Length) { NTSTATUS Status = STATUS_SUCCESS; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(String); if (Length) { Status = ConMgrWriteData(Channel, String, Length); if (NT_SUCCESS(Status)) ConMgrFlushData(Channel); } return Status; }
NTSTATUS NTAPI ChannelSetDescription(IN PSAC_CHANNEL Channel, IN PWCHAR Description) { CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Description); /* Lock the attributes while we copy the name */ ChannelLockAttributes(Channel); /* Copy the name and null-terminate it */ ASSERT(((wcslen(Description) + 1) * sizeof(WCHAR)) <= ((SAC_CHANNEL_NAME_SIZE + 1) * sizeof(WCHAR))); wcsncpy(Channel->DescriptionBuffer, Description, RTL_NUMBER_OF(Channel->DescriptionBuffer)); // bug Channel->DescriptionBuffer[SAC_CHANNEL_DESCRIPTION_SIZE] = UNICODE_NULL; /* Release the lock and return */ ChannelUnlockAttributes(Channel); return STATUS_SUCCESS; }
NTSTATUS NTAPI RawChannelIWrite(IN PSAC_CHANNEL Channel, IN PCHAR Buffer, IN ULONG BufferSize) { NTSTATUS Status; BOOLEAN IsFull; ULONG Index; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Buffer); CHECK_PARAMETER_WITH_STATUS(BufferSize > 0, STATUS_INVALID_BUFFER_SIZE); /* First, check if the input buffer still has space */ Status = RawChannelIBufferIsFull(Channel, &IsFull); if (!NT_SUCCESS(Status)) return Status; if (IsFull) return STATUS_UNSUCCESSFUL; /* Get the current buffer index */ Index = RawChannelGetIBufferIndex(Channel); if ((SAC_RAW_IBUFFER_SIZE - Index) < BufferSize) { return STATUS_INSUFFICIENT_RESOURCES; } /* Copy the new data */ RtlCopyMemory(&Channel->IBuffer[Index], Buffer, BufferSize); /* Update the index */ RawChannelSetIBufferIndex(Channel, BufferSize + Index); /* Signal the event, if one was set */ if (Channel->Flags & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) { ChannelSetEvent(Channel, HasNewDataEvent); } /* All done */ return STATUS_SUCCESS; }
NTSTATUS NTAPI ChannelGetDescription(IN PSAC_CHANNEL Channel, IN PWCHAR* Description) { CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Description); /* Allocate space to hold the description */ *Description = SacAllocatePool(sizeof(Channel->DescriptionBuffer), GLOBAL_BLOCK_TAG); CHECK_ALLOCATION(*Description); /* Lock the attributes while we copy the name */ ChannelLockAttributes(Channel); /* Copy the name and null-terminate it */ ASSERT(((wcslen(Channel->DescriptionBuffer) + 1) * sizeof(WCHAR)) <= ((SAC_CHANNEL_DESCRIPTION_SIZE + 1) * sizeof(WCHAR))); wcsncpy(*Description, Channel->DescriptionBuffer, RTL_NUMBER_OF(Channel->DescriptionBuffer)); // bug (*Description)[SAC_CHANNEL_DESCRIPTION_SIZE] = UNICODE_NULL; /* Release the lock and return */ ChannelUnlockAttributes(Channel); return STATUS_SUCCESS; }
NTSTATUS NTAPI RawChannelOWrite2(IN PSAC_CHANNEL Channel, IN PCHAR String, IN ULONG Size) { BOOLEAN Overflow; ULONG i, NextIndex; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(String); Overflow = FALSE; for (i = 0; i < Size; i++) { if ((Channel->OBufferIndex == Channel->OBufferFirstGoodIndex) && ((i) || (ChannelHasNewOBufferData(Channel)))) { Overflow = TRUE; } ASSERT(Channel->OBufferIndex < SAC_RAW_OBUFFER_SIZE); Channel->OBuffer[Channel->OBufferIndex] = String[i]; NextIndex = (Channel->OBufferIndex + 1) & (SAC_RAW_OBUFFER_SIZE - 1); Channel->OBufferIndex = NextIndex; if (Overflow) Channel->OBufferFirstGoodIndex = NextIndex; } _InterlockedExchange(&Channel->ChannelHasNewOBufferData, 1); return STATUS_SUCCESS; }
NTSTATUS NTAPI VTUTF8ChannelOWrite2(IN PSAC_CHANNEL Channel, IN PWCHAR String, IN ULONG Size) { PSAC_VTUTF8_SCREEN Screen; ULONG i, EscapeSize, R, C; PWSTR pwch; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(String); VTUTF8ChannelAssertCursor(Channel); /* Loop every character */ Screen = (PSAC_VTUTF8_SCREEN)Channel->OBuffer; for (i = 0; i < Size; i++) { /* Check what the character is */ pwch = &String[i]; switch (*pwch) { /* It's an escape sequence... */ case L'\x1B': /* Send it to the parser, see how long the sequence was */ EscapeSize = VTUTF8ChannelConsumeEscapeSequence(Channel, pwch); if (EscapeSize) { /* Consume that many characters for next time*/ i += EscapeSize - 1; } else { /* Invalid escape sequence, skip just the ESC character */ i++; } /* Keep going*/ break; /* It's a line feed */ case L'\n': /* Simply reset the column to zero on the current line */ Channel->CursorCol = 0; break; /* It's a carriage feed */ case L'\r': /* Move to the next row */ Channel->CursorRow++; /* Check if we hit the last row on the screen */ if (Channel->CursorRow >= SAC_VTUTF8_ROW_HEIGHT) { /* Go over every row before the last one */ for (R = 0; R < (SAC_VTUTF8_ROW_HEIGHT - 1); R++) { /* Sanity check, since we always copy one row below */ ASSERT((R + 1) < SAC_VTUTF8_ROW_HEIGHT); /* Loop every character on the row */ for (C = 0; C < SAC_VTUTF8_COL_WIDTH; C++) { /* And replace it with one from the row below */ Screen->Cell[R][C] = Screen->Cell[R + 1][C]; } } /* Now we're left with the before-last row, zero it out */ ASSERT(R == (SAC_VTUTF8_ROW_HEIGHT - 1)); RtlZeroMemory(&Screen->Cell[R], sizeof(Screen->Cell[R])); /* Reset the row back by one */ Channel->CursorRow--; VTUTF8ChannelAssertCursor(Channel); } break; /* It's a TAB character */ case L'\t': /* Loop the remaining characters until a multiple of 4 */ VTUTF8ChannelAssertCursor(Channel); for (C = (4 - Channel->CursorCol % 4); C; C--) { /* Fill each remaining character with a space */ VTUTF8ChannelAssertCursor(Channel); Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellFlags = Channel->CellFlags; Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellBackColor = Channel->CellBackColor; Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellForeColor = Channel->CellForeColor; Screen->Cell[Channel->CursorRow][Channel->CursorCol].Char = L' '; /* Move to the next character position, but don't overflow */ Channel->CursorCol++; if (Channel->CursorCol >= SAC_VTUTF8_COL_WIDTH) { Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1; } } /* All done, move to the next one */ VTUTF8ChannelAssertCursor(Channel); break; /* It's a backspace or delete character */ case L'\b': case L'\x7F': /* Move back one character, unless we had nothing typed */ if (Channel->CursorCol) Channel->CursorCol--; VTUTF8ChannelAssertCursor(Channel); break; /* It's some other character */ default: /* Is it non-printable? Ignore it and keep parsing */ if (*pwch < L' ') continue; /* Otherwise, print it out with the current attributes */ VTUTF8ChannelAssertCursor(Channel); Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellFlags = Channel->CellFlags; Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellBackColor = Channel->CellBackColor; Screen->Cell[Channel->CursorRow][Channel->CursorCol].CellForeColor = Channel->CellForeColor; Screen->Cell[Channel->CursorRow][Channel->CursorCol].Char = *pwch; /* Move forward one character, but make sure not to overflow */ Channel->CursorCol++; if (Channel->CursorCol == SAC_VTUTF8_COL_WIDTH) { Channel->CursorCol = SAC_VTUTF8_COL_WIDTH - 1; } /* All done, move to the next one */ VTUTF8ChannelAssertCursor(Channel); break; } } /* Parsing of the input string completed -- string was written */ VTUTF8ChannelAssertCursor(Channel); return STATUS_SUCCESS; }
NTSTATUS NTAPI VTUTF8ChannelOEcho(IN PSAC_CHANNEL Channel, IN PCHAR String, IN ULONG Size) { NTSTATUS Status = STATUS_SUCCESS; PWSTR pwch; ULONG i, k, TranslatedCount, UTF8TranslationSize; BOOLEAN Result; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(String); /* Return success if there's nothing to echo */ if (!(Size / sizeof(WCHAR))) return Status; /* Start with the input string */ pwch = (PWCHAR)String; /* First, figure out how much is outside of the block length alignment */ k = (Size / sizeof(WCHAR)) % MAX_UTF8_ENCODE_BLOCK_LENGTH; if (k) { /* Translate the misaligned portion */ Result = SacTranslateUnicodeToUtf8(pwch, k, Utf8ConversionBuffer, Utf8ConversionBufferSize, &UTF8TranslationSize, &TranslatedCount); ASSERT(k == TranslatedCount); if (!Result) { /* If we couldn't translate, write failure to break out below */ Status = STATUS_UNSUCCESSFUL; } else { /* Write the misaligned portion into the buffer */ Status = ConMgrWriteData(Channel, Utf8ConversionBuffer, UTF8TranslationSize); } /* If translation or write failed, bail out */ if (!NT_SUCCESS(Status)) goto Return; } /* Push the string to its new location (this could be a noop if aligned) */ pwch += k; /* Now figure out how many aligned blocks we have, and loop each one */ k = (Size / sizeof(WCHAR)) / MAX_UTF8_ENCODE_BLOCK_LENGTH; for (i = 0; i < k; i++) { /* Translate the aligned block */ Result = SacTranslateUnicodeToUtf8(pwch, MAX_UTF8_ENCODE_BLOCK_LENGTH, Utf8ConversionBuffer, Utf8ConversionBufferSize, &UTF8TranslationSize, &TranslatedCount); ASSERT(MAX_UTF8_ENCODE_BLOCK_LENGTH == TranslatedCount); ASSERT(UTF8TranslationSize > 0); if (!Result) { /* Set failure here, we'll break out below */ Status = STATUS_UNSUCCESSFUL; } else { /* Move the string location to the next aligned block */ pwch += MAX_UTF8_ENCODE_BLOCK_LENGTH; /* Write the aligned block into the buffer */ Status = ConMgrWriteData(Channel, Utf8ConversionBuffer, UTF8TranslationSize); } /* If translation or write failed, bail out */ if (!NT_SUCCESS(Status)) break; } Return: ASSERT(pwch == (PWSTR)(String + Size)); if (NT_SUCCESS(Status)) Status = ConMgrFlushData(Channel); return Status; }
NTSTATUS NTAPI ChannelCreate(IN PSAC_CHANNEL Channel, IN PSAC_CHANNEL_ATTRIBUTES Attributes, IN SAC_CHANNEL_ID ChannelId) { NTSTATUS Status; CHECK_PARAMETER1(Channel); CHECK_PARAMETER2(Attributes); /* If a close event is being passed in, it must exist, and vice-versa */ if (Attributes->Flag & SAC_CHANNEL_FLAG_CLOSE_EVENT) { CHECK_PARAMETER(Attributes->CloseEvent != NULL); } else { CHECK_PARAMETER(Attributes->CloseEvent == NULL); } /* If a new data event is being passed in, it must exist, and vice-versa */ if (Attributes->Flag & SAC_CHANNEL_FLAG_HAS_NEW_DATA_EVENT) { CHECK_PARAMETER(Attributes->HasNewDataEvent != NULL); } else { CHECK_PARAMETER(Attributes->HasNewDataEvent == NULL); } /* If a lock event is being passed in, it must exist, and vice-versa */ if (Attributes->Flag & SAC_CHANNEL_FLAG_LOCK_EVENT) { CHECK_PARAMETER(Attributes->LockEvent != NULL); } else { CHECK_PARAMETER(Attributes->LockEvent == NULL); } /* If a redraw event is being passed in, it must exist, and vice-versa */ if (Attributes->Flag & SAC_CHANNEL_FLAG_REDRAW_EVENT) { CHECK_PARAMETER(Attributes->RedrawEvent != NULL); } else { CHECK_PARAMETER(Attributes->RedrawEvent == NULL); } /* Initialize the channel structure */ RtlZeroMemory(Channel, sizeof(SAC_CHANNEL)); Channel->ChannelId = ChannelId; Channel->ChannelType = Attributes->ChannelType; Channel->Flags = Attributes->Flag; if (Attributes->Flag & SAC_CHANNEL_FLAG_APPLICATION) { Channel->ApplicationType = Attributes->ChannelId; } /* Initialize all the locks and events */ SacInitializeLock(&Channel->ChannelAttributeLock); SacInitializeLock(&Channel->ChannelOBufferLock); SacInitializeLock(&Channel->ChannelIBufferLock); ChannelInitializeEvent(Channel, Attributes, CloseEvent); ChannelInitializeEvent(Channel, Attributes, HasNewDataEvent); ChannelInitializeEvent(Channel, Attributes, LockEvent); ChannelInitializeEvent(Channel, Attributes, RedrawEvent); /* Set the name and description */ ChannelSetName(Channel, Attributes->NameBuffer); ChannelSetDescription(Channel, Attributes->DescriptionBuffer); /* Initialize the function table for the type of channel this is */ Status = ChannelInitializeVTable(Channel); if (!NT_SUCCESS(Status)) { /* This is critical */ SAC_DBG(SAC_DBG_INIT, "SAC Create Channel :: Failed to initialize vtable\n"); goto FailChannel; } /* Now call the channel specific type constructor */ Status = Channel->ChannelCreate(Channel); if (!NT_SUCCESS(Status)) { /* This is critical */ SAC_DBG(SAC_DBG_INIT, "SAC Create Channel :: Failed channel specific initialization\n"); goto FailChannel; } /* Finally, mark the channel as active */ ChannelSetStatus(Channel, Active); return STATUS_SUCCESS; FailChannel: /* Destroy the channel and return the failure code */ Channel->ChannelDestroy(Channel); return Status; }