diff --git a/sdk/xtdk/rtlfuncs.h b/sdk/xtdk/rtlfuncs.h index 8f06e33..6d14684 100644 --- a/sdk/xtdk/rtlfuncs.h +++ b/sdk/xtdk/rtlfuncs.h @@ -12,6 +12,7 @@ #include #include #include +#include /* Routines used by XTLDR */ @@ -122,6 +123,12 @@ RtlFillMemory(OUT PVOID Destination, IN SIZE_T Length, IN UCHAR Value); +XTAPI +XTSTATUS +RtlFormatWideString(IN PRTL_PRINT_CONTEXT Context, + IN PCWSTR Format, + IN VA_LIST ArgumentList); + XTAPI VOID RtlMoveMemory(OUT PVOID Destination, diff --git a/sdk/xtdk/rtltypes.h b/sdk/xtdk/rtltypes.h index a7a23ec..9ee713a 100644 --- a/sdk/xtdk/rtltypes.h +++ b/sdk/xtdk/rtltypes.h @@ -17,14 +17,33 @@ #define GUID_STRING_LENGTH 38 #define PARTUUID_STRING_LENGTH 13 +/* Maximum integer value string length */ +#define MAX_INTEGER_STRING_SIZE 25 + /* Floating point definitions */ #define DOUBLE_EXPONENT_MASK 0x7FF0000000000000ULL #define DOUBLE_EXPONENT_SHIFT 0x34 #define DOUBLE_EXPONENT_BIAS 0x3FF /* Runtime Library routine callbacks */ -typedef VOID (*PWRITE_CHARACTER)(IN CHAR Character); -typedef VOID (*PWRITE_WIDE_CHARACTER)(IN WCHAR Character); +typedef XTSTATUS (*PWRITE_CHARACTER)(IN CHAR Character); +typedef XTSTATUS (*PWRITE_WIDE_CHARACTER)(IN WCHAR Character); + +/* Variable types enumeration list */ +typedef enum _RTL_VARIABLE_TYPE +{ + Unknown, + AnsiString, + Boolean, + Char, + Float, + Guid, + Integer, + String, + UnicodeString, + WideChar, + WideString +} RTL_VARIABLE_TYPE, *PRTL_VARIABLE_TYPE; /* Runtime Library print context structure definition */ typedef struct _RTL_PRINT_CONTEXT @@ -32,7 +51,29 @@ typedef struct _RTL_PRINT_CONTEXT PWRITE_CHARACTER WriteCharacter; PWRITE_WIDE_CHARACTER WriteWideCharacter; ULONG CharactersWritten; - ULONG Limit; } RTL_PRINT_CONTEXT, *PRTL_PRINT_CONTEXT; +/* Runtime Library print format properties structure definition */ +typedef struct _RTL_PRINT_FORMAT_PROPERTIES +{ + RTL_VARIABLE_TYPE VariableType; + ULONG Radix; + LONG FieldWidth; + LONG IntegerSize; + LONG Precision; + BOOLEAN AlwaysPrintSign; + BOOLEAN LongDouble; + BOOLEAN LongInteger; + BOOLEAN LeftJustified; + BOOLEAN PrintUpperCase; + BOOLEAN PrintLeadingZeroes; + BOOLEAN PrintRadix; + BOOLEAN SpaceForPlus; + BOOLEAN ThousandsGrouping; + BOOLEAN UnsignedValue; + BOOLEAN FloatFormat; + BOOLEAN ScientificFormat; + BOOLEAN SignificantDigitPrecision; +} RTL_PRINT_FORMAT_PROPERTIES, *PRTL_PRINT_FORMAT_PROPERTIES; + #endif /* __XTDK_RTLTYPES_H */ diff --git a/xtoskrnl/includes/rtli.h b/xtoskrnl/includes/rtli.h index 5408e37..3563e02 100644 --- a/xtoskrnl/includes/rtli.h +++ b/xtoskrnl/includes/rtli.h @@ -290,4 +290,46 @@ XTCDECL VOID RtlRemoveEntryList(IN PLIST_ENTRY Entry); +XTAPI +XTSTATUS +RtlpFormatWideStringArgumentSpecifier(IN PRTL_PRINT_CONTEXT Context, + IN PCWSTR Format, + IN PVA_LIST ArgumentList, + IN OUT PULONG Index); + +XTAPI +ULONGLONG +RtlpGetWideStringArgument(IN PVA_LIST ArgumentList, + IN ULONG ArgumentNumber, + IN LONG ArgumentSize); + +XTAPI +ULONGLONG +RtlpGetWideStringSpecifierValue(IN PWCHAR *Format); + +XTAPI +XTSTATUS +RtlpWriteWideCharacter(IN PRTL_PRINT_CONTEXT Context, + IN WCHAR Character); + +XTAPI +XTSTATUS +RtlpWriteWideStringIntegerValue(IN PRTL_PRINT_CONTEXT Context, + IN PRTL_PRINT_FORMAT_PROPERTIES FormatProperties, + IN ULONGLONG Integer); + +XTAPI +XTSTATUS +RtlpWriteWideStringStringValue(PRTL_PRINT_CONTEXT Context, + PRTL_PRINT_FORMAT_PROPERTIES FormatProperties, + PCHAR String, + BOOLEAN Character); + +XTAPI +XTSTATUS +RtlpWriteWideStringValue(PRTL_PRINT_CONTEXT Context, + PRTL_PRINT_FORMAT_PROPERTIES FormatProperties, + PWCHAR String, + BOOLEAN Character); + #endif /* __XTOSKRNL_RTLI_H */ diff --git a/xtoskrnl/rtl/widestr.c b/xtoskrnl/rtl/widestr.c index 82cdb08..af69515 100644 --- a/xtoskrnl/rtl/widestr.c +++ b/xtoskrnl/rtl/widestr.c @@ -187,6 +187,81 @@ RtlConcatenateWideString(OUT PWCHAR Destination, return DestString; } +/** + * Formats a wide string according to the given printf-alike format string. + * + * @param Context + * Supplies a pointer to the print context structure. + * + * @param Format + * Supplies a pointer to the printf-alike format string. + * + * @param ArgumentList + * Supplies a list of arguments to the format string. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RtlFormatWideString(IN PRTL_PRINT_CONTEXT Context, + IN PCWSTR Format, + IN VA_LIST ArgumentList) +{ + VA_LIST LocalArgumentList; + XTSTATUS Status; + ULONG Index; + + /* Make sure, that we have valid context and write routine */ + if(Context == NULL || Context->WriteWideCharacter == NULL) + { + /* Invalid context or write routine not set */ + return FALSE; + } + + /* Check format string pointer */ + if(Format == NULL) + { + /* Write null string */ + Format = L"(null)"; + } + + /* Make a copy of the argument list */ + VA_COPY(LocalArgumentList, ArgumentList); + + /* Iterate through format string */ + Index = 0; + while(Format[Index] != L'\0') + { + /* Look for format specifier */ + if(Format[Index] == L'%') + { + /* Handle format along with arguments */ + Status = RtlpFormatWideStringArgumentSpecifier(Context, Format, &LocalArgumentList, &Index); + } + else + { + /* Write wide character and increase string index */ + Status = RtlpWriteWideCharacter(Context, Format[Index]); + Index++; + } + + /* Make sure character written successfully */ + if(Status != STATUS_SUCCESS) + { + /* Return status code */ + return Status; + } + } + + /* Clean up the argument list */ + VA_END(LocalArgumentList); + + /* Return success */ + return STATUS_SUCCESS; +} + /** * Reverses a characters order in a wide string. It modifies the original, input variable. * @@ -409,3 +484,1261 @@ RtlWideStringLength(IN CONST PWCHAR String, /* Return wide string length */ return Length; } + +/** + * Formats a wide string according to the given printf-alike format string and a list of arguments. + * + * @param Context + * Supplies a pointer to the print context structure. + * + * @param Format + * Supplies a pointer to the printf-alike format string. + * + * @param ArgumentList + * Supplies a list of arguments to the format string. + * + * @param Index + * Supplies a pointer to the position of the current format specifier, that will be advanced beyond the specifier. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RtlpFormatWideStringArgumentSpecifier(IN PRTL_PRINT_CONTEXT Context, + IN PCWSTR Format, + IN PVA_LIST ArgumentList, + IN OUT PULONG Index) +{ + RTL_PRINT_FORMAT_PROPERTIES FormatProperties; + WCHAR Specifier, WideCharArg; + LONGLONG SpecifierValue; + VA_LIST ArgumentsCopy; + LARGE_DOUBLE FloatArg; + PCWSTR FormatIndex; + ULONG ArgPosition; + PWCHAR WideStrArg; + ULONGLONG IntArg; + XTSTATUS Status; + PCHAR StrArg; + CHAR CharArg; + + /* Set current format specifier index and custom argument position */ + FormatIndex = Format + *Index + 1; + ArgPosition = 0; + + /* Initialize format properties */ + RtlZeroMemory(&FormatProperties, sizeof(RTL_PRINT_FORMAT_PROPERTIES)); + FormatProperties.IntegerSize = sizeof(INT); + FormatProperties.Precision = -1; + + + /* Make a copy of the argument list */ + VA_COPY(ArgumentsCopy, *ArgumentList); + + /* Lookup parameter field */ + if((*FormatIndex >= L'1') && (*FormatIndex <= L'9')) + { + /* POSIX extension found, read its value */ + SpecifierValue = RtlpGetWideStringSpecifierValue((PWSTR*)&FormatIndex); + + /* Make sure parameter field ends with '$' character */ + if(*FormatIndex == L'$') + { + /* Store number of parameter and increment index */ + ArgPosition = (ULONG)SpecifierValue; + FormatIndex++; + } + else + { + /* Field not ended with '$' character, treat it as width field */ + FormatProperties.FieldWidth = (ULONG)SpecifierValue; + } + } + + /* Lookup flags field */ + while(TRUE) + { + /* Look for a valid flag and set properties */ + if(*FormatIndex == L'\'') + { + /* Thousands grouping separator applied */ + FormatProperties.ThousandsGrouping = TRUE; + } + else if(*FormatIndex == L'-') + { + /* Left-align the output */ + FormatProperties.LeftJustified = TRUE; + } + else if(*FormatIndex == L' ') + { + /* Prepend a space for positive signed-numeric types */ + FormatProperties.SpaceForPlus = TRUE; + } + else if(*FormatIndex == L'+') + { + /* Prepend a plus for positive signed-numeric types */ + FormatProperties.AlwaysPrintSign = TRUE; + } + else if(*FormatIndex == L'#') + { + /* Convert to an alternate form */ + FormatProperties.PrintRadix = TRUE; + } + else if(*FormatIndex == L'0') + { + /* Prepend zeros for numeric types */ + FormatProperties.PrintLeadingZeroes = TRUE; + } + else + { + /* No more flags to read */ + break; + } + + /* Move to the next character */ + FormatIndex++; + } + + /* Check if output is left-justified */ + if(FormatProperties.LeftJustified) + { + /* Left justified output can't have leading zeros */ + FormatProperties.PrintLeadingZeroes = FALSE; + } + + /* Check if plus for positive signed-numeric types is enabled */ + if(FormatProperties.AlwaysPrintSign) + { + /* Do not append a space when plus character is enabled */ + FormatProperties.SpaceForPlus = FALSE; + } + + /* Lookup width field */ + if(*FormatIndex == L'*') + { + /* Skip dynamic width field indicator */ + FormatIndex++; + + /* Read dynamic width value from the argument list */ + FormatProperties.FieldWidth = VA_ARG(*ArgumentList, INT); + } + else if((*FormatIndex >= L'1') && (*FormatIndex <= L'9')) + { + /* Read a numeric width value */ + FormatProperties.FieldWidth = RtlpGetWideStringSpecifierValue((PWSTR*)&FormatIndex); + } + + /* Check if field width is set to negative value */ + if(FormatProperties.FieldWidth < 0) + { + /* Force left-aligned output and turn field width into positive value */ + FormatProperties.LeftJustified = TRUE; + FormatProperties.FieldWidth *= -1; + } + + /* Lookup precision field */ + if(*FormatIndex == L'.') + { + /* Skip precision field indicator */ + FormatIndex++; + + /* Look for a dynamic precision value indicator */ + if(*FormatIndex == L'*') + { + /* Read dynamic precision value from the argument list */ + FormatIndex++; + FormatProperties.Precision = VA_ARG(*ArgumentList, INT); + } + else if((*FormatIndex >= L'0') && (*FormatIndex <= L'9')) + { + /* Read a numeric precision value */ + FormatProperties.Precision = RtlpGetWideStringSpecifierValue((PWSTR*)&FormatIndex); + } + else + { + /* Set default precision */ + FormatProperties.Precision = 0; + } + } + + /* Check if precision is set to negative value */ + if(FormatProperties.Precision < 0) + { + /* Disable precision */ + FormatProperties.Precision = -1; + } + + /* Lookup argument length modifier */ + Specifier = *FormatIndex; + switch(Specifier) + { + case L'h': + /* SHORT-sized integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(SHORT); + if(*FormatIndex == L'h') + { + /* CHAR-sized integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(CHAR); + } + break; + case L'j': + /* INTMAX-sized integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(LONG_PTR); + break; + case L'l': + /* LONG-sized double/integer argument */ + FormatIndex++; + FormatProperties.LongDouble = TRUE; + FormatProperties.LongInteger = TRUE; + FormatProperties.IntegerSize = sizeof(LONG); + if(*FormatIndex == L'l') + { + /* LONGLONG-sized integer argument */ + FormatIndex++; + FormatProperties.LongDouble = FALSE; + FormatProperties.LongInteger = FALSE; + FormatProperties.IntegerSize = sizeof(LONGLONG); + } + break; + case L'q': + /* BSD extension: 64-bit (quad word) integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(LONGLONG); + break; + case L't': + /* PTRDIFF-sized integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(PVOID); + break; + case L'w': + /* MSVC extension: wide character or wide string argument */ + FormatIndex++; + FormatProperties.LongInteger = TRUE; + break; + case L'z': + /* SIZE_T-sized integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(SIZE_T); + break; + case L'I': + /* MSVC extension: SIZE_T-sized integer argument */ + FormatIndex++; + FormatProperties.IntegerSize = sizeof(SIZE_T); + if((*FormatIndex == L'3') && (*(FormatIndex + 1) == L'2')) + { + /* MSVC extension: 32-bit (double word) integer argument */ + FormatIndex += 2; + FormatProperties.LongInteger = TRUE; + FormatProperties.IntegerSize = sizeof(LONG); + } + else if((*FormatIndex == L'6') && (*(FormatIndex + 1) == L'4')) + { + /* MSVC extension: 64-bit (quad word) integer argument */ + FormatIndex += 2; + FormatProperties.IntegerSize = sizeof(LONGLONG); + } + break; + case L'L': + /* LONG-sized double argument */ + FormatIndex++; + FormatProperties.LongDouble = TRUE; + FormatProperties.IntegerSize = sizeof(LDOUBLE); + break; + } + + /* Lookup format specifier */ + Specifier = *FormatIndex++; + + /* Handle char and string modifiers */ + if(FormatProperties.LongInteger) + { + if(Specifier == L'c') + { + /* Wide character argument */ + Specifier = L'C'; + } + else if(Specifier == L's') + { + /* Wide string argument */ + Specifier = L'S'; + } + } + + /* Lookup format specifier */ + FormatProperties.UnsignedValue = TRUE; + switch(Specifier) + { + case L'a': + /* Double argument as hexadecimal number (lowercase) */ + FormatProperties.VariableType = Float; + FormatProperties.ScientificFormat = TRUE; + FormatProperties.Radix = 16; + break; + case L'A': + /* Double argument as hexadecimal number (uppercase) */ + FormatProperties.VariableType = Float; + FormatProperties.ScientificFormat = TRUE; + FormatProperties.PrintUpperCase = TRUE; + FormatProperties.Radix = 16; + break; + case L'b': + /* XTOS extension: Boolean argument (lowercase) */ + FormatProperties.VariableType = Boolean; + break; + case L'B': + /* XTOS extension: Boolean argument (uppercase) */ + FormatProperties.VariableType = Boolean; + FormatProperties.PrintUpperCase = TRUE; + break; + case L'c': + /* Character argument */ + FormatProperties.VariableType = Char; + break; + case L'C': + /* Wide character argument */ + FormatProperties.VariableType = WideChar; + break; + case L'd': + case L'i': + /* Signed integer argument as decimal number */ + FormatProperties.VariableType = Integer; + FormatProperties.Radix = 10; + FormatProperties.UnsignedValue = FALSE; + break; + case L'e': + /* Double argument in scientific notation (lowercase) */ + FormatProperties.VariableType = Float; + FormatProperties.ScientificFormat = TRUE; + break; + case L'E': + /* Double argument in scientific notation (uppercase) */ + FormatProperties.VariableType = Float; + FormatProperties.ScientificFormat = TRUE; + FormatProperties.PrintUpperCase = TRUE; + break; + case L'f': + /* Double argument as floating point number (lowercase) */ + FormatProperties.VariableType = Float; + FormatProperties.FloatFormat = TRUE; + break; + case L'F': + /* Double argument as floating point number (uppercase) */ + FormatProperties.VariableType = Float; + FormatProperties.FloatFormat = TRUE; + FormatProperties.PrintUpperCase = TRUE; + break; + case L'g': + /* Double argument as either floating point number or in scientific notation (lowercase) */ + FormatProperties.VariableType = Float; + FormatProperties.SignificantDigitPrecision = TRUE; + break; + case L'G': + /* Double argument as either floating point number or in scientific notation (uppercase) */ + FormatProperties.VariableType = Float; + FormatProperties.PrintUpperCase = TRUE; + FormatProperties.SignificantDigitPrecision = TRUE; + break; + case L'n': + /* Write number of characters written so far into an integer pointer parameter */ + FormatProperties.VariableType = Integer; + FormatProperties.IntegerSize = sizeof(PVOID); + break; + case L'o': + /* Unsigned integer argument as octal number */ + FormatProperties.VariableType = Integer; + FormatProperties.Radix = 8; + break; + case L'p': + /* Pointer argument as hexadecimal number (uppercase) */ + FormatProperties.VariableType = Integer; + FormatProperties.IntegerSize = sizeof(UINT_PTR); + FormatProperties.Radix = 16; + FormatProperties.PrintUpperCase = TRUE; + FormatProperties.PrintRadix = TRUE; + break; + case L's': + /* String argument */ + FormatProperties.VariableType = String; + break; + case L'S': + /* Wide string argument */ + FormatProperties.VariableType = WideString; + break; + case L'u': + /* Unsigned integer argument as decimal number */ + FormatProperties.VariableType = Integer; + FormatProperties.Radix = 10; + break; + case L'x': + /* Unsigned integer argument as hexadecimal number (lowercase) */ + FormatProperties.VariableType = Integer; + FormatProperties.Radix = 16; + break; + case L'X': + /* Unsigned integer argument as hexadecimal number (uppercase) */ + FormatProperties.VariableType = Integer; + FormatProperties.Radix = 16; + FormatProperties.PrintUpperCase = TRUE; + break; + case L'%': + /* Print '%' character */ + FormatProperties.VariableType = Unknown; + WideCharArg = L'%'; + break; + default: + /* Unknown format specifier, print '?' character */ + FormatProperties.VariableType = Unknown; + WideCharArg = L'?'; + break; + } + + /* Finally, write the formatted argument */ + if(FormatProperties.VariableType == Unknown) + { + /* Write defined wide character */ + Status = RtlpWriteWideStringValue(Context, &FormatProperties, &WideCharArg, TRUE); + } + if(FormatProperties.VariableType == Boolean) + { + /* Boolean type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + IntArg = RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, FormatProperties.IntegerSize); + } + else + { + /* Get argument value from the next argument */ + IntArg = VA_ARG(*ArgumentList, ULONGLONG); + } + + /* Check if using uppercase format */ + if(FormatProperties.PrintUpperCase) + { + /* Write 'TRUE' or 'FALSE' depending on argument value */ + Status = RtlpWriteWideStringValue(Context, &FormatProperties, IntArg ? L"TRUE" : L"FALSE", FALSE); + } + else + { + /* Write 'true' or 'false' depending on argument value */ + Status = RtlpWriteWideStringValue(Context, &FormatProperties, IntArg ? L"true" : L"false", FALSE); + } + } + else if(FormatProperties.VariableType == Char) + { + /* Character type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + CharArg = (UCHAR)RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, sizeof(UCHAR)); + } + else + { + /* Get argument value from the next argument */ + CharArg = VA_ARG(*ArgumentList, INT); + } + + /* Write formatted character */ + Status = RtlpWriteWideStringStringValue(Context, &FormatProperties, &CharArg, TRUE); + } + else if(FormatProperties.VariableType == WideChar) + { + /* Wide character type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + WideCharArg = (WCHAR)RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, sizeof(WCHAR)); + } + else + { + /* Get argument value from the next argument */ + WideCharArg = VA_ARG(*ArgumentList, INT); + } + + /* Write formatted wide character */ + Status = RtlpWriteWideStringValue(Context, &FormatProperties, &WideCharArg, TRUE); + } + else if(FormatProperties.VariableType == Float) + { + /* Float/Double type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + FloatArg.QuadPart = RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, sizeof(ULONGLONG)); + } + else + { + /* Get argument value from the next argument, depending on its size */ + if(FormatProperties.LongDouble) + { + FloatArg.DoublePart = VA_ARG(*ArgumentList, LDOUBLE); + } + else + { + FloatArg.DoublePart = VA_ARG(*ArgumentList, DOUBLE); + } + } + + /* Write formatted double value */ + // Status = RtlpWriteWideStringDoubleValue(Context, &FormatProperties, FloatArg.DoublePart); + } + else if(FormatProperties.VariableType == Integer) + { + /* Integer type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + IntArg = RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, FormatProperties.IntegerSize); + + /* Convert to required integer size */ + switch(FormatProperties.IntegerSize) + { + case sizeof(CHAR): + IntArg = (UCHAR)IntArg; + break; + case sizeof(SHORT): + IntArg = (USHORT)IntArg; + break; + case sizeof(LONG): + IntArg = (ULONG)IntArg; + break; + case sizeof(LONGLONG): + IntArg = (ULONGLONG)IntArg; + break; + default: + return STATUS_INVALID_PARAMETER; + } + } + else + { + /* Get argument value from the next argument, depending on its size */ + switch(FormatProperties.IntegerSize) + { + case sizeof(CHAR): + IntArg = (CHAR)VA_ARG(*ArgumentList, UINT); + break; + case sizeof(SHORT): + IntArg = (SHORT)VA_ARG(*ArgumentList, UINT); + break; + case sizeof(LONG): + IntArg = VA_ARG(*ArgumentList, ULONG); + break; + case sizeof(LONGLONG): + IntArg = VA_ARG(*ArgumentList, ULONGLONG); + break; + default: + return STATUS_INVALID_PARAMETER; + } + } + + /* Check if using 'n' specifier */ + if(Specifier == L'n') + { + /* Make sure, that integer pointer parameter is not NULL */ + if(IntArg != (UINT_PTR)NULL) + { + /* Store number of characters written in integer pointer parameter */ + *((PINT)(UINT_PTR)IntArg) = Context->CharactersWritten; + } + } + else + { + /* Write formatted integer value */ + Status = RtlpWriteWideStringIntegerValue(Context, &FormatProperties, IntArg); + } + } + else if(FormatProperties.VariableType == String) + { + /* String type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + IntArg = RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, sizeof(PCHAR)); + StrArg = (PCHAR)(UINT_PTR)IntArg; + } + else + { + /* Get argument value from the next argument */ + StrArg = VA_ARG(*ArgumentList, PCHAR); + } + + /* Write formatted string value */ + Status = RtlpWriteWideStringStringValue(Context, &FormatProperties, StrArg, FALSE); + } + else if(FormatProperties.VariableType == WideString) + { + /* Wide string type */ + if(ArgPosition != 0) + { + /* Get argument value from specified argument position */ + IntArg = RtlpGetWideStringArgument(&ArgumentsCopy, ArgPosition, sizeof(PWCHAR)); + WideStrArg = (PWCHAR)(UINT_PTR)IntArg; + } + else + { + /* Get argument value from the next argument */ + WideStrArg = VA_ARG(*ArgumentList, PWCHAR); + } + + /* Write formatted wide string value */ + Status = RtlpWriteWideStringValue(Context, &FormatProperties, WideStrArg, FALSE); + } + + /* Cleanup ArgumentsCopy object */ + VA_END(ArgumentsCopy); + + /* Increase index position according to number of characters consumed */ + *Index += ((UINT_PTR)FormatIndex - (UINT_PTR)(Format + *Index)) / sizeof(WCHAR); + + /* Return status code */ + return Status; +} + +/** + * Gets the positional argument by scanning the argument list. + * + * @param ArgumentList + * Supplies a pointer to the argument list. + * + * @param ArgumentNumber + * Supplies the argument number. + * + * @param ArgumentSize + * Supplies the expected size of the argument. + */ +XTAPI +ULONGLONG +RtlpGetWideStringArgument(IN PVA_LIST ArgumentList, + IN ULONG ArgumentNumber, + IN LONG ArgumentSize) +{ + VA_LIST ArgumentsCopy; + ULONGLONG Value; + ULONG Index; + + /* Make a copy of the argument list */ + VA_COPY(ArgumentsCopy, *ArgumentList); + + /* Skip all arguments before the specified one */ + for(Index = 1; Index < ArgumentNumber; Index += 1) + { + /* Skip the argument */ + Value = VA_ARG(ArgumentsCopy, LONGLONG); + } + + /* Set default value */ + Value = 0; + + /* Get the argument value depending on its declared size */ + switch(ArgumentSize) + { + case sizeof(CHAR): + Value = (UCHAR)VA_ARG(ArgumentsCopy, INT); + break; + case sizeof(SHORT): + Value = (USHORT)VA_ARG(ArgumentsCopy, INT); + break; + case sizeof(LONG): + Value = VA_ARG(ArgumentsCopy, LONG); + break; + case sizeof(LONGLONG): + Value = VA_ARG(ArgumentsCopy, LONGLONG); + break; + default: + Value = 0; + break; + } + + /* Cleanup ArgumentsCopy object and return the argument value */ + VA_END(ArgumentsCopy); + return Value; +} + +/** + * Gets the specifier integer value from the wide string advancing the pointer. + * + * @param Format + * Supplies a pointer to the wide string format, at integer value position. + * + * @return This routine returns a specifier integer value read from wide string format, or zero if no valid value found. + * + * @since XT 1.0 + */ +XTAPI +ULONGLONG +RtlpGetWideStringSpecifierValue(IN PWCHAR *Format) +{ + ULONG Count; + PWCHAR Fmt; + + /* Initialize variables */ + Fmt = *Format; + Count = 0; + + /* Read the specifier value */ + for(;; Fmt++) + { + switch(*Fmt) + { + case L'0' ... L'9': + /* Read the number from wide string */ + Count = Count * 10 + *Fmt - L'0'; + break; + default: + /* No more numbers, return count */ + *Format = Fmt; + return Count; + } + } + + /* Return zero if no value specified */ + return 0; +} + +/** + * Writes a wide character to the destination provided by the print context. + * + * @param Context + * Supplies a pointer to the print context structure. + * + * @param Character + * Supplies the wide character to write. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RtlpWriteWideCharacter(IN PRTL_PRINT_CONTEXT Context, + IN WCHAR Character) +{ + XTSTATUS Status; + + /* Write wide character and increment number of characters written so far */ + Status = Context->WriteWideCharacter(Character); + Context->CharactersWritten++; + + /* Return status code */ + return Status; +} + +/** + * Writes a wide string integer value to the destination provided by the print context. + * + * @param Context + * Supplies a pointer to the print context structure. + * + * @param FormatProperties + * Supplies a pointer to the print format properties structure, describing the style characteristics. + * + * @param Integer + * Supplies the integer value to write as a wide string. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RtlpWriteWideStringIntegerValue(IN PRTL_PRINT_CONTEXT Context, + IN PRTL_PRINT_FORMAT_PROPERTIES FormatProperties, + IN ULONGLONG Integer) +{ + LONG BufferIndex, FieldLength, IntegerLength, PrecisionLength, PrefixIndex, PrefixLength; + WCHAR Buffer[MAX_INTEGER_STRING_SIZE]; + ULONGLONG NextInteger, Remainder; + BOOLEAN Negative; + WCHAR Prefix[4]; + WCHAR Character; + XTSTATUS Status; + + /* Set default values */ + IntegerLength = 0; + Negative = FALSE; + + /* Check if this is signed integer */ + if(!FormatProperties->UnsignedValue) + { + /* Check integer size and extend to signed value */ + switch(FormatProperties->IntegerSize) + { + case sizeof(CHAR): + Integer = (CHAR)Integer; + break; + case sizeof(SHORT): + Integer = (SHORT)Integer; + break; + case sizeof(LONG): + Integer = (LONG)Integer; + break; + } + } + + /* Check if the integer value is zero */ + if(Integer == 0) + { + /* Cannot print radix if integer is zero */ + FormatProperties->PrintRadix = FALSE; + } + + /* Check if any of the integer value or precision is non-zero */ + if(Integer != 0 || FormatProperties->Precision != 0) + { + /* Check if the integer is negative */ + if(!FormatProperties->UnsignedValue && (LONGLONG)Integer < 0) + { + /* Mark the integer as negative and turn it positive */ + Negative = TRUE; + Integer *= -1; + } + + /* Initialize the buffer */ + RtlZeroMemory(Buffer, sizeof(Buffer)); + + /* Convert the integer into a reversed wide string */ + do + { + /* Get the next digit */ + NextInteger = RtlDivideUnsigned64(Integer, FormatProperties->Radix, &Remainder); + + /* Convert the digit into a character */ + Character = (WCHAR)Remainder; + if(Character > 9) + { + /* Check if the character should be upper case */ + if(FormatProperties->PrintUpperCase) + { + /* Get the uppercase character */ + Character = Character - 10 + L'A'; + } + else + { + /* Get the lowercase character */ + Character = Character - 10 + L'a'; + } + } + else + { + /* Get the numeric character */ + Character += L'0'; + } + + /* Store the character in the buffer */ + Buffer[IntegerLength] = Character; + IntegerLength += 1; + + /* Get next signed digit */ + Integer = NextInteger; + } + while(Integer > 0); + + /* Reverse the string representation of the integer */ + RtlReverseWideString(Buffer, IntegerLength); + } + + /* Handle the sign decoration */ + PrefixLength = 0; + if(!FormatProperties->UnsignedValue && Negative) + { + /* Signed negative value, write '-' character */ + Prefix[PrefixLength] = L'-'; + PrefixLength += 1; + } + else if(FormatProperties->AlwaysPrintSign) + { + /* Write '+' character for positive value */ + Prefix[PrefixLength] = L'+'; + PrefixLength += 1; + } + else if(FormatProperties->SpaceForPlus) + { + /* Write ' ' character for positive value */ + Prefix[PrefixLength] = L' '; + PrefixLength += 1; + } + + /* Handle the radix decoration */ + if(FormatProperties->PrintRadix) + { + if(FormatProperties->Radix == 8) + { + /* Check if leading zero is required */ + if(Buffer[0] != L'0') + { + /* Write '0' character */ + Prefix[PrefixLength] = L'0'; + PrefixLength += 1; + } + } + else if(FormatProperties->Radix == 16) + { + /* Write '0x' characters */ + Prefix[PrefixLength] = L'0'; + PrefixLength += 1; + + /* Check if uppercase is required */ + if(FormatProperties->PrintUpperCase != 0) + { + /* Write uppercase 'X' character */ + Prefix[PrefixLength] = L'X'; + } + else + { + /* Write lowercase 'x' character */ + Prefix[PrefixLength] = L'x'; + } + + PrefixLength += 1; + } + } + + /* Calculate the precision */ + PrecisionLength = 0; + if(IntegerLength < FormatProperties->Precision) + { + PrecisionLength = FormatProperties->Precision - IntegerLength; + } + + /* Calculate the field length */ + FieldLength = 0; + if(IntegerLength + PrefixLength + PrecisionLength < FormatProperties->FieldWidth) + { + FieldLength = FormatProperties->FieldWidth - (IntegerLength + PrefixLength + PrecisionLength); + } + + /* Check if leading zero padding is required and if field is left aligned */ + if(!FormatProperties->LeftJustified || FormatProperties->PrintLeadingZeroes) + { + Character = L' '; + if(FormatProperties->PrintLeadingZeroes) + { + /* Write leading zero padding characters */ + Character = L'0'; + for(PrefixIndex = 0; PrefixIndex < PrefixLength; PrefixIndex++) + { + Status = RtlpWriteWideCharacter(Context, Prefix[PrefixIndex]); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + } + + /* Clear prefix */ + PrefixLength = 0; + } + + /* Write additional field width characters */ + while(FieldLength > 0) + { + Status = RtlpWriteWideCharacter(Context, Character); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement field length */ + FieldLength--; + } + } + + /* Write the prefix characters */ + for(PrefixIndex = 0; PrefixIndex < PrefixLength; PrefixIndex++) + { + Status = RtlpWriteWideCharacter(Context, Prefix[PrefixIndex]); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + } + + /* Fill the precision characters with '0' */ + while(PrecisionLength > 0) + { + Status = RtlpWriteWideCharacter(Context, L'0'); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement precision length */ + PrecisionLength--; + } + + /* Write the actual integer value */ + for(BufferIndex = 0; BufferIndex < IntegerLength; BufferIndex++) + { + Status = RtlpWriteWideCharacter(Context, Buffer[BufferIndex]); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + } + + /* Write additional field width with ' ' characters */ + while(FieldLength > 0) + { + Status = RtlpWriteWideCharacter(Context, L' '); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement field length */ + FieldLength--; + } + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Writes a string value to the destination provided by the print context. + * + * @param Context + * Supplies a pointer to the print context structure. + * + * @param FormatProperties + * Supplies a pointer to the print format properties structure, describing the style characteristics. + * + * @param String + * Supplies the string value to write as a wide string. + * + * @param Character + * Specifies whether the string value is expected to be a single character or not. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RtlpWriteWideStringStringValue(PRTL_PRINT_CONTEXT Context, + PRTL_PRINT_FORMAT_PROPERTIES FormatProperties, + PCHAR String, + BOOLEAN Character) +{ + WCHAR WideCharacter[2]; + ULONG PaddingLength; + SIZE_T StringLength; + XTSTATUS Status; + + /* Check for NULL string */ + if(String == NULL) + { + /* Print '(null)' instead */ + String = "(null)"; + } + + /* Check if single character is expected */ + if(Character) + { + /* Force string length to 1 */ + StringLength = 1; + } + else + { + /* Get real string length */ + StringLength = RtlStringLength(String, 0); + } + + /* Check if string length exceeds precision limit */ + if((FormatProperties->Precision >= 0) && (StringLength > FormatProperties->Precision)) + { + /* Limit string length to precision number */ + StringLength = FormatProperties->Precision; + } + + /* Calculate padding */ + PaddingLength = 0; + if(FormatProperties->FieldWidth > StringLength) + { + PaddingLength = FormatProperties->FieldWidth - StringLength; + } + + /* Check is left side padding is required */ + if(!FormatProperties->LeftJustified) + { + /* Pad left */ + while(PaddingLength > 0) + { + /* Write space */ + Status = RtlpWriteWideCharacter(Context, L' '); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement padding length */ + PaddingLength--; + } + } + + /* Write string character by character */ + while(StringLength) + { + /* Prepare wide character to write */ + WideCharacter[0] = *String; + WideCharacter[1] = 0; + + /* Write wide character */ + Status = RtlpWriteWideCharacter(Context, *WideCharacter); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Get next character */ + StringLength--; + String++; + } + + /* Pad right, if required */ + while(PaddingLength > 0) + { + /* Write space */ + Status = RtlpWriteWideCharacter(Context, L' '); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement padding length */ + PaddingLength--; + } + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Writes a wide string value to the destination provided by the print context. + * + * @param Context + * Supplies a pointer to the print context structure. + * + * @param FormatProperties + * Supplies a pointer to the print format properties structure, describing the style characteristics. + * + * @param String + * Supplies the wide string value to write. + * + * @param Character + * Specifies whether the string value is expected to be a single character or not. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +RtlpWriteWideStringValue(PRTL_PRINT_CONTEXT Context, + PRTL_PRINT_FORMAT_PROPERTIES FormatProperties, + PWCHAR String, + BOOLEAN Character) +{ + ULONG PaddingLength; + SIZE_T StringLength; + XTSTATUS Status; + + /* Check for NULL string */ + if(String == NULL) + { + /* Print '(null)' instead */ + String = L"(null)"; + } + + /* Check if single character is expected */ + if(Character) + { + /* Force string length to 1 */ + StringLength = 1; + } + else + { + /* Get real string length */ + StringLength = RtlWideStringLength(String, 0); + } + + /* Check if string length exceeds precision limit */ + if((FormatProperties->Precision >= 0) && (StringLength > FormatProperties->Precision)) + { + /* Limit string length to precision number */ + StringLength = FormatProperties->Precision; + } + + /* Calculate padding */ + PaddingLength = 0; + if(FormatProperties->FieldWidth > StringLength) + { + PaddingLength = FormatProperties->FieldWidth - StringLength; + } + + + /* Check is left side padding is required */ + if(!FormatProperties->LeftJustified) + { + /* Pad left */ + while(PaddingLength > 0) + { + /* Write space */ + Status = RtlpWriteWideCharacter(Context, L' '); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement padding length */ + PaddingLength--; + } + } + + /* Write string character by character */ + while(StringLength != 0) + { + /* Write wide character */ + Status = RtlpWriteWideCharacter(Context, *String); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Get next character */ + StringLength--; + String++; + } + + /* Pad right, if required */ + while(PaddingLength > 0) + { + /* Write space */ + Status = RtlpWriteWideCharacter(Context, L' '); + if(Status != STATUS_SUCCESS) + { + /* Failed to write character, return status code */ + return Status; + } + + /* Decrement padding length */ + PaddingLength--; + } + + /* Return success */ + return STATUS_SUCCESS; +}