diff --git a/misc/tools/yori/CMakeLists.txt b/misc/tools/yori/CMakeLists.txt index 4a248d6094..3fa2df83bf 100644 --- a/misc/tools/yori/CMakeLists.txt +++ b/misc/tools/yori/CMakeLists.txt @@ -52,14 +52,18 @@ set(YORI_FILES yori/edit/edit.h yori/edit/options.c yori/lib/Makefile + yori/lib/airplane.c + yori/lib/bargraph.c yori/lib/builtin.c yori/lib/bytebuf.c + yori/lib/cabinet.c yori/lib/call.c yori/lib/cancel.c yori/lib/clip.c yori/lib/cmdline.c yori/lib/color.c yori/lib/condrv.c + yori/lib/cpuinfo.c yori/lib/cshot.c yori/lib/curdir.c yori/lib/cvtcons.c @@ -82,26 +86,49 @@ set(YORI_FILES yori/lib/group.c yori/lib/hash.c yori/lib/hexdump.c + yori/lib/http.c yori/lib/iconv.c + yori/lib/jobobj.c yori/lib/license.c yori/lib/lineread.c yori/lib/list.c yori/lib/malloc.c + yori/lib/movefile.c yori/lib/numkey.c + yori/lib/obenum.c yori/lib/osver.c yori/lib/path.c yori/lib/printf.c yori/lib/printf.inc yori/lib/printfa.c + yori/lib/priv.c yori/lib/process.c + yori/lib/progman.c + yori/lib/recycle.c + yori/lib/rsrc.c + yori/lib/scheme.c yori/lib/scut.c yori/lib/select.c yori/lib/strarray.c yori/lib/string.c yori/lib/strmenum.c yori/lib/temp.c + yori/lib/update.c yori/lib/util.c yori/lib/vt.c + yori/lib/ylhomedr.c + yori/lib/ylstralc.c + yori/lib/ylstrcat.c + yori/lib/ylstrcmp.c + yori/lib/ylstrcnt.c + yori/lib/ylstrcnv.c + yori/lib/ylstrfnd.c + yori/lib/ylstrhex.c + yori/lib/ylstrnum.c + yori/lib/ylstrsrt.c + yori/lib/ylstrtrm.c + yori/lib/ylvolpth.c + yori/lib/ylvtdbg.c yori/lib/yori.man yori/lib/yoricall.h yori/lib/yoricmpt.h diff --git a/misc/tools/yori/yori/crt/ep_cons.c b/misc/tools/yori/yori/crt/ep_cons.c index 11f1119c30..d981e10b10 100644 --- a/misc/tools/yori/yori/crt/ep_cons.c +++ b/misc/tools/yori/yori/crt/ep_cons.c @@ -40,6 +40,31 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1700) #pragma warning(disable: 26451) // Arithmetic overflow +#if (_MSC_VER >= 1939) +// +// These warnings aim to check for integer overflows when allocating memory, +// resulting in a smaller allocation than expected. Unfortunately the +// implementation is quite bad: +// +// - An allocator is a function with "alloc" in its name, which matches +// Yori's bounds check functions +// - There is no annotation or awareness of a bounds check function, so it +// will catch overflows that have already been checked. +// +// The "best" thing Yori could do here is implement bounds check functions, +// without "alloc" in their names, that return an integer value which is +// supplied directly to the allocator. Note this has the effect of +// disabling these warnings completely anyway, because they only apply to +// "alloc" functions and would not catch overflows as the input of the +// bounds check. +// +// This is quite frustrating because the class of bug these exist to catch +// does exist in Yori and is challenging to completely eliminate. +// +#pragma warning(disable: 26831) // Allocation size might be from overflow +#pragma warning(disable: 26832) // Allocation size from narrowing conversion +#pragma warning(disable: 26833) // Potential overflow before a bounds check +#endif #endif #if defined(_MSC_VER) && (_MSC_VER >= 1200) diff --git a/misc/tools/yori/yori/edit/edit.c b/misc/tools/yori/yori/edit/edit.c index b902e51c44..c19a3c2434 100644 --- a/misc/tools/yori/yori/edit/edit.c +++ b/misc/tools/yori/yori/edit/edit.c @@ -808,7 +808,7 @@ EditSaveFile( // if (Line->LengthInChars > 0 && (!AutoIndentActive || LineIndex != AutoIndentLine)) { - if (!YoriLibOutputTextToMultibyteDevice(TempHandle, Line)) { + if (!YoriLibOutputTextToMbyteDev(TempHandle, Line)) { CloseHandle(TempHandle); DeleteFile(TempFileName.StartOfString); YoriLibFreeStringContents(&TempFileName); @@ -816,7 +816,7 @@ EditSaveFile( return FALSE; } } - if (!YoriLibOutputTextToMultibyteDevice(TempHandle, &EditContext->Newline)) { + if (!YoriLibOutputTextToMbyteDev(TempHandle, &EditContext->Newline)) { CloseHandle(TempHandle); DeleteFile(TempFileName.StartOfString); YoriLibFreeStringContents(&TempFileName); @@ -1384,9 +1384,9 @@ EditSaveAsButtonClicked( CustomOptionArray[1].Values = LineEndingValues; CustomOptionArray[1].SelectedValue = 0; - if (YoriLibCompareStringWithLiteral(&EditContext->Newline, _T("\n")) == 0) { + if (YoriLibCompareStringLit(&EditContext->Newline, _T("\n")) == 0) { CustomOptionArray[1].SelectedValue = 1; - } else if (YoriLibCompareStringWithLiteral(&EditContext->Newline, _T("\r")) == 0) { + } else if (YoriLibCompareStringLit(&EditContext->Newline, _T("\r")) == 0) { CustomOptionArray[1].SelectedValue = 2; } @@ -1727,9 +1727,9 @@ EditFindNextMatchingString( Substring.StartOfString = Line->StartOfString + StartOffset; Substring.LengthInChars = Line->LengthInChars - StartOffset; if (EditContext->SearchMatchCase) { - Match = YoriLibFindFirstMatchingSubstring(&Substring, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindFirstMatchSubstr(&Substring, 1, &EditContext->SearchString, &Offset); } else { - Match = YoriLibFindFirstMatchingSubstringInsensitive(&Substring, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindFirstMatchSubstrIns(&Substring, 1, &EditContext->SearchString, &Offset); } if (Match != NULL) { @@ -1746,9 +1746,9 @@ EditFindNextMatchingString( for (LineIndex = StartLine + 1; LineIndex < LineCount; LineIndex++) { Line = YoriWinMultilineEditGetLineByIndex(EditContext->MultilineEdit, LineIndex); if (EditContext->SearchMatchCase) { - Match = YoriLibFindFirstMatchingSubstring(Line, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindFirstMatchSubstr(Line, 1, &EditContext->SearchString, &Offset); } else { - Match = YoriLibFindFirstMatchingSubstringInsensitive(Line, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindFirstMatchSubstrIns(Line, 1, &EditContext->SearchString, &Offset); } if (Match != NULL) { *NextMatchLine = LineIndex; @@ -1823,9 +1823,9 @@ EditFindPreviousMatchingString( } } if (EditContext->SearchMatchCase) { - Match = YoriLibFindLastMatchingSubstring(&Substring, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindLastMatchSubstr(&Substring, 1, &EditContext->SearchString, &Offset); } else { - Match = YoriLibFindLastMatchingSubstringInsensitive(&Substring, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindLastMatchSubstrIns(&Substring, 1, &EditContext->SearchString, &Offset); } if (Match != NULL) { @@ -1841,9 +1841,9 @@ EditFindPreviousMatchingString( for (LineIndex = StartLine; LineIndex > 0; LineIndex--) { Line = YoriWinMultilineEditGetLineByIndex(EditContext->MultilineEdit, LineIndex - 1); if (EditContext->SearchMatchCase) { - Match = YoriLibFindLastMatchingSubstring(Line, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindLastMatchSubstr(Line, 1, &EditContext->SearchString, &Offset); } else { - Match = YoriLibFindLastMatchingSubstringInsensitive(Line, 1, &EditContext->SearchString, &Offset); + Match = YoriLibFindLastMatchSubstrIns(Line, 1, &EditContext->SearchString, &Offset); } if (Match != NULL) { *NextMatchLine = LineIndex - 1; @@ -3163,14 +3163,14 @@ EditEncodingFromString( __in PYORI_STRING String ) { - if (YoriLibCompareStringWithLiteralInsensitive(String, _T("utf8")) == 0 && + if (YoriLibCompareStringLitIns(String, _T("utf8")) == 0 && YoriLibIsUtf8Supported()) { return CP_UTF8; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("ascii")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("ascii")) == 0) { return CP_OEMCP; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("ansi")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("ansi")) == 0) { return CP_ACP; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("utf16")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("utf16")) == 0) { return CP_UTF16; } return (DWORD)-1; @@ -3231,19 +3231,19 @@ ENTRYPOINT( if (YoriLibIsCommandLineOption(&ArgV[i], &Arg)) { - if (YoriLibCompareStringWithLiteralInsensitive(&Arg, _T("?")) == 0) { + if (YoriLibCompareStringLitIns(&Arg, _T("?")) == 0) { EditHelp(); return EXIT_SUCCESS; - } else if (YoriLibCompareStringWithLiteralInsensitive(&Arg, _T("license")) == 0) { + } else if (YoriLibCompareStringLitIns(&Arg, _T("license")) == 0) { YoriLibDisplayMitLicense(strEditCopyrightYear); return EXIT_SUCCESS; - } else if (YoriLibCompareStringWithLiteralInsensitive(&Arg, _T("a")) == 0) { + } else if (YoriLibCompareStringLitIns(&Arg, _T("a")) == 0) { GlobalEditContext.UseAsciiDrawing = TRUE; ArgumentUnderstood = TRUE; - } else if (YoriLibCompareStringWithLiteralInsensitive(&Arg, _T("b")) == 0) { + } else if (YoriLibCompareStringLitIns(&Arg, _T("b")) == 0) { GlobalEditContext.UseMonoDisplay = TRUE; ArgumentUnderstood = TRUE; - } else if (YoriLibCompareStringWithLiteralInsensitive(&Arg, _T("e")) == 0) { + } else if (YoriLibCompareStringLitIns(&Arg, _T("e")) == 0) { if (ArgC > i + 1) { DWORD NewEncoding; NewEncoding = EditEncodingFromString(&ArgV[i + 1]); @@ -3253,7 +3253,7 @@ ENTRYPOINT( i++; } } - } else if (YoriLibCompareStringWithLiteralInsensitive(&Arg, _T("r")) == 0) { + } else if (YoriLibCompareStringLitIns(&Arg, _T("r")) == 0) { GlobalEditContext.ReadOnly = TRUE; ArgumentUnderstood = TRUE; } diff --git a/misc/tools/yori/yori/lib/Makefile b/misc/tools/yori/yori/lib/Makefile index b52094c797..5548e2eb22 100644 --- a/misc/tools/yori/yori/lib/Makefile +++ b/misc/tools/yori/yori/lib/Makefile @@ -4,14 +4,18 @@ compile: yorilib.lib yoriver.obj !INCLUDE "..\config\common.mk" OBJS=\ + airplane.obj \ + bargraph.obj \ builtin.obj \ bytebuf.obj \ + cabinet.obj \ call.obj \ cancel.obj \ clip.obj \ cmdline.obj \ color.obj \ condrv.obj \ + cpuinfo.obj \ cshot.obj \ curdir.obj \ cvtcons.obj \ @@ -34,25 +38,47 @@ OBJS=\ group.obj \ hash.obj \ hexdump.obj \ + http.obj \ iconv.obj \ + jobobj.obj \ license.obj \ lineread.obj \ list.obj \ malloc.obj \ + movefile.obj \ numkey.obj \ + obenum.obj \ osver.obj \ path.obj \ printf.obj \ printfa.obj \ + priv.obj \ process.obj \ + progman.obj \ + recycle.obj \ + rsrc.obj \ scut.obj \ + scheme.obj \ select.obj \ strarray.obj \ - string.obj \ strmenum.obj \ temp.obj \ + update.obj \ util.obj \ vt.obj \ + ylhomedr.obj \ + ylstralc.obj \ + ylstrcat.obj \ + ylstrcmp.obj \ + ylstrcnt.obj \ + ylstrcnv.obj \ + ylstrfnd.obj \ + ylstrhex.obj \ + ylstrnum.obj \ + ylstrsrt.obj \ + ylstrtrm.obj \ + ylvolpth.obj \ + ylvtdbg.obj \ all: yorilib.lib yoriver.obj diff --git a/misc/tools/yori/yori/lib/airplane.c b/misc/tools/yori/yori/lib/airplane.c new file mode 100644 index 0000000000..7df6911ebc --- /dev/null +++ b/misc/tools/yori/yori/lib/airplane.c @@ -0,0 +1,177 @@ +/** + * @file lib/airplane.c + * + * Helper routines for manipulating airplane mode + * + * Copyright (c) 2023 Malcolm Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#pragma warning(disable: 4226) + +/** + A declaration for a GUID defining the radio API interface. + */ +const GUID CLSID_RadioManagement = { 0x581333F6L, 0x28DB, 0x41BE, { 0xBC, 0x7A, 0xFF, 0x20, 0x1F, 0x12, 0xF3, 0xF6 } }; + +/** + The IRadioManager interface. + */ +const GUID IID_IRadioManager = { 0xDB3AFBFBL, 0x08E6, 0x46C6, { 0xAA, 0x70, 0xBF, 0x9A, 0x34, 0xC3, 0x0A, 0xB7 } }; + +/** + Query Airplane Mode state. + + @param AirplaneModeEnabled On successful completion, set to TRUE to indicate + airplane mode is enabled; set to FALSE to indicate it is disabled. + + @param AirplaneModeChangable On successful completion, set to TRUE to indicate + airplane mode can be changed; set to FALSE to indicate it cannot + change. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibGetAirplaneMode( + __out PBOOLEAN AirplaneModeEnabled, + __out PBOOLEAN AirplaneModeChangable + ) +{ + IRadioManager *RadioManager = NULL; + HRESULT hRes; + BOOL bRadioEnabled; + BOOL bUiEnabled; + DWORD dwState; + BOOLEAN Result; + + Result = FALSE; + + YoriLibLoadOle32Functions(); + if (DllOle32.pCoCreateInstance == NULL || DllOle32.pCoInitialize == NULL) { + return FALSE; + } + + hRes = DllOle32.pCoInitialize(NULL); + if (!SUCCEEDED(hRes)) { + return FALSE; + } + + hRes = DllOle32.pCoCreateInstance(&CLSID_RadioManagement, NULL, CLSCTX_LOCAL_SERVER, &IID_IRadioManager, (void **)&RadioManager); + if (!SUCCEEDED(hRes)) { + return FALSE; + } + + bRadioEnabled = TRUE; + bUiEnabled = TRUE; + dwState = 0; + + hRes = RadioManager->Vtbl->GetSystemRadioState(RadioManager, &bRadioEnabled, &bUiEnabled, &dwState); + if (!SUCCEEDED(hRes)) { + Result = FALSE; + goto Exit; + } + + if (bRadioEnabled == FALSE) { + *AirplaneModeEnabled = TRUE; + } else { + *AirplaneModeEnabled = FALSE; + } + + if (bUiEnabled) { + *AirplaneModeChangable = TRUE; + } else { + *AirplaneModeChangable = FALSE; + } + + Result = TRUE; + +Exit: + + if (RadioManager != NULL) { + RadioManager->Vtbl->Release(RadioManager); + } + + return Result; +} + +/** + Set Airplane Mode state. + + @param AirplaneModeEnabled TRUE to indicate airplane mode should be enabled; + FALSE to indicate it should be disabled. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibSetAirplaneMode( + __in BOOLEAN AirplaneModeEnabled + ) +{ + IRadioManager *RadioManager = NULL; + HRESULT hRes; + BOOL bRadioEnabled; + BOOLEAN Result; + + Result = FALSE; + + YoriLibLoadOle32Functions(); + if (DllOle32.pCoCreateInstance == NULL || DllOle32.pCoInitialize == NULL) { + return FALSE; + } + + hRes = DllOle32.pCoInitialize(NULL); + if (!SUCCEEDED(hRes)) { + return FALSE; + } + + hRes = DllOle32.pCoCreateInstance(&CLSID_RadioManagement, NULL, CLSCTX_LOCAL_SERVER, &IID_IRadioManager, (void **)&RadioManager); + if (!SUCCEEDED(hRes)) { + return FALSE; + } + + bRadioEnabled = TRUE; + if (AirplaneModeEnabled) { + bRadioEnabled = FALSE; + } + + hRes = RadioManager->Vtbl->SetSystemRadioState(RadioManager, bRadioEnabled); + if (!SUCCEEDED(hRes)) { + Result = FALSE; + goto Exit; + } + + Result = TRUE; + +Exit: + + if (RadioManager != NULL) { + RadioManager->Vtbl->Release(RadioManager); + } + + return Result; +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/bargraph.c b/misc/tools/yori/yori/lib/bargraph.c new file mode 100644 index 0000000000..0163602490 --- /dev/null +++ b/misc/tools/yori/yori/lib/bargraph.c @@ -0,0 +1,178 @@ +/** + * @file lib/bargraph.c + * + * Yori bar graph for disk space, battery, memory, etc. + * + * Copyright (c) 2017-2023 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Display a bar graph indicating resource utilization. + + @param hOutput Handle to the device to output the graph to. + + @param TenthsOfPercent The fraction of the graph to fill, where the entire + graph would be described by 1000 units. + + @param GreenThreshold If TenthsOfPercent is beyond this value, the graph + should be displayed as green, indicating ample resources. Whether + the value is above or below this threshold to display green is + determined by whether this value is above or below the red threshold. + + @param RedThreshold If TenthsOfPercent is beyond this value, the graph + should be displayed as red, indicating resource scarcity. Whether + the value is above or below this threshold to display red is + determined by whether this value is above or below the green + threshold. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOLEAN +YoriLibDisplayBarGraph( + __in HANDLE hOutput, + __in DWORD TenthsOfPercent, + __in DWORD GreenThreshold, + __in DWORD RedThreshold + ) +{ + YORI_STRING Subset; + YORI_STRING Line; + WORD Background; + YORI_ALLOC_SIZE_T TotalBarSize; + YORI_ALLOC_SIZE_T CharsRequired; + DWORD WholeBarsSet; + DWORD PartialBarsSet; + DWORD Index; + UCHAR Ctrl; + USHORT Color; + BOOLEAN LowerIsBetter; + BOOL SupportsColor; + BOOL SupportsExtendedChars; + WORD Width; + + Width = 0; + YoriLibGetWindowDimensions(hOutput, &Width, NULL); + + // + // This view contains four cells of overhead (space plus bracket on each + // end.) Having fewer than around 10 cells would make a meaningless + // display. + // + + if (Width < 10) { + return FALSE; + } + + SupportsColor = FALSE; + SupportsExtendedChars = FALSE; + + if (!YoriLibQueryConsoleCapabilities(hOutput, &SupportsColor, &SupportsExtendedChars, NULL)) { + SupportsColor = FALSE; + SupportsExtendedChars = FALSE; + } + + CharsRequired = (YORI_ALLOC_SIZE_T)(Width + YORI_MAX_VT_ESCAPE_CHARS * 2); + if (!YoriLibAllocateString(&Line, CharsRequired)) { + return FALSE; + } + + Line.StartOfString[0] = ' '; + Line.StartOfString[1] = '['; + + YoriLibInitEmptyString(&Subset); + Subset.StartOfString = &Line.StartOfString[2]; + Subset.LengthAllocated = (YORI_ALLOC_SIZE_T)(Line.LengthAllocated - 2); + + Ctrl = YORILIB_ATTRCTRL_WINDOW_BG; + Background = (USHORT)(YoriLibVtGetDefaultColor() & 0xF0); + + LowerIsBetter = FALSE; + if (GreenThreshold < RedThreshold) { + LowerIsBetter = TRUE; + } + + if (SupportsColor) { + if (LowerIsBetter) { + if (TenthsOfPercent <= GreenThreshold) { + Color = (USHORT)(Background | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } else if (TenthsOfPercent >= RedThreshold) { + Color = (USHORT)(Background | FOREGROUND_RED | FOREGROUND_INTENSITY); + } else { + Color = (USHORT)(Background | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } + } else { + if (TenthsOfPercent >= GreenThreshold) { + Color = (USHORT)(Background | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } else if (TenthsOfPercent <= RedThreshold) { + Color = (USHORT)(Background | FOREGROUND_RED | FOREGROUND_INTENSITY); + } else { + Color = (USHORT)(Background | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + } + } + + YoriLibVtStringForTextAttribute(&Subset, Ctrl, Color); + } + + Line.LengthInChars = (YORI_ALLOC_SIZE_T)(2 + Subset.LengthInChars); + + TotalBarSize = (YORI_ALLOC_SIZE_T)(Width - 4); + PartialBarsSet = TotalBarSize * TenthsOfPercent / 500; + WholeBarsSet = PartialBarsSet / 2; + PartialBarsSet = PartialBarsSet % 2; + + if (SupportsExtendedChars) { + for (Index = 0; Index < WholeBarsSet; Index++) { + Line.StartOfString[Index + Line.LengthInChars] = 0x2588; + } + if (PartialBarsSet) { + Line.StartOfString[Index + Line.LengthInChars] = 0x258c; + Index++; + } + } else { + for (Index = 0; Index < WholeBarsSet; Index++) { + Line.StartOfString[Index + Line.LengthInChars] = '#'; + } + } + for (; Index < TotalBarSize; Index++) { + Line.StartOfString[Index + Line.LengthInChars] = ' '; + } + Line.LengthInChars = (YORI_ALLOC_SIZE_T)(Line.LengthInChars + TotalBarSize); + + Subset.StartOfString = &Line.StartOfString[Line.LengthInChars]; + Subset.LengthAllocated = (YORI_ALLOC_SIZE_T)(Line.LengthAllocated - Line.LengthInChars); + + if (SupportsColor) { + Subset.LengthInChars = (YORI_ALLOC_SIZE_T)YoriLibSPrintf(Subset.StartOfString, _T("%c[0m]\n"), 27); + } else { + Subset.LengthInChars = (YORI_ALLOC_SIZE_T)YoriLibSPrintf(Subset.StartOfString, _T("]\n")); + } + Line.LengthInChars = (YORI_ALLOC_SIZE_T)(Line.LengthInChars + Subset.LengthInChars); + + YoriLibOutputToDevice(hOutput, 0, _T("%y"), &Line); + YoriLibFreeStringContents(&Line); + + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/cabinet.c b/misc/tools/yori/yori/lib/cabinet.c new file mode 100644 index 0000000000..53384ff71d --- /dev/null +++ b/misc/tools/yori/yori/lib/cabinet.c @@ -0,0 +1,1799 @@ +/** + * @file lib/cabinet.c + * + * Yori shell extract .cab files + * + * Copyright (c) 2018-2022 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +/** + Context information to pass around as files are being expanded. + */ +typedef struct _YORI_LIB_CAB_EXPAND_CONTEXT { + + /** + The directory to expand files into. + */ + PYORI_STRING TargetDirectory; + + /** + If TRUE, all files not matching any list below are expanded. If not, + only files explicitly listed are expanded. + */ + BOOL DefaultInclude; + + /** + The number of files in the FilesToInclude array. + */ + DWORD NumberFilesToInclude; + + /** + An array of strings corresponding to files that should be expanded. + */ + PYORI_STRING FilesToInclude; + + /** + The number of files in the FilesToExclude array. + */ + DWORD NumberFilesToExclude; + + /** + An array of strings corresponding to files that should not be expanded. + */ + PYORI_STRING FilesToExclude; + + /** + A user specified callback to provide notification for a given file. + */ + PYORI_LIB_CAB_EXPAND_FILE_CALLBACK CommenceExtractCallback; + + /** + A user specified callback to provide notification for a given file. + */ + PYORI_LIB_CAB_EXPAND_FILE_CALLBACK CompleteExtractCallback; + + /** + Context information to pass to the user specified callback. + */ + PVOID UserContext; + + /** + Error code describing the result of the operation. + */ + DWORD ErrorCode; + + /** + Optionally points to a string to populate with error information to + display to a user. + */ + PYORI_STRING ErrorString; + +} YORI_LIB_CAB_EXPAND_CONTEXT, *PYORI_LIB_CAB_EXPAND_CONTEXT; + +/** + Context passed when adding files during compression operations. Used here + to indicate which encoding to use to interpret file names. + */ +typedef struct _YORI_CAB_ADD_CONTEXT { + + /** + TRUE if the narrow string specifying a file name is encoded in UTF-8. + If FALSE, the narrow string is encoded as the active code page. UTF-8 + is used for on disk names wherever support is present. + */ + BOOLEAN OnDiskNameIsUtf; + + /** + TRUE if the narrow string specifying a directory entry in a CAB should + be interpreted as UTF-8, FALSE if it should be interpreted as the active + code page. This is only set to TRUE if the file name contains extended + characters, since files can be extracted without UTF-8 decoding if no + extended characters are present. UTF-8 decoding doesn't exist in NT 3.x + either. + */ + BOOLEAN InCabNameIsUtf; +} YORI_CAB_ADD_CONTEXT, *PYORI_CAB_ADD_CONTEXT; + +/** + Allocate and convert a wide string into a narrow string in a specified + encoding. + + @param WideString Pointer to the wide string to convert. + + @param Encoding Specifies the encoding to use in the narrow string. + + @param NarrowString On successful completion, populated with an allocated + string containing the narrow encoded string. This should be freed + with @ref YoriLibFree . + + @return Win32 error code indicating the result of the operation, including + ERROR_SUCCESS to indicate success. + */ +__success(return == ERROR_SUCCESS) +DWORD +YoriLibCabWideToNarrow( + __in PYORI_STRING WideString, + __in DWORD Encoding, + __out LPSTR * NarrowString + ) +{ + BOOL DefaultUsed; + YORI_ALLOC_SIZE_T LengthNeeded; + LPSTR LocalNarrowString; + DWORD Error; + PBOOL DefaultPtr; + + DefaultUsed = FALSE; + + LengthNeeded = (YORI_ALLOC_SIZE_T)WideCharToMultiByte(Encoding, 0, WideString->StartOfString, WideString->LengthInChars, NULL, 0, NULL, NULL); + + LocalNarrowString = YoriLibMalloc((YORI_ALLOC_SIZE_T)(LengthNeeded + 1)); + if (LocalNarrowString == NULL) { + return ERROR_NOT_ENOUGH_MEMORY; + } + + // + // WideCharToMultiByte fails if DefaultUsed is specified with an encoding + // that doesn't support default characters, like UTF8 + // + + DefaultPtr = &DefaultUsed; + if (Encoding == CP_UTF8) { + DefaultPtr = NULL; + } + + if (WideCharToMultiByte(Encoding, 0, WideString->StartOfString, WideString->LengthInChars, LocalNarrowString, LengthNeeded, NULL, DefaultPtr) != (INT)(LengthNeeded)) { + Error = GetLastError(); + YoriLibFree(LocalNarrowString); + return Error; + } + + if (DefaultUsed) { + YoriLibFree(LocalNarrowString); + return ERROR_NO_UNICODE_TRANSLATION; + } + + LocalNarrowString[LengthNeeded] = '\0'; + *NarrowString = LocalNarrowString; + return ERROR_SUCCESS; +} + +/** + Convert a narrow string with a specified encoding into a newly allocated + wide string. + + @param NarrowString Pointer to the source narrow string. + + @param Encoding Specifies the encoding of the narrow string. + + @param WideString On successful completion, populated with a newly allocated + wide string. The caller should free this with + @ref YoriLibFreeStringContents . + + @return Win32 error code, including ERROR_SUCCESS to indicate successful + completion. + */ +__success(return == ERROR_SUCCESS) +DWORD +YoriLibCabNarrowToWide( + __in LPSTR NarrowString, + __in DWORD Encoding, + __out PYORI_STRING WideString + ) +{ + YORI_ALLOC_SIZE_T LengthNeeded; + YORI_STRING LocalString; + DWORD Error; + + LengthNeeded = (YORI_ALLOC_SIZE_T)MultiByteToWideChar(Encoding, 0, NarrowString, -1, NULL, 0); + if (!YoriLibAllocateString(&LocalString, (YORI_ALLOC_SIZE_T)(LengthNeeded + 1))) { + return ERROR_NOT_ENOUGH_MEMORY; + } + + if (MultiByteToWideChar(Encoding, 0, NarrowString, -1, LocalString.StartOfString, LengthNeeded) != (INT)(LengthNeeded)) { + Error = GetLastError(); + YoriLibFreeStringContents(&LocalString); + return Error; + } + + LocalString.StartOfString[LengthNeeded] = '\0'; + LocalString.LengthInChars = (YORI_ALLOC_SIZE_T)(LengthNeeded - 1); + memcpy(WideString, &LocalString, sizeof(YORI_STRING)); + return ERROR_SUCCESS; +} + +/** + Bits indicating a file should be opened read only. + */ +#define YORI_LIB_CAB_OPEN_READONLY (0x0000) + +/** + Bits indicating a file should be opened write only. + */ +#define YORI_LIB_CAB_OPEN_WRITEONLY (0x0001) + +/** + Bits indicating a file should be opened read and write. + */ +#define YORI_LIB_CAB_OPEN_READWRITE (0x0002) + +/** + The set of bits to compare when trying to discover the open mode. + */ +#define YORI_LIB_CAB_OPEN_MASK (0x0003) + +/** + A callback invoked during FCI to open a file. Note that this is used + on the same file multiple times and thus requires sharing with previous + opens. + + @param FileName A NULL terminated narrow string indicating the file name. + + @param Encoding Specifies the character encoding of the FileName string. + + @param OFlag The open flags, apparently modelled on the CRT but don't + really match those values. + + @param PMode No idea (per MSDN.) + + @return File handle. + */ +DWORD_PTR +YoriLibCabFileOpen( + __in LPSTR FileName, + __in DWORD Encoding, + __in INT OFlag, + __in INT PMode + ) +{ + HANDLE hFile; + DWORD DesiredAccess; + DWORD ShareMode; + DWORD Disposition; + DWORD OpenType = ((DWORD)OFlag & YORI_LIB_CAB_OPEN_MASK); + YORI_STRING WideFileName; + DWORD Error; + + UNREFERENCED_PARAMETER(PMode); + + switch(OpenType) { + case YORI_LIB_CAB_OPEN_READONLY: + DesiredAccess = GENERIC_READ; + Disposition = OPEN_EXISTING; + ShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE; + break; + case YORI_LIB_CAB_OPEN_WRITEONLY: + DesiredAccess = GENERIC_WRITE; + Disposition = CREATE_ALWAYS; + ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case YORI_LIB_CAB_OPEN_READWRITE: + DesiredAccess = GENERIC_READ | GENERIC_WRITE; + Disposition = OPEN_ALWAYS; + ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + default: + return (DWORD_PTR)INVALID_HANDLE_VALUE; + } + + Error = YoriLibCabNarrowToWide(FileName, Encoding, &WideFileName); + if (Error != ERROR_SUCCESS) { + SetLastError(Error); + return (DWORD_PTR)INVALID_HANDLE_VALUE; + } + + hFile = CreateFile(WideFileName.StartOfString, + DesiredAccess, + ShareMode, + NULL, + Disposition, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + YoriLibFreeStringContents(&WideFileName); + + return (DWORD_PTR)hFile; +} + +/** + A callback invoked during FDICopy to allocate memory. + + @param Bytes The number of bytes to allocate. + + @return Pointer to allocated memory or NULL on failure. + */ +LPVOID DIAMONDAPI +YoriLibCabAlloc( + __in DWORD Bytes + ) +{ + LPVOID Alloc; + YORI_ALLOC_SIZE_T ExtraBytes; + ExtraBytes = 0; + + if (Bytes > YORI_MAX_ALLOC_SIZE) { + return NULL; + } + + // + // Alpha cabinet appears to access bytes beyond its buffer. Fudge a bit. + // +#if _M_ALPHA + ExtraBytes = 0x1000; +#endif + + if (Bytes + ExtraBytes > YORI_MAX_ALLOC_SIZE) { + return NULL; + } + + Alloc = YoriLibMalloc((YORI_ALLOC_SIZE_T)(Bytes + ExtraBytes)); + return Alloc; +} + +/** + A callback invoked during FDICopy to free memory. + + @param Alloc The allocation to free. + */ +VOID DIAMONDAPI +YoriLibCabFree( + __in PVOID Alloc + ) +{ + YoriLibFree(Alloc); +} + +/** + Notification that a file has been placed into a CAB when compressing new + files. + + @param CabContext Pointer to the cabinet structure which can be populated + with new information. + + @param FileNameInCab Pointer to an narrow NULL terminated string indicating + the file that was placed in the CAB. + + @param FileLength Indicates the length of the file placed into the CAB, + in bytes. + + @param Continuation TRUE if the file has been split into segments and this + is a subsequent segment. + + @param Context Pointer to user context, currently ignored. + + @return Zero to indicate success. + */ +DWORD_PTR DIAMONDAPI +YoriLibCabFciFilePlaced( + __in PCAB_FCI_CONTEXT CabContext, + __in LPSTR FileNameInCab, + __in DWORD FileLength, + __in BOOL Continuation, + __in PVOID Context + ) +{ + UNREFERENCED_PARAMETER(CabContext); + UNREFERENCED_PARAMETER(FileNameInCab); + UNREFERENCED_PARAMETER(FileLength); + UNREFERENCED_PARAMETER(Continuation); + UNREFERENCED_PARAMETER(Context); + + return 0; +} + +/** + Bits indicating a file should be opened read only. + */ +#define YORI_LIB_CAB_OPEN_READONLY (0x0000) + +/** + Bits indicating a file should be opened write only. + */ +#define YORI_LIB_CAB_OPEN_WRITEONLY (0x0001) + +/** + Bits indicating a file should be opened read and write. + */ +#define YORI_LIB_CAB_OPEN_READWRITE (0x0002) + +/** + The set of bits to compare when trying to discover the open mode. + */ +#define YORI_LIB_CAB_OPEN_MASK (0x0003) + +/** + A callback invoked during FCI to open a file. Note that this is used + on the same file multiple times and thus requires sharing with previous + opens. + + @param FileName A NULL terminated narrow string indicating the file name. + The encoding is specified in the Context parameter. + + @param OFlag The open flags, apparently modelled on the CRT but don't + really match those values. + + @param PMode No idea (per MSDN.) + + @param Err Optionally points to an integer which could be populated with + extra error information. + + @param Context Points to user context, which indicates the character encoding + to apply. + + @return File handle. + */ +DWORD_PTR DIAMONDAPI +YoriLibCabFciFileOpen( + __in LPSTR FileName, + __in INT OFlag, + __in INT PMode, + __inout_opt PINT Err, + __inout PVOID Context + ) +{ + PYORI_CAB_ADD_CONTEXT AddContext = (PYORI_CAB_ADD_CONTEXT)Context; + DWORD Encoding; + + UNREFERENCED_PARAMETER(Err); + + Encoding = CP_ACP; + if (AddContext->OnDiskNameIsUtf) { + Encoding = CP_UTF8; + } + + return YoriLibCabFileOpen(FileName, Encoding, OFlag, PMode); +} + +/** + A callback invoked during FDICopy to open a file. Note that this is used + on the same file multiple times and thus requires sharing with previous + opens. + + @param FileName A NULL terminated narrow string indicating the file name. + + @param OFlag The open flags, apparently modelled on the CRT but don't + really match those values. + + @param PMode No idea (per MSDN.) + + @return File handle. + */ +DWORD_PTR DIAMONDAPI +YoriLibCabFdiFileOpen( + __in LPSTR FileName, + __in INT OFlag, + __in INT PMode + ) +{ + DWORD Encoding; + + // + // From observation, this callback is only invoked to open the cab + // itself, so the encoding here needs to match the encoding used to + // specify the cab. + // + + Encoding = CP_ACP; + if (YoriLibIsUtf8Supported()) { + Encoding = CP_UTF8; + } + + return YoriLibCabFileOpen(FileName, Encoding, OFlag, PMode); +} + + +/** + Combine a parent directory with the CAB's relative file name and output the + combined path and Unicode form of the relative file name. + + @param ParentDirectory Pointer to the parent directory to place files. + + @param FileName Pointer to a NULL terminated narrow string for the name of + the object within the CAB. + + @param Encoding Specifies the character encoding of the FileName string. + + @param FullPathName If specified, points to a Yori string to receive the + full path name. The memory is allocated in this routine and returned + with a reference. + + @param FileNameOnly If specified, points to a Yori string to receive the + file name without parent path. The memory is allocated in this + routine and is returned with a reference. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibCabBuildFileNames( + __in PYORI_STRING ParentDirectory, + __in LPSTR FileName, + __in DWORD Encoding, + __out_opt PYORI_STRING FullPathName, + __out_opt PYORI_STRING FileNameOnly + ) +{ + YORI_STRING FullPath; + YORI_STRING WideFileName; + YORI_ALLOC_SIZE_T ExtraChars; + DWORD CharsRequired; + DWORD Error; + + Error = YoriLibCabNarrowToWide(FileName, Encoding, &WideFileName); + if (Error != ERROR_SUCCESS) { + return FALSE; + } + + CharsRequired = ParentDirectory->LengthInChars; + CharsRequired = CharsRequired + 1 + WideFileName.LengthInChars + 1; + + if (!YoriLibIsSizeAllocatable(CharsRequired)) { + YoriLibFreeStringContents(&WideFileName); + return FALSE; + } + + if (!YoriLibAllocateString(&FullPath, (YORI_ALLOC_SIZE_T)CharsRequired)) { + YoriLibFreeStringContents(&WideFileName); + return FALSE; + } + + if (ParentDirectory->LengthInChars >= 1 && + ParentDirectory->StartOfString[ParentDirectory->LengthInChars - 1] == '\\') { + + FullPath.LengthInChars = YoriLibSPrintf(FullPath.StartOfString, _T("%y%y"), ParentDirectory, &WideFileName); + ExtraChars = 0; + } else { + FullPath.LengthInChars = YoriLibSPrintf(FullPath.StartOfString, _T("%y\\%y"), ParentDirectory, &WideFileName); + ExtraChars = 1; + } + + YoriLibFreeStringContents(&WideFileName); + + if (FullPathName != NULL) { + YoriLibCloneString(FullPathName, &FullPath); + } + + if (FileNameOnly != NULL) { + YoriLibCloneString(FileNameOnly, &FullPath); + FileNameOnly->StartOfString += ParentDirectory->LengthInChars + ExtraChars; + FileNameOnly->LengthInChars = (YORI_ALLOC_SIZE_T)(FileNameOnly->LengthInChars - ParentDirectory->LengthInChars - ExtraChars); + } + + YoriLibFreeStringContents(&FullPath); + + return TRUE; +} + +/** + Return TRUE to indicate a specified file should be expanded, or FALSE if + it should not be. + + @param FileName Pointer to a string containing the relative file name within + the CAB (ie., no destination path.) + + @param ExpandContext Pointer to the expand context containing the set of + files to include or exclude. + + @return TRUE to indicate a file should be expanded, or FALSE if it should + not be. + */ +BOOL +YoriLibCabShouldIncludeFile( + __in PYORI_STRING FileName, + __in PYORI_LIB_CAB_EXPAND_CONTEXT ExpandContext + ) +{ + BOOL IncludeFile = ExpandContext->DefaultInclude; + DWORD Count; + + if (IncludeFile) { + for (Count = 0; Count < ExpandContext->NumberFilesToExclude; Count++) { + if (YoriLibCompareStringIns(FileName, &ExpandContext->FilesToExclude[Count]) == 0) { + IncludeFile = FALSE; + break; + } + } + } + + if (!IncludeFile) { + for (Count = 0; Count < ExpandContext->NumberFilesToInclude; Count++) { + if (YoriLibCompareStringIns(FileName, &ExpandContext->FilesToInclude[Count]) == 0) { + IncludeFile = TRUE; + break; + } + } + } + + return IncludeFile; +} + +/** + Open a new file being extracted from a Cabinet. This implies the file is + being opened for write. Create a single parent directory if it doesn't + exist yet, and if the file exists already, try to move it out of the way + and attempt to delete it. Note that for running executables this case is + very common. + + @param FullPath Pointer to a string describing the file to open. + + @param ErrorCode Optionally points to a value to populate with the error code + encountered in the extraction process. + + @param ErrorString Optionally points to a string to populate with information + about any error encountered in the extraction process. + + @return Handle to the opened file, or INVALID_HANDLE_VALUE on failure. + */ +__success(return != INVALID_HANDLE_VALUE) +DWORD_PTR +YoriLibCabFileOpenForExtract( + __in PYORI_STRING FullPath, + __inout PDWORD ErrorCode, + __inout_opt PYORI_STRING ErrorString + ) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD Err = 0; + + while (TRUE) { + + // + // Try to open the target file + // + + hFile = CreateFile(FullPath->StartOfString, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (hFile != INVALID_HANDLE_VALUE) { + break; + } + + Err = GetLastError(); + + // + // If the path isn't found, try to create one parent component and + // retry the create. + // + + if (Err == ERROR_PATH_NOT_FOUND) { + LPTSTR LastSep; + YORI_ALLOC_SIZE_T OldLength = FullPath->LengthInChars; + + LastSep = YoriLibFindRightMostCharacter(FullPath, '\\'); + if (LastSep == NULL) { + break; + } + + *LastSep = '\0'; + FullPath->LengthInChars = (YORI_ALLOC_SIZE_T)(LastSep - FullPath->StartOfString); + + if (!YoriLibCreateDirectoryAndParents(FullPath)) { + Err = GetLastError(); + hFile = INVALID_HANDLE_VALUE; + } else { + + FullPath->LengthInChars = OldLength; + *LastSep = '\\'; + + hFile = CreateFile(FullPath->StartOfString, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) { + Err = GetLastError(); + } + } + + break; + + // + // If the file returns access denied, check if it's due to the + // hidden, system or read only bits. If they're set and can be + // cleared, retry. If not, fail. + // + + } else if (Err == ERROR_ACCESS_DENIED) { + DWORD Attributes; + Attributes = GetFileAttributes(FullPath->StartOfString); + if (Attributes != (DWORD)-1 && + ((Attributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) != 0) { + + Attributes = Attributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY); + if (!SetFileAttributes(FullPath->StartOfString, Attributes)) { + break; + } + } else { + break; + } + } else { + break; + } + } + + if (hFile == INVALID_HANDLE_VALUE) { + if (*ErrorCode == ERROR_SUCCESS) { + *ErrorCode = Err; + } + + if (ErrorString != NULL) { + LPTSTR ErrText; + ErrText = YoriLibGetWinErrorText(Err); + YoriLibYPrintf(ErrorString, _T("Error opening %y: %s"), FullPath, ErrText); + YoriLibFreeWinErrorText(ErrText); + } + } + + return (DWORD_PTR)hFile; +} + +/** + A callback invoked during FDICopy to read from a file. Note that these + callbacks always refer to the "current file position". + + @param FileHandle The file to read from. + + @param Buffer Pointer to a block of memory to place read data. + + @param ByteCount The number of bytes to read. + + @param Err Optionally points to an integer which could be populated with + extra error information. + + @param Context Optionally points to user context, which is currently unused. + + @return The number of bytes actually read or -1 on failure. + */ +DWORD DIAMONDAPI +YoriLibCabFciFileRead( + __in DWORD_PTR FileHandle, + __out PVOID Buffer, + __in DWORD ByteCount, + __inout_opt PINT Err, + __inout_opt PVOID Context + ) +{ + DWORD BytesRead; + UNREFERENCED_PARAMETER(Err); + UNREFERENCED_PARAMETER(Context); + if (!ReadFile((HANDLE)FileHandle, + Buffer, + ByteCount, + &BytesRead, + NULL)) { + return (DWORD)-1; + } + + return BytesRead; +} + +/** + A callback invoked during FDICopy to read from a file. Note that these + callbacks always refer to the "current file position". + + @param FileHandle The file to read from. + + @param Buffer Pointer to a block of memory to place read data. + + @param ByteCount The number of bytes to read. + + @return The number of bytes actually read or -1 on failure. + */ +DWORD DIAMONDAPI +YoriLibCabFdiFileRead( + __in DWORD_PTR FileHandle, + __out PVOID Buffer, + __in DWORD ByteCount + ) +{ + return YoriLibCabFciFileRead(FileHandle, Buffer, ByteCount, NULL, NULL); +} + +/** + A callback invoked during FDICopy to write to file. Note that these + callbacks always refer to the "current file position". + + @param FileHandle The file to write to. + + @param Buffer Pointer to a block of memory containing data to write. + + @param ByteCount The number of bytes to write. + + @param Err Optionally points to an integer which could be populated with + extra error information. + + @param Context Optionally points to user context, which is currently unused. + + @return The number of bytes actually written or -1 on failure. + */ +DWORD DIAMONDAPI +YoriLibCabFciFileWrite( + __in DWORD_PTR FileHandle, + __in PVOID Buffer, + __in DWORD ByteCount, + __inout_opt PINT Err, + __inout_opt PVOID Context + ) +{ + DWORD BytesWritten; + UNREFERENCED_PARAMETER(Err); + UNREFERENCED_PARAMETER(Context); + if (!WriteFile((HANDLE)FileHandle, + Buffer, + ByteCount, + &BytesWritten, + NULL)) { + return (DWORD)-1; + } + + return BytesWritten; +} + +/** + A callback invoked during FDICopy to write to file. Note that these + callbacks always refer to the "current file position". + + @param FileHandle The file to write to. + + @param Buffer Pointer to a block of memory containing data to write. + + @param ByteCount The number of bytes to write. + + @return The number of bytes actually written or -1 on failure. + */ +DWORD DIAMONDAPI +YoriLibCabFdiFileWrite( + __in DWORD_PTR FileHandle, + __in PVOID Buffer, + __in DWORD ByteCount + ) +{ + return YoriLibCabFciFileWrite(FileHandle, Buffer, ByteCount, NULL, NULL); +} + +/** + A callback invoked during FDICopy to close a file. + + @param FileHandle The file to close. + + @param Err Optionally points to an integer which could be populated with + extra error information. + + @param Context Optionally points to user context, which is currently unused. + + @return Zero for success, nonzero to indicate an error. + */ +INT DIAMONDAPI +YoriLibCabFciFileClose( + __in DWORD_PTR FileHandle, + __inout_opt PINT Err, + __inout_opt PVOID Context + ) +{ + UNREFERENCED_PARAMETER(Err); + UNREFERENCED_PARAMETER(Context); + + CloseHandle((HANDLE)FileHandle); + return 0; +} + +/** + A callback invoked during FDICopy to close a file. + + @param FileHandle The file to close. + + @return Zero for success, nonzero to indicate an error. + */ +INT DIAMONDAPI +YoriLibCabFdiFileClose( + __in DWORD_PTR FileHandle + ) +{ + return YoriLibCabFciFileClose(FileHandle, NULL, NULL); +} + +/** + A callback invoked during FDICopy to change current file position. + + @param FileHandle The file to set current position on. + + @param DistanceToMove The number of bytes to move. + + @param SeekType The origin of the move. + + @param Err Optionally points to an integer which could be populated with + extra error information. + + @param Context Optionally points to user context, which is currently unused. + + @return The new file position. + */ +DWORD DIAMONDAPI +YoriLibCabFciFileSeek( + __in DWORD_PTR FileHandle, + __in DWORD DistanceToMove, + __in INT SeekType, + __inout_opt PINT Err, + __inout_opt PVOID Context + ) +{ + DWORD NewPosition; + + UNREFERENCED_PARAMETER(Err); + UNREFERENCED_PARAMETER(Context); + + NewPosition = SetFilePointer((HANDLE)FileHandle, + DistanceToMove, + NULL, + SeekType); + + return NewPosition; +} + +/** + A callback invoked during FDICopy to change current file position. + + @param FileHandle The file to set current position on. + + @param DistanceToMove The number of bytes to move. + + @param SeekType The origin of the move. + + @return The new file position. + */ +DWORD DIAMONDAPI +YoriLibCabFdiFileSeek( + __in DWORD_PTR FileHandle, + __in DWORD DistanceToMove, + __in INT SeekType + ) +{ + return YoriLibCabFciFileSeek(FileHandle, DistanceToMove, SeekType, NULL, NULL); +} + +/** + A callback to delete a file when creating a CAB. This is used for temporary + files. + + @param FileName An ANSI NULL terminated string indicating the file to delete. + + @param Err Optionally points to an integer which could be populated with + extra error information. + + @param Context Optionally points to user context, which is currently unused. + + @return Zero to indicate success. This is speculation since I haven't found + this documented, but it appears to work. + */ +DWORD DIAMONDAPI +YoriLibCabFciFileDelete( + __in LPSTR FileName, + __inout_opt PINT Err, + __inout PVOID Context + ) +{ + PYORI_CAB_ADD_CONTEXT AddContext = (PYORI_CAB_ADD_CONTEXT)Context; + DWORD Encoding; + DWORD Error; + BOOL Result; + YORI_STRING WideFileName; + + UNREFERENCED_PARAMETER(Err); + + Encoding = CP_ACP; + if (AddContext->OnDiskNameIsUtf) { + Encoding = CP_UTF8; + } + + Error = YoriLibCabNarrowToWide(FileName, Encoding, &WideFileName); + if (Error != ERROR_SUCCESS) { + return 0; + } + + Result = DeleteFileW(WideFileName.StartOfString); + ASSERT(Result); + YoriLibFreeStringContents(&WideFileName); + return 0; +} + +/** + Return a temporary file name. Cabinet invokes this with a 256 byte buffer + (ie., less than MAX_PATH) so this routine constructs the temporary name on + the stack and copies back the result. + + @param FileName On successful completion, updated to contain a new temporary + file name. Note this is a NULL terminated ANSI string. + + @param FileNameLength Indicates the size of the FileName buffer. + + @param Context Optionally points to caller supplied context. This is + currently unused. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL DIAMONDAPI +YoriLibCabFciGetTempFile( + __in LPSTR FileName, + __in INT FileNameLength, + __inout PVOID Context + ) +{ + TCHAR TempPath[MAX_PATH]; + TCHAR LocalTempFile[MAX_PATH]; + LPSTR NarrowTempFile; + DWORD BytesToCopy = MAX_PATH; + PYORI_CAB_ADD_CONTEXT AddContext = (PYORI_CAB_ADD_CONTEXT)Context; + DWORD Error; + DWORD Encoding; + YORI_STRING WideTempFile; + + Encoding = CP_ACP; + if (AddContext->OnDiskNameIsUtf) { + Encoding = CP_UTF8; + } + + if (GetTempPath(sizeof(TempPath)/sizeof(TempPath[0]), TempPath) == 0) { + return FALSE; + } + if (GetTempFileName(TempPath, _T("YRI"), 0, LocalTempFile) == 0) { + return FALSE; + } + + YoriLibConstantString(&WideTempFile, LocalTempFile); + + Error = YoriLibCabWideToNarrow(&WideTempFile, Encoding, &NarrowTempFile); + if (Error != ERROR_SUCCESS) { + return FALSE; + } + + BytesToCopy = strlen(NarrowTempFile) + 1; + + if (FileNameLength < (INT)BytesToCopy) { + BytesToCopy = FileNameLength - 1; + } + memcpy(FileName, NarrowTempFile, BytesToCopy); + YoriLibFree(NarrowTempFile); + FileName[FileNameLength - 1] = '\0'; + return TRUE; +} + +/** + Called when creating a CAB to request a new CAB file and information about it. + Because this package is trying to only generate single CAB files (not chains) + this routine should never be invoked and isn't really handled. + + @param CabContext Pointer to the previous cab information. This could be + updated as needed to describe the next one. + + @param SizeOfLastCab The size of the last CAB file, in bytes. + + @param Context Optionally points to caller supplied context. This is + currently unused. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL DIAMONDAPI +YoriLibCabFciGetNextCabinet( + __in PCAB_FCI_CONTEXT CabContext, + __in DWORD SizeOfLastCab, + __inout_opt PVOID Context + ) +{ + UNREFERENCED_PARAMETER(CabContext); + UNREFERENCED_PARAMETER(SizeOfLastCab); + UNREFERENCED_PARAMETER(Context); + + ASSERT(FALSE); + + return TRUE; +} + +/** + A callback function that is called to indicate progress through compressing + files into a CAB. + + @param StatusType The type of notification. When this is 2, it's requesting + the final size of the CAB file to create. + + @param Value1 Type-specific data. + + @param Value2 Type-specific data. When StatusType is 2, this contains the + current size of the CAB file. + + @param Context Pointer to user supplied context, currently unused. + + @return Typically zero for success, or the size of the CAB file to create + when StatusType is 2. + */ +DWORD DIAMONDAPI +YoriLibCabFciStatus( + __in DWORD StatusType, + __in DWORD Value1, + __in DWORD Value2, + __inout_opt PVOID Context + ) +{ + UNREFERENCED_PARAMETER(Value1); + UNREFERENCED_PARAMETER(Context); + + if (StatusType == 2) { + return Value2; + } + + return 0; +} + +/** + Open a file for read for the purpose of adding it to a CAB, and return + metadata about the file being opened. + + @param FileName An ANSI NULL-terminated string describing the file to open. + + @param Date On successful completion, updated to point to the MS-DOS 16 bit + date stamp for the file. + + @param Time On successful completion, updated to point to the MS-DOS 16 bit + time stamp for the file. + + @param Attributes On successful completion, updated to point to the MS-DOS + 16 bit file attributes for the file. + + @param Err Updated to contain extra error information on failure. + + @param Context Pointer to user context, currently unused. + + @return A file handle. + */ +__success(return != INVALID_HANDLE_VALUE) +DWORD_PTR DIAMONDAPI +YoriLibCabFciGetOpenInfo( + __in LPSTR FileName, + __out PWORD Date, + __out PWORD Time, + __out PWORD Attributes, + __inout_opt PINT Err, + __inout PVOID Context + ) +{ + DWORD_PTR Handle; + BY_HANDLE_FILE_INFORMATION FileInfo; + PYORI_CAB_ADD_CONTEXT AddContext; + WORD NewAttributes; + DWORD Encoding; + + UNREFERENCED_PARAMETER(Err); + + AddContext = (PYORI_CAB_ADD_CONTEXT)Context; + Encoding = CP_ACP; + if (AddContext->OnDiskNameIsUtf) { + Encoding = CP_UTF8; + } + + Handle = YoriLibCabFileOpen(FileName, Encoding, YORI_LIB_CAB_OPEN_READONLY, 0); + if (Handle == (DWORD_PTR)INVALID_HANDLE_VALUE) { + return Handle; + } + + GetFileInformationByHandle((HANDLE)Handle, &FileInfo); + NewAttributes = (WORD)(FileInfo.dwFileAttributes & 0x3F); + if (AddContext->InCabNameIsUtf) { + NewAttributes = (WORD)(NewAttributes | YORI_CAB_NAME_IS_UTF); + } + *Attributes = NewAttributes; + YoriLibFileTimeToDosDateTime(&FileInfo.ftLastWriteTime, Date, Time); + + return Handle; +} + + +/** + A callback invoked during FDICopy to indicate events and state encountered + while processing the CAB file. + + @param NotifyType The type of the notification. + + @param Notification Pointer to a block of memory defining additional state + associated with the notification. The meaning of the fields in + this structure are NotificationType specific. The original FDI.H + header is the best documentation for the notifications and expected + transformations that can occur in this routine (don't bother with + MSDN.) + + @return Type specific result. Typically zero means success, and -1 means + error, although this is not always true. + */ +DWORD_PTR DIAMONDAPI +YoriLibCabNotify( + __in CAB_CB_FDI_NOTIFYTYPE NotifyType, + __in PCAB_CB_FDI_NOTIFICATION Notification + ) +{ + FILETIME TimeToSet; + LARGE_INTEGER liTemp; + TIME_ZONE_INFORMATION Tzi; + PYORI_LIB_CAB_EXPAND_CONTEXT ExpandContext; + YORI_STRING FullPath; + YORI_STRING FileName; + DWORD_PTR Handle; + DWORD Encoding; + + switch(NotifyType) { + case YoriLibCabNotifyCopyFile: + ExpandContext = (PYORI_LIB_CAB_EXPAND_CONTEXT)Notification->Context; + + Encoding = CP_ACP; + if (Notification->HalfAttributes & YORI_CAB_NAME_IS_UTF) { + Encoding = CP_UTF8; + } + + if (!YoriLibCabBuildFileNames(ExpandContext->TargetDirectory, Notification->String1, Encoding, &FullPath, &FileName)) { + ExpandContext->ErrorCode = ERROR_NOT_ENOUGH_MEMORY; + if (ExpandContext->ErrorString != NULL) { + YoriLibYPrintf(ExpandContext->ErrorString, _T("Could not build file name for directory %y CAB name %hs"), ExpandContext->TargetDirectory, Notification->String1); + } + return (DWORD_PTR)INVALID_HANDLE_VALUE; + } + if (YoriLibCabShouldIncludeFile(&FileName, ExpandContext)) { + if (ExpandContext->CommenceExtractCallback == NULL || + ExpandContext->CommenceExtractCallback(&FullPath, &FileName, ExpandContext->UserContext)) { + + Handle = YoriLibCabFileOpenForExtract(&FullPath, &ExpandContext->ErrorCode, ExpandContext->ErrorString); + } else { + Handle = 0; + } + } else { + Handle = 0; + } + YoriLibFreeStringContents(&FullPath); + YoriLibFreeStringContents(&FileName); + return Handle; + case YoriLibCabNotifyCloseFile: + if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_INVALID) { + Tzi.Bias = 0; + } + + // + // Convert the DOS time into a local time zone relative NT time + // + + YoriLibDosDateTimeToFileTime(Notification->TinyDate, Notification->TinyTime, &TimeToSet); + + // + // Apply the time zone bias adjustment to the NT time + // + + liTemp.LowPart = TimeToSet.dwLowDateTime; + liTemp.HighPart = TimeToSet.dwHighDateTime; + liTemp.QuadPart = liTemp.QuadPart + ((DWORDLONG)Tzi.Bias) * 10 * 1000 * 1000 * 60; + TimeToSet.dwLowDateTime = liTemp.LowPart; + TimeToSet.dwHighDateTime = liTemp.HighPart; + + // + // Set the time on the file + // + + SetFileTime((HANDLE)Notification->FileHandle, &TimeToSet, &TimeToSet, &TimeToSet); + YoriLibCabFdiFileClose(Notification->FileHandle); + + Encoding = CP_ACP; + if (Notification->HalfAttributes & YORI_CAB_NAME_IS_UTF) { + Encoding = CP_UTF8; + } + + ExpandContext = (PYORI_LIB_CAB_EXPAND_CONTEXT)Notification->Context; + if (YoriLibCabBuildFileNames(ExpandContext->TargetDirectory, Notification->String1, Encoding, &FullPath, &FileName)) { + SetFileAttributes(FullPath.StartOfString, Notification->HalfAttributes); + + if (ExpandContext->CompleteExtractCallback != NULL) { + ExpandContext->CompleteExtractCallback(&FullPath, &FileName, ExpandContext->UserContext); + } + YoriLibFreeStringContents(&FullPath); + YoriLibFreeStringContents(&FileName); + } + return 1; + case YoriLibCabNotifyNextCabinet: + if (Notification->FdiError != 0) { + return (DWORD_PTR)-1; + } + } + return 0; +} + +/** + Extract a cabinet file into a specified directory. + + @param CabFileName Pointer to the file name of the Cabinet to extract. + + @param TargetDirectory Pointer to the name of the directory to extract + into. + + @param IncludeAllByDefault If TRUE, files not listed in the below arrays + are expanded. If FALSE, only files explicitly listed are expanded. + + @param NumberFilesToInclude The number of files in the FilesToInclude array. + + @param FilesToInclude An array of strings corresponding to files that should + be expanded. + + @param NumberFilesToExclude The number of files in the FilesToExclude array. + + @param FilesToExclude An array of strings corresponding to files that should + not be expanded. + + @param CommenceExtractCallback Optionally points to a a function to invoke + for each file processed as part of extracting the CAB. This function + is invoked before extract and gives the user a chance to skip + particular files. + + @param CompleteExtractCallback Optionally points to a a function to invoke + for each file processed as part of extracting the CAB. This function + is invoked after extract and gives the user a chance to make extra + changes to files. + + @param UserContext Optionally points to context to pass to + CommenceExtractCallback and CompleteExtractCallback. + + @param ErrorCode Optionally points to a value to populate with the error code + encountered in the extraction process. + + @param ErrorString Optionally points to a string to populate with information + about any error encountered in the extraction process. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibExtractCab( + __in PYORI_STRING CabFileName, + __in PYORI_STRING TargetDirectory, + __in BOOL IncludeAllByDefault, + __in DWORD NumberFilesToExclude, + __in_opt PYORI_STRING FilesToExclude, + __in DWORD NumberFilesToInclude, + __in_opt PYORI_STRING FilesToInclude, + __in_opt PYORI_LIB_CAB_EXPAND_FILE_CALLBACK CommenceExtractCallback, + __in_opt PYORI_LIB_CAB_EXPAND_FILE_CALLBACK CompleteExtractCallback, + __in_opt PVOID UserContext, + __inout_opt PDWORD ErrorCode, + __inout_opt PYORI_STRING ErrorString + ) +{ + YORI_STRING FullCabFileName; + YORI_STRING CabParentDirectory; + YORI_STRING CabFileNameOnly; + YORI_STRING FullTargetDirectory; + LPTSTR FinalBackslash; + LPVOID hFdi; + CAB_CB_ERROR CabErrors; + LPSTR AnsiCabFileName; + LPSTR AnsiCabParentDirectory; + BOOL Result = FALSE; + YORI_LIB_CAB_EXPAND_CONTEXT ExpandContext; + DWORD Encoding; + DWORD Error; + + YoriLibLoadCabinetFunctions(); + if (DllCabinet.pFdiCreate == NULL || + DllCabinet.pFdiCopy == NULL) { + + if (ErrorCode != NULL) { + *ErrorCode = ERROR_MOD_NOT_FOUND; + } + if (ErrorString != NULL) { + YoriLibYPrintf(ErrorString, _T("Cabinet.dll not loaded or expected functions not found")); + } + return FALSE; + } + + YoriLibInitEmptyString(&FullCabFileName); + YoriLibInitEmptyString(&FullTargetDirectory); + AnsiCabParentDirectory = NULL; + AnsiCabFileName = NULL; + hFdi = NULL; + ZeroMemory(&ExpandContext, sizeof(ExpandContext)); + ExpandContext.DefaultInclude = IncludeAllByDefault; + ExpandContext.NumberFilesToInclude = NumberFilesToInclude; + ExpandContext.NumberFilesToExclude = NumberFilesToExclude; + ExpandContext.FilesToInclude = FilesToInclude; + ExpandContext.FilesToExclude = FilesToExclude; + ExpandContext.CommenceExtractCallback = CommenceExtractCallback; + ExpandContext.CompleteExtractCallback = CompleteExtractCallback; + ExpandContext.UserContext = UserContext; + ExpandContext.ErrorCode = ERROR_SUCCESS; + ExpandContext.ErrorString = ErrorString; + + if (!YoriLibUserStringToSingleFilePath(CabFileName, FALSE, &FullCabFileName)) { + if (ErrorCode != NULL) { + *ErrorCode = GetLastError(); + } + if (ErrorString != NULL) { + YoriLibYPrintf(ErrorString, _T("Cannot convert %y to full path"), CabFileName); + } + return FALSE; + } + + if (!YoriLibUserStringToSingleFilePath(TargetDirectory, FALSE, &FullTargetDirectory)) { + if (ErrorCode != NULL) { + *ErrorCode = GetLastError(); + } + if (ErrorString != NULL) { + YoriLibYPrintf(ErrorString, _T("Cannot convert %y to full path"), TargetDirectory); + } + return FALSE; + } + + // + // A full path should have a backslash somewhere + // + + FinalBackslash = YoriLibFindRightMostCharacter(&FullCabFileName, '\\'); + if (FinalBackslash == NULL) { + if (ErrorCode != NULL) { + *ErrorCode = ERROR_BAD_PATHNAME; + } + if (ErrorString != NULL) { + YoriLibYPrintf(ErrorString, _T("Cannot find final seperator in %y"), &FullCabFileName); + } + goto Exit; + } + + YoriLibInitEmptyString(&CabParentDirectory); + YoriLibInitEmptyString(&CabFileNameOnly); + + Encoding = CP_ACP; + if (YoriLibIsUtf8Supported()) { + Encoding = CP_UTF8; + } + + CabParentDirectory.StartOfString = FullCabFileName.StartOfString; + CabParentDirectory.LengthInChars = (YORI_ALLOC_SIZE_T)(FinalBackslash - FullCabFileName.StartOfString + 1); + + CabFileNameOnly.StartOfString = &FullCabFileName.StartOfString[CabParentDirectory.LengthInChars]; + CabFileNameOnly.LengthInChars = (YORI_ALLOC_SIZE_T)(FullCabFileName.LengthInChars - CabParentDirectory.LengthInChars); + + Error = YoriLibCabWideToNarrow(&CabParentDirectory, Encoding, &AnsiCabParentDirectory); + + if (Error != ERROR_SUCCESS) { + *ErrorCode = Error; + if (ErrorString != NULL) { + YoriLibYPrintf(ErrorString, _T("Error converting %y to ANSI"), &CabParentDirectory); + } + goto Exit; + } + + Error = YoriLibCabWideToNarrow(&CabFileNameOnly, Encoding, &AnsiCabFileName); + if (Error != ERROR_SUCCESS) { + *ErrorCode = Error; + if (ErrorString != NULL) { + YoriLibYPrintf(ErrorString, _T("Error converting %y to ANSI"), &CabFileNameOnly); + } + goto Exit; + } + + hFdi = DllCabinet.pFdiCreate(YoriLibCabAlloc, + YoriLibCabFree, + YoriLibCabFdiFileOpen, + YoriLibCabFdiFileRead, + YoriLibCabFdiFileWrite, + YoriLibCabFdiFileClose, + YoriLibCabFdiFileSeek, + -1, + &CabErrors); + + if (hFdi == NULL) { + if (ErrorCode != NULL && *ErrorCode == ERROR_SUCCESS) { + *ErrorCode = GetLastError(); + } + if (ErrorString != NULL && ErrorString->LengthInChars == 0) { + YoriLibYPrintf(ErrorString, _T("Error %i in pFdiCreate"), GetLastError()); + } + goto Exit; + } + + ExpandContext.TargetDirectory = &FullTargetDirectory; + + if (!DllCabinet.pFdiCopy(hFdi, + AnsiCabFileName, + AnsiCabParentDirectory, + 0, + YoriLibCabNotify, + NULL, + &ExpandContext)) { + if (ErrorCode != NULL && *ErrorCode == ERROR_SUCCESS) { + *ErrorCode = GetLastError(); + } + if (ErrorString != NULL && ErrorString->LengthInChars == 0) { + YoriLibYPrintf(ErrorString, _T("Error %i in pFdiCopy"), GetLastError()); + } + goto Exit; + } + + Result = TRUE; +Exit: + + if (DllCabinet.pFdiDestroy != NULL && hFdi != NULL) { + DllCabinet.pFdiDestroy(hFdi); + } + + YoriLibFreeStringContents(&FullCabFileName); + YoriLibFreeStringContents(&FullTargetDirectory); + if (AnsiCabParentDirectory != NULL) { + YoriLibFree(AnsiCabParentDirectory); + } + if (AnsiCabFileName != NULL) { + YoriLibFree(AnsiCabFileName); + } + return Result; +} + +/** + A structure owned by this module for each CAB file being created. This is + the nonopaque form of a handle returned from @ref YoriLibCreateCab . + */ +typedef struct _YORI_CAB_HANDLE { + + /** + A description of the CAB file being constructed. + */ + CAB_FCI_CONTEXT CompressContext; + + /** + Memory allocated to retrieve errors that occur during compression. + */ + CAB_CB_ERROR Err; + + /** + A handle returned from FCICreate that is used by the cabinet API for + future operations on the CAB. + */ + PVOID FciHandle; + + /** + Context passed to FciCreate which is passed to other functions. For + this purpose, GET_OPEN_INFO needs to know which encoding to apply to + the file name. + */ + YORI_CAB_ADD_CONTEXT AddContext; + +} YORI_CAB_HANDLE, *PYORI_CAB_HANDLE; + +/** + Create a new CAB file. Files can be added to it with + @ref YoriLibAddFileToCab . + + @param CabFileName The file name of the CAB to create on disk. Note that + due to limitations of the Cabinet API, this must be capable of being + converted to ANSI losslessly. + + @param Handle On successful completion, this is updated to contain an opaque + handle that can be used in @ref YoriLibAddFileToCab or + @ref YoriLibCloseCab . + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibCreateCab( + __in PYORI_STRING CabFileName, + __out PVOID * Handle + ) +{ + PYORI_CAB_HANDLE CabHandle; + DWORD Encoding; + BOOL DefaultUsed = FALSE; + PBOOL DefaultPtr; + int CharsCopied; + + YoriLibLoadCabinetFunctions(); + if (DllCabinet.pFciCreate == NULL || + DllCabinet.pFciAddFile == NULL) { + + return FALSE; + } + + CabHandle = YoriLibReferencedMalloc(sizeof(YORI_CAB_HANDLE)); + if (CabHandle == NULL) { + return FALSE; + } + + ZeroMemory(CabHandle, sizeof(YORI_CAB_HANDLE)); + + // + // We don't want to split data across multiple CABs. This feature + // was for floppy disks. Today, set the maximum size to as large + // as is possible. + // + + CabHandle->CompressContext.SizeAvailable = 0x7FFFF000; + CabHandle->CompressContext.ThresholdForNextFolder = 0x7FFFF000; + + CabHandle->AddContext.OnDiskNameIsUtf = FALSE; + Encoding = CP_ACP; + if (YoriLibIsUtf8Supported()) { + CabHandle->AddContext.OnDiskNameIsUtf = TRUE; + Encoding = CP_UTF8; + } + + // + // WideCharToMultiByte fails if DefaultUsed is specified with an encoding + // that doesn't support default characters, like UTF8 + // + + DefaultPtr = &DefaultUsed; + if (Encoding == CP_UTF8) { + DefaultPtr = NULL; + } + + CharsCopied = WideCharToMultiByte(Encoding, + 0, + CabFileName->StartOfString, + CabFileName->LengthInChars, + CabHandle->CompressContext.CabPath, + sizeof(CabHandle->CompressContext.CabPath), + NULL, + DefaultPtr); + + if (CharsCopied <= 0 || (DWORD)CharsCopied >= sizeof(CabHandle->CompressContext.CabPath)) { + YoriLibDereference(CabHandle); + return FALSE; + } + + if (DefaultUsed) { + YoriLibDereference(CabHandle); + return FALSE; + } + + CabHandle->FciHandle = DllCabinet.pFciCreate(&CabHandle->Err, + YoriLibCabFciFilePlaced, + YoriLibCabAlloc, + YoriLibCabFree, + YoriLibCabFciFileOpen, + YoriLibCabFciFileRead, + YoriLibCabFciFileWrite, + YoriLibCabFciFileClose, + YoriLibCabFciFileSeek, + YoriLibCabFciFileDelete, + YoriLibCabFciGetTempFile, + &CabHandle->CompressContext, + &CabHandle->AddContext); + + if (CabHandle->FciHandle == NULL) { + YoriLibDereference(CabHandle); + return FALSE; + } + + *Handle = CabHandle; + return TRUE; +} + +/** + Add a file to a CAB file. + + @param Handle The opaque handle returned from @ref YoriLibCreateCab. + + @param FileNameOnDisk The path to the file on disk. Note that due to + limitations of the Cabinet API, this must be capable of being + converted to ANSI losslessly. + + @param FileNameInCab The path to record the file as within the CAB. + Note that due to limitations of the Cabinet API, this must be + capable of being converted to ANSI losslessly. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibAddFileToCab( + __in PVOID Handle, + __in PYORI_STRING FileNameOnDisk, + __in PYORI_STRING FileNameInCab + ) +{ + PYORI_CAB_HANDLE CabHandle = (PYORI_CAB_HANDLE)Handle; + + LPSTR FileNameOnDiskAnsi; + LPSTR FileNameInCabAnsi; + BOOL Result; + DWORD Encoding; + DWORD Index; + DWORD Error; + + CabHandle->AddContext.InCabNameIsUtf = FALSE; + + Encoding = CP_ACP; + if (CabHandle->AddContext.OnDiskNameIsUtf) { + Encoding = CP_UTF8; + } + + Error = YoriLibCabWideToNarrow(FileNameOnDisk, Encoding, &FileNameOnDiskAnsi); + if (Error != ERROR_SUCCESS) { + return FALSE; + } + + Encoding = CP_ACP; + if (CabHandle->AddContext.OnDiskNameIsUtf) { + for (Index = 0; Index < FileNameInCab->LengthInChars; Index++) { + if (FileNameInCab->StartOfString[Index] >= 128) { + Encoding = CP_UTF8; + CabHandle->AddContext.InCabNameIsUtf = TRUE; + break; + } + } + } + + Error = YoriLibCabWideToNarrow(FileNameInCab, Encoding, &FileNameInCabAnsi); + if (Error != ERROR_SUCCESS) { + YoriLibFree(FileNameOnDiskAnsi); + return FALSE; + } + + Result = DllCabinet.pFciAddFile(CabHandle->FciHandle, + FileNameOnDiskAnsi, + FileNameInCabAnsi, + FALSE, + YoriLibCabFciGetNextCabinet, + YoriLibCabFciStatus, + YoriLibCabFciGetOpenInfo, + CAB_FCI_ALGORITHM_MSZIP); + + YoriLibFree(FileNameOnDiskAnsi); + YoriLibFree(FileNameInCabAnsi); + return Result; +} + +/** + Complete the creation of a new CAB file. + + @param Handle The opaque handle returned from @ref YoriLibCreateCab. + */ +VOID +YoriLibCloseCab( + __in PVOID Handle + ) +{ + PYORI_CAB_HANDLE CabHandle = (PYORI_CAB_HANDLE)Handle; + + if (DllCabinet.pFciFlushFolder) { + DllCabinet.pFciFlushFolder(CabHandle->FciHandle, YoriLibCabFciGetNextCabinet, YoriLibCabFciStatus); + } + if (DllCabinet.pFciFlushCabinet) { + DllCabinet.pFciFlushCabinet(CabHandle->FciHandle, FALSE, YoriLibCabFciGetNextCabinet, YoriLibCabFciStatus); + } + if (DllCabinet.pFciDestroy) { + DllCabinet.pFciDestroy(CabHandle->FciHandle); + } + + YoriLibDereference(CabHandle); +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/clip.c b/misc/tools/yori/yori/lib/clip.c index 09bdd3ba78..7043476fc9 100644 --- a/misc/tools/yori/yori/lib/clip.c +++ b/misc/tools/yori/yori/lib/clip.c @@ -275,7 +275,7 @@ YoriLibPasteText( StringLength = (YORI_ALLOC_SIZE_T)_tcslen(pMem); if (StringLength >= Buffer->LengthAllocated) { - if (!YoriLibReallocateStringWithoutPreservingContents(Buffer, (YORI_ALLOC_SIZE_T)(StringLength + 1))) { + if (!YoriLibReallocStringNoContents(Buffer, (YORI_ALLOC_SIZE_T)(StringLength + 1))) { DllKernel32.pGlobalUnlock(hMem); DllUser32.pCloseClipboard(); return FALSE; @@ -794,7 +794,7 @@ YoriLibCopyTextWithProcessFallback( } if (Buffer->LengthInChars > YoriLibProcessClipboard.LengthAllocated) { - if (!YoriLibReallocateStringWithoutPreservingContents(&YoriLibProcessClipboard, Buffer->LengthInChars)) { + if (!YoriLibReallocStringNoContents(&YoriLibProcessClipboard, Buffer->LengthInChars)) { return FALSE; } } @@ -826,7 +826,7 @@ YoriLibPasteTextWithProcessFallback( } if (YoriLibProcessClipboard.LengthInChars + 1 > Buffer->LengthAllocated) { - if (!YoriLibReallocateStringWithoutPreservingContents(Buffer, (YORI_ALLOC_SIZE_T)(YoriLibProcessClipboard.LengthInChars + 1))) { + if (!YoriLibReallocStringNoContents(Buffer, (YORI_ALLOC_SIZE_T)(YoriLibProcessClipboard.LengthInChars + 1))) { return FALSE; } } diff --git a/misc/tools/yori/yori/lib/cmdline.c b/misc/tools/yori/yori/lib/cmdline.c index 5c59359d7c..628cfce739 100644 --- a/misc/tools/yori/yori/lib/cmdline.c +++ b/misc/tools/yori/yori/lib/cmdline.c @@ -363,7 +363,7 @@ YoriLibExpandCommandVariables( break; } else { ExpandedString->LengthInChars = DestIndex; - if (!YoriLibReallocateString(ExpandedString, ExpandedString->LengthAllocated * 4)) { + if (!YoriLibReallocString(ExpandedString, ExpandedString->LengthAllocated * 4)) { YoriLibFreeStringContents(ExpandedString); return FALSE; } @@ -378,7 +378,7 @@ YoriLibExpandCommandVariables( if (DestIndex + 1 >= ExpandedString->LengthAllocated) { ExpandedString->LengthInChars = DestIndex; - if (!YoriLibReallocateString(ExpandedString, ExpandedString->LengthAllocated * 4)) { + if (!YoriLibReallocString(ExpandedString, ExpandedString->LengthAllocated * 4)) { YoriLibFreeStringContents(ExpandedString); return FALSE; } diff --git a/misc/tools/yori/yori/lib/color.c b/misc/tools/yori/yori/lib/color.c index 893ac8319b..56fa49124d 100644 --- a/misc/tools/yori/yori/lib/color.c +++ b/misc/tools/yori/yori/lib/color.c @@ -127,14 +127,14 @@ YoriLibAttributeFromString( // Look for the next element // - SingleElement.LengthInChars = YoriLibCountStringNotContainingChars(&SingleElement, _T("+")); + SingleElement.LengthInChars = YoriLibCntStringNotWithChars(&SingleElement, _T("+")); // // Walk through the string table for a match. // for (Element = 0; Element < sizeof(YoriLibColorStringTable)/sizeof(YoriLibColorStringTable[0]); Element++) { - if (YoriLibCompareStringWithLiteralInsensitive(&SingleElement, YoriLibColorStringTable[Element].String) == 0) { + if (YoriLibCompareStringLitIns(&SingleElement, YoriLibColorStringTable[Element].String) == 0) { if (Background) { if (YoriLibColorStringTable[Element].Attr.Ctrl != 0) { @@ -602,7 +602,7 @@ YoriLibGetMetadataColor( FoundColorString.LengthInChars = (YORI_ALLOC_SIZE_T)(Element.LengthInChars - FoundAttributeCodeString.LengthInChars - 1); - if (YoriLibCompareStringInsensitive(RequestedAttributeCodeString, &FoundAttributeCodeString) == 0) { + if (YoriLibCompareStringIns(RequestedAttributeCodeString, &FoundAttributeCodeString) == 0) { YoriLibAttributeFromString(&FoundColorString, &FoundColor); WindowColor.Ctrl = 0; WindowColor.Win32Attr = (UCHAR)YoriLibVtGetDefaultColor(); diff --git a/misc/tools/yori/yori/lib/cpuinfo.c b/misc/tools/yori/yori/lib/cpuinfo.c new file mode 100644 index 0000000000..9fc378dd6d --- /dev/null +++ b/misc/tools/yori/yori/lib/cpuinfo.c @@ -0,0 +1,181 @@ +/** + * @file lib/cpuinfo.c + * + * Yori CPU query routines + * + * Copyright (c) 2019-2022 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + + +/** + Query the system to find the number of high performance and high efficiency + logical processors. This is used when determining how many child tasks to + execute. On older systems (or processors!) this just returns the total + number of processors as performance processors. + + @param PerformanceLogicalProcessors On successful completion, updated to + indicate the number of high performance processors. + + @param EfficiencyLogicalProcessors On successful completion, updated to + indicate the number of high efficiency processors. + */ +VOID +YoriLibQueryCpuCount( + __out PWORD PerformanceLogicalProcessors, + __out PWORD EfficiencyLogicalProcessors + ) +{ + YORI_SYSTEM_INFO SysInfo; + + // + // If this function doesn't exist, the system is incapable of performing + // different scheduling on different types of processor, so just return + // the total number of processors (at the end of this function.) + // + + if (DllKernel32.pGetLogicalProcessorInformationEx != NULL) { + DWORD Err; + DWORD BytesInBuffer = 0; + DWORD CurrentOffset = 0; + DWORD GroupIndex; + WORD LocalEfficiencyProcessorCount; + WORD LocalPerformanceProcessorCount; + WORD LogicalProcessorCount; + WORD LogicalProcessorIndex; + DWORD_PTR LogicalProcessorMask; + PYORI_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ProcInfo = NULL; + PYORI_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Entry; + PYORI_PROCESSOR_GROUP_AFFINITY Group; + + // + // Query processor information from the system. This needs to allocate + // memory as needed to populate, so loop while the buffer is too small + // in order to allocate the correct amount. + // + + Err = ERROR_SUCCESS; + while(TRUE) { + if (DllKernel32.pGetLogicalProcessorInformationEx(YoriProcessorRelationAll, ProcInfo, &BytesInBuffer)) { + Err = ERROR_SUCCESS; + break; + } + + Err = GetLastError(); + if (Err == ERROR_INSUFFICIENT_BUFFER) { + if (ProcInfo != NULL) { + YoriLibFree(ProcInfo); + ProcInfo = NULL; + } + + if (!YoriLibIsSizeAllocatable(BytesInBuffer)) { + Err = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + ProcInfo = YoriLibMalloc((YORI_ALLOC_SIZE_T)BytesInBuffer); + if (ProcInfo == NULL) { + Err = ERROR_NOT_ENOUGH_MEMORY; + break; + } + } else { + break; + } + } + + if (Err == ERROR_SUCCESS) { + LocalEfficiencyProcessorCount = 0; + LocalPerformanceProcessorCount = 0; + Entry = ProcInfo; + while (Entry != NULL) { + if (Entry->Relationship == YoriProcessorRelationProcessorCore) { + + // + // Count how many logical processors are implemented by + // this core. + // + + LogicalProcessorCount = 0; + for (GroupIndex = 0; GroupIndex < Entry->u.Processor.GroupCount; GroupIndex++) { + Group = &Entry->u.Processor.GroupMask[GroupIndex]; + for (LogicalProcessorIndex = 0; LogicalProcessorIndex < 8 * sizeof(DWORD_PTR); LogicalProcessorIndex++) { + LogicalProcessorMask = 1; + LogicalProcessorMask = LogicalProcessorMask<Mask & LogicalProcessorMask) { + LogicalProcessorCount++; + } + } + } + if (Entry->u.Processor.EfficiencyClass == 0) { + LocalEfficiencyProcessorCount = (WORD)(LocalEfficiencyProcessorCount + LogicalProcessorCount); + } else { + LocalPerformanceProcessorCount = (WORD)(LocalPerformanceProcessorCount + LogicalProcessorCount); + } + } + CurrentOffset += Entry->SizeInBytes; + if (CurrentOffset >= BytesInBuffer) { + break; + } + Entry = YoriLibAddToPointer(ProcInfo, CurrentOffset); + } + + if (ProcInfo != NULL) { + YoriLibFree(ProcInfo); + } + + // + // On homogenous systems, all cores will report efficiency class + // zero, which is the most efficient class. For human + // compatibility, report these as performance cores instead. + // + + if (LocalPerformanceProcessorCount == 0) { + LocalPerformanceProcessorCount = LocalEfficiencyProcessorCount; + LocalEfficiencyProcessorCount = 0; + } + + // + // If this counted any processors, return them. Otherwise fall + // back to the legacy approach of not distinguishing processor + // classes. + // + + if (LocalPerformanceProcessorCount != 0) { + *PerformanceLogicalProcessors = LocalPerformanceProcessorCount; + *EfficiencyLogicalProcessors = LocalEfficiencyProcessorCount; + return; + } + } + } + + // + // Ultimate fallback: just return the processor count. Note + // GetSystemInfo cannot fail. + // + + GetSystemInfo((LPSYSTEM_INFO)&SysInfo); + *PerformanceLogicalProcessors = (WORD)SysInfo.dwNumberOfProcessors; + *EfficiencyLogicalProcessors = 0; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/cshot.c b/misc/tools/yori/yori/lib/cshot.c index a5d6bc7efc..320c8fa39d 100644 --- a/misc/tools/yori/yori/lib/cshot.c +++ b/misc/tools/yori/yori/lib/cshot.c @@ -125,7 +125,7 @@ YoriLibRewriteConsoleContents( } } - YoriLibVtSetConsoleTextAttributeOnDevice(hTarget, 0, 0, ReadBuffer[0].Attributes); + YoriLibVtSetConsoleTextAttrDev(hTarget, 0, 0, ReadBuffer[0].Attributes); LastAttribute = ReadBuffer[0].Attributes; for (ReadBufferOffset.Y = 0; ReadBufferOffset.Y < ReadBufferSize.Y; ReadBufferOffset.Y++) { @@ -137,7 +137,7 @@ YoriLibRewriteConsoleContents( CellIndex = ReadBufferOffset.Y * ReadBufferSize.X + ReadBufferOffset.X; if (ReadBuffer[CellIndex].Attributes != LastAttribute) { - YoriLibVtSetConsoleTextAttributeOnDevice(hTarget, 0, 0, ReadBuffer[CellIndex].Attributes); + YoriLibVtSetConsoleTextAttrDev(hTarget, 0, 0, ReadBuffer[CellIndex].Attributes); LastAttribute = ReadBuffer[CellIndex].Attributes; } YoriLibOutputToDevice(hTarget, 0, _T("%c"), ReadBuffer[CellIndex].Char.UnicodeChar); @@ -179,7 +179,7 @@ YoriLibGenerateVtStringFromConsoleBuffers( SHORT LineIndex; SHORT CharIndex; WORD LastAttribute; - TCHAR EscapeStringBuffer[YORI_MAX_INTERNAL_VT_ESCAPE_CHARS]; + TCHAR EscapeStringBuffer[YORI_MAX_VT_ESCAPE_CHARS]; YORI_STRING EscapeString; // diff --git a/misc/tools/yori/yori/lib/curdir.c b/misc/tools/yori/yori/lib/curdir.c index 403e0ac0bc..bdb47c02c1 100644 --- a/misc/tools/yori/yori/lib/curdir.c +++ b/misc/tools/yori/yori/lib/curdir.c @@ -98,9 +98,9 @@ YoriLibGetOnDiskCaseForPath( } FindHandle = FindFirstFile(NewPath.StartOfString, &FindData); if (FindHandle != INVALID_HANDLE_VALUE) { - if (YoriLibCompareStringWithLiteralInsensitive(&SearchComponent, FindData.cFileName) == 0) { + if (YoriLibCompareStringLitIns(&SearchComponent, FindData.cFileName) == 0) { memcpy(SearchComponent.StartOfString, FindData.cFileName, SearchComponent.LengthInChars * sizeof(TCHAR)); - } else if (YoriLibCompareStringWithLiteralInsensitive(&SearchComponent, FindData.cAlternateFileName) == 0) { + } else if (YoriLibCompareStringLitIns(&SearchComponent, FindData.cAlternateFileName) == 0) { memcpy(SearchComponent.StartOfString, FindData.cAlternateFileName, SearchComponent.LengthInChars * sizeof(TCHAR)); } FindClose(FindHandle); diff --git a/misc/tools/yori/yori/lib/cvtcons.c b/misc/tools/yori/yori/lib/cvtcons.c index 69423b2d01..39f1b64b03 100644 --- a/misc/tools/yori/yori/lib/cvtcons.c +++ b/misc/tools/yori/yori/lib/cvtcons.c @@ -283,7 +283,7 @@ YoriLibConsCnvProcessAndOutputEscape( UNREFERENCED_PARAMETER(hOutput); - YoriLibVtFinalColorFromSequence(CvtContext->CurrentAttributes, String, &CvtContext->CurrentAttributes); + YoriLibVtFinalColorFromEsc(CvtContext->CurrentAttributes, String, &CvtContext->CurrentAttributes); return TRUE; } diff --git a/misc/tools/yori/yori/lib/cvthtml.c b/misc/tools/yori/yori/lib/cvthtml.c index 678002da2c..0c7d429260 100644 --- a/misc/tools/yori/yori/lib/cvthtml.c +++ b/misc/tools/yori/yori/lib/cvthtml.c @@ -332,7 +332,7 @@ YoriLibHtmlGenerateTextString( AddToDestOffset = 0; SearchString.StartOfString = SrcPoint; SearchString.LengthInChars = SrcString->LengthInChars - SrcConsumed; - SrcOffset = YoriLibCountStringNotContainingChars(&SearchString, LookFor); + SrcOffset = YoriLibCntStringNotWithChars(&SearchString, LookFor); if (SrcOffset > 0) { if (DestOffset + SrcOffset < TextString->LengthAllocated) { memcpy(&TextString->StartOfString[DestOffset], SrcPoint, SrcOffset * sizeof(TCHAR)); @@ -531,7 +531,7 @@ YoriLibHtmlGenerateEscapeStringInternal( YoriLibInitEmptyString(&SearchString); SearchString.StartOfString = SrcPoint; SearchString.LengthInChars = RemainingLength; - SrcOffset = YoriLibCountStringContainingChars(&SearchString, _T("0123456789")); + SrcOffset = YoriLibCntStringWithChars(&SearchString, _T("0123456789")); SrcPoint += SrcOffset; RemainingLength = RemainingLength - SrcOffset; @@ -726,7 +726,7 @@ YoriLibHtmlCvtAppendWithReallocate( if (LengthNeeded > StringToAppendTo->LengthAllocated) { YORI_ALLOC_SIZE_T AllocSize; AllocSize = YoriLibMaximumAllocationInRange(LengthNeeded, LengthNeeded * 4); - if (!YoriLibReallocateString(StringToAppendTo, AllocSize)) { + if (!YoriLibReallocString(StringToAppendTo, AllocSize)) { return FALSE; } } @@ -973,10 +973,10 @@ YoriLibHtmlConvertToHtmlFromVt( return FALSE; } - if (!YoriLibProcessVtEscapesOnOpenStream(VtText->StartOfString, - VtText->LengthInChars, - (HANDLE)&HtmlContext, - &CallbackFunctions)) { + if (!YoriLibProcVtEscOnOpenStream(VtText->StartOfString, + VtText->LengthInChars, + (HANDLE)&HtmlContext, + &CallbackFunctions)) { if (FreeColorTable) { YoriLibDereference(HtmlContext.ColorTable); diff --git a/misc/tools/yori/yori/lib/cvtrtf.c b/misc/tools/yori/yori/lib/cvtrtf.c index 5b8b0cedae..f77e2ddcbf 100644 --- a/misc/tools/yori/yori/lib/cvtrtf.c +++ b/misc/tools/yori/yori/lib/cvtrtf.c @@ -466,7 +466,7 @@ YoriLibRtfGenerateEscapeString( YoriLibInitEmptyString(&SearchString); SearchString.StartOfString = SrcPoint; SearchString.LengthInChars = RemainingLength; - SrcOffset = YoriLibCountStringContainingChars(&SearchString, _T("0123456789")); + SrcOffset = YoriLibCntStringWithChars(&SearchString, _T("0123456789")); SrcPoint += SrcOffset; RemainingLength = RemainingLength - SrcOffset; @@ -577,7 +577,7 @@ YoriLibRtfCvtAppendWithReallocate( if (LengthNeeded > StringToAppendTo->LengthAllocated) { YORI_ALLOC_SIZE_T AllocSize; AllocSize = YoriLibMaximumAllocationInRange(LengthNeeded, LengthNeeded * 4); - if (!YoriLibReallocateString(StringToAppendTo, AllocSize)) { + if (!YoriLibReallocString(StringToAppendTo, AllocSize)) { return FALSE; } } @@ -817,10 +817,10 @@ YoriLibRtfConvertToRtfFromVt( return FALSE; } - if (!YoriLibProcessVtEscapesOnOpenStream(VtText->StartOfString, - VtText->LengthInChars, - (HANDLE)&RtfContext, - &CallbackFunctions)) { + if (!YoriLibProcVtEscOnOpenStream(VtText->StartOfString, + VtText->LengthInChars, + (HANDLE)&RtfContext, + &CallbackFunctions)) { if (FreeColorTable) { YoriLibDereference(RtfContext.ColorTable); diff --git a/misc/tools/yori/yori/lib/dblclk.c b/misc/tools/yori/yori/lib/dblclk.c index 9c2bce73fe..315a7e8aee 100644 --- a/misc/tools/yori/yori/lib/dblclk.c +++ b/misc/tools/yori/yori/lib/dblclk.c @@ -47,7 +47,7 @@ YoriLibGetSelectionDoubleClickBreakChars( YORI_STRING Substring; YoriLibInitEmptyString(BreakChars); - if (!YoriLibAllocateAndGetEnvironmentVariable(_T("YORIQUICKEDITBREAKCHARS"), BreakChars) || BreakChars->LengthInChars == 0) { + if (!YoriLibAllocateAndGetEnvVar(_T("YORIQUICKEDITBREAKCHARS"), BreakChars) || BreakChars->LengthInChars == 0) { // // 0x2500 is Unicode full horizontal line (used by sdir) @@ -112,7 +112,7 @@ YoriLibIsYoriQuickEditEnabled(VOID) YORI_ALLOC_SIZE_T CharsConsumed; YoriLibInitEmptyString(&EnvVar); - if (!YoriLibAllocateAndGetEnvironmentVariable(_T("YORIQUICKEDIT"), &EnvVar)) { + if (!YoriLibAllocateAndGetEnvVar(_T("YORIQUICKEDIT"), &EnvVar)) { return FALSE; } diff --git a/misc/tools/yori/yori/lib/dyld.c b/misc/tools/yori/yori/lib/dyld.c index 42828aa2a8..e210e91770 100644 --- a/misc/tools/yori/yori/lib/dyld.c +++ b/misc/tools/yori/yori/lib/dyld.c @@ -581,6 +581,35 @@ YoriLibLoadShfolderFunctions(VOID) return TRUE; } +/** + A structure containing pointers to userenv.dll functions that can be used if + they are found but programs do not have a hard dependency on. + */ +YORI_USERENV_FUNCTIONS DllUserEnv; + +/** + Load pointers to all optional userenv.dll functions. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL +YoriLibLoadUserEnvFunctions(VOID) +{ + + if (DllUserEnv.hDll != NULL) { + return TRUE; + } + + DllUserEnv.hDll = YoriLibLoadLibraryFromSystemDirectory(_T("USERENV.DLL")); + if (DllUserEnv.hDll == NULL) { + return FALSE; + } + + DllUserEnv.pCreateEnvironmentBlock = (PCREATE_ENVIRONMENT_BLOCK)GetProcAddress(DllUserEnv.hDll, "CreateEnvironmentBlock"); + DllUserEnv.pDestroyEnvironmentBlock = (PDESTROY_ENVIRONMENT_BLOCK)GetProcAddress(DllUserEnv.hDll, "DestroyEnvironmentBlock"); + return TRUE; +} + /** A structure containing pointers to version.dll functions that can be used if they are found but programs do not have a hard dependency on. diff --git a/misc/tools/yori/yori/lib/dyld_adv.c b/misc/tools/yori/yori/lib/dyld_adv.c index 15e97e9ef8..de17917e61 100644 --- a/misc/tools/yori/yori/lib/dyld_adv.c +++ b/misc/tools/yori/yori/lib/dyld_adv.c @@ -58,7 +58,6 @@ CONST YORI_DLL_NAME_MAP DllAdvApi32Symbols[] = { {(FARPROC *)&DllAdvApi32.pAddAccessAllowedAce, "AddAccessAllowedAce"}, {(FARPROC *)&DllAdvApi32.pAdjustTokenPrivileges, "AdjustTokenPrivileges"}, {(FARPROC *)&DllAdvApi32.pAllocateAndInitializeSid, "AllocateAndInitializeSid"}, - {(FARPROC *)&DllAdvApi32.pCheckTokenMembership, "CheckTokenMembership"}, {(FARPROC *)&DllAdvApi32.pCommandLineFromMsiDescriptor, "CommandLineFromMsiDescriptor"}, {(FARPROC *)&DllAdvApi32.pCryptAcquireContextW, "CryptAcquireContextW"}, {(FARPROC *)&DllAdvApi32.pCryptCreateHash, "CryptCreateHash"}, @@ -66,10 +65,12 @@ CONST YORI_DLL_NAME_MAP DllAdvApi32Symbols[] = { {(FARPROC *)&DllAdvApi32.pCryptGetHashParam, "CryptGetHashParam"}, {(FARPROC *)&DllAdvApi32.pCryptHashData, "CryptHashData"}, {(FARPROC *)&DllAdvApi32.pCryptReleaseContext, "CryptReleaseContext"}, + {(FARPROC *)&DllAdvApi32.pEqualSid, "EqualSid"}, {(FARPROC *)&DllAdvApi32.pFreeSid, "FreeSid"}, {(FARPROC *)&DllAdvApi32.pGetFileSecurityW, "GetFileSecurityW"}, {(FARPROC *)&DllAdvApi32.pGetLengthSid, "GetLengthSid"}, {(FARPROC *)&DllAdvApi32.pGetSecurityDescriptorOwner, "GetSecurityDescriptorOwner"}, + {(FARPROC *)&DllAdvApi32.pGetTokenInformation, "GetTokenInformation"}, {(FARPROC *)&DllAdvApi32.pImpersonateSelf, "ImpersonateSelf"}, {(FARPROC *)&DllAdvApi32.pInitializeAcl, "InitializeAcl"}, {(FARPROC *)&DllAdvApi32.pInitializeSecurityDescriptor, "InitializeSecurityDescriptor"}, diff --git a/misc/tools/yori/yori/lib/env.c b/misc/tools/yori/yori/lib/env.c index 93fa5ed9de..1891944405 100644 --- a/misc/tools/yori/yori/lib/env.c +++ b/misc/tools/yori/yori/lib/env.c @@ -47,6 +47,8 @@ YoriLibGetEnvironmentStrings( LPSTR OsEnvStringsA; YORI_ALLOC_SIZE_T CharCount; +#ifdef UNICODE + // // Use GetEnvironmentStringsW where it exists. Where it doesn't // exist (NT 3.1) we need to upconvert to Unicode. @@ -79,6 +81,9 @@ YoriLibGetEnvironmentStrings( } OsEnvStringsA = DllKernel32.pGetEnvironmentStrings(); +#else + OsEnvStringsA = GetEnvironmentStrings(); +#endif for (CharCount = 0; OsEnvStringsA[CharCount] != '\0' || OsEnvStringsA[CharCount + 1] != '\0'; CharCount++); @@ -86,7 +91,11 @@ YoriLibGetEnvironmentStrings( if (!YoriLibAllocateString(EnvStrings, CharCount)) { return FALSE; } +#ifdef UNICODE MultiByteToWideChar(CP_ACP, 0, OsEnvStringsA, CharCount, EnvStrings->StartOfString, CharCount); +#else + memcpy(EnvStrings->StartOfString, OsEnvStrings, CharCount * sizeof(TCHAR)); +#endif EnvStrings->LengthInChars = CharCount; return TRUE; @@ -105,7 +114,7 @@ YoriLibGetEnvironmentStrings( */ __success(return) BOOL -YoriLibAreEnvironmentStringsValid( +YoriLibAreEnvStringsValid( __inout PYORI_STRING EnvStrings ) { @@ -128,6 +137,8 @@ YoriLibAreEnvironmentStringsValid( return FALSE; } +#ifdef UNICODE + /** Checks if ANSI environment strings are double NULL terminated within the bounds of the allocation. If so, allocates a new Yori string to describe @@ -179,6 +190,8 @@ YoriLibAreAnsiEnvironmentStringsValid( return FALSE; } +#endif + /** Capture the value from an environment variable, allocating a Yori string of appropriate size to contain the contents. @@ -192,7 +205,7 @@ YoriLibAreAnsiEnvironmentStringsValid( */ __success(return) BOOL -YoriLibAllocateAndGetEnvironmentVariable( +YoriLibAllocateAndGetEnvVar( __in LPCTSTR Name, __inout PYORI_STRING Value ) @@ -206,7 +219,7 @@ YoriLibAllocateAndGetEnvironmentVariable( } if (LengthNeeded > Value->LengthAllocated) { - if (!YoriLibReallocateString(Value, LengthNeeded)) { + if (!YoriLibReallocString(Value, LengthNeeded)) { return FALSE; } } @@ -235,7 +248,7 @@ YoriLibAllocateAndGetEnvironmentVariable( */ __success(return) BOOL -YoriLibGetEnvironmentVariableAsNumber( +YoriLibGetEnvVarAsNumber( __in LPCTSTR Name, __out PYORI_MAX_SIGNED_T Value ) @@ -299,7 +312,7 @@ YoriLibGetEnvironmentVariableAsNumber( */ __success(return) BOOL -YoriLibAddEnvironmentComponentToString( +YoriLibAddEnvCompToString( __inout PYORI_STRING ExistingString, __in PCYORI_STRING NewComponent, __in BOOL InsertAtFront @@ -317,7 +330,7 @@ YoriLibAddEnvironmentComponentToString( ThisPath = _tcstok_s(ExistingString->StartOfString, _T(";"), &TokCtx); while (ThisPath != NULL) { if (ThisPath[0] != '\0') { - if (YoriLibCompareStringWithLiteralInsensitive(NewComponent, ThisPath) == 0) { + if (YoriLibCompareStringLitIns(NewComponent, ThisPath) == 0) { if (TokCtx != NULL) { TokCtx--; ASSERT(*TokCtx == '\0'); @@ -403,7 +416,7 @@ YoriLibAddEnvironmentComponentToString( */ __success(return) BOOL -YoriLibAddEnvironmentComponentReturnString( +YoriLibAddEnvCompReturnString( __in PYORI_STRING EnvironmentVariable, __in PYORI_STRING NewComponent, __in BOOL InsertAtFront, @@ -427,7 +440,7 @@ YoriLibAddEnvironmentComponentReturnString( Result->StartOfString[0] = '\0'; Result->LengthInChars = (YORI_ALLOC_SIZE_T)GetEnvironmentVariable(EnvironmentVariable->StartOfString, Result->StartOfString, Result->LengthAllocated); - YoriLibAddEnvironmentComponentToString(Result, NewComponent, InsertAtFront); + YoriLibAddEnvCompToString(Result, NewComponent, InsertAtFront); return TRUE; } @@ -447,7 +460,7 @@ YoriLibAddEnvironmentComponentReturnString( */ __success(return) BOOL -YoriLibAddEnvironmentComponent( +YoriLibAddEnvComponent( __in LPTSTR EnvironmentVariable, __in PYORI_STRING NewComponent, __in BOOL InsertAtFront @@ -458,7 +471,7 @@ YoriLibAddEnvironmentComponent( YoriLibConstantString(&YsEnvironmentVariable, EnvironmentVariable); - if (!YoriLibAddEnvironmentComponentReturnString(&YsEnvironmentVariable, NewComponent, InsertAtFront, &EnvData)) { + if (!YoriLibAddEnvCompReturnString(&YsEnvironmentVariable, NewComponent, InsertAtFront, &EnvData)) { return FALSE; } @@ -486,7 +499,7 @@ YoriLibAddEnvironmentComponent( */ __success(return) BOOL -YoriLibRemoveEnvironmentComponentFromString( +YoriLibRmEnvCompFromString( __in PYORI_STRING String, __in PYORI_STRING ComponentToRemove, __out PYORI_STRING Result @@ -510,7 +523,7 @@ YoriLibRemoveEnvironmentComponentFromString( ThisPath = _tcstok_s(String->StartOfString, _T(";"), &TokCtx); while (ThisPath != NULL) { if (ThisPath[0] != '\0') { - if (YoriLibCompareStringWithLiteralInsensitive(ComponentToRemove, ThisPath) != 0) { + if (YoriLibCompareStringLitIns(ComponentToRemove, ThisPath) != 0) { if (NewOffset != 0) { CharsPopulated = YoriLibSPrintf(&NewPathData[NewOffset], _T(";%s"), ThisPath); } else { @@ -571,7 +584,7 @@ YoriLibRemoveEnvironmentComponentFromString( */ __success(return) BOOL -YoriLibRemoveEnvironmentComponentReturnString( +YoriLibRmEnvCompReturnString( __in PYORI_STRING EnvironmentVariable, __in PYORI_STRING ComponentToRemove, __out PYORI_STRING Result @@ -596,7 +609,7 @@ YoriLibRemoveEnvironmentComponentReturnString( PathData.StartOfString[0] = '\0'; PathData.LengthInChars = (YORI_ALLOC_SIZE_T)GetEnvironmentVariable(EnvironmentVariable->StartOfString, PathData.StartOfString, PathData.LengthAllocated); - Success = YoriLibRemoveEnvironmentComponentFromString(&PathData, ComponentToRemove, Result); + Success = YoriLibRmEnvCompFromString(&PathData, ComponentToRemove, Result); YoriLibFreeStringContents(&PathData); @@ -615,7 +628,7 @@ YoriLibRemoveEnvironmentComponentReturnString( */ __success(return) BOOL -YoriLibRemoveEnvironmentComponent( +YoriLibRemoveEnvComponent( __in LPTSTR EnvironmentVariable, __in PYORI_STRING ComponentToRemove ) @@ -626,7 +639,7 @@ YoriLibRemoveEnvironmentComponent( YoriLibConstantString(&YsEnvironmentVariable, EnvironmentVariable); - if (!YoriLibRemoveEnvironmentComponentReturnString(&YsEnvironmentVariable, ComponentToRemove, &CombinedString)) { + if (!YoriLibRmEnvCompReturnString(&YsEnvironmentVariable, ComponentToRemove, &CombinedString)) { return FALSE; } diff --git a/misc/tools/yori/yori/lib/fileenum.c b/misc/tools/yori/yori/lib/fileenum.c index 51fb85c534..1ab90d2b81 100644 --- a/misc/tools/yori/yori/lib/fileenum.c +++ b/misc/tools/yori/yori/lib/fileenum.c @@ -210,7 +210,7 @@ YoriLibForEachFileEnum( // if (ForEachContext->EffectiveFileSpec.LengthInChars >= sizeof("file:///")) { - if (YoriLibCompareStringWithLiteralInsensitiveCount(&ForEachContext->EffectiveFileSpec, _T("file:///"), sizeof("file:///") - 1) == 0) { + if (YoriLibCompareStringLitInsCnt(&ForEachContext->EffectiveFileSpec, _T("file:///"), sizeof("file:///") - 1) == 0) { ForEachContext->EffectiveFileSpec.StartOfString = &ForEachContext->EffectiveFileSpec.StartOfString[sizeof("file:///") - 1]; ForEachContext->EffectiveFileSpec.LengthInChars -= sizeof("file:///") - 1; } @@ -698,7 +698,7 @@ YoriLibForEachFile( } SingleCharMode = FALSE; - CharsToOperator = YoriLibCountStringNotContainingChars(FileSpec, _T("{[")); + CharsToOperator = YoriLibCntStringNotWithChars(FileSpec, _T("{[")); // // If there are no [ or { operators, expand any ~ operators and @@ -731,7 +731,7 @@ YoriLibForEachFile( SubstituteValues.StartOfString = &FileSpec->StartOfString[CharsToOperator + 1]; SubstituteValues.LengthInChars = FileSpec->LengthInChars - CharsToOperator - 1; - CharsToOperator = YoriLibCountStringNotContainingChars(&SubstituteValues, SingleCharMode?_T("]"):_T("}")); + CharsToOperator = YoriLibCntStringNotWithChars(&SubstituteValues, SingleCharMode?_T("]"):_T("}")); if (CharsToOperator == SubstituteValues.LengthInChars) { return YoriLibForEachFileEnum(FileSpec, MatchFlags, Depth, Callback, ErrorCallback, Context); } @@ -769,7 +769,7 @@ YoriLibForEachFile( } else { while(TRUE) { MatchValue = SubstituteValues; - CharsToOperator = YoriLibCountStringNotContainingChars(&SubstituteValues, _T(",")); + CharsToOperator = YoriLibCntStringNotWithChars(&SubstituteValues, _T(",")); MatchValue.LengthInChars = CharsToOperator; diff --git a/misc/tools/yori/yori/lib/filefilt.c b/misc/tools/yori/yori/lib/filefilt.c index 1e16a21baf..f691d12e50 100644 --- a/misc/tools/yori/yori/lib/filefilt.c +++ b/misc/tools/yori/yori/lib/filefilt.c @@ -302,41 +302,41 @@ YoriLibFileFiltParseFilterOperator( // YoriLibInitEmptyString(ErrorSubstring); - if (YoriLibCompareStringWithLiteral(Operator, _T(">")) == 0) { + if (YoriLibCompareStringLit(Operator, _T(">")) == 0) { Criteria->CompareFn = MatchedOption->CompareFn; Criteria->TruthStates[YORI_LIB_LESS_THAN] = FALSE; Criteria->TruthStates[YORI_LIB_GREATER_THAN] = TRUE; Criteria->TruthStates[YORI_LIB_EQUAL] = FALSE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T(">=")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T(">=")) == 0) { Criteria->CompareFn = MatchedOption->CompareFn; Criteria->TruthStates[YORI_LIB_LESS_THAN] = FALSE; Criteria->TruthStates[YORI_LIB_GREATER_THAN] = TRUE; Criteria->TruthStates[YORI_LIB_EQUAL] = TRUE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T("<")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T("<")) == 0) { Criteria->CompareFn = MatchedOption->CompareFn; Criteria->TruthStates[YORI_LIB_LESS_THAN] = TRUE; Criteria->TruthStates[YORI_LIB_GREATER_THAN] = FALSE; Criteria->TruthStates[YORI_LIB_EQUAL] = FALSE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T("<=")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T("<=")) == 0) { Criteria->CompareFn = MatchedOption->CompareFn; Criteria->TruthStates[YORI_LIB_LESS_THAN] = TRUE; Criteria->TruthStates[YORI_LIB_GREATER_THAN] = FALSE; Criteria->TruthStates[YORI_LIB_EQUAL] = TRUE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T("!=")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T("!=")) == 0) { Criteria->CompareFn = MatchedOption->CompareFn; Criteria->TruthStates[YORI_LIB_LESS_THAN] = TRUE; Criteria->TruthStates[YORI_LIB_GREATER_THAN] = TRUE; Criteria->TruthStates[YORI_LIB_EQUAL] = FALSE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T("=")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T("=")) == 0) { Criteria->CompareFn = MatchedOption->CompareFn; Criteria->TruthStates[YORI_LIB_LESS_THAN] = FALSE; Criteria->TruthStates[YORI_LIB_GREATER_THAN] = FALSE; Criteria->TruthStates[YORI_LIB_EQUAL] = TRUE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T("&")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T("&")) == 0) { Criteria->CompareFn = MatchedOption->BitwiseCompareFn; Criteria->TruthStates[YORI_LIB_EQUAL] = TRUE; Criteria->TruthStates[YORI_LIB_NOT_EQUAL] = FALSE; - } else if (YoriLibCompareStringWithLiteral(Operator, _T("!&")) == 0) { + } else if (YoriLibCompareStringLit(Operator, _T("!&")) == 0) { Criteria->CompareFn = MatchedOption->BitwiseCompareFn; Criteria->TruthStates[YORI_LIB_EQUAL] = FALSE; Criteria->TruthStates[YORI_LIB_NOT_EQUAL] = TRUE; @@ -416,11 +416,11 @@ YoriLibFileFiltParseFilterOptAndOperator( YoriLibTrimSpaces(&SwitchName); - SwitchName.LengthInChars = YoriLibCountStringNotContainingChars(&SwitchName, _T("&<>=!")); + SwitchName.LengthInChars = YoriLibCntStringNotWithChars(&SwitchName, _T("&<>=!")); FoundOpt = NULL; for (Count = 0; Count < sizeof(YoriLibFileFiltFilterOptions)/sizeof(YoriLibFileFiltFilterOptions[0]); Count++) { - if (YoriLibCompareStringWithLiteralInsensitive(&SwitchName, YoriLibFileFiltFilterOptions[Count].Switch) == 0) { + if (YoriLibCompareStringLitIns(&SwitchName, YoriLibFileFiltFilterOptions[Count].Switch) == 0) { FoundOpt = &YoriLibFileFiltFilterOptions[Count]; break; } @@ -439,7 +439,7 @@ YoriLibFileFiltParseFilterOptAndOperator( Operator->StartOfString = SwitchName.StartOfString + SwitchName.LengthInChars; Operator->LengthInChars = FilterElement->LengthInChars - SwitchName.LengthInChars - (YORI_ALLOC_SIZE_T)(SwitchName.StartOfString - FilterElement->StartOfString); - Operator->LengthInChars = YoriLibCountStringContainingChars(Operator, _T("&<>=!")); + Operator->LengthInChars = YoriLibCntStringWithChars(Operator, _T("&<>=!")); return TRUE; } @@ -531,7 +531,7 @@ YoriLibFileFiltParseColorElement( CharsRemaining = FilterElement->LengthInChars - Operator.LengthInChars - (YORI_ALLOC_SIZE_T)(Operator.StartOfString - FilterElement->StartOfString); Value.LengthInChars = CharsRemaining; - CharsToCompare = YoriLibCountStringNotContainingChars(&Value, _T(",")); + CharsToCompare = YoriLibCntStringNotWithChars(&Value, _T(",")); if (Value.LengthInChars == CharsToCompare) { YoriLibInitEmptyString(ErrorSubstring); ErrorSubstring->StartOfString = FilterElement->StartOfString; diff --git a/misc/tools/yori/yori/lib/fileinfo.c b/misc/tools/yori/yori/lib/fileinfo.c index 81697bc5ba..441a90d70d 100644 --- a/misc/tools/yori/yori/lib/fileinfo.c +++ b/misc/tools/yori/yori/lib/fileinfo.c @@ -2881,15 +2881,15 @@ YoriLibGenerateArch( __in PYORI_STRING String ) { - if (YoriLibCompareStringWithLiteralInsensitive(String, _T("None")) == 0) { + if (YoriLibCompareStringLitIns(String, _T("None")) == 0) { Entry->Architecture = 0; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("i386")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("i386")) == 0) { Entry->Architecture = IMAGE_FILE_MACHINE_I386; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("amd64")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("amd64")) == 0) { Entry->Architecture = IMAGE_FILE_MACHINE_AMD64; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("arm")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("arm")) == 0) { Entry->Architecture = IMAGE_FILE_MACHINE_ARMNT; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("arm64")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("arm64")) == 0) { Entry->Architecture = IMAGE_FILE_MACHINE_ARM64; } else { return FALSE; @@ -2914,9 +2914,9 @@ YoriLibGenerateCaseSensitivity( __in PYORI_STRING String ) { - if (YoriLibCompareStringWithLiteralInsensitive(String, _T("ci")) == 0) { + if (YoriLibCompareStringLitIns(String, _T("ci")) == 0) { Entry->CaseSensitive = FALSE; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("cs")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("cs")) == 0) { Entry->CaseSensitive = TRUE; } else { return FALSE; @@ -2941,25 +2941,25 @@ YoriLibGenerateCompressionAlgorithm( __in PYORI_STRING String ) { - if (YoriLibCompareStringWithLiteralInsensitive(String, _T("None")) == 0) { + if (YoriLibCompareStringLitIns(String, _T("None")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionNone; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("LZNT")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("LZNT")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionLznt; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("NTFS")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("NTFS")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionNtfsUnknown; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("WIM")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("WIM")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionWim; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("LZX")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("LZX")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionLzx; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Xp4")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Xp4")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionXpress4k; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Xp8")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Xp8")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionXpress8k; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Xp16")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Xp16")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionXpress16k; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("File")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("File")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionWofFileUnknown; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Wof")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Wof")) == 0) { Entry->CompressionAlgorithm = YoriLibCompressionWofUnknown; } else { return FALSE; @@ -3512,35 +3512,35 @@ YoriLibGenerateSubsystem ( __in PYORI_STRING String ) { - if (YoriLibCompareStringWithLiteralInsensitive(String, _T("None")) == 0) { + if (YoriLibCompareStringLitIns(String, _T("None")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_UNKNOWN; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("NT")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("NT")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_NATIVE; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("GUI")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("GUI")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Cons")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Cons")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("OS/2")) == 0 || YoriLibCompareStringWithLiteralInsensitive(String, _T("OS2")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("OS/2")) == 0 || YoriLibCompareStringLitIns(String, _T("OS2")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_OS2_CUI; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Posx")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Posx")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_POSIX_CUI; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("w9x")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("w9x")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_NATIVE_WINDOWS; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("CE")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("CE")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CE_GUI; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("EFIa")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("EFIa")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_APPLICATION; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("EFIb")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("EFIb")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("EFId")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("EFId")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("EFIr")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("EFIr")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_EFI_ROM; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Xbox")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Xbox")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_XBOX; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Xbcc")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Xbcc")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG; - } else if (YoriLibCompareStringWithLiteralInsensitive(String, _T("Boot")) == 0) { + } else if (YoriLibCompareStringLitIns(String, _T("Boot")) == 0) { Entry->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION; } else { return FALSE; diff --git a/misc/tools/yori/yori/lib/fullpath.c b/misc/tools/yori/yori/lib/fullpath.c index c7f4012790..43bd061e5c 100644 --- a/misc/tools/yori/yori/lib/fullpath.c +++ b/misc/tools/yori/yori/lib/fullpath.c @@ -1482,549 +1482,6 @@ YoriLibGetFullPathNameRelativeTo( return TRUE; } - -/** - Convert a specified shell folder, by a known folder GUID, into its string - form. This function is only available in Vista+. - - @param FolderType The identifier of the directory. - - @param ExpandedSymbol On successful completion, populated with a string - identifying the path to the directory. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibExpandShellDirectoryGuid( - __in CONST GUID * FolderType, - __inout PYORI_STRING ExpandedSymbol - ) -{ - PWSTR ExpandedString; - YORI_ALLOC_SIZE_T LocationLength; - - YoriLibLoadShell32Functions(); - YoriLibLoadOle32Functions(); - - if (DllShell32.pSHGetKnownFolderPath == NULL || - DllOle32.pCoTaskMemFree == NULL) { - return FALSE; - } - - - if (DllShell32.pSHGetKnownFolderPath(FolderType, 0, NULL, &ExpandedString) != 0) { - return FALSE; - } - - LocationLength = (YORI_ALLOC_SIZE_T)_tcslen(ExpandedString); - - if (!YoriLibAllocateString(ExpandedSymbol, LocationLength + 1)) { - DllOle32.pCoTaskMemFree(ExpandedString); - return FALSE; - } - - memcpy(ExpandedSymbol->StartOfString, ExpandedString, (LocationLength + 1) * sizeof(TCHAR)); - ExpandedSymbol->LengthInChars = LocationLength; - - DllOle32.pCoTaskMemFree(ExpandedString); - return TRUE; -} - - -/** - Convert a specified shell folder, by identifier, into its string form. - - @param FolderType The identifier of the directory. - - @param ExpandedSymbol On successful completion, populated with a string - identifying the path to the directory. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibExpandShellDirectory( - __in INT FolderType, - __inout PYORI_STRING ExpandedSymbol - ) -{ - YoriLibLoadShell32Functions(); - if (DllShell32.pSHGetSpecialFolderPathW == NULL) { - YoriLibLoadShfolderFunctions(); - if (DllShfolder.pSHGetFolderPathW == NULL) { - return FALSE; - } - } - - if (!YoriLibAllocateString(ExpandedSymbol, MAX_PATH)) { - return FALSE; - } - - ExpandedSymbol->StartOfString[0] = '\0'; - - if (DllShell32.pSHGetSpecialFolderPathW != NULL) { - if (DllShell32.pSHGetSpecialFolderPathW(NULL, ExpandedSymbol->StartOfString, FolderType, FALSE) < 0) { - YoriLibFreeStringContents(ExpandedSymbol); - return FALSE; - } - } else { - if (!SUCCEEDED(DllShfolder.pSHGetFolderPathW(NULL, FolderType, NULL, 0, ExpandedSymbol->StartOfString))) { - YoriLibFreeStringContents(ExpandedSymbol); - return FALSE; - } - } - - ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)_tcslen(ExpandedSymbol->StartOfString); - - return TRUE; -} - -/** - A mapping between a '~' prefixed special directory name and a CSIDL that the - shell uses to identify it. - */ -typedef struct _YORI_LIB_CSIDL_MAP { - - /** - The special directory name. - */ - LPTSTR DirName; - - /** - The corresponding CSIDL. - */ - DWORD Csidl; -} YORI_LIB_CSIDL_MAP, *PYORI_LIB_CSIDL_MAP; - -/** - A table of special directory names whose locations can be obtained via - SHGetSpecialFolderPath or SHGetFolderPath. - */ -CONST YORI_LIB_CSIDL_MAP YoriLibSpecialDirectoryMap[] = { - {_T("~APPDATA"), CSIDL_APPDATA}, - {_T("~APPDATALOCAL"), CSIDL_LOCALAPPDATA}, - {_T("~COMMONAPPDATA"), CSIDL_COMMON_APPDATA}, - {_T("~COMMONDESKTOP"), CSIDL_COMMON_DESKTOPDIRECTORY}, - {_T("~COMMONDOCUMENTS"), CSIDL_COMMON_DOCUMENTS}, - {_T("~COMMONPROGRAMS"), CSIDL_COMMON_PROGRAMS}, - {_T("~COMMONSTART"), CSIDL_COMMON_STARTMENU}, - {_T("~DESKTOP"), CSIDL_DESKTOPDIRECTORY}, - {_T("~DOCUMENTS"), CSIDL_PERSONAL}, - {_T("~LOCALAPPDATA"), CSIDL_LOCALAPPDATA}, - {_T("~PROGRAMFILES"), CSIDL_PROGRAM_FILES}, -#ifdef _WIN64 - {_T("~PROGRAMFILES32"), CSIDL_PROGRAM_FILESX86}, - {_T("~PROGRAMFILES64"), CSIDL_PROGRAM_FILES}, -#else - {_T("~PROGRAMFILES32"), CSIDL_PROGRAM_FILES}, -#endif - {_T("~PROGRAMS"), CSIDL_PROGRAMS}, - {_T("~START"), CSIDL_STARTMENU}, - {_T("~STARTUP"), CSIDL_STARTUP}, - {_T("~SYSTEM"), CSIDL_SYSTEM}, - {_T("~WINDOWS"), CSIDL_WINDOWS} -}; - -/** - Translate a special directory name into its expanded form if the directory - name is defined via a CSIDL that can be resolved with SHGetSpecialFolderPath - et al. - - @param SymbolToExpand The special directory name, prefixed with '~'. - - @param ExpandedSymbol On successful completion, populated with the directory - corresponding to the special directory name. - - @return TRUE to indicate a match was found and expansion successfully - performed, FALSE if the symbol name should be checked for other - matches. - */ -__success(return) -BOOL -YoriLibExpandDirectoryFromMap( - __in PYORI_STRING SymbolToExpand, - __inout PYORI_STRING ExpandedSymbol - ) -{ - DWORD Index; - - for (Index = 0; Index < sizeof(YoriLibSpecialDirectoryMap)/sizeof(YoriLibSpecialDirectoryMap[0]); Index++) { - if (YoriLibCompareStringWithLiteralInsensitive(SymbolToExpand, YoriLibSpecialDirectoryMap[Index].DirName) == 0) { - return YoriLibExpandShellDirectory(YoriLibSpecialDirectoryMap[Index].Csidl, ExpandedSymbol); - } - } - - return FALSE; -} - -/** - Expand a directory component in a path, specified via a tilde and description, - into its corresponding physical directory. - - @param SymbolToExpand The tilde based directory reference. - - @param ExpandedSymbol On successful completion, populated with the directory - corresponding to the reference. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibExpandHomeSymbol( - __in PYORI_STRING SymbolToExpand, - __inout PYORI_STRING ExpandedSymbol - ) -{ - if (YoriLibCompareStringWithLiteralInsensitive(SymbolToExpand, _T("~")) == 0) { - DWORD BytesNeeded; - BytesNeeded = GetEnvironmentVariable(_T("HOMEDRIVE"), NULL, 0) + GetEnvironmentVariable(_T("HOMEPATH"), NULL, 0); - if (!YoriLibIsSizeAllocatable(BytesNeeded)) { - return FALSE; - } - - if (!YoriLibAllocateString(ExpandedSymbol, (YORI_ALLOC_SIZE_T)BytesNeeded)) { - return FALSE; - } - - ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)GetEnvironmentVariable(_T("HOMEDRIVE"), ExpandedSymbol->StartOfString, ExpandedSymbol->LengthAllocated); - ExpandedSymbol->LengthInChars = ExpandedSymbol->LengthInChars + - (YORI_ALLOC_SIZE_T)GetEnvironmentVariable(_T("HOMEPATH"), - &ExpandedSymbol->StartOfString[ExpandedSymbol->LengthInChars], - ExpandedSymbol->LengthAllocated - ExpandedSymbol->LengthInChars); - return TRUE; - } else if (YoriLibCompareStringWithLiteralInsensitive(SymbolToExpand, _T("~APPDIR")) == 0) { - LPTSTR FinalSlash; - - // - // Unlike most other Win32 APIs, this one has no way to indicate - // how much space it needs. We can be wasteful here though, since - // it'll be freed immediately. - // - - if (!YoriLibAllocateString(ExpandedSymbol, 32768)) { - return FALSE; - } - - ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)GetModuleFileName(NULL, ExpandedSymbol->StartOfString, ExpandedSymbol->LengthAllocated); - FinalSlash = YoriLibFindRightMostCharacter(ExpandedSymbol, '\\'); - if (FinalSlash == NULL) { - YoriLibFreeStringContents(ExpandedSymbol); - return FALSE; - } - - ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)(FinalSlash - ExpandedSymbol->StartOfString); - return TRUE; - } else if (YoriLibExpandDirectoryFromMap(SymbolToExpand, ExpandedSymbol)) { - return TRUE; - } else if (YoriLibCompareStringWithLiteralInsensitive(SymbolToExpand, _T("~DOWNLOADS")) == 0) { - BOOL Result; - - // - // If a Vista era function to find the Downloads folder exists, - // use it. - // - - YoriLibLoadShell32Functions(); - if (DllShell32.pSHGetKnownFolderPath != NULL) { - return YoriLibExpandShellDirectoryGuid(&FOLDERID_Downloads, ExpandedSymbol); - } - - // - // If not, Downloads is a subdirectory of Documents. This function - // allocates a MAX_PATH buffer because the underlying API doesn't - // specify a length. - // - - Result = YoriLibExpandShellDirectory(CSIDL_PERSONAL, ExpandedSymbol); - if (Result) { - if (ExpandedSymbol->LengthInChars + sizeof("\\Downloads") < ExpandedSymbol->LengthAllocated) { - memcpy(&ExpandedSymbol->StartOfString[ExpandedSymbol->LengthInChars], - _T("\\Downloads"), - (sizeof("\\Downloads") - 1) * sizeof(TCHAR)); - ExpandedSymbol->LengthInChars = ExpandedSymbol->LengthInChars + sizeof("\\Downloads") - 1; - return TRUE; - } - } - } - - ExpandedSymbol->StartOfString = SymbolToExpand->StartOfString; - ExpandedSymbol->LengthInChars = SymbolToExpand->LengthInChars; - return TRUE; -} - -/** - Expand all tilde based home references in a file path and return the - expanded form. - - @param FileString A string specifying a path that may include tilde based - directory references. - - @param ExpandedString On successful completion, contains the corresponding - full path. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibExpandHomeDirectories( - __in PYORI_STRING FileString, - __out PYORI_STRING ExpandedString - ) -{ - YORI_ALLOC_SIZE_T CharIndex; - YORI_STRING BeforeSymbol; - YORI_STRING AfterSymbol; - YORI_STRING SymbolToExpand; - YORI_STRING ExpandedSymbol; - BOOL PreviousWasSeperator = TRUE; - - for (CharIndex = 0; CharIndex < FileString->LengthInChars; CharIndex++) { - if (FileString->StartOfString[CharIndex] == '~' && - (CharIndex == 0 || YoriLibIsSep(FileString->StartOfString[CharIndex - 1]))) { - - YoriLibInitEmptyString(&BeforeSymbol); - YoriLibInitEmptyString(&AfterSymbol); - YoriLibInitEmptyString(&SymbolToExpand); - - BeforeSymbol.StartOfString = FileString->StartOfString; - BeforeSymbol.LengthInChars = CharIndex; - - SymbolToExpand.StartOfString = &FileString->StartOfString[CharIndex]; - SymbolToExpand.LengthInChars = 0; - - while (CharIndex < FileString->LengthInChars && !YoriLibIsSep(FileString->StartOfString[CharIndex])) { - SymbolToExpand.LengthInChars++; - CharIndex++; - } - - AfterSymbol.StartOfString = &FileString->StartOfString[CharIndex]; - AfterSymbol.LengthInChars = FileString->LengthInChars - CharIndex; - - YoriLibInitEmptyString(&ExpandedSymbol); - - if (!YoriLibExpandHomeSymbol(&SymbolToExpand, &ExpandedSymbol)) { - return FALSE; - } - - YoriLibInitEmptyString(ExpandedString); - YoriLibYPrintf(ExpandedString, _T("%y%y%y"), &BeforeSymbol, &ExpandedSymbol, &AfterSymbol); - YoriLibFreeStringContents(&ExpandedSymbol); - if (ExpandedString->StartOfString != NULL) { - return TRUE; - } - return FALSE; - } - - if (YoriLibIsSep(FileString->StartOfString[CharIndex])) { - PreviousWasSeperator = TRUE; - } else { - PreviousWasSeperator = FALSE; - } - } - - return FALSE; -} - -/** - Return TRUE if the argument is a special DOS device name, FALSE if it is - a regular file. DOS device names include things like AUX, CON, PRN etc. - In Yori, a DOS device name is only a DOS device name if it does not - contain any path information. - - @param File The file name to check. - - @return TRUE to indicate this argument is a DOS device name, FALSE to - indicate that it is a regular file. - */ -BOOL -YoriLibIsFileNameDeviceName( - __in PCYORI_STRING File - ) -{ - YORI_STRING NameToCheck; - YORI_ALLOC_SIZE_T Offset; - BOOLEAN Prefixed; - - YoriLibInitEmptyString(&NameToCheck); - Offset = 0; - Prefixed = FALSE; - if (YoriLibIsPathPrefixed(File)) { - Offset = sizeof("\\\\.\\") - 1; - Prefixed = TRUE; - } - - NameToCheck.StartOfString = &File->StartOfString[Offset]; - NameToCheck.LengthInChars = File->LengthInChars - Offset; - - - // - // If it's \\.\x: treat it as a device. Note this cannot have any - // trailing characters, or it'd be a file. - // - - if (Prefixed && - NameToCheck.LengthInChars == 2) { - - if (YoriLibIsDriveLetterWithColon(&NameToCheck)) { - return TRUE; - } - } - - // - // Check for a physical drive name. Note this also checks that the - // prefix is a dot. - // - - if (Prefixed) { - if (YoriLibCompareStringWithLiteralInsensitiveCount(File, _T("\\\\.\\PHYSICALDRIVE"), sizeof("\\\\.\\PHYSICALDRIVE") - 1) == 0 || - YoriLibCompareStringWithLiteralInsensitiveCount(File, _T("\\\\.\\HARDDISK"), sizeof("\\\\.\\HARDDISK") - 1) == 0 || - YoriLibCompareStringWithLiteralInsensitiveCount(File, _T("\\\\.\\CDROM"), sizeof("\\\\.\\CDROM") - 1) == 0) { - return TRUE; - } - } - - if (NameToCheck.LengthInChars < 3 || NameToCheck.LengthInChars > 4) { - return FALSE; - } - - if (YoriLibCompareStringWithLiteralInsensitive(&NameToCheck, _T("CON")) == 0 || - YoriLibCompareStringWithLiteralInsensitive(&NameToCheck, _T("AUX")) == 0 || - YoriLibCompareStringWithLiteralInsensitive(&NameToCheck, _T("PRN")) == 0 || - YoriLibCompareStringWithLiteralInsensitive(&NameToCheck, _T("NUL")) == 0) { - - return TRUE; - } - - if (NameToCheck.LengthInChars < 4) { - return FALSE; - } - - if (YoriLibCompareStringWithLiteralInsensitiveCount(&NameToCheck, _T("LPT"), 3) == 0 && - (NameToCheck.StartOfString[3] >= '1' && NameToCheck.StartOfString[3] <= '9')) { - - return TRUE; - } - - if (YoriLibCompareStringWithLiteralInsensitiveCount(&NameToCheck, _T("COM"), 3) == 0 && - (NameToCheck.StartOfString[3] >= '1' && NameToCheck.StartOfString[3] <= '9')) { - - return TRUE; - } - - - return FALSE; -} - -/** - Resolves a user string which must refer to a single file into a physical path - for that file. - - @param UserString The string as specified by the user. This is currently - required to be NULL terminated. - - @param ReturnEscapedPath TRUE if the resulting path should be prefixed with - \\?\, FALSE to indicate a traditional Win32 path. - - @param FullPath On successful completion, this pointer is updated to point to - the full path string. This string should be uninitialized on input and - is allocated within this routine. The caller is expected to free this - with @ref YoriLibFreeStringContents. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibUserStringToSingleFilePath( - __in PCYORI_STRING UserString, - __in BOOL ReturnEscapedPath, - __out PYORI_STRING FullPath - ) -{ - YORI_STRING PathToTranslate; - YORI_STRING YsFilePrefix; - YORI_STRING ExpandedString; - BOOL ReturnValue = FALSE; - - YoriLibInitEmptyString(&YsFilePrefix); - YsFilePrefix.StartOfString = UserString->StartOfString; - YsFilePrefix.LengthInChars = sizeof("file:///") - 1; - - YoriLibInitEmptyString(&PathToTranslate); - if (YoriLibCompareStringWithLiteralInsensitive(&YsFilePrefix, _T("file:///")) == 0) { - PathToTranslate.StartOfString = &UserString->StartOfString[YsFilePrefix.LengthInChars]; - PathToTranslate.LengthInChars = UserString->LengthInChars - YsFilePrefix.LengthInChars; - } else { - PathToTranslate.StartOfString = UserString->StartOfString; - PathToTranslate.LengthInChars = UserString->LengthInChars; - } - - YoriLibInitEmptyString(FullPath); - - if (YoriLibExpandHomeDirectories(&PathToTranslate, &ExpandedString)) { - ReturnValue = YoriLibGetFullPathNameReturnAllocation(&ExpandedString, ReturnEscapedPath, FullPath, NULL); - YoriLibFreeStringContents(&ExpandedString); - } else { - ReturnValue = YoriLibGetFullPathNameReturnAllocation(&PathToTranslate, ReturnEscapedPath, FullPath, NULL); - } - - if (ReturnValue == 0) { - YoriLibFreeStringContents(FullPath); - return 0; - } - - return ReturnValue; -} - -/** - Checks if a file name refers to a device, and if so returns a path to the - device. If it does not refer to a device, the path is resolved into a file - path for the specified file. - - @param UserString The string as specified by the user. This is currently - required to be NULL terminated. - - @param ReturnEscapedPath TRUE if the resulting path should be prefixed with - \\?\, FALSE to indicate a traditional Win32 path. - - @param FullPath On successful completion, this pointer is updated to point to - the full path string. This string should be uninitialized on input and - is allocated within this routine. The caller is expected to free this - with @ref YoriLibFreeStringContents. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibUserStringToSingleFilePathOrDevice( - __in PCYORI_STRING UserString, - __in BOOL ReturnEscapedPath, - __out PYORI_STRING FullPath - ) -{ - if (YoriLibIsFileNameDeviceName(UserString)) { - YORI_ALLOC_SIZE_T CharsNeeded; - BOOL Prefixed; - - CharsNeeded = UserString->LengthInChars + 1; - Prefixed = YoriLibIsPathPrefixed(UserString); - - if (!Prefixed && ReturnEscapedPath) { - CharsNeeded += sizeof("\\\\.\\") - 1; - } - if (!YoriLibAllocateString(FullPath, CharsNeeded)) { - return FALSE; - } - if (!Prefixed && ReturnEscapedPath) { - FullPath->LengthInChars = YoriLibSPrintf(FullPath->StartOfString, _T("\\\\.\\%y"), UserString); - } else { - FullPath->LengthInChars = YoriLibSPrintf(FullPath->StartOfString, _T("%y"), UserString); - } - return TRUE; - } - return YoriLibUserStringToSingleFilePath(UserString, ReturnEscapedPath, FullPath); -} - /** Converts a path from \\?\ or \\.\ form into a regular, non-escaped form. This requires a reallocate for UNC paths, which insert a character in the @@ -2107,518 +1564,4 @@ YoriLibUnescapePath( return TRUE; } -/** - Return the volume name of the volume that is hosting a particular file. This - is normally done via the Win32 GetVolumePathName API, which was added in - Windows 2000 to support mount points; on older versions this behavior is - emulated by returning the drive letter or UNC share name. - - @param FileName Pointer to the file name to obtain the volume for. - - @param VolumeName On successful completion, populated with a path to the - volume name. This string is expected to be initialized on entry and - may be reallocated within this routine. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibGetVolumePathName( - __in PYORI_STRING FileName, - __inout PYORI_STRING VolumeName - ) -{ - BOOL FreeOnFailure = FALSE; - ASSERT(YoriLibIsStringNullTerminated(FileName)); - - // - // This function expects a full/escaped path, because Win32 has no way - // to determine the buffer length if it's anything else. - // - - if (FileName->LengthInChars < 4 || - !YoriLibIsSep(FileName->StartOfString[0]) || - !YoriLibIsSep(FileName->StartOfString[1]) || - (FileName->StartOfString[2] != '?' && FileName->StartOfString[2] != '.') || - !YoriLibIsSep(FileName->StartOfString[3])) { - - return FALSE; - } - - // - // The volume name can be as long as the file name, plus a NULL - // terminator. - // - - if (VolumeName->LengthAllocated <= FileName->LengthInChars) { - YoriLibFreeStringContents(VolumeName); - if (!YoriLibAllocateString(VolumeName, FileName->LengthInChars + 1)) { - return FALSE; - } - FreeOnFailure = TRUE; - } - - // - // If Win32 support exists, use it. - // - - if (DllKernel32.pGetVolumePathNameW != NULL) { - if (!DllKernel32.pGetVolumePathNameW(FileName->StartOfString, VolumeName->StartOfString, VolumeName->LengthAllocated)) { - if (FreeOnFailure) { - YoriLibFreeStringContents(VolumeName); - } - return FALSE; - } - VolumeName->LengthInChars = (YORI_ALLOC_SIZE_T)_tcslen(VolumeName->StartOfString); - - // - // For some reason Windows doesn't add the prefix to this string, - // which is really broken - a volume name of "C:" is not a volume - // name, it's a reference to a current directory. - // - - if (!YoriLibIsPathPrefixed(VolumeName)) { - YORI_STRING EscapedVolumeName; - YoriLibInitEmptyString(&EscapedVolumeName); - if (!YoriLibGetFullPathNameReturnAllocation(VolumeName, TRUE, &EscapedVolumeName, NULL)) { - if (FreeOnFailure) { - YoriLibFreeStringContents(VolumeName); - } - return FALSE; - } - - // - // If it fits in the existing allocation, use that. This allows - // the caller to supply an appropriately sized buffer and - // expect the result in that buffer. - // - - if (EscapedVolumeName.LengthInChars < VolumeName->LengthAllocated) { - memcpy(VolumeName->StartOfString, EscapedVolumeName.StartOfString, EscapedVolumeName.LengthInChars * sizeof(TCHAR)); - VolumeName->LengthInChars = EscapedVolumeName.LengthInChars; - VolumeName->StartOfString[VolumeName->LengthInChars] = '\0'; - } else { - YoriLibFreeStringContents(VolumeName); - YoriLibCloneString(VolumeName, &EscapedVolumeName); - FreeOnFailure = TRUE; - } - - YoriLibFreeStringContents(&EscapedVolumeName); - } - - // - // If it ends in a backslash, truncate it - // - - if (VolumeName->LengthInChars > 0 && - YoriLibIsSep(VolumeName->StartOfString[VolumeName->LengthInChars - 1])) { - - VolumeName->LengthInChars--; - VolumeName->StartOfString[VolumeName->LengthInChars] = '\0'; - } - return TRUE; - } - - // - // If Win32 support doesn't exist, we know that mount points can't - // exist so we can return only the drive letter path, or the UNC - // path with server and share. - // - - if (!YoriLibIsFullPathUnc(FileName)) { - if (FileName->LengthInChars >= 6) { - memcpy(VolumeName->StartOfString, FileName->StartOfString, 6 * sizeof(TCHAR)); - VolumeName->StartOfString[6] = '\0'; - VolumeName->LengthInChars = 6; - return TRUE; - } - } else { - if (FileName->LengthInChars >= sizeof("\\\\?\\UNC\\")) { - YORI_STRING Subset; - LPTSTR Slash; - YORI_ALLOC_SIZE_T CharsToCopy; - - YoriLibInitEmptyString(&Subset); - Subset.StartOfString = FileName->StartOfString + sizeof("\\\\?\\UNC\\"); - Subset.LengthInChars = FileName->LengthInChars - sizeof("\\\\?\\UNC\\"); - - Slash = YoriLibFindLeftMostCharacter(&Subset, '\\'); - if (Slash != NULL) { - Subset.LengthInChars = Subset.LengthInChars - (YORI_ALLOC_SIZE_T)(Slash - Subset.StartOfString); - Subset.StartOfString = Slash; - - if (Subset.LengthInChars > 0) { - Subset.LengthInChars--; - Subset.StartOfString++; - Slash = YoriLibFindLeftMostCharacter(&Subset, '\\'); - if (Slash != NULL) { - CharsToCopy = (YORI_ALLOC_SIZE_T)(Slash - FileName->StartOfString); - } else { - CharsToCopy = (YORI_ALLOC_SIZE_T)(Subset.StartOfString - FileName->StartOfString) + Subset.LengthInChars; - } - - memcpy(VolumeName->StartOfString, FileName->StartOfString, CharsToCopy * sizeof(TCHAR)); - VolumeName->StartOfString[CharsToCopy] = '\0'; - VolumeName->LengthInChars = CharsToCopy; - return TRUE; - } - } - } - } - - if (FreeOnFailure) { - YoriLibFreeStringContents(VolumeName); - } - VolumeName->LengthInChars = 0; - return FALSE; -} - -/** - Determine if the specified directory supports long file names. - - @param PathName Pointer to the directory to check. - - @param LongNameSupport On successful completion, updated to TRUE to indicate - long name support, FALSE to indicate no long name support. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOLEAN -YoriLibPathSupportsLongNames( - __in PCYORI_STRING PathName, - __out PBOOLEAN LongNameSupport - ) -{ - YORI_STRING VolumeLabel; - YORI_STRING FsName; - YORI_STRING FullPathName; - YORI_STRING VolRootName; - DWORD ShortSerialNumber; - DWORD Capabilities; - DWORD MaxComponentLength; - BOOLEAN Result; - - YoriLibInitEmptyString(&VolumeLabel); - YoriLibInitEmptyString(&FsName); - YoriLibInitEmptyString(&FullPathName); - YoriLibInitEmptyString(&VolRootName); - Result = FALSE; - - if (!YoriLibAllocateString(&VolumeLabel, 256)) { - goto Exit; - } - - if (!YoriLibAllocateString(&FsName, 256)) { - goto Exit; - } - - if (!YoriLibUserStringToSingleFilePath(PathName, TRUE, &FullPathName)) { - goto Exit; - } - - // - // We want to translate the user specified path into a volume root. - // Windows 2000 and above have a nice API for this, which says it's - // guaranteed to return less than or equal to the size of the input - // string, so we allocate the input string, plus space for a trailing - // backslash and a NULL terminator. - // - - if (!YoriLibAllocateString(&VolRootName, FullPathName.LengthInChars + 2)) { - goto Exit; - } - - if (!YoriLibGetVolumePathName(&FullPathName, &VolRootName)) { - goto Exit; - } - - // - // GetVolumeInformation wants a name with a trailing backslash. Add one - // if needed. - // - - if (VolRootName.LengthInChars > 0 && - VolRootName.LengthInChars + 1 < VolRootName.LengthAllocated && - VolRootName.StartOfString[VolRootName.LengthInChars - 1] != '\\') { - - VolRootName.StartOfString[VolRootName.LengthInChars] = '\\'; - VolRootName.StartOfString[VolRootName.LengthInChars + 1] = '\0'; - VolRootName.LengthInChars++; - } - - if (GetVolumeInformation(VolRootName.StartOfString, - VolumeLabel.StartOfString, - VolumeLabel.LengthAllocated, - &ShortSerialNumber, - &MaxComponentLength, - &Capabilities, - FsName.StartOfString, - FsName.LengthAllocated)) { - - Result = TRUE; - if (MaxComponentLength >= 255) { - *LongNameSupport = TRUE; - } else { - *LongNameSupport = FALSE; - } - } - -Exit: - YoriLibFreeStringContents(&FullPathName); - YoriLibFreeStringContents(&VolRootName); - YoriLibFreeStringContents(&FsName); - YoriLibFreeStringContents(&VolumeLabel); - - return Result; -} - - -/** - Context structure used to preserve state about the next volume to return - when a native platform implementation of FindFirstVolume et al are not - available. - */ -typedef struct _YORI_LIB_FIND_VOLUME_CONTEXT { - - /** - Indicates the number of the drive to probe on the next call to - @ref YoriLibFindNextVolume . - */ - DWORD NextDriveLetter; -} YORI_LIB_FIND_VOLUME_CONTEXT, *PYORI_LIB_FIND_VOLUME_CONTEXT; - -/** - Returns the next volume on the system following a previous call to - @ref YoriLibFindFirstVolume. When no more volumes are available, this - function will return FALSE and set last error to ERROR_NO_MORE_FILES. - When this occurs, the handle must be closed with - @ref YoriLibFindVolumeClose. - - @param FindHandle The handle previously returned from - @ref YoriLibFindFirstVolume . - - @param VolumeName On successful completion, populated with the path to the - next volume found. - - @param BufferLength Specifies the length, in characters, of the VolumeName - buffer. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibFindNextVolume( - __in HANDLE FindHandle, - __out_ecount(BufferLength) LPWSTR VolumeName, - __in DWORD BufferLength - ) -{ - if (DllKernel32.pFindFirstVolumeW && - DllKernel32.pFindNextVolumeW && - DllKernel32.pFindVolumeClose && - DllKernel32.pGetVolumePathNamesForVolumeNameW) { - - return DllKernel32.pFindNextVolumeW(FindHandle, VolumeName, BufferLength); - } else { - PYORI_LIB_FIND_VOLUME_CONTEXT FindContext = (PYORI_LIB_FIND_VOLUME_CONTEXT)FindHandle; - TCHAR ProbeString[sizeof("A:\\")]; - DWORD DriveType; - - while(TRUE) { - if (FindContext->NextDriveLetter + 'A' > 'Z') { - SetLastError(ERROR_NO_MORE_FILES); - return FALSE; - } - - ProbeString[0] = (TCHAR)(FindContext->NextDriveLetter + 'A'); - ProbeString[1] = ':'; - ProbeString[2] = '\\'; - ProbeString[3] = '\0'; - - DriveType = GetDriveType(ProbeString); - if (DriveType != DRIVE_UNKNOWN && - DriveType != DRIVE_NO_ROOT_DIR) { - - if (BufferLength >= sizeof(ProbeString)/sizeof(ProbeString[0])) { - memcpy(VolumeName, ProbeString, (sizeof(ProbeString)/sizeof(ProbeString[0]) - 1) * sizeof(TCHAR)); - VolumeName[sizeof(ProbeString)/sizeof(ProbeString[0]) - 1] = '\0'; - FindContext->NextDriveLetter++; - return TRUE; - } else { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return FALSE; - } - } - - FindContext->NextDriveLetter++; - } - } - - return FALSE; -} - -/** - Close a handle returned from @ref YoriLibFindFirstVolume . - - @param FindHandle The handle to close. - - @return TRUE to indicate success, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibFindVolumeClose( - __in HANDLE FindHandle - ) -{ - if (DllKernel32.pFindFirstVolumeW && - DllKernel32.pFindNextVolumeW && - DllKernel32.pFindVolumeClose && - DllKernel32.pGetVolumePathNamesForVolumeNameW) { - - return DllKernel32.pFindVolumeClose(FindHandle); - } else { - YoriLibFree(FindHandle); - } - return TRUE; -} - -/** - Returns the first volume on the system and a handle to use for subsequent - volumes with @ref YoriLibFindNextVolume . This handle must be closed with - @ref YoriLibFindVolumeClose. - - @param VolumeName On successful completion, populated with the path to the - first volume found. - - @param BufferLength Specifies the length, in characters, of the VolumeName - buffer. - - @return On successful completion, an opaque handle to use for subsequent - matches by calling @ref YoriLibFindNextVolume , and terminated by - calling @ref YoriLibFindVolumeClose . On failure, - INVALID_HANDLE_VALUE. - */ -__success(return != INVALID_HANDLE_VALUE) -HANDLE -YoriLibFindFirstVolume( - __out LPWSTR VolumeName, - __in DWORD BufferLength - ) -{ - - // - // Windows 2000 supports mount points but doesn't provide the API - // needed to find a human name for them, so we treat it like NT4 - // and only look for drive letter paths. Not including mount points - // seems like a lesser evil than giving the user volume GUIDs. - // - - if (DllKernel32.pFindFirstVolumeW && - DllKernel32.pFindNextVolumeW && - DllKernel32.pFindVolumeClose && - DllKernel32.pGetVolumePathNamesForVolumeNameW) { - - return DllKernel32.pFindFirstVolumeW(VolumeName, BufferLength); - } else { - PYORI_LIB_FIND_VOLUME_CONTEXT FindContext; - - FindContext = YoriLibMalloc(sizeof(YORI_LIB_FIND_VOLUME_CONTEXT)); - if (FindContext == NULL) { - return INVALID_HANDLE_VALUE; - } - - FindContext->NextDriveLetter = 0; - - if (YoriLibFindNextVolume((HANDLE)FindContext, VolumeName, BufferLength)) { - return (HANDLE)FindContext; - } else { - YoriLibFindVolumeClose((HANDLE)FindContext); - } - } - return INVALID_HANDLE_VALUE; -} - -/** - Wrapper that calls GetDiskFreeSpaceEx if present, and if not uses 64 bit - math to calculate total and free disk space up to the limit (which should - be around 8Tb with a 4Kb cluster size.) - - @param DirectoryName Specifies the drive or directory to calculate free - space for. - - @param BytesAvailable Optionally points to a location to receive the amount - of allocatable space on successful completion. - - @param TotalBytes Optionally points to a location to receive the amount - of total space on successful completion. - - @param FreeBytes Optionally points to a location to receive the amount - of unused space on successful completion. - - @return TRUE to indicate successful completion, FALSE to indicate failure. - */ -__success(return) -BOOL -YoriLibGetDiskFreeSpace( - __in LPCTSTR DirectoryName, - __out_opt PLARGE_INTEGER BytesAvailable, - __out_opt PLARGE_INTEGER TotalBytes, - __out_opt PLARGE_INTEGER FreeBytes - ) -{ - LARGE_INTEGER LocalBytesAvailable; - LARGE_INTEGER LocalTotalBytes; - LARGE_INTEGER LocalFreeBytes; - DWORD LocalSectorsPerCluster; - DWORD LocalBytesPerSector; - LARGE_INTEGER LocalAllocationSize; - LARGE_INTEGER LocalNumberOfFreeClusters; - LARGE_INTEGER LocalTotalNumberOfClusters; - BOOL Result; - - if (DllKernel32.pGetDiskFreeSpaceExW != NULL) { - Result = DllKernel32.pGetDiskFreeSpaceExW(DirectoryName, - &LocalBytesAvailable, - &LocalTotalBytes, - &LocalFreeBytes); - if (!Result) { - return FALSE; - } - } else { - - LocalNumberOfFreeClusters.HighPart = 0; - LocalTotalNumberOfClusters.HighPart = 0; - - Result = GetDiskFreeSpace(DirectoryName, - &LocalSectorsPerCluster, - &LocalBytesPerSector, - &LocalNumberOfFreeClusters.LowPart, - &LocalTotalNumberOfClusters.LowPart); - - if (!Result) { - return FALSE; - } - - LocalAllocationSize.QuadPart = LocalSectorsPerCluster * LocalBytesPerSector; - - LocalBytesAvailable.QuadPart = LocalAllocationSize.QuadPart * LocalNumberOfFreeClusters.QuadPart; - LocalFreeBytes.QuadPart = LocalBytesAvailable.QuadPart; - LocalTotalBytes.QuadPart = LocalAllocationSize.QuadPart * LocalTotalNumberOfClusters.QuadPart; - } - - if (BytesAvailable != NULL) { - BytesAvailable->QuadPart = LocalBytesAvailable.QuadPart; - } - if (TotalBytes != NULL) { - TotalBytes->QuadPart = LocalTotalBytes.QuadPart; - } - if (FreeBytes != NULL) { - FreeBytes->QuadPart = LocalFreeBytes.QuadPart; - } - - return TRUE; -} - - // vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/group.c b/misc/tools/yori/yori/lib/group.c index eb07a649d0..80f47aa28e 100644 --- a/misc/tools/yori/yori/lib/group.c +++ b/misc/tools/yori/yori/lib/group.c @@ -28,6 +28,97 @@ #include "yorilib.h" +/** + Query whether the specified group SID is present and enabled in the specified + access token. + + @param TokenHandle A handle to an access token. If NULL, the current thread's + token is used if available, otherwise the current process's token. + + @param SidToCheck The group SID to check for. + + @param IsMember On successful completion, set to TRUE to indicate the SID is + present and enabled in the access token, FALSE if not. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibCheckTokenMembership( + __in_opt HANDLE TokenHandle, + __in PSID SidToCheck, + __out PBOOL IsMember + ) +{ + BOOL TokenOpened = FALSE; + PTOKEN_GROUPS Groups; + DWORD GroupsSize; + YORI_ALLOC_SIZE_T Count; + + YoriLibLoadAdvApi32Functions(); + + if (DllAdvApi32.pOpenThreadToken == NULL || + DllAdvApi32.pOpenProcessToken == NULL || + DllAdvApi32.pGetTokenInformation == NULL || + DllAdvApi32.pEqualSid == NULL) { + + return FALSE; + } + + if (TokenHandle == NULL) { + if (!DllAdvApi32.pOpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &TokenHandle)) { + if (GetLastError() != ERROR_NO_TOKEN) { + return FALSE; + } + if (!DllAdvApi32.pOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &TokenHandle)) { + return FALSE; + } + } + TokenOpened = TRUE; + } + + if (DllAdvApi32.pGetTokenInformation(TokenHandle, TokenGroups, NULL, 0, &GroupsSize) || + (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { + if (TokenOpened) { + CloseHandle(TokenHandle); + } + return FALSE; + } + + Groups = YoriLibMalloc(GroupsSize); + if (Groups == NULL) { + if (TokenOpened) { + CloseHandle(TokenHandle); + } + return FALSE; + } + + if (!DllAdvApi32.pGetTokenInformation(TokenHandle, TokenGroups, Groups, GroupsSize, &GroupsSize)) { + if (TokenOpened) { + CloseHandle(TokenHandle); + } + YoriLibFree(Groups); + return FALSE; + } + + *IsMember = FALSE; + + for (Count = 0; Count < Groups->GroupCount; Count++) { + if (DllAdvApi32.pEqualSid(Groups->Groups[Count].Sid, SidToCheck) && + (Groups->Groups[Count].Attributes & SE_GROUP_ENABLED) != 0) { + + *IsMember = TRUE; + break; + } + } + + if (TokenOpened) { + CloseHandle(TokenHandle); + } + YoriLibFree(Groups); + return TRUE; +} + /** Query whether the current process is running as part of the specified group. @@ -59,9 +150,7 @@ YoriLibIsCurrentUserInGroup( YoriLibLoadAdvApi32Functions(); - if (DllAdvApi32.pLookupAccountNameW == NULL || - DllAdvApi32.pCheckTokenMembership == NULL) { - + if (DllAdvApi32.pLookupAccountNameW == NULL) { return FALSE; } @@ -76,7 +165,7 @@ YoriLibIsCurrentUserInGroup( return FALSE; } - if (!DllAdvApi32.pCheckTokenMembership(NULL, &Sid.Sid, IsMember)) { + if (!YoriLibCheckTokenMembership(NULL, &Sid.Sid, IsMember)) { return FALSE; } @@ -107,7 +196,6 @@ YoriLibIsCurrentUserInWellKnownGroup( YoriLibLoadAdvApi32Functions(); if (DllAdvApi32.pAllocateAndInitializeSid == NULL || - DllAdvApi32.pCheckTokenMembership == NULL || DllAdvApi32.pFreeSid == NULL) { return FALSE; @@ -128,7 +216,7 @@ YoriLibIsCurrentUserInWellKnownGroup( return FALSE; } - if (!DllAdvApi32.pCheckTokenMembership(NULL, Sid, IsMember)) { + if (!YoriLibCheckTokenMembership(NULL, Sid, IsMember)) { DllAdvApi32.pFreeSid(Sid); return FALSE; } diff --git a/misc/tools/yori/yori/lib/hash.c b/misc/tools/yori/yori/lib/hash.c index ca5f574e9b..c6c8734ad3 100644 --- a/misc/tools/yori/yori/lib/hash.c +++ b/misc/tools/yori/yori/lib/hash.c @@ -201,7 +201,7 @@ YoriLibHashLookupByKey( ListEntry = YoriLibGetNextListEntry(&HashTable->Buckets[BucketIndex].ListHead, NULL); while (ListEntry != NULL) { HashEntry = CONTAINING_RECORD(ListEntry, YORI_HASH_ENTRY, ListEntry); - if (YoriLibCompareStringInsensitive(KeyString, &HashEntry->Key) == 0) { + if (YoriLibCompareStringIns(KeyString, &HashEntry->Key) == 0) { break; } HashEntry = NULL; diff --git a/misc/tools/yori/yori/lib/http.c b/misc/tools/yori/yori/lib/http.c new file mode 100644 index 0000000000..2a56b6b070 --- /dev/null +++ b/misc/tools/yori/yori/lib/http.c @@ -0,0 +1,1089 @@ +/** + * @file lib/http.c + * + * Yori fallback HTTP support + * + * Copyright (c) 2023 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + The type of the handle. This occurs because the WinInet interface returns an + HINTERNET for many APIs but it means different things in different contexts. + */ +typedef enum _YORI_LIB_INTERNET_HANDLE_TYPE { + YoriLibUndefinedHandle = 0, + YoriLibInternetHandle = 1, + YoriLibUrlHandle = 2 +} YORI_LIB_INTERNET_HANDLE_TYPE; + +/** + Information describing each response header in an HTTP response. + */ +typedef struct _YORI_LIB_HTTP_HEADER_LINE { + + /** + The list of headers returned from an HTTP request. + */ + YORI_LIST_ENTRY ListEntry; + + /** + A string describing the entire line without parsing. + */ + YORI_STRING EntireLine; + + /** + The variable component of a response header. + */ + YORI_STRING Variable; + + /** + The value component of a response header. + */ + YORI_STRING Value; + +} YORI_LIB_HTTP_HEADER_LINE, *PYORI_LIB_HTTP_HEADER_LINE; + +/** + A nonopaque representation of an HINTERNET handle. + */ +typedef struct _YORI_LIB_INTERNET_HANDLE { + + /** + The type of the handle. + */ + YORI_LIB_INTERNET_HANDLE_TYPE HandleType; + + /** + Different types of data depending on the type of the handle. + */ + union { + struct { + /** + For handles opened with YoriLibInternetOpen, contains the user + agent, being the only value supported with YoriLibInternetOpen. + */ + YORI_STRING UserAgent; + } Internet; + struct { + + /** + For handles opened with YoriLibInternetOpenUrl, points back to + the handle opened with YoriLibInternetOpen. + */ + struct _YORI_LIB_INTERNET_HANDLE *InternetHandle; + + /** + The Url. This allocation is owned within this module and can be + updated in response to redirects. + */ + YORI_STRING Url; + + /** + Headers supplied by the user. This allocation is owned by the + caller since this module never modifies it. + */ + YORI_STRING UserRequestHeaders; + + /** + The byte buffer containing the response from the server. + */ + YORI_LIB_BYTE_BUFFER ByteBuffer; + + /** + A list of response headers once they have been parsed. + */ + YORI_LIST_ENTRY HttpResponseHeaders; + + /** + The current offset within the buffer for InternetRead requests. + */ + DWORDLONG CurrentReadOffset; + + /** + The offset within ByteBuffer containing the HTTP payload. + */ + DWORD HttpBodyOffset; + + /** + Once the response has been processed, the major version of the + HTTP response. + */ + DWORD HttpMajorVersion; + + /** + Once the response has been processed, the minor version of the + HTTP response. + */ + DWORD HttpMinorVersion; + + /** + Once the response has been processed, the HTTP status code of + the response. + */ + DWORD HttpStatusCode; + + } Url; + } u; +} YORI_LIB_INTERNET_HANDLE, *PYORI_LIB_INTERNET_HANDLE; + + +/** + Initializes the use of the WinInet compatibility library. + + @param UserAgent Pointer to a NULL terminated string that specifies the + value to supply HTTP servers for a user agent. + + @param AccessType Proxies are not supported by this library; must be zero. + + @param ProxyName Proxies are not supported by this library; must be NULL. + + @param ProxyBypass Proxies are not supported by this library; must be NULL. + + @param Flags Flags for the operation. No flags are supported by this + library; must be zero. + + @return On successful completion, returns a handle for use in later + functions. On failure, returns NULL. This handle must be closed + with @ref YoriLibInternetCloseHandle . + */ +LPVOID WINAPI +YoriLibInternetOpen( + __in LPCTSTR UserAgent, + __in DWORD AccessType, + __in_opt LPCTSTR ProxyName, + __in_opt LPCTSTR ProxyBypass, + __in DWORD Flags + ) +{ + PYORI_LIB_INTERNET_HANDLE Handle; + WSADATA WSAData; + YORI_ALLOC_SIZE_T Length; + + // + // This library is super minimal. Fail if the request is for anything + // fancy. + // + + if (AccessType != 0 || + ProxyName != NULL || + ProxyBypass != NULL || + Flags != 0) { + + return NULL; + } + + // + // This library is implementing HTTP on TCP. No TCP support, no HTTP support. + // + + YoriLibLoadWsock32Functions(); + if (DllWsock32.pclosesocket == NULL || + DllWsock32.pconnect == NULL || + DllWsock32.pgethostbyname == NULL || + DllWsock32.precv == NULL || + DllWsock32.psend == NULL || + DllWsock32.psocket == NULL || + DllWsock32.pWSACleanup == NULL || + DllWsock32.pWSAStartup == NULL) { + + return NULL; + } + + if (DllWsock32.pWSAStartup(MAKEWORD(1, 1), &WSAData) != 0) { + return NULL; + } + + Handle = YoriLibMalloc(sizeof(YORI_LIB_INTERNET_HANDLE)); + if (Handle == NULL) { + return NULL; + } + + ZeroMemory(Handle, sizeof(YORI_LIB_INTERNET_HANDLE)); + Handle->HandleType = YoriLibInternetHandle; + + if (UserAgent != NULL) { + Length = (YORI_ALLOC_SIZE_T)_tcslen(UserAgent); + if (!YoriLibAllocateString(&Handle->u.Internet.UserAgent, Length + 1)) { + YoriLibFree(Handle); + return NULL; + } + + memcpy(Handle->u.Internet.UserAgent.StartOfString, UserAgent, Length * sizeof(TCHAR)); + Handle->u.Internet.UserAgent.LengthInChars = Length; + YoriLibTrimTrailingNewlines(&Handle->u.Internet.UserAgent); + Handle->u.Internet.UserAgent.StartOfString[Length] = '\0'; + } + + return Handle; +} + +/** + Clean up a Url handle to prepare for reuse. The same handle can be used for + multiple requests due to HTTP redirects. + + @param UrlRequest Pointer to the URL handle. + */ +VOID +YoriLibHttpResetUrlRequest( + __inout PYORI_LIB_INTERNET_HANDLE UrlRequest + ) +{ + PYORI_LIST_ENTRY ListEntry; + PYORI_LIB_HTTP_HEADER_LINE ResponseLine; + + YoriLibByteBufferReset(&UrlRequest->u.Url.ByteBuffer); + + ListEntry = NULL; + ListEntry = YoriLibGetNextListEntry(&UrlRequest->u.Url.HttpResponseHeaders, NULL); + while (ListEntry != NULL) { + ResponseLine = CONTAINING_RECORD(ListEntry, YORI_LIB_HTTP_HEADER_LINE, ListEntry); + YoriLibRemoveListItem(ListEntry); + YoriLibFreeStringContents(&ResponseLine->EntireLine); + YoriLibFreeStringContents(&ResponseLine->Variable); + YoriLibFreeStringContents(&ResponseLine->Value); + YoriLibDereference(ResponseLine); + ListEntry = YoriLibGetNextListEntry(&UrlRequest->u.Url.HttpResponseHeaders, NULL); + } +} + +/** + Close a HINTERNET handle. Note this can be a Url handle or an Internet handle. + + @param hInternet The HINTERNET handle to close. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL WINAPI +YoriLibInternetCloseHandle( + __in LPVOID hInternet + ) +{ + PYORI_LIB_INTERNET_HANDLE Handle; + + if (hInternet == NULL) { + return FALSE; + } + + Handle = (PYORI_LIB_INTERNET_HANDLE)hInternet; + + ASSERT(Handle->HandleType == YoriLibInternetHandle || + Handle->HandleType == YoriLibUrlHandle); + + if (Handle->HandleType != YoriLibInternetHandle && + Handle->HandleType != YoriLibUrlHandle) { + return FALSE; + } + + if (Handle->HandleType == YoriLibInternetHandle) { + YoriLibFreeStringContents(&Handle->u.Internet.UserAgent); + DllWsock32.pWSACleanup(); + } else if (Handle->HandleType == YoriLibUrlHandle) { + YoriLibHttpResetUrlRequest(Handle); + YoriLibFreeStringContents(&Handle->u.Url.Url); + YoriLibInitEmptyString(&Handle->u.Url.UserRequestHeaders); + YoriLibByteBufferCleanup(&Handle->u.Url.ByteBuffer); + } + + YoriLibFree(Handle); + return TRUE; +} + +/** + Find a header by name in the HTTP response. + + @param UrlRequest Pointer to the URL handle containing all parsed response + headers. + + @param Header The name of the header to find. + + @return If the header is found, pointer to the response header. If no header + is found, returns NULL. + */ +PYORI_LIB_HTTP_HEADER_LINE +YoriLibHttpFindResponseHeader( + __in PYORI_LIB_INTERNET_HANDLE UrlRequest, + __in LPCTSTR Header + ) +{ + PYORI_LIST_ENTRY ListEntry; + PYORI_LIB_HTTP_HEADER_LINE ResponseLine; + + ListEntry = NULL; + ListEntry = YoriLibGetNextListEntry(&UrlRequest->u.Url.HttpResponseHeaders, ListEntry); + while (ListEntry != NULL) { + ResponseLine = CONTAINING_RECORD(ListEntry, YORI_LIB_HTTP_HEADER_LINE, ListEntry); + if (YoriLibCompareStringLitIns(&ResponseLine->Variable, Header) == 0) { + return ResponseLine; + } + ListEntry = YoriLibGetNextListEntry(&UrlRequest->u.Url.HttpResponseHeaders, ListEntry); + } + + return NULL; +} + + +/** + Once the HTTP response payload has been received, parse the response into + a series of headers, and parse the status line into major/minor versions and + HTTP status code. If the status code is a redirect code, construct a new + redirect URL and prepare the request to be reissued to a new URL. + + @param UrlRequest Pointer to the request containing the received payload. + + @param RedirectUrl On successful completion, updated to contain a new URL if + the request should be redirected. Will be left as an empty string if + no redirection should be performed. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOLEAN +YoriLibHttpProcessResponseHeaders( + __in PYORI_LIB_INTERNET_HANDLE UrlRequest, + __inout PYORI_STRING RedirectUrl + ) +{ + UCHAR * AnsiBuffer; + UCHAR * LineStart; + DWORD Index; + DWORD CharIndex; + YORI_ALLOC_SIZE_T LineLengthInChars; + YORI_ALLOC_SIZE_T CharsConsumed; + YORI_MAX_SIGNED_T llTemp; + PYORI_LIB_HTTP_HEADER_LINE ResponseLine; + PYORI_LIST_ENTRY ListEntry; + YORI_STRING Str; + LPTSTR Colon; + + UNREFERENCED_PARAMETER(RedirectUrl); + + Index = 0; + AnsiBuffer = UrlRequest->u.Url.ByteBuffer.Buffer; + LineStart = AnsiBuffer; + LineLengthInChars = 0; + + // + // MSFIX: Should impose some fairly arbitrary limit on the maximum + // size of headers. This is because we don't want to limit the size + // of the HTTP payload needlessly, but still want to be robust to + // malicious input trying to cause this to overflow or degrade. + // + + for (Index = 0; Index < UrlRequest->u.Url.ByteBuffer.BytesPopulated; Index++) { + if (AnsiBuffer[Index] == '\r' || AnsiBuffer[Index] == '\n') { + + // + // Advance beyond one line break, but only one line break. If + // there's \r\n, advance two chars; if it's \r or \n, advance by + // one. One of these will be done automatically at the next + // pass through the loop. + // + + if (AnsiBuffer[Index] == '\r' && + Index + 1 < UrlRequest->u.Url.ByteBuffer.BytesPopulated && + AnsiBuffer[Index + 1] == '\n') { + + Index = Index + 1; + } + + // + // An empty line indicates no more headers and the start of + // payload. Note that we just swalled the newline, so payload + // starts at the current Index offset. + // + + if (LineLengthInChars == 0) { + Index++; + break; + } + + ResponseLine = YoriLibReferencedMalloc(sizeof(YORI_LIB_HTTP_HEADER_LINE) + (LineLengthInChars + 1) * sizeof(TCHAR)); + if (ResponseLine == NULL) { + return FALSE; + } + ZeroMemory(ResponseLine, sizeof(YORI_LIB_HTTP_HEADER_LINE)); + + ResponseLine->EntireLine.StartOfString = (LPTSTR)(ResponseLine + 1); + ResponseLine->EntireLine.MemoryToFree = ResponseLine; + YoriLibReference(ResponseLine); + + for (CharIndex = 0; CharIndex < LineLengthInChars; CharIndex++) { + // MSFIX: Filter characters here? + ResponseLine->EntireLine.StartOfString[CharIndex] = LineStart[CharIndex]; + } + ResponseLine->EntireLine.StartOfString[LineLengthInChars] = '\0'; + ResponseLine->EntireLine.LengthInChars = LineLengthInChars; + + ResponseLine->Variable.StartOfString = ResponseLine->EntireLine.StartOfString; + ResponseLine->Variable.MemoryToFree = ResponseLine; + YoriLibReference(ResponseLine); + ResponseLine->Variable.LengthInChars = LineLengthInChars; + + Colon = YoriLibFindLeftMostCharacter(&ResponseLine->EntireLine, ':'); + + if (Colon != NULL) { + ResponseLine->Variable.LengthInChars = (YORI_ALLOC_SIZE_T)(Colon - ResponseLine->Variable.StartOfString); + YoriLibTrimSpaces(&ResponseLine->Variable); + + ResponseLine->Value.StartOfString = Colon + 1; + ResponseLine->Value.LengthInChars = ResponseLine->EntireLine.LengthInChars - ResponseLine->Variable.LengthInChars - 1; + ResponseLine->Value.MemoryToFree = ResponseLine; + YoriLibReference(ResponseLine); + YoriLibTrimSpaces(&ResponseLine->Value); + } + + YoriLibAppendList(&UrlRequest->u.Url.HttpResponseHeaders, &ResponseLine->ListEntry); + + LineStart = &AnsiBuffer[Index + 1]; + LineLengthInChars = 0; + } else { + LineLengthInChars++; + } + } + + UrlRequest->u.Url.HttpBodyOffset = Index; + + ASSERT(Index <= UrlRequest->u.Url.ByteBuffer.BytesPopulated); + + ASSERT(Index >= UrlRequest->u.Url.ByteBuffer.BytesPopulated || + (UrlRequest->u.Url.ByteBuffer.Buffer[Index] != '\n' && + UrlRequest->u.Url.ByteBuffer.Buffer[Index] != '\r')); + UrlRequest->u.Url.CurrentReadOffset = 0; + + ListEntry = YoriLibGetNextListEntry(&UrlRequest->u.Url.HttpResponseHeaders, NULL); + if (ListEntry == NULL) { + return FALSE; + } + + // + // An HTTP status response should be + // + // HTTP/1.0 200 Ok + // + + ResponseLine = CONTAINING_RECORD(ListEntry, YORI_LIB_HTTP_HEADER_LINE, ListEntry); + if (YoriLibCompareStringLitCnt(&ResponseLine->EntireLine, _T("HTTP/"), sizeof("HTTP/") - 1) != 0) { + return FALSE; + } + + YoriLibInitEmptyString(&Str); + Str.StartOfString = &ResponseLine->EntireLine.StartOfString[sizeof("HTTP/") - 1]; + Str.LengthInChars = ResponseLine->EntireLine.LengthInChars - sizeof("HTTP/") + 1; + + if (!YoriLibStringToNumberBase(&Str, 10, FALSE, &llTemp, &CharsConsumed)) { + return FALSE; + } + + if (CharsConsumed == 0 || llTemp != 1) { + return FALSE; + } + + UrlRequest->u.Url.HttpMajorVersion = (DWORD)llTemp; + + Str.StartOfString = Str.StartOfString + CharsConsumed; + Str.LengthInChars = Str.LengthInChars - CharsConsumed; + + if (Str.LengthInChars == 0 || Str.StartOfString[0] != '.') { + return FALSE; + } + + Str.StartOfString = Str.StartOfString + 1; + Str.LengthInChars = Str.LengthInChars - 1; + + if (!YoriLibStringToNumberBase(&Str, 10, FALSE, &llTemp, &CharsConsumed)) { + return FALSE; + } + + if (CharsConsumed == 0 || (llTemp != 0 && llTemp != 1)) { + return FALSE; + } + + UrlRequest->u.Url.HttpMinorVersion = (DWORD)llTemp; + + Str.StartOfString = Str.StartOfString + CharsConsumed; + Str.LengthInChars = Str.LengthInChars - CharsConsumed; + + YoriLibTrimSpaces(&Str); + + if (Str.LengthInChars == 0) { + return FALSE; + } + + if (!YoriLibStringToNumberBase(&Str, 10, FALSE, &llTemp, &CharsConsumed)) { + return FALSE; + } + + UrlRequest->u.Url.HttpStatusCode = (DWORD)llTemp; + + if (UrlRequest->u.Url.HttpStatusCode == 301 || + UrlRequest->u.Url.HttpStatusCode == 302) { + + ResponseLine = YoriLibHttpFindResponseHeader(UrlRequest, _T("Location")); + if (ResponseLine != NULL) { + YoriLibCloneString(RedirectUrl, &ResponseLine->Value); + } + } + + return TRUE; +} + +/** + Connect to a specified URL, download contents, parse headers, and redirect as + necessary. + + @param UrlRequest Pointer to a URL handle containing a URL to connect to. + + @param RedirectUrl On successful completion, updated to contain a new URL if + the request should be redirected. Will be left as an empty string if + no redirection should be performed. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOLEAN +YoriLibHttpProcessUrlRequest( + __in PYORI_LIB_INTERNET_HANDLE UrlRequest, + __inout PYORI_STRING RedirectUrl + ) +{ + YORI_STRING HostSubset; + YORI_STRING Request; + LPCTSTR EndOfHost; + struct hostent * addr; + struct sockaddr_in sin; + UCHAR * AnsiBuffer; + SOCKET s; + DWORD Length; + DWORD TotalLength; + YORI_ALLOC_SIZE_T BytesRemaining; + + // + // MSFIX Implement this + // + + YoriLibInitEmptyString(RedirectUrl); + + // + // Currently this code only speaks http. Note there is no TLS support + // here. + // + + if (YoriLibCompareStringLitInsCnt(&UrlRequest->u.Url.Url, _T("http://"), sizeof("http://") - 1) != 0) { + return FALSE; + } + + YoriLibInitEmptyString(&HostSubset); + HostSubset.StartOfString = &UrlRequest->u.Url.Url.StartOfString[sizeof("http://") - 1]; + HostSubset.LengthInChars = UrlRequest->u.Url.Url.LengthInChars - sizeof("http://") + 1; + EndOfHost = YoriLibFindLeftMostCharacter(&HostSubset, '/'); + if (EndOfHost != NULL) { + HostSubset.LengthInChars = (YORI_ALLOC_SIZE_T)(EndOfHost - HostSubset.StartOfString); + } else { + return FALSE; + } + + YoriLibInitEmptyString(&Request); + YoriLibYPrintf(&Request, + _T("GET %s HTTP/1.0\r\n%y\r\nUser-Agent: %y(YoriWinInet %i.%02i)\r\n\r\n"), + EndOfHost, + &UrlRequest->u.Url.UserRequestHeaders, + &UrlRequest->u.Url.InternetHandle->u.Internet.UserAgent, + YORI_VER_MAJOR, YORI_VER_MINOR); + + if (Request.StartOfString == NULL) { + return FALSE; + } + + AnsiBuffer = YoriLibMalloc(HostSubset.LengthInChars + 1); + if (AnsiBuffer == NULL) { + YoriLibFreeStringContents(&Request); + return FALSE; + } + + YoriLibSPrintfA(AnsiBuffer, "%y", &HostSubset); + addr = DllWsock32.pgethostbyname(AnsiBuffer); + if (addr == NULL || addr->h_addrtype != AF_INET || addr->h_length != sizeof(DWORD)) { + YoriLibFree(AnsiBuffer); + YoriLibFreeStringContents(&Request); + return FALSE; + } + + YoriLibFree(AnsiBuffer); + + s = DllWsock32.psocket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + YoriLibFreeStringContents(&Request); + return FALSE; + } + + ZeroMemory(&sin, sizeof(sin)); + + // MSFIX Probably should parse the port from the host name + sin.sin_family = AF_INET; + sin.sin_port = 0x5000; // 80, in hex, in big endian + memcpy(&sin.sin_addr.s_addr, addr->h_addr, addr->h_length); + + if (DllWsock32.pconnect(s, &sin, sizeof(sin)) != 0) { + YoriLibFreeStringContents(&Request); + DllWsock32.pclosesocket(s); + return FALSE; + } + + AnsiBuffer = YoriLibMalloc(Request.LengthInChars + 1); + if (AnsiBuffer == NULL) { + YoriLibFreeStringContents(&Request); + DllWsock32.pclosesocket(s); + return FALSE; + } + + YoriLibSPrintfA(AnsiBuffer, "%y", &Request); + if (DllWsock32.psend(s, AnsiBuffer, Request.LengthInChars, 0) != (int)Request.LengthInChars) { + YoriLibFree(AnsiBuffer); + YoriLibFreeStringContents(&Request); + DllWsock32.pclosesocket(s); + return FALSE; + } + + YoriLibFreeStringContents(&Request); + YoriLibFree(AnsiBuffer); + + YoriLibByteBufferReset(&UrlRequest->u.Url.ByteBuffer); + TotalLength = 0; + while(TRUE) { + AnsiBuffer = YoriLibByteBufferGetPointerToEnd(&UrlRequest->u.Url.ByteBuffer, 256 * 1024, &BytesRemaining); + Length = DllWsock32.precv(s, AnsiBuffer, (DWORD)BytesRemaining, 0); + if (Length == 0) { + break; + } + YoriLibByteBufferAddToPopulatedLength(&UrlRequest->u.Url.ByteBuffer, Length); + } + + DllWsock32.pclosesocket(s); + + if (!YoriLibHttpProcessResponseHeaders(UrlRequest, RedirectUrl)) { + return FALSE; + } + + // YoriLibHttpOutputUrlResponse(UrlRequest); + + return TRUE; +} + +/** + Munge an original URL and a Location redirect header into a fully specified + new URL. + + @param OriginalUrl The original URL that returned a redirect. + + @param LocationHeader The redirection URL returned from the original URL. + + @param RedirectUrl On successful completion, updated to contain a new URL + which is a fully specified location, removing any /../ components. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibHttpMergeRedirectUrl( + __in PYORI_STRING OriginalUrl, + __in PYORI_STRING LocationHeader, + __out PYORI_STRING RedirectUrl + ) +{ + YORI_STRING CombinedString; + YORI_ALLOC_SIZE_T EffectiveRoot; + YORI_ALLOC_SIZE_T CurrentReadIndex; + YORI_ALLOC_SIZE_T CurrentWriteIndex; + BOOLEAN PreviousWasSeperator; + + // + // There are three types of URLs to consider: + // - Absolute URL, where the location header is the complete URL + // - Host relative URL, starting in / - append the LocationHeader to the + // host part of the OriginalUrl + // - URL purely relative to the original URL + // + + if (YoriLibCompareStringLitInsCnt(LocationHeader, _T("http://"), sizeof("http://") - 1) == 0) { + YoriLibCloneString(RedirectUrl, LocationHeader); + return TRUE; + } + + YoriLibInitEmptyString(&CombinedString); + + // + // MSFIX Support host relative URLs. Should be simple but not currently + // used. + // + + if (LocationHeader->LengthInChars > 0 && + LocationHeader->StartOfString[0] == '/') { + + return FALSE; + } else { + + if (!YoriLibAllocateString(&CombinedString, OriginalUrl->LengthInChars + LocationHeader->LengthInChars + 1)) { + return FALSE; + } + + memcpy(CombinedString.StartOfString, OriginalUrl->StartOfString, OriginalUrl->LengthInChars * sizeof(TCHAR)); + CombinedString.LengthInChars = OriginalUrl->LengthInChars; + memcpy(&CombinedString.StartOfString[OriginalUrl->LengthInChars], LocationHeader->StartOfString, LocationHeader->LengthInChars * sizeof(TCHAR)); + CombinedString.LengthInChars = OriginalUrl->LengthInChars + LocationHeader->LengthInChars; + } + + // + // Check if we have a fully qualified URL. If not, this program doesn't + // know what to do next. + // + + if (YoriLibCompareStringLitInsCnt(&CombinedString, _T("http://"), sizeof("http://") - 1) != 0) { + YoriLibFreeStringContents(&CombinedString); + return FALSE; + } + + EffectiveRoot = sizeof("http://") - 1; + PreviousWasSeperator = FALSE; + + for (; EffectiveRoot < CombinedString.LengthInChars; EffectiveRoot++) { + if (CombinedString.StartOfString[EffectiveRoot] == '/') { + PreviousWasSeperator = TRUE; + break; + } + if (CombinedString.StartOfString[EffectiveRoot] == '?') { + break; + } + } + + CurrentWriteIndex = EffectiveRoot; + for (CurrentReadIndex = EffectiveRoot; CurrentReadIndex < CombinedString.LengthInChars; CurrentReadIndex++) { + + // + // If the component is /../, back up, but don't continue beyond + // EffectiveRoot + // + + if (PreviousWasSeperator && + CurrentReadIndex + 2 < CombinedString.LengthInChars && + CombinedString.StartOfString[CurrentReadIndex] == '.' && + CombinedString.StartOfString[CurrentReadIndex + 1] == '.' && + CombinedString.StartOfString[CurrentReadIndex + 2] == '/') { + + PreviousWasSeperator = FALSE; + + // + // Walk back one component or until the root + // + + CurrentWriteIndex--; + while (CurrentWriteIndex > EffectiveRoot) { + + CurrentWriteIndex--; + + if (CombinedString.StartOfString[CurrentWriteIndex] == '/') { + break; + } + } + + // + // If we were already on effective root when entering + // this block, we backed up one char too many already + // + + if (CurrentWriteIndex < EffectiveRoot) { + CurrentWriteIndex = EffectiveRoot; + } + + // + // If we get to the root, check if the previous + // char was a seperator. Messy because UNC and + // drive letters resolve this differently + // + + if (CurrentWriteIndex == EffectiveRoot) { + CurrentWriteIndex--; + if (CombinedString.StartOfString[CurrentWriteIndex] == '/') { + PreviousWasSeperator = TRUE; + } + CurrentWriteIndex++; + } + + CurrentReadIndex++; + continue; + } + + // + // Note if this is a seperator or not. If it's the final char and + // a seperator, drop it + // + + if (CombinedString.StartOfString[CurrentReadIndex] == '/') { + PreviousWasSeperator = TRUE; + } else { + PreviousWasSeperator = FALSE; + } + + CombinedString.StartOfString[CurrentWriteIndex] = CombinedString.StartOfString[CurrentReadIndex]; + CurrentWriteIndex++; + } + + CombinedString.StartOfString[CurrentWriteIndex] = '\0'; + CombinedString.LengthInChars = CurrentWriteIndex; + + memcpy(RedirectUrl, &CombinedString, sizeof(YORI_STRING)); + return TRUE; +} + +/** + Opens a specified URL resource. This includes downloading all contents. + + @param hInternet Handle to an internet resource opened with + @ref YoriLibInternetOpen . + + @param Url Specifies a NULL terminated URL to access. This library only + supports non-SSL HTTP connections. + + @param Headers Specifies any additional HTTP headers that should be supplied. + + @param HeadersLength The length of Headers, in characters. If -1, Headers + is assumed to be a NULL terminated string. + + @param Flags Not supported by this library; must be zero. + + @param Context Not supported by this library; must be zero. + + @return On successful completion, returns a handle to the URL resource. + On failure, returns NULL. The handle must be closed with + @ref YoriLibInternetCloseHandle . + */ +LPVOID WINAPI +YoriLibInternetOpenUrl( + __in LPVOID hInternet, + __in LPCTSTR Url, + __in LPCTSTR Headers, + __in DWORD HeadersLength, + __in DWORD Flags, + __in DWORD_PTR Context + ) +{ + PYORI_LIB_INTERNET_HANDLE UrlHandle; + PYORI_LIB_INTERNET_HANDLE Handle; + YORI_STRING RedirectUrl; + YORI_STRING LocationHeader; + YORI_ALLOC_SIZE_T Length; + + if (hInternet == NULL || Flags != 0 || Context != 0) { + return NULL; + } + Handle = (PYORI_LIB_INTERNET_HANDLE)hInternet; + if (Handle->HandleType != YoriLibInternetHandle) { + return NULL; + } + + UrlHandle = YoriLibMalloc(sizeof(YORI_LIB_INTERNET_HANDLE)); + if (UrlHandle == NULL) { + return NULL; + } + + ZeroMemory(UrlHandle, sizeof(YORI_LIB_INTERNET_HANDLE)); + UrlHandle->HandleType = YoriLibUrlHandle; + YoriLibInitializeListHead(&UrlHandle->u.Url.HttpResponseHeaders); + + Length = (YORI_ALLOC_SIZE_T)_tcslen(Url); + if (!YoriLibAllocateString(&UrlHandle->u.Url.Url, Length + 1)) { + YoriLibInternetCloseHandle(UrlHandle); + return NULL; + } + + memcpy(UrlHandle->u.Url.Url.StartOfString, Url, Length * sizeof(TCHAR)); + UrlHandle->u.Url.Url.LengthInChars = Length; + UrlHandle->u.Url.Url.StartOfString[Length] = '\0'; + + UrlHandle->u.Url.UserRequestHeaders.StartOfString = (LPTSTR)Headers; + if (HeadersLength == (DWORD)-1 && Headers != NULL) { + UrlHandle->u.Url.UserRequestHeaders.LengthInChars = (YORI_ALLOC_SIZE_T)_tcslen(Headers); + } else { + UrlHandle->u.Url.UserRequestHeaders.LengthInChars = (YORI_ALLOC_SIZE_T)HeadersLength; + } + + YoriLibTrimTrailingNewlines(&UrlHandle->u.Url.UserRequestHeaders); + UrlHandle->u.Url.InternetHandle = Handle; + + if (!YoriLibByteBufferInitialize(&UrlHandle->u.Url.ByteBuffer, 1024 * 1024)) { + YoriLibInternetCloseHandle(UrlHandle); + return NULL; + } + + while (TRUE) { + YoriLibHttpResetUrlRequest(UrlHandle); + YoriLibInitEmptyString(&LocationHeader); + if (!YoriLibHttpProcessUrlRequest(UrlHandle, &LocationHeader)) { + YoriLibInternetCloseHandle(UrlHandle); + return NULL; + } + + if (LocationHeader.StartOfString == NULL) { + break; + } + + YoriLibInitEmptyString(&RedirectUrl); + if (!YoriLibHttpMergeRedirectUrl(&UrlHandle->u.Url.Url, &LocationHeader, &RedirectUrl)) { + YoriLibFreeStringContents(&LocationHeader); + YoriLibInternetCloseHandle(UrlHandle); + return NULL; + } + + YoriLibFreeStringContents(&LocationHeader); + YoriLibFreeStringContents(&UrlHandle->u.Url.Url); + YoriLibCloneString(&UrlHandle->u.Url.Url, &RedirectUrl); + YoriLibFreeStringContents(&RedirectUrl); + } + + return UrlHandle; +} + + +/** + Reads data from a successful handle opened via @ref YoriLibInternetOpenUrl . + + @param hRequest A handle returned from @ref YoriLibInternetOpenUrl . + + @param Buffer On successful completion, updated to contain data read from the + handle. + + @param BytesToRead The maximum number of bytes that can be read into Buffer. + + @param BytesRead On successful completion, updated to contain the number of + bytes successfully read into Buffer. This may be less than + BytesToRead . + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL WINAPI +YoriLibInternetReadFile( + __in LPVOID hRequest, + __out_bcount(BytesToRead) LPVOID Buffer, + __in DWORD BytesToRead, + __out PDWORD BytesRead + ) +{ + PYORI_LIB_INTERNET_HANDLE UrlHandle; + DWORDLONG BufferOffset; + DWORD BytesToCopy; + PUCHAR Source; + + UrlHandle = (PYORI_LIB_INTERNET_HANDLE)hRequest; + + if (UrlHandle->HandleType != YoriLibUrlHandle) { + return FALSE; + } + + BufferOffset = UrlHandle->u.Url.HttpBodyOffset; + BufferOffset = BufferOffset + UrlHandle->u.Url.CurrentReadOffset; + + BytesToCopy = BytesToRead; + if (BufferOffset + BytesToCopy > UrlHandle->u.Url.ByteBuffer.BytesPopulated) { + BytesToCopy = (DWORD)(UrlHandle->u.Url.ByteBuffer.BytesPopulated - BufferOffset); + } + + if (BytesToCopy == 0) { + *BytesRead = 0; + return TRUE; + } + + Source = YoriLibAddToPointer(UrlHandle->u.Url.ByteBuffer.Buffer, BufferOffset); + memcpy(Buffer, Source, BytesToCopy); + *BytesRead = BytesToCopy; + + UrlHandle->u.Url.CurrentReadOffset = UrlHandle->u.Url.CurrentReadOffset + BytesToCopy; + return TRUE; +} + +/** + Query information associated with an HTTP request. + + @param hRequest A handle returned from @ref YoriLibInternetOpenUrl . + + @param InfoLevel The type of information requested. + + @param Buffer On successful completion, populated with the requested + information. + + @param BufferLength Specifies the length of Buffer, in bytes. On successful + completion, updated to indicate the number of bytes copied into + Buffer. + + @param Index Pointer to a zero based header index for repeated queries. Not + supported by this library. + + @return TRUE to indicate successful completion, FALSE to indicate failure. + */ +__success(return) +BOOL WINAPI +YoriLibHttpQueryInfo( + __in LPVOID hRequest, + __in DWORD InfoLevel, + __out LPVOID Buffer, + __inout PDWORD BufferLength, + __inout PDWORD Index + ) +{ + DWORD InfoLevelModifier; + DWORD InfoLevelIndex; + PYORI_LIB_INTERNET_HANDLE UrlHandle; + PDWORD OutputNumber; + + UrlHandle = (PYORI_LIB_INTERNET_HANDLE)hRequest; + + if (UrlHandle->HandleType != YoriLibUrlHandle) { + return FALSE; + } + + InfoLevelModifier = (InfoLevel & 0xF0000000); + InfoLevelIndex = (InfoLevel & 0x0000FFFF); + + if (InfoLevelModifier != HTTP_QUERY_FLAG_NUMBER || + InfoLevelIndex != HTTP_QUERY_STATUS_CODE) { + + return FALSE; + } + + if (Buffer == NULL || + BufferLength == NULL || + (*BufferLength) < sizeof(DWORD)) { + + return FALSE; + } + + if (Index != NULL) { + *Index = 0; + } + + OutputNumber = (PDWORD)Buffer; + *OutputNumber = UrlHandle->u.Url.HttpStatusCode; + *BufferLength = sizeof(DWORD); + + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/iconv.c b/misc/tools/yori/yori/lib/iconv.c index 7ca1f8e0c3..cec28de501 100644 --- a/misc/tools/yori/yori/lib/iconv.c +++ b/misc/tools/yori/yori/lib/iconv.c @@ -146,7 +146,7 @@ YoriLibSetMultibyteInputEncoding( @return The number of bytes needed to store the output form. */ YORI_ALLOC_SIZE_T -YoriLibGetMultibyteOutputSizeNeeded( +YoriLibGetMbyteOutputSizeNeeded( __in LPCTSTR StringBuffer, __in YORI_ALLOC_SIZE_T BufferLength ) diff --git a/misc/tools/yori/yori/lib/jobobj.c b/misc/tools/yori/yori/lib/jobobj.c new file mode 100644 index 0000000000..ff312410fa --- /dev/null +++ b/misc/tools/yori/yori/lib/jobobj.c @@ -0,0 +1,86 @@ +/** + * @file lib/jobobj.c + * + * Yori wrappers around Windows Job Object functionality. Loads dynamically + * to allow for fallback if the host OS doesn't support it. + * + * Copyright (c) 2017 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Attempt to create an unnamed job object. If the functionality is not + supported by the host OS, returns NULL. + + @return Handle to a job object, or NULL on failure. + */ +HANDLE +YoriLibCreateJobObject(VOID) +{ + if (DllKernel32.pCreateJobObjectW == NULL) { + return NULL; + } + return DllKernel32.pCreateJobObjectW(NULL, NULL); +} + +/** + Assign a process to a job object. If the functionality is not + supported by the host OS, returns FALSE. + + @return TRUE on success, FALSE on failure. + */ +BOOL +YoriLibAssignProcessToJobObject( + __in HANDLE hJob, + __in HANDLE hProcess + ) +{ + if (DllKernel32.pAssignProcessToJobObject == NULL) { + return FALSE; + } + return DllKernel32.pAssignProcessToJobObject(hJob, hProcess); +} + +/** + Set the process priority to be used by a job object. If this functionality + is not supported by the host OS, returns FALSE. + + @return TRUE on success, FALSE on failure. + */ +BOOL +YoriLibLimitJobObjectPriority( + __in HANDLE hJob, + __in DWORD Priority + ) +{ + YORI_JOB_BASIC_LIMIT_INFORMATION LimitInfo; + if (DllKernel32.pSetInformationJobObject == NULL) { + return FALSE; + } + ZeroMemory(&LimitInfo, sizeof(LimitInfo)); + LimitInfo.Flags = 0x20; + LimitInfo.Priority = Priority; + return DllKernel32.pSetInformationJobObject(hJob, 2, &LimitInfo, sizeof(LimitInfo)); +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/lineread.c b/misc/tools/yori/yori/lib/lineread.c index ebad10100d..1e2f6fec4d 100644 --- a/misc/tools/yori/yori/lib/lineread.c +++ b/misc/tools/yori/yori/lib/lineread.c @@ -117,7 +117,7 @@ YoriLibCopyLineToUserBufferW( if (CharsNeeded > UserString->LengthAllocated) { UserString->LengthInChars = 0; - if (!YoriLibReallocateString(UserString, CharsNeeded + 64)) { + if (!YoriLibReallocString(UserString, CharsNeeded + 64)) { return FALSE; } } @@ -708,8 +708,6 @@ YoriLibReadLineToStringEx( break; } - ResetEvent(FileHandle); - // // Note that this delay is not exercised once the process // starts pushing data into the pipe. Think of this as diff --git a/misc/tools/yori/yori/lib/movefile.c b/misc/tools/yori/yori/lib/movefile.c new file mode 100644 index 0000000000..be4d36d8cf --- /dev/null +++ b/misc/tools/yori/yori/lib/movefile.c @@ -0,0 +1,275 @@ +/** + * @file lib/movefile.c + * + * Yori shell move or rename a file + * + * Copyright (c) 2017-2020 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, MOVEESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +/** + Rename or move a file or directory. This routine will copy and replace + files as needed to complete the request. + + @param Source Pointer to the source of the rename. + + @param FullDest Pointer to the target of the rename. + + @param ReplaceExisting If TRUE, any existing file is overwritten. If FALSE, + existing files are retained and the call fails with last error set + appropriately. + + @param PosixSemantics If TRUE, the rename should apply POSIX semantics to + remove an in use file from the namespace immediately. If FALSE, Win32 + semantics are applied. + + @return Win32 error code, ERROR_SUCCESS to indicate success, or appropriate + Win32 error. + */ +DWORD +YoriLibMoveFile( + __in PYORI_STRING Source, + __in PYORI_STRING FullDest, + __in BOOLEAN ReplaceExisting, + __in BOOLEAN PosixSemantics + ) +{ + DWORD OsMajor; + DWORD OsMinor; + DWORD OsBuild; + DWORD Flags; + DWORD Error; + DWORD Attributes; + DWORD NewAttributes; + + ASSERT(YoriLibIsStringNullTerminated(Source)); + ASSERT(YoriLibIsStringNullTerminated(FullDest)); + + if (PosixSemantics) { + PYORI_FILE_RENAME_INFO RenameInfo; + YORI_ALLOC_SIZE_T RenameInfoSize; + HANDLE hFile; + + if (DllKernel32.pSetFileInformationByHandle == NULL) { + return ERROR_PROC_NOT_FOUND; + } + + hFile = CreateFile(Source->StartOfString, + DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return GetLastError(); + } + + // + // The string requires NULL termination and the structure includes + // one character, so allocate the structure plus the length of the + // string. + // + + RenameInfoSize = sizeof(YORI_FILE_RENAME_INFO) + FullDest->LengthInChars * sizeof(TCHAR); + RenameInfo = YoriLibMalloc(RenameInfoSize); + if (RenameInfo == NULL) { + CloseHandle(hFile); + return ERROR_NOT_ENOUGH_MEMORY; + } + + RenameInfo->Flags = FILE_RENAME_FLAG_POSIX_SEMANTICS; + if (ReplaceExisting) { + RenameInfo->Flags = RenameInfo->Flags | FILE_RENAME_FLAG_REPLACE_IF_EXISTS; + } + + RenameInfo->RootDirectory = NULL; + RenameInfo->FileNameLength = FullDest->LengthInChars * sizeof(TCHAR); + memcpy(RenameInfo->FileName, FullDest->StartOfString, FullDest->LengthInChars * sizeof(TCHAR)); + RenameInfo->FileName[FullDest->LengthInChars] = '\0'; + + Error = ERROR_SUCCESS; + if (!DllKernel32.pSetFileInformationByHandle(hFile, FileRenameInfoEx, RenameInfo, RenameInfoSize)) { + Error = GetLastError(); + if (ReplaceExisting && Error == ERROR_ACCESS_DENIED) { + Attributes = GetFileAttributes(FullDest->StartOfString); + if (Attributes != (DWORD)-1 && + (Attributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0) { + + NewAttributes = Attributes & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + if (SetFileAttributes(FullDest->StartOfString, NewAttributes)) { + if (!DllKernel32.pSetFileInformationByHandle(hFile, FileRenameInfoEx, RenameInfo, RenameInfoSize)) { + SetFileAttributes(FullDest->StartOfString, Attributes); + } else { + Error = ERROR_SUCCESS; + } + } + } + } + } + + CloseHandle(hFile); + YoriLibFree(RenameInfo); + + // + // If the rename would cross volumes, this must be implemented by + // copy/delete, which SetFileInformationByHandle doesn't implement, + // so give up on POSIX and try boring Win32. + // + + if (Error != ERROR_NOT_SAME_DEVICE) { + return Error; + } + } + + Flags = MOVEFILE_COPY_ALLOWED; + if (ReplaceExisting) { + Flags = Flags | MOVEFILE_REPLACE_EXISTING; + } + + if (!MoveFileEx(Source->StartOfString, FullDest->StartOfString, Flags)) { + Error = GetLastError(); + if (ReplaceExisting && Error == ERROR_ACCESS_DENIED) { + Attributes = GetFileAttributes(FullDest->StartOfString); + if (Attributes != (DWORD)-1 && + (Attributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0) { + + NewAttributes = Attributes & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + if (!SetFileAttributes(FullDest->StartOfString, NewAttributes)) { + return Error; + } + + if (!MoveFileEx(Source->StartOfString, FullDest->StartOfString, Flags)) { + SetFileAttributes(FullDest->StartOfString, Attributes); + return Error; + } + Error = ERROR_SUCCESS; + } + } + + if (Error != ERROR_SUCCESS) { + return Error; + } + } + + YoriLibGetOsVersion(&OsMajor, &OsMinor, &OsBuild); + + // + // Windows 2000 and above claim to support inherited ACLs, except they + // really depend on applications to perform the inheritance. On these + // systems, attempt to reset the ACL on the target, which really resets + // security and picks up state from the parent. Don't do this on older + // systems, because that will result in a file with no ACL. Note this + // can fail due to not having access on the file, or a file system + // that doesn't support security, or because the file is no longer there, + // so errors here are just ignored. Nano is missing most of AdvApi32, + // so this code currently doesn't run there. + // + + if (OsMajor >= 5 && + DllAdvApi32.pSetNamedSecurityInfoW != NULL && + DllAdvApi32.pInitializeAcl != NULL) { + + ACL EmptyAcl; + + memset(&EmptyAcl, 0, sizeof(EmptyAcl)); + + DllAdvApi32.pInitializeAcl(&EmptyAcl, sizeof(EmptyAcl), ACL_REVISION); + DllAdvApi32.pSetNamedSecurityInfoW(FullDest->StartOfString, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, &EmptyAcl, NULL); + } + + return ERROR_SUCCESS; +} + +/** + Call CopyFile, and if the operation fails, check if it's due to readonly, + hidden or system attributes on the target, clear those and retry. + + @param SourceFile The source file name, expected to be NULL terminated. + + @param DestFile The destination file name, expected to be NULL terminated. + + @return The Win32 error code, possibly ERROR_SUCCESS or appropriate error + on failure. + */ +DWORD +YoriLibCopyFile( + __in PYORI_STRING SourceFile, + __in PYORI_STRING DestFile + ) +{ + DWORD Error; + DWORD Attributes; + DWORD NewAttributes; + BOOL Result; + BOOL Cancelled; + + ASSERT(YoriLibIsStringNullTerminated(SourceFile)); + ASSERT(YoriLibIsStringNullTerminated(DestFile)); + + if (DllKernel32.pCopyFileW == NULL && + DllKernel32.pCopyFileExW == NULL) { + + return ERROR_PROC_NOT_FOUND; + } + + Error = ERROR_SUCCESS; + if (DllKernel32.pCopyFileW != NULL) { + Result = DllKernel32.pCopyFileW(SourceFile->StartOfString, DestFile->StartOfString, FALSE); + } else { + Cancelled = FALSE; + Result = DllKernel32.pCopyFileExW(SourceFile->StartOfString, DestFile->StartOfString, NULL, NULL, &Cancelled, 0); + } + if (!Result) { + Error = GetLastError(); + if (Error == ERROR_ACCESS_DENIED) { + Attributes = GetFileAttributes(DestFile->StartOfString); + if (Attributes != (DWORD)-1 && + (Attributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0) { + + NewAttributes = Attributes & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + if (!SetFileAttributes(DestFile->StartOfString, NewAttributes)) { + return Error; + } + + if (DllKernel32.pCopyFileW != NULL) { + Result = DllKernel32.pCopyFileW(SourceFile->StartOfString, DestFile->StartOfString, FALSE); + } else { + Cancelled = FALSE; + Result = DllKernel32.pCopyFileExW(SourceFile->StartOfString, DestFile->StartOfString, NULL, NULL, &Cancelled, 0); + } + + if (!Result) { + SetFileAttributes(DestFile->StartOfString, Attributes); + return Error; + } + Error = ERROR_SUCCESS; + } + } + } + + return Error; +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/obenum.c b/misc/tools/yori/yori/lib/obenum.c new file mode 100644 index 0000000000..c31567eab8 --- /dev/null +++ b/misc/tools/yori/yori/lib/obenum.c @@ -0,0 +1,265 @@ +/** + * @file lib/obenum.c + * + * Yori shell enumerate object manager objects + * + * Copyright (c) 2017-2022 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +#include +#include + +/** + Initialize an OBJECT_ATTRIBUTES structure. This is patterned on the + "official" InitializeObjectAttributes, but since it needed to be redone + anyway this takes the liberty of simplifying a YORI_STRING to UNICODE_STRING + conversion. + + @param ObjectAttributes On successful completion, populated with object + attributes that can be used to call NT APIs. + + @param RootDirectory Handle to an object which Name is being opened relative + to. If NULL, Name is a fully specified path. + + @param Name The name of the object to open. + + @param Attributes Attributes for the open. + */ +VOID +YoriLibInitializeObjectAttributes( + __out PYORI_OBJECT_ATTRIBUTES ObjectAttributes, + __in_opt HANDLE RootDirectory, + __in_opt PCYORI_STRING Name, + __in DWORD Attributes + ) +{ + ObjectAttributes->Length = FIELD_OFFSET(YORI_OBJECT_ATTRIBUTES, NameStorage); + ObjectAttributes->RootDirectory = RootDirectory; + ObjectAttributes->Attributes = Attributes; + ObjectAttributes->SecurityDescriptor = NULL; + ObjectAttributes->SecurityQOS = NULL; + + if (Name == NULL) { + ObjectAttributes->Name = NULL; + } else { + ObjectAttributes->Name = &ObjectAttributes->NameStorage; + ObjectAttributes->NameStorage.LengthInBytes = (WORD)(Name->LengthInChars * sizeof(TCHAR)); + ObjectAttributes->NameStorage.LengthAllocatedInBytes = (WORD)(Name->LengthAllocated * sizeof(TCHAR)); + ObjectAttributes->NameStorage.Buffer = Name->StartOfString; + } +} + +/** + Structure definition for an object manager directory entry. + */ +typedef struct _YORI_OBJECT_DIRECTORY_INFORMATION { + + /** + The name of the object. + */ + YORI_UNICODE_STRING ObjectName; + + /** + The type of the object. + */ + YORI_UNICODE_STRING ObjectType; +} YORI_OBJECT_DIRECTORY_INFORMATION, *PYORI_OBJECT_DIRECTORY_INFORMATION; + +/** + Enumerate all entries within an object manager directory and call a callback + function for each entry found. + + @param DirectoryName Pointer to the object manager directory to enumerate. + + @param MatchFlags Flags to apply to the enumeration. + + @param Callback Pointer to a callback function to invoke for each entry. + + @param ErrorCallback Pointer to a callback to invoke if any errors are + encountered. + + @param Context An opaque context pointer that will be supplied to the + callback functions. + + @return TRUE to indicate all objects were successfully enumerated, FALSE to + indicate that not all entries could be enumerated. + */ +__success(return) +BOOL +YoriLibForEachObjectEnum( + __in PCYORI_STRING DirectoryName, + __in DWORD MatchFlags, + __in PYORILIB_OBJECT_ENUM_FN Callback, + __in_opt PYORILIB_OBJECT_ENUM_ERROR_FN ErrorCallback, + __in_opt PVOID Context + ) +{ + LONG NtStatus; + HANDLE DirHandle; + YORI_OBJECT_ATTRIBUTES ObjectAttributes; + PYORI_OBJECT_DIRECTORY_INFORMATION Buffer; + PYORI_OBJECT_DIRECTORY_INFORMATION Entry; + YORI_ALLOC_SIZE_T NameOnlyOffset; + YORI_ALLOC_SIZE_T BufferSize; + DWORD EnumContext; + DWORD BytesReturned; + BOOLEAN Restart; + + YORI_STRING FullObjectName; + YORI_STRING ObjectName; + YORI_STRING ObjectType; + + UNREFERENCED_PARAMETER(Callback); + UNREFERENCED_PARAMETER(MatchFlags); + + if (DllNtDll.pNtOpenDirectoryObject == NULL || + DllNtDll.pNtQueryDirectoryObject == NULL) { + + return FALSE; + } + + YoriLibInitializeObjectAttributes(&ObjectAttributes, NULL, DirectoryName, 0); + + NtStatus = DllNtDll.pNtOpenDirectoryObject(&DirHandle, DIRECTORY_QUERY, &ObjectAttributes); + if (NtStatus != 0) { + if (ErrorCallback != NULL) { + ErrorCallback(DirectoryName, NtStatus, Context); + } + return FALSE; + } + + // + // This is really because the API is lacking annotations. If we got + // here, it succeeded. + // + + __analysis_assume(DirHandle != NULL); + + BufferSize = 60 * 1024; + Buffer = YoriLibMalloc(BufferSize); + if (Buffer == NULL) { + CloseHandle(DirHandle); + if (ErrorCallback != NULL) { + ErrorCallback(DirectoryName, STATUS_INSUFFICIENT_RESOURCES, Context); + } + return FALSE; + } + + EnumContext = 0; + Restart = TRUE; + YoriLibInitEmptyString(&FullObjectName); + YoriLibInitEmptyString(&ObjectName); + YoriLibInitEmptyString(&ObjectType); + NameOnlyOffset = 0; + + // + // Loop filling a buffer with entries, then processing the entries + // + + while (TRUE) { + NtStatus = DllNtDll.pNtQueryDirectoryObject(DirHandle, Buffer, BufferSize, FALSE, Restart, &EnumContext, &BytesReturned); + + // + // If there are no more entries, enumeration is complete + // + + if (NtStatus == STATUS_NO_MORE_ENTRIES) { + break; + } + + // + // If an error is returned that's not indicating more entries (which + // is a success code), indicate the problem and exit. + // + + if (NtStatus != 0 && NtStatus != STATUS_MORE_ENTRIES) { + if (ErrorCallback != NULL) { + ErrorCallback(DirectoryName, NtStatus, Context); + } + YoriLibFreeStringContents(&FullObjectName); + YoriLibFree(Buffer); + CloseHandle(DirHandle); + return FALSE; + } + + Restart = FALSE; + + // + // The buffer is filled with UNICODE_STRING structures from the + // front, followed by string buffers. This means BytesReturned isn't + // a valid way to know how many string entries there are. This API + // indicates the termination condition by having a UNICODE_STRING + // of empty strings. + // + + Entry = Buffer; + while (Entry->ObjectName.LengthInBytes != 0) { + ObjectName.StartOfString = Entry->ObjectName.Buffer; + ObjectName.LengthInChars = Entry->ObjectName.LengthInBytes / sizeof(TCHAR); + ObjectType.StartOfString = Entry->ObjectType.Buffer; + ObjectType.LengthInChars = Entry->ObjectType.LengthInBytes / sizeof(TCHAR); + + // + // Construct a buffer for a full path name if necessary. This + // can be reused across entries so long as the name component + // fits. Keep the directory component unchanged across new + // name components. + // + + if (FullObjectName.LengthAllocated < DirectoryName->LengthInChars + 1 + ObjectName.LengthInChars + 1) { + YoriLibFreeStringContents(&FullObjectName); + if (!YoriLibAllocateString(&FullObjectName, DirectoryName->LengthInChars + 1 + ObjectName.LengthInChars + 1 + 100)) { + YoriLibFree(Buffer); + CloseHandle(DirHandle); + if (ErrorCallback != NULL) { + ErrorCallback(DirectoryName, STATUS_INSUFFICIENT_RESOURCES, Context); + } + return FALSE; + } + + FullObjectName.LengthInChars = YoriLibSPrintfS(FullObjectName.StartOfString, FullObjectName.LengthAllocated, _T("%y"), DirectoryName); + if (FullObjectName.LengthInChars > 0 && + !YoriLibIsSep(FullObjectName.StartOfString[FullObjectName.LengthInChars - 1])) { + FullObjectName.StartOfString[FullObjectName.LengthInChars] = '\\'; + FullObjectName.LengthInChars = FullObjectName.LengthInChars + 1; + } + + NameOnlyOffset = FullObjectName.LengthInChars; + } + + memcpy(&FullObjectName.StartOfString[NameOnlyOffset], ObjectName.StartOfString, ObjectName.LengthInChars * sizeof(TCHAR)); + FullObjectName.LengthInChars = NameOnlyOffset + ObjectName.LengthInChars; + FullObjectName.StartOfString[FullObjectName.LengthInChars] = '\0'; + + if (!Callback(&FullObjectName, &ObjectName, &ObjectType, Context)) { + break; + } + Entry++; + } + } + + YoriLibFreeStringContents(&FullObjectName); + YoriLibFree(Buffer); + CloseHandle(DirHandle); + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/osver.c b/misc/tools/yori/yori/lib/osver.c index 552287e07e..0a968090a3 100644 --- a/misc/tools/yori/yori/lib/osver.c +++ b/misc/tools/yori/yori/lib/osver.c @@ -588,7 +588,7 @@ YoriLibDoesSystemSupportBackgroundColors(VOID) YORI_MAX_SIGNED_T Enabled; YoriLibBackgroundColorSupported = FALSE; - if (!YoriLibGetEnvironmentVariableAsNumber(_T("YORIBACKGROUND"), &Enabled)) { + if (!YoriLibGetEnvVarAsNumber(_T("YORIBACKGROUND"), &Enabled)) { Enabled = 0; } diff --git a/misc/tools/yori/yori/lib/priv.c b/misc/tools/yori/yori/lib/priv.c new file mode 100644 index 0000000000..b5f934d734 --- /dev/null +++ b/misc/tools/yori/yori/lib/priv.c @@ -0,0 +1,170 @@ +/** + * @file lib/priv.c + * + * Yori privilege manipulation routines + * + * Copyright (c) 2014-2019 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Attempt to enable a specified privilege. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableNamedPrivilege( + LPTSTR PrivilegeName + ) +{ + HANDLE ProcessToken; + DWORD Err; + + YoriLibLoadAdvApi32Functions(); + + // + // Attempt to enable backup privilege. This allows us to enumerate and recurse + // through objects which normally ACLs would prevent. + // + + if (DllAdvApi32.pOpenProcessToken != NULL && + DllAdvApi32.pLookupPrivilegeValueW != NULL && + DllAdvApi32.pAdjustTokenPrivileges != NULL && + DllAdvApi32.pOpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &ProcessToken)) { + struct { + TOKEN_PRIVILEGES TokenPrivileges; + LUID_AND_ATTRIBUTES BackupPrivilege; + } PrivilegesToChange; + + if (!DllAdvApi32.pLookupPrivilegeValueW(NULL, PrivilegeName, &PrivilegesToChange.TokenPrivileges.Privileges[0].Luid)) { + CloseHandle(ProcessToken); + return FALSE; + } + + PrivilegesToChange.TokenPrivileges.PrivilegeCount = 1; + PrivilegesToChange.TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!DllAdvApi32.pAdjustTokenPrivileges(ProcessToken, FALSE, (PTOKEN_PRIVILEGES)&PrivilegesToChange, sizeof(PrivilegesToChange), NULL, NULL)) { + CloseHandle(ProcessToken); + return FALSE; + } + + Err = GetLastError(); + CloseHandle(ProcessToken); + if (Err == ERROR_NOT_ALL_ASSIGNED) { + return FALSE; + } + return TRUE; + } + + return FALSE; +} + +/** + Attempt to enable backup privilege to allow Administrators to enumerate and + open more objects successfully. If this fails the application may encounter + more objects it cannot accurately account for, but it is not fatal, or + unexpected. + + @return TRUE to indicate that the privilege enablement was attempted + successfully. + */ +BOOL +YoriLibEnableBackupPrivilege(VOID) +{ + YoriLibEnableNamedPrivilege(SE_BACKUP_NAME); + return TRUE; +} + +/** + Attempt to enable debug privilege to allow Administrators to take kernel + dumps. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableDebugPrivilege(VOID) +{ + return YoriLibEnableNamedPrivilege(SE_DEBUG_NAME); +} + +/** + Attempt to enable manage volume privilege to mount VHD files. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableManageVolumePrivilege(VOID) +{ + return YoriLibEnableNamedPrivilege(SE_MANAGE_VOLUME_NAME); +} + + +/** + Attempt to enable shutdown privilege to the system to shut down. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableShutdownPrivilege(VOID) +{ + return YoriLibEnableNamedPrivilege(SE_SHUTDOWN_NAME); +} + +/** + Attempt to enable symbolic link privilege to allow creation of symbolic + links. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableSymbolicLinkPrivilege(VOID) +{ + return YoriLibEnableNamedPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME); +} + +/** + Attempt to enable system time privilege to allow Administrators to change the + system clock. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableSystemTimePrivilege(VOID) +{ + return YoriLibEnableNamedPrivilege(SE_SYSTEMTIME_NAME); +} + +/** + Attempt to enable take ownership privilege to allow Administrators update + system registry keys. + + @return TRUE to indicate that the privilege enablement was successful. + */ +BOOL +YoriLibEnableTakeOwnershipPrivilege(VOID) +{ + return YoriLibEnableNamedPrivilege(SE_TAKE_OWNERSHIP_NAME); +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/progman.c b/misc/tools/yori/yori/lib/progman.c new file mode 100644 index 0000000000..dbf2453a3b --- /dev/null +++ b/misc/tools/yori/yori/lib/progman.c @@ -0,0 +1,288 @@ +/** + * @file lib/progman.c + * + * Yori Program Manager DDE interfaces + * + * Copyright (c) 2021 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Pointless callback because DDEML expects it. + + @param Type Ignored. + + @param Fmt Ignored. + + @param ConvHandle Ignored. + + @param StringHandle1 Ignored. + + @param StringHandle2 Ignored. + + @param DataHandle Ignored. + + @param Data1 Ignored. + + @param Data2 Ignored. + + @return NULL. + */ +HDDEDATA CALLBACK +YoriLibDdeCallback( + __in DWORD Type, + __in DWORD Fmt, + __in HCONV ConvHandle, + __in HSZ StringHandle1, + __in HSZ StringHandle2, + __in HDDEDATA DataHandle, + __in DWORD_PTR Data1, + __in DWORD_PTR Data2 + ) +{ + UNREFERENCED_PARAMETER(Type); + UNREFERENCED_PARAMETER(Fmt); + UNREFERENCED_PARAMETER(ConvHandle); + UNREFERENCED_PARAMETER(StringHandle1); + UNREFERENCED_PARAMETER(StringHandle2); + UNREFERENCED_PARAMETER(DataHandle); + UNREFERENCED_PARAMETER(Data1); + UNREFERENCED_PARAMETER(Data2); + return (HDDEDATA)NULL; +} + +/** + Check if the string contains characters which would need to be escaped to + communicate over DDE. + + @param String Pointer to the string to check. + + @return TRUE to indicate the string contains invalid chars, FALSE if it + does not. + */ +BOOL +YoriLibDoesStringContainDDEInvalidChars( + __in PCYORI_STRING String + ) +{ + DWORD Index; + + // + // A string better not contain an argument seperator or command + // terminator character. + // + + for (Index = 0; Index < String->LengthInChars; Index++) { + if (String->StartOfString[Index] == ',' || + String->StartOfString[Index] == ')') { + + return TRUE; + } + } + + return FALSE; +} + +/** + Create or modify a Program Manager item. + + @param GroupName Pointer to the name of the Program Manager group. + + @param ItemName The name of the Program Manager item. + + @param ItemPath The path that the Program Manager item should point to. + + @param WorkingDirectory If specified, the current directory to set when + launching the executable. + + @param IconPath If specified, the path to the binary containing the icon for + the shortcut. + + @param IconIndex The index of the icon within any executable or DLL used as + the source of the icon. This is ignored unless IconPath is specified. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibAddProgmanItem( + __in PCYORI_STRING GroupName, + __in PCYORI_STRING ItemName, + __in PCYORI_STRING ItemPath, + __in_opt PCYORI_STRING WorkingDirectory, + __in_opt PCYORI_STRING IconPath, + __in DWORD IconIndex + ) +{ + HSZ ProgmanHandle; + HDDEDATA CommandHandle; + HCONV ConvHandle; + DWORD DdeInstance; + DWORD Result; + CONVCONTEXT ConvContext; + YORI_STRING CommandString; + + // + // Check for characters that can't be communicated over DDE. There's an + // escaping protocol for these, but it hardly seems worth implementing + // given that this function is very unlikely to see these characters. + // + + if (YoriLibDoesStringContainDDEInvalidChars(GroupName) || + YoriLibDoesStringContainDDEInvalidChars(ItemName) || + YoriLibDoesStringContainDDEInvalidChars(ItemPath)) { + + return FALSE; + } + + if (WorkingDirectory != NULL && YoriLibDoesStringContainDDEInvalidChars(WorkingDirectory)) { + return FALSE; + } + + if (IconPath != NULL && YoriLibDoesStringContainDDEInvalidChars(IconPath)) { + return FALSE; + } + + YoriLibLoadUser32Functions(); + + if (DllUser32.pDdeClientTransaction == NULL || + DllUser32.pDdeConnect == NULL || + DllUser32.pDdeCreateDataHandle == NULL || + DllUser32.pDdeCreateStringHandleW == NULL || + DllUser32.pDdeDisconnect == NULL || + DllUser32.pDdeFreeStringHandle == NULL || + DllUser32.pDdeInitializeW == NULL || + DllUser32.pDdeUninitialize == NULL) { + + return FALSE; + } + + // + // Initialize DDE and connect to Program Manager. + // + + DdeInstance = 0; + if (DllUser32.pDdeInitializeW(&DdeInstance, (PFNCALLBACK)YoriLibDdeCallback, APPCMD_CLIENTONLY, 0) != DMLERR_NO_ERROR) { + return FALSE; + } + + ProgmanHandle = DllUser32.pDdeCreateStringHandleW(DdeInstance, _T("PROGMAN"), CP_WINUNICODE); + if (ProgmanHandle == (HSZ)NULL) { + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + ZeroMemory(&ConvContext, sizeof(ConvContext)); + ConvContext.cb = sizeof(ConvContext); + ConvHandle = DllUser32.pDdeConnect(DdeInstance, ProgmanHandle, ProgmanHandle, &ConvContext); + DllUser32.pDdeFreeStringHandle(DdeInstance, ProgmanHandle); + if (ConvHandle == (HCONV)NULL) { + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + // + // Create or switch to the group. + // + + YoriLibInitEmptyString(&CommandString); + YoriLibYPrintf(&CommandString, _T("[CreateGroup(%y)]"), GroupName); + if (CommandString.StartOfString == NULL) { + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + CommandHandle = DllUser32.pDdeCreateDataHandle(DdeInstance, (LPBYTE)CommandString.StartOfString, (CommandString.LengthInChars + 1) * sizeof(TCHAR), 0, (HSZ)NULL, CF_UNICODETEXT, 0); + if (CommandHandle == (HDDEDATA)NULL) { + YoriLibFreeStringContents(&CommandString); + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + if (DllUser32.pDdeClientTransaction((LPBYTE)CommandHandle, 0xFFFFFFFF, ConvHandle, (HSZ)NULL, 0, XTYP_EXECUTE, 3000, &Result) == (HDDEDATA)NULL) { + // + // Note that CommandHandle may be leaked here. Avoiding this + // requires establishing what happened (whether it made it to + // the remote side or not.) + // + YoriLibFreeStringContents(&CommandString); + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + // + // At this point CommandHandle is owned by the remote side. + // + // Create the program item. + // + + CommandString.LengthInChars = 0; + if (IconPath != NULL && WorkingDirectory != NULL) { + YoriLibYPrintf(&CommandString, _T("[AddItem(\"%y\",%y,\"%y\",%i,-1,-1,\"%y\")]"), ItemPath, ItemName, IconPath, IconIndex, WorkingDirectory); + } else if (WorkingDirectory != NULL) { + YoriLibYPrintf(&CommandString, _T("[AddItem(\"%y\",%y,,,-1,-1,\"%y\")]"), ItemPath, ItemName, WorkingDirectory); + } else if (IconPath != NULL) { + YoriLibYPrintf(&CommandString, _T("[AddItem(\"%y\",%y,\"%y\",%i)]"), ItemPath, ItemName, IconPath, IconIndex); + } else { + YoriLibYPrintf(&CommandString, _T("[AddItem(\"%y\",%y)]"), ItemPath, ItemName); + } + + if (CommandString.LengthInChars == 0) { + YoriLibFreeStringContents(&CommandString); + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + CommandHandle = DllUser32.pDdeCreateDataHandle(DdeInstance, (LPBYTE)CommandString.StartOfString, (CommandString.LengthInChars + 1) * sizeof(TCHAR), 0, (HSZ)NULL, CF_UNICODETEXT, 0); + if (CommandHandle == (HDDEDATA)NULL) { + YoriLibFreeStringContents(&CommandString); + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + if (DllUser32.pDdeClientTransaction((LPBYTE)CommandHandle, 0xFFFFFFFF, ConvHandle, (HSZ)NULL, 0, XTYP_EXECUTE, 3000, &Result) == (HDDEDATA)NULL) { + // + // Note that CommandHandle may be leaked here. Avoiding this + // requires establishing what happened (whether it made it to + // the remote side or not.) + // + YoriLibFreeStringContents(&CommandString); + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + return FALSE; + } + + YoriLibFreeStringContents(&CommandString); + DllUser32.pDdeDisconnect(ConvHandle); + DllUser32.pDdeUninitialize(DdeInstance); + + return TRUE; +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/recycle.c b/misc/tools/yori/yori/lib/recycle.c new file mode 100644 index 0000000000..30314d0c3c --- /dev/null +++ b/misc/tools/yori/yori/lib/recycle.c @@ -0,0 +1,96 @@ +/** + * @file lib/recycle.c + * + * Yori shell send files to the recycle bin. + * + * Copyright (c) 2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS ERASE A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE ERASE ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + + +/** + Attempt to send an object to the recycle bin. + + @param FilePath Pointer to the file path to delete. + + @return TRUE if the object was sent to the recycle bin, FALSE if not. + */ +BOOL +YoriLibRecycleBinFile( + __in PYORI_STRING FilePath + ) +{ + YORI_SHFILEOP FileOp; + YORI_STRING FilePathWithDoubleNull; + INT Result; + + YoriLibLoadShell32Functions(); + + // + // If loading shell failed or we couldn't find the function, recycling + // won't happen. + // + + if (DllShell32.pSHFileOperationW == NULL) { + return FALSE; + } + + // + // Create a double NULL terminated file name. + // + + YoriLibInitEmptyString(&FilePathWithDoubleNull); + if (!YoriLibAllocateString(&FilePathWithDoubleNull, FilePath->LengthInChars + 2)) { + return FALSE; + } + + // + // Shell will explode if it sees \\?\, so try to reconvert back to + // Win32 limited paths. + // + + YoriLibUnescapePath(FilePath, &FilePathWithDoubleNull); + + ASSERT(FilePathWithDoubleNull.LengthAllocated >= FilePathWithDoubleNull.LengthInChars + 2); + FilePathWithDoubleNull.StartOfString[FilePathWithDoubleNull.LengthInChars] = '\0'; + FilePathWithDoubleNull.StartOfString[FilePathWithDoubleNull.LengthInChars + 1] = '\0'; + + // + // Ask shell to send the object to the recycle bin. + // + + ZeroMemory(&FileOp, sizeof(FileOp)); + FileOp.Function = YORI_SHFILEOP_DELETE; + FileOp.Source = FilePathWithDoubleNull.StartOfString; + FileOp.Flags = YORI_SHFILEOP_FLAG_SILENT|YORI_SHFILEOP_FLAG_NOCONFIRMATION|YORI_SHFILEOP_FLAG_ALLOWUNDO|YORI_SHFILEOP_FLAG_NOERRORUI; + + Result = DllShell32.pSHFileOperationW(&FileOp); + YoriLibFreeStringContents(&FilePathWithDoubleNull); + + if (Result == 0) { + return TRUE; + } + return FALSE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/rsrc.c b/misc/tools/yori/yori/lib/rsrc.c new file mode 100644 index 0000000000..fbb61acaa5 --- /dev/null +++ b/misc/tools/yori/yori/lib/rsrc.c @@ -0,0 +1,160 @@ +/** + * @file lib/rsrc.c + * + * Yori shell send files to the recycle bin. + * + * Copyright (c) 2024 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS ERASE A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE ERASE ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +/** + Load a consecutive range of string resources into an in-memory array. The + string array is allocated within this routine, however the strings + themselves are stored within the executable's resource section and are + accessible via a direct memory mapping into that resource section. So + while the string array needs to be deallocated with @ref YoriLibDereference , + the elements have no allocation. + + @param InitialElement Specifies the first string resource ID to load. This + must be a multiple of 16, since string resources are stored as tables + containing 16 elements each. + + @param NumberElements Specifies the number of string resources to load. + + @param StringArray On successful completion, updated to contain a pointer to + an array of YORI_STRINGs, where the array is NumberElements in length. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibLoadStringResourceArray( + __in YORI_ALLOC_SIZE_T InitialElement, + __in YORI_ALLOC_SIZE_T NumberElements, + __out PYORI_STRING * StringArray + ) +{ + YORI_ALLOC_SIZE_T Index; + LPWSTR Ptr; + PYORI_STRING LocalStringArray; + PYORI_STRING String; + YORI_ALLOC_SIZE_T StringIndex; + YORI_ALLOC_SIZE_T StringTableId; + YORI_ALLOC_SIZE_T StringTableCount; + YORI_ALLOC_SIZE_T StringCount; + HGLOBAL hRsrcMem; + HRSRC hRsrc; + + // + // Strings are arranged as tables with 16 elements in each. For + // simplicity, require strings to be aligned on table boundaries. + // + ASSERT((InitialElement % 16) == 0); + if ((InitialElement % 16) != 0) { + return FALSE; + } + + LocalStringArray = YoriLibReferencedMalloc(sizeof(YORI_STRING) * NumberElements); + if (LocalStringArray == NULL) { + return FALSE; + } + + for (Index = 0; Index < NumberElements; Index++) { + YoriLibInitEmptyString(&LocalStringArray[Index]); + } + + StringTableCount = (NumberElements + 15) / 16; + StringTableId = InitialElement / 16 + 1; + StringCount = NumberElements; + + for (Index = 0; Index < StringTableCount; Index++) { + hRsrc = FindResource(NULL, MAKEINTRESOURCE(Index + StringTableId), RT_STRING); + if (hRsrc != NULL) { + hRsrcMem = LoadResource(NULL, hRsrc); + if (hRsrcMem != NULL) { + Ptr = LockResource(hRsrcMem); + if (Ptr) { + for (StringIndex = 0; StringIndex < StringCount && StringIndex < 16; StringIndex++) { + String = &LocalStringArray[Index * 16 + StringIndex]; + String->LengthInChars = *Ptr; + String->StartOfString = (Ptr + 1); + + Ptr = Ptr + 1 + (*Ptr); + } + } + } + } + StringCount = StringCount - 16; + } + *StringArray = LocalStringArray; + return TRUE; +} + +/** + Load a consecutive range of string resources into an in-memory array and + validate that all strings have been successfully loaded. The string array + is allocated within this routine, however the strings themselves are stored + within the executable's resource section and are accessible via a direct + memory mapping into that resource section. So while the string array needs + to be deallocated with @ref YoriLibDereference , the elements have no + allocation. + + @param InitialElement Specifies the first string resource ID to load. This + must be a multiple of 16, since string resources are stored as tables + containing 16 elements each. + + @param NumberElements Specifies the number of string resources to load. + + @param StringArray On successful completion, updated to contain a pointer to + an array of YORI_STRINGs, where the array is NumberElements in length. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibLoadAndVerifyStringResourceArray( + __in YORI_ALLOC_SIZE_T InitialElement, + __in YORI_ALLOC_SIZE_T NumberElements, + __out PYORI_STRING * StringArray + ) +{ + PYORI_STRING LocalStringArray; + YORI_ALLOC_SIZE_T Index; + + if (!YoriLibLoadStringResourceArray(InitialElement, NumberElements, &LocalStringArray)) { + return FALSE; + } + + for (Index = 0; Index < NumberElements; Index++) { + if (LocalStringArray[Index].StartOfString == NULL) { + YoriLibDereference(LocalStringArray); + return FALSE; + } + } + + *StringArray = LocalStringArray; + + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/scheme.c b/misc/tools/yori/yori/lib/scheme.c new file mode 100644 index 0000000000..1f34f565b9 --- /dev/null +++ b/misc/tools/yori/yori/lib/scheme.c @@ -0,0 +1,471 @@ +/** + * @file lib/scheme.c + * + * Load and process console color schemes. + * + * This module implements string parsing and rule application to select a + * given set of color attributes to render any particular file with. + * + * Copyright (c) 2022 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + An array of color names, as used by the scheme format. These are in order + to allow for mapping of Win32 colors (by index) to string. + */ +LPCTSTR YoriLibSchemeColorNames[] = { + _T("BLACK"), + _T("BLUE"), + _T("GREEN"), + _T("CYAN"), + _T("RED"), + _T("MAGENTA"), + _T("YELLOW"), + _T("WHITE") +}; + +/** + An array of intensity values, as used by the scheme format. These are in + order to allow for mapping of Win32 intensity (by index) to string. + */ +LPCTSTR YoriLibSchemeColorPrefixes[] = { + _T("DARK"), + _T("BRIGHT") +}; + +/** + Parse a single comma delimited RGB value into a COLORREF. + + @param ColorName Pointer to the string form of the RGB value. + + @param ColorValue On successful completion, updated to contain the RGB + color. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibParseSchemeColorString( + __in LPTSTR ColorName, + __out COLORREF *ColorValue + ) +{ + YORI_MAX_SIGNED_T Temp; + YORI_STRING SubString; + YORI_ALLOC_SIZE_T CharsConsumed; + UCHAR Red; + UCHAR Green; + UCHAR Blue; + + YoriLibConstantString(&SubString, ColorName); + YoriLibTrimSpaces(&SubString); + + *ColorValue = 0; + + if (!YoriLibStringToNumber(&SubString, FALSE, &Temp, &CharsConsumed) || + CharsConsumed == 0) { + + return FALSE; + } + + Red = (UCHAR)Temp; + + SubString.StartOfString = SubString.StartOfString + CharsConsumed; + SubString.LengthInChars = SubString.LengthInChars - CharsConsumed; + + while (SubString.LengthInChars > 0 && + (SubString.StartOfString[0] == ',' || SubString.StartOfString[0] == ' ')) { + + SubString.StartOfString++; + SubString.LengthInChars--; + } + + if (!YoriLibStringToNumber(&SubString, FALSE, &Temp, &CharsConsumed) || + CharsConsumed == 0) { + + return FALSE; + } + + Green = (UCHAR)Temp; + + SubString.StartOfString = SubString.StartOfString + CharsConsumed; + SubString.LengthInChars = SubString.LengthInChars - CharsConsumed; + + while (SubString.LengthInChars > 0 && + (SubString.StartOfString[0] == ',' || SubString.StartOfString[0] == ' ')) { + + SubString.StartOfString++; + SubString.LengthInChars--; + } + + if (!YoriLibStringToNumber(&SubString, FALSE, &Temp, &CharsConsumed) || + CharsConsumed == 0) { + + return FALSE; + } + + Blue = (UCHAR)Temp; + *ColorValue = RGB(Red, Green, Blue); + return TRUE; +} + +/** + Update an array of 16 RGB color values by loading the values from a scheme + file. + + @param IniFileName Pointer to the INI file name. + + @param ColorTable On successful completion, updated with 16 RGB color values + in Win32 order. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibLoadColorTableFromScheme( + __in PCYORI_STRING IniFileName, + __out_ecount(16) COLORREF *ColorTable + ) +{ + DWORD Index; + DWORD ColorIndex; + DWORD PrefixIndex; + TCHAR ValueName[16]; + TCHAR Value[64]; + COLORREF Color; + + if (DllKernel32.pGetPrivateProfileStringW == NULL) { + return FALSE; + } + + for (PrefixIndex = 0; PrefixIndex < sizeof(YoriLibSchemeColorPrefixes)/sizeof(YoriLibSchemeColorPrefixes[0]); PrefixIndex++) { + for (ColorIndex = 0; ColorIndex < sizeof(YoriLibSchemeColorNames)/sizeof(YoriLibSchemeColorNames[0]); ColorIndex++) { + YoriLibSPrintf(ValueName, _T("%s_%s"), YoriLibSchemeColorPrefixes[PrefixIndex], YoriLibSchemeColorNames[ColorIndex]); + DllKernel32.pGetPrivateProfileStringW(_T("Table"), ValueName, _T(""), Value, sizeof(Value)/sizeof(Value[0]), IniFileName->StartOfString); + + if (!YoriLibParseSchemeColorString(Value, &Color)) { + return FALSE; + } + + Index = PrefixIndex * sizeof(YoriLibSchemeColorNames)/sizeof(YoriLibSchemeColorNames[0]) + ColorIndex; + + ColorTable[Index] = Color; + } + } + + return TRUE; +} + +/** + Load a single color in INTENSITY_COLOR form into a 4 bit Win32 + representation. + + @param String Pointer to the INTENSITY_COLOR string form. + + @param OutColor On successful completion, updated to contain the Win32 color + value. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibLoadColorFromSchemeString( + __in PCYORI_STRING String, + __out PUCHAR OutColor + ) +{ + YORI_STRING Dark; + YORI_STRING Bright; + UCHAR Index; + UCHAR Color; + YORI_STRING StringValue; + + StringValue.StartOfString = String->StartOfString; + StringValue.LengthInChars = String->LengthInChars; + + Color = 0; + YoriLibConstantString(&Dark, YoriLibSchemeColorPrefixes[0]); + YoriLibConstantString(&Bright, YoriLibSchemeColorPrefixes[1]); + if (YoriLibCompareStringInsCnt(&StringValue, &Dark, Dark.LengthInChars) == 0) { + StringValue.StartOfString = StringValue.StartOfString + Dark.LengthInChars; + StringValue.LengthInChars = StringValue.LengthInChars - Dark.LengthInChars; + } else if (YoriLibCompareStringInsCnt(&StringValue, &Bright, Bright.LengthInChars) == 0) { + Color = (UCHAR)(Color | FOREGROUND_INTENSITY); + StringValue.StartOfString = StringValue.StartOfString + Bright.LengthInChars; + StringValue.LengthInChars = StringValue.LengthInChars - Bright.LengthInChars; + } else { + return FALSE; + } + + if (StringValue.LengthInChars < 1 || StringValue.StartOfString[0] != '_') { + return FALSE; + } + + StringValue.StartOfString = StringValue.StartOfString + 1; + StringValue.LengthInChars = StringValue.LengthInChars - 1; + + for (Index = 0; Index < sizeof(YoriLibSchemeColorNames)/sizeof(YoriLibSchemeColorNames[0]); Index++) { + if (YoriLibCompareStringLitIns(&StringValue, YoriLibSchemeColorNames[Index]) == 0) { + Color = (UCHAR)(Color + Index); + break; + } + } + + if (Index == sizeof(YoriLibSchemeColorNames)/sizeof(YoriLibSchemeColorNames[0])) { + return FALSE; + } + + *OutColor = Color; + return TRUE; +} + +/** + Load a Foreground and Background color in INTENSITY_COLOR form from an INI + section. + + @param IniFileName Pointer to the INI file name. + + @param SectionName Pointer to the section within the INI file. + + @param WindowColor On successful completion, updated to contain the Win32 + color including background and foreground components. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibLoadSectionColorFromScheme( + __in PCYORI_STRING IniFileName, + __in LPCTSTR SectionName, + __out PUCHAR WindowColor + ) +{ + TCHAR Value[64]; + YORI_STRING StringValue; + UCHAR Foreground; + UCHAR Background; + + if (DllKernel32.pGetPrivateProfileStringW == NULL) { + return FALSE; + } + + StringValue.StartOfString = Value; + StringValue.LengthAllocated = sizeof(Value)/sizeof(Value[0]); + + StringValue.LengthInChars = (YORI_ALLOC_SIZE_T)DllKernel32.pGetPrivateProfileStringW(SectionName, + _T("Foreground"), + _T(""), + Value, + sizeof(Value)/sizeof(Value[0]), + IniFileName->StartOfString); + + if (!YoriLibLoadColorFromSchemeString(&StringValue, &Foreground)) { + return FALSE; + } + + StringValue.LengthInChars = (YORI_ALLOC_SIZE_T)DllKernel32.pGetPrivateProfileStringW(SectionName, + _T("Background"), + _T(""), + Value, + sizeof(Value)/sizeof(Value[0]), + IniFileName->StartOfString); + + if (!YoriLibLoadColorFromSchemeString(&StringValue, &Background)) { + return FALSE; + } + + *WindowColor = (UCHAR)((Background << 4) | Foreground); + return TRUE; +} + +/** + Load a Foreground and Background color in INTENSITY_COLOR form from an INI + file describing the default window color. + + @param IniFileName Pointer to the INI file name. + + @param WindowColor On successful completion, updated to contain the Win32 + color including background and foreground components. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibLoadWindowColorFromScheme( + __in PCYORI_STRING IniFileName, + __out PUCHAR WindowColor + ) +{ + return YoriLibLoadSectionColorFromScheme(IniFileName, _T("Screen"), WindowColor); +} + +/** + Load a Foreground and Background color in INTENSITY_COLOR form from an INI + file describing the popup color. + + @param IniFileName Pointer to the INI file name. + + @param WindowColor On successful completion, updated to contain the Win32 + color including background and foreground components. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibLoadPopupColorFromScheme( + __in PCYORI_STRING IniFileName, + __out PUCHAR WindowColor + ) +{ + return YoriLibLoadSectionColorFromScheme(IniFileName, _T("Popup"), WindowColor); +} + +/** + Save an array of 16 RGB values corresponding to Win32 colors to a scheme + INI file. + + @param IniFileName Pointer to the INI file name. + + @param ColorTable Pointer to an array of 16 colors to save. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL +YoriLibSaveColorTableToScheme( + __in PCYORI_STRING IniFileName, + __in COLORREF *ColorTable + ) +{ + DWORD Index; + DWORD ColorIndex; + DWORD PrefixIndex; + TCHAR ValueName[16]; + TCHAR Value[64]; + COLORREF Color; + + if (DllKernel32.pWritePrivateProfileStringW == NULL) { + return FALSE; + } + + for (PrefixIndex = 0; PrefixIndex < sizeof(YoriLibSchemeColorPrefixes)/sizeof(YoriLibSchemeColorPrefixes[0]); PrefixIndex++) { + for (ColorIndex = 0; ColorIndex < sizeof(YoriLibSchemeColorNames)/sizeof(YoriLibSchemeColorNames[0]); ColorIndex++) { + YoriLibSPrintf(ValueName, _T("%s_%s"), YoriLibSchemeColorPrefixes[PrefixIndex], YoriLibSchemeColorNames[ColorIndex]); + Index = PrefixIndex * sizeof(YoriLibSchemeColorNames)/sizeof(YoriLibSchemeColorNames[0]) + ColorIndex; + Color = ColorTable[Index]; + YoriLibSPrintf(Value, _T("%i, %i, %i"), GetRValue(Color), GetGValue(Color), GetBValue(Color)); + DllKernel32.pWritePrivateProfileStringW(_T("Table"), ValueName, Value, IniFileName->StartOfString); + } + } + + return TRUE; +} + +/** + Save a Foreground and Background color in INTENSITY_COLOR form to the + specified section of an INI file. + + @param IniFileName Pointer to the INI file name. + + @param SectionName Pointer to the section within the INI file. + + @param WindowColor The color to save. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL +YoriLibSaveSectionColorToScheme( + __in PCYORI_STRING IniFileName, + __in LPCTSTR SectionName, + __in UCHAR WindowColor + ) + +{ + TCHAR Value[64]; + UCHAR Intensity; + UCHAR Color; + UCHAR Component; + + if (DllKernel32.pWritePrivateProfileStringW == NULL) { + return FALSE; + } + + Component = (UCHAR)(WindowColor & 0xF); + + Intensity = (UCHAR)(Component >> 3); + Color = (UCHAR)(Component & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)); + + YoriLibSPrintf(Value, _T("%s_%s"), YoriLibSchemeColorPrefixes[Intensity], YoriLibSchemeColorNames[Component & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)]); + DllKernel32.pWritePrivateProfileStringW(SectionName, _T("Foreground"), Value, IniFileName->StartOfString); + + Component = (UCHAR)((WindowColor & 0xF0) >> 4); + + Intensity = (UCHAR)(Component >> 3); + Color = (UCHAR)(Component & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)); + + YoriLibSPrintf(Value, _T("%s_%s"), YoriLibSchemeColorPrefixes[Intensity], YoriLibSchemeColorNames[Component & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)]); + DllKernel32.pWritePrivateProfileStringW(SectionName, _T("Background"), Value, IniFileName->StartOfString); + + return TRUE; +} + +/** + Save a Foreground and Background color in INTENSITY_COLOR form describing the + default window color to the specified section of an INI file. + + @param IniFileName Pointer to the INI file name. + + @param WindowColor The color to save. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL +YoriLibSaveWindowColorToScheme( + __in PCYORI_STRING IniFileName, + __in UCHAR WindowColor + ) +{ + return YoriLibSaveSectionColorToScheme(IniFileName, _T("Screen"), WindowColor); +} + +/** + Save a Foreground and Background color in INTENSITY_COLOR form describing the + popup color to the specified section of an INI file. + + @param IniFileName Pointer to the INI file name. + + @param WindowColor The color to save. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOL +YoriLibSavePopupColorToScheme( + __in PCYORI_STRING IniFileName, + __in UCHAR WindowColor + ) +{ + return YoriLibSaveSectionColorToScheme(IniFileName, _T("Popup"), WindowColor); +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/scut.c b/misc/tools/yori/yori/lib/scut.c index c9803117fe..59bcecded4 100644 --- a/misc/tools/yori/yori/lib/scut.c +++ b/misc/tools/yori/yori/lib/scut.c @@ -680,8 +680,8 @@ YoriLibExecuteShortcut( YsExt.StartOfString = Extension; YsExt.LengthInChars = ExpandedFileTarget.LengthInChars - (YORI_ALLOC_SIZE_T)(Extension - ExpandedFileTarget.StartOfString); - if (YoriLibCompareStringWithLiteralInsensitive(&YsExt, _T(".exe")) == 0 || - YoriLibCompareStringWithLiteralInsensitive(&YsExt, _T(".com")) == 0) { + if (YoriLibCompareStringLitIns(&YsExt, _T(".exe")) == 0 || + YoriLibCompareStringLitIns(&YsExt, _T(".com")) == 0) { STARTUPINFO si; PROCESS_INFORMATION pi; diff --git a/misc/tools/yori/yori/lib/update.c b/misc/tools/yori/yori/lib/update.c new file mode 100644 index 0000000000..16927e301b --- /dev/null +++ b/misc/tools/yori/yori/lib/update.c @@ -0,0 +1,1035 @@ +/** + * @file lib/update.c + * + * Code to update a file from the internet including the running + * executable. + * + * Copyright (c) 2016-2023 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + A table of constant error strings corresponding to an error number. + */ +LPCTSTR +YoriLibUpdErrorStrings[] = { + _T("Success"), + _T("Could not initialize WinInet"), + _T("Could not connect to server"), + _T("Could not read data from server"), + _T("Data read from server is incorrect"), + _T("Could not write data to temporary local file"), + _T("Could not replace existing file with new file") +}; + +/** + Update an existing local file from a new local file. + + @param ExistingPath Path to the existing file. If NULL, this means the + currently running executable should be updated. + + @param NewPath Path to the file that should replace ExistingFile. + + @return TRUE to indicate success, FALSE to indicate error. + */ +BOOL +YoriLibUpdateBinaryFromFile( + __in_opt PCYORI_STRING ExistingPath, + __in PCYORI_STRING NewPath + ) +{ + YORI_STRING MyPath; + YORI_STRING OldPath; + PCYORI_STRING PathToReplace; + HANDLE hMyBinary; + + ASSERT(ExistingPath == NULL || YoriLibIsStringNullTerminated(ExistingPath)); + ASSERT(YoriLibIsStringNullTerminated(NewPath)); + YoriLibInitEmptyString(&MyPath); + YoriLibInitEmptyString(&OldPath); + + if (ExistingPath == NULL) { + + // + // Unlike most other Win32 APIs, this one has no way to indicate how + // much space it needs. + // + + if (!YoriLibAllocateString(&MyPath, 32768)) { + return FALSE; + } + + // + // If the file name to replace is NULL, replace the currently + // existing binary. + // + + MyPath.LengthInChars = (YORI_ALLOC_SIZE_T)GetModuleFileName(NULL, MyPath.StartOfString, MyPath.LengthAllocated); + if (MyPath.LengthInChars == 0) { + YoriLibFreeStringContents(&MyPath); + return FALSE; + } + + PathToReplace = &MyPath; + } else { + LPTSTR FinalBackslash; + + // + // If the file name to replace is a full path, defined as containing + // a backslash, replace that file path. + // + + FinalBackslash = YoriLibFindRightMostCharacter(ExistingPath, '\\'); + + if (FinalBackslash != NULL) { + PathToReplace = ExistingPath; + } else { + + // + // Unlike most other Win32 APIs, this one has no way to indicate how + // much space it needs. + // + + if (!YoriLibAllocateString(&MyPath, 32768)) { + return FALSE; + } + + // + // If it's a file name only, assume that it refers to a file in + // the same path as the existing binary. + // + + MyPath.LengthInChars = (YORI_ALLOC_SIZE_T)GetModuleFileName(NULL, MyPath.StartOfString, MyPath.LengthAllocated); + if (MyPath.LengthInChars == 0) { + YoriLibFreeStringContents(&MyPath); + return FALSE; + } + + FinalBackslash = YoriLibFindRightMostCharacter(&MyPath, '\\'); + if (FinalBackslash != NULL) { + YORI_ALLOC_SIZE_T RemainingLength = (YORI_ALLOC_SIZE_T)(MyPath.LengthAllocated - (FinalBackslash - MyPath.StartOfString + 1)); + if (ExistingPath->LengthInChars >= RemainingLength) { + return FALSE; + } + YoriLibSPrintfS(FinalBackslash + 1, RemainingLength, _T("%y"), ExistingPath); + MyPath.LengthInChars = MyPath.LengthInChars + ExistingPath->LengthInChars; + PathToReplace = &MyPath; + } else { + PathToReplace = ExistingPath; + } + } + } + + // + // If the file already exists, move it to a backup name. + // + + if (GetFileAttributes(PathToReplace->StartOfString) != (DWORD)-1) { + if (!YoriLibRenameFileToBackupName(PathToReplace, &OldPath)) { + YoriLibFreeStringContents(&MyPath); + return FALSE; + } + } + + // + // Rename the new file to where the old file was. If it fails, try to + // move the old binary back. If that fails, there's not much we can do. + // + + if (!MoveFileEx(NewPath->StartOfString, PathToReplace->StartOfString, MOVEFILE_COPY_ALLOWED)) { + if (OldPath.LengthInChars > 0) { + MoveFileEx(OldPath.StartOfString, PathToReplace->StartOfString, MOVEFILE_COPY_ALLOWED); + YoriLibFreeStringContents(&OldPath); + } + return FALSE; + } + + // + // Try to delete the old binary. Do this by opening a delete + // on close handle and not closing it. The close will occur + // when the process terminates, which hopefully means it won't + // conflict with the open that's running the program right now. + // + // If this fails just leave the old binary around. Note next + // time this process is run it is overwritten. + // + + if (OldPath.LengthInChars > 0) { + hMyBinary = CreateFile(OldPath.StartOfString, + DELETE, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_DELETE_ON_CLOSE, + NULL); + YoriLibFreeStringContents(&OldPath); + } + + return TRUE; +} + +/** + An array of human readable day names in HTTP format. + */ +LPCSTR YoriLibDayNames[] = { + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat"}; + +/** + An array of human readable month names in HTTP format. + */ +LPCSTR YoriLibMonthNames[] = { + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"}; + + +/** + The size of a single read buffer. This will be given to WinInet as a single + operation. We can read more bytes than this, it will just be done in multiple + operations to WinInet. + */ +#if YORI_MAX_ALLOC_SIZE >= (1024 * 1024) +#define UPDATE_READ_SIZE (1024 * 1024) +#else +#define UPDATE_READ_SIZE (60 * 1024) +#endif + +/** + Construct the HTTP headers to attach to the request. This code is shared + between WinInet and WinHttp. + + @param Url Pointer to the Url to access. + + @param IfModifiedSince Optionally points to a timestamp where only newer + resources should be downloaded. + + @param OutputHeader On successful completion, populated with a newly + allocated string containing all of the necessary HTTP headers. + + @param HostSubset On successful completion, updated to point to the substring + within Url that refers to the host name to access. + + @param ObjectSubset On successful completion, updated to point to the + beginning of the Url string containing the object to access. This is + immediately following the host name and continues to the end of the + string, so this is null terminated, and no new allocation is needed. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibUpdateBuildHttpHeaders( + __in PCYORI_STRING Url, + __in_opt PSYSTEMTIME IfModifiedSince, + __out PYORI_STRING OutputHeader, + __out PYORI_STRING HostSubset, + __out LPTSTR *ObjectSubset + ) +{ + LPTSTR EndOfHost; + YORI_STRING HostHeader; + YORI_STRING IfModifiedSinceHeader; + YORI_STRING CombinedHeader; + YORI_STRING ProtocolDelimiter; + YORI_ALLOC_SIZE_T StartOfHost; + + // + // Newer versions of Windows will add a Host: header. Old versions send + // an HTTP 1.1 request without one, which Apache doesn't like. + // + + YoriLibInitEmptyString(&HostHeader); + YoriLibInitEmptyString(HostSubset); + *ObjectSubset = NULL; + + YoriLibConstantString(&ProtocolDelimiter, _T("://")); + if (YoriLibFindFirstMatchSubstr(Url, 1, &ProtocolDelimiter, &StartOfHost)) { + StartOfHost = StartOfHost + ProtocolDelimiter.LengthInChars; + HostSubset->StartOfString = &Url->StartOfString[StartOfHost]; + HostSubset->LengthInChars = Url->LengthInChars - StartOfHost; + EndOfHost = YoriLibFindLeftMostCharacter(HostSubset, '/'); + if (EndOfHost != NULL) { + HostSubset->LengthInChars = (YORI_ALLOC_SIZE_T)(EndOfHost - HostSubset->StartOfString); + *ObjectSubset = EndOfHost; + YoriLibYPrintf(&HostHeader, _T("Host: %y\r\n"), HostSubset); + } else { + return FALSE; + } + } else { + return FALSE; + } + + // + // If the caller only wanted to fetch a resource if it's newer than an + // existing file, generate that header now. + // + + YoriLibInitEmptyString(&IfModifiedSinceHeader); + if (IfModifiedSince != NULL) { + YoriLibYPrintf(&IfModifiedSinceHeader, + _T("If-Modified-Since: %hs, %02i %hs %04i %02i:%02i:%02i GMT\r\n"), + YoriLibDayNames[IfModifiedSince->wDayOfWeek], + IfModifiedSince->wDay, + YoriLibMonthNames[IfModifiedSince->wMonth - 1], + IfModifiedSince->wYear, + IfModifiedSince->wHour, + IfModifiedSince->wMinute, + IfModifiedSince->wSecond); + } + + + // + // Merge headers. If we have only one or the other, this is just a + // reference with no allocation. + // + + YoriLibInitEmptyString(&CombinedHeader); + if (IfModifiedSinceHeader.LengthInChars > 0 && HostHeader.LengthInChars > 0) { + YoriLibYPrintf(&CombinedHeader, _T("%y%y"), &HostHeader, &IfModifiedSinceHeader); + } else if (IfModifiedSinceHeader.LengthInChars > 0) { + YoriLibCloneString(&CombinedHeader, &IfModifiedSinceHeader); + } else if (HostHeader.LengthInChars > 0) { + YoriLibCloneString(&CombinedHeader, &HostHeader); + } + + // + // Now all the headers are merged, we don't need the component parts. + // + + YoriLibFreeStringContents(&HostHeader); + YoriLibFreeStringContents(&IfModifiedSinceHeader); + + memcpy(OutputHeader, &CombinedHeader, sizeof(YORI_STRING)); + return TRUE; +} + +/** + Download a file from the internet and store it in a local location using + WinInet.dll. This function is only used once WinInet is loaded. + + @param Dll Pointer to the Dll function table to use. This allows this + function to operate against WinInet.dll or a different structure + with the same function signatures, which is used by the mini-HTTP + client. + + @param Url The Url to download the file from. + + @param TargetName If specified, the local location to store the file. + If not specified, the current executable name is used. + + @param Agent The user agent to report to the remote web server. + + @param IfModifiedSince If specified, indicates a timestamp where a new + object should only be downloaded if it is newer. + + @return An update error code indicating success or appropriate error. + */ +YORI_LIB_UPDATE_ERROR +YoriLibUpdateBinaryFromUrlWinInet( + __in PYORI_WININET_FUNCTIONS Dll, + __in PCYORI_STRING Url, + __in_opt PCYORI_STRING TargetName, + __in PCYORI_STRING Agent, + __in_opt PSYSTEMTIME IfModifiedSince + ) +{ + PVOID hInternet = NULL; + PVOID NewBinary = NULL; + PUCHAR NewBinaryData = NULL; + DWORD ErrorBufferSize = 0; + DWORD ActualBinarySize; + YORI_STRING TempName; + YORI_STRING TempPath; + YORI_STRING PrefixString; + HANDLE hTempFile = INVALID_HANDLE_VALUE; + BOOL SuccessfullyComplete = FALSE; + BOOL WinInetOnlySupportsAnsi = FALSE; + DWORD dwError; + YORI_LIB_UPDATE_ERROR Return = YoriLibUpdErrorSuccess; + YORI_STRING CombinedHeader; + YORI_STRING HostSubset; + LPTSTR ObjectName; + + ASSERT(YoriLibIsStringNullTerminated(Url)); + ASSERT(YoriLibIsStringNullTerminated(Agent)); + ASSERT(TargetName == NULL || YoriLibIsStringNullTerminated(TargetName)); + + YoriLibInitEmptyString(&TempName); + YoriLibInitEmptyString(&TempPath); + + // + // Open an internet connection with default proxy settings. + // + + hInternet = Dll->pInternetOpenW(Agent->StartOfString, + 0, + NULL, + NULL, + 0); + + if (hInternet == NULL) { + DWORD LastError; + LastError = GetLastError(); + + // + // Internet Explorer 3 helpfully exports Unicode functions and then + // doesn't implement them. In this case we have to downconvert to + // ANSI ourselves. If a resource needs a particular encoding (ie., + // goes beyond 7 bit ASCII) then this will likely fail, but typically + // the resources we use fit within that limitation. + // + + if (LastError == ERROR_CALL_NOT_IMPLEMENTED) { + LPSTR AnsiAgent; + YORI_ALLOC_SIZE_T BytesForAnsiAgent; + + if (Dll->pInternetOpenA == NULL || + Dll->pInternetOpenUrlA == NULL) { + + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + WinInetOnlySupportsAnsi = TRUE; + + BytesForAnsiAgent = (YORI_ALLOC_SIZE_T)WideCharToMultiByte(CP_ACP, + 0, + Agent->StartOfString, + Agent->LengthInChars, + NULL, + 0, + NULL, + NULL); + + AnsiAgent = YoriLibMalloc(BytesForAnsiAgent + 1); + if (AnsiAgent == NULL) { + Return = YoriLibUpdErrorInetInit; + return FALSE; + } + + WideCharToMultiByte(CP_ACP, + 0, + Agent->StartOfString, + Agent->LengthInChars, + AnsiAgent, + BytesForAnsiAgent, + NULL, + NULL); + AnsiAgent[BytesForAnsiAgent] = '\0'; + + hInternet = Dll->pInternetOpenA(AnsiAgent, + 0, + NULL, + NULL, + 0); + + YoriLibFree(AnsiAgent); + } + } + + if (hInternet == NULL) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + if (!YoriLibUpdateBuildHttpHeaders(Url, IfModifiedSince, &CombinedHeader, &HostSubset, &ObjectName)) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + // + // Request the desired URL and check the status is HTTP success. + // + + if (WinInetOnlySupportsAnsi) { + YORI_ALLOC_SIZE_T AnsiCombinedHeaderLength; + LPSTR AnsiCombinedHeader; + YORI_ALLOC_SIZE_T AnsiUrlLength; + LPSTR AnsiUrl; + + AnsiCombinedHeaderLength = (YORI_ALLOC_SIZE_T)WideCharToMultiByte(CP_ACP, + 0, + CombinedHeader.StartOfString, + CombinedHeader.LengthInChars, + NULL, + 0, + NULL, + NULL); + AnsiUrlLength = (YORI_ALLOC_SIZE_T)WideCharToMultiByte(CP_ACP, + 0, + Url->StartOfString, + Url->LengthInChars, + NULL, + 0, + NULL, + NULL); + + AnsiCombinedHeader = YoriLibMalloc(AnsiCombinedHeaderLength + 1); + if (AnsiCombinedHeader == NULL) { + YoriLibFreeStringContents(&CombinedHeader); + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + AnsiUrl = YoriLibMalloc(AnsiUrlLength + 1); + if (AnsiUrl == NULL) { + YoriLibFree(AnsiCombinedHeader); + YoriLibFreeStringContents(&CombinedHeader); + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + WideCharToMultiByte(CP_ACP, + 0, + CombinedHeader.StartOfString, + CombinedHeader.LengthInChars, + AnsiCombinedHeader, + AnsiCombinedHeaderLength, + NULL, + NULL); + AnsiCombinedHeader[AnsiCombinedHeaderLength] = '\0'; + + WideCharToMultiByte(CP_ACP, + 0, + Url->StartOfString, + Url->LengthInChars, + AnsiUrl, + AnsiUrlLength, + NULL, + NULL); + AnsiUrl[AnsiUrlLength] = '\0'; + + NewBinary = Dll->pInternetOpenUrlA(hInternet, + AnsiUrl, + AnsiCombinedHeader, + AnsiCombinedHeaderLength, + 0, + 0); + YoriLibFree(AnsiUrl); + YoriLibFree(AnsiCombinedHeader); + + } else { + + NewBinary = Dll->pInternetOpenUrlW(hInternet, + Url->StartOfString, + CombinedHeader.StartOfString, + CombinedHeader.LengthInChars, + 0, + 0); + } + + if (NewBinary == NULL) { + YoriLibFreeStringContents(&CombinedHeader); + Return = YoriLibUpdErrorInetConnect; + goto Exit; + } + + YoriLibFreeStringContents(&CombinedHeader); + + ErrorBufferSize = sizeof(dwError); + ActualBinarySize = 0; + dwError = 0; + + if (WinInetOnlySupportsAnsi) { + if (!Dll->pHttpQueryInfoA(NewBinary, + HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, + &dwError, + &ErrorBufferSize, + &ActualBinarySize)) { + Return = YoriLibUpdErrorInetConnect; + goto Exit; + } + } else { + if (!Dll->pHttpQueryInfoW(NewBinary, + HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, + &dwError, + &ErrorBufferSize, + &ActualBinarySize)) { + Return = YoriLibUpdErrorInetConnect; + goto Exit; + } + } + + if (dwError != 200) { + if (dwError != 304 || IfModifiedSince == NULL) { + Return = YoriLibUpdErrorInetConnect; + } + goto Exit; + } + + // + // Create a temporary file to hold the contents. + // + + if (!YoriLibGetTempPath(&TempPath, 0)) { + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + + YoriLibConstantString(&PrefixString, _T("UPD")); + if (!YoriLibGetTempFileName(&TempPath, &PrefixString, &hTempFile, &TempName)) { + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + + NewBinaryData = YoriLibMalloc(UPDATE_READ_SIZE); + if (NewBinaryData == NULL) { + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + + // + // Read from the internet location and save to the temporary file. + // + + while (Dll->pInternetReadFile(NewBinary, NewBinaryData, UPDATE_READ_SIZE, &ActualBinarySize)) { + + DWORD DataWritten; + + if (ActualBinarySize == 0) { + SuccessfullyComplete = TRUE; + break; + } + + if (!WriteFile(hTempFile, NewBinaryData, ActualBinarySize, &DataWritten, NULL) || + DataWritten != ActualBinarySize) { + + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + } + + // + // The only acceptable reason to fail is all the data has been + // received. + // + + if (!SuccessfullyComplete) { + Return = YoriLibUpdErrorInetRead; + goto Exit; + } + + // + // For validation, if the request is to modify the current executable + // check that the result is an executable. + // + + if (TargetName == NULL) { + SetFilePointer(hTempFile, 0, NULL, FILE_BEGIN); + if (!ReadFile(hTempFile, NewBinaryData, 2, &ActualBinarySize, NULL) || + ActualBinarySize != 2 || + NewBinaryData[0] != 'M' || + NewBinaryData[1] != 'Z' ) { + + Return = YoriLibUpdErrorInetContents; + goto Exit; + } + } + + // + // Now update the binary with the local file. + // + + CloseHandle(hTempFile); + YoriLibFree(NewBinaryData); + NewBinaryData = NULL; + hTempFile = INVALID_HANDLE_VALUE; + + if (YoriLibUpdateBinaryFromFile(TargetName, &TempName)) { + Return = YoriLibUpdErrorSuccess; + } else { + Return = YoriLibUpdErrorFileReplace; + } + +Exit: + + if (NewBinaryData != NULL) { + YoriLibFree(NewBinaryData); + } + + if (hTempFile != INVALID_HANDLE_VALUE) { + CloseHandle(hTempFile); + DeleteFile(TempName.StartOfString); + } + + YoriLibFreeStringContents(&TempPath); + YoriLibFreeStringContents(&TempName); + + if (NewBinary != NULL) { + Dll->pInternetCloseHandle(NewBinary); + } + + if (hInternet != NULL) { + Dll->pInternetCloseHandle(hInternet); + } + + return Return; +} + +/** + Download a file from the internet and store it in a local location using + WinHttp.dll. This function is only used once WinHttp is loaded. + + @param Url The Url to download the file from. + + @param TargetName If specified, the local location to store the file. + If not specified, the current executable name is used. + + @param Agent The user agent to report to the remote web server. + + @param IfModifiedSince If specified, indicates a timestamp where a new + object should only be downloaded if it is newer. + + @return An update error code indicating success or appropriate error. + */ +YORI_LIB_UPDATE_ERROR +YoriLibUpdateBinaryFromUrlWinHttp( + __in PCYORI_STRING Url, + __in_opt PCYORI_STRING TargetName, + __in PCYORI_STRING Agent, + __in_opt PSYSTEMTIME IfModifiedSince + ) +{ + PVOID hInternet = NULL; + PVOID hConnect = NULL; + PVOID hRequest = NULL; + YORI_LIB_UPDATE_ERROR Return = YoriLibUpdErrorSuccess; + YORI_STRING HostSubset; + YORI_STRING CombinedHeader; + LPTSTR HostName; + LPTSTR ObjectName; + DWORD dwError; + YORI_STRING TempName; + YORI_STRING TempPath; + YORI_STRING PrefixString; + HANDLE hTempFile = INVALID_HANDLE_VALUE; + PUCHAR NewBinaryData = NULL; + DWORD ActualBinarySize; + DWORD ErrorBufferSize = 0; + BOOL SuccessfullyComplete = FALSE; + + ASSERT(YoriLibIsStringNullTerminated(Url)); + ASSERT(YoriLibIsStringNullTerminated(Agent)); + ASSERT(TargetName == NULL || YoriLibIsStringNullTerminated(TargetName)); + + YoriLibInitEmptyString(&CombinedHeader); + YoriLibInitEmptyString(&TempName); + YoriLibInitEmptyString(&TempPath); + + // + // Open an internet connection with default proxy settings. + // + + hInternet = DllWinHttp.pWinHttpOpen(Agent->StartOfString, + 0, + NULL, + NULL, + 0); + + if (hInternet == NULL) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + YoriLibInitEmptyString(&CombinedHeader); + if (!YoriLibUpdateBuildHttpHeaders(Url, IfModifiedSince, &CombinedHeader, &HostSubset, &ObjectName)) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + HostName = YoriLibCStringFromYoriString(&HostSubset); + if (HostName == NULL) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + hConnect = DllWinHttp.pWinHttpConnect(hInternet, HostName, 0, 0); + YoriLibDereference(HostName); + if (hConnect == NULL) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + hRequest = DllWinHttp.pWinHttpOpenRequest(hConnect, _T("GET"), ObjectName, NULL, NULL, NULL, 0); + if (hRequest == NULL) { + Return = YoriLibUpdErrorInetInit; + goto Exit; + } + + if (!DllWinHttp.pWinHttpSendRequest(hRequest, + CombinedHeader.StartOfString, + CombinedHeader.LengthInChars, + NULL, + 0, + 0, + 0)) { + Return = YoriLibUpdErrorInetConnect; + goto Exit; + } + + if (!DllWinHttp.pWinHttpReceiveResponse(hRequest, NULL)) { + Return = YoriLibUpdErrorInetConnect; + goto Exit; + } + + ErrorBufferSize = sizeof(dwError); + if (!DllWinHttp.pWinHttpQueryHeaders(hRequest, + HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, + NULL, + &dwError, + &ErrorBufferSize, + NULL)) { + Return = YoriLibUpdErrorInetConnect; + goto Exit; + } + + if (dwError != 200) { + if (dwError != 304 || IfModifiedSince == NULL) { + Return = YoriLibUpdErrorInetConnect; + } + goto Exit; + } + + // + // Create a temporary file to hold the contents. + // + + if (!YoriLibGetTempPath(&TempPath, 0)) { + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + + YoriLibConstantString(&PrefixString, _T("UPD")); + if (!YoriLibGetTempFileName(&TempPath, &PrefixString, &hTempFile, &TempName)) { + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + + NewBinaryData = YoriLibMalloc(UPDATE_READ_SIZE); + if (NewBinaryData == NULL) { + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + + // + // Read from the internet location and save to the temporary file. + // + + while (DllWinHttp.pWinHttpReadData(hRequest, NewBinaryData, UPDATE_READ_SIZE, &ActualBinarySize)) { + + DWORD DataWritten; + + if (ActualBinarySize == 0) { + SuccessfullyComplete = TRUE; + break; + } + + if (!WriteFile(hTempFile, NewBinaryData, ActualBinarySize, &DataWritten, NULL) || + DataWritten != ActualBinarySize) { + + Return = YoriLibUpdErrorFileWrite; + goto Exit; + } + } + + // + // The only acceptable reason to fail is all the data has been + // received. + // + + if (!SuccessfullyComplete) { + Return = YoriLibUpdErrorInetRead; + goto Exit; + } + + // + // For validation, if the request is to modify the current executable + // check that the result is an executable. + // + + if (TargetName == NULL) { + SetFilePointer(hTempFile, 0, NULL, FILE_BEGIN); + if (!ReadFile(hTempFile, NewBinaryData, 2, &ActualBinarySize, NULL) || + ActualBinarySize != 2 || + NewBinaryData[0] != 'M' || + NewBinaryData[1] != 'Z' ) { + + Return = YoriLibUpdErrorInetContents; + goto Exit; + } + } + + // + // Now update the binary with the local file. + // + + CloseHandle(hTempFile); + hTempFile = INVALID_HANDLE_VALUE; + + if (!YoriLibUpdateBinaryFromFile(TargetName, &TempName)) { + Return = YoriLibUpdErrorFileReplace; + } + +Exit: + + if (NewBinaryData != NULL) { + YoriLibFree(NewBinaryData); + } + + if (hTempFile != INVALID_HANDLE_VALUE) { + CloseHandle(hTempFile); + DeleteFile(TempName.StartOfString); + } + + YoriLibFreeStringContents(&CombinedHeader); + YoriLibFreeStringContents(&TempPath); + YoriLibFreeStringContents(&TempName); + + if (hConnect != NULL) { + DllWinHttp.pWinHttpCloseHandle(hConnect); + } + + if (hRequest != NULL) { + DllWinHttp.pWinHttpCloseHandle(hRequest); + } + + if (hInternet != NULL) { + DllWinHttp.pWinHttpCloseHandle(hInternet); + } + + return Return; +} + +/** + Download a file from the internet and store it in a local location. + + @param Url The Url to download the file from. + + @param TargetName If specified, the local location to store the file. + If not specified, the current executable name is used. + + @param Agent The user agent to report to the remote web server. + + @param IfModifiedSince If specified, indicates a timestamp where a new + object should only be downloaded if it is newer. + + @return An update error code indicating success or appropriate error. + */ +YORI_LIB_UPDATE_ERROR +YoriLibUpdateBinaryFromUrl( + __in PCYORI_STRING Url, + __in_opt PCYORI_STRING TargetName, + __in PCYORI_STRING Agent, + __in_opt PSYSTEMTIME IfModifiedSince + ) +{ + YORI_WININET_FUNCTIONS StubWinInet; + + ASSERT(YoriLibIsStringNullTerminated(Url)); + ASSERT(YoriLibIsStringNullTerminated(Agent)); + + // + // Dynamically load WinInet. This means we don't have to resolve + // imports unless we're really using it for something, and we can + // degrade gracefully if it's not there (original 95/NT.) + // + + YoriLibLoadWinInetFunctions(); + + if (DllWinInet.pInternetOpenW != NULL && + DllWinInet.pInternetOpenUrlW != NULL && + DllWinInet.pHttpQueryInfoW != NULL && + DllWinInet.pInternetReadFile != NULL && + DllWinInet.pInternetCloseHandle != NULL) { + + return YoriLibUpdateBinaryFromUrlWinInet(&DllWinInet, Url, TargetName, Agent, IfModifiedSince); + } + + // + // If WinInet isn't present, load WinHttp. This path is taken on + // Nano server. + // + + YoriLibLoadWinHttpFunctions(); + + if (DllWinHttp.pWinHttpCloseHandle != NULL && + DllWinHttp.pWinHttpConnect != NULL && + DllWinHttp.pWinHttpOpen != NULL && + DllWinHttp.pWinHttpOpenRequest != NULL && + DllWinHttp.pWinHttpQueryHeaders != NULL && + DllWinHttp.pWinHttpReadData != NULL && + DllWinHttp.pWinHttpReceiveResponse != NULL && + DllWinHttp.pWinHttpSendRequest != NULL) { + + return YoriLibUpdateBinaryFromUrlWinHttp(Url, TargetName, Agent, IfModifiedSince); + } + + // + // If neither of the above work, use our hard coded fallback. This is + // really intended for NT 3.1 or other HTTP-less environments. + // + + ZeroMemory(&StubWinInet, sizeof(StubWinInet)); + + StubWinInet.pInternetOpenW = YoriLibInternetOpen; + StubWinInet.pInternetOpenUrlW = YoriLibInternetOpenUrl; + StubWinInet.pHttpQueryInfoW = YoriLibHttpQueryInfo; + StubWinInet.pInternetReadFile = YoriLibInternetReadFile; + StubWinInet.pInternetCloseHandle = YoriLibInternetCloseHandle; + + return YoriLibUpdateBinaryFromUrlWinInet(&StubWinInet, Url, TargetName, Agent, IfModifiedSince); +} + +/** + Returns a constant (not allocated) string corresponding to the specified + update error code. + + @param Error The update error code. + + @return A pointer to a constant string with the error text. + */ +LPCTSTR +YoriLibUpdateErrorString( + __in YORI_LIB_UPDATE_ERROR Error + ) +{ + if (Error >= 0 && Error < YoriLibUpdErrorMax) { + return YoriLibUpdErrorStrings[Error]; + } + return _T("Not an update error"); +} diff --git a/misc/tools/yori/yori/lib/util.c b/misc/tools/yori/yori/lib/util.c index d8087ce616..e766e64eca 100644 --- a/misc/tools/yori/yori/lib/util.c +++ b/misc/tools/yori/yori/lib/util.c @@ -448,9 +448,9 @@ YoriLibIsPathUrl( __in PCYORI_STRING PackagePath ) { - if (YoriLibCompareStringWithLiteralInsensitiveCount(PackagePath, _T("http://"), sizeof("http://") - 1) == 0 || - YoriLibCompareStringWithLiteralInsensitiveCount(PackagePath, _T("https://"), sizeof("https://") - 1) == 0 || - YoriLibCompareStringWithLiteralInsensitiveCount(PackagePath, _T("ftp://"), sizeof("ftp://") - 1) == 0) { + if (YoriLibCompareStringLitInsCnt(PackagePath, _T("http://"), sizeof("http://") - 1) == 0 || + YoriLibCompareStringLitInsCnt(PackagePath, _T("https://"), sizeof("https://") - 1) == 0 || + YoriLibCompareStringLitInsCnt(PackagePath, _T("ftp://"), sizeof("ftp://") - 1) == 0) { return TRUE; } diff --git a/misc/tools/yori/yori/lib/vt.c b/misc/tools/yori/yori/lib/vt.c index d3906da5cf..96a1bb47cd 100644 --- a/misc/tools/yori/yori/lib/vt.c +++ b/misc/tools/yori/yori/lib/vt.c @@ -52,7 +52,7 @@ WORD YoriLibVtResetColor; if the value has not yet been determined, and will be queried before changing the console color. */ -BOOLEAN YoriLibVtResetColorSet; +BOOLEAN YoriLibVtResetColorSet = FALSE; /** The line ending to apply when writing to a file. Note that this is not @@ -142,7 +142,7 @@ YoriLibVtGetLineEnding(VOID) @return TRUE to indicate success, FALSE to indicate failure. */ BOOL -YoriLibOutputTextToMultibyteDevice( +YoriLibOutputTextToMbyteDev( __in HANDLE hOutput, __in PCYORI_STRING String ) @@ -156,7 +156,7 @@ YoriLibOutputTextToMultibyteDevice( YORI_ALLOC_SIZE_T AnsiBytesNeeded; LPSTR AnsiBuf; - AnsiBytesNeeded = (YORI_ALLOC_SIZE_T)YoriLibGetMultibyteOutputSizeNeeded(String->StartOfString, String->LengthInChars); + AnsiBytesNeeded = (YORI_ALLOC_SIZE_T)YoriLibGetMbyteOutputSizeNeeded(String->StartOfString, String->LengthInChars); if (AnsiBytesNeeded > (int)sizeof(AnsiStackBuf)) { AnsiBuf = YoriLibMalloc(AnsiBytesNeeded); @@ -201,7 +201,7 @@ YoriLibOutputTextToMultibyteDevice( @return TRUE to indicate success, FALSE to indicate failure. */ BOOL -YoriLibOutputTextToMultibyteNormalizeLineEnding( +YoriLibOutputTextMbyteFixLnEnd( __in HANDLE hOutput, __in PCYORI_STRING String ) @@ -218,7 +218,7 @@ YoriLibOutputTextToMultibyteNormalizeLineEnding( SearchString.LengthInChars = String->LengthInChars; while(TRUE) { - NonLineEndLength = YoriLibCountStringNotContainingChars(&SearchString, _T("\r\n")); + NonLineEndLength = YoriLibCntStringNotWithChars(&SearchString, _T("\r\n")); CharsToIgnore = 0; GenerateLineEnd = FALSE; @@ -243,14 +243,14 @@ YoriLibOutputTextToMultibyteNormalizeLineEnding( DisplayString.StartOfString = SearchString.StartOfString; DisplayString.LengthInChars = NonLineEndLength; - if (!YoriLibOutputTextToMultibyteDevice(hOutput, &DisplayString)) { + if (!YoriLibOutputTextToMbyteDev(hOutput, &DisplayString)) { return FALSE; } } if (GenerateLineEnd) { YoriLibConstantString(&DisplayString, YoriLibVtLineEnding); - if (!YoriLibOutputTextToMultibyteDevice(hOutput, &DisplayString)) { + if (!YoriLibOutputTextToMbyteDev(hOutput, &DisplayString)) { return FALSE; } } @@ -326,7 +326,7 @@ YoriLibConsoleEndStream( @return TRUE for success, FALSE on failure. */ BOOL -YoriLibConsoleProcessAndOutputText( +YoriLibConsoleProcOutputText( __in HANDLE hOutput, __in PCYORI_STRING String, __inout PYORI_MAX_UNSIGNED_T Context @@ -356,7 +356,7 @@ YoriLibConsoleProcessAndOutputText( can't exactly fail. */ BOOL -YoriLibConsoleProcessAndIgnoreEscape( +YoriLibConsoleProcIgnoreEsc( __in HANDLE hOutput, __in PCYORI_STRING String, __inout PYORI_MAX_UNSIGNED_T Context @@ -369,6 +369,20 @@ YoriLibConsoleProcessAndIgnoreEscape( return TRUE; } +/** + A mapping of ANSI 8 color indexes to Win32 colors. + */ +CONST WORD +YoriLibColorTable[] = {0, + FOREGROUND_RED, + FOREGROUND_GREEN, + FOREGROUND_RED|FOREGROUND_GREEN, + FOREGROUND_BLUE, + FOREGROUND_BLUE|FOREGROUND_RED, + FOREGROUND_BLUE|FOREGROUND_GREEN, + FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED}; + + /** Specifies the value to indicate a new color derives its foreground from the existing color. @@ -405,7 +419,7 @@ YoriLibConsoleProcessAndIgnoreEscape( @return TRUE to indicate successful completion, FALSE to indicate failure. */ BOOL -YoriLibVtFinalColorFromSequenceEx( +YoriLibVtFinalColorFromEscEx( __in WORD InitialColor, __in PCYORI_STRING EscapeSequence, __out PWORD FinalColor, @@ -433,15 +447,6 @@ YoriLibVtFinalColorFromSequenceEx( DWORD Code; BOOLEAN NewUnderline = FALSE; - DWORD ColorTable[] = {0, - FOREGROUND_RED, - FOREGROUND_GREEN, - FOREGROUND_RED|FOREGROUND_GREEN, - FOREGROUND_BLUE, - FOREGROUND_BLUE|FOREGROUND_RED, - FOREGROUND_BLUE|FOREGROUND_GREEN, - FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED}; - CurrentPoint++; RemainingLength--; @@ -482,16 +487,16 @@ YoriLibVtFinalColorFromSequenceEx( NewColor = (WORD)((NewColor & ~(0xf0)) | (YoriLibVtResetColor & 0xf0)); } else if (Code >= 30 && Code <= 37) { ComponentsUsed = (WORD)(ComponentsUsed & ~(INITIAL_COMPONENT_FOREGROUND)); - NewColor = (WORD)((NewColor & ~(0xf)) | ColorTable[Code - 30]); + NewColor = (WORD)((NewColor & ~(0xf)) | YoriLibColorTable[Code - 30]); } else if (Code >= 40 && Code <= 47) { ComponentsUsed = (WORD)(ComponentsUsed & ~(INITIAL_COMPONENT_BACKGROUND)); - NewColor = (WORD)((NewColor & ~(0xf0)) | (ColorTable[Code - 40]<<4)); + NewColor = (WORD)((NewColor & ~(0xf0)) | (YoriLibColorTable[Code - 40]<<4)); } else if (Code >= 90 && Code <= 97) { ComponentsUsed = (WORD)(ComponentsUsed & ~(INITIAL_COMPONENT_FOREGROUND)); - NewColor = (WORD)((NewColor & ~(0xf)) | FOREGROUND_INTENSITY | ColorTable[Code - 90]); + NewColor = (WORD)((NewColor & ~(0xf)) | FOREGROUND_INTENSITY | YoriLibColorTable[Code - 90]); } else if (Code >= 100 && Code <= 107) { ComponentsUsed = (WORD)(ComponentsUsed & ~(INITIAL_COMPONENT_BACKGROUND)); - NewColor = (WORD)((NewColor & ~(0xf0)) | BACKGROUND_INTENSITY | (ColorTable[Code - 100]<<4)); + NewColor = (WORD)((NewColor & ~(0xf0)) | BACKGROUND_INTENSITY | (YoriLibColorTable[Code - 100]<<4)); } // @@ -503,7 +508,7 @@ YoriLibVtFinalColorFromSequenceEx( YoriLibInitEmptyString(&SearchString); SearchString.StartOfString = CurrentPoint; SearchString.LengthInChars = RemainingLength; - CurrentOffset = YoriLibCountStringContainingChars(&SearchString, _T("0123456789")); + CurrentOffset = YoriLibCntStringWithChars(&SearchString, _T("0123456789")); CurrentPoint += CurrentOffset; RemainingLength = RemainingLength - CurrentOffset; @@ -539,7 +544,7 @@ YoriLibVtFinalColorFromSequenceEx( @return TRUE to indicate successful completion, FALSE to indicate failure. */ BOOL -YoriLibVtFinalColorFromSequence( +YoriLibVtFinalColorFromEsc( __in WORD InitialColor, __in PCYORI_STRING EscapeSequence, __out PWORD FinalColor @@ -547,7 +552,7 @@ YoriLibVtFinalColorFromSequence( { WORD InitialComponentsUsed; - return YoriLibVtFinalColorFromSequenceEx(InitialColor, EscapeSequence, FinalColor, &InitialComponentsUsed); + return YoriLibVtFinalColorFromEscEx(InitialColor, EscapeSequence, FinalColor, &InitialComponentsUsed); } /** @@ -563,7 +568,7 @@ YoriLibVtFinalColorFromSequence( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibConsoleProcessAndOutputEscape( +YoriLibConsoleProcOutputEsc( __in HANDLE hOutput, __in PCYORI_STRING String, __inout PYORI_MAX_UNSIGNED_T Context @@ -595,7 +600,7 @@ YoriLibConsoleProcessAndOutputEscape( // the result without querying the previous color. // - if (YoriLibVtFinalColorFromSequenceEx(0, String, &NewColor, &InitialComponentsUsed)) { + if (YoriLibVtFinalColorFromEscEx(0, String, &NewColor, &InitialComponentsUsed)) { if (InitialComponentsUsed == 0) { SetConsoleTextAttribute(hOutput, NewColor); *Context = ((1 << 16) | NewColor); @@ -616,7 +621,7 @@ YoriLibConsoleProcessAndOutputEscape( } - if (YoriLibVtFinalColorFromSequence(NewColor, String, &NewColor)) { + if (YoriLibVtFinalColorFromEsc(NewColor, String, &NewColor)) { SetConsoleTextAttribute(hOutput, NewColor); *Context = ((1 << 16) | NewColor); } @@ -632,14 +637,14 @@ YoriLibConsoleProcessAndOutputEscape( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibConsoleSetFunctions( +YoriLibConsoleSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ) { CallbackFunctions->InitializeStream = YoriLibConsoleInitializeStream; CallbackFunctions->EndStream = YoriLibConsoleEndStream; - CallbackFunctions->ProcessAndOutputText = YoriLibConsoleProcessAndOutputText; - CallbackFunctions->ProcessAndOutputEscape = YoriLibConsoleProcessAndOutputEscape; + CallbackFunctions->ProcessAndOutputText = YoriLibConsoleProcOutputText; + CallbackFunctions->ProcessAndOutputEscape = YoriLibConsoleProcOutputEsc; CallbackFunctions->Context = 0; return TRUE; } @@ -653,14 +658,14 @@ YoriLibConsoleSetFunctions( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibConsoleNoEscapeSetFunctions( +YoriLibConsoleNoEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ) { CallbackFunctions->InitializeStream = YoriLibConsoleInitializeStream; CallbackFunctions->EndStream = YoriLibConsoleEndStream; - CallbackFunctions->ProcessAndOutputText = YoriLibConsoleProcessAndOutputText; - CallbackFunctions->ProcessAndOutputEscape = YoriLibConsoleProcessAndIgnoreEscape; + CallbackFunctions->ProcessAndOutputText = YoriLibConsoleProcOutputText; + CallbackFunctions->ProcessAndOutputEscape = YoriLibConsoleProcIgnoreEsc; CallbackFunctions->Context = 0; return TRUE; } @@ -674,14 +679,14 @@ YoriLibConsoleNoEscapeSetFunctions( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibConsoleIncludeEscapeSetFunctions( +YoriLibConsoleIncludeEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ) { CallbackFunctions->InitializeStream = YoriLibConsoleInitializeStream; CallbackFunctions->EndStream = YoriLibConsoleEndStream; - CallbackFunctions->ProcessAndOutputText = YoriLibConsoleProcessAndOutputText; - CallbackFunctions->ProcessAndOutputEscape = YoriLibConsoleProcessAndOutputText; + CallbackFunctions->ProcessAndOutputText = YoriLibConsoleProcOutputText; + CallbackFunctions->ProcessAndOutputEscape = YoriLibConsoleProcOutputText; CallbackFunctions->Context = 0; return TRUE; } @@ -746,14 +751,14 @@ YoriLibUtf8TextEndStream( @return TRUE for success, FALSE on failure. */ BOOL -YoriLibUtf8TextProcessAndOutputText( +YoriLibUtf8TextProcOutputText( __in HANDLE hOutput, __in PCYORI_STRING String, __inout PYORI_MAX_UNSIGNED_T Context ) { UNREFERENCED_PARAMETER(Context); - return YoriLibOutputTextToMultibyteNormalizeLineEnding(hOutput, String); + return YoriLibOutputTextMbyteFixLnEnd(hOutput, String); } /** @@ -768,7 +773,7 @@ YoriLibUtf8TextProcessAndOutputText( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibUtf8TextProcessAndOutputEscape( +YoriLibUtf8TextProcOutputEsc( __in HANDLE hOutput, __in PCYORI_STRING String, __inout PYORI_MAX_UNSIGNED_T Context @@ -790,14 +795,14 @@ YoriLibUtf8TextProcessAndOutputEscape( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibUtf8TextNoEscapesSetFunctions( +YoriLibUtf8TextNoEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ) { CallbackFunctions->InitializeStream = YoriLibUtf8TextInitializeStream; CallbackFunctions->EndStream = YoriLibUtf8TextEndStream; - CallbackFunctions->ProcessAndOutputText = YoriLibUtf8TextProcessAndOutputText; - CallbackFunctions->ProcessAndOutputEscape = YoriLibUtf8TextProcessAndOutputEscape; + CallbackFunctions->ProcessAndOutputText = YoriLibUtf8TextProcOutputText; + CallbackFunctions->ProcessAndOutputEscape = YoriLibUtf8TextProcOutputEsc; CallbackFunctions->Context = 0; return TRUE; } @@ -811,171 +816,23 @@ YoriLibUtf8TextNoEscapesSetFunctions( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibUtf8TextWithEscapesSetFunctions( +YoriLibUtf8TextWithEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ) { CallbackFunctions->InitializeStream = YoriLibUtf8TextInitializeStream; CallbackFunctions->EndStream = YoriLibUtf8TextEndStream; - CallbackFunctions->ProcessAndOutputText = YoriLibUtf8TextProcessAndOutputText; - CallbackFunctions->ProcessAndOutputEscape = YoriLibUtf8TextProcessAndOutputText; + CallbackFunctions->ProcessAndOutputText = YoriLibUtf8TextProcOutputText; + CallbackFunctions->ProcessAndOutputEscape = YoriLibUtf8TextProcOutputText; CallbackFunctions->Context = 0; return TRUE; } -// -// Debugger functions -// - -/** - Initialize the output stream with any header information. For debugger - output, this is pointless. - - @param hOutput The output stream to initialize. - - @param Context Pointer to context (unused.) - - @return TRUE for success, FALSE on failure. - */ -BOOL -YoriLibDebuggerInitializeStream( - __in HANDLE hOutput, - __inout PYORI_MAX_UNSIGNED_T Context - ) -{ - UNREFERENCED_PARAMETER(hOutput); - UNREFERENCED_PARAMETER(Context); - - return TRUE; -} - -/** - End processing for the specified stream. For debugger output, this is - pointless. - - @param hOutput Handle to the output device, ignored for debugger. - - @param Context Pointer to context (unused.) - - @return TRUE for success, FALSE on failure. - */ -BOOL -YoriLibDebuggerEndStream( - __in HANDLE hOutput, - __inout PYORI_MAX_UNSIGNED_T Context - ) -{ - UNREFERENCED_PARAMETER(hOutput); - UNREFERENCED_PARAMETER(Context); - - return TRUE; -} - -/** - Output text between escapes to the debugger. - - @param hOutput Handle to the output device, ignored for debugger. - - @param String Pointer to the string to output. - - @param Context Pointer to context (unused.) - - @return TRUE for success, FALSE on failure. - */ -BOOL -YoriLibDebuggerProcessAndOutputText( - __in HANDLE hOutput, - __in PCYORI_STRING String, - __inout PYORI_MAX_UNSIGNED_T Context - ) -{ - TCHAR StackBuf[64 + 1]; - LPTSTR Buffer; - DWORD ReadIndex; - DWORD WriteIndex; - - UNREFERENCED_PARAMETER(hOutput); - UNREFERENCED_PARAMETER(Context); - - if (String->LengthInChars <= 64) { - Buffer = StackBuf; - } else { - Buffer = YoriLibMalloc((String->LengthInChars + 1) * sizeof(TCHAR)); - if (Buffer == NULL) { - return FALSE; - } - } - - WriteIndex = 0; - for (ReadIndex = 0; ReadIndex < String->LengthInChars; ReadIndex++) { - if (String->StartOfString[ReadIndex] == '\r') { - if (ReadIndex + 1 < String->LengthInChars && - String->StartOfString[ReadIndex] == '\n') { - - ReadIndex++; - } else { - Buffer[WriteIndex++] = '\n'; - } - } else { - Buffer[WriteIndex++] = String->StartOfString[ReadIndex]; - } - } - - Buffer[WriteIndex] = '\0'; - - OutputDebugString(Buffer); - if (Buffer != StackBuf) { - YoriLibFree(Buffer); - } - - return TRUE; -} - -/** - A dummy callback function to receive an escape and not do anything with it. - - @param hOutput Handle to the output device (ignored.) - - @param String Pointer to a buffer describing the escape (ignored.) - - @param Context Pointer to context (unused.) - - @return TRUE for success, FALSE for failure. - */ -BOOL -YoriLibDebuggerProcessAndOutputEscape( - __in HANDLE hOutput, - __in PCYORI_STRING String, - __inout PYORI_MAX_UNSIGNED_T Context - ) -{ - UNREFERENCED_PARAMETER(hOutput); - UNREFERENCED_PARAMETER(String); - UNREFERENCED_PARAMETER(Context); - - return TRUE; -} - /** - Initialize callback functions to a set which will output text to the debugger - and remove any escape sequences. - - @param CallbackFunctions The callback functions to initialize. - - @return TRUE for success, FALSE for failure. + A list of break characters that indicate a range of text up to this char + should be processed as text. */ -BOOL -YoriLibDebuggerSetFunctions( - __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions - ) -{ - CallbackFunctions->InitializeStream = YoriLibDebuggerInitializeStream; - CallbackFunctions->EndStream = YoriLibDebuggerEndStream; - CallbackFunctions->ProcessAndOutputText = YoriLibDebuggerProcessAndOutputText; - CallbackFunctions->ProcessAndOutputEscape = YoriLibDebuggerProcessAndOutputEscape; - CallbackFunctions->Context = 0; - return TRUE; -} +CONST TCHAR YoriLibVtEscape[] = {27, '\0'}; /** Walk through an input string and process any VT100/ANSI escapes by invoking @@ -993,7 +850,7 @@ YoriLibDebuggerSetFunctions( @return TRUE to indicate success, FALSE to indicate failure. */ BOOL -YoriLibProcessVtEscapesOnOpenStream( +YoriLibProcVtEscOnOpenStream( __in LPTSTR String, __in YORI_ALLOC_SIZE_T StringLength, __in HANDLE hOutput, @@ -1003,7 +860,6 @@ YoriLibProcessVtEscapesOnOpenStream( LPTSTR CurrentPoint; YORI_ALLOC_SIZE_T CurrentOffset; YORI_ALLOC_SIZE_T PreviouslyConsumed; - TCHAR VtEscape[] = {27, '\0'}; YORI_STRING SearchString; YORI_STRING DisplayString; @@ -1012,7 +868,7 @@ YoriLibProcessVtEscapesOnOpenStream( YoriLibInitEmptyString(&DisplayString); SearchString.StartOfString = CurrentPoint; SearchString.LengthInChars = StringLength; - CurrentOffset = YoriLibCountStringNotContainingChars(&SearchString, VtEscape); + CurrentOffset = YoriLibCntStringNotWithChars(&SearchString, YoriLibVtEscape); PreviouslyConsumed = 0; while (TRUE) { @@ -1048,7 +904,7 @@ YoriLibProcessVtEscapesOnOpenStream( YORI_ALLOC_SIZE_T EndOfEscape; SearchString.StartOfString = &CurrentPoint[2]; SearchString.LengthInChars = StringLength - PreviouslyConsumed - 2; - EndOfEscape = YoriLibCountStringContainingChars(&SearchString, _T("0123456789;")); + EndOfEscape = YoriLibCntStringWithChars(&SearchString, _T("0123456789;")); // // If our buffer is full and we still have an incomplete escape, @@ -1100,7 +956,7 @@ YoriLibProcessVtEscapesOnOpenStream( SearchString.StartOfString = CurrentPoint; SearchString.LengthInChars = StringLength - PreviouslyConsumed; - CurrentOffset = YoriLibCountStringNotContainingChars(&SearchString, VtEscape); + CurrentOffset = YoriLibCntStringNotWithChars(&SearchString, YoriLibVtEscape); } return TRUE; @@ -1124,7 +980,7 @@ YoriLibProcessVtEscapesOnOpenStream( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibProcessVtEscapesOnNewStream( +YoriLibProcVtEscOnNewStream( __in LPTSTR String, __in YORI_ALLOC_SIZE_T StringLength, __in HANDLE hOutput, @@ -1135,7 +991,7 @@ YoriLibProcessVtEscapesOnNewStream( Callbacks->InitializeStream(hOutput, &Callbacks->Context); - Result = YoriLibProcessVtEscapesOnOpenStream(String, StringLength, hOutput, Callbacks); + Result = YoriLibProcVtEscOnOpenStream(String, StringLength, hOutput, Callbacks); Callbacks->EndStream(hOutput, &Callbacks->Context); return Result; @@ -1154,10 +1010,10 @@ YoriLibProcessVtEscapesOnNewStream( @return TRUE for success, FALSE for failure. */ -BOOL +BOOL CDECL YoriLibOutputInternal( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in LPCTSTR szFmt, __in va_list marker ) @@ -1184,19 +1040,19 @@ YoriLibOutputInternal( // if (hOut == YORI_LIB_DEBUGGER_HANDLE) { - YoriLibDebuggerSetFunctions(&Callbacks); + YoriLibDbgSetFn(&Callbacks); } else if (GetConsoleMode(hOut, &CurrentMode)) { if ((Flags & YORI_LIB_OUTPUT_STRIP_VT) != 0) { - YoriLibConsoleNoEscapeSetFunctions(&Callbacks); + YoriLibConsoleNoEscSetFn(&Callbacks); } else if ((Flags & YORI_LIB_OUTPUT_PASSTHROUGH_VT) != 0) { - YoriLibConsoleIncludeEscapeSetFunctions(&Callbacks); + YoriLibConsoleIncludeEscSetFn(&Callbacks); } else { - YoriLibConsoleSetFunctions(&Callbacks); + YoriLibConsoleSetFn(&Callbacks); } } else if ((Flags & YORI_LIB_OUTPUT_STRIP_VT) != 0) { - YoriLibUtf8TextNoEscapesSetFunctions(&Callbacks); + YoriLibUtf8TextNoEscSetFn(&Callbacks); } else { - YoriLibUtf8TextWithEscapesSetFunctions(&Callbacks); + YoriLibUtf8TextWithEscSetFn(&Callbacks); } len = YoriLibVSPrintfSize(szFmt, marker); @@ -1214,7 +1070,7 @@ YoriLibOutputInternal( len = YoriLibVSPrintf(buf, len, szFmt, marker); __analysis_assume(hOut != 0); - Result = YoriLibProcessVtEscapesOnNewStream(buf, len, hOut, &Callbacks); + Result = YoriLibProcVtEscOnNewStream(buf, len, hOut, &Callbacks); if (buf != stack_buf) { YoriLibFree(buf); @@ -1233,7 +1089,7 @@ YoriLibOutputInternal( */ BOOL YoriLibOutput( - __in DWORD Flags, + __in WORD Flags, __in LPCTSTR szFmt, ... ) @@ -1275,7 +1131,7 @@ YoriLibOutput( BOOL YoriLibOutputString( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in PYORI_STRING String ) { @@ -1290,19 +1146,19 @@ YoriLibOutputString( if (GetConsoleMode(hOut, &CurrentMode)) { if ((Flags & YORI_LIB_OUTPUT_STRIP_VT) != 0) { - YoriLibConsoleNoEscapeSetFunctions(&Callbacks); + YoriLibConsoleNoEscSetFn(&Callbacks); } else if ((Flags & YORI_LIB_OUTPUT_PASSTHROUGH_VT) != 0) { - YoriLibConsoleIncludeEscapeSetFunctions(&Callbacks); + YoriLibConsoleIncludeEscSetFn(&Callbacks); } else { - YoriLibConsoleSetFunctions(&Callbacks); + YoriLibConsoleSetFn(&Callbacks); } } else if ((Flags & YORI_LIB_OUTPUT_STRIP_VT) != 0) { - YoriLibUtf8TextNoEscapesSetFunctions(&Callbacks); + YoriLibUtf8TextNoEscSetFn(&Callbacks); } else { - YoriLibUtf8TextWithEscapesSetFunctions(&Callbacks); + YoriLibUtf8TextWithEscSetFn(&Callbacks); } - Result = YoriLibProcessVtEscapesOnNewStream(String->StartOfString, String->LengthInChars, hOut, &Callbacks); + Result = YoriLibProcVtEscOnNewStream(String->StartOfString, String->LengthInChars, hOut, &Callbacks); return Result; } @@ -1321,7 +1177,7 @@ YoriLibOutputString( BOOL YoriLibOutputToDevice( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in LPCTSTR szFmt, ... ) @@ -1361,9 +1217,9 @@ YoriLibVtStringForTextAttribute( CHAR AnsiForeground; CHAR AnsiBackground; - if (String->LengthAllocated < YORI_MAX_INTERNAL_VT_ESCAPE_CHARS) { + if (String->LengthAllocated < YORI_MAX_VT_ESCAPE_CHARS) { YoriLibFreeStringContents(String); - if (!YoriLibAllocateString(String, YORI_MAX_INTERNAL_VT_ESCAPE_CHARS)) { + if (!YoriLibAllocateString(String, YORI_MAX_VT_ESCAPE_CHARS)) { return FALSE; } } @@ -1415,14 +1271,14 @@ YoriLibVtStringForTextAttribute( @return TRUE to indicate success, FALSE to indicate failure. */ BOOL -YoriLibVtSetConsoleTextAttributeOnDevice( +YoriLibVtSetConsoleTextAttrDev( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in UCHAR Ctrl, __in WORD Attribute ) { - TCHAR OutputStringBuffer[YORI_MAX_INTERNAL_VT_ESCAPE_CHARS]; + TCHAR OutputStringBuffer[YORI_MAX_VT_ESCAPE_CHARS]; YORI_STRING OutputString; YoriLibInitEmptyString(&OutputString); @@ -1460,8 +1316,8 @@ YoriLibVtSetConsoleTextAttributeOnDevice( @return TRUE for success, FALSE for failure. */ BOOL -YoriLibVtSetConsoleTextAttribute( - __in DWORD Flags, +YoriLibVtSetConsoleTextAttr( + __in WORD Flags, __in WORD Attribute ) { @@ -1471,7 +1327,7 @@ YoriLibVtSetConsoleTextAttribute( } else { hOut = GetStdHandle(STD_OUTPUT_HANDLE); } - return YoriLibVtSetConsoleTextAttributeOnDevice(hOut, Flags, 0, Attribute); + return YoriLibVtSetConsoleTextAttrDev(hOut, Flags, 0, Attribute); } /** @@ -1509,7 +1365,7 @@ YoriLibStripVtEscapes( YoriLibInitEmptyString(&EscapeSubset); EscapeSubset.StartOfString = &VtText->StartOfString[CharIndex + 2]; EscapeSubset.LengthInChars = VtText->LengthInChars - CharIndex - 2; - EndOfEscape = YoriLibCountStringContainingChars(&EscapeSubset, _T("0123456789;")); + EndOfEscape = YoriLibCntStringWithChars(&EscapeSubset, _T("0123456789;")); if (VtText->LengthInChars > CharIndex + 2 + EndOfEscape) { EscapeChars += 3 + EndOfEscape; CharIndex += 2 + EndOfEscape; @@ -1533,7 +1389,7 @@ YoriLibStripVtEscapes( YoriLibInitEmptyString(&EscapeSubset); EscapeSubset.StartOfString = &VtText->StartOfString[CharIndex + 2]; EscapeSubset.LengthInChars = VtText->LengthInChars - CharIndex - 2; - EndOfEscape = YoriLibCountStringContainingChars(&EscapeSubset, _T("0123456789;")); + EndOfEscape = YoriLibCntStringWithChars(&EscapeSubset, _T("0123456789;")); if (VtText->LengthInChars > CharIndex + 2 + EndOfEscape) { EscapeChars += 3 + EndOfEscape; CharIndex += 2 + EndOfEscape; @@ -1584,7 +1440,7 @@ YoriLibGetWindowDimensions( } if (Width != NULL) { - if (!YoriLibGetEnvironmentVariableAsNumber(_T("COLUMNS"), &Temp)) { + if (!YoriLibGetEnvVarAsNumber(_T("COLUMNS"), &Temp)) { *Width = 80; } else { *Width = (WORD)Temp; @@ -1592,7 +1448,7 @@ YoriLibGetWindowDimensions( } if (Height != NULL) { - if (!YoriLibGetEnvironmentVariableAsNumber(_T("LINES"), &Temp)) { + if (!YoriLibGetEnvVarAsNumber(_T("LINES"), &Temp)) { *Height = 25; } else { *Height = (WORD)Temp; @@ -1677,7 +1533,7 @@ YoriLibQueryConsoleCapabilities( // YoriLibInitEmptyString(&TermString); - if (!YoriLibAllocateAndGetEnvironmentVariable(_T("YORITERM"), &TermString)) { + if (!YoriLibAllocateAndGetEnvVar(_T("YORITERM"), &TermString)) { return TRUE; } @@ -1696,19 +1552,19 @@ YoriLibQueryConsoleCapabilities( SubString.LengthInChars = (YORI_ALLOC_SIZE_T)(NextSeperator - SubString.StartOfString); } - if (YoriLibCompareStringWithLiteralInsensitive(&SubString, _T("color")) == 0) { + if (YoriLibCompareStringLitIns(&SubString, _T("color")) == 0) { if (SupportsColor != NULL) { *SupportsColor = TRUE; } } - if (YoriLibCompareStringWithLiteralInsensitive(&SubString, _T("extendedchars")) == 0) { + if (YoriLibCompareStringLitIns(&SubString, _T("extendedchars")) == 0) { if (SupportsExtendedChars != NULL) { *SupportsExtendedChars = TRUE; } } - if (YoriLibCompareStringWithLiteralInsensitive(&SubString, _T("autolinewrap")) == 0) { + if (YoriLibCompareStringLitIns(&SubString, _T("autolinewrap")) == 0) { if (SupportsAutoLineWrap != NULL) { *SupportsAutoLineWrap = TRUE; } diff --git a/misc/tools/yori/yori/lib/ylhomedr.c b/misc/tools/yori/yori/lib/ylhomedr.c new file mode 100644 index 0000000000..c0f198750d --- /dev/null +++ b/misc/tools/yori/yori/lib/ylhomedr.c @@ -0,0 +1,572 @@ +/** + * @file lib/ylhomedr.c + * + * Expansion of home directory locations to their full counterparts. + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Convert a specified shell folder, by a known folder GUID, into its string + form. This function is only available in Vista+. + + @param FolderType The identifier of the directory. + + @param ExpandedSymbol On successful completion, populated with a string + identifying the path to the directory. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibExpandShellDirectoryGuid( + __in CONST GUID * FolderType, + __inout PYORI_STRING ExpandedSymbol + ) +{ + PWSTR ExpandedString; + YORI_ALLOC_SIZE_T LocationLength; + + YoriLibLoadShell32Functions(); + YoriLibLoadOle32Functions(); + + if (DllShell32.pSHGetKnownFolderPath == NULL || + DllOle32.pCoTaskMemFree == NULL) { + return FALSE; + } + + + if (DllShell32.pSHGetKnownFolderPath(FolderType, 0, NULL, &ExpandedString) != 0) { + return FALSE; + } + + LocationLength = (YORI_ALLOC_SIZE_T)_tcslen(ExpandedString); + + if (!YoriLibAllocateString(ExpandedSymbol, LocationLength + 1)) { + DllOle32.pCoTaskMemFree(ExpandedString); + return FALSE; + } + + memcpy(ExpandedSymbol->StartOfString, ExpandedString, (LocationLength + 1) * sizeof(TCHAR)); + ExpandedSymbol->LengthInChars = LocationLength; + + DllOle32.pCoTaskMemFree(ExpandedString); + return TRUE; +} + + +/** + Convert a specified shell folder, by identifier, into its string form. + + @param FolderType The identifier of the directory. + + @param ExpandedSymbol On successful completion, populated with a string + identifying the path to the directory. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibExpandShellDirectory( + __in INT FolderType, + __inout PYORI_STRING ExpandedSymbol + ) +{ + YoriLibLoadShell32Functions(); + if (DllShell32.pSHGetSpecialFolderPathW == NULL) { + YoriLibLoadShfolderFunctions(); + if (DllShfolder.pSHGetFolderPathW == NULL) { + return FALSE; + } + } + + if (!YoriLibAllocateString(ExpandedSymbol, MAX_PATH)) { + return FALSE; + } + + ExpandedSymbol->StartOfString[0] = '\0'; + + if (DllShell32.pSHGetSpecialFolderPathW != NULL) { + if (DllShell32.pSHGetSpecialFolderPathW(NULL, ExpandedSymbol->StartOfString, FolderType, FALSE) < 0) { + YoriLibFreeStringContents(ExpandedSymbol); + return FALSE; + } + } else { + if (!SUCCEEDED(DllShfolder.pSHGetFolderPathW(NULL, FolderType, NULL, 0, ExpandedSymbol->StartOfString))) { + YoriLibFreeStringContents(ExpandedSymbol); + return FALSE; + } + } + + ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)_tcslen(ExpandedSymbol->StartOfString); + + return TRUE; +} + +/** + A mapping between a '~' prefixed special directory name and a CSIDL that the + shell uses to identify it. + */ +typedef struct _YORI_LIB_CSIDL_MAP { + + /** + The special directory name. + */ + LPTSTR DirName; + + /** + The corresponding CSIDL. + */ + DWORD Csidl; +} YORI_LIB_CSIDL_MAP, *PYORI_LIB_CSIDL_MAP; + +/** + A table of special directory names whose locations can be obtained via + SHGetSpecialFolderPath or SHGetFolderPath. + */ +CONST YORI_LIB_CSIDL_MAP YoriLibSpecialDirectoryMap[] = { + {_T("~APPDATA"), CSIDL_APPDATA}, + {_T("~APPDATALOCAL"), CSIDL_LOCALAPPDATA}, + {_T("~COMMONAPPDATA"), CSIDL_COMMON_APPDATA}, + {_T("~COMMONDESKTOP"), CSIDL_COMMON_DESKTOPDIRECTORY}, + {_T("~COMMONDOCUMENTS"), CSIDL_COMMON_DOCUMENTS}, + {_T("~COMMONPROGRAMS"), CSIDL_COMMON_PROGRAMS}, + {_T("~COMMONSTART"), CSIDL_COMMON_STARTMENU}, + {_T("~DESKTOP"), CSIDL_DESKTOPDIRECTORY}, + {_T("~DOCUMENTS"), CSIDL_PERSONAL}, + {_T("~LOCALAPPDATA"), CSIDL_LOCALAPPDATA}, + {_T("~PROGRAMFILES"), CSIDL_PROGRAM_FILES}, +#ifdef _WIN64 + {_T("~PROGRAMFILES32"), CSIDL_PROGRAM_FILESX86}, + {_T("~PROGRAMFILES64"), CSIDL_PROGRAM_FILES}, +#else + {_T("~PROGRAMFILES32"), CSIDL_PROGRAM_FILES}, +#endif + {_T("~PROGRAMS"), CSIDL_PROGRAMS}, + {_T("~START"), CSIDL_STARTMENU}, + {_T("~STARTUP"), CSIDL_STARTUP}, + {_T("~SYSTEM"), CSIDL_SYSTEM}, + {_T("~WINDOWS"), CSIDL_WINDOWS} +}; + +/** + Translate a special directory name into its expanded form if the directory + name is defined via a CSIDL that can be resolved with SHGetSpecialFolderPath + et al. + + @param SymbolToExpand The special directory name, prefixed with '~'. + + @param ExpandedSymbol On successful completion, populated with the directory + corresponding to the special directory name. + + @return TRUE to indicate a match was found and expansion successfully + performed, FALSE if the symbol name should be checked for other + matches. + */ +__success(return) +BOOL +YoriLibExpandDirectoryFromMap( + __in PYORI_STRING SymbolToExpand, + __inout PYORI_STRING ExpandedSymbol + ) +{ + DWORD Index; + + for (Index = 0; Index < sizeof(YoriLibSpecialDirectoryMap)/sizeof(YoriLibSpecialDirectoryMap[0]); Index++) { + if (YoriLibCompareStringLitIns(SymbolToExpand, YoriLibSpecialDirectoryMap[Index].DirName) == 0) { + return YoriLibExpandShellDirectory(YoriLibSpecialDirectoryMap[Index].Csidl, ExpandedSymbol); + } + } + + return FALSE; +} + +/** + Expand a directory component in a path, specified via a tilde and description, + into its corresponding physical directory. + + @param SymbolToExpand The tilde based directory reference. + + @param ExpandedSymbol On successful completion, populated with the directory + corresponding to the reference. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibExpandHomeSymbol( + __in PYORI_STRING SymbolToExpand, + __inout PYORI_STRING ExpandedSymbol + ) +{ + if (YoriLibCompareStringLitIns(SymbolToExpand, _T("~")) == 0) { + DWORD BytesNeeded; + BytesNeeded = GetEnvironmentVariable(_T("HOMEDRIVE"), NULL, 0) + GetEnvironmentVariable(_T("HOMEPATH"), NULL, 0); + if (!YoriLibIsSizeAllocatable(BytesNeeded)) { + return FALSE; + } + + if (!YoriLibAllocateString(ExpandedSymbol, (YORI_ALLOC_SIZE_T)BytesNeeded)) { + return FALSE; + } + + ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)GetEnvironmentVariable(_T("HOMEDRIVE"), ExpandedSymbol->StartOfString, ExpandedSymbol->LengthAllocated); + ExpandedSymbol->LengthInChars = ExpandedSymbol->LengthInChars + + (YORI_ALLOC_SIZE_T)GetEnvironmentVariable(_T("HOMEPATH"), + &ExpandedSymbol->StartOfString[ExpandedSymbol->LengthInChars], + ExpandedSymbol->LengthAllocated - ExpandedSymbol->LengthInChars); + return TRUE; + } else if (YoriLibCompareStringLitIns(SymbolToExpand, _T("~APPDIR")) == 0) { + LPTSTR FinalSlash; + + // + // Unlike most other Win32 APIs, this one has no way to indicate + // how much space it needs. We can be wasteful here though, since + // it'll be freed immediately. + // + + if (!YoriLibAllocateString(ExpandedSymbol, 32768)) { + return FALSE; + } + + ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)GetModuleFileName(NULL, ExpandedSymbol->StartOfString, ExpandedSymbol->LengthAllocated); + FinalSlash = YoriLibFindRightMostCharacter(ExpandedSymbol, '\\'); + if (FinalSlash == NULL) { + YoriLibFreeStringContents(ExpandedSymbol); + return FALSE; + } + + ExpandedSymbol->LengthInChars = (YORI_ALLOC_SIZE_T)(FinalSlash - ExpandedSymbol->StartOfString); + return TRUE; + } else if (YoriLibExpandDirectoryFromMap(SymbolToExpand, ExpandedSymbol)) { + return TRUE; + } else if (YoriLibCompareStringLitIns(SymbolToExpand, _T("~DOWNLOADS")) == 0) { + BOOL Result; + + // + // If a Vista era function to find the Downloads folder exists, + // use it. + // + + YoriLibLoadShell32Functions(); + if (DllShell32.pSHGetKnownFolderPath != NULL) { + return YoriLibExpandShellDirectoryGuid(&FOLDERID_Downloads, ExpandedSymbol); + } + + // + // If not, Downloads is a subdirectory of Documents. This function + // allocates a MAX_PATH buffer because the underlying API doesn't + // specify a length. + // + + Result = YoriLibExpandShellDirectory(CSIDL_PERSONAL, ExpandedSymbol); + if (Result) { + if (ExpandedSymbol->LengthInChars + sizeof("\\Downloads") < ExpandedSymbol->LengthAllocated) { + memcpy(&ExpandedSymbol->StartOfString[ExpandedSymbol->LengthInChars], + _T("\\Downloads"), + (sizeof("\\Downloads") - 1) * sizeof(TCHAR)); + ExpandedSymbol->LengthInChars = ExpandedSymbol->LengthInChars + sizeof("\\Downloads") - 1; + return TRUE; + } + } + } + + ExpandedSymbol->StartOfString = SymbolToExpand->StartOfString; + ExpandedSymbol->LengthInChars = SymbolToExpand->LengthInChars; + return TRUE; +} + +/** + Expand all tilde based home references in a file path and return the + expanded form. + + @param FileString A string specifying a path that may include tilde based + directory references. + + @param ExpandedString On successful completion, contains the corresponding + full path. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibExpandHomeDirectories( + __in PYORI_STRING FileString, + __out PYORI_STRING ExpandedString + ) +{ + YORI_ALLOC_SIZE_T CharIndex; + YORI_STRING BeforeSymbol; + YORI_STRING AfterSymbol; + YORI_STRING SymbolToExpand; + YORI_STRING ExpandedSymbol; + BOOL PreviousWasSeperator = TRUE; + + for (CharIndex = 0; CharIndex < FileString->LengthInChars; CharIndex++) { + if (FileString->StartOfString[CharIndex] == '~' && + (CharIndex == 0 || YoriLibIsSep(FileString->StartOfString[CharIndex - 1]))) { + + YoriLibInitEmptyString(&BeforeSymbol); + YoriLibInitEmptyString(&AfterSymbol); + YoriLibInitEmptyString(&SymbolToExpand); + + BeforeSymbol.StartOfString = FileString->StartOfString; + BeforeSymbol.LengthInChars = CharIndex; + + SymbolToExpand.StartOfString = &FileString->StartOfString[CharIndex]; + SymbolToExpand.LengthInChars = 0; + + while (CharIndex < FileString->LengthInChars && !YoriLibIsSep(FileString->StartOfString[CharIndex])) { + SymbolToExpand.LengthInChars++; + CharIndex++; + } + + AfterSymbol.StartOfString = &FileString->StartOfString[CharIndex]; + AfterSymbol.LengthInChars = FileString->LengthInChars - CharIndex; + + YoriLibInitEmptyString(&ExpandedSymbol); + + if (!YoriLibExpandHomeSymbol(&SymbolToExpand, &ExpandedSymbol)) { + return FALSE; + } + + YoriLibInitEmptyString(ExpandedString); + YoriLibYPrintf(ExpandedString, _T("%y%y%y"), &BeforeSymbol, &ExpandedSymbol, &AfterSymbol); + YoriLibFreeStringContents(&ExpandedSymbol); + if (ExpandedString->StartOfString != NULL) { + return TRUE; + } + return FALSE; + } + + if (YoriLibIsSep(FileString->StartOfString[CharIndex])) { + PreviousWasSeperator = TRUE; + } else { + PreviousWasSeperator = FALSE; + } + } + + return FALSE; +} + +/** + Return TRUE if the argument is a special DOS device name, FALSE if it is + a regular file. DOS device names include things like AUX, CON, PRN etc. + In Yori, a DOS device name is only a DOS device name if it does not + contain any path information. + + @param File The file name to check. + + @return TRUE to indicate this argument is a DOS device name, FALSE to + indicate that it is a regular file. + */ +BOOL +YoriLibIsFileNameDeviceName( + __in PCYORI_STRING File + ) +{ + YORI_STRING NameToCheck; + YORI_ALLOC_SIZE_T Offset; + BOOLEAN Prefixed; + + YoriLibInitEmptyString(&NameToCheck); + Offset = 0; + Prefixed = FALSE; + if (YoriLibIsPathPrefixed(File)) { + Offset = sizeof("\\\\.\\") - 1; + Prefixed = TRUE; + } + + NameToCheck.StartOfString = &File->StartOfString[Offset]; + NameToCheck.LengthInChars = File->LengthInChars - Offset; + + + // + // If it's \\.\x: treat it as a device. Note this cannot have any + // trailing characters, or it'd be a file. + // + + if (Prefixed && + NameToCheck.LengthInChars == 2) { + + if (YoriLibIsDriveLetterWithColon(&NameToCheck)) { + return TRUE; + } + } + + // + // Check for a physical drive name. Note this also checks that the + // prefix is a dot. + // + + if (Prefixed) { + if (YoriLibCompareStringLitInsCnt(File, _T("\\\\.\\PHYSICALDRIVE"), sizeof("\\\\.\\PHYSICALDRIVE") - 1) == 0 || + YoriLibCompareStringLitInsCnt(File, _T("\\\\.\\HARDDISK"), sizeof("\\\\.\\HARDDISK") - 1) == 0 || + YoriLibCompareStringLitInsCnt(File, _T("\\\\.\\CDROM"), sizeof("\\\\.\\CDROM") - 1) == 0) { + return TRUE; + } + } + + if (NameToCheck.LengthInChars < 3 || NameToCheck.LengthInChars > 4) { + return FALSE; + } + + if (YoriLibCompareStringLitIns(&NameToCheck, _T("CON")) == 0 || + YoriLibCompareStringLitIns(&NameToCheck, _T("AUX")) == 0 || + YoriLibCompareStringLitIns(&NameToCheck, _T("PRN")) == 0 || + YoriLibCompareStringLitIns(&NameToCheck, _T("NUL")) == 0) { + + return TRUE; + } + + if (NameToCheck.LengthInChars < 4) { + return FALSE; + } + + if (YoriLibCompareStringLitInsCnt(&NameToCheck, _T("LPT"), 3) == 0 && + (NameToCheck.StartOfString[3] >= '1' && NameToCheck.StartOfString[3] <= '9')) { + + return TRUE; + } + + if (YoriLibCompareStringLitInsCnt(&NameToCheck, _T("COM"), 3) == 0 && + (NameToCheck.StartOfString[3] >= '1' && NameToCheck.StartOfString[3] <= '9')) { + + return TRUE; + } + + + return FALSE; +} + +/** + Resolves a user string which must refer to a single file into a physical path + for that file. + + @param UserString The string as specified by the user. This is currently + required to be NULL terminated. + + @param ReturnEscapedPath TRUE if the resulting path should be prefixed with + \\?\, FALSE to indicate a traditional Win32 path. + + @param FullPath On successful completion, this pointer is updated to point to + the full path string. This string should be uninitialized on input and + is allocated within this routine. The caller is expected to free this + with @ref YoriLibFreeStringContents. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibUserStringToSingleFilePath( + __in PCYORI_STRING UserString, + __in BOOL ReturnEscapedPath, + __out PYORI_STRING FullPath + ) +{ + YORI_STRING PathToTranslate; + YORI_STRING YsFilePrefix; + YORI_STRING ExpandedString; + BOOL ReturnValue = FALSE; + + YoriLibInitEmptyString(&YsFilePrefix); + YsFilePrefix.StartOfString = UserString->StartOfString; + YsFilePrefix.LengthInChars = sizeof("file:///") - 1; + + YoriLibInitEmptyString(&PathToTranslate); + if (YoriLibCompareStringLitIns(&YsFilePrefix, _T("file:///")) == 0) { + PathToTranslate.StartOfString = &UserString->StartOfString[YsFilePrefix.LengthInChars]; + PathToTranslate.LengthInChars = UserString->LengthInChars - YsFilePrefix.LengthInChars; + } else { + PathToTranslate.StartOfString = UserString->StartOfString; + PathToTranslate.LengthInChars = UserString->LengthInChars; + } + + YoriLibInitEmptyString(FullPath); + + if (YoriLibExpandHomeDirectories(&PathToTranslate, &ExpandedString)) { + ReturnValue = YoriLibGetFullPathNameReturnAllocation(&ExpandedString, ReturnEscapedPath, FullPath, NULL); + YoriLibFreeStringContents(&ExpandedString); + } else { + ReturnValue = YoriLibGetFullPathNameReturnAllocation(&PathToTranslate, ReturnEscapedPath, FullPath, NULL); + } + + if (ReturnValue == 0) { + YoriLibFreeStringContents(FullPath); + return 0; + } + + return ReturnValue; +} + +/** + Checks if a file name refers to a device, and if so returns a path to the + device. If it does not refer to a device, the path is resolved into a file + path for the specified file. + + @param UserString The string as specified by the user. This is currently + required to be NULL terminated. + + @param ReturnEscapedPath TRUE if the resulting path should be prefixed with + \\?\, FALSE to indicate a traditional Win32 path. + + @param FullPath On successful completion, this pointer is updated to point to + the full path string. This string should be uninitialized on input and + is allocated within this routine. The caller is expected to free this + with @ref YoriLibFreeStringContents. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibUserStringToSingleFilePathOrDevice( + __in PCYORI_STRING UserString, + __in BOOL ReturnEscapedPath, + __out PYORI_STRING FullPath + ) +{ + if (YoriLibIsFileNameDeviceName(UserString)) { + YORI_ALLOC_SIZE_T CharsNeeded; + BOOL Prefixed; + + CharsNeeded = UserString->LengthInChars + 1; + Prefixed = YoriLibIsPathPrefixed(UserString); + + if (!Prefixed && ReturnEscapedPath) { + CharsNeeded += sizeof("\\\\.\\") - 1; + } + if (!YoriLibAllocateString(FullPath, CharsNeeded)) { + return FALSE; + } + if (!Prefixed && ReturnEscapedPath) { + FullPath->LengthInChars = YoriLibSPrintf(FullPath->StartOfString, _T("\\\\.\\%y"), UserString); + } else { + FullPath->LengthInChars = YoriLibSPrintf(FullPath->StartOfString, _T("%y"), UserString); + } + return TRUE; + } + return YoriLibUserStringToSingleFilePath(UserString, ReturnEscapedPath, FullPath); +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstralc.c b/misc/tools/yori/yori/lib/ylstralc.c new file mode 100644 index 0000000000..5b51c7df98 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstralc.c @@ -0,0 +1,301 @@ +/** + * @file lib/ylstralc.c + * + * Yori string allocation routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Initialize a Yori string with no contents. + + @param String Pointer to the string to initialize. + */ +VOID +YoriLibInitEmptyString( + __out PYORI_STRING String + ) +{ + String->MemoryToFree = NULL; + String->StartOfString = NULL; + String->LengthAllocated = 0; + String->LengthInChars = 0; +} + +/** + Free any memory being used by a Yori string. This frees the internal + string buffer, but the structure itself is caller allocated. + + @param String Pointer to the string to free. + */ +VOID +YoriLibFreeStringContents( + __inout PYORI_STRING String + ) +{ + if (String->MemoryToFree != NULL) { + YoriLibDereference(String->MemoryToFree); + } + + YoriLibInitEmptyString(String); +} + +/** + Allocates memory in a Yori string to hold a specified number of characters. + This routine will not free any previous allocation or copy any previous + string contents. + + @param String Pointer to the string to allocate. + + @param CharsToAllocate The number of characters to allocate in the string. + + @return TRUE to indicate the allocate was successful, FALSE if it was not. + */ +BOOL +YoriLibAllocateString( + __out PYORI_STRING String, + __in YORI_ALLOC_SIZE_T CharsToAllocate + ) +{ + YoriLibInitEmptyString(String); + if (CharsToAllocate > YORI_MAX_ALLOC_SIZE / sizeof(TCHAR)) { + return FALSE; + } + String->MemoryToFree = YoriLibReferencedMalloc(CharsToAllocate * sizeof(TCHAR)); + if (String->MemoryToFree == NULL) { + return FALSE; + } + String->LengthAllocated = CharsToAllocate; + String->StartOfString = String->MemoryToFree; + return TRUE; +} + +/** + Reallocates memory in a Yori string to hold a specified number of characters, + preserving any previous contents and deallocating any previous buffer. + + @param String Pointer to the string to reallocate. + + @param CharsToAllocate The number of characters to allocate in the string. + + @return TRUE to indicate the allocate was successful, FALSE if it was not. + */ +BOOL +YoriLibReallocString( + __inout PYORI_STRING String, + __in YORI_ALLOC_SIZE_T CharsToAllocate + ) +{ + LPTSTR NewMemoryToFree; + + if (CharsToAllocate <= String->LengthInChars) { + return FALSE; + } + + NewMemoryToFree = YoriLibReferencedMalloc(CharsToAllocate * sizeof(TCHAR)); + if (NewMemoryToFree == NULL) { + return FALSE; + } + + if (String->LengthInChars > 0) { + memcpy(NewMemoryToFree, String->StartOfString, String->LengthInChars * sizeof(TCHAR)); + } + + if (String->MemoryToFree) { + YoriLibDereference(String->MemoryToFree); + } + + String->MemoryToFree = NewMemoryToFree; + String->LengthAllocated = CharsToAllocate; + String->StartOfString = String->MemoryToFree; + return TRUE; +} + +/** + Reallocates memory in a Yori string to hold a specified number of characters, + without preserving contents, and deallocate the previous buffer on success. + + @param String Pointer to the string to reallocate. + + @param CharsToAllocate The number of characters to allocate in the string. + + @return TRUE to indicate the allocate was successful, FALSE if it was not. + */ +BOOL +YoriLibReallocStringNoContents( + __inout PYORI_STRING String, + __in YORI_ALLOC_SIZE_T CharsToAllocate + ) +{ + LPTSTR NewMemoryToFree; + + if (CharsToAllocate <= String->LengthInChars) { + return FALSE; + } + + NewMemoryToFree = YoriLibReferencedMalloc(CharsToAllocate * sizeof(TCHAR)); + if (NewMemoryToFree == NULL) { + return FALSE; + } + + if (String->MemoryToFree) { + YoriLibDereference(String->MemoryToFree); + } + + String->MemoryToFree = NewMemoryToFree; + String->LengthAllocated = CharsToAllocate; + String->StartOfString = String->MemoryToFree; + String->LengthInChars = 0; + return TRUE; +} + +/** + Allocate a new buffer to hold a NULL terminated form of the contents of a + Yori string. The caller should free this buffer when it is no longer needed + by calling @ref YoriLibDereference . + + @param String Pointer to the Yori string to convert into a NULL terminated + string. + + @return Pointer to a NULL terminated string, or NULL on failure. + */ +LPTSTR +YoriLibCStringFromYoriString( + __in PYORI_STRING String + ) +{ + LPTSTR Return; + + Return = YoriLibReferencedMalloc((String->LengthInChars + 1) * sizeof(TCHAR)); + if (Return == NULL) { + return NULL; + } + + memcpy(Return, String->StartOfString, String->LengthInChars * sizeof(TCHAR)); + Return[String->LengthInChars] = '\0'; + return Return; +} + +/** + Create a Yori string that points to a previously existing NULL terminated + string constant. The lifetime of the buffer containing the string is + managed by the caller. + + @param String Pointer to the Yori string to initialize with a preexisting + NULL terminated string. + + @param Value Pointer to the NULL terminated string to initialize String with. + */ +VOID +YoriLibConstantString( + __out _Post_satisfies_(String->StartOfString != NULL) PYORI_STRING String, + __in LPCTSTR Value + ) +{ + String->MemoryToFree = NULL; + String->StartOfString = (LPTSTR)Value; + String->LengthInChars = (YORI_ALLOC_SIZE_T)_tcslen(Value); + String->LengthAllocated = String->LengthInChars + 1; +} + +/** + Copy the contents of one Yori string to another by referencing any existing + allocation. + + @param Dest The Yori string to be populated with new contents. This string + will be reinitialized, and this function makes no attempt to free or + preserve previous contents. + + @param Src The string that contains contents to propagate into the new + string. + */ +VOID +YoriLibCloneString( + __out PYORI_STRING Dest, + __in PYORI_STRING Src + ) +{ + if (Src->MemoryToFree != NULL) { + YoriLibReference(Src->MemoryToFree); + } + + memcpy(Dest, Src, sizeof(YORI_STRING)); +} + +/** + Copy the contents of one Yori string to another by deep copying the string + contents. + + @param Dest The Yori string to be populated with new contents. This string + will be allocated, and this function makes no attempt to free or + preserve previous contents. + + @param Src The string that contains contents to propagate into the new + string. + */ +BOOLEAN +YoriLibCopyString( + __out PYORI_STRING Dest, + __in PCYORI_STRING Src + ) +{ + if (!YoriLibAllocateString(Dest, Src->LengthInChars + 1)) { + return FALSE; + } + + memcpy(Dest->StartOfString, Src->StartOfString, Src->LengthInChars * sizeof(TCHAR)); + Dest->LengthInChars = Src->LengthInChars; + Dest->StartOfString[Dest->LengthInChars] = '\0'; + return TRUE; +} + +/** + Return TRUE if the Yori string is NULL terminated. If it is not NULL + terminated, return FALSE. + + @param String Pointer to the string to check. + + @return TRUE if the string is NULL terminated, FALSE if it is not. + */ +BOOL +YoriLibIsStringNullTerminated( + __in PCYORI_STRING String + ) +{ + // + // Check that the string is of a sane size. This is really to check + // whether the string has been initialized and populated correctly. + // + + ASSERT(String->LengthAllocated <= 0x1000000); + + if (String->LengthAllocated > String->LengthInChars && + String->StartOfString[String->LengthInChars] == '\0') { + + return TRUE; + } + return FALSE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrcat.c b/misc/tools/yori/yori/lib/ylstrcat.c new file mode 100644 index 0000000000..23bb106b58 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrcat.c @@ -0,0 +1,99 @@ +/** + * @file lib/ylstrcat.c + * + * Yori string concatenation routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Concatenate one yori string to an existing yori string. Note the first + string may be reallocated within this routine and the caller is expected to + free it with @ref YoriLibFreeStringContents . + + @param String The first string, which will be updated to have the contents + of the second string appended to it. + + @param AppendString The string to copy to the end of the first string. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOLEAN +YoriLibStringConcat( + __inout PYORI_STRING String, + __in PCYORI_STRING AppendString + ) +{ + DWORD LengthRequired; + + LengthRequired = String->LengthInChars + AppendString->LengthInChars + 1; + if (!YoriLibIsSizeAllocatable(LengthRequired)) { + return FALSE; + } + if (LengthRequired > String->LengthAllocated) { + if (YoriLibIsSizeAllocatable(LengthRequired + 0x100)) { + LengthRequired = LengthRequired + 0x100; + } + if (!YoriLibReallocString(String, (YORI_ALLOC_SIZE_T)LengthRequired)) { + return FALSE; + } + } + + memcpy(&String->StartOfString[String->LengthInChars], AppendString->StartOfString, AppendString->LengthInChars * sizeof(TCHAR)); + String->LengthInChars = String->LengthInChars + AppendString->LengthInChars; + String->StartOfString[String->LengthInChars] = '\0'; + return TRUE; +} + +/** + Concatenate a literal string to an existing yori string. Note the first + string may be reallocated within this routine and the caller is expected to + free it with @ref YoriLibFreeStringContents . + + @param String The first string, which will be updated to have the contents + of the second string appended to it. + + @param AppendString The string to copy to the end of the first string. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +BOOLEAN +YoriLibStringConcatWithLiteral( + __inout PYORI_STRING String, + __in LPCTSTR AppendString + ) +{ + YORI_STRING AppendYoriString; + + // + // We need to length count the literal to ensure buffer sizes anyway, + // so convert it into a length counted string. + // + + YoriLibConstantString(&AppendYoriString, AppendString); + return YoriLibStringConcat(String, &AppendYoriString); +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrcmp.c b/misc/tools/yori/yori/lib/ylstrcmp.c new file mode 100644 index 0000000000..803ce4912d --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrcmp.c @@ -0,0 +1,359 @@ +/** + * @file lib/ylstrcmp.c + * + * Yori string comparison routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Compare a Yori string against a NULL terminated string up to a specified + maximum number of characters. If the strings are equal, return zero; if + the first string is lexicographically earlier than the second, return + negative; if the second string is lexicographically earlier than the first, + return positive. + + @param Str1 The first (Yori) string to compare. + + @param str2 The second (NULL terminated) string to compare. + + @param count The number of characters to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringLitCnt( + __in PCYORI_STRING Str1, + __in LPCTSTR str2, + __in YORI_ALLOC_SIZE_T count + ) +{ + YORI_ALLOC_SIZE_T Index = 0; + + if (count == 0) { + return 0; + } + + while(TRUE) { + + if (Index == Str1->LengthInChars) { + if (str2[Index] == '\0') { + return 0; + } else { + return -1; + } + } else if (str2[Index] == '\0') { + return 1; + } + + if (Str1->StartOfString[Index] < str2[Index]) { + return -1; + } else if (Str1->StartOfString[Index] > str2[Index]) { + return 1; + } + + Index++; + + if (Index == count) { + return 0; + } + } + return 0; +} + +/** + Compare a Yori string against a NULL terminated string. If the strings are + equal, return zero; if the first string is lexicographically earlier than + the second, return negative; if the second string is lexicographically + earlier than the first, return positive. + + @param Str1 The first (Yori) string to compare. + + @param str2 The second (NULL terminated) string to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringLit( + __in PCYORI_STRING Str1, + __in LPCTSTR str2 + ) +{ + return YoriLibCompareStringLitCnt(Str1, str2, (YORI_ALLOC_SIZE_T)-1); +} + +/** + Convert a single english character to its uppercase form. + + @param c The character to convert. + + @return The uppercase form of the character. + */ +TCHAR +YoriLibUpcaseChar( + __in TCHAR c + ) +{ + if (c >= 'a' && c <= 'z') { + return (TCHAR)(c - 'a' + 'A'); + } + return c; +} + +/** + Compare a Yori string against a NULL terminated string up to a specified + maximum number of characters without regard to case. If the strings are + equal, return zero; if the first string is lexicographically earlier than + the second, return negative; if the second string is lexicographically + earlier than the first, return positive. + + @param Str1 The first (Yori) string to compare. + + @param str2 The second (NULL terminated) string to compare. + + @param count The number of characters to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringLitInsCnt( + __in PCYORI_STRING Str1, + __in LPCTSTR str2, + __in YORI_ALLOC_SIZE_T count + ) +{ + YORI_ALLOC_SIZE_T Index = 0; + + if (count == 0) { + return 0; + } + + while(TRUE) { + + if (Index == Str1->LengthInChars) { + if (str2[Index] == '\0') { + return 0; + } else { + return -1; + } + } else if (str2[Index] == '\0') { + return 1; + } + + if (YoriLibUpcaseChar(Str1->StartOfString[Index]) < YoriLibUpcaseChar(str2[Index])) { + return -1; + } else if (YoriLibUpcaseChar(Str1->StartOfString[Index]) > YoriLibUpcaseChar(str2[Index])) { + return 1; + } + + Index++; + + if (Index == count) { + return 0; + } + } + return 0; +} + +/** + Compare a Yori string against a NULL terminated string without regard to + case. If the strings are equal, return zero; if the first string is + lexicographically earlier than the second, return negative; if the second + string is lexicographically earlier than the first, return positive. + + @param Str1 The first (Yori) string to compare. + + @param str2 The second (NULL terminated) string to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringLitIns( + __in PCYORI_STRING Str1, + __in LPCTSTR str2 + ) +{ + return YoriLibCompareStringLitInsCnt(Str1, str2, (YORI_ALLOC_SIZE_T)-1); +} + +/** + Compare two Yori strings up to a specified maximum number of characters. + If the strings are equal, return zero; if the first string is + lexicographically earlier than the second, return negative; if the second + string is lexicographically earlier than the first, return positive. + + @param Str1 The first (Yori) string to compare. + + @param Str2 The second (NULL terminated) string to compare. + + @param count The number of characters to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringCnt( + __in PCYORI_STRING Str1, + __in PCYORI_STRING Str2, + __in YORI_ALLOC_SIZE_T count + ) +{ + YORI_ALLOC_SIZE_T Index = 0; + + if (count == 0) { + return 0; + } + + while(TRUE) { + + if (Index == Str1->LengthInChars) { + if (Index == Str2->LengthInChars) { + return 0; + } else { + return -1; + } + } else if (Index == Str2->LengthInChars) { + return 1; + } + + if (Str1->StartOfString[Index] < Str2->StartOfString[Index]) { + return -1; + } else if (Str1->StartOfString[Index] > Str2->StartOfString[Index]) { + return 1; + } + + Index++; + + if (Index == count) { + return 0; + } + } + return 0; +} + +/** + Compare two Yori strings. If the strings are equal, return zero; if the + first string is lexicographically earlier than the second, return negative; + if the second string is lexicographically earlier than the first, return + positive. + + @param Str1 The first string to compare. + + @param Str2 The second string to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareString( + __in PCYORI_STRING Str1, + __in PCYORI_STRING Str2 + ) +{ + return YoriLibCompareStringCnt(Str1, Str2, (YORI_ALLOC_SIZE_T)-1); +} + +/** + Compare two Yori strings up to a specified maximum number of characters + without regard to case. If the strings are equal, return zero; if the + first string is lexicographically earlier than the second, return negative; + if the second string is lexicographically earlier than the first, return + positive. + + @param Str1 The first (Yori) string to compare. + + @param Str2 The second (NULL terminated) string to compare. + + @param count The number of characters to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringInsCnt( + __in PCYORI_STRING Str1, + __in PCYORI_STRING Str2, + __in YORI_ALLOC_SIZE_T count + ) +{ + YORI_ALLOC_SIZE_T Index = 0; + + if (count == 0) { + return 0; + } + + while(TRUE) { + + if (Index == Str1->LengthInChars) { + if (Index == Str2->LengthInChars) { + return 0; + } else { + return -1; + } + } else if (Index == Str2->LengthInChars) { + return 1; + } + + if (YoriLibUpcaseChar(Str1->StartOfString[Index]) < YoriLibUpcaseChar(Str2->StartOfString[Index])) { + return -1; + } else if (YoriLibUpcaseChar(Str1->StartOfString[Index]) > YoriLibUpcaseChar(Str2->StartOfString[Index])) { + return 1; + } + + Index++; + + if (Index == count) { + return 0; + } + } + return 0; +} + +/** + Compare two Yori strings without regard to case. If the strings are equal, + return zero; if the first string is lexicographically earlier than the + second, return negative; if the second string is lexicographically earlier + than the first, return positive. + + @param Str1 The first string to compare. + + @param Str2 The second string to compare. + + @return Zero for equality; -1 if the first is less than the second; 1 if + the first is greater than the second. + */ +int +YoriLibCompareStringIns( + __in PCYORI_STRING Str1, + __in PCYORI_STRING Str2 + ) +{ + return YoriLibCompareStringInsCnt(Str1, Str2, (YORI_ALLOC_SIZE_T)-1); +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrcnt.c b/misc/tools/yori/yori/lib/ylstrcnt.c new file mode 100644 index 0000000000..dbd6e6ea42 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrcnt.c @@ -0,0 +1,185 @@ +/** + * @file lib/ylstrcnt.c + * + * Yori string couting routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Count the number of identical characters between two strings. + + @param Str1 The first string to compare. + + @param Str2 The second string to compare. + + @return The number of identical characters. + */ +YORI_ALLOC_SIZE_T +YoriLibCntStringMatchChars( + __in PYORI_STRING Str1, + __in PYORI_STRING Str2 + ) +{ + YORI_ALLOC_SIZE_T Index; + + Index = 0; + + while (Index < Str1->LengthInChars && + Index < Str2->LengthInChars && + Str1->StartOfString[Index] == Str2->StartOfString[Index]) { + + Index++; + } + + return Index; +} + +/** + Count the number of identical characters between two strings without regard + to case. + + @param Str1 The first string to compare. + + @param Str2 The second string to compare. + + @return The number of identical characters. + */ +YORI_ALLOC_SIZE_T +YoriLibCntStringMatchCharsIns( + __in PYORI_STRING Str1, + __in PYORI_STRING Str2 + ) +{ + YORI_ALLOC_SIZE_T Index; + + Index = 0; + + while (Index < Str1->LengthInChars && + Index < Str2->LengthInChars && + YoriLibUpcaseChar(Str1->StartOfString[Index]) == YoriLibUpcaseChar(Str2->StartOfString[Index])) { + + Index++; + } + + return Index; +} + +/** + Return the count of consecutive characters in String that are listed in the + characters of the NULL terminated chars array. + + @param String The string to check for consecutive characters. + + @param chars A null terminated list of characters to look for. + + @return The number of characters in String that occur in chars. + */ +YORI_ALLOC_SIZE_T +YoriLibCntStringWithChars( + __in PCYORI_STRING String, + __in LPCTSTR chars + ) +{ + YORI_ALLOC_SIZE_T len = 0; + YORI_ALLOC_SIZE_T i; + + for (len = 0; len < String->LengthInChars; len++) { + for (i = 0; chars[i] != '\0'; i++) { + if (String->StartOfString[len] == chars[i]) { + break; + } + } + if (chars[i] == '\0') { + return len; + } + } + + return len; +} + +/** + Return the count of characters in String that are none of the characters + in the NULL terminated match array. + + @param String The string to check for consecutive characters. + + @param match A null terminated list of characters to look for. + + @return The number of characters in String that do not occur in match. + */ +YORI_ALLOC_SIZE_T +YoriLibCntStringNotWithChars( + __in PCYORI_STRING String, + __in LPCTSTR match + ) +{ + YORI_ALLOC_SIZE_T len = 0; + YORI_ALLOC_SIZE_T i; + + for (len = 0; len < String->LengthInChars; len++) { + for (i = 0; match[i] != '\0'; i++) { + if (String->StartOfString[len] == match[i]) { + return len; + } + } + } + + return len; +} + +/** + Return the count of consecutive characters at the end of String that are + listed in the characters of the NULL terminated chars array. + + @param String The string to check for consecutive characters. + + @param chars A null terminated list of characters to look for. + + @return The number of characters in String that occur in chars. + */ +YORI_ALLOC_SIZE_T +YoriLibCntStringTrailingChars( + __in PCYORI_STRING String, + __in LPCTSTR chars + ) +{ + YORI_ALLOC_SIZE_T len = 0; + YORI_ALLOC_SIZE_T i; + + for (len = String->LengthInChars; len > 0; len--) { + for (i = 0; chars[i] != '\0'; i++) { + if (String->StartOfString[len - 1] == chars[i]) { + break; + } + } + if (chars[i] == '\0') { + return String->LengthInChars - len; + } + } + + return String->LengthInChars - len; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrcnv.c b/misc/tools/yori/yori/lib/ylstrcnv.c new file mode 100644 index 0000000000..5b4f4ad917 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrcnv.c @@ -0,0 +1,324 @@ +/** + * @file lib/ylstrcnv.c + * + * Yori string conversion routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + An ordered array of suffixes, where each suffix represents sizes 1024 times + larger than the previous one. + */ +CONST TCHAR YoriLibSizeSuffixes[] = {'b', 'k', 'm', 'g', 't', '?'}; + +/** + Parse a string specifying a file size and return a 64 bit unsigned integer + from the result. This function can parse prefixed hex or decimal inputs, and + understands file size qualifiers (k, m, g et al.) + + @param String Pointer to the string to parse. + + @return The parsed, 64 bit unsigned integer value from the string. + */ +LARGE_INTEGER +YoriLibStringToFileSize( + __in PCYORI_STRING String + ) +{ + DWORD SuffixLevel = 0; + LARGE_INTEGER FileSize; + DWORD i; + YORI_ALLOC_SIZE_T CharsConsumed; + YORI_MAX_SIGNED_T llTemp; + + llTemp = 0; + if (!YoriLibStringToNumber(String, TRUE, &llTemp, &CharsConsumed)) { + YoriLibLiAssignUnsigned(&FileSize, llTemp); + return FileSize; + } + YoriLibLiAssignUnsigned(&FileSize, llTemp); + + if (CharsConsumed < String->LengthInChars) { + TCHAR SuffixChar = String->StartOfString[CharsConsumed]; + + for (i = 0; i < sizeof(YoriLibSizeSuffixes)/sizeof(YoriLibSizeSuffixes[0]); i++) { + if (SuffixChar == YoriLibSizeSuffixes[i] || SuffixChar == (YoriLibSizeSuffixes[i] + 'A' - 'a')) { + SuffixLevel = i; + break; + } + } + + while(SuffixLevel) { + + FileSize.HighPart = (FileSize.HighPart << 10) + (FileSize.LowPart >> 22); + FileSize.LowPart = (FileSize.LowPart << 10); + + SuffixLevel--; + } + } + + return FileSize; +} + +/** + Convert a 64 bit file size into a string. This function returns 3 or 4 + significant digits followed by a suffix indicating the magnitude, for a + total of 5 chars. + + @param String On successful completion, updated to contain the string form + of the file size. The caller should ensure this is allocated with + six chars on input. + + @param FileSize The numeric file size. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibFileSizeToString( + __inout PYORI_STRING String, + __in PLARGE_INTEGER FileSize + ) +{ + DWORD SuffixLevel = 0; + LARGE_INTEGER Size; + LARGE_INTEGER OldSize; + + Size.HighPart = FileSize->HighPart; + Size.LowPart = FileSize->LowPart; + OldSize.HighPart = Size.HighPart; + OldSize.LowPart = Size.LowPart; + + if (String->LengthAllocated < sizeof("12.3k")) { + return FALSE; + } + + + while (Size.HighPart != 0 || Size.LowPart > 9999) { + SuffixLevel++; + OldSize.HighPart = Size.HighPart; + OldSize.LowPart = Size.LowPart; + + // + // Conceptually we want to divide by 1024. We do this + // in two 32-bit shifts combining back the high bits + // so we don't need full compiler support. + // + + Size.LowPart = (Size.LowPart >> 10) + ((Size.HighPart & 0x3ff) << 22); + Size.HighPart = (Size.HighPart >> 10) & 0x003fffff; + } + + if (SuffixLevel >= sizeof(YoriLibSizeSuffixes)/sizeof(TCHAR)) { + SuffixLevel = sizeof(YoriLibSizeSuffixes)/sizeof(TCHAR) - 1; + } + + if (Size.LowPart < 100 && SuffixLevel > 0) { + OldSize.LowPart = (OldSize.LowPart % 1024) * 10 / 1024; + String->LengthInChars = YoriLibSPrintfS(String->StartOfString, + String->LengthAllocated, + _T("%2i.%1i%c"), + (int)Size.LowPart, + (int)OldSize.LowPart, + YoriLibSizeSuffixes[SuffixLevel]); + } else { + String->LengthInChars = YoriLibSPrintfS(String->StartOfString, + String->LengthAllocated, + _T("%4i%c"), + (int)Size.LowPart, + YoriLibSizeSuffixes[SuffixLevel]); + } + return TRUE; +} + +/** + Parse a string specifying a file date and return a timestamp from the result. + + @param String Pointer to the string to parse. + + @param Date On successful completion, updated to point to a timestamp. + + @param CharsConsumed Optionally points to a value to update with the number of + characters consumed from String to generate the requested output. + + @return TRUE to indicate success, FALSE to indicate parse failure. + */ +__success(return) +BOOL +YoriLibStringToDate( + __in PCYORI_STRING String, + __out LPSYSTEMTIME Date, + __out_opt PYORI_ALLOC_SIZE_T CharsConsumed + ) +{ + YORI_STRING Substring; + YORI_ALLOC_SIZE_T CurrentCharsConsumed; + YORI_ALLOC_SIZE_T TotalCharsConsumed; + YORI_MAX_SIGNED_T llTemp; + + YoriLibInitEmptyString(&Substring); + Substring.StartOfString = String->StartOfString; + Substring.LengthInChars = String->LengthInChars; + TotalCharsConsumed = 0; + + if (!YoriLibStringToNumber(&Substring, TRUE, &llTemp, &CurrentCharsConsumed)) { + return FALSE; + } + + Date->wYear = (WORD)llTemp; + if (Date->wYear < 100) { + Date->wYear += 2000; + } + + TotalCharsConsumed = TotalCharsConsumed + CurrentCharsConsumed; + + if (CurrentCharsConsumed < Substring.LengthInChars && + (Substring.StartOfString[CurrentCharsConsumed] == '/' || Substring.StartOfString[CurrentCharsConsumed] == '-')) { + Substring.LengthInChars -= CurrentCharsConsumed + 1; + Substring.StartOfString += CurrentCharsConsumed + 1; + + if (!YoriLibStringToNumber(&Substring, TRUE, &llTemp, &CurrentCharsConsumed)) { + return FALSE; + } + + Date->wMonth = (WORD)llTemp; + TotalCharsConsumed = TotalCharsConsumed + CurrentCharsConsumed + 1; + + if (CurrentCharsConsumed < Substring.LengthInChars && + (Substring.StartOfString[CurrentCharsConsumed] == '/' || Substring.StartOfString[CurrentCharsConsumed] == '-')) { + Substring.LengthInChars -= CurrentCharsConsumed + 1; + Substring.StartOfString += CurrentCharsConsumed + 1; + + if (!YoriLibStringToNumber(&Substring, TRUE, &llTemp, &CurrentCharsConsumed)) { + return FALSE; + } + + Date->wDay = (WORD)llTemp; + TotalCharsConsumed = TotalCharsConsumed + CurrentCharsConsumed + 1; + } + } + + if (CharsConsumed != NULL) { + *CharsConsumed = TotalCharsConsumed; + } + + return TRUE; +} + +/** + Parse a string specifying a file time and return a timestamp from the result. + + @param String Pointer to the string to parse. + + @param Date On successful completion, updated to point to a timestamp. + + @return TRUE to indicate success, FALSE to indicate parse failure. + */ +__success(return) +BOOL +YoriLibStringToTime( + __in PCYORI_STRING String, + __out LPSYSTEMTIME Date + ) +{ + YORI_STRING Substring; + YORI_ALLOC_SIZE_T CharsConsumed; + YORI_MAX_SIGNED_T llTemp; + + YoriLibInitEmptyString(&Substring); + Substring.StartOfString = String->StartOfString; + Substring.LengthInChars = String->LengthInChars; + + if (!YoriLibStringToNumber(&Substring, TRUE, &llTemp, &CharsConsumed)) { + return FALSE; + } + + Date->wHour = (WORD)llTemp; + + if (CharsConsumed < Substring.LengthInChars && Substring.StartOfString[CharsConsumed] == ':') { + Substring.LengthInChars = Substring.LengthInChars - (CharsConsumed + 1); + Substring.StartOfString += CharsConsumed + 1; + + if (!YoriLibStringToNumber(&Substring, TRUE, &llTemp, &CharsConsumed)) { + return FALSE; + } + + Date->wMinute = (WORD)llTemp; + + if (CharsConsumed < Substring.LengthInChars && Substring.StartOfString[CharsConsumed] == ':') { + Substring.LengthInChars = Substring.LengthInChars - (CharsConsumed + 1); + Substring.StartOfString += CharsConsumed + 1; + + if (!YoriLibStringToNumber(&Substring, TRUE, &llTemp, &CharsConsumed)) { + return FALSE; + } + + Date->wSecond = (WORD)llTemp; + } + } + + return TRUE; +} + +/** + Parse a string specifying a file date and time and return a timestamp from + the result. + + @param String Pointer to the string to parse. + + @param Date On successful completion, updated to point to a timestamp. + + @return TRUE to indicate success, FALSE to indicate parse failure. + */ +__success(return) +BOOL +YoriLibStringToDateTime( + __in PCYORI_STRING String, + __out LPSYSTEMTIME Date + ) +{ + YORI_STRING Substring; + YORI_ALLOC_SIZE_T CharsConsumed; + + if (!YoriLibStringToDate(String, Date, &CharsConsumed)) { + return FALSE; + } + + Substring.StartOfString = String->StartOfString; + Substring.LengthInChars = String->LengthInChars; + + if (CharsConsumed < Substring.LengthInChars && Substring.StartOfString[CharsConsumed] == ':') { + Substring.StartOfString += CharsConsumed + 1; + Substring.LengthInChars = Substring.LengthInChars - (CharsConsumed + 1); + + if (!YoriLibStringToTime(&Substring, Date)) { + return FALSE; + } + } + + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrfnd.c b/misc/tools/yori/yori/lib/ylstrfnd.c new file mode 100644 index 0000000000..47c1470b53 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrfnd.c @@ -0,0 +1,303 @@ +/** + * @file lib/ylstrfnd.c + * + * Yori string find routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Search through a string looking to see if any substrings can be located. + Returns the first match in offet from the beginning of the string order. + This routine looks for matches case sensitively. + + @param String The string to search through. + + @param NumberMatches The number of substrings to look for. + + @param MatchArray An array of strings corresponding to the matches to + look for. + + @param StringOffsetOfMatch On successful completion, returns the offset + within the string of the match. + + @return If a match is found, returns a pointer to the entry in MatchArray + corresponding to the substring that was matched. If no match is + found, returns NULL. + */ +PYORI_STRING +YoriLibFindFirstMatchSubstr( + __in PCYORI_STRING String, + __in YORI_ALLOC_SIZE_T NumberMatches, + __in PYORI_STRING MatchArray, + __out_opt PYORI_ALLOC_SIZE_T StringOffsetOfMatch + ) +{ + YORI_STRING RemainingString; + YORI_ALLOC_SIZE_T CheckCount; + + YoriLibInitEmptyString(&RemainingString); + RemainingString.StartOfString = String->StartOfString; + RemainingString.LengthInChars = String->LengthInChars; + + while (RemainingString.LengthInChars > 0) { + for (CheckCount = 0; CheckCount < NumberMatches; CheckCount++) { + if (YoriLibCompareStringCnt(&RemainingString, &MatchArray[CheckCount], MatchArray[CheckCount].LengthInChars) == 0) { + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = String->LengthInChars - RemainingString.LengthInChars; + } + return &MatchArray[CheckCount]; + } + } + + RemainingString.LengthInChars--; + RemainingString.StartOfString++; + } + + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = 0; + } + return NULL; +} + +/** + Search through a string looking to see if any substrings can be located. + Returns the first match in offet from the beginning of the string order. + This routine looks for matches insensitively. + + @param String The string to search through. + + @param NumberMatches The number of substrings to look for. + + @param MatchArray An array of strings corresponding to the matches to + look for. + + @param StringOffsetOfMatch On successful completion, returns the offset + within the string of the match. + + @return If a match is found, returns a pointer to the entry in MatchArray + corresponding to the substring that was matched. If no match is + found, returns NULL. + */ +PYORI_STRING +YoriLibFindFirstMatchSubstrIns( + __in PCYORI_STRING String, + __in YORI_ALLOC_SIZE_T NumberMatches, + __in PYORI_STRING MatchArray, + __out_opt PYORI_ALLOC_SIZE_T StringOffsetOfMatch + ) +{ + YORI_STRING RemainingString; + YORI_ALLOC_SIZE_T CheckCount; + + YoriLibInitEmptyString(&RemainingString); + RemainingString.StartOfString = String->StartOfString; + RemainingString.LengthInChars = String->LengthInChars; + + while (RemainingString.LengthInChars > 0) { + for (CheckCount = 0; CheckCount < NumberMatches; CheckCount++) { + if (YoriLibCompareStringInsCnt(&RemainingString, &MatchArray[CheckCount], MatchArray[CheckCount].LengthInChars) == 0) { + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = String->LengthInChars - RemainingString.LengthInChars; + } + return &MatchArray[CheckCount]; + } + } + + RemainingString.LengthInChars--; + RemainingString.StartOfString++; + } + + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = 0; + } + return NULL; +} + +/** + Search through a string looking to see if any substrings can be located. + Returns the last match in offet from the end of the string order. + This routine looks for matches case sensitively. + + @param String The string to search through. + + @param NumberMatches The number of substrings to look for. + + @param MatchArray An array of strings corresponding to the matches to + look for. + + @param StringOffsetOfMatch On successful completion, returns the offset + within the string of the match. + + @return If a match is found, returns a pointer to the entry in MatchArray + corresponding to the substring that was matched. If no match is + found, returns NULL. + */ +PYORI_STRING +YoriLibFindLastMatchSubstr( + __in PCYORI_STRING String, + __in YORI_ALLOC_SIZE_T NumberMatches, + __in PYORI_STRING MatchArray, + __out_opt PYORI_ALLOC_SIZE_T StringOffsetOfMatch + ) +{ + YORI_STRING RemainingString; + YORI_ALLOC_SIZE_T CheckCount; + + YoriLibInitEmptyString(&RemainingString); + + while (TRUE) { + + RemainingString.LengthInChars++; + if (RemainingString.LengthInChars > String->LengthInChars) { + break; + } + RemainingString.StartOfString = &String->StartOfString[String->LengthInChars - RemainingString.LengthInChars]; + + for (CheckCount = 0; CheckCount < NumberMatches; CheckCount++) { + if (YoriLibCompareStringCnt(&RemainingString, &MatchArray[CheckCount], MatchArray[CheckCount].LengthInChars) == 0) { + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = String->LengthInChars - RemainingString.LengthInChars; + } + return &MatchArray[CheckCount]; + } + } + } + + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = 0; + } + return NULL; +} + +/** + Search through a string looking to see if any substrings can be located. + Returns the last match in offet from the end of the string order. + This routine looks for matches case insensitively. + + @param String The string to search through. + + @param NumberMatches The number of substrings to look for. + + @param MatchArray An array of strings corresponding to the matches to + look for. + + @param StringOffsetOfMatch On successful completion, returns the offset + within the string of the match. + + @return If a match is found, returns a pointer to the entry in MatchArray + corresponding to the substring that was matched. If no match is + found, returns NULL. + */ +PYORI_STRING +YoriLibFindLastMatchSubstrIns( + __in PCYORI_STRING String, + __in YORI_ALLOC_SIZE_T NumberMatches, + __in PYORI_STRING MatchArray, + __out_opt PYORI_ALLOC_SIZE_T StringOffsetOfMatch + ) +{ + YORI_STRING RemainingString; + YORI_ALLOC_SIZE_T CheckCount; + + YoriLibInitEmptyString(&RemainingString); + + while (TRUE) { + + RemainingString.LengthInChars++; + if (RemainingString.LengthInChars > String->LengthInChars) { + break; + } + RemainingString.StartOfString = &String->StartOfString[String->LengthInChars - RemainingString.LengthInChars]; + + for (CheckCount = 0; CheckCount < NumberMatches; CheckCount++) { + if (YoriLibCompareStringInsCnt(&RemainingString, &MatchArray[CheckCount], MatchArray[CheckCount].LengthInChars) == 0) { + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = String->LengthInChars - RemainingString.LengthInChars; + } + return &MatchArray[CheckCount]; + } + } + } + + if (StringOffsetOfMatch != NULL) { + *StringOffsetOfMatch = 0; + } + return NULL; +} + +/** + Search through a string finding the leftmost instance of a character. If + no match is found, return NULL. + + @param String The string to search. + + @param CharToFind The character to find. + + @return Pointer to the leftmost matching character, or NULL if no match was + found. + */ +LPTSTR +YoriLibFindLeftMostCharacter( + __in PCYORI_STRING String, + __in TCHAR CharToFind + ) +{ + YORI_ALLOC_SIZE_T Index; + for (Index = 0; Index < String->LengthInChars; Index++) { + if (String->StartOfString[Index] == CharToFind) { + return &String->StartOfString[Index]; + } + } + return NULL; +} + + +/** + Search through a string finding the rightmost instance of a character. If + no match is found, return NULL. + + @param String The string to search. + + @param CharToFind The character to find. + + @return Pointer to the rightmost matching character, or NULL if no match was + found. + */ +LPTSTR +YoriLibFindRightMostCharacter( + __in PCYORI_STRING String, + __in TCHAR CharToFind + ) +{ + YORI_ALLOC_SIZE_T Index; + for (Index = String->LengthInChars; Index > 0; Index--) { + if (String->StartOfString[Index - 1] == CharToFind) { + return &String->StartOfString[Index - 1]; + } + } + return NULL; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrhex.c b/misc/tools/yori/yori/lib/ylstrhex.c new file mode 100644 index 0000000000..984cb04255 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrhex.c @@ -0,0 +1,177 @@ +/** + * @file lib/ylstrhex.c + * + * Yori string to hex routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Parse a string containing hex digits and generate a string that encodes each + two hex digits into a byte. + + @param String Pointer to the string to parse. + + @param Buffer Pointer to a buffer to populate with a string representation of + the hex characters. Note this string is always 8 bit, not TCHARs. + + @param BufferSize Specifies the length of Buffer, in bytes. + + @return TRUE to indicate parse success, FALSE to indicate failure. Failure + occurs if the input stream is not compliant hex, or is not aligned in + two character pairs. + */ +__success(return) +BOOL +YoriLibStringToHexBuffer( + __in PYORI_STRING String, + __out_ecount(BufferSize) PUCHAR Buffer, + __in YORI_ALLOC_SIZE_T BufferSize + ) +{ + UCHAR DestChar; + TCHAR SourceChar; + YORI_ALLOC_SIZE_T Offset; + YORI_ALLOC_SIZE_T Digit; + YORI_ALLOC_SIZE_T StrIndex; + + // + // Loop through the output string + // + + StrIndex = 0; + for (Offset = 0; Offset < BufferSize; Offset++) { + + DestChar = 0; + Digit = 0; + + // + // So long as we have a valid character, populate thischar. Do this exactly + // twice, so we have one complete byte. + // + + while (Digit < 2 && StrIndex < String->LengthInChars) { + + SourceChar = String->StartOfString[StrIndex]; + + if ((SourceChar >= '0' && SourceChar <= '9') || + (SourceChar >= 'a' && SourceChar <= 'f') || + (SourceChar >= 'A' && SourceChar <= 'F')) { + + DestChar *= 16; + if (SourceChar >= '0' && SourceChar <= '9') { + DestChar = (UCHAR)(DestChar + SourceChar - '0'); + } else if (SourceChar >= 'a' && SourceChar <= 'f') { + DestChar = (UCHAR)(DestChar + SourceChar - 'a' + 10); + } else if (SourceChar >= 'A' && SourceChar <= 'F') { + DestChar = (UCHAR)(DestChar + SourceChar - 'A' + 10); + } + + StrIndex++; + Digit++; + } else { + break; + } + } + + // + // If we did actually get two input chars, output the byte and go back for + // more. Otherwise, the input doesn't match the output requirement + // + + if (Digit == 2) { + Buffer[Offset] = DestChar; + } else { + return FALSE; + } + } + return TRUE; +} + +/** + Parse a buffer containing binary data and generate a string that encodes the + data into hex (implying two chars per byte.) + + @param Buffer Pointer to a buffer to parse. + + @param BufferSize Specifies the length of Buffer, in bytes. + + @param String Pointer to the string to populate. + + @return TRUE to indicate parse success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibHexBufferToString( + __in PUCHAR Buffer, + __in YORI_ALLOC_SIZE_T BufferSize, + __in PYORI_STRING String + ) +{ + UCHAR SourceChar; + UCHAR SourceNibble; + YORI_ALLOC_SIZE_T Offset; + YORI_ALLOC_SIZE_T StrIndex; + + // + // Currently this routine assumes the caller allocated a large enough + // buffer. The buffer should hold two chars per byte plus a NULL. + // + + if (String->LengthAllocated <= BufferSize * sizeof(TCHAR)) { + return FALSE; + } + + // + // Loop through the buffer + // + + StrIndex = 0; + for (Offset = 0; Offset < BufferSize; Offset++) { + + SourceChar = Buffer[Offset]; + SourceNibble = (UCHAR)(SourceChar >> 4); + if (SourceNibble >= 10) { + String->StartOfString[StrIndex] = (UCHAR)('a' + SourceNibble - 10); + } else { + String->StartOfString[StrIndex] = (UCHAR)('0' + SourceNibble); + } + + StrIndex++; + SourceNibble = (UCHAR)(SourceChar & 0xF); + if (SourceNibble >= 10) { + String->StartOfString[StrIndex] = (UCHAR)('a' + SourceNibble - 10); + } else { + String->StartOfString[StrIndex] = (UCHAR)('0' + SourceNibble); + } + + StrIndex++; + } + + String->StartOfString[StrIndex] = '\0'; + String->LengthInChars = StrIndex; + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrnum.c b/misc/tools/yori/yori/lib/ylstrnum.c new file mode 100644 index 0000000000..c8bbe4966b --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrnum.c @@ -0,0 +1,375 @@ +/** + * @file lib/ylstrnum.c + * + * Yori string to number routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + This routine attempts to convert a string to a number using only positive + decimal integers. + + @param String Pointer to the string to convert into integer form. + + @return The number from the string. Zero if the string does not contain + a valid number. + */ +DWORD +YoriLibDecimalStringToInt( + __in PYORI_STRING String + ) +{ + DWORD Ret = 0; + YORI_ALLOC_SIZE_T Index; + TCHAR Char; + + for (Index = 0; Index < String->LengthInChars; Index++) { + Char = String->StartOfString[Index]; + if (Char < '0' || Char > '9') { + break; + } + Ret = Ret * 10; + Ret += Char - '0'; + } + return Ret; +} + +/** + This routine attempts to convert a string to a number using a specified + number base (ie., decimal or hexadecimal or octal or binary.) + + @param String Pointer to the string to convert into integer form. + + @param Base The number base. Must be 10 or 16 or 8 or 2. + + @param IgnoreSeperators If TRUE, continue to generate a number across comma + delimiters. If FALSE, terminate on a comma. + + @param Number On successful completion, this is updated to contain the + resulting number. + + @param CharsConsumed On successful completion, this is updated to indicate + the number of characters from the string that were used to generate + the number. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibStringToNumberBase( + __in PCYORI_STRING String, + __in WORD Base, + __in BOOL IgnoreSeperators, + __out PYORI_MAX_SIGNED_T Number, + __out PYORI_ALLOC_SIZE_T CharsConsumed + ) +{ + YORI_MAX_SIGNED_T SignedResult; + YORI_MAX_UNSIGNED_T Result; + YORI_ALLOC_SIZE_T Index; + BOOL Negative = FALSE; + + Result = 0; + Index = 0; + + while (String->LengthInChars > Index) { + if (String->StartOfString[Index] == '-') { + if (Negative) { + Negative = FALSE; + } else { + Negative = TRUE; + } + Index++; + } else { + break; + } + } + + for (; String->LengthInChars > Index; Index++) { + if (!IgnoreSeperators || String->StartOfString[Index] != ',') { + if (Base == 10) { + if (String->StartOfString[Index] < '0' || String->StartOfString[Index] > '9') { + break; + } + Result *= Base; + Result += String->StartOfString[Index] - '0'; + } else if (Base == 16) { + TCHAR Char; + Char = YoriLibUpcaseChar(String->StartOfString[Index]); + if (Char >= '0' && Char <= '9') { + Result *= Base; + Result += Char - '0'; + } else if (Char >= 'A' && Char <= 'F') { + Result *= Base; + Result += Char - 'A' + 10; + } else { + break; + } + } else if (Base == 8) { + if (String->StartOfString[Index] < '0' || String->StartOfString[Index] >= '8') { + break; + } + Result *= Base; + Result += String->StartOfString[Index] - '0'; + } else if (Base == 2) { + if (String->StartOfString[Index] != '0' && String->StartOfString[Index] != '1') { + break; + } + Result *= Base; + Result += String->StartOfString[Index] - '0'; + } + } + } + + if (Negative) { + SignedResult = (YORI_MAX_SIGNED_T)0 - (YORI_MAX_SIGNED_T)Result; + } else { + SignedResult = (YORI_MAX_SIGNED_T)Result; + } + + *CharsConsumed = Index; + *Number = SignedResult; + return TRUE; +} + +/** + This routine attempts to convert a string to a number using all available + parsing. As of this writing, it understands 0x and 0n and 0o and 0x prefixes + as well as negative numbers. + + @param String Pointer to the string to convert into integer form. + + @param IgnoreSeperators If TRUE, continue to generate a number across comma + delimiters. If FALSE, terminate on a comma. + + @param Number On successful completion, this is updated to contain the + resulting number. + + @param CharsConsumed On successful completion, this is updated to indicate + the number of characters from the string that were used to generate + the number. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibStringToNumber( + __in PCYORI_STRING String, + __in BOOL IgnoreSeperators, + __out PYORI_MAX_SIGNED_T Number, + __out PYORI_ALLOC_SIZE_T CharsConsumed + ) +{ + YORI_MAX_UNSIGNED_T Result; + YORI_MAX_SIGNED_T SignedResult; + YORI_ALLOC_SIZE_T Index; + YORI_ALLOC_SIZE_T Base = 10; + BOOL Negative = FALSE; + + Result = 0; + Index = 0; + + while (String->LengthInChars > Index) { + if (String->StartOfString[Index] == '0' && + String->LengthInChars > Index + 1 && + String->StartOfString[Index + 1] == 'x') { + Base = 16; + Index += 2; + } else if (String->StartOfString[Index] == '0' && + String->LengthInChars > Index + 1 && + String->StartOfString[Index + 1] == 'n') { + Base = 10; + Index += 2; + } else if (String->StartOfString[Index] == '0' && + String->LengthInChars > Index + 1 && + String->StartOfString[Index + 1] == 'o') { + Base = 8; + Index += 2; + } else if (String->StartOfString[Index] == '0' && + String->LengthInChars > Index + 1 && + String->StartOfString[Index + 1] == 'b') { + Base = 2; + Index += 2; + } else if (String->StartOfString[Index] == '-') { + if (Negative) { + Negative = FALSE; + } else { + Negative = TRUE; + } + Index++; + } else { + break; + } + } + + for (; String->LengthInChars > Index; Index++) { + if (!IgnoreSeperators || String->StartOfString[Index] != ',') { + if (Base == 10) { + if (String->StartOfString[Index] < '0' || String->StartOfString[Index] > '9') { + break; + } + Result *= Base; + Result += String->StartOfString[Index] - '0'; + } else if (Base == 16) { + TCHAR Char; + Char = YoriLibUpcaseChar(String->StartOfString[Index]); + if (Char >= '0' && Char <= '9') { + Result *= Base; + Result += Char - '0'; + } else if (Char >= 'A' && Char <= 'F') { + Result *= Base; + Result += Char - 'A' + 10; + } else { + break; + } + } else if (Base == 8) { + if (String->StartOfString[Index] < '0' || String->StartOfString[Index] >= '8') { + break; + } + Result *= Base; + Result += String->StartOfString[Index] - '0'; + } else if (Base == 2) { + if (String->StartOfString[Index] != '0' && String->StartOfString[Index] != '1') { + break; + } + Result *= Base; + Result += String->StartOfString[Index] - '0'; + } + } + } + + if (Negative) { + SignedResult = (YORI_MAX_SIGNED_T)0 - (YORI_MAX_SIGNED_T)Result; + } else { + SignedResult = (YORI_MAX_SIGNED_T)Result; + } + + *CharsConsumed = Index; + *Number = SignedResult; + return TRUE; +} + +/** + Generate a string from a signed 64 bit integer. If the string is not + large enough to contain the result, it is reallocated by this routine. + + @param String Pointer to the Yori string to populate with the string + form of the number + + @param Number The integer to render into a string form. + + @param Base The number base to use. Supported values are from 2 through + 36, but typically only 10 and 16 are useful. + + @param DigitsPerGroup The number of digits to output between seperators. + If this value is zero, no seperators are inserted. + + @param GroupSeperator The character to insert between groups. + + @return TRUE for success, and FALSE for failure. This routine will not + fail if passed a string with sufficient allocation to contain + the result. + */ +__success(return) +BOOL +YoriLibNumberToString( + __inout PYORI_STRING String, + __in YORI_MAX_SIGNED_T Number, + __in WORD Base, + __in WORD DigitsPerGroup, + __in TCHAR GroupSeperator + ) +{ + YORI_ALLOC_SIZE_T Index; + YORI_MAX_UNSIGNED_T Num; + YORI_MAX_UNSIGNED_T IndexValue; + YORI_ALLOC_SIZE_T Digits; + YORI_ALLOC_SIZE_T DigitIndex; + + Index = 0; + if (Number < 0) { + Index++; + Num = 0; + Num = Num - Number; + } else { + Num = Number; + } + + // + // Count the number of Digits we have in the user's + // input. Stop if we hit the format specifier. + // Code below will preserve low order values. + // + + Digits = 1; + IndexValue = Num; + while (IndexValue > (WORD)(Base - 1)) { + IndexValue = IndexValue / Base; + Digits++; + } + + if (DigitsPerGroup != 0) { + Digits = Digits + ((Digits - 1) / DigitsPerGroup); + } + + Index = Index + Digits; + + if (String->LengthAllocated < Index + 1) { + YoriLibFreeStringContents(String); + if (!YoriLibAllocateString(String, Index + 1)) { + return FALSE; + } + } + String->StartOfString[Index] = '\0'; + String->LengthInChars = Index; + Index--; + DigitIndex = 0; + + do { + if (DigitsPerGroup != 0 && (DigitIndex % (DigitsPerGroup + 1)) == DigitsPerGroup) { + String->StartOfString[Index] = GroupSeperator; + } else { + IndexValue = Num % Base; + + if (IndexValue > 9) { + String->StartOfString[Index] = (UCHAR)(IndexValue + 'a' - 10); + } else { + String->StartOfString[Index] = (UCHAR)(IndexValue + '0'); + } + + Num /= Base; + } + DigitIndex++; + Index--; + } while(Num > 0); + + if (Number < 0) { + String->StartOfString[Index] = '-'; + } + + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrsrt.c b/misc/tools/yori/yori/lib/ylstrsrt.c new file mode 100644 index 0000000000..ed4de8b87c --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrsrt.c @@ -0,0 +1,178 @@ +/** + * @file lib/ylstrsrt.c + * + * Yori string sorting routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Swap the contents of two strings. This leaves the string data in its + current location and is only updating the pointers and lengths within the + string structures. + + @param Str1 Pointer to the first string. + + @param Str2 Pointer to the second string. + */ +VOID +YoriLibSwapStrings( + __inout PYORI_STRING Str1, + __inout PYORI_STRING Str2 + ) +{ + YORI_STRING SwapString; + + memcpy(&SwapString, Str2, sizeof(YORI_STRING)); + memcpy(Str2, Str1, sizeof(YORI_STRING)); + memcpy(Str1, &SwapString, sizeof(YORI_STRING)); +} + +/** + Sort an array of strings. This is currently implemented using a handrolled + quicksort. + + @param StringArray Pointer to an array of strings. + + @param Count The number of elements in the array. + */ +VOID +YoriLibSortStringArray( + __in_ecount(Count) PYORI_STRING StringArray, + __in YORI_ALLOC_SIZE_T Count + ) +{ + YORI_ALLOC_SIZE_T FirstOffset; + YORI_ALLOC_SIZE_T Index; + + if (Count <= 1) { + return; + } + + FirstOffset = 0; + + while (TRUE) { + YORI_ALLOC_SIZE_T BreakPoint; + YORI_ALLOC_SIZE_T LastOffset; + YORI_STRING MidPoint; + volatile DWORD LastSwapFirst; + volatile DWORD LastSwapLast; + + BreakPoint = Count / 2; + memcpy(&MidPoint, &StringArray[BreakPoint], sizeof(YORI_STRING)); + + FirstOffset = 0; + LastOffset = Count - 1; + + // + // Scan from the beginning looking for an item that should be sorted + // after the midpoint. + // + + for (FirstOffset = 0; FirstOffset < LastOffset; FirstOffset++) { + if (YoriLibCompareStringIns(&StringArray[FirstOffset],&MidPoint) >= 0) { + for (; LastOffset > FirstOffset; LastOffset--) { + if (YoriLibCompareStringIns(&StringArray[LastOffset],&MidPoint) < 0) { + LastSwapFirst = FirstOffset; + LastSwapLast = LastOffset; + YoriLibSwapStrings(&StringArray[FirstOffset], &StringArray[LastOffset]); + LastOffset--; + break; + } + } + if (LastOffset <= FirstOffset) { + break; + } + } + } + + ASSERT(FirstOffset == LastOffset); + FirstOffset = LastOffset; + if (YoriLibCompareStringIns(&StringArray[FirstOffset], &MidPoint) < 0) { + FirstOffset++; + } + + // + // If no copies occurred, check if everything in the list is + // sorted. + // + + if (FirstOffset == 0 || FirstOffset == Count) { + for (Index = 0; Index < Count - 1; Index++) { + if (YoriLibCompareStringIns(&StringArray[Index], &StringArray[Index + 1]) > 0) { + break; + } + } + if (Index == Count - 1) { + return; + } + + // + // Nothing was copied and it's not because it's identical. This + // implies a very bad choice of midpoint that belongs either at + // the beginning or the end (so nothing could move past it.) + // Move it around and repeat the process (which will get a new + // midpoint.) + // + + if (FirstOffset == 0) { + ASSERT(YoriLibCompareStringIns(&StringArray[0], &MidPoint) > 0); + + if (YoriLibCompareStringIns(&StringArray[0], &MidPoint) > 0) { + YoriLibSwapStrings(&StringArray[0], &StringArray[BreakPoint]); + } + } else { + ASSERT(YoriLibCompareStringIns(&MidPoint, &StringArray[Count - 1]) > 0); + YoriLibSwapStrings(&StringArray[Count - 1], &StringArray[BreakPoint]); + } + } else { + break; + } + } + + // + // If there are two sets (FirstOffset is not at either end), recurse. + // + + if (FirstOffset && (Count - FirstOffset)) { + if (FirstOffset > 0) { + YoriLibSortStringArray(StringArray, FirstOffset); + } + if ((Count - FirstOffset) > 0) { + YoriLibSortStringArray(&StringArray[FirstOffset], Count - FirstOffset); + } + } + + // + // Check that it's now sorted + // + for (Index = 0; Index < Count - 1; Index++) { + if (YoriLibCompareStringIns(&StringArray[Index], &StringArray[Index + 1]) > 0) { + break; + } + } + ASSERT (Index == Count - 1); +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylstrtrm.c b/misc/tools/yori/yori/lib/ylstrtrm.c new file mode 100644 index 0000000000..94595424f0 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylstrtrm.c @@ -0,0 +1,132 @@ +/** + * @file lib/ylstrtrm.c + * + * Yori string trim routines + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Remove spaces from the beginning and end of a Yori string. + Note this implies advancing the StartOfString pointer, so a caller + cannot assume this pointer is unchanged across the call. + + @param String Pointer to the Yori string to remove spaces from. + */ +VOID +YoriLibTrimSpaces( + __in PYORI_STRING String + ) +{ + while (String->LengthInChars > 0) { + if (String->StartOfString[0] == ' ') { + String->StartOfString++; + String->LengthInChars--; + } else { + break; + } + } + + while (String->LengthInChars > 0) { + if (String->StartOfString[String->LengthInChars - 1] == ' ') { + String->LengthInChars--; + } else { + break; + } + } +} + +/** + Remove newlines from the end of a Yori string. + + @param String Pointer to the Yori string to remove newlines from. + */ +VOID +YoriLibTrimTrailingNewlines( + __in PYORI_STRING String + ) +{ + while (String->LengthInChars > 0 && + (String->StartOfString[String->LengthInChars - 1] == '\n' || + String->StartOfString[String->LengthInChars - 1] == '\r')) { + + String->LengthInChars--; + } +} + +/** + Remove NULL terminated from the end of a Yori string. + + @param String Pointer to the Yori string to remove NULLs from. + */ +VOID +YoriLibTrimNullTerminators( + __in PYORI_STRING String + ) +{ + while (String->LengthInChars > 0) { + if (String->StartOfString[String->LengthInChars - 1] == '\0') { + String->LengthInChars--; + } else { + break; + } + } +} + +/** + This function right aligns a Yori string by moving characters in place + to ensure the total length of the string equals the specified alignment. + + @param String Pointer to the string to align. + + @param Align The number of characters that the string should contain. If + it currently has less than this number, spaces are inserted at the + beginning of the string. + */ +VOID +YoriLibRightAlignString( + __in PYORI_STRING String, + __in YORI_ALLOC_SIZE_T Align + ) +{ + YORI_ALLOC_SIZE_T Index; + YORI_ALLOC_SIZE_T Delta; + if (String->LengthInChars >= Align) { + return; + } + ASSERT(String->LengthAllocated >= Align); + if (String->LengthAllocated < Align) { + return; + } + Delta = Align - String->LengthInChars; + for (Index = Align - 1; Index >= Delta; Index--) { + String->StartOfString[Index] = String->StartOfString[Index - Delta]; + } + for (Index = 0; Index < Delta; Index++) { + String->StartOfString[Index] = ' '; + } + String->LengthInChars = Align; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylvolpth.c b/misc/tools/yori/yori/lib/ylvolpth.c new file mode 100644 index 0000000000..a10364b46d --- /dev/null +++ b/misc/tools/yori/yori/lib/ylvolpth.c @@ -0,0 +1,544 @@ +/** + * @file lib/ylvolpth.c + * + * Volume enumeration and information routines. + * + * Copyright (c) 2017-2018 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +/** + Return the volume name of the volume that is hosting a particular file. This + is normally done via the Win32 GetVolumePathName API, which was added in + Windows 2000 to support mount points; on older versions this behavior is + emulated by returning the drive letter or UNC share name. + + @param FileName Pointer to the file name to obtain the volume for. + + @param VolumeName On successful completion, populated with a path to the + volume name. This string is expected to be initialized on entry and + may be reallocated within this routine. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibGetVolumePathName( + __in PYORI_STRING FileName, + __inout PYORI_STRING VolumeName + ) +{ + BOOL FreeOnFailure = FALSE; + ASSERT(YoriLibIsStringNullTerminated(FileName)); + + // + // This function expects a full/escaped path, because Win32 has no way + // to determine the buffer length if it's anything else. + // + + if (FileName->LengthInChars < 4 || + !YoriLibIsSep(FileName->StartOfString[0]) || + !YoriLibIsSep(FileName->StartOfString[1]) || + (FileName->StartOfString[2] != '?' && FileName->StartOfString[2] != '.') || + !YoriLibIsSep(FileName->StartOfString[3])) { + + return FALSE; + } + + // + // The volume name can be as long as the file name, plus a NULL + // terminator. + // + + if (VolumeName->LengthAllocated <= FileName->LengthInChars) { + YoriLibFreeStringContents(VolumeName); + if (!YoriLibAllocateString(VolumeName, FileName->LengthInChars + 1)) { + return FALSE; + } + FreeOnFailure = TRUE; + } + + // + // If Win32 support exists, use it. + // + + if (DllKernel32.pGetVolumePathNameW != NULL) { + if (!DllKernel32.pGetVolumePathNameW(FileName->StartOfString, VolumeName->StartOfString, VolumeName->LengthAllocated)) { + if (FreeOnFailure) { + YoriLibFreeStringContents(VolumeName); + } + return FALSE; + } + VolumeName->LengthInChars = (YORI_ALLOC_SIZE_T)_tcslen(VolumeName->StartOfString); + + // + // For some reason Windows doesn't add the prefix to this string, + // which is really broken - a volume name of "C:" is not a volume + // name, it's a reference to a current directory. + // + + if (!YoriLibIsPathPrefixed(VolumeName)) { + YORI_STRING EscapedVolumeName; + YoriLibInitEmptyString(&EscapedVolumeName); + if (!YoriLibGetFullPathNameReturnAllocation(VolumeName, TRUE, &EscapedVolumeName, NULL)) { + if (FreeOnFailure) { + YoriLibFreeStringContents(VolumeName); + } + return FALSE; + } + + // + // If it fits in the existing allocation, use that. This allows + // the caller to supply an appropriately sized buffer and + // expect the result in that buffer. + // + + if (EscapedVolumeName.LengthInChars < VolumeName->LengthAllocated) { + memcpy(VolumeName->StartOfString, EscapedVolumeName.StartOfString, EscapedVolumeName.LengthInChars * sizeof(TCHAR)); + VolumeName->LengthInChars = EscapedVolumeName.LengthInChars; + VolumeName->StartOfString[VolumeName->LengthInChars] = '\0'; + } else { + YoriLibFreeStringContents(VolumeName); + YoriLibCloneString(VolumeName, &EscapedVolumeName); + FreeOnFailure = TRUE; + } + + YoriLibFreeStringContents(&EscapedVolumeName); + } + + // + // If it ends in a backslash, truncate it + // + + if (VolumeName->LengthInChars > 0 && + YoriLibIsSep(VolumeName->StartOfString[VolumeName->LengthInChars - 1])) { + + VolumeName->LengthInChars--; + VolumeName->StartOfString[VolumeName->LengthInChars] = '\0'; + } + return TRUE; + } + + // + // If Win32 support doesn't exist, we know that mount points can't + // exist so we can return only the drive letter path, or the UNC + // path with server and share. + // + + if (!YoriLibIsFullPathUnc(FileName)) { + if (FileName->LengthInChars >= 6) { + memcpy(VolumeName->StartOfString, FileName->StartOfString, 6 * sizeof(TCHAR)); + VolumeName->StartOfString[6] = '\0'; + VolumeName->LengthInChars = 6; + return TRUE; + } + } else { + if (FileName->LengthInChars >= sizeof("\\\\?\\UNC\\")) { + YORI_STRING Subset; + LPTSTR Slash; + YORI_ALLOC_SIZE_T CharsToCopy; + + YoriLibInitEmptyString(&Subset); + Subset.StartOfString = FileName->StartOfString + sizeof("\\\\?\\UNC\\"); + Subset.LengthInChars = FileName->LengthInChars - sizeof("\\\\?\\UNC\\"); + + Slash = YoriLibFindLeftMostCharacter(&Subset, '\\'); + if (Slash != NULL) { + Subset.LengthInChars = Subset.LengthInChars - (YORI_ALLOC_SIZE_T)(Slash - Subset.StartOfString); + Subset.StartOfString = Slash; + + if (Subset.LengthInChars > 0) { + Subset.LengthInChars--; + Subset.StartOfString++; + Slash = YoriLibFindLeftMostCharacter(&Subset, '\\'); + if (Slash != NULL) { + CharsToCopy = (YORI_ALLOC_SIZE_T)(Slash - FileName->StartOfString); + } else { + CharsToCopy = (YORI_ALLOC_SIZE_T)(Subset.StartOfString - FileName->StartOfString) + Subset.LengthInChars; + } + + memcpy(VolumeName->StartOfString, FileName->StartOfString, CharsToCopy * sizeof(TCHAR)); + VolumeName->StartOfString[CharsToCopy] = '\0'; + VolumeName->LengthInChars = CharsToCopy; + return TRUE; + } + } + } + } + + if (FreeOnFailure) { + YoriLibFreeStringContents(VolumeName); + } + VolumeName->LengthInChars = 0; + return FALSE; +} + +/** + Determine if the specified directory supports long file names. + + @param PathName Pointer to the directory to check. + + @param LongNameSupport On successful completion, updated to TRUE to indicate + long name support, FALSE to indicate no long name support. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOLEAN +YoriLibPathSupportsLongNames( + __in PCYORI_STRING PathName, + __out PBOOLEAN LongNameSupport + ) +{ + YORI_STRING VolumeLabel; + YORI_STRING FsName; + YORI_STRING FullPathName; + YORI_STRING VolRootName; + DWORD ShortSerialNumber; + DWORD Capabilities; + DWORD MaxComponentLength; + BOOLEAN Result; + + YoriLibInitEmptyString(&VolumeLabel); + YoriLibInitEmptyString(&FsName); + YoriLibInitEmptyString(&FullPathName); + YoriLibInitEmptyString(&VolRootName); + Result = FALSE; + + if (!YoriLibAllocateString(&VolumeLabel, 256)) { + goto Exit; + } + + if (!YoriLibAllocateString(&FsName, 256)) { + goto Exit; + } + + if (!YoriLibUserStringToSingleFilePath(PathName, TRUE, &FullPathName)) { + goto Exit; + } + + // + // We want to translate the user specified path into a volume root. + // Windows 2000 and above have a nice API for this, which says it's + // guaranteed to return less than or equal to the size of the input + // string, so we allocate the input string, plus space for a trailing + // backslash and a NULL terminator. + // + + if (!YoriLibAllocateString(&VolRootName, FullPathName.LengthInChars + 2)) { + goto Exit; + } + + if (!YoriLibGetVolumePathName(&FullPathName, &VolRootName)) { + goto Exit; + } + + // + // GetVolumeInformation wants a name with a trailing backslash. Add one + // if needed. + // + + if (VolRootName.LengthInChars > 0 && + VolRootName.LengthInChars + 1 < VolRootName.LengthAllocated && + VolRootName.StartOfString[VolRootName.LengthInChars - 1] != '\\') { + + VolRootName.StartOfString[VolRootName.LengthInChars] = '\\'; + VolRootName.StartOfString[VolRootName.LengthInChars + 1] = '\0'; + VolRootName.LengthInChars++; + } + + if (GetVolumeInformation(VolRootName.StartOfString, + VolumeLabel.StartOfString, + VolumeLabel.LengthAllocated, + &ShortSerialNumber, + &MaxComponentLength, + &Capabilities, + FsName.StartOfString, + FsName.LengthAllocated)) { + + Result = TRUE; + if (MaxComponentLength >= 255) { + *LongNameSupport = TRUE; + } else { + *LongNameSupport = FALSE; + } + } + +Exit: + YoriLibFreeStringContents(&FullPathName); + YoriLibFreeStringContents(&VolRootName); + YoriLibFreeStringContents(&FsName); + YoriLibFreeStringContents(&VolumeLabel); + + return Result; +} + + +/** + Context structure used to preserve state about the next volume to return + when a native platform implementation of FindFirstVolume et al are not + available. + */ +typedef struct _YORI_LIB_FIND_VOLUME_CONTEXT { + + /** + Indicates the number of the drive to probe on the next call to + @ref YoriLibFindNextVolume . + */ + DWORD NextDriveLetter; +} YORI_LIB_FIND_VOLUME_CONTEXT, *PYORI_LIB_FIND_VOLUME_CONTEXT; + +/** + Returns the next volume on the system following a previous call to + @ref YoriLibFindFirstVolume. When no more volumes are available, this + function will return FALSE and set last error to ERROR_NO_MORE_FILES. + When this occurs, the handle must be closed with + @ref YoriLibFindVolumeClose. + + @param FindHandle The handle previously returned from + @ref YoriLibFindFirstVolume . + + @param VolumeName On successful completion, populated with the path to the + next volume found. + + @param BufferLength Specifies the length, in characters, of the VolumeName + buffer. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibFindNextVolume( + __in HANDLE FindHandle, + __out_ecount(BufferLength) LPWSTR VolumeName, + __in DWORD BufferLength + ) +{ + if (DllKernel32.pFindFirstVolumeW && + DllKernel32.pFindNextVolumeW && + DllKernel32.pFindVolumeClose && + DllKernel32.pGetVolumePathNamesForVolumeNameW) { + + return DllKernel32.pFindNextVolumeW(FindHandle, VolumeName, BufferLength); + } else { + PYORI_LIB_FIND_VOLUME_CONTEXT FindContext = (PYORI_LIB_FIND_VOLUME_CONTEXT)FindHandle; + TCHAR ProbeString[sizeof("A:\\")]; + DWORD DriveType; + + while(TRUE) { + if (FindContext->NextDriveLetter + 'A' > 'Z') { + SetLastError(ERROR_NO_MORE_FILES); + return FALSE; + } + + ProbeString[0] = (TCHAR)(FindContext->NextDriveLetter + 'A'); + ProbeString[1] = ':'; + ProbeString[2] = '\\'; + ProbeString[3] = '\0'; + + DriveType = GetDriveType(ProbeString); + if (DriveType != DRIVE_UNKNOWN && + DriveType != DRIVE_NO_ROOT_DIR) { + + if (BufferLength >= sizeof(ProbeString)/sizeof(ProbeString[0])) { + memcpy(VolumeName, ProbeString, (sizeof(ProbeString)/sizeof(ProbeString[0]) - 1) * sizeof(TCHAR)); + VolumeName[sizeof(ProbeString)/sizeof(ProbeString[0]) - 1] = '\0'; + FindContext->NextDriveLetter++; + return TRUE; + } else { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + } + + FindContext->NextDriveLetter++; + } + } + + return FALSE; +} + +/** + Close a handle returned from @ref YoriLibFindFirstVolume . + + @param FindHandle The handle to close. + + @return TRUE to indicate success, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibFindVolumeClose( + __in HANDLE FindHandle + ) +{ + if (DllKernel32.pFindFirstVolumeW && + DllKernel32.pFindNextVolumeW && + DllKernel32.pFindVolumeClose && + DllKernel32.pGetVolumePathNamesForVolumeNameW) { + + return DllKernel32.pFindVolumeClose(FindHandle); + } else { + YoriLibFree(FindHandle); + } + return TRUE; +} + +/** + Returns the first volume on the system and a handle to use for subsequent + volumes with @ref YoriLibFindNextVolume . This handle must be closed with + @ref YoriLibFindVolumeClose. + + @param VolumeName On successful completion, populated with the path to the + first volume found. + + @param BufferLength Specifies the length, in characters, of the VolumeName + buffer. + + @return On successful completion, an opaque handle to use for subsequent + matches by calling @ref YoriLibFindNextVolume , and terminated by + calling @ref YoriLibFindVolumeClose . On failure, + INVALID_HANDLE_VALUE. + */ +__success(return != INVALID_HANDLE_VALUE) +HANDLE +YoriLibFindFirstVolume( + __out LPWSTR VolumeName, + __in DWORD BufferLength + ) +{ + + // + // Windows 2000 supports mount points but doesn't provide the API + // needed to find a human name for them, so we treat it like NT4 + // and only look for drive letter paths. Not including mount points + // seems like a lesser evil than giving the user volume GUIDs. + // + + if (DllKernel32.pFindFirstVolumeW && + DllKernel32.pFindNextVolumeW && + DllKernel32.pFindVolumeClose && + DllKernel32.pGetVolumePathNamesForVolumeNameW) { + + return DllKernel32.pFindFirstVolumeW(VolumeName, BufferLength); + } else { + PYORI_LIB_FIND_VOLUME_CONTEXT FindContext; + + FindContext = YoriLibMalloc(sizeof(YORI_LIB_FIND_VOLUME_CONTEXT)); + if (FindContext == NULL) { + return INVALID_HANDLE_VALUE; + } + + FindContext->NextDriveLetter = 0; + + if (YoriLibFindNextVolume((HANDLE)FindContext, VolumeName, BufferLength)) { + return (HANDLE)FindContext; + } else { + YoriLibFindVolumeClose((HANDLE)FindContext); + } + } + return INVALID_HANDLE_VALUE; +} + +/** + Wrapper that calls GetDiskFreeSpaceEx if present, and if not uses 64 bit + math to calculate total and free disk space up to the limit (which should + be around 8Tb with a 4Kb cluster size.) + + @param DirectoryName Specifies the drive or directory to calculate free + space for. + + @param BytesAvailable Optionally points to a location to receive the amount + of allocatable space on successful completion. + + @param TotalBytes Optionally points to a location to receive the amount + of total space on successful completion. + + @param FreeBytes Optionally points to a location to receive the amount + of unused space on successful completion. + + @return TRUE to indicate successful completion, FALSE to indicate failure. + */ +__success(return) +BOOL +YoriLibGetDiskFreeSpace( + __in LPCTSTR DirectoryName, + __out_opt PLARGE_INTEGER BytesAvailable, + __out_opt PLARGE_INTEGER TotalBytes, + __out_opt PLARGE_INTEGER FreeBytes + ) +{ + LARGE_INTEGER LocalBytesAvailable; + LARGE_INTEGER LocalTotalBytes; + LARGE_INTEGER LocalFreeBytes; + DWORD LocalSectorsPerCluster; + DWORD LocalBytesPerSector; + LARGE_INTEGER LocalAllocationSize; + LARGE_INTEGER LocalNumberOfFreeClusters; + LARGE_INTEGER LocalTotalNumberOfClusters; + BOOL Result; + + if (DllKernel32.pGetDiskFreeSpaceExW != NULL) { + Result = DllKernel32.pGetDiskFreeSpaceExW(DirectoryName, + &LocalBytesAvailable, + &LocalTotalBytes, + &LocalFreeBytes); + if (!Result) { + return FALSE; + } + } else { + + LocalNumberOfFreeClusters.HighPart = 0; + LocalTotalNumberOfClusters.HighPart = 0; + + Result = GetDiskFreeSpace(DirectoryName, + &LocalSectorsPerCluster, + &LocalBytesPerSector, + &LocalNumberOfFreeClusters.LowPart, + &LocalTotalNumberOfClusters.LowPart); + + if (!Result) { + return FALSE; + } + + LocalAllocationSize.QuadPart = LocalSectorsPerCluster * LocalBytesPerSector; + + LocalBytesAvailable.QuadPart = LocalAllocationSize.QuadPart * LocalNumberOfFreeClusters.QuadPart; + LocalFreeBytes.QuadPart = LocalBytesAvailable.QuadPart; + LocalTotalBytes.QuadPart = LocalAllocationSize.QuadPart * LocalTotalNumberOfClusters.QuadPart; + } + + if (BytesAvailable != NULL) { + BytesAvailable->QuadPart = LocalBytesAvailable.QuadPart; + } + if (TotalBytes != NULL) { + TotalBytes->QuadPart = LocalTotalBytes.QuadPart; + } + if (FreeBytes != NULL) { + FreeBytes->QuadPart = LocalFreeBytes.QuadPart; + } + + return TRUE; +} + + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/ylvtdbg.c b/misc/tools/yori/yori/lib/ylvtdbg.c new file mode 100644 index 0000000000..8808deb882 --- /dev/null +++ b/misc/tools/yori/yori/lib/ylvtdbg.c @@ -0,0 +1,184 @@ +/** + * @file lib/vt.c + * + * Support output to the debugger. + * + * Copyright (c) 2015-2021 Malcolm J. Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "yoripch.h" +#include "yorilib.h" + +// +// Debugger functions +// + +/** + Initialize the output stream with any header information. For debugger + output, this is pointless. + + @param hOutput The output stream to initialize. + + @param Context Pointer to context (unused.) + + @return TRUE for success, FALSE on failure. + */ +BOOL +YoriLibDbgInitializeStream( + __in HANDLE hOutput, + __inout PYORI_MAX_UNSIGNED_T Context + ) +{ + UNREFERENCED_PARAMETER(hOutput); + UNREFERENCED_PARAMETER(Context); + + return TRUE; +} + +/** + End processing for the specified stream. For debugger output, this is + pointless. + + @param hOutput Handle to the output device, ignored for debugger. + + @param Context Pointer to context (unused.) + + @return TRUE for success, FALSE on failure. + */ +BOOL +YoriLibDbgEndStream( + __in HANDLE hOutput, + __inout PYORI_MAX_UNSIGNED_T Context + ) +{ + UNREFERENCED_PARAMETER(hOutput); + UNREFERENCED_PARAMETER(Context); + + return TRUE; +} + +/** + Output text between escapes to the debugger. + + @param hOutput Handle to the output device, ignored for debugger. + + @param String Pointer to the string to output. + + @param Context Pointer to context (unused.) + + @return TRUE for success, FALSE on failure. + */ +BOOL +YoriLibDbgProcOutputText( + __in HANDLE hOutput, + __in PCYORI_STRING String, + __inout PYORI_MAX_UNSIGNED_T Context + ) +{ + TCHAR StackBuf[64 + 1]; + LPTSTR Buffer; + DWORD ReadIndex; + DWORD WriteIndex; + + UNREFERENCED_PARAMETER(hOutput); + UNREFERENCED_PARAMETER(Context); + + if (String->LengthInChars <= 64) { + Buffer = StackBuf; + } else { + Buffer = YoriLibMalloc((String->LengthInChars + 1) * sizeof(TCHAR)); + if (Buffer == NULL) { + return FALSE; + } + } + + WriteIndex = 0; + for (ReadIndex = 0; ReadIndex < String->LengthInChars; ReadIndex++) { + if (String->StartOfString[ReadIndex] == '\r') { + if (ReadIndex + 1 < String->LengthInChars && + String->StartOfString[ReadIndex] == '\n') { + + ReadIndex++; + } else { + Buffer[WriteIndex++] = '\n'; + } + } else { + Buffer[WriteIndex++] = String->StartOfString[ReadIndex]; + } + } + + Buffer[WriteIndex] = '\0'; + + OutputDebugString(Buffer); + if (Buffer != StackBuf) { + YoriLibFree(Buffer); + } + + return TRUE; +} + +/** + A dummy callback function to receive an escape and not do anything with it. + + @param hOutput Handle to the output device (ignored.) + + @param String Pointer to a buffer describing the escape (ignored.) + + @param Context Pointer to context (unused.) + + @return TRUE for success, FALSE for failure. + */ +BOOL +YoriLibDbgProcOutputEscape( + __in HANDLE hOutput, + __in PCYORI_STRING String, + __inout PYORI_MAX_UNSIGNED_T Context + ) +{ + UNREFERENCED_PARAMETER(hOutput); + UNREFERENCED_PARAMETER(String); + UNREFERENCED_PARAMETER(Context); + + return TRUE; +} + +/** + Initialize callback functions to a set which will output text to the debugger + and remove any escape sequences. + + @param CallbackFunctions The callback functions to initialize. + + @return TRUE for success, FALSE for failure. + */ +BOOL +YoriLibDbgSetFn( + __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions + ) +{ + CallbackFunctions->InitializeStream = YoriLibDbgInitializeStream; + CallbackFunctions->EndStream = YoriLibDbgEndStream; + CallbackFunctions->ProcessAndOutputText = YoriLibDbgProcOutputText; + CallbackFunctions->ProcessAndOutputEscape = YoriLibDbgProcOutputEscape; + CallbackFunctions->Context = 0; + return TRUE; +} + +// vim:sw=4:ts=4:et: diff --git a/misc/tools/yori/yori/lib/yoricmpt.h b/misc/tools/yori/yori/lib/yoricmpt.h index a133e155c8..6a78c328dc 100644 --- a/misc/tools/yori/yori/lib/yoricmpt.h +++ b/misc/tools/yori/yori/lib/yoricmpt.h @@ -4,7 +4,7 @@ * Yori shell header file to define OS things that the compilation environment * doesn't support. * - * Copyright (c) 2017-2021 Malcolm J. Smith + * Copyright (c) 2017-2024 Malcolm J. Smith * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -2818,6 +2818,41 @@ typedef struct RETRIEVAL_POINTERS_BUFFER { } RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER; #endif +#ifndef FSCTL_MOVE_FILE +/** + Specifies the FSCTL_MOVE_FILE numerical representation if the compilation + environment doesn't provide it. + */ +#define FSCTL_MOVE_FILE CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 29, METHOD_BUFFERED, FILE_ANY_ACCESS) + +/** + Specifies information required to move a file to a different cluster. + */ +typedef struct _MOVE_FILE_DATA { + + /** + A handle to the file. This FSCTL is sent to the volume. + */ + HANDLE FileHandle; + + /** + The file offset to move. + */ + LARGE_INTEGER StartingVcn; + + /** + The target cluster on the volume to move to. + */ + LARGE_INTEGER StartingLcn; + + /** + The number of clusters to move. + */ + DWORD ClusterCount; + +} MOVE_FILE_DATA, *PMOVE_FILE_DATA; +#endif + #ifndef FSCTL_QUERY_ALLOCATED_RANGES /** Specifies the FSCTL_QUERY_ALLOCATED_RANGES numerical representation if the @@ -8718,18 +8753,6 @@ ALLOCATE_AND_INITIALIZE_SID(PSID_IDENTIFIER_AUTHORITY, BYTE, DWORD, DWORD, DWORD */ typedef ALLOCATE_AND_INITIALIZE_SID *PALLOCATE_AND_INITIALIZE_SID; -/** - Prototype for the CheckTokenMembership function. - */ -typedef -BOOL WINAPI -CHECK_TOKEN_MEMBERSHIP(HANDLE, PSID, PBOOL); - -/** - Prototype for a pointer to the CheckTokenMembership function. - */ -typedef CHECK_TOKEN_MEMBERSHIP *PCHECK_TOKEN_MEMBERSHIP; - /** Prototype for the CommandLineFromMsiDescriptor function. */ @@ -8814,6 +8837,18 @@ CRYPT_RELEASE_CONTEXT(DWORD_PTR, DWORD); */ typedef CRYPT_RELEASE_CONTEXT *PCRYPT_RELEASE_CONTEXT; +/** + Prototype for the EqualSid function. + */ +typedef +BOOL WINAPI +EQUAL_SID(PSID, PSID); + +/** + Prototype for a pointer to the EqualSid function. + */ +typedef EQUAL_SID *PEQUAL_SID; + /** Prototype for the FreeSid function. */ @@ -8862,6 +8897,18 @@ GET_SECURITY_DESCRIPTOR_OWNER(PSECURITY_DESCRIPTOR, PSID, LPBOOL); */ typedef GET_SECURITY_DESCRIPTOR_OWNER *PGET_SECURITY_DESCRIPTOR_OWNER; +/** + A prototype for the GetTokenInformation function. + */ +typedef +BOOL WINAPI +GET_TOKEN_INFORMATION(HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD); + +/** + Prototype for a pointer to the GetTokenInformation function. + */ +typedef GET_TOKEN_INFORMATION *PGET_TOKEN_INFORMATION; + /** A prototype for the ImpersonateSelf function. */ @@ -9204,11 +9251,6 @@ typedef struct _YORI_ADVAPI32_FUNCTIONS { */ PALLOCATE_AND_INITIALIZE_SID pAllocateAndInitializeSid; - /** - If it's available on the current system, a pointer to CheckTokenMembership. - */ - PCHECK_TOKEN_MEMBERSHIP pCheckTokenMembership; - /** If it's available on the current system, a pointer to CommandLineFromMsiDescriptor. */ @@ -9244,6 +9286,11 @@ typedef struct _YORI_ADVAPI32_FUNCTIONS { */ PCRYPT_RELEASE_CONTEXT pCryptReleaseContext; + /** + If it's available on the current system, a pointer to EqualSid. + */ + PEQUAL_SID pEqualSid; + /** If it's available on the current system, a pointer to FreeSid. */ @@ -9264,6 +9311,11 @@ typedef struct _YORI_ADVAPI32_FUNCTIONS { */ PGET_SECURITY_DESCRIPTOR_OWNER pGetSecurityDescriptorOwner; + /** + If it's available on the current system, a pointer to GetTokenInformation. + */ + PGET_TOKEN_INFORMATION pGetTokenInformation; + /** If it's available on the current system, a pointer to ImpersonateSelf. */ @@ -10897,6 +10949,56 @@ typedef struct _YORI_USER32_FUNCTIONS { extern YORI_USER32_FUNCTIONS DllUser32; +/** + A prototype for the CreateEnvironmentBlock function. + */ +typedef +BOOL WINAPI +CREATE_ENVIRONMENT_BLOCK(LPVOID *, HANDLE, BOOL); + +/** + A prototype for a pointer to the CreateEnvironmentBlock function. + */ +typedef CREATE_ENVIRONMENT_BLOCK *PCREATE_ENVIRONMENT_BLOCK; + +/** + A prototype for the DestroyEnvironmentBlock function. + */ +typedef +BOOL WINAPI +DESTROY_ENVIRONMENT_BLOCK(LPVOID); + +/** + A prototype for a pointer to the DestroyEnvironmentBlock function. + */ +typedef DESTROY_ENVIRONMENT_BLOCK *PDESTROY_ENVIRONMENT_BLOCK; + +/** + A structure containing optional function pointers to userenv.dll exported + functions which programs can operate without having hard dependencies on. + */ +typedef struct _YORI_USERENV_FUNCTIONS { + /** + A handle to the Dll module. + */ + HINSTANCE hDll; + + /** + If it's available on the current system, a pointer to + CreateEnvironmentBlock. + */ + PCREATE_ENVIRONMENT_BLOCK pCreateEnvironmentBlock; + + /** + If it's available on the current system, a pointer to + DestroyEnvironmentBlock. + */ + PDESTROY_ENVIRONMENT_BLOCK pDestroyEnvironmentBlock; + +} YORI_USERENV_FUNCTIONS, *PYORI_USERENV_FUNCTIONS; + +extern YORI_USERENV_FUNCTIONS DllUserEnv; + /** A prototype for the GetFileVersionInfoSizeW function. */ diff --git a/misc/tools/yori/yori/lib/yorilib.h b/misc/tools/yori/yori/lib/yorilib.h index e1c626a9e7..76dc020d3a 100644 --- a/misc/tools/yori/yori/lib/yorilib.h +++ b/misc/tools/yori/yori/lib/yorilib.h @@ -1302,6 +1302,9 @@ YoriLibLoadShfolderFunctions(VOID); BOOL YoriLibLoadUser32Functions(VOID); +BOOL +YoriLibLoadUserEnvFunctions(VOID); + BOOL YoriLibLoadVersionFunctions(VOID); @@ -1336,10 +1339,12 @@ YoriLibGetEnvironmentStrings( __success(return) BOOL -YoriLibAreEnvironmentStringsValid( +YoriLibAreEnvStringsValid( __inout PYORI_STRING EnvStrings ); +#ifdef UNICODE + __success(return) BOOL YoriLibAreAnsiEnvironmentStringsValid( @@ -1348,23 +1353,25 @@ YoriLibAreAnsiEnvironmentStringsValid( __out PYORI_STRING UnicodeStrings ); +#endif + __success(return) BOOL -YoriLibAllocateAndGetEnvironmentVariable( +YoriLibAllocateAndGetEnvVar( __in LPCTSTR Name, __inout PYORI_STRING Value ); __success(return) BOOL -YoriLibGetEnvironmentVariableAsNumber( +YoriLibGetEnvVarAsNumber( __in LPCTSTR Name, __out PYORI_MAX_SIGNED_T Value ); __success(return) BOOL -YoriLibAddEnvironmentComponentToString( +YoriLibAddEnvCompToString( __inout PYORI_STRING ExistingString, __in PCYORI_STRING NewComponent, __in BOOL InsertAtFront @@ -1372,7 +1379,7 @@ YoriLibAddEnvironmentComponentToString( __success(return) BOOL -YoriLibAddEnvironmentComponentReturnString( +YoriLibAddEnvCompReturnString( __in PYORI_STRING EnvironmentVariable, __in PYORI_STRING NewComponent, __in BOOL InsertAtFront, @@ -1381,7 +1388,7 @@ YoriLibAddEnvironmentComponentReturnString( __success(return) BOOL -YoriLibAddEnvironmentComponent( +YoriLibAddEnvComponent( __in LPTSTR EnvironmentVariable, __in PYORI_STRING NewComponent, __in BOOL InsertAtFront @@ -1389,7 +1396,7 @@ YoriLibAddEnvironmentComponent( __success(return) BOOL -YoriLibRemoveEnvironmentComponentFromString( +YoriLibRmEnvCompFromString( __in PYORI_STRING String, __in PYORI_STRING ComponentToRemove, __out PYORI_STRING Result @@ -1397,7 +1404,7 @@ YoriLibRemoveEnvironmentComponentFromString( __success(return) BOOL -YoriLibRemoveEnvironmentComponentReturnString( +YoriLibRmEnvCompReturnString( __in PYORI_STRING EnvironmentVariable, __in PYORI_STRING ComponentToRemove, __out PYORI_STRING Result @@ -1405,7 +1412,7 @@ YoriLibRemoveEnvironmentComponentReturnString( __success(return) BOOL -YoriLibRemoveEnvironmentComponent( +YoriLibRemoveEnvComponent( __in LPTSTR EnvironmentVariable, __in PYORI_STRING ComponentToRemove ); @@ -2599,6 +2606,14 @@ YoriLibGetDiskFreeSpace( // *** GROUP.C *** +__success(return) +BOOL +YoriLibCheckTokenMembership( + __in_opt HANDLE TokenHandle, + __in PSID SidToCheck, + __out PBOOL IsMember + ); + __success(return) BOOL YoriLibIsCurrentUserInGroup( @@ -2819,7 +2834,7 @@ YoriLibSetMultibyteInputEncoding( ); YORI_ALLOC_SIZE_T -YoriLibGetMultibyteOutputSizeNeeded( +YoriLibGetMbyteOutputSizeNeeded( __in LPCTSTR StringBuffer, __in YORI_ALLOC_SIZE_T BufferLength ); @@ -3733,13 +3748,13 @@ YoriLibAllocateString( ); BOOL -YoriLibReallocateString( +YoriLibReallocString( __inout PYORI_STRING String, __in YORI_ALLOC_SIZE_T CharsToAllocate ); BOOL -YoriLibReallocateStringWithoutPreservingContents( +YoriLibReallocStringNoContents( __inout PYORI_STRING String, __in YORI_ALLOC_SIZE_T CharsToAllocate ); @@ -3779,7 +3794,7 @@ YoriLibDecimalStringToInt( __success(return) BOOL -YoriLibStringToNumberSpecifyBase( +YoriLibStringToNumberBase( __in PCYORI_STRING String, __in WORD Base, __in BOOL IgnoreSeperators, @@ -3833,26 +3848,26 @@ YoriLibRightAlignString( ); int -YoriLibCompareStringWithLiteral( +YoriLibCompareStringLit( __in PCYORI_STRING Str1, __in LPCTSTR str2 ); int -YoriLibCompareStringWithLiteralCount( +YoriLibCompareStringLitCnt( __in PCYORI_STRING Str1, __in LPCTSTR str2, __in YORI_ALLOC_SIZE_T count ); int -YoriLibCompareStringWithLiteralInsensitive( +YoriLibCompareStringLitIns( __in PCYORI_STRING Str1, __in LPCTSTR str2 ); int -YoriLibCompareStringWithLiteralInsensitiveCount( +YoriLibCompareStringLitInsCnt( __in PCYORI_STRING Str1, __in LPCTSTR str2, __in YORI_ALLOC_SIZE_T count @@ -3865,57 +3880,57 @@ YoriLibCompareString( ); int -YoriLibCompareStringInsensitive( +YoriLibCompareStringIns( __in PCYORI_STRING Str1, __in PCYORI_STRING Str2 ); int -YoriLibCompareStringInsensitiveCount( +YoriLibCompareStringInsCnt( __in PCYORI_STRING Str1, __in PCYORI_STRING Str2, __in YORI_ALLOC_SIZE_T count ); int -YoriLibCompareStringCount( +YoriLibCompareStringCnt( __in PCYORI_STRING Str1, __in PCYORI_STRING Str2, __in YORI_ALLOC_SIZE_T count ); YORI_ALLOC_SIZE_T -YoriLibCountStringMatchingChars( +YoriLibCntStringMatchChars( __in PYORI_STRING Str1, __in PYORI_STRING Str2 ); YORI_ALLOC_SIZE_T -YoriLibCountStringMatchingCharsInsensitive( +YoriLibCntStringMatchCharsIns( __in PYORI_STRING Str1, __in PYORI_STRING Str2 ); YORI_ALLOC_SIZE_T -YoriLibCountStringContainingChars( +YoriLibCntStringWithChars( __in PCYORI_STRING String, __in LPCTSTR chars ); YORI_ALLOC_SIZE_T -YoriLibCountStringNotContainingChars( +YoriLibCntStringNotWithChars( __in PCYORI_STRING String, __in LPCTSTR match ); YORI_ALLOC_SIZE_T -YoriLibCountStringTrailingChars( +YoriLibCntStringTrailingChars( __in PCYORI_STRING String, __in LPCTSTR chars ); PYORI_STRING -YoriLibFindFirstMatchingSubstring( +YoriLibFindFirstMatchSubstr( __in PCYORI_STRING String, __in YORI_ALLOC_SIZE_T NumberMatches, __in PYORI_STRING MatchArray, @@ -3923,7 +3938,7 @@ YoriLibFindFirstMatchingSubstring( ); PYORI_STRING -YoriLibFindFirstMatchingSubstringInsensitive( +YoriLibFindFirstMatchSubstrIns( __in PCYORI_STRING String, __in YORI_ALLOC_SIZE_T NumberMatches, __in PYORI_STRING MatchArray, @@ -3931,7 +3946,7 @@ YoriLibFindFirstMatchingSubstringInsensitive( ); PYORI_STRING -YoriLibFindLastMatchingSubstring( +YoriLibFindLastMatchSubstr( __in PCYORI_STRING String, __in YORI_ALLOC_SIZE_T NumberMatches, __in PYORI_STRING MatchArray, @@ -3939,7 +3954,7 @@ YoriLibFindLastMatchingSubstring( ); PYORI_STRING -YoriLibFindLastMatchingSubstringInsensitive( +YoriLibFindLastMatchSubstrIns( __in PCYORI_STRING String, __in YORI_ALLOC_SIZE_T NumberMatches, __in PYORI_STRING MatchArray, @@ -4015,13 +4030,13 @@ YoriLibSortStringArray( ); BOOLEAN -YoriLibStringConcatenate( +YoriLibStringConcat( __inout PYORI_STRING String, __in PCYORI_STRING AppendString ); BOOLEAN -YoriLibStringConcatenateWithLiteral( +YoriLibStringConcatWithLiteral( __inout PYORI_STRING String, __in LPCTSTR AppendString ); @@ -4253,10 +4268,10 @@ YoriLibShellExecuteInstanceToError( The maximum length of a string used to describe a VT sequence generated internally by one of these tools. */ -#define YORI_MAX_INTERNAL_VT_ESCAPE_CHARS sizeof("E[0;999;999;1m") +#define YORI_MAX_VT_ESCAPE_CHARS sizeof("E[0;999;999;1m") BOOL -YoriLibOutputTextToMultibyteDevice( +YoriLibOutputTextToMbyteDev( __in HANDLE hOutput, __in PCYORI_STRING String ); @@ -4342,27 +4357,32 @@ typedef struct _YORI_LIB_VT_CALLBACK_FUNCTIONS { } YORI_LIB_VT_CALLBACK_FUNCTIONS, *PYORI_LIB_VT_CALLBACK_FUNCTIONS; BOOL -YoriLibConsoleSetFunctions( +YoriLibConsoleSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ); BOOL -YoriLibConsoleNoEscapeSetFunctions( +YoriLibConsoleNoEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ); BOOL -YoriLibUtf8TextWithEscapesSetFunctions( +YoriLibUtf8TextWithEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ); BOOL -YoriLibUtf8TextNoEscapesSetFunctions( +YoriLibUtf8TextNoEscSetFn( __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions ); BOOL -YoriLibProcessVtEscapesOnOpenStream( +YoriLibDbgSetFn( + __out PYORI_LIB_VT_CALLBACK_FUNCTIONS CallbackFunctions + ); + +BOOL +YoriLibProcVtEscOnOpenStream( __in LPTSTR String, __in YORI_ALLOC_SIZE_T StringLength, __in HANDLE hOutput, @@ -4371,7 +4391,7 @@ YoriLibProcessVtEscapesOnOpenStream( BOOL YoriLibOutput( - __in DWORD Flags, + __in WORD Flags, __in LPCTSTR szFmt, ... ); @@ -4379,7 +4399,7 @@ YoriLibOutput( BOOL YoriLibOutputToDevice( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in LPCTSTR szFmt, ... ); @@ -4387,14 +4407,14 @@ YoriLibOutputToDevice( BOOL YoriLibOutputString( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in PYORI_STRING String ); BOOL -YoriLibVtSetConsoleTextAttributeOnDevice( +YoriLibVtSetConsoleTextAttrDev( __in HANDLE hOut, - __in DWORD Flags, + __in WORD Flags, __in UCHAR Ctrl, __in WORD Attribute ); @@ -4408,8 +4428,8 @@ YoriLibVtStringForTextAttribute( ); BOOL -YoriLibVtSetConsoleTextAttribute( - __in DWORD Flags, +YoriLibVtSetConsoleTextAttr( + __in WORD Flags, __in WORD Attribute ); @@ -4430,7 +4450,7 @@ WORD YoriLibVtGetDefaultColor(VOID); BOOL -YoriLibVtFinalColorFromSequence( +YoriLibVtFinalColorFromEsc( __in WORD InitialColor, __in PCYORI_STRING EscapeSequence, __out PWORD FinalColor diff --git a/misc/tools/yori/yori/libdlg/about.c b/misc/tools/yori/yori/libdlg/about.c index 155ca78fac..592efc080d 100644 --- a/misc/tools/yori/yori/libdlg/about.c +++ b/misc/tools/yori/yori/libdlg/about.c @@ -113,13 +113,19 @@ YoriDlgAbout( DisplayLength = WindowSize.X - 10; if (CenteredText->LengthInChars > 0) { - CenteredLabelLinesRequired = YoriWinLabelCountLinesRequiredForText(CenteredText, DisplayLength, &CenteredLabelWidthRequired); + CenteredLabelLinesRequired = YoriWinLabelCountLinesRequiredForText(WinMgrHandle, + CenteredText, + DisplayLength, + &CenteredLabelWidthRequired); } else { CenteredLabelLinesRequired = 0; CenteredLabelWidthRequired = 0; } if (LeftText->LengthInChars > 0) { - LeftLabelLinesRequired = YoriWinLabelCountLinesRequiredForText(LeftText, DisplayLength, &LeftLabelWidthRequired); + LeftLabelLinesRequired = YoriWinLabelCountLinesRequiredForText(WinMgrHandle, + LeftText, + DisplayLength, + &LeftLabelWidthRequired); } else { LeftLabelLinesRequired = 0; LeftLabelWidthRequired = 0; diff --git a/misc/tools/yori/yori/libdlg/device.c b/misc/tools/yori/yori/libdlg/device.c index ea672f3b91..4ed2c80d74 100644 --- a/misc/tools/yori/yori/libdlg/device.c +++ b/misc/tools/yori/yori/libdlg/device.c @@ -320,9 +320,9 @@ YoriDlgDevObjectFoundCallback( // If it's any other object type, exit now. // - if (YoriLibCompareStringWithLiteral(ObjectType, _T("SymbolicLink")) == 0) { + if (YoriLibCompareStringLit(ObjectType, _T("SymbolicLink")) == 0) { IsLink = TRUE; - } else if (YoriLibCompareStringWithLiteral(ObjectType, _T("Device")) != 0) { + } else if (YoriLibCompareStringLit(ObjectType, _T("Device")) != 0) { return TRUE; } @@ -337,7 +337,7 @@ YoriDlgDevObjectFoundCallback( YoriLibConstantString(&MatchArray[1], _T("HardDisk")); YoriLibConstantString(&MatchArray[2], _T("CDROM")); - if (YoriLibFindFirstMatchingSubstringInsensitive(NameOnly, sizeof(MatchArray)/sizeof(MatchArray[0]), MatchArray, &MatchOffset) != NULL && + if (YoriLibFindFirstMatchSubstrIns(NameOnly, sizeof(MatchArray)/sizeof(MatchArray[0]), MatchArray, &MatchOffset) != NULL && MatchOffset == 0) { IncludeObject = TRUE; @@ -625,7 +625,7 @@ YoriDlgDevice( Area.Bottom = (WORD)(WindowSize.Y - OptionCount - 6); Area.Right = (WORD)(WindowSize.X - 2); - Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS); + Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS | YORI_WIN_LIST_STYLE_AUTO_HSCROLLBAR); if (Ctrl == NULL) { YoriWinDestroyWindow(Parent); return FALSE; diff --git a/misc/tools/yori/yori/libdlg/dir.c b/misc/tools/yori/yori/libdlg/dir.c index 44db17bc06..681443fb7a 100644 --- a/misc/tools/yori/yori/libdlg/dir.c +++ b/misc/tools/yori/yori/libdlg/dir.c @@ -269,7 +269,7 @@ YoriDlgDirOkButtonClicked( // if (PathComponent.StartOfString == NULL && - YoriLibCompareStringWithLiteral(&FileComponent, _T(".")) == 0) { + YoriLibCompareStringLit(&FileComponent, _T(".")) == 0) { ASSERT(!WildFound); YoriLibCloneString(&FullFilePath, &State->CurrentDirectory); @@ -736,7 +736,7 @@ YoriDlgDir( Area.Bottom = (WORD)(WindowSize.Y - OptionCount - 4); Area.Right = (WORD)(WindowSize.X - 2); - Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS); + Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS | YORI_WIN_LIST_STYLE_AUTO_HSCROLLBAR); if (Ctrl == NULL) { YoriWinDestroyWindow(Parent); return FALSE; diff --git a/misc/tools/yori/yori/libdlg/file.c b/misc/tools/yori/yori/libdlg/file.c index de595cac45..8ee52b3d1b 100644 --- a/misc/tools/yori/yori/libdlg/file.c +++ b/misc/tools/yori/yori/libdlg/file.c @@ -853,7 +853,7 @@ YoriDlgFile( Area.Bottom = (WORD)(WindowSize.Y - OptionCount - 4); Area.Right = (WORD)(WindowSize.X / 2 - 1); - Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS); + Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS | YORI_WIN_LIST_STYLE_AUTO_HSCROLLBAR); if (Ctrl == NULL) { YoriWinDestroyWindow(Parent); return FALSE; @@ -881,7 +881,7 @@ YoriDlgFile( Area.Bottom = (WORD)(WindowSize.Y - OptionCount - 4); Area.Right = (WORD)(WindowSize.X - 2); - Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS); + Ctrl = YoriWinListCreate(Parent, &Area, YORI_WIN_LIST_STYLE_VSCROLLBAR | YORI_WIN_LIST_STYLE_DESELECT_ON_LOSE_FOCUS | YORI_WIN_LIST_STYLE_AUTO_HSCROLLBAR); if (Ctrl == NULL) { YoriWinDestroyWindow(Parent); return FALSE; diff --git a/misc/tools/yori/yori/libdlg/msgbox.c b/misc/tools/yori/yori/libdlg/msgbox.c index eb2a6e96a8..1ef2e86dfb 100644 --- a/misc/tools/yori/yori/libdlg/msgbox.c +++ b/misc/tools/yori/yori/libdlg/msgbox.c @@ -108,7 +108,10 @@ YoriDlgMessageBox( // DisplayLength = WindowSize.X - 8; - LabelLinesRequired = YoriWinLabelCountLinesRequiredForText(Text, DisplayLength, &LabelWidthRequired); + LabelLinesRequired = YoriWinLabelCountLinesRequiredForText(WinMgrHandle, + Text, + DisplayLength, + &LabelWidthRequired); // // Vertically, the window has 7 lines of overhead (title bar, padding