New cmder.exe args and shared install capability (#1696)

@MartiUK: Squashing to avoid adding multiple "fixed" commits

* add args to init.bat

* adding args to cmder launcher

* reworked command line parsing and added a /C [path] arg for individual user config location

* removed unnecessary includes

* make shell init scripts work with CMDER_USER_CONFIG

* update tasks and readme.md

* fix git version check

* readme updates

* add register/unregister back in

* fixed git version again

* removed error if user defines user-aliases store file was not present an init.bat launch

* added enhance_path method to only update path if required

* added enhance_path method to only update path if required

* fixed a path prepend issue in enhance path

* init.bat with args is executed outside cmder/conemu sets cmder_root properly. Thanks @DRSDavidSoft

* fixed enhance path append issue

* implements recursive `/bin` path enhancing.

https://github.com/cmderdev/cmder/issues/1624

* added max depth

* changes command line arg to max_depth

* set max_depth default

* readme.md updates

* add back cmder /c [path] arg so it can be used with admin sessions since the env is not shared.

* readme.md updates

* fix /c setting of cmder_user_config

* changelog and readme

* remove bad arg

* fixed command line parsing and updated command line help on error

* Fixed ConEmu.xml file handling so it works again

* Added default user config root location if '/c' is specified and next arg is not another arg
This commit is contained in:
Dax T Games
2018-03-13 10:38:27 -05:00
committed by Martin Kemp
parent ff2e09849e
commit 997e799138
10 changed files with 950 additions and 348 deletions

View File

@ -60,6 +60,9 @@
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>

View File

@ -3,6 +3,10 @@
#include <Shlwapi.h>
#include "resource.h"
#include <vector>
#include <shlobj.h>
#include <regex>
#include <iostream>
#pragma comment(lib, "Shlwapi.lib")
@ -53,33 +57,6 @@ typedef struct _option
typedef std::pair<std::wstring, std::wstring> optpair;
optpair GetOption()
{
wchar_t * cmd = GetCommandLine();
int argc;
wchar_t ** argv = CommandLineToArgvW(cmd, &argc);
optpair pair;
if (argc == 1)
{
// no commandline argument...
pair = optpair(L"/START", L"");
}
else if (argc == 2 && argv[1][0] != L'/')
{
// only a single argument: this should be a path...
pair = optpair(L"/START", argv[1]);
}
else
{
pair = optpair(argv[1], argc > 2 ? argv[2] : L"");
}
LocalFree(argv);
return pair;
}
bool FileExists(const wchar_t * filePath)
{
HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
@ -93,7 +70,7 @@ bool FileExists(const wchar_t * filePath)
return false;
}
void StartCmder(std::wstring path, bool is_single_mode, std::wstring taskName = L"")
void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstring taskName = L"", std::wstring cfgRoot = L"")
{
#if USE_TASKBAR_API
wchar_t appId[MAX_PATH] = { 0 };
@ -104,10 +81,20 @@ void StartCmder(std::wstring path, bool is_single_mode, std::wstring taskName =
wchar_t backupCfgPath[MAX_PATH] = { 0 };
wchar_t cpuCfgPath[MAX_PATH] = { 0 };
wchar_t userCfgPath[MAX_PATH] = { 0 };
wchar_t oldCfgPath[MAX_PATH] = { 0 };
wchar_t defaultCfgPath[MAX_PATH] = { 0 };
wchar_t conEmuPath[MAX_PATH] = { 0 };
wchar_t configDirPath[MAX_PATH] = { 0 };
wchar_t userConfigDirPath[MAX_PATH] = { 0 };
wchar_t userBinDirPath[MAX_PATH] = { 0 };
wchar_t userProfiledDirPath[MAX_PATH] = { 0 };
wchar_t args[MAX_PATH * 2 + 256] = { 0 };
std::wstring cmderStart = path;
std::wstring cmderTask = taskName;
std::copy(cfgRoot.begin(), cfgRoot.end(), userConfigDirPath);
userConfigDirPath[cfgRoot.length()] = 0;
GetModuleFileName(NULL, exeDir, sizeof(exeDir));
#if USE_TASKBAR_API
@ -118,28 +105,103 @@ void StartCmder(std::wstring path, bool is_single_mode, std::wstring taskName =
PathCombine(icoPath, exeDir, L"icons\\cmder.ico");
// Check for machine-specific then user config source file.
PathCombine(cpuCfgPath, exeDir, L"config\\ConEmu-%COMPUTERNAME%.xml");
ExpandEnvironmentStrings(cpuCfgPath, cpuCfgPath, sizeof(cpuCfgPath) / sizeof(cpuCfgPath[0]));
PathCombine(userCfgPath, exeDir, L"config\\user-ConEmu.xml");
if (PathFileExists(cpuCfgPath)) {
wcsncpy_s(oldCfgPath, cpuCfgPath, sizeof(cpuCfgPath));
wcsncpy_s(backupCfgPath, cpuCfgPath, sizeof(cpuCfgPath));
PathCombine(configDirPath, exeDir, L"config");
if (wcscmp(userConfigDirPath, L"") == 0)
{
PathCombine(userConfigDirPath, exeDir, L"config");
}
else if (PathFileExists(userCfgPath)) {
wcsncpy_s(oldCfgPath, userCfgPath, sizeof(userCfgPath));
wcsncpy_s(backupCfgPath, userCfgPath, sizeof(userCfgPath));
}
else {
PathCombine(oldCfgPath, exeDir, L"config\\ConEmu.xml");
wcsncpy_s(backupCfgPath, userCfgPath, sizeof(userCfgPath));
else
{
PathCombine(userBinDirPath, userConfigDirPath, L"bin");
SHCreateDirectoryEx(0, userBinDirPath, 0);
PathCombine(userConfigDirPath, userConfigDirPath, L"config");
SHCreateDirectoryEx(0, userConfigDirPath, 0);
PathCombine(userProfiledDirPath, userConfigDirPath, L"profile.d");
SHCreateDirectoryEx(0, userProfiledDirPath, 0);
}
// Set path to vendored ConEmu config file
PathCombine(cfgPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.xml");
// Set path to Cmder default ConEmu config file
PathCombine(defaultCfgPath, exeDir, L"config\\ConEmu.xml");
// Check for machine-specific then user config source file.
PathCombine(cpuCfgPath, userConfigDirPath, L"ConEmu-%COMPUTERNAME%.xml");
ExpandEnvironmentStrings(cpuCfgPath, cpuCfgPath, sizeof(cpuCfgPath) / sizeof(cpuCfgPath[0]));
PathCombine(userCfgPath, userConfigDirPath, L"user-ConEmu.xml");
if (PathFileExists(cpuCfgPath)) {
if (PathFileExists(cfgPath)) {
if (!CopyFile(cfgPath, cpuCfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy ConEmu.xml file to ConEmu-%COMPUTERNAME%.xml backup location! Restart Cmder as administrator."
: L"Failed to copy ConEmu.xml file to ConEmu-%COMPUTERNAME%.xml backup location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
else
{
if (!CopyFile(cpuCfgPath, cfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy ConEmu-%COMPUTERNAME%.xml file to vendored ConEmu.xml location! Restart Cmder as administrator."
: L"Failed to copy ConEmu-%COMPUTERNAME%.xml file to vendored ConEmu.xml location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
}
else if (PathFileExists(userCfgPath)) {
if (PathFileExists(cfgPath)) {
if (!CopyFile(cfgPath, userCfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy ConEmu.xml file to backup location! Restart Cmder as administrator."
: L"Failed to copy ConEmu.xml file to backup location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
else
{
if (!CopyFile(userCfgPath, cfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy ConEmu.xml file to vendored ConEmu.xml location! Restart Cmder as administrator."
: L"Failed to copy ConEmu.xml file to vendored ConEmu.xml location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
}
else if (PathFileExists(cfgPath)) {
if (!CopyFile(cfgPath, userCfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy ConEmu.xml file to user-conemu.xml backup location! Restart Cmder as administrator."
: L"Failed to copy ConEmu.xml file to user-conemu.xml backup location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
else {
if (!CopyFile(defaultCfgPath, cfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy Cmder default ConEmu.xml file to vendored ConEmu.xml location! Restart Cmder as administrator."
: L"Failed to copy Cmder default ConEmu.xml file to vendored ConEmu.xml location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
SYSTEM_INFO sysInfo;
GetNativeSystemInfo(&sysInfo);
if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
@ -149,51 +211,44 @@ void StartCmder(std::wstring path, bool is_single_mode, std::wstring taskName =
PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.exe");
}
if (FileExists(oldCfgPath) && !FileExists(cfgPath))
{
if (!CopyFile(oldCfgPath, cfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to copy ConEmu.xml file to new location! Restart Cmder as administrator."
: L"Failed to copy ConEmu.xml file to new location!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
}
else if (!CopyFile(cfgPath, backupCfgPath, FALSE))
{
MessageBox(NULL,
(GetLastError() == ERROR_ACCESS_DENIED)
? L"Failed to backup ConEmu.xml file to ./config folder!"
: L"Failed to backup ConEmu.xml file to ./config folder!", MB_TITLE, MB_ICONSTOP);
exit(1);
}
if (streqi(path.c_str(), L""))
if (streqi(cmderStart.c_str(), L""))
{
TCHAR buff[MAX_PATH];
const DWORD ret = GetEnvironmentVariable(L"USERPROFILE", buff, MAX_PATH);
path = buff;
cmderStart = buff;
}
if (is_single_mode)
{
swprintf_s(args, L"/single /Icon \"%s\" /Title Cmder /dir \"%s\"", icoPath, path.c_str());
if (!streqi(cmderTask.c_str(), L"")) {
swprintf_s(args, L"%s /single /Icon \"%s\" /Title Cmder /dir \"%s\" /run {%s}", args, icoPath, cmderStart.c_str(), cmderTask.c_str());
}
else {
swprintf_s(args, L"%s /single /Icon \"%s\" /Title Cmder /dir \"%s\"", args, icoPath, cmderStart.c_str());
}
}
else
{
swprintf_s(args, L"/Icon \"%s\" /Title Cmder /dir \"%s\"", icoPath, path.c_str());
if (!streqi(cmderTask.c_str(), L"")) {
swprintf_s(args, L"/Icon \"%s\" /Title Cmder /dir \"%s\" /run {%s}", icoPath, cmderStart.c_str(), cmderTask.c_str());
}
else {
swprintf_s(args, L"%s /Icon \"%s\" /Title Cmder /dir \"%s\"", args, icoPath, cmderStart.c_str());
}
}
if (!taskName.empty()) {
swprintf_s(args, L"%s /run {%s}", args, taskName.c_str());
}
SetEnvironmentVariable(L"CMDER_ROOT", exeDir);
if (wcscmp(userConfigDirPath, configDirPath) != 0)
{
SetEnvironmentVariable(L"CMDER_USER_CONFIG", userConfigDirPath);
SetEnvironmentVariable(L"CMDER_USER_BIN", userBinDirPath);
}
// Ensure EnvironmentVariables are propagated.
STARTUPINFO si = { 0 };
si.cb = sizeof(STARTUPINFO);
#if USE_TASKBAR_API
si.lpTitle = appId;
@ -255,34 +310,33 @@ void RegisterShellMenu(std::wstring opt, wchar_t* keyBaseName)
GetModuleFileName(NULL, exePath, sizeof(exePath));
wchar_t commandStr[MAX_PATH + 20] = { 0 };
swprintf_s(commandStr, L"\"%s\" \"%%V\"", exePath);
wchar_t commandStr[MAX_PATH + 20] = { 0 };
swprintf_s(commandStr, L"\"%s\" \"%%V\"", exePath);
// Now that we have `commandStr`, it's OK to change `exePath`...
PathRemoveFileSpec(exePath);
// Now that we have `commandStr`, it's OK to change `exePath`...
PathRemoveFileSpec(exePath);
PathCombine(icoPath, exePath, L"icons\\cmder.ico");
PathCombine(icoPath, exePath, L"icons\\cmder.ico");
// Now set the registry keys
// Now set the registry keys
HKEY root = GetRootKey(opt);
HKEY root = GetRootKey(opt);
HKEY cmderKey;
FAIL_ON_ERROR(RegCreateKeyEx(root, keyBaseName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL));
HKEY cmderKey;
FAIL_ON_ERROR(RegCreateKeyEx(root, keyBaseName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL));
FAIL_ON_ERROR(RegSetValue(cmderKey, L"", REG_SZ, L"Cmder Here", NULL));
FAIL_ON_ERROR(RegSetValueEx(cmderKey, L"NoWorkingDirectory", 0, REG_SZ, (BYTE *)L"", 2));
FAIL_ON_ERROR(RegSetValue(cmderKey, L"", REG_SZ, L"Cmder Here", NULL));
FAIL_ON_ERROR(RegSetValueEx(cmderKey, L"NoWorkingDirectory", 0, REG_SZ, (BYTE *)L"", 2));
FAIL_ON_ERROR(RegSetValueEx(cmderKey, L"Icon", 0, REG_SZ, (BYTE *)icoPath, wcslen(icoPath) * sizeof(wchar_t)));
FAIL_ON_ERROR(RegSetValueEx(cmderKey, L"Icon", 0, REG_SZ, (BYTE *)icoPath, wcslen(icoPath) * sizeof(wchar_t)));
HKEY command;
FAIL_ON_ERROR(RegCreateKeyEx(cmderKey, L"command", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &command, NULL));
HKEY command;
FAIL_ON_ERROR(RegCreateKeyEx(cmderKey, L"command", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &command, NULL));
FAIL_ON_ERROR(RegSetValue(command, L"", REG_SZ, commandStr, NULL));
FAIL_ON_ERROR(RegSetValue(command, L"", REG_SZ, commandStr, NULL));
RegCloseKey(command);
RegCloseKey(cmderKey);
RegCloseKey(root);
RegCloseKey(command);
RegCloseKey(cmderKey);
RegCloseKey(root);
}
void UnregisterShellMenu(std::wstring opt, wchar_t* keyBaseName)
@ -299,6 +353,106 @@ void UnregisterShellMenu(std::wstring opt, wchar_t* keyBaseName)
RegCloseKey(root);
}
struct cmderOptions
{
std::wstring cmderCfgRoot = L"";
std::wstring cmderStart = L"";
std::wstring cmderTask = L"";
std::wstring cmderRegScope = L"USER";
bool cmderSingle = false;
bool registerApp = false;
bool unRegisterApp = false;
bool error = false;
};
cmderOptions GetOption()
{
cmderOptions cmderOptions;
LPWSTR *szArgList;
int argCount;
szArgList = CommandLineToArgvW(GetCommandLine(), &argCount);
for (int i = 1; i < argCount; i++)
{
// MessageBox(NULL, szArgList[i], L"Arglist contents", MB_OK);
if (_wcsicmp(L"/c", szArgList[i]) == 0)
{
TCHAR userProfile[MAX_PATH];
const DWORD ret = GetEnvironmentVariable(L"USERPROFILE", userProfile, MAX_PATH);
wchar_t cmderCfgRoot[MAX_PATH] = { 0 };
PathCombine(cmderCfgRoot, userProfile, L"cmder_cfg");
cmderOptions.cmderCfgRoot = cmderCfgRoot;
if (szArgList[i + 1] != NULL && szArgList[i + 1][0] != '/') {
cmderOptions.cmderCfgRoot = szArgList[i + 1];
i++;
}
}
else if (_wcsicmp(L"/start", szArgList[i]) == 0)
{
if (PathFileExists(szArgList[i + 1]))
{
cmderOptions.cmderStart = szArgList[i + 1];
i++;
}
else {
MessageBox(NULL, szArgList[i + 1], L"/START - Folder doses not exist!", MB_OK);
}
}
else if (_wcsicmp(L"/task", szArgList[i]) == 0)
{
cmderOptions.cmderTask = szArgList[i + 1];
i++;
}
else if (_wcsicmp(L"/single", szArgList[i]) == 0)
{
cmderOptions.cmderSingle = true;
}
else if (_wcsicmp(L"/register", szArgList[i]) == 0)
{
cmderOptions.registerApp = true;
cmderOptions.unRegisterApp = false;
if (szArgList[i + 1] != NULL)
{
if (_wcsicmp(L"all", szArgList[i + 1]) == 0 || _wcsicmp(L"user", szArgList[i + 1]) == 0)
{
cmderOptions.cmderRegScope = szArgList[i + 1];
i++;
}
}
}
else if (_wcsicmp(L"/unregister", szArgList[i]) == 0)
{
cmderOptions.unRegisterApp = true;
cmderOptions.registerApp = false;
if (szArgList[i + 1] != NULL)
{
if (_wcsicmp(L"all", szArgList[i + 1]) == 0 || _wcsicmp(L"user", szArgList[i + 1]) == 0)
{
cmderOptions.cmderRegScope = szArgList[i + 1];
i++;
}
}
}
else if (cmderOptions.cmderStart == L"" && PathFileExists(szArgList[i]))
{
cmderOptions.cmderStart = szArgList[i];
}
else {
MessageBox(NULL, L"Unrecognized parameter.\n\nValid options:\n\n /c [CMDER User Root Path]\n\n /task [ConEmu Task Name]\n\n [/start [Start in Path] | [Start in Path]]\n\n /single\n\nor\n\n /register [USER | ALL]\n\nor\n\n /unregister [USER | ALL]\n", MB_TITLE, MB_OK);
cmderOptions.error = true;
}
}
LocalFree(szArgList);
return cmderOptions;
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
@ -308,35 +462,25 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
UNREFERENCED_PARAMETER(lpCmdLine);
UNREFERENCED_PARAMETER(nCmdShow);
optpair opt = GetOption();
cmderOptions cmderOptions = GetOption();
if (streqi(opt.first.c_str(), L"/START"))
{
StartCmder(opt.second, false);
if (cmderOptions.registerApp == true ) {
RegisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_BACKGROUND);
RegisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_LISTITEM);
}
else if (streqi(opt.first.c_str(), L"/SINGLE"))
else if (cmderOptions.unRegisterApp == true )
{
StartCmder(opt.second, true);
UnregisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_BACKGROUND);
UnregisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_LISTITEM);
}
else if (streqi(opt.first.c_str(), L"/TASK"))
else if (cmderOptions.error == true)
{
StartCmder(L"", false, opt.second);
}
else if (streqi(opt.first.c_str(), L"/REGISTER"))
{
RegisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_BACKGROUND);
RegisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_LISTITEM);
}
else if (streqi(opt.first.c_str(), L"/UNREGISTER"))
{
UnregisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_BACKGROUND);
UnregisterShellMenu(opt.second, SHELL_MENU_REGISTRY_PATH_LISTITEM);
return 1;
}
else
{
MessageBox(NULL, L"Unrecognized parameter.\n\nValid options:\n /START <path>\n /SINGLE <path>\n /TASK <name>\n /REGISTER [USER/ALL]\n /UNREGISTER [USER/ALL]", MB_TITLE, MB_OK);
return 1;
StartCmder(cmderOptions.cmderStart, cmderOptions.cmderSingle, cmderOptions.cmderTask, cmderOptions.cmderCfgRoot);
}
return 0;
}