mirror of
https://github.com/reactos/reactos.git
synced 2024-11-03 21:34:00 +00:00
34158fc46f
* Initial implementation of the explorer command line parser. Not used by explorer-new, yet. svn path=/branches/shell-experiments/; revision=64081
417 lines
12 KiB
C++
417 lines
12 KiB
C++
/*
|
|
* ReactOS browseui
|
|
*
|
|
* Copyright 2014 David Quintana <gigaherz@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include <strsafe.h>
|
|
|
|
extern "C"
|
|
BOOL WINAPI GUIDFromStringW(
|
|
_In_ PCWSTR psz,
|
|
_Out_ LPGUID pguid
|
|
);
|
|
|
|
static BOOL _CopyAndUnquoteText(LPCWSTR strFieldSource, LPWSTR strField, size_t cchField)
|
|
{
|
|
WCHAR cChar;
|
|
PWSTR tmpField = strField;
|
|
size_t lenField = 1;
|
|
BOOL inQuote = FALSE;
|
|
|
|
// Remove leading whitespace
|
|
cChar = *strFieldSource;
|
|
while (cChar == L' ' || cChar == L'\t' || cChar == L'\n' || cChar == L'\r')
|
|
{
|
|
strFieldSource = CharNextW(strFieldSource);
|
|
cChar = *strFieldSource;
|
|
}
|
|
|
|
while (cChar && cChar != L'=' && cChar != L',')
|
|
{
|
|
if (cChar == L'"')
|
|
{
|
|
// [1] is always valid read because of null-termination
|
|
if (inQuote && strFieldSource[1] == L'"')
|
|
{
|
|
if (lenField < cchField)
|
|
{
|
|
// Append
|
|
*(tmpField++) = L'"';
|
|
++lenField;
|
|
}
|
|
|
|
// Skip second quote
|
|
strFieldSource++;
|
|
}
|
|
else
|
|
{
|
|
inQuote = !inQuote;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (inQuote || (cChar != L'=' && cChar != L','))
|
|
{
|
|
if (lenField < cchField)
|
|
{
|
|
// Append
|
|
*(tmpField++) = cChar;
|
|
++lenField;
|
|
}
|
|
}
|
|
}
|
|
|
|
strFieldSource = CharNextW(strFieldSource);
|
|
cChar = *strFieldSource;
|
|
}
|
|
|
|
// Remove trailing whitespace
|
|
while (tmpField > strField)
|
|
{
|
|
tmpField = CharPrevW(strField, tmpField);
|
|
cChar = *tmpField;
|
|
if (cChar != L' ' && cChar != L'\t' && cChar != L'\n' && cChar != L'\r')
|
|
{
|
|
tmpField = CharNextW(tmpField);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Terminate output string
|
|
*tmpField = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL _FindNextArg(PCWSTR * pstrFieldSource)
|
|
{
|
|
PCWSTR strFieldSource = *pstrFieldSource;
|
|
WCHAR cChar = *strFieldSource;
|
|
BOOL inQuote = FALSE;
|
|
|
|
while (cChar)
|
|
{
|
|
if (!inQuote && (cChar == L'=' || cChar == L','))
|
|
break;
|
|
|
|
if (cChar == L'"')
|
|
inQuote = !inQuote;
|
|
|
|
strFieldSource = CharNextW(strFieldSource);
|
|
cChar = *strFieldSource;
|
|
}
|
|
|
|
if (cChar == 0)
|
|
{
|
|
*pstrFieldSource = strFieldSource;
|
|
return FALSE;
|
|
}
|
|
|
|
*pstrFieldSource = CharNextW(strFieldSource);
|
|
return TRUE;
|
|
}
|
|
|
|
static PCWSTR _FindFirstField(PCWSTR strFieldSource)
|
|
{
|
|
//Find end of first arg, because
|
|
// behaviour is different if the first separator is an '='
|
|
BOOL inQuote = FALSE;
|
|
PCWSTR tmpArgs = strFieldSource;
|
|
WCHAR cChar = *tmpArgs;
|
|
while (cChar)
|
|
{
|
|
if (cChar == L'=')
|
|
break;
|
|
|
|
if (cChar == L',')
|
|
break;
|
|
|
|
if (cChar == L'\"')
|
|
inQuote = !inQuote;
|
|
|
|
tmpArgs = CharNextW(tmpArgs);
|
|
cChar = *tmpArgs;
|
|
}
|
|
|
|
// Skip the text before the first equal sign, if not quoted, unless the arg 0 was requested.
|
|
if (*tmpArgs == L'=' && !inQuote)
|
|
{
|
|
strFieldSource = ++tmpArgs;
|
|
TRACE("Skipped content before the first '=', remainder=%S\n", strFieldSource);
|
|
}
|
|
|
|
return strFieldSource;
|
|
}
|
|
|
|
static BOOL _ReadNextArg(PCWSTR * pstrFieldSource, PWSTR strField, size_t cchField)
|
|
{
|
|
// Copy and unquote text
|
|
_CopyAndUnquoteText(*pstrFieldSource, strField, cchField);
|
|
|
|
return _FindNextArg(pstrFieldSource);
|
|
}
|
|
|
|
static LPITEMIDLIST _ILReadFromSharedMemory(PCWSTR strField)
|
|
{
|
|
LPITEMIDLIST ret = NULL;
|
|
|
|
// Ensure it really is an IDLIST-formatted parameter
|
|
// Format for IDLIST params: ":pid:shared"
|
|
if (*strField != L':')
|
|
return NULL;
|
|
|
|
HANDLE hData = (HANDLE) StrToIntW(strField + 1);
|
|
PWSTR strSecond = StrChrW(strField + 1, L':');
|
|
|
|
if (strSecond)
|
|
{
|
|
int pid = StrToIntW(strSecond + 1);
|
|
void* pvShared = SHLockShared(hData, pid);
|
|
if (pvShared)
|
|
{
|
|
ret = ILClone((LPCITEMIDLIST) pvShared);
|
|
SHUnlockShared(pvShared);
|
|
SHFreeShared(hData, pid);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT _ParsePathToPidl(PWSTR strPath, LPITEMIDLIST * pidl)
|
|
{
|
|
CComPtr<IShellFolder> psfDesktop;
|
|
|
|
HRESULT hr = SHGetDesktopFolder(&psfDesktop);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
return psfDesktop->ParseDisplayName(NULL, NULL, strPath, NULL, pidl, NULL);
|
|
}
|
|
|
|
static LPITEMIDLIST _GetDocumentsPidl()
|
|
{
|
|
CComPtr<IShellFolder> ppshf;
|
|
LPITEMIDLIST pidl;
|
|
WCHAR guid [] = L"::{450d8fba-ad25-11d0-98a8-0800361b1103}";
|
|
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_MYDOCUMENTS, &pidl)))
|
|
return pidl;
|
|
|
|
if (FAILED(SHGetDesktopFolder(&ppshf)))
|
|
return NULL;
|
|
|
|
if (FAILED(ppshf->ParseDisplayName(NULL, NULL, guid, NULL, &pidl, NULL)))
|
|
return NULL;
|
|
|
|
return pidl;
|
|
}
|
|
|
|
/*************************************************************************
|
|
* SHExplorerParseCmdLine [BROWSEUI.107]
|
|
*/
|
|
extern "C"
|
|
UINT
|
|
WINAPI
|
|
SHExplorerParseCmdLine(ExplorerCommandLineParseResults * pInfo)
|
|
{
|
|
WCHAR strField[MAX_PATH];
|
|
WCHAR strDir[MAX_PATH];
|
|
|
|
PCWSTR strCmdLine = GetCommandLineW();
|
|
PCWSTR strFieldArray = PathGetArgsW(strCmdLine);
|
|
|
|
if (!*strFieldArray)
|
|
{
|
|
pInfo->dwFlags = 9;
|
|
pInfo->pidlPath = _GetDocumentsPidl();
|
|
if (!pInfo->pidlPath)
|
|
{
|
|
GetWindowsDirectoryW(strDir, MAX_PATH);
|
|
PathStripToRootW(strDir);
|
|
pInfo->pidlPath = ILCreateFromPathW(strDir);
|
|
}
|
|
return (LONG) (pInfo->pidlPath);
|
|
}
|
|
|
|
PCWSTR strNextArg = _FindFirstField(strFieldArray);
|
|
|
|
BOOL hasNext = TRUE;
|
|
|
|
hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
|
|
while (TRUE)
|
|
{
|
|
// Basic flags-only params first
|
|
if (!StrCmpIW(strField, L"/N"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_N | SH_EXPLORER_CMDLINE_FLAG_ONE;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/S"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_S;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/E"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_E;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/SELECT"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_SELECT;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/NOUI"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_NOUI;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"-embedding"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_EMBED;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/SEPARATE"))
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_SEPARATE;
|
|
TRACE("CmdLine Parser: Parsed %S flag. dwFlags=%08lx\n", strField, pInfo->dwFlags);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/INPROC"))
|
|
{
|
|
// No idea what Inproc is supposed to do, but it gets a GUID, and parses it.
|
|
|
|
TRACE("CmdLine Parser: Found %S flag\n", strField);
|
|
|
|
if (!hasNext)
|
|
return FALSE;
|
|
|
|
hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
|
|
|
|
if (!GUIDFromStringW(strField, &(pInfo->guidInproc)))
|
|
return FALSE;
|
|
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_INPROC;
|
|
|
|
TRACE("CmdLine Parser: Parsed /INPROC flag. dwFlags=%08lx, guidInproc=%S\n", pInfo->dwFlags, strField);
|
|
}
|
|
else if (!StrCmpIW(strField, L"/ROOT"))
|
|
{
|
|
LPITEMIDLIST pidlRoot = NULL;
|
|
|
|
// The window should be rooted
|
|
|
|
TRACE("CmdLine Parser: Found %S flag\n", strField);
|
|
|
|
if (!pInfo->pidlPath)
|
|
return FALSE;
|
|
|
|
if (!hasNext)
|
|
return FALSE;
|
|
|
|
hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
|
|
|
|
// Root may be a pidl
|
|
if (!StrCmpIW(strField, L"/IDLIST"))
|
|
{
|
|
if (hasNext)
|
|
{
|
|
hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
|
|
}
|
|
pidlRoot = _ILReadFromSharedMemory(strField);
|
|
}
|
|
else
|
|
{
|
|
// Or just a path string
|
|
_ParsePathToPidl(strField, &pidlRoot);
|
|
}
|
|
|
|
pInfo->pidlRoot = pidlRoot;
|
|
|
|
// The root defaults to the desktop
|
|
if (!pidlRoot)
|
|
{
|
|
if (FAILED(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &(pInfo->pidlRoot))))
|
|
pInfo->pidlRoot = NULL;
|
|
}
|
|
|
|
// TODO: Create rooted PIDL from pInfo->pidlPath and pInfo->pidlRoot
|
|
|
|
TRACE("CmdLine Parser: Parsed /ROOT flag. dwFlags=%08lx, pidlRoot=%p\n", pInfo->dwFlags, pInfo->pidlRoot);
|
|
}
|
|
else
|
|
{
|
|
// Anything else is part of the target path to browse to
|
|
TRACE("CmdLine Parser: Found target path %S\n", strField);
|
|
|
|
// Which can be a shared-memory itemidlist
|
|
if (!StrCmpIW(strField, L"/IDLIST"))
|
|
{
|
|
LPITEMIDLIST pidlArg;
|
|
|
|
if (!hasNext)
|
|
return FALSE;
|
|
|
|
hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
|
|
pidlArg = _ILReadFromSharedMemory(strField);
|
|
if (!pidlArg)
|
|
return FALSE;
|
|
|
|
if (pInfo->pidlPath)
|
|
ILFree(pInfo->pidlPath);
|
|
pInfo->pidlPath = pidlArg;
|
|
|
|
TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, pidlPath=%p\n", pInfo->dwFlags, pInfo->pidlPath);
|
|
}
|
|
else
|
|
{
|
|
// Or just a plain old string.
|
|
|
|
LPITEMIDLIST pidlPath = ILCreateFromPathW(strField);
|
|
|
|
pInfo->pidlPath = pidlPath;
|
|
|
|
if (pidlPath)
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_IDLIST;
|
|
TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, pidlPath=%p\n", pInfo->dwFlags, pInfo->pidlPath);
|
|
}
|
|
else
|
|
{
|
|
// The path could not be parsed into an ID List,
|
|
// so pass it on as a plain string.
|
|
|
|
PWSTR field = StrDupW(strField);
|
|
pInfo->strPath = field;
|
|
if (field)
|
|
{
|
|
pInfo->dwFlags |= SH_EXPLORER_CMDLINE_FLAG_STRING;
|
|
TRACE("CmdLine Parser: Parsed target path. dwFlags=%08lx, strPath=%S\n", pInfo->dwFlags, field);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (!hasNext)
|
|
break;
|
|
hasNext = _ReadNextArg(&strNextArg, strField, _countof(strField));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|