diff --git a/QuickLook.Native/QuickLook.Native32/DOpus.cpp b/QuickLook.Native/QuickLook.Native32/DOpus.cpp index 09689c6..116e4d8 100644 --- a/QuickLook.Native/QuickLook.Native32/DOpus.cpp +++ b/QuickLook.Native/QuickLook.Native32/DOpus.cpp @@ -34,111 +34,111 @@ PCHAR pXmlBuffer; void DOpus::GetSelected(PWCHAR buffer) { - /* - * CPU Disasm - * 6A44B588 |. 68 A839526A PUSH OFFSET 6A5239A8 ; /WindowName = "Directory Opus" - * 6A44B58D |. 68 6459526A PUSH OFFSET 6A525964 ; |ClassName = "DOpus.ParentWindow" - * 6A44B592 |. 894424 40 MOV DWORD PTR SS:[ESP+40],EAX ; | - * 6A44B596 |. C74424 30 140 MOV DWORD PTR SS:[ESP+30],14 ; | - * 6A44B59E |. FFD7 CALL EDI ; \USER32.FindWindowW - * ... - * 00BC2E9B |. 50 PUSH EAX ; /lParam = 12FE80C -> 15 ; - * 00BC2E9C |. 57 PUSH EDI ; |wParam ; class = dopusrt.getinfo - * 00BC2E9D |. 6A 4A PUSH 4A ; |Msg = WM_COPYDATA - * 00BC2E9F |. FF75 FC PUSH DWORD PTR SS:[LOCAL.1] ; |hWnd => [LOCAL.1] ; class = DOpus.ParentWindow, text = Directory Opus - * 00BC2EA2 |. FF15 0802C000 CALL DWORD PTR DS:[<&USER32.SendMessageW ; \USER32.SendMessageW - * - * CPU Stack - * 012FE80C |00000015  - * 012FE810 |00000010 - * 012FE814 |013A26C0 ; UNICODE "listsel" - */ + /* + * CPU Disasm + * 6A44B588 |. 68 A839526A PUSH OFFSET 6A5239A8 ; /WindowName = "Directory Opus" + * 6A44B58D |. 68 6459526A PUSH OFFSET 6A525964 ; |ClassName = "DOpus.ParentWindow" + * 6A44B592 |. 894424 40 MOV DWORD PTR SS:[ESP+40],EAX ; | + * 6A44B596 |. C74424 30 140 MOV DWORD PTR SS:[ESP+30],14 ; | + * 6A44B59E |. FFD7 CALL EDI ; \USER32.FindWindowW + * ... + * 00BC2E9B |. 50 PUSH EAX ; /lParam = 12FE80C -> 15 ; + * 00BC2E9C |. 57 PUSH EDI ; |wParam ; class = dopusrt.getinfo + * 00BC2E9D |. 6A 4A PUSH 4A ; |Msg = WM_COPYDATA + * 00BC2E9F |. FF75 FC PUSH DWORD PTR SS:[LOCAL.1] ; |hWnd => [LOCAL.1] ; class = DOpus.ParentWindow, text = Directory Opus + * 00BC2EA2 |. FF15 0802C000 CALL DWORD PTR DS:[<&USER32.SendMessageW ; \USER32.SendMessageW + * + * CPU Stack + * 012FE80C |00000015  + * 012FE810 |00000010 + * 012FE814 |013A26C0 ; UNICODE "listsel" + */ - if (hMsgWnd == nullptr) - PrepareMessageWindow(); - if (hMsgWnd == nullptr) - return; + if (hMsgWnd == nullptr) + PrepareMessageWindow(); + if (hMsgWnd == nullptr) + return; - PWCHAR data = DOPUS_IPC_LP_DATA; - COPYDATASTRUCT cds; - cds.dwData = DOPUS_IPC_LP_INFO; - cds.cbData = static_cast(wcslen(data) + 1) * sizeof WCHAR; - cds.lpData = data; + PWCHAR data = DOPUS_IPC_LP_DATA; + COPYDATASTRUCT cds; + cds.dwData = DOPUS_IPC_LP_INFO; + cds.cbData = static_cast(wcslen(data) + 1) * sizeof WCHAR; + cds.lpData = data; - auto ret = SendMessage(FindWindow(DOPUS_CLASS, DOPUS_NAME), WM_COPYDATA, reinterpret_cast(hMsgWnd), - reinterpret_cast(&cds)); - if (!ret) - return; + auto ret = SendMessage(FindWindow(DOPUS_CLASS, DOPUS_NAME), WM_COPYDATA, reinterpret_cast(hMsgWnd), + reinterpret_cast(&cds)); + if (!ret) + return; - WaitForSingleObject(hGetResultEvent, 2000); + WaitForSingleObject(hGetResultEvent, 2000); - ParseXmlBuffer(buffer); + ParseXmlBuffer(buffer); - delete[] pXmlBuffer; + delete[] pXmlBuffer; } void DOpus::ParseXmlBuffer(PWCHAR buffer) { - /* - * - * - * - * - * - * ... - */ + /* + * + * + * + * + * + * ... + */ - using namespace rapidxml; + using namespace rapidxml; - xml_document<> doc; - doc.parse<0>(pXmlBuffer); + xml_document<> doc; + doc.parse<0>(pXmlBuffer); - auto results = doc.first_node("results"); - auto items = results->first_node("items"); - for (auto item = items->first_node("item"); item; item = item->next_sibling("item")) - { - auto path = item->first_attribute("path")->value(); + auto results = doc.first_node("results"); + auto items = results->first_node("items"); + for (auto item = items->first_node("item"); item; item = item->next_sibling("item")) + { + auto path = item->first_attribute("path")->value(); - auto size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, nullptr, 0); - auto b = new WCHAR[size]; - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, b, size); + auto size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, nullptr, 0); + auto b = new WCHAR[size]; + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, b, size); - wcscpy_s(buffer, wcslen(b) + 1, b); // DOpus supports Long Path + wcscpy_s(buffer, wcslen(b) + 1, b); // DOpus supports Long Path - delete[] b; - return; // we now cares only the first result - } + delete[] b; + return; // we now cares only the first result + } } void DOpus::PrepareMessageWindow() { - WNDCLASSEX wx = {sizeof wx}; - wx.cbSize = sizeof(WNDCLASSEX); - wx.lpfnWndProc = msgWindowProc; - wx.lpszClassName = MSGWINDOW_CLASS; + WNDCLASSEX wx = {sizeof wx}; + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = msgWindowProc; + wx.lpszClassName = MSGWINDOW_CLASS; - if (RegisterClassEx(&wx)) - hMsgWnd = CreateWindowEx(0, MSGWINDOW_CLASS, L"", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); + if (RegisterClassEx(&wx)) + hMsgWnd = CreateWindowEx(0, MSGWINDOW_CLASS, L"", 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); - hGetResultEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + hGetResultEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); } LRESULT CALLBACK DOpus::msgWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - switch (uMsg) - { - case WM_COPYDATA: - { - auto cds = reinterpret_cast(lParam); - auto buf = static_cast(cds->lpData); + switch (uMsg) + { + case WM_COPYDATA: + { + auto cds = reinterpret_cast(lParam); + auto buf = static_cast(cds->lpData); - pXmlBuffer = new CHAR[cds->cbData + 1]{'\0'}; - memcpy(pXmlBuffer, buf, cds->cbData); + pXmlBuffer = new CHAR[cds->cbData + 1]{'\0'}; + memcpy(pXmlBuffer, buf, cds->cbData); - SetEvent(hGetResultEvent); - return 0; - } - default: - return DefWindowProc(hWnd, uMsg, wParam, lParam); - } + SetEvent(hGetResultEvent); + return 0; + } + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } } diff --git a/QuickLook.Native/QuickLook.Native32/DialogHook.cpp b/QuickLook.Native/QuickLook.Native32/DialogHook.cpp index 7feb7e2..030f9cc 100644 --- a/QuickLook.Native/QuickLook.Native32/DialogHook.cpp +++ b/QuickLook.Native/QuickLook.Native32/DialogHook.cpp @@ -34,122 +34,122 @@ static WCHAR filePathBuffer[MAX_PATH] = {'\0'}; void DialogHook::GetSelected(PWCHAR buffer) { - if (HelperMethods::IsUWP()) - return; + if (HelperMethods::IsUWP()) + return; - auto hwndfg = GetForegroundWindow(); - DWORD pid = 0; - auto tid = GetWindowThreadProcessId(hwndfg, &pid); + auto hwndfg = GetForegroundWindow(); + DWORD pid = 0; + auto tid = GetWindowThreadProcessId(hwndfg, &pid); - auto hProc = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid); - if (hProc == nullptr) - return; + auto hProc = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid); + if (hProc == nullptr) + return; - if (WM_HOOK_NOTIFY == 0) - WM_HOOK_NOTIFY = RegisterWindowMessage(L"WM_QUICKLOOK_HOOK_NOTIFY_MSG"); + if (WM_HOOK_NOTIFY == 0) + WM_HOOK_NOTIFY = RegisterWindowMessage(L"WM_QUICKLOOK_HOOK_NOTIFY_MSG"); - BOOL isTargetWoW64 = false; - if (!IsWow64Process(hProc, &isTargetWoW64)) - return; + BOOL isTargetWoW64 = false; + if (!IsWow64Process(hProc, &isTargetWoW64)) + return; - CloseHandle(hProc); + CloseHandle(hProc); - BOOL isSelfWoW64 = false; - if (!IsWow64Process(GetCurrentProcess(), &isSelfWoW64)) - return; + BOOL isSelfWoW64 = false; + if (!IsWow64Process(GetCurrentProcess(), &isSelfWoW64)) + return; - // if QuickLook is 64bit and target is 32bit, obtain result from the helper - if (isTargetWoW64 && !isSelfWoW64) - { - GetSelectedFromWoW64HookHelper(buffer); - } - else - { - if (ghHook != nullptr) - UnhookWindowsHookEx(ghHook); - ghHook = SetWindowsHookEx(WH_CALLWNDPROC, static_cast(MsgHookProc), ModuleFromAddress(MsgHookProc), tid); - if (ghHook == nullptr) - return; + // if QuickLook is 64bit and target is 32bit, obtain result from the helper + if (isTargetWoW64 && !isSelfWoW64) + { + GetSelectedFromWoW64HookHelper(buffer); + } + else + { + if (ghHook != nullptr) + UnhookWindowsHookEx(ghHook); + ghHook = SetWindowsHookEx(WH_CALLWNDPROC, static_cast(MsgHookProc), ModuleFromAddress(MsgHookProc), tid); + if (ghHook == nullptr) + return; - SendMessage(hwndfg, WM_HOOK_NOTIFY, 0, 0); - - GetLongPathName(filePathBuffer, buffer, MAX_PATH_EX); - } + SendMessage(hwndfg, WM_HOOK_NOTIFY, 0, 0); + + GetLongPathName(filePathBuffer, buffer, MAX_PATH_EX); + } } void DialogHook::getSelectedInternal(CComPtr psb, PWCHAR buffer) { - CComPtr psv; - if (FAILED(psb->QueryActiveShellView(&psv))) - return; + CComPtr psv; + if (FAILED(psb->QueryActiveShellView(&psv))) + return; - CComPtr dao; - if (FAILED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast(&dao)))) - return; + CComPtr dao; + if (FAILED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast(&dao)))) + return; - return HelperMethods::ObtainFirstItem(dao, buffer); + return HelperMethods::ObtainFirstItem(dao, buffer); } void DialogHook::GetSelectedFromWoW64HookHelper(PWCHAR buffer) { - if (!WoW64HookHelper::CheckStatus()) - if (!WoW64HookHelper::Launch()) - return; + if (!WoW64HookHelper::CheckStatus()) + if (!WoW64HookHelper::Launch()) + return; - auto hHelperWnd = FindWindowEx(HWND_MESSAGE, nullptr, WoW64HookHelper::GetMsgWindowClassName(), nullptr); - if (hHelperWnd == nullptr) - return; + auto hHelperWnd = FindWindowEx(HWND_MESSAGE, nullptr, WoW64HookHelper::GetMsgWindowClassName(), nullptr); + if (hHelperWnd == nullptr) + return; - auto hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, MAX_PATH * sizeof WCHAR, - SHARED_MEM_NAME); - if (hMapFile == nullptr) - return; + auto hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, MAX_PATH * sizeof WCHAR, + SHARED_MEM_NAME); + if (hMapFile == nullptr) + return; - auto sharedBuffer = static_cast(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, MAX_PATH * sizeof WCHAR)); - if (sharedBuffer == nullptr) - { - CloseHandle(hMapFile); - return; - } + auto sharedBuffer = static_cast(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, MAX_PATH * sizeof WCHAR)); + if (sharedBuffer == nullptr) + { + CloseHandle(hMapFile); + return; + } - SendMessage(hHelperWnd, WM_HOOK_NOTIFY, 0, 0); + SendMessage(hHelperWnd, WM_HOOK_NOTIFY, 0, 0); - // the sharedBuffer should now ready - GetLongPathName(sharedBuffer, buffer, MAX_PATH_EX); + // the sharedBuffer should now ready + GetLongPathName(sharedBuffer, buffer, MAX_PATH_EX); - UnmapViewOfFile(sharedBuffer); - CloseHandle(hMapFile); + UnmapViewOfFile(sharedBuffer); + CloseHandle(hMapFile); } HMODULE DialogHook::ModuleFromAddress(PVOID pv) { - MEMORY_BASIC_INFORMATION mbi; - return VirtualQuery(pv, &mbi, sizeof mbi) != 0 ? static_cast(mbi.AllocationBase) : nullptr; + MEMORY_BASIC_INFORMATION mbi; + return VirtualQuery(pv, &mbi, sizeof mbi) != 0 ? static_cast(mbi.AllocationBase) : nullptr; } LRESULT DialogHook::MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam) { - if (nCode < 0) - goto CONTINUE; + if (nCode < 0) + goto CONTINUE; - auto pMSG = reinterpret_cast(lParam); + auto pMSG = reinterpret_cast(lParam); - // only response to WM_HOOK_NOTIFY - if (pMSG->message != WM_HOOK_NOTIFY) - goto CONTINUE; + // only response to WM_HOOK_NOTIFY + if (pMSG->message != WM_HOOK_NOTIFY) + goto CONTINUE; - UnhookWindowsHookEx(ghHook); - ghHook = nullptr; + UnhookWindowsHookEx(ghHook); + ghHook = nullptr; - // get current selected item - auto psb = reinterpret_cast(SendMessage(GetForegroundWindow(), WM_USER + 7, 0, 0)); - if (psb == nullptr) - goto CONTINUE; + // get current selected item + auto psb = reinterpret_cast(SendMessage(GetForegroundWindow(), WM_USER + 7, 0, 0)); + if (psb == nullptr) + goto CONTINUE; - getSelectedInternal(psb, filePathBuffer); + getSelectedInternal(psb, filePathBuffer); - return 0; + return 0; CONTINUE: - return CallNextHookEx(ghHook, nCode, wParam, lParam); + return CallNextHookEx(ghHook, nCode, wParam, lParam); } diff --git a/QuickLook.Native/QuickLook.Native32/DialogHook.h b/QuickLook.Native/QuickLook.Native32/DialogHook.h index a3351ea..c2b867f 100644 --- a/QuickLook.Native/QuickLook.Native32/DialogHook.h +++ b/QuickLook.Native/QuickLook.Native32/DialogHook.h @@ -19,11 +19,11 @@ class DialogHook { public: - static void GetSelected(PWCHAR buffer); + static void GetSelected(PWCHAR buffer); private: - static void getSelectedInternal(CComPtr psb, PWCHAR buffer); - static void GetSelectedFromWoW64HookHelper(PWCHAR buffer); - static HMODULE ModuleFromAddress(PVOID pv); - static LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam); + static void getSelectedInternal(CComPtr psb, PWCHAR buffer); + static void GetSelectedFromWoW64HookHelper(PWCHAR buffer); + static HMODULE ModuleFromAddress(PVOID pv); + static LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam); }; diff --git a/QuickLook.Native/QuickLook.Native32/DllExport.cpp b/QuickLook.Native/QuickLook.Native32/DllExport.cpp index ac5b22d..f194fef 100644 --- a/QuickLook.Native/QuickLook.Native32/DllExport.cpp +++ b/QuickLook.Native/QuickLook.Native32/DllExport.cpp @@ -27,18 +27,18 @@ EXPORT void Init() { #ifdef WIN64 - WoW64HookHelper::Launch(); + WoW64HookHelper::Launch(); #endif - DOpus::PrepareMessageWindow(); + DOpus::PrepareMessageWindow(); MultiCommander::PrepareMessageWindow(); } EXPORT Shell32::FocusedWindowType GetFocusedWindowType() { - return Shell32::GetFocusedWindowType(); + return Shell32::GetFocusedWindowType(); } EXPORT void GetCurrentSelection(PWCHAR buffer) { - Shell32::GetCurrentSelection(buffer); + Shell32::GetCurrentSelection(buffer); } diff --git a/QuickLook.Native/QuickLook.Native32/Everything.cpp b/QuickLook.Native/QuickLook.Native32/Everything.cpp index 0041756..920ef45 100644 --- a/QuickLook.Native/QuickLook.Native32/Everything.cpp +++ b/QuickLook.Native/QuickLook.Native32/Everything.cpp @@ -20,66 +20,66 @@ void Everything::GetSelected(PWCHAR buffer) { - auto hWinFG = GetForegroundWindow(); - - // Everything v1.5 IPC via hidden window. - HWND hWinI = FindWindowExW(hWinFG, NULL, EVERYTHING_IPC_HIDDEN_WIN_CLASS, NULL); - - if (hWinI != nullptr) { - int pLength = GetWindowTextLength(hWinI); - wchar_t* pText = new wchar_t[pLength + 1]; - GetWindowText(hWinI, pText, pLength + 1); - wcsncpy_s(buffer, MAX_PATH_EX, pText, pLength); - return; // Success. Clipboard access unnecessary. - } + auto hWinFG = GetForegroundWindow(); + + // Everything v1.5 IPC via hidden window. + HWND hWinI = FindWindowExW(hWinFG, NULL, EVERYTHING_IPC_HIDDEN_WIN_CLASS, NULL); + + if (hWinI != nullptr) { + int pLength = GetWindowTextLength(hWinI); + wchar_t* pText = new wchar_t[pLength + 1]; + GetWindowText(hWinI, pText, pLength + 1); + wcsncpy_s(buffer, MAX_PATH_EX, pText, pLength); + return; // Success. Clipboard access unnecessary. + } - HWND hWin = FindWindowW(EVERYTHING_IPC_WINDOW_CLASS, NULL); + HWND hWin = FindWindowW(EVERYTHING_IPC_WINDOW_CLASS, NULL); - if (hWin != nullptr) { - // Everything IPC Clipboard - SendMessageW( - hWin, - WM_COMMAND, - MAKEWPARAM(EVERYTHING_IPC_COPY_TO_CLIPBOARD, 0), - 0); + if (hWin != nullptr) { + // Everything IPC Clipboard + SendMessageW( + hWin, + WM_COMMAND, + MAKEWPARAM(EVERYTHING_IPC_COPY_TO_CLIPBOARD, 0), + 0); - Sleep(100); - - if (!OpenClipboard(nullptr)) - return; - - auto hData = GetClipboardData(CF_UNICODETEXT); - if (hData == nullptr) - return; + Sleep(100); + + if (!OpenClipboard(nullptr)) + return; + + auto hData = GetClipboardData(CF_UNICODETEXT); + if (hData == nullptr) + return; - auto pText = static_cast(GlobalLock(hData)); - if (pText == nullptr) - return; + auto pText = static_cast(GlobalLock(hData)); + if (pText == nullptr) + return; - auto p = wcsstr(pText, L"\r\n"); - auto l = p == nullptr ? wcslen(pText) : p - pText; - wcsncpy_s(buffer, MAX_PATH_EX, pText, l); // Everything supports Long Path - - GlobalUnlock(hData); - CloseClipboard(); - } + auto p = wcsstr(pText, L"\r\n"); + auto l = p == nullptr ? wcslen(pText) : p - pText; + wcsncpy_s(buffer, MAX_PATH_EX, pText, l); // Everything supports Long Path + + GlobalUnlock(hData); + CloseClipboard(); + } } bool Everything::MatchClass(PWCHAR classBuffer) { - WCHAR sMatchC[256] = { '\0' }; - WCHAR sMatchS[256] = EVERYTHING_IPC_WINDOW_CLASS; - size_t iLen = wcslen(sMatchS); - wcsncpy_s(sMatchC, classBuffer, iLen); - return (0 == wcscmp(sMatchC, sMatchS)); + WCHAR sMatchC[256] = { '\0' }; + WCHAR sMatchS[256] = EVERYTHING_IPC_WINDOW_CLASS; + size_t iLen = wcslen(sMatchS); + wcsncpy_s(sMatchC, classBuffer, iLen); + return (0 == wcscmp(sMatchC, sMatchS)); } void Everything::backupClipboard() { - // TODO + // TODO } void Everything::restoreClipboard() { - // TODO + // TODO } diff --git a/QuickLook.Native/QuickLook.Native32/Everything.h b/QuickLook.Native/QuickLook.Native32/Everything.h index 6e4b4be..4dd53ac 100644 --- a/QuickLook.Native/QuickLook.Native32/Everything.h +++ b/QuickLook.Native/QuickLook.Native32/Everything.h @@ -15,18 +15,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#define EVERYTHING_IPC_HIDDEN_WIN_CLASS L"EVERYTHING_RESULT_LIST_FOCUS" -#define EVERYTHING_IPC_WINDOW_CLASS L"EVERYTHING" -#define EVERYTHING_IPC_COPY_TO_CLIPBOARD 41007 +#define EVERYTHING_IPC_HIDDEN_WIN_CLASS L"EVERYTHING_RESULT_LIST_FOCUS" +#define EVERYTHING_IPC_WINDOW_CLASS L"EVERYTHING" +#define EVERYTHING_IPC_COPY_TO_CLIPBOARD 41007 #pragma once class Everything { public: - static void GetSelected(PWCHAR buffer); - static bool MatchClass(PWCHAR classBuffer); + static void GetSelected(PWCHAR buffer); + static bool MatchClass(PWCHAR classBuffer); private: - static void backupClipboard(); - static void restoreClipboard(); + static void backupClipboard(); + static void restoreClipboard(); }; diff --git a/QuickLook.Native/QuickLook.Native32/HelperMethods.cpp b/QuickLook.Native/QuickLook.Native32/HelperMethods.cpp index c9c01c0..de2c796 100644 --- a/QuickLook.Native/QuickLook.Native32/HelperMethods.cpp +++ b/QuickLook.Native/QuickLook.Native32/HelperMethods.cpp @@ -20,114 +20,114 @@ void HelperMethods::GetSelectedInternal(CComPtr psb, PWCHAR buffer) { - CComPtr psv; - if (FAILED(psb->QueryActiveShellView(&psv))) - return; + CComPtr psv; + if (FAILED(psb->QueryActiveShellView(&psv))) + return; - CComPtr dao; - if (FAILED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast(&dao)))) - return; + CComPtr dao; + if (FAILED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast(&dao)))) + return; - return ObtainFirstItem(dao, buffer); + return ObtainFirstItem(dao, buffer); } void HelperMethods::ObtainFirstItem(CComPtr dao, PWCHAR buffer) { - FORMATETC formatetc; - STGMEDIUM medium = {sizeof medium}; + FORMATETC formatetc; + STGMEDIUM medium = {sizeof medium}; - formatetc.cfFormat = CF_HDROP; - formatetc.ptd = nullptr; - formatetc.dwAspect = DVASPECT_CONTENT; - formatetc.lindex = -1; - formatetc.tymed = TYMED_HGLOBAL; + formatetc.cfFormat = CF_HDROP; + formatetc.ptd = nullptr; + formatetc.dwAspect = DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = TYMED_HGLOBAL; - medium.tymed = TYMED_HGLOBAL; + medium.tymed = TYMED_HGLOBAL; - if (FAILED(dao->GetData(&formatetc, &medium))) - return; + if (FAILED(dao->GetData(&formatetc, &medium))) + return; - int n = DragQueryFile(HDROP(medium.hGlobal), 0xFFFFFFFF, nullptr, 0); + int n = DragQueryFile(HDROP(medium.hGlobal), 0xFFFFFFFF, nullptr, 0); - if (n < 1) - return; + if (n < 1) + return; - WCHAR localBuffer[MAX_PATH] = { '\0' }; - DragQueryFile(HDROP(medium.hGlobal), 0, localBuffer, MAX_PATH); - - GetLongPathName(localBuffer, buffer, MAX_PATH_EX); + WCHAR localBuffer[MAX_PATH] = { '\0' }; + DragQueryFile(HDROP(medium.hGlobal), 0, localBuffer, MAX_PATH); + + GetLongPathName(localBuffer, buffer, MAX_PATH_EX); } bool HelperMethods::IsListaryToolbarVisible() { - auto CALLBACK findListaryWindowProc = [](__in HWND hwnd, __in LPARAM lParam)-> BOOL - { - WCHAR classBuffer[MAX_PATH] = {'\0'}; - if (FAILED(GetClassName(hwnd, classBuffer, MAX_PATH))) - return TRUE; + auto CALLBACK findListaryWindowProc = [](__in HWND hwnd, __in LPARAM lParam)-> BOOL + { + WCHAR classBuffer[MAX_PATH] = {'\0'}; + if (FAILED(GetClassName(hwnd, classBuffer, MAX_PATH))) + return TRUE; - if (wcsncmp(classBuffer, L"Listary_WidgetWin_", 18) == 0) - { - if (IsWindowVisible(hwnd)) - { - *reinterpret_cast(lParam) = true; - return FALSE; - } - } - return TRUE; - }; + if (wcsncmp(classBuffer, L"Listary_WidgetWin_", 18) == 0) + { + if (IsWindowVisible(hwnd)) + { + *reinterpret_cast(lParam) = true; + return FALSE; + } + } + return TRUE; + }; - auto found = false; - EnumWindows(findListaryWindowProc, reinterpret_cast(&found)); + auto found = false; + EnumWindows(findListaryWindowProc, reinterpret_cast(&found)); - return found; + return found; } // Windows 10 1909 replaced the search box in the File Explorer by a UWP control. // gti.flags is always 0 for UWP applications. bool HelperMethods::IsExplorerSearchBoxFocused() { - auto* hwnd = GetFocusedControl(); + auto* hwnd = GetFocusedControl(); - WCHAR classBuffer[MAX_PATH] = { '\0' }; - if (FAILED(GetClassName(hwnd, classBuffer, MAX_PATH))) - return false; + WCHAR classBuffer[MAX_PATH] = { '\0' }; + if (FAILED(GetClassName(hwnd, classBuffer, MAX_PATH))) + return false; - return wcscmp(classBuffer, L"Windows.UI.Core.CoreWindow") == 0; + return wcscmp(classBuffer, L"Windows.UI.Core.CoreWindow") == 0; } bool HelperMethods::IsCursorActivated(HWND hwnd) { - auto tId = GetWindowThreadProcessId(hwnd, nullptr); + auto tId = GetWindowThreadProcessId(hwnd, nullptr); - GUITHREADINFO gti = { sizeof gti }; - GetGUIThreadInfo(tId, >i); + GUITHREADINFO gti = { sizeof gti }; + GetGUIThreadInfo(tId, >i); - return gti.flags || gti.hwndCaret || IsListaryToolbarVisible(); + return gti.flags || gti.hwndCaret || IsListaryToolbarVisible(); } bool HelperMethods::IsUWP() { - auto pGCPFN = decltype(&GetCurrentPackageFullName)( - GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetCurrentPackageFullName")); + auto pGCPFN = decltype(&GetCurrentPackageFullName)( + GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetCurrentPackageFullName")); - if (!pGCPFN) - return false; + if (!pGCPFN) + return false; - UINT32 pn = 0; - return pGCPFN(&pn, nullptr) == ERROR_INSUFFICIENT_BUFFER; + UINT32 pn = 0; + return pGCPFN(&pn, nullptr) == ERROR_INSUFFICIENT_BUFFER; } HWND HelperMethods::GetFocusedControl() { - auto tid = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); + auto tid = GetWindowThreadProcessId(GetForegroundWindow(), nullptr); - if (0 == AttachThreadInput(GetCurrentThreadId(), tid, TRUE)) - return nullptr; + if (0 == AttachThreadInput(GetCurrentThreadId(), tid, TRUE)) + return nullptr; - auto* hwnd = GetFocus(); + auto* hwnd = GetFocus(); - AttachThreadInput(GetCurrentThreadId(), tid, FALSE); + AttachThreadInput(GetCurrentThreadId(), tid, FALSE); - return hwnd; + return hwnd; } diff --git a/QuickLook.Native/QuickLook.Native32/HelperMethods.h b/QuickLook.Native/QuickLook.Native32/HelperMethods.h index 94c713c..cc936a6 100644 --- a/QuickLook.Native/QuickLook.Native32/HelperMethods.h +++ b/QuickLook.Native/QuickLook.Native32/HelperMethods.h @@ -19,13 +19,13 @@ class HelperMethods { public: - static void GetSelectedInternal(CComPtr psb, PWCHAR buffer); - static void ObtainFirstItem(CComPtr dao, PWCHAR buffer); - static bool IsCursorActivated(HWND hwndfg); - static bool IsExplorerSearchBoxFocused(); - static bool HelperMethods::IsUWP(); + static void GetSelectedInternal(CComPtr psb, PWCHAR buffer); + static void ObtainFirstItem(CComPtr dao, PWCHAR buffer); + static bool IsCursorActivated(HWND hwndfg); + static bool IsExplorerSearchBoxFocused(); + static bool HelperMethods::IsUWP(); private: - static bool IsListaryToolbarVisible(); - static HWND GetFocusedControl(); + static bool IsListaryToolbarVisible(); + static HWND GetFocusedControl(); }; diff --git a/QuickLook.Native/QuickLook.Native32/MultiCommander.cpp b/QuickLook.Native/QuickLook.Native32/MultiCommander.cpp index 025250f..3829ef2 100644 --- a/QuickLook.Native/QuickLook.Native32/MultiCommander.cpp +++ b/QuickLook.Native/QuickLook.Native32/MultiCommander.cpp @@ -27,9 +27,9 @@ void MultiCommander::GetSelected(PWCHAR buffer) } COPYDATASTRUCT cds; - cds.dwData = MULTICMD_CPF_GETCURITEMFULL | MULTICMD_CPF_SOURCE; - cds.cbData = 0; - cds.lpData = nullptr; + cds.dwData = MULTICMD_CPF_GETCURITEMFULL | MULTICMD_CPF_SOURCE; + cds.cbData = 0; + cds.lpData = nullptr; ResetEvent(hGetResultEvent); @@ -37,12 +37,12 @@ void MultiCommander::GetSelected(PWCHAR buffer) FindWindow(MULTICMD_CLASS, nullptr), WM_COPYDATA, reinterpret_cast(hMsgWnd), - reinterpret_cast(&cds) + reinterpret_cast(&cds) ); - if (!ret || WAIT_OBJECT_0 != WaitForSingleObject(hGetResultEvent, 2000)) { + if (!ret || WAIT_OBJECT_0 != WaitForSingleObject(hGetResultEvent, 2000)) { return; - } + } auto path = reinterpret_cast(pCurrentItemPath); wcscpy_s(buffer, wcslen(path) + 1, path); @@ -78,21 +78,21 @@ bool MultiCommander::PrepareMessageWindow() LRESULT CALLBACK MultiCommander::msgWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) - { - case WM_COPYDATA: - { + { + case WM_COPYDATA: + { delete[] pCurrentItemPath; - auto cds = reinterpret_cast(lParam); - auto buf = static_cast(cds->lpData); + auto cds = reinterpret_cast(lParam); + auto buf = static_cast(cds->lpData); - pCurrentItemPath = new CHAR[cds->cbData + 1]{ '\0' }; - memcpy(pCurrentItemPath, buf, cds->cbData); + pCurrentItemPath = new CHAR[cds->cbData + 1]{ '\0' }; + memcpy(pCurrentItemPath, buf, cds->cbData); SetEvent(hGetResultEvent); return 0; - } - default: + } + default: return DefWindowProc(hWnd, uMsg, wParam, lParam); - } + } } diff --git a/QuickLook.Native/QuickLook.Native32/MultiCommander.h b/QuickLook.Native/QuickLook.Native32/MultiCommander.h index 44074f3..0d2ae31 100644 --- a/QuickLook.Native/QuickLook.Native32/MultiCommander.h +++ b/QuickLook.Native/QuickLook.Native32/MultiCommander.h @@ -24,13 +24,13 @@ class MultiCommander { public: - static void GetSelected(PWCHAR buffer); + static void GetSelected(PWCHAR buffer); static bool PrepareMessageWindow(); MultiCommander() = delete; private: static HWND hMsgWnd; - static HANDLE hGetResultEvent; - static PCHAR pCurrentItemPath; + static HANDLE hGetResultEvent; + static PCHAR pCurrentItemPath; static LRESULT CALLBACK msgWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); }; diff --git a/QuickLook.Native/QuickLook.Native32/Shell32.cpp b/QuickLook.Native/QuickLook.Native32/Shell32.cpp index 7f0cf6c..ececd44 100644 --- a/QuickLook.Native/QuickLook.Native32/Shell32.cpp +++ b/QuickLook.Native/QuickLook.Native32/Shell32.cpp @@ -28,157 +28,157 @@ using namespace std; Shell32::FocusedWindowType Shell32::GetFocusedWindowType() { - auto hwndfg = GetForegroundWindow(); + auto hwndfg = GetForegroundWindow(); - if (HelperMethods::IsCursorActivated(hwndfg)) - return INVALID; + if (HelperMethods::IsCursorActivated(hwndfg)) + return INVALID; - WCHAR classBuffer[MAX_PATH] = { '\0' }; - if (FAILED(GetClassName(hwndfg, classBuffer, MAX_PATH))) - return INVALID; + WCHAR classBuffer[MAX_PATH] = { '\0' }; + if (FAILED(GetClassName(hwndfg, classBuffer, MAX_PATH))) + return INVALID; if (wcscmp(classBuffer, MULTICMD_CLASS) == 0) { return MULTICOMMANDER; } - if (wcscmp(classBuffer, L"dopus.lister") == 0) - { - return DOPUS; - } - if (Everything::MatchClass(classBuffer)) - { - return EVERYTHING; - } - if (wcscmp(classBuffer, L"WorkerW") == 0 || wcscmp(classBuffer, L"Progman") == 0) - { - if (FindWindowEx(hwndfg, nullptr, L"SHELLDLL_DefView", nullptr) != nullptr) - { - return DESKTOP; - } - } - if (wcscmp(classBuffer, L"ExploreWClass") == 0 || wcscmp(classBuffer, L"CabinetWClass") == 0) - { - if (!HelperMethods::IsExplorerSearchBoxFocused()) - { - return EXPLORER; - } - } - if (wcscmp(classBuffer, L"#32770") == 0) - { - if (FindWindowEx(hwndfg, nullptr, L"DUIViewWndClassName", nullptr) != nullptr) - { - if (!HelperMethods::IsExplorerSearchBoxFocused()) - { - return DIALOG; - } - } - } + if (wcscmp(classBuffer, L"dopus.lister") == 0) + { + return DOPUS; + } + if (Everything::MatchClass(classBuffer)) + { + return EVERYTHING; + } + if (wcscmp(classBuffer, L"WorkerW") == 0 || wcscmp(classBuffer, L"Progman") == 0) + { + if (FindWindowEx(hwndfg, nullptr, L"SHELLDLL_DefView", nullptr) != nullptr) + { + return DESKTOP; + } + } + if (wcscmp(classBuffer, L"ExploreWClass") == 0 || wcscmp(classBuffer, L"CabinetWClass") == 0) + { + if (!HelperMethods::IsExplorerSearchBoxFocused()) + { + return EXPLORER; + } + } + if (wcscmp(classBuffer, L"#32770") == 0) + { + if (FindWindowEx(hwndfg, nullptr, L"DUIViewWndClassName", nullptr) != nullptr) + { + if (!HelperMethods::IsExplorerSearchBoxFocused()) + { + return DIALOG; + } + } + } - return INVALID; + return INVALID; } void Shell32::GetCurrentSelection(PWCHAR buffer) { - switch (GetFocusedWindowType()) - { - case DESKTOP: - getSelectedFromDesktop(buffer); - break; - case EXPLORER: - getSelectedFromExplorer(buffer); - break; - case DIALOG: - DialogHook::GetSelected(buffer); - break; - case EVERYTHING: - Everything::GetSelected(buffer); - break; - case DOPUS: - DOpus::GetSelected(buffer); - break; + switch (GetFocusedWindowType()) + { + case DESKTOP: + getSelectedFromDesktop(buffer); + break; + case EXPLORER: + getSelectedFromExplorer(buffer); + break; + case DIALOG: + DialogHook::GetSelected(buffer); + break; + case EVERYTHING: + Everything::GetSelected(buffer); + break; + case DOPUS: + DOpus::GetSelected(buffer); + break; case MULTICOMMANDER: MultiCommander::GetSelected(buffer); break; - default: - break; - } + default: + break; + } } void Shell32::getSelectedFromExplorer(PWCHAR buffer) { - CoInitialize(nullptr); + CoInitialize(nullptr); - CComPtr psw; - if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows))) - return; + CComPtr psw; + if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows))) + return; - auto hwndfgw = GetForegroundWindow(); - auto hwndfgt = FindWindowEx(hwndfgw, nullptr, L"ShellTabWindowClass", nullptr); + auto hwndfgw = GetForegroundWindow(); + auto hwndfgt = FindWindowEx(hwndfgw, nullptr, L"ShellTabWindowClass", nullptr); - auto count = 0L; - psw->get_Count(&count); + auto count = 0L; + psw->get_Count(&count); - for (auto i = 0; i < count; i++) - { - VARIANT vi; - VariantInit(&vi); - V_VT(&vi) = VT_I4; - V_I4(&vi) = i; + for (auto i = 0; i < count; i++) + { + VARIANT vi; + VariantInit(&vi); + V_VT(&vi) = VT_I4; + V_I4(&vi) = i; - CComPtr pdisp; - if (S_OK != psw->Item(vi, &pdisp)) - continue; + CComPtr pdisp; + if (S_OK != psw->Item(vi, &pdisp)) + continue; - CComPtr psp; - if (FAILED(pdisp->QueryInterface(IID_IServiceProvider, reinterpret_cast(&psp)))) - continue; + CComPtr psp; + if (FAILED(pdisp->QueryInterface(IID_IServiceProvider, reinterpret_cast(&psp)))) + continue; - CComPtr psb; - if (FAILED(psp->QueryService(IID_IShellBrowser, IID_IShellBrowser, reinterpret_cast(&psb)))) - continue; + CComPtr psb; + if (FAILED(psp->QueryService(IID_IShellBrowser, IID_IShellBrowser, reinterpret_cast(&psb)))) + continue; - HWND phwnd; - if (FAILED(psb->GetWindow(&phwnd))) - continue; + HWND phwnd; + if (FAILED(psb->GetWindow(&phwnd))) + continue; - if (hwndfgw != phwnd && (hwndfgt != nullptr && hwndfgt != phwnd)) - continue; + if (hwndfgw != phwnd && (hwndfgt != nullptr && hwndfgt != phwnd)) + continue; - if (HelperMethods::IsCursorActivated(0)) - continue; + if (HelperMethods::IsCursorActivated(0)) + continue; - HelperMethods::GetSelectedInternal(psb, buffer); - return; - } + HelperMethods::GetSelectedInternal(psb, buffer); + return; + } } void Shell32::getSelectedFromDesktop(PWCHAR buffer) { - CoInitialize(nullptr); + CoInitialize(nullptr); - CComPtr psw; - CComPtr pwba; + CComPtr psw; + CComPtr pwba; - if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows))) - return; + if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows))) + return; - VARIANT pvarLoc; - VariantInit(&pvarLoc); - long phwnd; - if (FAILED(psw->FindWindowSW(&pvarLoc, &pvarLoc, SWC_DESKTOP, &phwnd, SWFO_NEEDDISPATCH, reinterpret_cast( - &pwba)))) - return; + VARIANT pvarLoc; + VariantInit(&pvarLoc); + long phwnd; + if (FAILED(psw->FindWindowSW(&pvarLoc, &pvarLoc, SWC_DESKTOP, &phwnd, SWFO_NEEDDISPATCH, reinterpret_cast( + &pwba)))) + return; - if (HelperMethods::IsCursorActivated(reinterpret_cast(LongToHandle(phwnd)))) - return; + if (HelperMethods::IsCursorActivated(reinterpret_cast(LongToHandle(phwnd)))) + return; - CComPtr psp; - if (FAILED(pwba->QueryInterface(IID_IServiceProvider, reinterpret_cast(&psp)))) - return; + CComPtr psp; + if (FAILED(pwba->QueryInterface(IID_IServiceProvider, reinterpret_cast(&psp)))) + return; - CComPtr psb; - if (FAILED(psp->QueryService(IID_IShellBrowser, IID_IShellBrowser, reinterpret_cast(&psb)))) - return; + CComPtr psb; + if (FAILED(psp->QueryService(IID_IShellBrowser, IID_IShellBrowser, reinterpret_cast(&psb)))) + return; - HelperMethods::GetSelectedInternal(psb, buffer); + HelperMethods::GetSelectedInternal(psb, buffer); } diff --git a/QuickLook.Native/QuickLook.Native32/Shell32.h b/QuickLook.Native/QuickLook.Native32/Shell32.h index 9a6a9e3..3685e3a 100644 --- a/QuickLook.Native/QuickLook.Native32/Shell32.h +++ b/QuickLook.Native/QuickLook.Native32/Shell32.h @@ -22,21 +22,21 @@ class Shell32 { public: - enum FocusedWindowType - { - INVALID, - DESKTOP, - EXPLORER, - DIALOG, - EVERYTHING, - DOPUS, + enum FocusedWindowType + { + INVALID, + DESKTOP, + EXPLORER, + DIALOG, + EVERYTHING, + DOPUS, MULTICOMMANDER, - }; + }; - static FocusedWindowType GetFocusedWindowType(); - static void GetCurrentSelection(PWCHAR buffer); + static FocusedWindowType GetFocusedWindowType(); + static void GetCurrentSelection(PWCHAR buffer); private: - static void getSelectedFromDesktop(PWCHAR buffer); - static void getSelectedFromExplorer(PWCHAR buffer); + static void getSelectedFromDesktop(PWCHAR buffer); + static void getSelectedFromExplorer(PWCHAR buffer); }; diff --git a/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.cpp b/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.cpp index 3febda6..6b2442b 100644 --- a/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.cpp +++ b/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.cpp @@ -27,54 +27,54 @@ HANDLE hJob = nullptr; bool WoW64HookHelper::CheckStatus() { - DWORD running = -1; - GetExitCodeProcess(hHelper, &running); - return running == STILL_ACTIVE; + DWORD running = -1; + GetExitCodeProcess(hHelper, &running); + return running == STILL_ACTIVE; } bool WoW64HookHelper::Launch() { #ifndef WIN64 - return true; + return true; #endif - if (HelperMethods::IsUWP()) - return true; + if (HelperMethods::IsUWP()) + return true; - if (CheckStatus()) - return true; + if (CheckStatus()) + return true; - createJob(); + createJob(); - WCHAR fullPath[MAX_PATH] = {'\0'}; - GetModuleFileName(nullptr, fullPath, MAX_PATH - 1); - auto p = wcsrchr(fullPath, L'\\'); - memcpy(p, HELPER_FILE, wcslen(HELPER_FILE) * sizeof WCHAR); + WCHAR fullPath[MAX_PATH] = {'\0'}; + GetModuleFileName(nullptr, fullPath, MAX_PATH - 1); + auto p = wcsrchr(fullPath, L'\\'); + memcpy(p, HELPER_FILE, wcslen(HELPER_FILE) * sizeof WCHAR); - STARTUPINFO si = {sizeof si}; - PROCESS_INFORMATION pi = {nullptr}; - si.cb = sizeof si; + STARTUPINFO si = {sizeof si}; + PROCESS_INFORMATION pi = {nullptr}; + si.cb = sizeof si; - CreateProcess(fullPath, RUN_ARG, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi); - hHelper = pi.hProcess; + CreateProcess(fullPath, RUN_ARG, nullptr, nullptr, false, 0, nullptr, nullptr, &si, &pi); + hHelper = pi.hProcess; - AssignProcessToJobObject(hJob, hHelper); + AssignProcessToJobObject(hJob, hHelper); - return CheckStatus(); + return CheckStatus(); } void WoW64HookHelper::createJob() { - if (hJob != nullptr) - return; + if (hJob != nullptr) + return; - hJob = CreateJobObject(nullptr, nullptr); + hJob = CreateJobObject(nullptr, nullptr); - JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation = {sizeof BasicLimitInformation}; - BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo = {sizeof lpJobObjectInfo}; - lpJobObjectInfo.BasicLimitInformation = BasicLimitInformation; + JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation = {sizeof BasicLimitInformation}; + BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION lpJobObjectInfo = {sizeof lpJobObjectInfo}; + lpJobObjectInfo.BasicLimitInformation = BasicLimitInformation; - SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &lpJobObjectInfo, - sizeof JOBOBJECT_EXTENDED_LIMIT_INFORMATION); + SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &lpJobObjectInfo, + sizeof JOBOBJECT_EXTENDED_LIMIT_INFORMATION); } diff --git a/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.h b/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.h index 2954748..b3df07c 100644 --- a/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.h +++ b/QuickLook.Native/QuickLook.Native32/WoW64HookHelper.h @@ -22,14 +22,14 @@ class WoW64HookHelper { public: - static PWCHAR GetMsgWindowClassName() - { - return L"QUICKLOOK_WOW64HOOKHELPER_MSG_CLASS"; - } + static PWCHAR GetMsgWindowClassName() + { + return L"QUICKLOOK_WOW64HOOKHELPER_MSG_CLASS"; + } - static bool CheckStatus(); - static bool Launch(); + static bool CheckStatus(); + static bool Launch(); private: - static void createJob(); + static void createJob(); }; diff --git a/QuickLook.Native/QuickLook.Native32/dllmain.cpp b/QuickLook.Native/QuickLook.Native32/dllmain.cpp index 689a185..fd32c0a 100644 --- a/QuickLook.Native/QuickLook.Native32/dllmain.cpp +++ b/QuickLook.Native/QuickLook.Native32/dllmain.cpp @@ -20,14 +20,14 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - default: - break; - } - return TRUE; + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + default: + break; + } + return TRUE; } diff --git a/QuickLook.Native/QuickLook.Native32/rapidxml.hpp b/QuickLook.Native/QuickLook.Native32/rapidxml.hpp index f98f70d..666bd64 100644 --- a/QuickLook.Native/QuickLook.Native32/rapidxml.hpp +++ b/QuickLook.Native/QuickLook.Native32/rapidxml.hpp @@ -56,49 +56,49 @@ namespace rapidxml namespace rapidxml { - //! Parse error exception. - //! This exception is thrown by the parser when an error occurs. - //! Use what() function to get human-readable error message. - //! Use where() function to get a pointer to position within source text where error was detected. - //!

- //! If throwing exceptions by the parser is undesirable, - //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. - //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. - //! This function must be defined by the user. - //!

- //! This class derives from std::exception class. - class parse_error : public std::exception - { - public: + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error : public std::exception + { + public: - //! Constructs parse error - parse_error(const char* what, void* where) - : m_what(what) - , m_where(where) - { - } + //! Constructs parse error + parse_error(const char* what, void* where) + : m_what(what) + , m_where(where) + { + } - //! Gets human readable description of error. - //! \return Pointer to null terminated description of the error. - const char* what() const throw() override - { - return m_what; - } + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + const char* what() const throw() override + { + return m_what; + } - //! Gets pointer to character data where error happened. - //! Ch should be the same as char type of xml_document that produced the error. - //! \return Pointer to location within the parsed string where error occured. - template - Ch* where() const - { - return reinterpret_cast(m_where); - } + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch* where() const + { + return reinterpret_cast(m_where); + } - private: + private: - const char* m_what; - void* m_where; - }; + const char* m_what; + void* m_where; + }; } #endif @@ -130,2449 +130,2449 @@ namespace rapidxml namespace rapidxml { - // Forward declarations - template - class xml_node; - template - class xml_attribute; - template - class xml_document; + // Forward declarations + template + class xml_node; + template + class xml_attribute; + template + class xml_document; - //! Enumeration listing all node types produced by the parser. - //! Use xml_node::type() function to query node type. - enum node_type - { - node_document, - //!< A document node. Name and value are empty. - node_element, - //!< An element node. Name contains element name. Value contains text of first data node. - node_data, - //!< A data node. Name is empty. Value contains data text. - node_cdata, - //!< A CDATA node. Name is empty. Value contains data text. - node_comment, - //!< A comment node. Name is empty. Value contains comment text. - node_declaration, - //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. - node_doctype, - //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. - node_pi //!< A PI node. Name contains target. Value contains instructions. - }; + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, + //!< A document node. Name and value are empty. + node_element, + //!< An element node. Name contains element name. Value contains text of first data node. + node_data, + //!< A data node. Name is empty. Value contains data text. + node_cdata, + //!< A CDATA node. Name is empty. Value contains data text. + node_comment, + //!< A comment node. Name is empty. Value contains comment text. + node_declaration, + //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, + //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; - /////////////////////////////////////////////////////////////////////// - // Parsing flags + /////////////////////////////////////////////////////////////////////// + // Parsing flags - //! Parse flag instructing the parser to not create data nodes. - //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_data_nodes = 0x1; + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; - //! Parse flag instructing the parser to not use text of first data node as a value of parent element. - //! Can be combined with other flags by use of | operator. - //! Note that child data nodes of element node take precendence over its value when printing. - //! That is, if element has one or more child data nodes and a value, the value will be ignored. - //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. - //!

- //! See xml_document::parse() function. - const int parse_no_element_values = 0x2; + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; - //! Parse flag instructing the parser to not place zero terminators after strings in the source text. - //! By default zero terminators are placed, modifying source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_string_terminators = 0x4; + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; - //! Parse flag instructing the parser to not translate entities in the source text. - //! By default entities are translated, modifying source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_entity_translation = 0x8; + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; - //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. - //! By default, UTF-8 handling is enabled. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_no_utf8 = 0x10; + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; - //! Parse flag instructing the parser to create XML declaration node. - //! By default, declaration node is not created. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_declaration_node = 0x20; + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; - //! Parse flag instructing the parser to create comments nodes. - //! By default, comment nodes are not created. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_comment_nodes = 0x40; + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; - //! Parse flag instructing the parser to create DOCTYPE node. - //! By default, doctype node is not created. - //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_doctype_node = 0x80; + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; - //! Parse flag instructing the parser to create PI nodes. - //! By default, PI nodes are not created. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_pi_nodes = 0x100; + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; - //! Parse flag instructing the parser to validate closing tag names. - //! If not set, name inside closing tag is irrelevant to the parser. - //! By default, closing tags are not validated. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_validate_closing_tags = 0x200; + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; - //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. - //! By default, whitespace is not trimmed. - //! This flag does not cause the parser to modify source text. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_trim_whitespace = 0x400; + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; - //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. - //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. - //! By default, whitespace is not normalized. - //! If this flag is specified, source text will be modified. - //! Can be combined with other flags by use of | operator. - //!

- //! See xml_document::parse() function. - const int parse_normalize_whitespace = 0x800; + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; - // Compound flags + // Compound flags - //! Parse flags which represent default behaviour of the parser. - //! This is always equal to 0, so that all other flags can be simply ored together. - //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. - //! This also means that meaning of each flag is a negation of the default setting. - //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, - //! and using the flag will disable it. - //!

- //! See xml_document::parse() function. - const int parse_default = 0; + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; - //! A combination of parse flags that forbids any modifications of the source text. - //! This also results in faster parsing. However, note that the following will occur: - //!
    - //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • - //!
  • entities will not be translated
  • - //!
  • whitespace will not be normalized
  • - //!
- //! See xml_document::parse() function. - const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; - //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. - //!

- //! See xml_document::parse() function. - const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; - //! A combination of parse flags resulting in largest amount of data being extracted. - //! This usually results in slowest parsing. - //!

- //! See xml_document::parse() function. - const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | - parse_validate_closing_tags; + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | + parse_validate_closing_tags; - /////////////////////////////////////////////////////////////////////// - // Internals + /////////////////////////////////////////////////////////////////////// + // Internals - //! \cond internal - namespace internal - { - // Struct that contains lookup tables for the parser - // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). - template - struct lookup_tables - { - static const unsigned char lookup_whitespace[256]; // Whitespace table - static const unsigned char lookup_node_name[256]; // Node name table - static const unsigned char lookup_text[256]; // Text table - static const unsigned char lookup_text_pure_no_ws[256]; // Text table - static const unsigned char lookup_text_pure_with_ws[256]; // Text table - static const unsigned char lookup_attribute_name[256]; // Attribute name table - static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote - static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote - static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes - static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes - static const unsigned char lookup_digits[256]; // Digits - static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters - }; + //! \cond internal + namespace internal + { + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; - // Find length of the string - template - std::size_t measure(const Ch* p) - { - const Ch* tmp = p; - while (*tmp) - ++tmp; - return tmp - p; - } + // Find length of the string + template + std::size_t measure(const Ch* p) + { + const Ch* tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } - // Compare strings for equality - template - bool compare(const Ch* p1, std::size_t size1, const Ch* p2, std::size_t size2, bool case_sensitive) - { - if (size1 != size2) - return false; - if (case_sensitive) - { - for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) - if (*p1 != *p2) - return false; - } - else - { - for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) - if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast - (*p2)]) - return false; - } - return true; - } - } + // Compare strings for equality + template + bool compare(const Ch* p1, std::size_t size1, const Ch* p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch* end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast + (*p2)]) + return false; + } + return true; + } + } - //! \endcond + //! \endcond - /////////////////////////////////////////////////////////////////////// - // Memory pool + /////////////////////////////////////////////////////////////////////// + // Memory pool - //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. - //! In most cases, you will not need to use this class directly. - //! However, if you need to create nodes manually or modify names/values of nodes, - //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. - //! Not only is this faster than allocating them by using new operator, - //! but also their lifetime will be tied to the lifetime of document, - //! possibly simplyfing memory management. - //!

- //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. - //! You can also call allocate_string() function to allocate strings. - //! Such strings can then be used as names or values of nodes without worrying about their lifetime. - //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, - //! or when the pool is destroyed. - //!

- //! It is also possible to create a standalone memory_pool, and use it - //! to allocate nodes, whose lifetime will not be tied to any document. - //!

- //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. - //! Until static memory is exhausted, no dynamic memory allocations are done. - //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, - //! by using global new[] and delete[] operators. - //! This behaviour can be changed by setting custom allocation routines. - //! Use set_allocator() function to set them. - //!

- //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. - //! This value defaults to the size of pointer on target architecture. - //!

- //! To obtain absolutely top performance from the parser, - //! it is important that all nodes are allocated from a single, contiguous block of memory. - //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. - //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT - //! to obtain best wasted memory to performance compromise. - //! To do it, define their values before rapidxml.hpp file is included. - //! \param Ch Character type of created nodes. - template - class memory_pool - { - public: + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + public: - //! \cond internal - typedef void*(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory - typedef void (free_func)(void*); // Type of user-defined function used to free memory - //! \endcond + //! \cond internal + typedef void*(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void*); // Type of user-defined function used to free memory + //! \endcond - //! Constructs empty pool with default allocator functions. - memory_pool() - : m_alloc_func(nullptr) - , m_free_func(nullptr) - { - init(); - } + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(nullptr) + , m_free_func(nullptr) + { + init(); + } - //! Destroys pool and frees all the memory. - //! This causes memory occupied by nodes allocated by the pool to be freed. - //! Nodes allocated from the pool are no longer valid. - ~memory_pool() - { - clear(); - } + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } - //! Allocates a new node from the pool, and optionally assigns name and value to it. - //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param type Type of node to create. - //! \param name Name to assign to the node, or 0 to assign no name. - //! \param value Value to assign to the node, or 0 to assign no value. - //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. - //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. - //! \return Pointer to allocated node. This pointer will never be NULL. - xml_node* allocate_node(node_type type, - const Ch* name = nullptr, const Ch* value = nullptr, - std::size_t name_size = 0, std::size_t value_size = 0) - { - void* memory = allocate_aligned(sizeof(xml_node)); - xml_node* node = new(memory) xml_node(type); - if (name) - { - if (name_size > 0) - node->name(name, name_size); - else - node->name(name); - } - if (value) - { - if (value_size > 0) - node->value(value, value_size); - else - node->value(value); - } - return node; - } + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node* allocate_node(node_type type, + const Ch* name = nullptr, const Ch* value = nullptr, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void* memory = allocate_aligned(sizeof(xml_node)); + xml_node* node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } - //! Allocates a new attribute from the pool, and optionally assigns name and value to it. - //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param name Name to assign to the attribute, or 0 to assign no name. - //! \param value Value to assign to the attribute, or 0 to assign no value. - //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. - //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. - //! \return Pointer to allocated attribute. This pointer will never be NULL. - xml_attribute* allocate_attribute(const Ch* name = nullptr, const Ch* value = nullptr, - std::size_t name_size = 0, std::size_t value_size = 0) - { - void* memory = allocate_aligned(sizeof(xml_attribute)); - xml_attribute* attribute = new(memory) xml_attribute; - if (name) - { - if (name_size > 0) - attribute->name(name, name_size); - else - attribute->name(name); - } - if (value) - { - if (value_size > 0) - attribute->value(value, value_size); - else - attribute->value(value); - } - return attribute; - } + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute* allocate_attribute(const Ch* name = nullptr, const Ch* value = nullptr, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void* memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute* attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } - //! Allocates a char array of given size from the pool, and optionally copies a given string to it. - //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. - //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function - //! will call rapidxml::parse_error_handler() function. - //! \param source String to initialize the allocated memory with, or 0 to not initialize it. - //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. - //! \return Pointer to allocated char array. This pointer will never be NULL. - Ch* allocate_string(const Ch* source = nullptr, std::size_t size = 0) - { - assert(source || size); // Either source or size (or both) must be specified - if (size == 0) - size = internal::measure(source) + 1; - Ch* result = static_cast(allocate_aligned(size * sizeof(Ch))); - if (source) - for (std::size_t i = 0; i < size; ++i) - result[i] = source[i]; - return result; - } + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch* allocate_string(const Ch* source = nullptr, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch* result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } - //! Clones an xml_node and its hierarchy of child nodes and attributes. - //! Nodes and attributes are allocated from this memory pool. - //! Names and values are not cloned, they are shared between the clone and the source. - //! Result node can be optionally specified as a second parameter, - //! in which case its contents will be replaced with cloned source node. - //! This is useful when you want to clone entire document. - //! \param source Node to clone. - //! \param result Node to put results in, or 0 to automatically allocate result node - //! \return Pointer to cloned node. This pointer will never be NULL. - xml_node* clone_node(const xml_node* source, xml_node* result = nullptr) - { - // Prepare result node - if (result) - { - result->remove_all_attributes(); - result->remove_all_nodes(); - result->type(source->type()); - } - else - result = allocate_node(source->type()); + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node* clone_node(const xml_node* source, xml_node* result = nullptr) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); - // Clone name and value - result->name(source->name(), source->name_size()); - result->value(source->value(), source->value_size()); + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); - // Clone child nodes and attributes - for (xml_node* child = source->first_node(); child; child = child->next_sibling()) - result->append_node(clone_node(child)); - for (xml_attribute* attr = source->first_attribute(); attr; attr = attr->next_attribute()) - result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + // Clone child nodes and attributes + for (xml_node* child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute* attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); - return result; - } + return result; + } - //! Clears the pool. - //! This causes memory occupied by nodes allocated by the pool to be freed. - //! Any nodes or strings allocated from the pool will no longer be valid. - void clear() - { - while (m_begin != m_static_memory) - { - char* previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; - if (m_free_func) - m_free_func(m_begin); - else - delete[] m_begin; - m_begin = previous_begin; - } - init(); - } + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char* previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } - //! Sets or resets the user-defined memory allocation functions for the pool. - //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. - //! Allocation function must not return invalid pointer on failure. It should either throw, - //! stop the program, or use longjmp() function to pass control to other place of program. - //! If it returns invalid pointer, results are undefined. - //!

- //! User defined allocation functions must have the following forms: - //!
- //!
void *allocate(std::size_t size); - //!
void free(void *pointer); - //!

- //! \param af Allocation function, or 0 to restore default function - //! \param ff Free function, or 0 to restore default function - void set_allocator(alloc_func* af, free_func* ff) - { - assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet - m_alloc_func = af; - m_free_func = ff; - } + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func* af, free_func* ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } - private: + private: - struct header - { - char* previous_begin; - }; + struct header + { + char* previous_begin; + }; - void init() - { - m_begin = m_static_memory; - m_ptr = align(m_begin); - m_end = m_static_memory + sizeof(m_static_memory); - } + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } - char* align(char* ptr) - { - std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - - 1)); - return ptr + alignment; - } + char* align(char* ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - + 1)); + return ptr + alignment; + } - char* allocate_raw(std::size_t size) - { - // Allocate - void* memory; - if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] - { - memory = m_alloc_func(size); - assert(memory); - // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp - } - else - { - memory = new char[size]; + char* allocate_raw(std::size_t size) + { + // Allocate + void* memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); + // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; #ifdef RAPIDXML_NO_EXCEPTIONS if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc RAPIDXML_PARSE_ERROR("out of memory", 0); #endif - } - return static_cast(memory); - } - - void* allocate_aligned(std::size_t size) - { - // Calculate aligned pointer - char* result = align(m_ptr); - - // If not enough memory left in current pool, allocate a new pool - if (result + size > m_end) - { - // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) - std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; - if (pool_size < size) - pool_size = size; - - // Allocate - std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; - // 2 alignments required in worst case: one for header, one for actual allocation - char* raw_memory = allocate_raw(alloc_size); - - // Setup new pool in allocated memory - char* pool = align(raw_memory); - header* new_header = reinterpret_cast
(pool); - new_header->previous_begin = m_begin; - m_begin = raw_memory; - m_ptr = pool + sizeof(header); - m_end = raw_memory + alloc_size; - - // Calculate aligned pointer again using new pool - result = align(m_ptr); - } - - // Update pool and return aligned pointer - m_ptr = result + size; - return result; - } - - char* m_begin; // Start of raw memory making up current pool - char* m_ptr; // First free byte in current pool - char* m_end; // One past last available byte in current pool - char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory - alloc_func* m_alloc_func; // Allocator function, or 0 if default is to be used - free_func* m_free_func; // Free function, or 0 if default is to be used - }; - - /////////////////////////////////////////////////////////////////////////// - // XML base - - //! Base class for xml_node and xml_attribute implementing common functions: - //! name(), name_size(), value(), value_size() and parent(). - //! \param Ch Character type to use - template - class xml_base - { - public: - - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - - // Construct a base with empty name, value and parent - xml_base() - : m_name(nullptr) - , m_value(nullptr) - , m_parent(nullptr) - { - } - - /////////////////////////////////////////////////////////////////////////// - // Node data access - - //! Gets name of the node. - //! Interpretation of name depends on type of node. - //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. - //!

- //! Use name_size() function to determine length of the name. - //! \return Name of node, or empty string if node has no name. - Ch* name() const - { - return m_name ? m_name : nullstr(); - } - - //! Gets size of node name, not including terminator character. - //! This function works correctly irrespective of whether name is or is not zero terminated. - //! \return Size of node name, in characters. - std::size_t name_size() const - { - return m_name ? m_name_size : 0; - } - - //! Gets value of node. - //! Interpretation of value depends on type of node. - //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. - //!

- //! Use value_size() function to determine length of the value. - //! \return Value of node, or empty string if node has no value. - Ch* value() const - { - return m_value ? m_value : nullstr(); - } - - //! Gets size of node value, not including terminator character. - //! This function works correctly irrespective of whether value is or is not zero terminated. - //! \return Size of node value, in characters. - std::size_t value_size() const - { - return m_value ? m_value_size : 0; - } - - /////////////////////////////////////////////////////////////////////////// - // Node modification - - //! Sets name of node to a non zero-terminated string. - //! See \ref ownership_of_strings. - //!

- //! Note that node does not own its name or value, it only stores a pointer to it. - //! It will not delete or otherwise free the pointer on destruction. - //! It is reponsibility of the user to properly manage lifetime of the string. - //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - - //! on destruction of the document the string will be automatically freed. - //!

- //! Size of name must be specified separately, because name does not have to be zero terminated. - //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). - //! \param name Name of node to set. Does not have to be zero terminated. - //! \param size Size of name, in characters. This does not include zero terminator, if one is present. - void name(const Ch* name, std::size_t size) - { - m_name = const_cast(name); - m_name_size = size; - } - - //! Sets name of node to a zero-terminated string. - //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). - //! \param name Name of node to set. Must be zero terminated. - void name(const Ch* name) - { - this->name(name, internal::measure(name)); - } - - //! Sets value of node to a non zero-terminated string. - //! See \ref ownership_of_strings. - //!

- //! Note that node does not own its name or value, it only stores a pointer to it. - //! It will not delete or otherwise free the pointer on destruction. - //! It is reponsibility of the user to properly manage lifetime of the string. - //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - - //! on destruction of the document the string will be automatically freed. - //!

- //! Size of value must be specified separately, because it does not have to be zero terminated. - //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). - //!

- //! If an element has a child node of type node_data, it will take precedence over element value when printing. - //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. - //! \param value value of node to set. Does not have to be zero terminated. - //! \param size Size of value, in characters. This does not include zero terminator, if one is present. - void value(const Ch* value, std::size_t size) - { - m_value = const_cast(value); - m_value_size = size; - } - - //! Sets value of node to a zero-terminated string. - //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). - //! \param value Vame of node to set. Must be zero terminated. - void value(const Ch* value) - { - this->value(value, internal::measure(value)); - } - - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - - //! Gets node parent. - //! \return Pointer to parent node, or 0 if there is no parent. - xml_node* parent() const - { - return m_parent; - } - - protected: - - // Return empty string - static Ch* nullstr() - { - static Ch zero = Ch('\0'); - return &zero; - } - - Ch* m_name; // Name of node, or 0 if no name - Ch* m_value; // Value of node, or 0 if no value - std::size_t m_name_size; // Length of node name, or undefined of no name - std::size_t m_value_size; // Length of node value, or undefined if no value - xml_node* m_parent; // Pointer to parent node, or 0 if none - }; - - //! Class representing attribute node of XML document. - //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). - //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. - //! Thus, this text must persist in memory for the lifetime of attribute. - //! \param Ch Character type to use. - template - class xml_attribute : public xml_base - { - friend class xml_node; - - public: - - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - - //! Constructs an empty attribute with the specified type. - //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. - xml_attribute() - { - } - - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - - //! Gets document of which attribute is a child. - //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. - xml_document* document() const - { - if (xml_node* node = this->parent()) - { - while (node->parent()) - node = node->parent(); - return node->type() == node_document ? static_cast *>(node) : nullptr; - } - return nullptr; - } - - //! Gets previous attribute, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute* previous_attribute(const Ch* name = nullptr, std::size_t name_size = 0, - bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute* attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return nullptr; - } - return this->m_parent ? m_prev_attribute : nullptr; - } - - //! Gets next attribute, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute* next_attribute(const Ch* name = nullptr, std::size_t name_size = 0, - bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute* attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return nullptr; - } - return this->m_parent ? m_next_attribute : nullptr; - } - - private: - - xml_attribute* m_prev_attribute; - // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero - xml_attribute* m_next_attribute; - // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero - }; - - /////////////////////////////////////////////////////////////////////////// - // XML node - - //! Class representing a node of XML document. - //! Each node may have associated name and value strings, which are available through name() and value() functions. - //! Interpretation of name and value depends on type of the node. - //! Type of node can be determined by using type() function. - //!

- //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. - //! Thus, this text must persist in the memory for the lifetime of node. - //! \param Ch Character type to use. - template - class xml_node : public xml_base - { - public: - - /////////////////////////////////////////////////////////////////////////// - // Construction & destruction - - //! Constructs an empty node with the specified type. - //! Consider using memory_pool of appropriate document to allocate nodes manually. - //! \param type Type of node to construct. - xml_node(node_type type) - : m_type(type) - , m_first_node(nullptr) - , m_first_attribute(nullptr) - { - } - - /////////////////////////////////////////////////////////////////////////// - // Node data access - - //! Gets type of node. - //! \return Type of node. - node_type type() const - { - return m_type; - } - - /////////////////////////////////////////////////////////////////////////// - // Related nodes access - - //! Gets document of which node is a child. - //! \return Pointer to document that contains this node, or 0 if there is no parent document. - xml_document* document() const - { - xml_node* node = const_cast *>(this); - while (node->parent()) - node = node->parent(); - return node->type() == node_document ? static_cast *>(node) : nullptr; - } - - //! Gets first child node, optionally matching node name. - //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found child, or 0 if not found. - xml_node* first_node(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node* child = m_first_node; child; child = child->next_sibling()) - if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) - return child; - return nullptr; - } - return m_first_node; - } - - //! Gets last child node, optionally matching node name. - //! Behaviour is undefined if node has no children. - //! Use first_node() to test if node has children. - //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found child, or 0 if not found. - xml_node* last_node(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(m_first_node); // Cannot query for last child if node has no children - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node* child = m_last_node; child; child = child->previous_sibling()) - if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) - return child; - return nullptr; - } - return m_last_node; - } - - //! Gets previous sibling node, optionally matching node name. - //! Behaviour is undefined if node has no parent. - //! Use parent() to test if node has a parent. - //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found sibling, or 0 if not found. - xml_node* previous_sibling(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(this->m_parent); // Cannot query for siblings if node has no parent - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node* sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) - if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) - return sibling; - return nullptr; - } - return m_prev_sibling; - } - - //! Gets next sibling node, optionally matching node name. - //! Behaviour is undefined if node has no parent. - //! Use parent() to test if node has a parent. - //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found sibling, or 0 if not found. - xml_node* next_sibling(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const - { - assert(this->m_parent); // Cannot query for siblings if node has no parent - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_node* sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) - if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) - return sibling; - return nullptr; - } - return m_next_sibling; - } - - //! Gets first attribute of node, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute* first_attribute(const Ch* name = nullptr, std::size_t name_size = 0, - bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute* attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return nullptr; - } - return m_first_attribute; - } - - //! Gets last attribute of node, optionally matching attribute name. - //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero - //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string - //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters - //! \return Pointer to found attribute, or 0 if not found. - xml_attribute* last_attribute(const Ch* name = nullptr, std::size_t name_size = 0, - bool case_sensitive = true) const - { - if (name) - { - if (name_size == 0) - name_size = internal::measure(name); - for (xml_attribute* attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) - if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) - return attribute; - return nullptr; - } - return m_first_attribute ? m_last_attribute : nullptr; - } - - /////////////////////////////////////////////////////////////////////////// - // Node modification - - //! Sets type of node. - //! \param type Type of node to set. - void type(node_type type) - { - m_type = type; - } - - /////////////////////////////////////////////////////////////////////////// - // Node manipulation - - //! Prepends a new child node. - //! The prepended child becomes the first child, and all existing children are moved one position back. - //! \param child Node to prepend. - void prepend_node(xml_node* child) - { - assert(child && !child->parent() && child->type() != node_document); - if (first_node()) - { - child->m_next_sibling = m_first_node; - m_first_node->m_prev_sibling = child; - } - else - { - child->m_next_sibling = 0; - m_last_node = child; - } - m_first_node = child; - child->m_parent = this; - child->m_prev_sibling = 0; - } - - //! Appends a new child node. - //! The appended child becomes the last child. - //! \param child Node to append. - void append_node(xml_node* child) - { - assert(child && !child->parent() && child->type() != node_document); - if (first_node()) - { - child->m_prev_sibling = m_last_node; - m_last_node->m_next_sibling = child; - } - else - { - child->m_prev_sibling = 0; - m_first_node = child; - } - m_last_node = child; - child->m_parent = this; - child->m_next_sibling = 0; - } - - //! Inserts a new child node at specified place inside the node. - //! All children after and including the specified node are moved one position back. - //! \param where Place where to insert the child, or 0 to insert at the back. - //! \param child Node to insert. - void insert_node(xml_node* where, xml_node* child) - { - assert(!where || where->parent() == this); - assert(child && !child->parent() && child->type() != node_document); - if (where == m_first_node) - prepend_node(child); - else if (where == nullptr) - append_node(child); - else - { - child->m_prev_sibling = where->m_prev_sibling; - child->m_next_sibling = where; - where->m_prev_sibling->m_next_sibling = child; - where->m_prev_sibling = child; - child->m_parent = this; - } - } - - //! Removes first child node. - //! If node has no children, behaviour is undefined. - //! Use first_node() to test if node has children. - void remove_first_node() - { - assert(first_node()); - xml_node* child = m_first_node; - m_first_node = child->m_next_sibling; - if (child->m_next_sibling) - child->m_next_sibling->m_prev_sibling = 0; - else - m_last_node = 0; - child->m_parent = 0; - } - - //! Removes last child of the node. - //! If node has no children, behaviour is undefined. - //! Use first_node() to test if node has children. - void remove_last_node() - { - assert(first_node()); - xml_node* child = m_last_node; - if (child->m_prev_sibling) - { - m_last_node = child->m_prev_sibling; - child->m_prev_sibling->m_next_sibling = 0; - } - else - m_first_node = 0; - child->m_parent = 0; - } - - //! Removes specified child from the node - // \param where Pointer to child to be removed. - void remove_node(xml_node* where) - { - assert(where && where->parent() == this); - assert(first_node()); - if (where == m_first_node) - remove_first_node(); - else if (where == m_last_node) - remove_last_node(); - else - { - where->m_prev_sibling->m_next_sibling = where->m_next_sibling; - where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; - where->m_parent = 0; - } - } - - //! Removes all child nodes (but not attributes). - void remove_all_nodes() - { - for (xml_node* node = first_node(); node; node = node->m_next_sibling) - node->m_parent = 0; - m_first_node = 0; - } - - //! Prepends a new attribute to the node. - //! \param attribute Attribute to prepend. - void prepend_attribute(xml_attribute* attribute) - { - assert(attribute && !attribute->parent()); - if (first_attribute()) - { - attribute->m_next_attribute = m_first_attribute; - m_first_attribute->m_prev_attribute = attribute; - } - else - { - attribute->m_next_attribute = 0; - m_last_attribute = attribute; - } - m_first_attribute = attribute; - attribute->m_parent = this; - attribute->m_prev_attribute = 0; - } - - //! Appends a new attribute to the node. - //! \param attribute Attribute to append. - void append_attribute(xml_attribute* attribute) - { - assert(attribute && !attribute->parent()); - if (first_attribute()) - { - attribute->m_prev_attribute = m_last_attribute; - m_last_attribute->m_next_attribute = attribute; - } - else - { - attribute->m_prev_attribute = 0; - m_first_attribute = attribute; - } - m_last_attribute = attribute; - attribute->m_parent = this; - attribute->m_next_attribute = 0; - } - - //! Inserts a new attribute at specified place inside the node. - //! All attributes after and including the specified attribute are moved one position back. - //! \param where Place where to insert the attribute, or 0 to insert at the back. - //! \param attribute Attribute to insert. - void insert_attribute(xml_attribute* where, xml_attribute* attribute) - { - assert(!where || where->parent() == this); - assert(attribute && !attribute->parent()); - if (where == m_first_attribute) - prepend_attribute(attribute); - else if (where == nullptr) - append_attribute(attribute); - else - { - attribute->m_prev_attribute = where->m_prev_attribute; - attribute->m_next_attribute = where; - where->m_prev_attribute->m_next_attribute = attribute; - where->m_prev_attribute = attribute; - attribute->m_parent = this; - } - } - - //! Removes first attribute of the node. - //! If node has no attributes, behaviour is undefined. - //! Use first_attribute() to test if node has attributes. - void remove_first_attribute() - { - assert(first_attribute()); - xml_attribute* attribute = m_first_attribute; - if (attribute->m_next_attribute) - { - attribute->m_next_attribute->m_prev_attribute = 0; - } - else - m_last_attribute = 0; - attribute->m_parent = 0; - m_first_attribute = attribute->m_next_attribute; - } - - //! Removes last attribute of the node. - //! If node has no attributes, behaviour is undefined. - //! Use first_attribute() to test if node has attributes. - void remove_last_attribute() - { - assert(first_attribute()); - xml_attribute* attribute = m_last_attribute; - if (attribute->m_prev_attribute) - { - attribute->m_prev_attribute->m_next_attribute = 0; - m_last_attribute = attribute->m_prev_attribute; - } - else - m_first_attribute = 0; - attribute->m_parent = 0; - } - - //! Removes specified attribute from node. - //! \param where Pointer to attribute to be removed. - void remove_attribute(xml_attribute* where) - { - assert(first_attribute() && where->parent() == this); - if (where == m_first_attribute) - remove_first_attribute(); - else if (where == m_last_attribute) - remove_last_attribute(); - else - { - where->m_prev_attribute->m_next_attribute = where->m_next_attribute; - where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; - where->m_parent = 0; - } - } - - //! Removes all attributes of node. - void remove_all_attributes() - { - for (xml_attribute* attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) - attribute->m_parent = 0; - m_first_attribute = 0; - } - - private: - - /////////////////////////////////////////////////////////////////////////// - // Restrictions - - // No copying - xml_node(const xml_node&); - void operator =(const xml_node&); - - /////////////////////////////////////////////////////////////////////////// - // Data members - - // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. - // This is required for maximum performance, as it allows the parser to omit initialization of - // unneded/redundant values. - // - // The rules are as follows: - // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively - // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage - // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage - - node_type m_type; // Type of node; always valid - xml_node* m_first_node; // Pointer to first child node, or 0 if none; always valid - xml_node* m_last_node; - // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero - xml_attribute* m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid - xml_attribute* m_last_attribute; - // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero - xml_node* m_prev_sibling; - // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero - xml_node* m_next_sibling; - // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero - }; - - /////////////////////////////////////////////////////////////////////////// - // XML document - - //! This class represents root of the DOM hierarchy. - //! It is also an xml_node and a memory_pool through public inheritance. - //! Use parse() function to build a DOM tree from a zero-terminated XML text string. - //! parse() function allocates memory for nodes and attributes by using functions of xml_document, - //! which are inherited from memory_pool. - //! To access root node of the document, use the document itself, as if it was an xml_node. - //! \param Ch Character type to use. - template - class xml_document : public xml_node, public memory_pool - { - public: - - //! Constructs empty XML document - xml_document() - : xml_node(node_document) - { - } - - //! Parses zero-terminated XML string according to given flags. - //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. - //! The string must persist for the lifetime of the document. - //! In case of error, rapidxml::parse_error exception will be thrown. - //!

- //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. - //! Make sure that data is zero-terminated. - //!

- //! Document can be parsed into multiple times. - //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. - //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. - template - void parse(Ch* text) - { - assert(text); - - // Remove current contents - this->remove_all_nodes(); - this->remove_all_attributes(); - - // Parse BOM, if any - parse_bom(text); - - // Parse children - while (true) - { - // Skip whitespace before node - skip(text); - if (*text == 0) - break; - - // Parse and append new child - if (*text == Ch('<')) - { - ++text; // Skip '<' - if (xml_node* node = parse_node(text)) - this->append_node(node); - } - else - RAPIDXML_PARSE_ERROR("expected <", text); - } - } - - //! Clears the document by deleting all nodes and clearing the memory pool. - //! All nodes owned by document pool are destroyed. - void clear() - { - this->remove_all_nodes(); - this->remove_all_attributes(); - memory_pool::clear(); - } - - private: - - /////////////////////////////////////////////////////////////////////// - // Internal character utility functions - - // Detect whitespace character - struct whitespace_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; - } - }; - - // Detect node name character - struct node_name_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; - } - }; - - // Detect attribute name character - struct attribute_name_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; - } - }; - - // Detect text character (PCDATA) - struct text_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; - } - }; - - // Detect text character (PCDATA) that does not require processing - struct text_pure_no_ws_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; - } - }; - - // Detect text character (PCDATA) that does not require processing - struct text_pure_with_ws_pred - { - static unsigned char test(Ch ch) - { - return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; - } - }; - - // Detect attribute value character - template - struct attribute_value_pred - { - static unsigned char test(Ch ch) - { - if (Quote == Ch('\'')) - return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; - if (Quote == Ch('\"')) - return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; - return 0; // Should never be executed, to avoid warnings on Comeau - } - }; - - // Detect attribute value character - template - struct attribute_value_pure_pred - { - static unsigned char test(Ch ch) - { - if (Quote == Ch('\'')) - return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; - if (Quote == Ch('\"')) - return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; - return 0; // Should never be executed, to avoid warnings on Comeau - } - }; - - // Insert coded character, using UTF8 or 8-bit ASCII - template - static void insert_coded_character(Ch*& text, unsigned long code) - { - if (Flags & parse_no_utf8) - { - // Insert 8-bit ASCII character - // Todo: possibly verify that code is less than 256 and use replacement char otherwise? - text[0] = static_cast(code); - text += 1; - } - else - { - // Insert UTF8 sequence - if (code < 0x80) // 1 byte sequence - { - text[0] = static_cast(code); - text += 1; - } - else if (code < 0x800) // 2 byte sequence - { - text[1] = static_cast((code | 0x80) & 0xBF); - code >>= 6; - text[0] = static_cast(code | 0xC0); - text += 2; - } - else if (code < 0x10000) // 3 byte sequence - { - text[2] = static_cast((code | 0x80) & 0xBF); - code >>= 6; - text[1] = static_cast((code | 0x80) & 0xBF); - code >>= 6; - text[0] = static_cast(code | 0xE0); - text += 3; - } - else if (code < 0x110000) // 4 byte sequence - { - text[3] = static_cast((code | 0x80) & 0xBF); - code >>= 6; - text[2] = static_cast((code | 0x80) & 0xBF); - code >>= 6; - text[1] = static_cast((code | 0x80) & 0xBF); - code >>= 6; - text[0] = static_cast(code | 0xF0); - text += 4; - } - else // Invalid, only codes up to 0x10FFFF are allowed in Unicode - { - RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); - } - } - } - - // Skip characters until predicate evaluates to true - template - static void skip(Ch*& text) - { - Ch* tmp = text; - while (StopPred::test(*tmp)) - ++tmp; - text = tmp; - } - - // Skip characters until predicate evaluates to true while doing the following: - // - replacing XML character entity references with proper characters (' & " < > &#...;) - // - condensing whitespace sequences to single space character - template - static Ch* skip_and_expand_character_refs(Ch*& text) - { - // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip - if (Flags & parse_no_entity_translation && - !(Flags & parse_normalize_whitespace) && - !(Flags & parse_trim_whitespace)) - { - skip(text); - return text; - } - - // Use simple skip until first modification is detected - skip(text); - - // Use translation skip - Ch* src = text; - Ch* dest = src; - while (StopPred::test(*src)) - { - // If entity translation is enabled - if (!(Flags & parse_no_entity_translation)) - { - // Test if replacement is needed - if (src[0] == Ch('&')) - { - switch (src[1]) - { - // & ' - case Ch('a'): - if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) - { - *dest = Ch('&'); - ++dest; - src += 5; - continue; - } - if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) - { - *dest = Ch('\''); - ++dest; - src += 6; - continue; - } - break; - - // " - case Ch('q'): - if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) - { - *dest = Ch('"'); - ++dest; - src += 6; - continue; - } - break; - - // > - case Ch('g'): - if (src[2] == Ch('t') && src[3] == Ch(';')) - { - *dest = Ch('>'); - ++dest; - src += 4; - continue; - } - break; - - // < - case Ch('l'): - if (src[2] == Ch('t') && src[3] == Ch(';')) - { - *dest = Ch('<'); - ++dest; - src += 4; - continue; - } - break; - - // &#...; - assumes ASCII - case Ch('#'): - if (src[2] == Ch('x')) - { - unsigned long code = 0; - src += 3; // Skip &#x - while (true) - { - unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; - if (digit == 0xFF) - break; - code = code * 16 + digit; - ++src; - } - insert_coded_character(dest, code); // Put character in output - } - else - { - unsigned long code = 0; - src += 2; // Skip &# - while (true) - { - unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; - if (digit == 0xFF) - break; - code = code * 10 + digit; - ++src; - } - insert_coded_character(dest, code); // Put character in output - } - if (*src == Ch(';')) - ++src; - else - RAPIDXML_PARSE_ERROR("expected ;", src); - continue; - - // Something else - default: - // Ignore, just copy '&' verbatim - break; - } - } - } - - // If whitespace condensing is enabled - if (Flags & parse_normalize_whitespace) - { - // Test if condensing is needed - if (whitespace_pred::test(*src)) - { - *dest = Ch(' '); - ++dest; // Put single space in dest - ++src; // Skip first whitespace char - // Skip remaining whitespace chars - while (whitespace_pred::test(*src)) - ++src; - continue; - } - } - - // No replacement, only copy character - *dest++ = *src++; - } - - // Return new end - text = src; - return dest; - } - - /////////////////////////////////////////////////////////////////////// - // Internal parsing functions - - // Parse BOM, if any - template - void parse_bom(Ch*& text) - { - // UTF-8? - if (static_cast(text[0]) == 0xEF && - static_cast(text[1]) == 0xBB && - static_cast(text[2]) == 0xBF) - { - text += 3; // Skup utf-8 bom - } - } - - // Parse XML declaration ( - xml_node* parse_xml_declaration(Ch*& text) - { - // If parsing of declaration is disabled - if (!(Flags & parse_declaration_node)) - { - // Skip until end of declaration - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - text += 2; // Skip '?>' - return nullptr; - } - - // Create declaration - xml_node* declaration = this->allocate_node(node_declaration); - - // Skip whitespace before attributes or ?> - skip(text); - - // Parse declaration attributes - parse_node_attributes(text, declaration); - - // Skip ?> - if (text[0] != Ch('?') || text[1] != Ch('>')) - RAPIDXML_PARSE_ERROR("expected ?>", text); - text += 2; - - return declaration; - } - - // Parse XML comment (' - return nullptr; // Do not produce comment node - } - - // Remember value start - Ch* value = text; - - // Skip until end of comment - while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - - // Create comment node - xml_node* comment = this->allocate_node(node_comment); - comment->value(value, text - value); - - // Place zero terminator after comment value - if (!(Flags & parse_no_string_terminators)) - *text = Ch('\0'); - - text += 3; // Skip '-->' - return comment; - } - - // Parse DOCTYPE - template - xml_node* parse_doctype(Ch*& text) - { - // Remember value start - Ch* value = text; - - // Skip to > - while (*text != Ch('>')) - { - // Determine character type - switch (*text) - { - // If '[' encountered, scan for matching ending ']' using naive algorithm with depth - // This works for all W3C test files except for 2 most wicked - case Ch('['): - { - ++text; // Skip '[' - int depth = 1; - while (depth > 0) - { - switch (*text) - { - case Ch('['): ++depth; - break; - case Ch(']'): --depth; - break; - case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); - } - ++text; - } - break; - } - - // Error on end of text - case Ch('\0'): - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - - // Other character, skip it - default: - ++text; - } - } - - // If DOCTYPE nodes enabled - if (Flags & parse_doctype_node) - { - // Create a new doctype node - xml_node* doctype = this->allocate_node(node_doctype); - doctype->value(value, text - value); - - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - *text = Ch('\0'); - - text += 1; // skip '>' - return doctype; - } - text += 1; // skip '>' - return nullptr; - } - - // Parse PI - template - xml_node* parse_pi(Ch*& text) - { - // If creation of PI nodes is enabled - if (Flags & parse_pi_nodes) - { - // Create pi node - xml_node* pi = this->allocate_node(node_pi); - - // Extract PI target name - Ch* name = text; - skip(text); - if (text == name) - RAPIDXML_PARSE_ERROR("expected PI target", text); - pi->name(name, text - name); - - // Skip whitespace between pi target and pi - skip(text); - - // Remember start of pi - Ch* value = text; - - // Skip to '?>' - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (*text == Ch('\0')) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - - // Set pi value (verbatim, no entity expansion or whitespace normalization) - pi->value(value, text - value); - - // Place zero terminator after name and value - if (!(Flags & parse_no_string_terminators)) - { - pi->name()[pi->name_size()] = Ch('\0'); - pi->value()[pi->value_size()] = Ch('\0'); - } - - text += 2; // Skip '?>' - return pi; - } - // Skip to '?>' - while (text[0] != Ch('?') || text[1] != Ch('>')) - { - if (*text == Ch('\0')) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - text += 2; // Skip '?>' - return nullptr; - } - - // Parse and append data - // Return character that ends data. - // This is necessary because this character might have been overwritten by a terminating 0 - template - Ch parse_and_append_data(xml_node* node, Ch*& text, Ch* contents_start) - { - // Backup to contents start if whitespace trimming is disabled - if (!(Flags & parse_trim_whitespace)) - text = contents_start; - - // Skip until end of data - Ch *value = text, *end; - if (Flags & parse_normalize_whitespace) - end = skip_and_expand_character_refs(text); - else - end = skip_and_expand_character_refs(text); - - // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > - if (Flags & parse_trim_whitespace) - { - if (Flags & parse_normalize_whitespace) - { - // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end - if (*(end - 1) == Ch(' ')) - --end; - } - else - { - // Backup until non-whitespace character is found - while (whitespace_pred::test(*(end - 1))) - --end; - } - } - - // If characters are still left between end and value (this test is only necessary if normalization is enabled) - // Create new data node - if (!(Flags & parse_no_data_nodes)) - { - xml_node* data = this->allocate_node(node_data); - data->value(value, end - value); - node->append_node(data); - } - - // Add data to parent node if no data exists yet - if (!(Flags & parse_no_element_values)) - if (*node->value() == Ch('\0')) - node->value(value, end - value); - - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - { - Ch ch = *text; - *end = Ch('\0'); - return ch; // Return character that ends data; this is required because zero terminator overwritten it - } - - // Return character that ends data - return *text; - } - - // Parse CDATA - template - xml_node* parse_cdata(Ch*& text) - { - // If CDATA is disabled - if (Flags & parse_no_data_nodes) - { - // Skip until end of cdata - while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - text += 3; // Skip ]]> - return nullptr; // Do not produce CDATA node - } - - // Skip until end of cdata - Ch* value = text; - while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) - { - if (!text[0]) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - - // Create new cdata node - xml_node* cdata = this->allocate_node(node_cdata); - cdata->value(value, text - value); - - // Place zero terminator after value - if (!(Flags & parse_no_string_terminators)) - *text = Ch('\0'); - - text += 3; // Skip ]]> - return cdata; - } - - // Parse element node - template - xml_node* parse_element(Ch*& text) - { - // Create element node - xml_node* element = this->allocate_node(node_element); - - // Extract element name - Ch* name = text; - skip(text); - if (text == name) - RAPIDXML_PARSE_ERROR("expected element name", text); - element->name(name, text - name); - - // Skip whitespace between element name and attributes or > - skip(text); - - // Parse attributes, if any - parse_node_attributes(text, element); - - // Determine ending type - if (*text == Ch('>')) - { - ++text; - parse_node_contents(text, element); - } - else if (*text == Ch('/')) - { - ++text; - if (*text != Ch('>')) - RAPIDXML_PARSE_ERROR("expected >", text); - ++text; - } - else - RAPIDXML_PARSE_ERROR("expected >", text); - - // Place zero terminator after name - if (!(Flags & parse_no_string_terminators)) - element->name()[element->name_size()] = Ch('\0'); - - // Return parsed element - return element; - } - - // Determine node type, and parse it - template - xml_node* parse_node(Ch*& text) - { - // Parse proper node type - switch (text[0]) - { - // <... - default: - // Parse and append element node - return parse_element(text); - - // (text); - } - // Parse PI - return parse_pi(text); - - // (text); - } - break; - - // (text); - } - break; - - // (text); - } - } // switch - - // Attempt to skip other, unrecognized node types starting with ')) - { - if (*text == 0) - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - ++text; - } - ++text; // Skip '>' - return nullptr; // No node recognized - } - } - - // Parse contents of the node - children, data etc. - template - void parse_node_contents(Ch*& text, xml_node* node) - { - // For all children and text - while (true) - { - // Skip whitespace between > and node contents - Ch* contents_start = text; // Store start of node contents before whitespace is skipped - skip(text); - Ch next_char = *text; - - // After data nodes, instead of continuing the loop, control jumps here. - // This is because zero termination inside parse_and_append_data() function - // would wreak havoc with the above code. - // Also, skipping whitespace after data nodes is unnecessary. - after_data_node: - - // Determine what comes next: node closing, child node, data node, or 0? - switch (next_char) - { - // Node closing or child node - case Ch('<'): - { - if (text[1] == Ch('/')) - { - // Node closing - text += 2; // Skip '(text); - if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) - RAPIDXML_PARSE_ERROR("invalid closing tag name", text); - } - else - { - // No validation, just skip name - skip(text); - } - // Skip remaining whitespace after node name - skip(text); - if (*text != Ch('>')) - RAPIDXML_PARSE_ERROR("expected >", text); - ++text; // Skip '>' - return; // Node closed, finished parsing contents - } - // Child node - ++text; // Skip '<' - if (xml_node* child = parse_node(text)) - node->append_node(child); - } - break; - - // End of data - error - case Ch('\0'): - RAPIDXML_PARSE_ERROR("unexpected end of data", text); - - // Data node - default: - next_char = parse_and_append_data(node, text, contents_start); - goto after_data_node; // Bypass regular processing after data nodes - } - } - } - - // Parse XML attributes of the node - template - void parse_node_attributes(Ch*& text, xml_node* node) - { - // For all attributes - while (attribute_name_pred::test(*text)) - { - // Extract attribute name - Ch* name = text; - ++text; // Skip first character of attribute name - skip(text); - if (text == name) - RAPIDXML_PARSE_ERROR("expected attribute name", name); - - // Create new attribute - xml_attribute* attribute = this->allocate_attribute(); - attribute->name(name, text - name); - node->append_attribute(attribute); - - // Skip whitespace after attribute name - skip(text); - - // Skip = - if (*text != Ch('=')) - RAPIDXML_PARSE_ERROR("expected =", text); - ++text; - - // Add terminating zero after name - if (!(Flags & parse_no_string_terminators)) - attribute->name()[attribute->name_size()] = 0; - - // Skip whitespace after = - skip(text); - - // Skip quote and remember if it was ' or " - Ch quote = *text; - if (quote != Ch('\'') && quote != Ch('"')) - RAPIDXML_PARSE_ERROR("expected ' or \"", text); - ++text; - - // Extract attribute value and expand char refs in it - Ch *value = text, *end; - const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes - if (quote == Ch('\'')) - end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags - >(text); - else - end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags - >(text); - - // Set attribute value - attribute->value(value, end - value); - - // Make sure that end quote is present - if (*text != quote) - RAPIDXML_PARSE_ERROR("expected ' or \"", text); - ++text; // Skip quote - - // Add terminating zero after value - if (!(Flags & parse_no_string_terminators)) - attribute->value()[attribute->value_size()] = 0; - - // Skip whitespace after attribute value - skip(text); - } - } - }; - - //! \cond internal - namespace internal - { - // Whitespace (space \n \r \t) - template - const unsigned char lookup_tables::lookup_whitespace[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F - }; - - // Node name (anything but space \n \r \t / > ? \0) - template - const unsigned char lookup_tables::lookup_node_name[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Text (i.e. PCDATA) (anything but < \0) - template - const unsigned char lookup_tables::lookup_text[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled - // (anything but < \0 &) - template - const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled - // (anything but < \0 & space \n \r \t) - template - const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute name (anything but space \n \r \t / < > = ? ! \0) - template - const unsigned char lookup_tables::lookup_attribute_name[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with single quote (anything but ' \0) - template - const unsigned char lookup_tables::lookup_attribute_data_1[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with single quote that does not require processing (anything but ' \0 &) - template - const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with double quote (anything but " \0) - template - const unsigned char lookup_tables::lookup_attribute_data_2[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Attribute data with double quote that does not require processing (anything but " \0 &) - template - const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 - 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F - }; - - // Digits (dec and hex, 255 denotes end of numeric character reference) - template - const unsigned char lookup_tables::lookup_digits[256] = - { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 1 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 2 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, // 3 - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 4 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 5 - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 6 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 7 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 8 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 9 - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // A - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // B - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // C - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // D - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // E - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 // F - }; - - // Upper case conversion - template - const unsigned char lookup_tables::lookup_upcase[256] = - { - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 - 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, // 7 - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 8 - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 9 - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // A - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // B - 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // C - 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // D - 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // E - 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // F - }; - } - - //! \endcond + } + return static_cast(memory); + } + + void* allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char* result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; + // 2 alignments required in worst case: one for header, one for actual allocation + char* raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char* pool = align(raw_memory); + header* new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char* m_begin; // Start of raw memory making up current pool + char* m_ptr; // First free byte in current pool + char* m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func* m_alloc_func; // Allocator function, or 0 if default is to be used + free_func* m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(nullptr) + , m_value(nullptr) + , m_parent(nullptr) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch* name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch* value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch* name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch* name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch* value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch* value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node* parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch* nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch* m_name; // Name of node, or 0 if no name + Ch* m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node* m_parent; // Pointer to parent node, or 0 if none + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute : public xml_base + { + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document* document() const + { + if (xml_node* node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : nullptr; + } + return nullptr; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute* previous_attribute(const Ch* name = nullptr, std::size_t name_size = 0, + bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute* attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return nullptr; + } + return this->m_parent ? m_prev_attribute : nullptr; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute* next_attribute(const Ch* name = nullptr, std::size_t name_size = 0, + bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute* attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return nullptr; + } + return this->m_parent ? m_next_attribute : nullptr; + } + + private: + + xml_attribute* m_prev_attribute; + // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute* m_next_attribute; + // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node : public xml_base + { + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(nullptr) + , m_first_attribute(nullptr) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document* document() const + { + xml_node* node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : nullptr; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node* first_node(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node* child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return nullptr; + } + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node* last_node(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node* child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return nullptr; + } + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node* previous_sibling(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node* sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return nullptr; + } + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node* next_sibling(const Ch* name = nullptr, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node* sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return nullptr; + } + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute* first_attribute(const Ch* name = nullptr, std::size_t name_size = 0, + bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute* attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return nullptr; + } + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute* last_attribute(const Ch* name = nullptr, std::size_t name_size = 0, + bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute* attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return nullptr; + } + return m_first_attribute ? m_last_attribute : nullptr; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node* child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node* child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node* where, xml_node* child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == nullptr) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node* child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node* child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node* where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node* node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute* attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute* attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute* where, xml_attribute* attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == nullptr) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute* attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute* attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute* where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute* attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node&); + void operator =(const xml_node&); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node* m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node* m_last_node; + // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute* m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute* m_last_attribute; + // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node* m_prev_sibling; + // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node* m_next_sibling; + // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document : public xml_node, public memory_pool + { + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch* text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (true) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node* node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch*& text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); + code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch*& text) + { + Ch* tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch* skip_and_expand_character_refs(Ch*& text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch* src = text; + Ch* dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (true) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (true) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); + ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + } + + // Return new end + text = src; + return dest; + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch*& text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node* parse_xml_declaration(Ch*& text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return nullptr; + } + + // Create declaration + xml_node* declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return nullptr; // Do not produce comment node + } + + // Remember value start + Ch* value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node* comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node* parse_doctype(Ch*& text) + { + // Remember value start + Ch* value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; + break; + case Ch(']'): --depth; + break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node* doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + text += 1; // skip '>' + return nullptr; + } + + // Parse PI + template + xml_node* parse_pi(Ch*& text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node* pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch* name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch* value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return nullptr; + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node* node, Ch*& text, Ch* contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node* data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node* parse_cdata(Ch*& text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return nullptr; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch* value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node* cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node* parse_element(Ch*& text) + { + // Create element node + xml_node* element = this->allocate_node(node_element); + + // Extract element name + Ch* name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node* parse_node(Ch*& text) + { + // Parse proper node type + switch (text[0]) + { + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + // Parse PI + return parse_pi(text); + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return nullptr; // No node recognized + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch*& text, xml_node* node) + { + // For all children and text + while (true) + { + // Skip whitespace between > and node contents + Ch* contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + // Node closing or child node + case Ch('<'): + { + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + // Child node + ++text; // Skip '<' + if (xml_node* child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch*& text, xml_node* node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch* name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute* attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags + >(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags + >(text); + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + }; + + //! \cond internal + namespace internal + { + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 1 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, // 3 + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 4 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 5 + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 6 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 7 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 8 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 9 + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // A + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // B + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // C + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // D + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // E + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, // 7 + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 8 + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 9 + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // A + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // B + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // C + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // D + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // E + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // F + }; + } + + //! \endcond } // Undefine internal macros diff --git a/QuickLook.Native/QuickLook.WoW64HookHelper/QuickLook.WoW64HookHelper.cpp b/QuickLook.Native/QuickLook.WoW64HookHelper/QuickLook.WoW64HookHelper.cpp index 4484097..ee1d241 100644 --- a/QuickLook.Native/QuickLook.WoW64HookHelper/QuickLook.WoW64HookHelper.cpp +++ b/QuickLook.Native/QuickLook.WoW64HookHelper/QuickLook.WoW64HookHelper.cpp @@ -38,67 +38,67 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { - // do not run when double-clicking - if (wcsstr(GetCommandLine(), RUN_ARG) == nullptr) - { - MessageBox(nullptr, L"This executable is not designed to launch directly.", L"QuickLook.WoW64HookHelper", 0); - return 0; - } + // do not run when double-clicking + if (wcsstr(GetCommandLine(), RUN_ARG) == nullptr) + { + MessageBox(nullptr, L"This executable is not designed to launch directly.", L"QuickLook.WoW64HookHelper", 0); + return 0; + } - pDll = LoadLibrary(L"QuickLook.Native32.dll"); - pGCS = reinterpret_cast(GetProcAddress(pDll, "GetCurrentSelection")); + pDll = LoadLibrary(L"QuickLook.Native32.dll"); + pGCS = reinterpret_cast(GetProcAddress(pDll, "GetCurrentSelection")); - WM_HOOK_NOTIFY = RegisterWindowMessage(L"WM_QUICKLOOK_HOOK_NOTIFY_MSG"); + WM_HOOK_NOTIFY = RegisterWindowMessage(L"WM_QUICKLOOK_HOOK_NOTIFY_MSG"); - WNDCLASS wc = {}; - wc.lpfnWndProc = WndProc; - wc.lpszClassName = MSG_WINDOW_CLASS; - if (!RegisterClass(&wc)) - return 0; + WNDCLASS wc = {}; + wc.lpfnWndProc = WndProc; + wc.lpszClassName = MSG_WINDOW_CLASS; + if (!RegisterClass(&wc)) + return 0; - hMsgWindow = CreateWindow(MSG_WINDOW_CLASS, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); - if (hMsgWindow == nullptr) - return 0; + hMsgWindow = CreateWindow(MSG_WINDOW_CLASS, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); + if (hMsgWindow == nullptr) + return 0; - MSG msg; - while (GetMessage(&msg, nullptr, 0, 0) > 0) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return msg.wParam; + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (msg == WM_HOOK_NOTIFY) - { - GetCurrentSelection(); - return 0; - } - return DefWindowProc(hwnd, msg, wParam, lParam); + if (msg == WM_HOOK_NOTIFY) + { + GetCurrentSelection(); + return 0; + } + return DefWindowProc(hwnd, msg, wParam, lParam); } void GetCurrentSelection() { - // This function runs inside the target process. Some of them may already support Long Path. - // Therefore, we must assume all of them support Long Path to avoid buffer overflow. - WCHAR dllBuffer[MAX_PATH_EX] = {'\0'}; - pGCS(dllBuffer); + // This function runs inside the target process. Some of them may already support Long Path. + // Therefore, we must assume all of them support Long Path to avoid buffer overflow. + WCHAR dllBuffer[MAX_PATH_EX] = {'\0'}; + pGCS(dllBuffer); - auto hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, SHARED_MEM_NAME); - if (hMapFile == nullptr) - return; + auto hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, SHARED_MEM_NAME); + if (hMapFile == nullptr) + return; - auto buffer = static_cast(MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 0)); - if (buffer == nullptr) - { - CloseHandle(hMapFile); - return; - } + auto buffer = static_cast(MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 0)); + if (buffer == nullptr) + { + CloseHandle(hMapFile); + return; + } - wcscpy_s(buffer, wcslen(dllBuffer) + 1, dllBuffer); + wcscpy_s(buffer, wcslen(dllBuffer) + 1, dllBuffer); - UnmapViewOfFile(buffer); - CloseHandle(hMapFile); + UnmapViewOfFile(buffer); + CloseHandle(hMapFile); } diff --git a/QuickLook/FocusMonitor.cs b/QuickLook/FocusMonitor.cs index 0fa2070..e59dd36 100644 --- a/QuickLook/FocusMonitor.cs +++ b/QuickLook/FocusMonitor.cs @@ -62,6 +62,6 @@ internal class FocusMonitor internal static FocusMonitor GetInstance() { - return _instance ?? (_instance = new FocusMonitor()); + return _instance ??= new FocusMonitor(); } } diff --git a/QuickLook/KeystrokeDispatcher.cs b/QuickLook/KeystrokeDispatcher.cs index 4a112ed..8644930 100644 --- a/QuickLook/KeystrokeDispatcher.cs +++ b/QuickLook/KeystrokeDispatcher.cs @@ -43,11 +43,11 @@ internal class KeystrokeDispatcher : IDisposable { InstallKeyHook(KeyDownEventHandler, KeyUpEventHandler); - _validKeys = new HashSet( + _validKeys = [ Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Enter, Keys.Space, Keys.Escape - ]); + ]; } public void Dispose() diff --git a/QuickLook/NativeMethods/QuickLook.cs b/QuickLook/NativeMethods/QuickLook.cs index 556372a..fcd19ef 100644 --- a/QuickLook/NativeMethods/QuickLook.cs +++ b/QuickLook/NativeMethods/QuickLook.cs @@ -83,7 +83,7 @@ internal static class QuickLook internal static string GetCurrentSelection() { - StringBuilder sb = new StringBuilder(MaxPath); + StringBuilder sb = new(MaxPath); // communicate with COM in a separate STA thread var thread = new Thread(() => { @@ -107,7 +107,7 @@ internal static class QuickLook // We got a quoted string which breaks ResolveShortcut sb = sb.Replace("\"", string.Empty, 0, 1).Replace("\"", string.Empty, sb.Length - 1, 1); } - return ResolveShortcut(sb?.ToString() ?? String.Empty); + return ResolveShortcut(sb?.ToString() ?? string.Empty); } private static string ResolveShortcut(string path)