diff --git a/Readme.md b/Readme.md index f2209db..84f9cdd 100644 --- a/Readme.md +++ b/Readme.md @@ -8,7 +8,7 @@ Cmder is a **software package** created out of pure frustration over absence of ## Why use it -The main advantage of Cmder is portability. It is designed to be totally self-contained with no external dependencies. That makes is great for **USB Sticks** or **Dropbox**. So you can carry your console, aliases and binaries (like wget, curl and git) with you anywhere. +The main advantage of Cmder is portability. It is designed to be totally self-contained with no external dependencies. That makes it great for **USB Sticks** or **Dropbox**. So you can carry your console, aliases and binaries (like wget, curl and git) with you anywhere. ## Installation @@ -25,13 +25,14 @@ The main advantage of Cmder is portability. It is designed to be totally self-co * `Ctrl + t` : new tab dialog (maybe you want to open cmd as admin?) * `Ctrl + w` : close tab +* `Ctrl + d` : close tab (if pressed on empty command) * `Ctrl + alt + number` : fast new tab: `1` - CMD, `2` - Powershell `*` - More to come * `Alt + enter`: Fullscreen ### Shell * `Shift + Up` : Traverse up in directory structure (lovely feature!) -* `End, Home, ctrl` : Traversing text with as usual on Windows +* `End, Home, Ctrl` : Traversing text with as usual on Windows * `Ctrl + r` : History search * `Shift + mouse` : Select and copy text from buffer diff --git a/bin/alias.bat b/bin/alias.bat index 8e048ae..4ec1882 100644 --- a/bin/alias.bat +++ b/bin/alias.bat @@ -13,8 +13,8 @@ if not ["%_temp%"] == ["%_temp2%"] ( goto:eof ) -echo %* >> %~dp0..\config\aliases -doskey /macrofile=%~dp0..\config\aliases +echo %* >> "%CMDER_ROOT%\config\aliases" +doskey /macrofile="%CMDER_ROOT%\config\aliases" echo Alias created endlocal goto:eof diff --git a/build_cmder.rb b/build.rb similarity index 81% rename from build_cmder.rb rename to build.rb index b380eea..7665541 100644 --- a/build_cmder.rb +++ b/build.rb @@ -74,6 +74,14 @@ unless find_on_path('7z.exe') exit(1) end +build_exe = true +unless find_on_path('msbuild.exe') + puts 'msbuild.exe not found. We need that to build the executable.' + puts 'Do you want to continue? [Y/n]' + build_exe = false + exit(1) unless gets.chomp.downcase == 'y' +end + puts 'Cleanup' if Dir.exists?('vendor') @@ -84,8 +92,19 @@ Dir.chdir('vendor') puts 'Getting files' -get_file('clink', 'label:Type-Archive label=Featured') -get_file('conemu-maximus5', 'label:Type-Archive label=Preview label=Featured') +get_file('clink', 'label:Type-Archive label:Featured') +get_file('conemu-maximus5', 'label:Type-Archive label:Preview label:Featured') get_file('msysgit', 'label:Type-Archive label:Featured') +puts 'Creating executable' + +if build_exe + Dir.chdir('../launcher') + status = system('msbuild /p:Configuration=Release') + unless status + puts 'Looks like the build failied' + exit(1) + end +end + puts 'Done, bye' diff --git a/config/ConEmu.xml b/config/ConEmu.xml index f2c98ff..bead00c 100644 --- a/config/ConEmu.xml +++ b/config/ConEmu.xml @@ -77,7 +77,7 @@ - + diff --git a/config/aliases b/config/aliases index da3b047..8859444 100644 --- a/config/aliases +++ b/config/aliases @@ -1,4 +1,4 @@ e.=explorer . gl=git log --oneline --all --graph --decorate $* ls=ls --color $* -pwd=cd \ No newline at end of file +pwd=cd diff --git a/config/prompt.lua b/config/prompt.lua new file mode 100644 index 0000000..5b0c239 --- /dev/null +++ b/config/prompt.lua @@ -0,0 +1,5 @@ +function lambda_prompt_filter() + clink.prompt.value = string.gsub(clink.prompt.value, "{lamb}", "λ") +end + +clink.prompt.register_filter(lambda_prompt_filter, 40) \ No newline at end of file diff --git a/launcher/.gitignore b/launcher/.gitignore new file mode 100644 index 0000000..0036705 --- /dev/null +++ b/launcher/.gitignore @@ -0,0 +1,181 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +*.filters + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.azurePubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ +## TODO: If the tool you use requires repositories.config, also uncomment the next line +#!packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ diff --git a/launcher/CmderLauncher.sln b/launcher/CmderLauncher.sln new file mode 100644 index 0000000..e4934d3 --- /dev/null +++ b/launcher/CmderLauncher.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmderLauncher", "CmderLauncher.vcxproj", "{4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Debug|Win32.ActiveCfg = Debug|Win32 + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Debug|Win32.Build.0 = Debug|Win32 + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Release|Win32.ActiveCfg = Release|Win32 + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/launcher/CmderLauncher.vcxproj b/launcher/CmderLauncher.vcxproj new file mode 100644 index 0000000..f4ab5c3 --- /dev/null +++ b/launcher/CmderLauncher.vcxproj @@ -0,0 +1,98 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {4A8485A5-B7DD-4C44-B7F6-3E2765DD0CD3} + Win32Proj + CmderLauncher + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + Cmder + + + false + Cmder + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + + + MinSpace + true + false + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Size + + + Windows + true + true + true + + + copy $(TargetPath) $(SolutionDir)..\$(TargetFileName) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launcher/src/CmderLauncher.cpp b/launcher/src/CmderLauncher.cpp new file mode 100644 index 0000000..677ee13 --- /dev/null +++ b/launcher/src/CmderLauncher.cpp @@ -0,0 +1,231 @@ +#include +#include +#include +#include "resource.h" +#include + +#pragma comment(lib, "Shlwapi.lib") + +#ifndef UNICODE +#error "Must be compiled with unicode support." +#endif + +#define USE_TASKBAR_API (_WIN32_WINNT >= _WIN32_WINNT_WIN7) + +#define MB_TITLE L"Cmder Launcher" +#define SHELL_MENU_REGISTRY_PATH L"Directory\\Background\\shell\\Cmder" + +#define streqi(a, b) (_wcsicmp((a), (b)) == 0) + +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#define __WFUNCTION__ WIDEN(__FUNCTION__) + +#define FAIL_ON_ERROR(x) { DWORD ec; if ((ec = (x)) != ERROR_SUCCESS) { ShowErrorAndExit(ec, __WFUNCTION__, __LINE__); } } + +void ShowErrorAndExit(DWORD ec, const wchar_t * func, int line) +{ + wchar_t * buffer; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, ec, 0, (LPWSTR) &buffer, 0, NULL) == 0) + { + buffer = L"Unknown error. FormatMessage failed."; + } + + wchar_t message[1024]; + swprintf_s(message, L"%s\nFunction: %s\nLine: %d", buffer, func, line); + LocalFree(buffer); + + MessageBox(NULL, message, MB_TITLE, MB_OK | MB_ICONERROR); + exit(1); +} + +typedef struct _option +{ + std::wstring name; + bool hasVal; + std::wstring value; + bool set; +} option; + +typedef std::pair optpair; + + +optpair GetOption() +{ + wchar_t * cmd = GetCommandLine(); + int argc; + wchar_t ** argv = CommandLineToArgvW(cmd, &argc); + optpair pair; + + if (argc == 1) + { + pair = optpair(L"/START", L""); + } + else if (argc == 2 && argv[1][0] != L'/') + { + pair = optpair(L"/START", argv[1]); + } + else + { + pair = optpair(argv[1], argc > 2 ? argv[2] : L""); + } + + LocalFree(argv); + + return pair; +} + +void StartCmder(std::wstring path) +{ +#if USE_TASKBAR_API + wchar_t appId[MAX_PATH] = { 0 }; +#endif + wchar_t exeDir[MAX_PATH] = { 0 }; + wchar_t icoPath[MAX_PATH] = { 0 }; + wchar_t cfgPath[MAX_PATH] = { 0 }; + wchar_t conEmuPath[MAX_PATH] = { 0 }; + wchar_t args[MAX_PATH * 2 + 256] = { 0 }; + + GetModuleFileName(NULL, exeDir, sizeof(exeDir)); + +#if USE_TASKBAR_API + wcscpy_s(appId, exeDir); +#endif + + PathRemoveFileSpec(exeDir); + + PathCombine(icoPath, exeDir, L"icons\\cmder.ico"); + PathCombine(cfgPath, exeDir, L"config\\ConEmu.xml"); + PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.exe"); + + swprintf_s(args, L"/Icon \"%s\" /Title Cmder /LoadCfgFile \"%s\"", icoPath, cfgPath); + + SetEnvironmentVariable(L"CMDER_ROOT", exeDir); + SetEnvironmentVariable(L"CMDER_START", path.c_str()); + + STARTUPINFO si = { 0 }; + si.cb = sizeof(STARTUPINFO); +#if USE_TASKBAR_API + si.lpTitle = appId; + si.dwFlags = STARTF_TITLEISAPPID; +#endif + + PROCESS_INFORMATION pi; + + CreateProcess(conEmuPath, args, NULL, NULL, false, 0, NULL, NULL, &si, &pi); +} + +bool IsUserOnly(std::wstring opt) +{ + bool userOnly; + + if (streqi(opt.c_str(), L"ALL")) + { + userOnly = false; + } + else if (streqi(opt.c_str(), L"USER")) + { + userOnly = true; + } + else + { + MessageBox(NULL, L"Unrecognized option for /REGISTER or /UNREGISTER. Must be either ALL or USER.", MB_TITLE, MB_OK); + exit(1); + } + + return userOnly; +} + +HKEY GetRootKey(std::wstring opt) +{ + HKEY root; + + if (IsUserOnly(opt)) + { + FAIL_ON_ERROR(RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Classes", 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &root, NULL)); + } + else + { + root = HKEY_CLASSES_ROOT; + } + + return root; +} + +void RegisterShellMenu(std::wstring opt) +{ + HKEY root = GetRootKey(opt); + + HKEY cmderKey; + FAIL_ON_ERROR( + RegCreateKeyEx(root, SHELL_MENU_REGISTRY_PATH, 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)); + + HKEY command; + FAIL_ON_ERROR( + RegCreateKeyEx(cmderKey, L"command", 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &command, NULL)); + + wchar_t exePath[MAX_PATH] = { 0 }; + + GetModuleFileName(NULL, exePath, sizeof(exePath)); + + wchar_t commandStr[MAX_PATH + 20] = { 0 }; + swprintf_s(commandStr, L"\"%s\" \"%%V\"", exePath); + + FAIL_ON_ERROR(RegSetValue(command, L"", REG_SZ, commandStr, NULL)); + + RegCloseKey(command); + RegCloseKey(cmderKey); + RegCloseKey(root); +} + +void UnregisterShellMenu(std::wstring opt) +{ + HKEY root = GetRootKey(opt); + HKEY cmderKey; + FAIL_ON_ERROR( + RegCreateKeyEx(root, SHELL_MENU_REGISTRY_PATH, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL)); + FAIL_ON_ERROR(RegDeleteTree(cmderKey, NULL)); + FAIL_ON_ERROR(RegDeleteKey(root, SHELL_MENU_REGISTRY_PATH)); + RegCloseKey(cmderKey); + RegCloseKey(root); +} + +int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPTSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(nCmdShow); + + optpair opt = GetOption(); + + if (streqi(opt.first.c_str(), L"/START")) + { + StartCmder(opt.second); + } + else if (streqi(opt.first.c_str(), L"/REGISTER")) + { + RegisterShellMenu(opt.second); + } + else if (streqi(opt.first.c_str(), L"/UNREGISTER")) + { + UnregisterShellMenu(opt.second); + } + else + { + MessageBox(NULL, L"Unrecognized parameter.\n\nValid options:\n /START \n /REGISTER [USER/ALL]\n /UNREGISTER [USER/ALL]", MB_TITLE, MB_OK); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/launcher/src/Resource.rc b/launcher/src/Resource.rc new file mode 100644 index 0000000..42eec58 Binary files /dev/null and b/launcher/src/Resource.rc differ diff --git a/launcher/src/resource.h b/launcher/src/resource.h new file mode 100644 index 0000000..5746935 Binary files /dev/null and b/launcher/src/resource.h differ diff --git a/pack.rb b/pack.rb new file mode 100644 index 0000000..e69de29 diff --git a/vendor/init.bat b/vendor/init.bat index 7d3a653..996ae3e 100644 --- a/vendor/init.bat +++ b/vendor/init.bat @@ -2,16 +2,9 @@ :: Sets some nice defaults :: Created as part of cmder project - -:: Setting prompt style -@for /f "tokens=2 delims=:." %%x in ('chcp') do @set cp=%%x -:: The slow part -:: World without Unicode is a sad world -@chcp 65001>nul -:: It has to be lambda, I already made a logo -@prompt $E[1;32;40m$P$S{git}$S$_$E[1;30;40mλ$S$E[0m -@chcp %cp%>nul - +:: Change the prompt style +:: Mmm tasty lamb +@prompt $E[1;32;40m$P$S{git}$S$_$E[1;30;40m{lamb}$S$E[0m :: Pick right version of clink @if "%PROCESSOR_ARCHITECTURE%"=="x86" ( @@ -20,24 +13,23 @@ set architecture=64 ) -@set rootDir="%~dp0\.." - :: Run clink -@%rootDir%\vendor\clink\clink_x%architecture%.exe inject --quiet --profile %rootDir%\config +@"%CMDER_ROOT%\vendor\clink\clink_x%architecture%.exe" inject --quiet --profile "%CMDER_ROOT%\config" :: Prepare for msysgit :: I do not even know, copypasted from their .bat @set PLINK_PROTOCOL=ssh -@if not defined TERM set TERM=msys +@if not defined TERM set TERM=cygwin :: Enhance Path -@set git_install_root=%rootDir%\vendor\msysgit -@set PATH=%PATH%;%rootDir%\bin;%git_install_root%\bin;%git_install_root%\mingw\bin;%git_install_root%\cmd;%git_install_root%\share\vim\vim73; +@set git_install_root=%CMDER_ROOT%\vendor\msysgit +@set PATH=%PATH%;%CMDER_ROOT%\bin;%git_install_root%\bin;%git_install_root%\mingw\bin;%git_install_root%\cmd;%git_install_root%\share\vim\vim73; :: Add aliases -@doskey /macrofile=%rootDir%\config\aliases +@doskey /macrofile=%CMDER_ROOT%\config\aliases :: Set home path @set HOME=%USERPROFILE% -@echo Welcome to cmder! + +@if defined CMDER_START cd /d "%CMDER_START%"