Repository: cmderdev/cmder Branch: master Commit: 7f22d2eb73af Files: 69 Total size: 406.4 KB Directory structure: gitextract_zyedj6fh/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yml │ │ ├── config.yml │ │ ├── feature-request.yml │ │ └── question.yml │ ├── dependabot.yml │ ├── stale.yml │ └── workflows/ │ ├── branches.yml │ ├── build.yml │ ├── codeql.yml │ ├── tests.yml │ └── vendor.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cmder.bat ├── LICENSE ├── README.md ├── SECURITY.md ├── bin/ │ └── Readme.md ├── config/ │ └── Readme.md ├── icons/ │ └── cmder_icon.psd ├── launcher/ │ ├── .gitignore │ ├── CmderLauncher.sln │ ├── CmderLauncher.vcxproj │ └── src/ │ ├── CmderLauncher.cpp │ ├── app.manifest │ ├── resource.h │ ├── resource.rc │ ├── strings.rc2 │ └── version.rc2.sample ├── opt/ │ └── Readme.md ├── packignore ├── scripts/ │ ├── build.ps1 │ ├── pack.ps1 │ ├── update.ps1 │ └── utils.ps1 └── vendor/ ├── ConEmu.xml.default ├── Readme.md ├── bin/ │ ├── alias.cmd │ ├── cexec.cmd │ ├── cmder_diag.cmd │ ├── cmder_diag.ps1 │ ├── cmder_diag.sh │ ├── cmder_shell.cmd │ ├── excd.cmd │ ├── timer.cmd │ ├── vscode_init.cmd │ └── vscode_init_args.cmd.default ├── clink.lua ├── clink_settings.default ├── cmder.sh ├── cmder_exinit ├── cmder_prompt_config.lua.default ├── git-prompt.sh ├── init.bat ├── lib/ │ ├── lib_base.cmd │ ├── lib_console.cmd │ ├── lib_git.cmd │ ├── lib_path.cmd │ ├── lib_profile.cmd │ └── start-ssh-agent.sh ├── profile.ps1 ├── psmodules/ │ └── Cmder.ps1 ├── sources.json ├── user_aliases.cmd.default ├── user_profile.cmd.default ├── user_profile.ps1.default └── user_profile.sh.default ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto *.cmd text eol=crlf *.bat text eol=crlf *.ps1 text eol=crlf *.md text eol=lf *.sh text eol=lf ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yml ================================================ name: "🐞 Bug report (encountered problems/errors)" description: Something is not working as it should title: "[Bug] " labels: "🐛 Type: Bug" body: - type: markdown attributes: value: | Thank you for reporting a bug for the Cmder project! ------------------------------------------------------------------ Please make sure you read and follow the following instructions carefully before reporting bugs, and/or requesting new features. - You can find the version of Cmder.exe and ConEmu.exe binaries using Right Click → Properties → Details menu. - type: textarea id: version attributes: label: Version Information description: | Please write your Cmder and ConEmu version below. If applicable, write down your Windows edition too. **👉 See:** [How to find out which Cmder version I'm using](https://github.com/cmderdev/cmder/wiki/Cmder-troubleshooting) placeholder: | Cmder version: Operating system: value: | Cmder version: Operating system: render: markdown validations: required: true - type: dropdown id: edition attributes: label: Cmder Edition description: What edition of Cmder are you running? options: - Cmder Full (with Git) - Cmder Mini - N/A validations: required: true - type: markdown attributes: value: | Make sure that you have: - Read both the README.md and the Wiki: | **README.md** | **Wiki** | | ------------- | -------- | | 🌐 [Open Link](https://github.com/cmderdev/cmder/blob/master/README.md) | 🌐 [Open Link](https://github.com/cmderdev/cmder/wiki) | (What you may be asking here could already be explained there!) - Searched for existing issues (including the **closed** ones) for similar problems here: 🗃 https://github.com/cmderdev/cmder/issues?q=is:issue - Please understand that Cmder uses ConEmu as the default underlying Terminal Emulator. If your issue is regarding the **Terminal Emulator**, please visit the ConEmu issues page: https://github.com/Maximus5/ConEmu/issues?q=is:issue If there isn't an existing issue, you may open a new one there. (We don't resolve issues regarding ConEmu here, so, please make sure you open the issue in the correct place.) more info: https://conemu.github.io/en/ThirdPartyProblems.html - If you are having an issue with any of the **upstream technologies** that are used by Cmder, please make sure that the issue is reproducible _only_ when used in combination with Cmder. We may not directly address the issues related to the following tools: - **[Clink](https://github.com/chrisant996/clink)**, the default shell in Cmder - **[ConEmu](https://github.com/Maximus5/ConEmu)**, the terminal emulator - **[Git/MinGW](https://github.com/git-for-windows/git)**, which also provide *NIX tools (such as `ls`, `mv`, `cp`, etc) - **[clink-completions](https://github.com/vladimir-kotikov/clink-completions)**, which provides autocompletion for clink We'll try our best to help you -- but we recommend creating an issue specifically at each of the corresponding repositories for the best result. **👉 Note:** Try to reproduce the bug you're reporting, on a stand-alone edition of each tool, without using Cmder. If the bug applies when the mentioned tools are NOT used within Cmder, there's a good chance that you should open the bug at the corresponding repo instead. - Lastly, have a look at the official documentation for Cmder over our website, and our wiki. Read more about Cmder on ConEmu docs: https://conemu.github.io/en/cmder.html #### Prerequisites before submitting an issue - We do not support any versions older than the current release series, if you are using an older Cmder please update to the latest version first. - Verify that the issue is not already fixed and is reproducible in the **[latest official Cmder version](https://github.com/cmderdev/cmder/releases).** - Check the **[current issues list](https://github.com/cmderdev/cmder/issues?q=is%3Aissue)** and perform a **search of the issue tracker (including closed ones)** to avoid posting a duplicate bug report. - Make sure this is not a support request or question, both of which are better suited for either the **[discussions section](https://github.com/cmderdev/cmder/discussions)**, or the **[questions section](https://github.com/cmderdev/cmder/issues/new?template=question.yml)**. - Verify that the **[wiki](https://github.com/cmderdev/cmder/wiki)** did not contain a suitable solution either. Thank you for making sure you are opening a new valid issue! ♥ - type: textarea id: description attributes: label: Description of the issue description: Provide a clear and concise description of the problem here. Explain the actual behavior vs the expected behavior. validations: required: true - type: textarea id: reproduction attributes: label: How to reproduce description: Please provide reliable steps to reproduce the problem. placeholder: | 1. In this environment... 2. With this config... 3. Run '...' 4. See error... validations: required: false - type: textarea id: notes attributes: label: Additional context description: Add screenshots, etc. (Anything that will provide more context about the problem) validations: required: false - type: checkboxes id: checklist attributes: label: Checklist description: Please check all boxes that apply options: - label: I have read the documentation. required: true - label: I have searched for similar issues and found none that describe my issue. required: true - label: I have reproduced the issue on the latest version of Cmder. required: true - label: I am certain my issues are not related to ConEmu, Clink, or other third-party tools that Cmder uses. required: true - type: markdown attributes: value: | ### Some tips on how to write a good bug report with the required information. - Make sure the description is worded well enough to be understood, and with as much context and examples as possible. - Post a screenshot or the command that triggered the problem, if applicable. - Use the *Preview* tab to see how your issue will actually look like, before sending it. ⚠ If for some reason you can not see the text you just wrote, make sure you've read the instructions clearly - Avoid using ambiguous phrases like: doesn't work, there's a problem, etc. Help us reproduce the issue by explaining what went wrong, and what did you expect to happen. - Please keep the ticket language to English only here. We can't process your issue if it's written in Russian or Chinese as we can't understand them. - You can find the version of Cmder.exe and ConEmu.exe binaries using Right Click → Properties → Details menu. - Put an `x` into all the boxes `[ ]` relevant to your issue (correct example: `[x]` -- not like this: `[ x]` or `[x ]`). - Any text that is between the comment tags will get ignored, e.g.: ` src/app.manifest %(AdditionalManifestFiles) _USING_V110_SDK71_;%(PreprocessorDefinitions) Level3 MinSpace true false WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true Size MultiThreaded Windows true true true src/app.manifest %(AdditionalManifestFiles) copy "$(TargetPath)" "$(SolutionDir)..\$(TargetFileName)" Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true Windows true src/app.manifest %(AdditionalManifestFiles) _USING_V110_SDK71_;%(PreprocessorDefinitions) Level3 MinSpace true false WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true Size MultiThreaded Windows true true true src/app.manifest %(AdditionalManifestFiles) copy $(TargetPath) $(SolutionDir)..\$(TargetFileName) RC ================================================ FILE: launcher/src/CmderLauncher.cpp ================================================ #include #include #include #include "resource.h" #include #include #include #include #pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "comctl32.lib") #pragma warning( disable : 4091 ) #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_BACKGROUND L"Directory\\Background\\shell\\Cmder" #define SHELL_MENU_REGISTRY_PATH_LISTITEM L"Directory\\shell\\Cmder" #define SHELL_MENU_REGISTRY_DRIVE_PATH_BACKGROUND L"Drive\\Background\\shell\\Cmder" #define SHELL_MENU_REGISTRY_DRIVE_PATH_LISTITEM L"Drive\\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 TaskDialogOpen( PCWSTR mainStr, PCWSTR contentStr ) { HRESULT hr = NULL; TASKDIALOGCONFIG tsk = {sizeof(tsk)}; HWND hOwner = NULL; HINSTANCE hInstance = GetModuleHandle(NULL); PCWSTR tskTitle = MAKEINTRESOURCE(IDS_TITLE); tsk.hInstance = hInstance; tsk.pszMainIcon = MAKEINTRESOURCE(IDI_CMDER); tsk.pszWindowTitle = tskTitle; tsk.pszMainInstruction = mainStr; tsk.pszContent = contentStr; TASKDIALOG_BUTTON btns[1] = { { IDOK, L"OK" } }; tsk.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION|TDF_ENABLE_HYPERLINKS; tsk.pButtons = btns; tsk.cButtons = _countof(btns); tsk.hwndParent = hOwner; int selectedButtonId = IDOK; hr = TaskDialogIndirect( &tsk, &selectedButtonId, NULL, NULL ); } 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; bool FileExists(const wchar_t * filePath) { HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); return true; } return false; } void StartCmder(std::wstring path = L"", bool is_single_mode = false, std::wstring taskName = L"", std::wstring title = L"", std::wstring iconPath = L"", std::wstring cfgRoot = L"", bool use_user_cfg = true, std::wstring conemu_args = L"") { #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 backupCfgPath[MAX_PATH] = { 0 }; wchar_t cpuCfgPath[MAX_PATH] = { 0 }; wchar_t userCfgPath[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 userProfilePath[MAX_PATH] = { 0 }; wchar_t legacyUserProfilePath[MAX_PATH] = { 0 }; wchar_t userAliasesPath[MAX_PATH] = { 0 }; wchar_t legacyUserAliasesPath[MAX_PATH] = { 0 }; wchar_t args[MAX_PATH * 2 + 256] = { 0 }; wchar_t userConEmuCfgPath[MAX_PATH] = { 0 }; std::wstring cmderStart = path; std::wstring cmderTask = taskName; std::wstring cmderTitle = title; std::wstring cmderConEmuArgs = conemu_args; std::copy(cfgRoot.begin(), cfgRoot.end(), userConfigDirPath); userConfigDirPath[cfgRoot.length()] = 0; GetModuleFileName(NULL, exeDir, sizeof(exeDir)); #if USE_TASKBAR_API wcscpy_s(appId, exeDir); #endif PathRemoveFileSpec(exeDir); if (PathFileExists(iconPath.c_str())) { std::copy(iconPath.begin(), iconPath.end(), icoPath); icoPath[iconPath.length()] = 0; } else { PathCombine(icoPath, exeDir, L"icons\\cmder.ico"); } PathCombine(configDirPath, exeDir, L"config"); /* Convert legacy user-profile.cmd to new name user_profile.cmd */ PathCombine(legacyUserProfilePath, configDirPath, L"user-profile.cmd"); if (PathFileExists(legacyUserProfilePath)) { PathCombine(userProfilePath, configDirPath, L"user_profile.cmd"); char *lPr = (char *)malloc(MAX_PATH); char *pR = (char *)malloc(MAX_PATH); size_t i; wcstombs_s(&i, lPr, (size_t)MAX_PATH, legacyUserProfilePath, (size_t)MAX_PATH); wcstombs_s(&i, pR, (size_t)MAX_PATH, userProfilePath, (size_t)MAX_PATH); rename(lPr, pR); } /* Convert legacy user-aliases.cmd to new name user_aliases.cmd */ PathCombine(legacyUserAliasesPath, configDirPath, L"user-aliases.cmd"); if (PathFileExists(legacyUserAliasesPath)) { PathCombine(userAliasesPath, configDirPath, L"user_aliases.cmd"); char *lPr = (char *)malloc(MAX_PATH); char *pR = (char *)malloc(MAX_PATH); size_t i; wcstombs_s(&i, lPr, (size_t)MAX_PATH, legacyUserAliasesPath, (size_t)MAX_PATH); wcstombs_s(&i, pR, (size_t)MAX_PATH, userAliasesPath, (size_t)MAX_PATH); rename(lPr, pR); } /* Was /c [path] specified? */ if (wcscmp(userConfigDirPath, L"") == 0) { // No - It wasn't. PathCombine(userConfigDirPath, exeDir, L"config"); } else { // Yes - It was. 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); /* Convert legacy user-profile.cmd to new name user_profile.cmd */ PathCombine(legacyUserProfilePath, userConfigDirPath, L"user-profile.cmd"); if (PathFileExists(legacyUserProfilePath)) { PathCombine(userProfilePath, userConfigDirPath, L"user_profile.cmd"); char *lPr = (char *)malloc(MAX_PATH); char *pR = (char *)malloc(MAX_PATH); size_t i; wcstombs_s(&i, lPr, (size_t)MAX_PATH, legacyUserProfilePath, (size_t)MAX_PATH); wcstombs_s(&i, pR, (size_t)MAX_PATH, userProfilePath, (size_t)MAX_PATH); rename(lPr, pR); } /* Convert legacy user-aliases.cmd to new name user_aliases.cmd */ PathCombine(legacyUserAliasesPath, userConfigDirPath, L"user-aliases.cmd"); if (PathFileExists(legacyUserAliasesPath)) { PathCombine(userAliasesPath, userConfigDirPath, L"user_aliases.cmd"); char *lPr = (char *)malloc(MAX_PATH); char *pR = (char *)malloc(MAX_PATH); size_t i; wcstombs_s(&i, lPr, (size_t)MAX_PATH, legacyUserAliasesPath, (size_t)MAX_PATH); wcstombs_s(&i, pR, (size_t)MAX_PATH, userAliasesPath, (size_t)MAX_PATH); rename(lPr, pR); } } // 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"vendor\\ConEmu.xml.default"); // Check for machine-specific then user config source file. PathCombine(cpuCfgPath, userConfigDirPath, L"ConEmu-%COMPUTERNAME%.xml"); ExpandEnvironmentStrings(cpuCfgPath, cpuCfgPath, sizeof(cpuCfgPath) / sizeof(cpuCfgPath[0])); // Set path to Cmder user ConEmu config file PathCombine(userCfgPath, userConfigDirPath, L"user-ConEmu.xml"); if ( PathFileExists(cpuCfgPath) || use_user_cfg == false ) // config/ConEmu-%COMPUTERNAME%.xml file exists or /m was specified on command line, use machine specific config. { if (cfgRoot.length() == 0) // '/c [path]' was NOT specified { if (PathFileExists(cfgPath)) // vendor/conemu-maximus5/ConEmu.xml file exists, copy vendor/conemu-maximus5/ConEmu.xml to config/ConEmu-%COMPUTERNAME%.xml. { if (!CopyFile(cfgPath, cpuCfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/ConEmu-%COMPUTERNAME%.xml! Access Denied." : L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/ConEmu-%COMPUTERNAME%.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } else // vendor/conemu-maximus5/ConEmu.xml config file does not exist, copy config/ConEmu-%COMPUTERNAME%.xml to vendor/conemu-maximus5/ConEmu.xml file { if (!CopyFile(cpuCfgPath, cfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy config/ConEmu-%COMPUTERNAME%.xml file to vendor/conemu-maximus5/ConEmu.xml! Access Denied." : L"Failed to copy config/ConEmu-%COMPUTERNAME%.xml file to vendor/conemu-maximus5/ConEmu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } } else // '/c [path]' was specified, don't copy anything and use existing conemu-%COMPUTERNAME%.xml to start comemu. { if (use_user_cfg == false && PathFileExists(cfgPath) && !PathFileExists(cpuCfgPath)) // vendor/conemu-maximus5/ConEmu.xml file exists, copy vendor/conemu-maximus5/ConEmu.xml to config/ConEmu-%COMPUTERNAME%.xml. { if (!CopyFile(cfgPath, cpuCfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/ConEmu-%COMPUTERNAME%.xml! Access Denied." : L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/ConEmu-%COMPUTERNAME%.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } PathCombine(userConEmuCfgPath, userConfigDirPath, L"ConEmu-%COMPUTERNAME%.xml"); ExpandEnvironmentStrings(userConEmuCfgPath, userConEmuCfgPath, sizeof(userConEmuCfgPath) / sizeof(userConEmuCfgPath[0])); } } else if (PathFileExists(userCfgPath)) // config/user_conemu.xml exists, use it. { if (cfgRoot.length() == 0) // '/c [path]' was NOT specified { if (PathFileExists(cfgPath)) // vendor/conemu-maximus5/ConEmu.xml exists, copy vendor/conemu-maximus5/ConEmu.xml to config/user_conemu.xml. { if (!CopyFile(cfgPath, userCfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/user-conemu.xml! Access Denied." : L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/user-conemu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } else // vendor/conemu-maximus5/ConEmu.xml does not exist, copy config/user-conemu.xml to vendor/conemu-maximus5/ConEmu.xml { if (!CopyFile(userCfgPath, cfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy config/user-conemu.xml file to vendor/conemu-maximus5/ConEmu.xml! Access Denied." : L"Failed to copy config/user-conemu.xml file to vendor/conemu-maximus5/ConEmu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } } else // '/c [path]' was specified, don't copy anything and use existing user_conemu.xml to start comemu. { PathCombine(userConEmuCfgPath, userConfigDirPath, L"user-ConEmu.xml"); } } else if (cfgRoot.length() == 0) // '/c [path]' was NOT specified { if (PathFileExists(cfgPath)) // vendor/conemu-maximus5/ConEmu.xml exists, copy vendor/conemu-maximus5/ConEmu.xml to config/user_conemu.xml { if (!CopyFile(cfgPath, userCfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/user-conemu.xml! Access Denied." : L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/user-conemu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } else // vendor/ConEmu.xml.default config exists, copy Cmder vendor/ConEmu.xml.default file to vendor/conemu-maximus5/ConEmu.xml. { if (!CopyFile(defaultCfgPath, cfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/ConEmu.xml.default file to vendor/conemu-maximus5/ConEmu.xml! Access Denied." : L"Failed to copy vendor/ConEmu.xml.default file to vendor/conemu-maximus5/ConEmu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } } else { if (!CopyFile(defaultCfgPath, cfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/ConEmu.xml.default file to vendor/conemu-maximus5/ConEmu.xml! Access Denied." : L"Failed to copy vendor/ConEmu.xml.default file to vendor/conemu-maximus5/ConEmu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } } } else if (PathFileExists(cfgPath)) // vendor/conemu-maximus5/ConEmu.xml exists, copy vendor/conemu-maximus5/ConEmu.xml to config/user_conemu.xml { if (!CopyFile(cfgPath, userCfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/user-conemu.xml! Access Denied." : L"Failed to copy vendor/conemu-maximus5/ConEmu.xml file to config/user-conemu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } PathCombine(userConEmuCfgPath, userConfigDirPath, L"user-ConEmu.xml"); } else // '/c [path]' was specified and 'vendor/ConEmu.xml.default' config exists, copy Cmder 'vendor/ConEmu.xml.default' file to '[user specified path]/config/user_ConEmu.xml'. { if ( ! CopyFile(defaultCfgPath, userCfgPath, FALSE)) { MessageBox(NULL, (GetLastError() == ERROR_ACCESS_DENIED) ? L"Failed to copy vendor/ConEmu.xml.default file to [user specified path]/config/user_ConEmu.xml! Access Denied." : L"Failed to copy vendor/ConEmu.xml.default file to [user specified path]/config/user_ConEmu.xml!", MB_TITLE, MB_ICONSTOP); exit(1); } PathCombine(userConEmuCfgPath, userConfigDirPath, L"user-ConEmu.xml"); } SYSTEM_INFO sysInfo; GetNativeSystemInfo(&sysInfo); if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu64.exe"); } else { PathCombine(conEmuPath, exeDir, L"vendor\\conemu-maximus5\\ConEmu.exe"); } swprintf_s(args, L"%s /Icon \"%s\"", args, icoPath); if (!streqi(cmderStart.c_str(), L"")) { swprintf_s(args, L"%s /dir \"%s\"", args, cmderStart.c_str()); } if (is_single_mode) { swprintf_s(args, L"%s /single", args); } if (!streqi(cmderTitle.c_str(), L"")) { swprintf_s(args, L"%s /title \"%s\"", args, cmderTitle.c_str()); } if (cfgRoot.length() != 0) { swprintf_s(args, L"%s -loadcfgfile \"%s\"", args, userConEmuCfgPath); } if (!streqi(cmderConEmuArgs.c_str(), L"")) { swprintf_s(args, L"%s %s", args, cmderConEmuArgs.c_str()); } // The `/run` arg and its value MUST be the last arg of ConEmu // see : https://conemu.github.io/en/ConEmuArgs.html // > This must be the last used switch (excepting -new_console and -cur_console) if (!streqi(cmderTask.c_str(), L"")) { swprintf_s(args, L"%s /run {%s}", args, cmderTask.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; si.dwFlags = STARTF_TITLEISAPPID; #endif PROCESS_INFORMATION pi; if (!CreateProcess(conEmuPath, args, NULL, NULL, false, 0, NULL, NULL, &si, &pi)) { MessageBox(NULL, _T("Unable to create the ConEmu process!"), _T("Error"), MB_OK); return; } } 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, wchar_t* keyBaseName, std::wstring cfgRoot, bool single) { wchar_t userConfigDirPath[MAX_PATH] = { 0 }; // First, get the paths we will use wchar_t exePath[MAX_PATH] = { 0 }; wchar_t icoPath[MAX_PATH] = { 0 }; GetModuleFileName(NULL, exePath, sizeof(exePath)); wchar_t commandStr[MAX_PATH + 20] = { 0 }; wchar_t baseCommandStr[MAX_PATH + 20] = { 0 }; if (!single) { swprintf_s(baseCommandStr, L"\"%s\"", exePath); } else { swprintf_s(baseCommandStr, L"\"%s\" /single", exePath); } if (cfgRoot.length() == 0) // '/c [path]' was NOT specified { swprintf_s(commandStr, L"%s \"%%V\"", baseCommandStr); } else { std::copy(cfgRoot.begin(), cfgRoot.end(), userConfigDirPath); userConfigDirPath[cfgRoot.length()] = 0; swprintf_s(commandStr, L"%s /c \"%s\" \"%%V\"", baseCommandStr, userConfigDirPath); } // Now that we have `commandStr`, it's OK to change `exePath`... PathRemoveFileSpec(exePath); PathCombine(icoPath, exePath, L"icons\\cmder.ico"); // Now set the registry keys HKEY root = GetRootKey(opt); 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(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)); FAIL_ON_ERROR(RegSetValue(command, L"", REG_SZ, commandStr, NULL)); RegCloseKey(command); RegCloseKey(cmderKey); RegCloseKey(root); } void UnregisterShellMenu(std::wstring opt, wchar_t* keyBaseName) { HKEY root = GetRootKey(opt); HKEY cmderKey; FAIL_ON_ERROR(RegCreateKeyEx(root, keyBaseName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &cmderKey, NULL)); FAIL_ON_ERROR(RegDeleteTree(cmderKey, NULL)); RegDeleteKeyEx(root, keyBaseName, KEY_ALL_ACCESS, NULL); RegCloseKey(cmderKey); RegCloseKey(root); } struct cmderOptions { std::wstring cmderCfgRoot = L""; std::wstring cmderStart = L""; std::wstring cmderTask = L""; std::wstring cmderTitle = L"Cmder"; std::wstring cmderIcon = L""; std::wstring cmderRegScope = L"USER"; std::wstring cmderConEmuArgs = L""; bool cmderSingle = false; bool cmderUserCfg = true; 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 (cmderOptions.error == false) { 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) { int len = wcslen(szArgList[i + 1]); if (wcscmp(&szArgList[i + 1][len - 1], L"\"") == 0) { szArgList[i + 1][len - 1] = '\0'; } if (PathFileExists(szArgList[i + 1])) { cmderOptions.cmderStart = szArgList[i + 1]; i++; } else { MessageBox(NULL, szArgList[i + 1], L"/START - Folder does not exist!", MB_OK); } } else if (_wcsicmp(L"/task", szArgList[i]) == 0) { cmderOptions.cmderTask = szArgList[i + 1]; i++; } else if (_wcsicmp(L"/title", szArgList[i]) == 0) { cmderOptions.cmderTitle = szArgList[i + 1]; i++; } else if (_wcsicmp(L"/icon", szArgList[i]) == 0) { cmderOptions.cmderIcon = szArgList[i + 1]; i++; } else if (_wcsicmp(L"/single", szArgList[i]) == 0) { cmderOptions.cmderSingle = true; } else if (_wcsicmp(L"/m", szArgList[i]) == 0) { cmderOptions.cmderUserCfg = false; } 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++; } } } /* Used for passing arguments to conemu prog */ else if (_wcsicmp(L"/x", szArgList[i]) == 0) { cmderOptions.cmderConEmuArgs = szArgList[i + 1]; i++; } /* Bare double dash, remaining commandline is for conemu */ else if (_wcsicmp(L"--", szArgList[i]) == 0) { std::wstring cmdline = std::wstring(GetCommandLineW()); auto doubledash = cmdline.find(L" -- "); if (doubledash != std::string::npos) { cmderOptions.cmderConEmuArgs = cmdline.substr(doubledash + 4); } break; } else if (cmderOptions.cmderStart == L"") { int len = wcslen(szArgList[i]); if (wcscmp(&szArgList[i][len - 1], L"\"") == 0) { szArgList[i][len - 1] = '\0'; } if (PathFileExists(szArgList[i])) { cmderOptions.cmderStart = szArgList[i]; i++; } else { cmderOptions.error = true; } } else { cmderOptions.error = true; } } } if (cmderOptions.error == true) { wchar_t validOptions[512]; HMODULE hMod = GetModuleHandle(NULL); LoadString(hMod, IDS_SWITCHES, validOptions, 512); // display list of valid options on unrecognized parameter TaskDialogOpen( L"Unrecognized parameter.", validOptions ); } LocalFree(szArgList); return cmderOptions; } 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); cmderOptions cmderOptions = GetOption(); if (cmderOptions.registerApp == true) { RegisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_BACKGROUND, cmderOptions.cmderCfgRoot, cmderOptions.cmderSingle); RegisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_LISTITEM, cmderOptions.cmderCfgRoot, cmderOptions.cmderSingle); RegisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_DRIVE_PATH_BACKGROUND, cmderOptions.cmderCfgRoot, cmderOptions.cmderSingle); RegisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_DRIVE_PATH_LISTITEM, cmderOptions.cmderCfgRoot, cmderOptions.cmderSingle); } else if (cmderOptions.unRegisterApp == true) { UnregisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_BACKGROUND); UnregisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_PATH_LISTITEM); UnregisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_DRIVE_PATH_BACKGROUND); UnregisterShellMenu(cmderOptions.cmderRegScope, SHELL_MENU_REGISTRY_DRIVE_PATH_LISTITEM); } else if (cmderOptions.error == true) { return 1; } else { StartCmder(cmderOptions.cmderStart, cmderOptions.cmderSingle, cmderOptions.cmderTask, cmderOptions.cmderTitle, cmderOptions.cmderIcon, cmderOptions.cmderCfgRoot, cmderOptions.cmderUserCfg, cmderOptions.cmderConEmuArgs); } return 0; } ================================================ FILE: launcher/src/app.manifest ================================================ Cmder Console Emulator true/pm ================================================ FILE: launcher/src/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Resource.rc // #define IDI_CMDER 101 #define IDS_TITLE 102 #define IDS_SWITCHES 803 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: launcher/src/resource.rc ================================================ /* _ ___ _ __ ___ __| | ___ _ __ / __| '_ ` _ \ / _` |/ _ \ '__| | (__| | | | | | (_| | __/ | \___|_| |_| |_|\__,_|\___|_| ============================================================================= The Cmder Console Emulator Project */ #include "resource.h" #include "version.rc2" #include "strings.rc2" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h\0" END 2 TEXTINCLUDE BEGIN "#include ""winres.h""\r\n" "\0" END 3 TEXTINCLUDE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_CMDER ICON "..\\..\\icons\\cmder.ico" #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION CMDER_MAJOR_VERSION,CMDER_MINOR_VERSION,CMDER_REVISION_VERSION,CMDER_BUILD_VERSION PRODUCTVERSION CMDER_MAJOR_VERSION,CMDER_MINOR_VERSION,CMDER_REVISION_VERSION,CMDER_BUILD_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS (CMDER_DEBUGFLAG | CMDER_BUILDFLAGS) FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_APP FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "100904b0" BEGIN VALUE "CompanyName", CMDER_COMPANY_NAME_STR "\0" VALUE "FileDescription", CMDER_FILE_DESCRIPTION_STR "\0" VALUE "FileVersion", CMDER_VERSION_STR "\0" VALUE "InternalName", CMDER_INTERNAL_NAME_STR "\0" VALUE "LegalCopyright", "Copyright (C) " CMDER_COPYRIGHT_YEAR_STR " " CMDER_COMPANY_NAME_STR "\0" VALUE "OriginalFilename", CMDER_ORIGINAL_FILENAME_STR "\0" VALUE "ProductName", CMDER_PRODUCT_NAME_STR "\0" VALUE "ProductVersion", CMDER_VERSION_STR "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x1009, 1200 END END ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: launcher/src/strings.rc2 ================================================ ///////////////////////////////////////////////////////////////////////////// // Corresponding ids should be defined in `resource.h` file. STRINGTABLE { IDS_TITLE "Cmder Launcher" IDS_SWITCHES L"Valid options:\n\n /c [CMDER User Root Path]\n /task [ConEmu Task Name]\n /icon [CMDER Icon Path]\n [/start [Start in Path] | [Start in Path]]\n /single\n /m\n /x [ConEmu extra arguments]\n\nor, either:\n /register [USER | ALL]\n /unregister [USER | ALL]" } ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: launcher/src/version.rc2.sample ================================================ /** * WARNING: This file should NOT be manually modified! * The contents will be automatically generated using the `.ps1` PowerShell scripts, * during builds by the CI. */ ///////////////////////////////////////////////////////////////////////////// // Define the version numbers and build information manually here: #define CMDER_MAJOR_VERSION {Cmder-Major-Version} #define CMDER_MINOR_VERSION {Cmder-Minor-Version} #define CMDER_REVISION_VERSION {Cmder-Revision-Version} #define CMDER_BUILD_VERSION {Cmder-Build-Version} #define CMDER_VERSION_STR {Cmder-Version-Str} #define CMDER_PRODUCT_NAME_STR "Cmder" #define CMDER_FILE_DESCRIPTION_STR "Cmder: Lovely Console Emulator." #define CMDER_INTERNAL_NAME_STR "Cmder" #define CMDER_ORIGINAL_FILENAME_STR "Cmder.exe" #define CMDER_COMPANY_NAME_STR "Samuel Vasko" #define CMDER_COPYRIGHT_YEAR_STR "2016" #define CMDER_DEBUGFLAG 0x0L // set to 0x1L to enable debug mode #define CMDER_BUILDFLAGS 0x0L ///////////////////////////////////////////////////////////////////////////// ================================================ FILE: opt/Readme.md ================================================ ## Bin This folder is for optional user packages and will not be automatically injected into the PATH. Use `%lib_path% enhance_path "%cmder_root%\[path to folder]"` in `%cmder_root%\config\user_profile.cmd` or `%cmder_root%\config\profile.d\*.cmd` to add to the path. ================================================ FILE: packignore ================================================ launcher .gitignore .gitattributes .git .github .vs .vscode .idea *.md build scripts config\.history packignore Thumbs.db icons\cmder_icon.psd icons\icon_16*.png icons\icon_32*.png icons\icon_48*.png icons\icon_256*.png Cmder.bat vendor\tmp appveyor.yml vendor\cmder.sh vendor\git-prompt.sh config\user-* clink_history* *.log ================================================ FILE: scripts/build.ps1 ================================================ <# .Synopsis Build Cmder .DESCRIPTION Use this script to build your own edition of Cmder This script builds dependencies from current vendor/sources.json file and unpacks them. You will need to make this script executable by setting your Powershell Execution Policy to Remote signed Then unblock the script for execution with UnblockFile .\build.ps1 .EXAMPLE .\build.ps1 Executes the default build for Cmder; ConEmu, clink. This is equivalent to the "minimum" style package in the releases .EXAMPLE .\build.ps1 -Compile Recompile the launcher executable if you have the requisite build tools for C++ installed. .EXAMPLE .\build.ps1 -Compile -NoVendor Skip all downloads and only build launcher. .EXAMPLE .\build -verbose Execute the build and see what's going on. .EXAMPLE .\build.ps1 -SourcesPath '~/custom/vendors.json' Build Cmder with your own packages. See vendor/sources.json for the syntax you need to copy. .NOTES AUTHORS Samuel Vasko, Jack Bennett Part of the Cmder project. .LINK http://cmder.app/ - Project Home #> [CmdletBinding(SupportsShouldProcess = $true)] Param( # CmdletBinding will give us; # -verbose switch to turn on logging and # -whatif switch to not actually make changes # Path to the vendor configuration source file [string]$sourcesPath = "$PSScriptRoot\..\vendor\sources.json", # Vendor folder location [string]$saveTo = "$PSScriptRoot\..\vendor\", # Launcher folder location [string]$launcher = "$PSScriptRoot\..\launcher", # Config folder location [string]$config = "$PSScriptRoot\..\config", # Using this option will skip all downloads, if you only need to build launcher [switch]$noVendor, # Build launcher if you have MSBuild tools installed [switch]$Compile ) # Get the scripts and Cmder root dirs we are building in. $cmder_root = Resolve-Path "$PSScriptRoot\.." # Dot source util functions into this scope . "$PSScriptRoot\utils.ps1" $ErrorActionPreference = "Stop" if ($Compile) { # Check for requirements Ensure-Executable "msbuild" # Get the version string $version = Get-VersionStr Push-Location -Path $launcher Create-RC $version ($launcher + '\src\version.rc2') Write-Verbose "Building the launcher..." # Reference: https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference msbuild CmderLauncher.vcxproj /t:Clean,Build /p:configuration=Release /m if ($LastExitCode -ne 0) { throw "MSBuild failed to build the launcher executable." } Pop-Location } if (-not $noVendor) { # Check for requirements Ensure-Exists $sourcesPath Ensure-Executable "7z" # Get the vendor sources $sources = Get-Content $sourcesPath | Out-String | ConvertFrom-Json Push-Location -Path $saveTo New-Item -Type Directory -Path (Join-Path $saveTo "/tmp/") -ErrorAction SilentlyContinue >$null $vend = $pwd # Preserve modified (by user) ConEmu setting file if ($config -ne "") { $ConEmuXml = Join-Path $saveTo "conemu-maximus5\ConEmu.xml" if (Test-Path $ConEmuXml -pathType leaf) { $ConEmuXmlSave = Join-Path $config "ConEmu.xml" Write-Verbose "Backup '$ConEmuXml' to '$ConEmuXmlSave'" Copy-Item $ConEmuXml $ConEmuXmlSave } else { $ConEmuXml = "" } } else { $ConEmuXml = "" } # Kill ssh-agent.exe if it is running from the $env:cmder_root we are building foreach ($ssh_agent in $(Get-Process ssh-agent -ErrorAction SilentlyContinue)) { if ([string]$($ssh_agent.path) -Match [string]$cmder_root.replace('\', '\\')) { Write-Verbose $("Stopping " + $ssh_agent.path + "!") Stop-Process $ssh_agent.id } } foreach ($s in $sources) { Write-Verbose "Getting vendored $($s.name) $($s.version)..." # We do not care about the extensions/type of archive $tempArchive = "tmp/$($s.name).tmp" Delete-Existing $tempArchive Delete-Existing $s.name Download-File -Url $s.url -File $vend\$tempArchive -ErrorAction Stop Extract-Archive $tempArchive $s.name if ((Get-ChildItem $s.name).Count -eq 1) { Flatten-Directory($s.name) } # Write current version to .cmderver file, for later. "$($s.version)" | Out-File "$($s.name)/.cmderver" } # Restore ConEmu user configuration if ($ConEmuXml -ne "") { Write-Verbose "Restore '$ConEmuXmlSave' to '$ConEmuXml'" Copy-Item $ConEmuXmlSave $ConEmuXml } # Put vendor\cmder.sh in /etc/profile.d so it runs when we start bash or mintty if ( (Test-Path $($saveTo + "git-for-windows/etc/profile.d") ) ) { Write-Verbose "Adding cmder.sh /etc/profile.d" Copy-Item $($saveTo + "cmder.sh") $($saveTo + "git-for-windows/etc/profile.d/cmder.sh") } # Replace /etc/profile.d/git-prompt.sh with cmder lambda prompt so it runs when we start bash or mintty if ( !(Test-Path $($saveTo + "git-for-windows/etc/profile.d/git-prompt.sh.bak") ) ) { Write-Verbose "Replacing /etc/profile.d/git-prompt.sh with our git-prompt.sh" Move-Item $($saveTo + "git-for-windows/etc/profile.d/git-prompt.sh") $($saveTo + "git-for-windows/etc/profile.d/git-prompt.sh.bak") Copy-Item $($saveTo + "git-prompt.sh") $($saveTo + "git-for-windows/etc/profile.d/git-prompt.sh") } Pop-Location } if (-not $Compile -or $noVendor) { Write-Warning "You are not building the full project, Use -Compile without -noVendor" Write-Warning "This cannot be a release. Test build only!" return } Write-Verbose "Successfully built Cmder v$version!" if ( $Env:APPVEYOR -eq 'True' ) { Add-AppveyorMessage -Message "Building Cmder v$version was successful." -Category Information } if ( $Env:GITHUB_ACTIONS -eq 'true' ) { Write-Output "::notice title=Build Complete::Building Cmder v$version was successful." } Write-Host -ForegroundColor green "All good and done!" ================================================ FILE: scripts/pack.ps1 ================================================ <# .Synopsis Pack Cmder .DESCRIPTION Use this script to pack Cmder into release archives You will need to make this script executable by setting your Powershell Execution Policy to Remote signed Then unblock the script for execution with UnblockFile .\pack.ps1 .EXAMPLE .\pack.ps1 Creates default archives for Cmder .EXAMPLE .\pack.ps1 -verbose Creates default archives for Cmder with plenty of information .NOTES AUTHORS Samuel Vasko, Jack Bennett, Martin Kemp Part of the Cmder project. .LINK https://github.com/cmderdev/cmder - Project Home #> [CmdletBinding(SupportsShouldProcess = $true)] Param( # CmdletBinding will give us; # -verbose switch to turn on logging and # -whatif switch to not actually make changes # Path to the vendor configuration source file [string]$cmderRoot = "$PSScriptRoot\..", # Vendor folder locaton [string]$saveTo = "$PSScriptRoot\..\build" ) $cmderRoot = Resolve-Path $cmderRoot . "$PSScriptRoot\utils.ps1" $ErrorActionPreference = "Stop" Ensure-Executable "7z" $targets = @{ "cmder.7z" = "-t7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -myx=7 -mqs=on"; "cmder.zip" = "-mm=Deflate -mfb=128 -mpass=3"; "cmder_mini.zip" = "-xr!`"vendor\git-for-windows`""; } Push-Location -Path $cmderRoot Delete-Existing "$cmderRoot\Version*" Delete-Existing "$cmderRoot\build\*" if (-not (Test-Path -PathType container $saveTo)) { (New-Item -ItemType Directory -Path $saveTo) | Out-Null } $saveTo = Resolve-Path $saveTo $version = Get-VersionStr (New-Item -ItemType file "$cmderRoot\Version $version") | Out-Null if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { Write-Verbose "Packing Cmder $version in $saveTo..." $excluded = (Get-Content -Path "$cmderRoot\packignore") -Split [System.Environment]::NewLine | Where-Object { $_ } Get-ChildItem $cmderRoot -Force -Exclude $excluded } foreach ($t in $targets.GetEnumerator()) { Create-Archive "$cmderRoot" "$saveTo\$($t.Name)" $t.Value $hash = (Digest-Hash "$saveTo\$($t.Name)") Add-Content -path "$saveTo\hashes.txt" -value ($t.Name + ' ' + $hash) } Pop-Location ================================================ FILE: scripts/update.ps1 ================================================ <# .Synopsis Update Cmder vendored dependencies .DESCRIPTION This script updates dependencies to the latest version in vendor/sources.json file. You will need to make this script executable by setting your Powershell Execution Policy to Remote signed Then unblock the script for execution with UnblockFile .\build.ps1 .EXAMPLE .\build.ps1 Updates the dependency sources in the default location, the vendor/sources.json file. .EXAMPLE .\build -verbose Updates the dependency sources and see what's going on. .EXAMPLE .\build.ps1 -SourcesPath '~/custom/vendors.json' Specify the path to update dependency sources file at. .NOTES AUTHORS David Refoua Part of the Cmder project. .LINK http://cmder.app/ - Project Home #> [CmdletBinding(SupportsShouldProcess = $true)] Param( # CmdletBinding will give us; # -verbose switch to turn on logging and # -whatif switch to not actually make changes # Path to the vendor configuration source file [string]$sourcesPath = "$PSScriptRoot\..\vendor\sources.json", # Include pre-release versions (RC, beta, alpha, etc.) # By default, only stable releases are considered [switch]$IncludePrerelease = $false ) # Get the root directory of the cmder project. $cmder_root = Resolve-Path "$PSScriptRoot\.." # Dot source util functions into this scope . "$PSScriptRoot\utils.ps1" $ErrorActionPreference = "Stop" # Attempts to match the current link with the new link, returning the count of matching characters. function Match-Filenames { param ( $url, $downloadUrl, $fromEnd ) $filename = [System.IO.Path]::GetFileName($url) $filenameDownload = [System.IO.Path]::GetFileName($downloadUrl) $position = 0 if ([String]::IsNullOrEmpty($filename) -or [String]::IsNullOrEmpty($filenameDownload)) { throw "Either one or both filenames are empty!" } if ($fromEnd) { $arr = $filename -split "" [array]::Reverse($arr) $filename = $arr -join '' $arr = $filenameDownload -split "" [array]::Reverse($arr) $filenameDownload = $arr -join '' } while ($filename.Substring($position, 1) -eq $filenameDownload.Substring($position, 1)) { $position++ if ( ($position -ge $filename.Length) -or ($position -ge $filenameDownload.Length) ) { break } } return $position } # Checks if a release is a pre-release based on GitHub API flag and version tag keywords # Pre-release keywords include: -rc (release candidate), -beta, -alpha, -preview, -pre function Test-IsPrerelease { param ( [Parameter(Mandatory = $true)] $release ) # Check if marked as pre-release by GitHub if ($release.prerelease -eq $true) { return $true } # Check for common pre-release keywords in tag name # This catches versions like v2.50.0-rc, v1.0.0-beta, v1.0.0-alpha, etc. $prereleaseKeywords = @('-rc', '-beta', '-alpha', '-preview', '-pre') foreach ($keyword in $prereleaseKeywords) { if ($release.tag_name -ilike "*$keyword*") { return $true } } return $false } # Uses the GitHub api in order to fetch the current download links for the latest releases of the repo. function Fetch-DownloadUrl { param ( [Parameter(Mandatory = $true)] $urlStr, [Parameter(Mandatory = $false)] [bool]$includePrerelease = $false ) $url = [uri] $urlStr if ((-not $url) -or ($null -eq $url) -or ($url -eq '')) { throw "Failed to parse url: $urlStr" } if (-not ("http", "https" -contains $url.Scheme)) { throw "unknown source scheme: $($url.Scheme)" } if (-not ($url.Host -ilike "*github.com")) { throw "unknown source domain: $($url.Host)" } $p = $url.Segments.Split([Environment]::NewLine) $headers = @{} if ($env:GITHUB_TOKEN) { $headers["Authorization"] = "token $($env:GITHUB_TOKEN)" } # Api server for GitHub $urlHost = "api.github.com" # Path for releases end-point $urlPath = [IO.Path]::Combine('repos', $p[1], $p[2], 'releases').Trim('/') $apiUrl = [uri] (New-Object System.UriBuilder -ArgumentList $url.Scheme, $urlHost, -1, $urlPath).Uri $info = Invoke-RestMethod -Uri $apiUrl -Headers $headers $downloadLinks = (New-Object System.Collections.Generic.List[System.Object]) $charCount = 0 if (-not ($info -is [array])) { throw "The response received from API server is invalid" } :loop foreach ($i in $info) { # Skip pre-release versions unless explicitly included # Pre-releases include RC (Release Candidate), beta, alpha, and other test versions if (-not $includePrerelease -and (Test-IsPrerelease $i)) { Write-Verbose "Skipping pre-release version: $($i.tag_name)" continue } if (-not ($i.assets -is [array])) { continue } foreach ($a in $i.assets) { if ([String]::IsNullOrEmpty($a.browser_download_url)) { continue } # Skip some download links as we're not interested in them if ( $a.browser_download_url -ilike "*_symbols*" ) { continue } $score = Match-Filenames $url $a.browser_download_url # Skip links that don't match or are less similar if ( ($score -eq 0) -or ($score -lt $charCount) ) { continue } # If we reach the same download link as we have if ( $score -eq [System.IO.Path]::GetFileName($url).Length ) { } $charCount = $score $downloadLinks.Add($a.browser_download_url) } # If at least one download link was found, don't continue with older releases if ( $downloadLinks.Length -gt 0 ) { break :loop } } # Special case for archive downloads of repository if (($null -eq $downloadLinks) -or (-not $downloadLinks)) { if ((($p | ForEach-Object { $_.Trim('/') }) -contains "archive")) { # Find the first release that matches our pre-release filtering criteria $selectedRelease = $null foreach ($release in $info) { # Apply the same filtering logic if (-not $includePrerelease -and (Test-IsPrerelease $release)) { continue } # Use the first release that passes the filter $selectedRelease = $release break } if ($selectedRelease -and $selectedRelease.tag_name) { for ($i = 0; $i -lt $p.Length; $i++) { if ($p[$i].Trim('/') -eq "archive") { $p[$i + 1] = $selectedRelease.tag_name + ".zip" $downloadLinks = $url.Scheme + "://" + $url.Host + ($p -join '') return $downloadLinks } } } } return '' } $temp = $downloadLinks | Where-Object { (Match-Filenames $url $_) -eq $charCount } $downloadLinks = (New-Object System.Collections.Generic.List[System.Object]) $charCount = 0 foreach ($l in $temp) { $score = Match-Filenames $url $l true if ( ($score -eq 0) -or ($score -lt $charCount) ) { continue } $charCount = $score } $downloadLinks = $temp | Where-Object { (Match-Filenames $url $_ true) -eq $charCount } if (($null -eq $downloadLinks) -or (-not $downloadLinks)) { throw "No suitable download links matched for the url!" } if (-not($downloadLinks -is [String])) { throw "Found multiple matches for the same url:`n" + $downloadLinks } return $downloadLinks } $count = 0 # Read the current sources content $sources = Get-Content $sourcesPath | Out-String | ConvertFrom-Json foreach ($s in $sources) { Write-Verbose "Updating sources link for $($s.name)..." Write-Verbose "Old Link: $($s.url)" $downloadUrl = Fetch-DownloadUrl $s.url -includePrerelease $IncludePrerelease if (($null -eq $downloadUrl) -or ($downloadUrl -eq '')) { Write-Verbose "No new links were found" continue } Write-Verbose "Link: $downloadUrl" $url = [uri] $downloadUrl $version = '' if (($url.Segments[-3] -eq "download/") -and ($url.Segments[-2].StartsWith("v"))) { $version = $url.Segments[-2].TrimStart('v').TrimEnd('/') } if (($url.Segments[-2] -eq "archive/")) { $version = [System.IO.Path]::GetFileNameWithoutExtension($url.Segments[-1].TrimStart('v').TrimEnd('/')) } if ($version -eq '') { throw "Unable to extract version from url string" } Write-Verbose "Version: $version" if ( $s.version -ne $version ) { # if ( ([System.Version] $s.version) -gt ([System.Version] $version) ) { # throw "The current version $($s.version) is already newer than the found version $version!" # } $count++ } $s.url = $downloadUrl $s.version = $version } $sources | ConvertTo-Json | Set-Content $sourcesPath if ($count -eq 0) { Write-Host -ForegroundColor yellow "No new releases were found." return } if ($Env:APPVEYOR -eq 'True') { Add-AppveyorMessage -Message "Successfully updated $count dependencies." -Category Information } if ($Env:GITHUB_ACTIONS -eq 'true') { Write-Output "::notice title=Task Complete::Successfully updated $count dependencies." } Write-Host -ForegroundColor green "Successfully updated $count dependencies." ================================================ FILE: scripts/utils.ps1 ================================================ function Ensure-Exists($path) { if (-not (Test-Path $path)) { Write-Error "Missing required $path! Ensure it is installed" exit 1 } return $true > $null } function Ensure-Executable($command) { try { Get-Command $command -ErrorAction Stop > $null } catch { if( ($command -eq "7z") -and (Test-Path "$env:programfiles\7-zip\7z.exe") ){ Set-Alias -Name "7z" -Value "$env:programfiles\7-zip\7z.exe" -Scope script } elseif( ($command -eq "7z") -and (Test-Path "$env:programw6432\7-zip\7z.exe") ) { Set-Alias -Name "7z" -Value "$env:programw6432\7-zip\7z.exe" -Scope script } else { Write-Error "Missing $command! Ensure it is installed and on in the PATH" exit 1 } } } function Delete-Existing($path) { if (Test-Path $path) { Write-Verbose "Remove existing $path" } Remove-Item -Recurse -Force $path -ErrorAction SilentlyContinue } function Extract-Archive($source, $target) { Write-Verbose $("Extracting Archive '$cmder_root\vendor\" + $source.replace('/','\') + " to '$cmder_root\vendor\$target'") Invoke-Expression "7z x -y -o`"$($target)`" `"$source`" > `$null" if ($LastExitCode -ne 0) { Write-Error "Extracting of $source failed" } Remove-Item $source } function Create-Archive($source, $target, $params) { $command = "7z a -x@`"$source\packignore`" $params `"$target`" `"*`" > `$null" Write-Verbose "Creating Archive from '$source' in '$target' with parameters '$params'" Push-Location $source Invoke-Expression $command Pop-Location if ($LastExitCode -ne 0) { Write-Error "Compressing $source failed" } } # If directory contains only one child directory # Flatten it instead function Flatten-Directory($name) { $name = Resolve-Path $name $moving = "$($name)_moving" Rename-Item $name -NewName $moving Write-Verbose "Flattening the '$name' directory..." $child = (Get-ChildItem $moving)[0] | Resolve-Path Move-Item -Path $child -Destination $name Remove-Item -Recurse $moving } function Digest-Hash($path) { if (Get-Command Get-FileHash -ErrorAction SilentlyContinue) { return (Get-FileHash -Algorithm SHA256 -Path $path).Hash } return Invoke-Expression "md5sum $path" } function Set-GHVariable { param( [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$Value ) Write-Verbose "Setting CI variable $Name to $Value" -Verbose if ($env:GITHUB_ENV) { Write-Output "$Name=$Value" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 } } function Get-GHTempPath { $temp = [System.IO.Path]::GetTempPath() if ($env:RUNNER_TEMP) { $temp = $env:RUNNER_TEMP } Write-Verbose "Get CI Temp path: $temp" -Verbose return $temp } function Get-VersionStr { # Clear existing variable if ($string) { Clear-Variable -name string } # Determine if git is available if (Get-Command "git.exe" -ErrorAction SilentlyContinue) { # Determine if the current directory is a git repository $GitPresent = Invoke-Expression "git rev-parse --is-inside-work-tree" -ErrorAction SilentlyContinue if ( $GitPresent -eq 'true' ) { $string = Invoke-Expression "git describe --abbrev=0 --tags" } } # Fallback used when Git is not available if ( -not($string) ) { $string = Parse-Changelog ($PSScriptRoot + '\..\' + 'CHANGELOG.md') } # Add build number, if AppVeyor is present if ( $Env:APPVEYOR -eq 'True' ) { $string = $string + '.' + $Env:APPVEYOR_BUILD_NUMBER } elseif ( $Env:GITHUB_ACTIONS -eq 'true' ) { $string = $string + '.' + $Env:GITHUB_RUN_NUMBER } # Remove starting 'v' characters $string = $string -replace '^v+','' # normalize version string return $string } function Parse-Changelog($file) { # Define the regular expression to match the version string from changelog [regex]$regex = '^## \[(?[\w\-\.]+)\]\([^\n()]+\)\s+\([^\n()]+\)$'; # Find the first match of the version string which means the latest version $version = Select-String -Path $file -Pattern $regex | Select-Object -First 1 | ForEach-Object { $_.Matches.Groups[1].Value } return $version } function Create-RC($string, $path) { $version = $string + '.0.0.0.0' # padding for version string if ( !(Test-Path "$path.sample") ) { throw "Invalid path provided for resources file." } $resource = Get-Content -Path "$path.sample" $pattern = @( "Cmder-Major-Version", "Cmder-Minor-Version", "Cmder-Revision-Version", "Cmder-Build-Version" ) $index = 0 # Replace all non-numeric characters to dots and split to array $version = $version -replace '[^0-9]+','.' -split '\.' foreach ($fragment in $version) { if ( !$fragment ) { break } elseif ($index -le $pattern.length) { $resource = $resource.Replace( "{" + $pattern[$index++] + "}", $fragment ) } } # Add the version string $resource = $resource.Replace( "{Cmder-Version-Str}", '"' + $string + '"' ) # Write the results Set-Content -Path $path -Value $resource } function Register-Cmder() { [CmdletBinding()] Param ( # Text for the context menu item. $MenuText = "Cmder Here" , # Defaults to the current Cmder directory when run from Cmder. $PathToExe = (Join-Path $env:CMDER_ROOT "cmder.exe") , # Commands the context menu will execute. $Command = "%V" , # Defaults to the icons folder in the Cmder package. $icon = (Split-Path $PathToExe | Join-Path -ChildPath 'icons/cmder.ico') ) Begin { New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null } Process { New-Item -Path "HKCR:\Directory\Shell\Cmder" -Force -Value $MenuText New-ItemProperty -Path "HKCR:\Directory\Shell\Cmder" -Force -Name "Icon" -Value `"$icon`" New-ItemProperty -Path "HKCR:\Directory\Shell\Cmder" -Force -Name "NoWorkingDirectory" New-Item -Path "HKCR:\Directory\Shell\Cmder\Command" -Force -Value "`"$PathToExe`" `"$Command`" " New-Item -Path "HKCR:\Directory\Background\Shell\Cmder" -Force -Value $MenuText New-ItemProperty -Path "HKCR:\Directory\Background\Shell\Cmder" -Force -Name "Icon" -Value `"$icon`" New-ItemProperty -Path "HKCR:\Directory\Background\Shell\Cmder" -Force -Name "NoWorkingDirectory" New-Item -Path "HKCR:\Directory\Background\Shell\Cmder\Command" -Force -Value "`"$PathToExe`" `"$Command`" " } End { Remove-PSDrive -Name HKCR } } function Unregister-Cmder { Begin { New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT > $null } Process { Remove-Item -Path "HKCR:\Directory\Shell\Cmder" -Recurse Remove-Item -Path "HKCR:\Directory\Background\Shell\Cmder" -Recurse } End { Remove-PSDrive -Name HKCR } } function Download-File { param ( $Url, $File ) [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $useBitTransfer = $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and ($PSVersionTable.PSVersion.Major -le 5) $File = $File -replace "/", "\" try { if ($useBitTransfer) { Start-BitsTransfer -Source $Url -Destination $File -DisplayName "Downloading '$Url' to $File" return } } catch { Write-Error "Failed to download file using BITS, reason: $_`nUsing fallback method instead...`n" -ErrorAction:Continue } Write-Verbose "Downloading from $Url to $File`n" $wc = New-Object System.Net.WebClient if ($env:https_proxy) { $wc.proxy = (New-Object System.Net.WebProxy($env:https_proxy)) } $wc.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials; $wc.DownloadFile($Url, $File) } ================================================ FILE: vendor/ConEmu.xml.default ================================================ ================================================ FILE: vendor/Readme.md ================================================ ## Vendor Third parties software & init script. ================================================ FILE: vendor/bin/alias.cmd ================================================ @echo off if "%ALIASES%" == "" ( set ALIASES="%CMDER_ROOT%\config\user_aliases.cmd" ) setlocal enabledelayedexpansion if "%~1" == "" echo Use /? for help & echo. & goto :p_show :: check command usage rem #region parseargument goto parseargument :do_shift shift :parseargument set currentarg=%~1 if /i "%currentarg%" equ "/f" ( set ALIASES=%~2 set _f=%~2 shift goto :do_shift ) else if /i "%currentarg%" == "/reload" ( goto :p_reload ) else if "%currentarg%" equ "/H" ( goto :p_help ) else if "%currentarg%" equ "/h" ( goto :p_help ) else if "%currentarg%" equ "/?" ( goto :p_help ) else if /i "%currentarg%" equ "/d" ( if "%~2" neq "" ( if "%~3" equ "" ( :: /d flag for delete existing alias call :p_del %~2 shift goto :eof ) ) ) else if "%currentarg%" neq "" ( if "%~2" equ "" ( :: Show the specified alias doskey /macros | %WINDIR%\System32\findstr /b %currentarg%= && exit /b echo insufficient parameters. goto :p_help ) else ( :: handle quotes within command definition, e.g. quoted long file names set _x=%* ) ) rem #endregion parseargument if "%ALIASES%" neq "%CMDER_ROOT%\config\user_aliases.cmd" ( set _x=!_x:/f "%ALIASES%" =! if not exist "%ALIASES%" ( echo ;= @echo off>"%ALIASES%" echo ;= rem Call DOSKEY and use this file as the macrofile>>"%ALIASES%" echo ;= %%SystemRoot%%\system32\doskey /listsize=1000 /macrofile=%%0%%>>"%ALIASES%" echo ;= rem In batch mode, jump to the end of the file>>"%ALIASES%" echo ;= goto:eof>>"%ALIASES%" echo ;= Add aliases below here>>"%ALIASES%" ) ) :: validate alias for /f "delims== tokens=1,* usebackq" %%G in (`echo "!_x!"`) do ( set alias_name=%%G set alias_value=%%H ) :: leading quotes added while validating set alias_name=!alias_name:~1! :: trailing quotes added while validating set alias_value=!alias_value:~0,-1! ::remove spaces set _temp=%alias_name: =% if not ["%_temp%"] == ["%alias_name%"] ( echo Your alias name can not contain a space endlocal exit /b ) :: replace already defined alias %WINDIR%\System32\findstr /b /l /v /i "%alias_name%=" "%ALIASES%" >> "%ALIASES%.tmp" echo %alias_name%=%alias_value% >> "%ALIASES%.tmp" && type "%ALIASES%.tmp" > "%ALIASES%" & @del /f /q "%ALIASES%.tmp" doskey /macrofile="%ALIASES%" endlocal exit /b :p_del set del_alias=%~1 %WINDIR%\System32\findstr /b /l /v /i "%del_alias%=" "%ALIASES%" >> "%ALIASES%.tmp" type "%ALIASES%".tmp > "%ALIASES%" & @del /f /q "%ALIASES%.tmp" doskey %del_alias%= doskey /macrofile="%ALIASES%" goto:eof :p_reload doskey /macrofile="%ALIASES%" echo Aliases reloaded exit /b :p_show doskey /macros|%WINDIR%\System32\findstr /v /r "^;=" | sort exit /b :p_help echo.Usage: echo. echo. alias [options] [alias=alias command] echo. echo.Options: echo. echo. Note: Options MUST precede the alias definition. echo. echo. /d [alias] Delete an [alias]. echo. /f [macrofile] Path to the [macrofile] you want to store the new alias in. echo. Default: %cmder_root%\config\user_aliases.cmd echo. /reload Reload the aliases file. Can be used with /f argument. echo. Default: %cmder_root%\config\user_aliases.cmd echo. echo. If alias is called with no parameters, it will display the list of existing echo. aliases. echo. echo. In the alias command, you can use the following notations: echo. echo. ^^^^^^^^%% - %% signs in env vars must be escaped if preserving the variable echo. in he alias is desired. Variables in aliases surrounded by double echo. quotes only require '^^%%' vs '^^^^^^^^%%' echo. $* - allows the alias to assume all the parameters of the supplied echo. command. echo. $1-$9 - Allows you to separate parameter by number, much like %%1 in echo. batch. echo. $T - Command separator, allowing you to string several commands echo. together into one alias. echo. echo. For more information, read DOSKEY /? exit /b ================================================ FILE: vendor/bin/cexec.cmd ================================================ @echo off if "%~1" equ "" goto :wrongSyntax if not defined CMDER_USER_FLAGS ( :: in case nothing was passed to %CMDER_USER_FLAGS% set "CMDER_USER_FLAGS= " ) set "feNot=false" goto :parseArgument :doShift shift :parseArgument set "currenArgu=%~1" if /i "%currenArgu%" equ "/setPath" ( set ccall=call "%~dp0cexec.cmd" set cexec="%~dp0cexec.cmd" ) else if /i "%currenArgu%" == "/?" ( goto :help ) else if /i "%currenArgu%" equ "/help" ( goto :help ) else if /i "%currenArgu%" equ "/h" ( goto :help ) else if /i "%currenArgu%" equ "NOT" ( set "feNot=true" goto :doShift ) else ( if "%~1" equ "" goto :wrongSyntax if "%~2" equ "" goto :wrongSyntax set "feFlagName=%~1" set "feCommand=%~2" if not "%~3" equ "" ( set "feParam=%~3" ) goto :detect ) :detect :: to avoid erroneous deteciton like "/do" "/doNOT", which both have a "/do" :: we added a space after the flag name, like "/do ", which won't match "/doN" set "feFlagName=%feFlagName% " :: echo. :: echo %CMDER_USER_FLAGS% :: echo %feNOT% :: echo %feFlagName% :: echo %feCommand% :: echo %feParam% :: echo. echo %CMDER_USER_FLAGS% | %WINDIR%\System32\find /i "%feFlagName%">nul if "%ERRORLEVEL%" == "0" ( if "%feNOT%" == "false" ( endlocal && call %feCommand% %feParam% exit /b 0 ) ) else ( if "%feNOT%" == "true" ( endlocal && call %feCommand% %feParam% exit /b 0 ) ) endlocal exit /b 1 :wrongSyntax echo The syntax of the command is incorrect. echo. echo use /? for help echo. endlocal exit /b :help echo. echo CExec - Conditional Exec echo. echo Handles with custom arguments for cmder's init.bat. echo written by xiazeyu, inspired DRSDavidSoft. echo. echo Usage: echo. echo cexec /setPath [NOT] flagName command/program [parameters] echo. echo /setPath Generate a global variables %%ccall%% and %%cexec%% for echo quicker use. Following arguments will be ignored. echo. echo NOT Specifies that cexec should carry out echo the command only if the flag is missing. echo. echo /[flagName] Specifies which flag name is to detect. It's recommended echo to use a pair of double quotation marks to wrap echo your flag name to avoid exceed expectation. echo. echo command/program Specifies the command to carry out if the echo argument name is detected. It's recommended to echo use a pair of double quotation marks to echo wrap your command to avoid exceed expectation. echo. echo parameters These are the parameters passed to the command/program. echo It's recommended to use a pair of double quotation marks echo to wrap your flag name to avoid exceed expectation. echo. echo Examples: echo. echo These examples are expected to be written in %cmder_root%/config/user-profile.cmd echo CExec evaluates the environment variable "CMDER_USER_FLAGS" and conditionally echo caries out actions based on flags that are passed. echo. echo Case 1: echo. echo The following command in `user_profile.cmd` would execute "notepad.exe" and continue running the `user_profile.cmd` echo. echo "%ccall%" "/startNotepad" "start" "notepad.exe" echo. echo If you pass parameter to init.bat like: echo. echo init.bat /startNotepad echo. echo Case 2: echo. echo The following command in `user_profile.cmd` would execute "notepad.exe" and stop running the `user_profile.cmd` echo. echo "%cexec%" NOT "/dontStartNotepad" "start" "notepad.exe" echo. echo UNLESS you pass parameter to init.bat like: echo. echo init.bat /dontStartNotepad echo. endlocal exit /b ================================================ FILE: vendor/bin/cmder_diag.cmd ================================================ @echo off (echo. echo ------------------------------------ echo set echo ------------------------------------ set echo. echo ------------------------------------ echo where git echo ------------------------------------ where git echo. echo ------------------------------------ echo where clink echo ------------------------------------ where clink echo. echo ------------------------------------ echo systeminfo echo ------------------------------------ systeminfo echo ------------------------------------ echo dir "%cmder_root%" echo ------------------------------------ dir "%cmder_root%" echo. echo ------------------------------------ echo dir "%cmder_root%\vendor" echo ------------------------------------ dir "%cmder_root%\vendor" echo. echo ------------------------------------ echo dir /s "%cmder_root%\bin" echo ------------------------------------ dir /s "%cmder_root%\bin" echo. echo ------------------------------------ echo dir /s "%cmder_root%\config" echo ------------------------------------ dir /s "%cmder_root%\config" echo. echo ------------------------------------ echo Make sure you sanitize this output of private data prior to posting it online for review by the CMDER Team! echo ------------------------------------ ) > "%temp%\cmder_diag_cmd.log" type "%temp%\cmder_diag_cmd.log" echo. echo Above output was saved in "%temp%\cmder_diag_cmd.log" ================================================ FILE: vendor/bin/cmder_diag.ps1 ================================================ if (test-path $env:temp\cmder_diag_ps.log) { remove-item $env:temp\cmder_diag_ps.log } $cmder_diag = { "" "------------------------------------" "get-childitem env:" "------------------------------------" get-childitem env: | ft -autosize -wrap 2>&1 "" "------------------------------------" "get-command git -all -ErrorAction SilentlyContinue" "------------------------------------" get-command git -all -ErrorAction SilentlyContinue "" "------------------------------------" "get-command clink -all -ErrorAction SilentlyContinue" "------------------------------------" get-command clink -all -ErrorAction SilentlyContinue "" "------------------------------------" "systeminfo" "------------------------------------" systeminfo 2>&1 "------------------------------------" "get-childitem '$env:CMDER_ROOT'" "------------------------------------" get-childitem "$env:CMDER_ROOT" |ft LastWriteTime,mode,length,FullName "" "------------------------------------" "get-childitem '$env:CMDER_ROOT/vendor'" "------------------------------------" get-childitem "$env:CMDER_ROOT/vendor" |ft LastWriteTime,mode,length,FullName "" "------------------------------------" "get-childitem -s '$env:CMDER_ROOT/bin'" "------------------------------------" get-childitem -s "$env:CMDER_ROOT/bin" |ft LastWriteTime,mode,length,FullName "" "------------------------------------" "get-childitem -s '$env:CMDER_ROOT/config'" "------------------------------------" get-childitem -s "$env:CMDER_ROOT/config" |ft LastWriteTime,mode,length,FullName "" "------------------------------------" "Make sure you sanitize this output of private data prior to posting it online for review by the CMDER Team!" "------------------------------------" } & $cmder_diag | out-file -filePath $env:temp\cmder_diag_ps.log get-content "$env:temp\cmder_diag_ps.log" write-host "" write-host Above output was saved in "$env:temp\cmder_diag_ps.log" ================================================ FILE: vendor/bin/cmder_diag.sh ================================================ #!/usr/bin/env bash [[ -f "$TEMP/cmder_diag_sh.log" ]] && rm -f "$TEMP/cmder_diag_sh.log" (echo '' echo ------------------------------------ echo env echo ------------------------------------ env 2>&1 echo '' echo ------------------------------------ echo which git echo ------------------------------------ which git 2>&1 echo '' echo ------------------------------------ echo which clink echo ------------------------------------ which clink 2>&1 echo '' echo ------------------------------------ echo systeminfo echo ------------------------------------ systeminfo 2>&1 echo ------------------------------------ echo ls -la "$CMDER_ROOT" echo ------------------------------------ ls -la "$CMDER_ROOT" 2>&1 echo '' echo ------------------------------------ echo ls -la "$CMDER_ROOT/vendor" echo ------------------------------------ ls -la "$CMDER_ROOT/vendor" 2>&1 echo '' echo ------------------------------------ echo ls -la /s "$CMDER_ROOT/bin" echo ------------------------------------ ls -laR /s "$CMDER_ROOT/bin" 2>&1 echo '' echo ------------------------------------ echo ls -la /s "$CMDER_ROOT/config" echo ------------------------------------ ls -laR /s "$CMDER_ROOT/config" 2>&1 echo '' echo ------------------------------------ echo Make sure you sanitize this output of private data prior to posting it online for review by the CMDER Team! echo ------------------------------------ ) > "$TEMP/cmder_diag_sh.log" cat "$TEMP/cmder_diag_sh.log" echo '' echo Above output was saved in "$TEMP/cmder_diag_sh.log" ================================================ FILE: vendor/bin/cmder_shell.cmd ================================================ @echo off set CMDER_ROOT=%~dp0..\..\ if "%cmder_init%" == "1" ( "%CMDER_ROOT%\vendor\clink\clink.bat" inject -q --profile "%CMDER_ROOT%\config" --scripts "%CMDER_ROOT%\vendor" ) else ( set cmder_init=1 ) pushd "%CMDER_ROOT%" call "%CMDER_ROOT%\vendor\init.bat" /f %* popd ================================================ FILE: vendor/bin/excd.cmd ================================================ @echo off set excd=%* set excd=%excd:"=% set excd_param=/d if /i "%excd:~0,2%"=="/d" set "excd=%excd:~2%" if "%excd:~0,1%"=="~" (set excd=%userprofile%\%excd:~1%) if "%excd:~0,1%"=="/" (set excd_param=) cd %excd_param% %excd% ================================================ FILE: vendor/bin/timer.cmd ================================================ @echo off set start=%~1 set end=%~2 set options="tokens=1-4 delims=:.," for /f %options% %%a in ("%start%") do set start_h=%%a&set /a start_m=100%%b %% 100&set /a start_s=100%%c %% 100&set /a start_ms=100%%d %% 100 for /f %options% %%a in ("%end%") do set end_h=%%a&set /a end_m=100%%b %% 100&set /a end_s=100%%c %% 100&set /a end_ms=100%%d %% 100 set /a hours=%end_h%-%start_h% set /a mins=%end_m%-%start_m% set /a secs=%end_s%-%start_s% set /a ms=%end_ms%-%start_ms% if %ms% lss 0 set /a secs = %secs% - 1 & set /a ms = 100%ms% if %secs% lss 0 set /a mins = %mins% - 1 & set /a secs = 60%secs% if %mins% lss 0 set /a hours = %hours% - 1 & set /a mins = 60%mins% if %hours% lss 0 set /a hours = 24%hours% if 1%ms% lss 100 set ms=0%ms% :: Mission accomplished set /a totalsecs = %hours%*3600 + %mins%*60 + %secs% echo Elapsed Time: %hours%:%mins%:%secs%.%ms% (%totalsecs%.%ms%s total) ================================================ FILE: vendor/bin/vscode_init.cmd ================================================ @echo off rem Find root dir if not defined CMDER_ROOT ( for /f "delims=" %%i in ("%~dp0\..\..") do ( set "cmder_root=%%~fi" ) ) if defined cmder_user_bin ( set CMDER_VSCODE_INIT_ARGS=%cmder_user_bin%\vscode_init_args.cmd ) else ( set CMDER_VSCODE_INIT_ARGS=%CMDER_ROOT%\bin\vscode_init_args.cmd ) if not exist "%CMDER_VSCODE_INIT_ARGS%" ( echo Creating initial "%CMDER_VSCODE_INIT_ARGS%"... copy "%CMDER_ROOT%\vendor\bin\vscode_init_args.cmd.default" "%CMDER_VSCODE_INIT_ARGS%" ) else ( call "%CMDER_VSCODE_INIT_ARGS%" ) IF [%1] == [] ( REM -- manually opened console (Ctrl + Shift + `) -- CALL "%~dp0..\init.bat" ) ELSE ( REM -- task -- CALL cmd %* exit ) ================================================ FILE: vendor/bin/vscode_init_args.cmd.default ================================================ @echo off rem Below are the default Cmder session settings: rem rem See "%CMDER_ROOT%\README.md" for details on these settings. rem rem `Cmder.exe` Arguments: rem ---------------------- rem rem `/c [cmder_user_cfg_root] rem set cmder_user_bin=[cmder_user_cfg_root]\bin rem set cmder_user_config=[cmder_user_cfg_root]\config rem rem `init.bat` Arguments rem -------------------- rem rem `/d` rem debug_output=0 rem rem `/v` rem verbose_output=0 rem rem `/f` rem fast_init=0 rem rem `/nix_tools` rem nix_tools=1 rem rem `/t` rem time_init=0 rem rem `/max_depth` rem max_depth=1 rem rem `/user_aliases` rem user_aliases= rem rem `/git_install_root` rem GIT_INSTALL_ROOT= rem rem `/home` rem HOME= rem rem `/svn_ssh` rem SVN_SSH= echo Applying Cmder VSCode settings from '%~0'... if defined CMDER_CONFIGURED ( rem Set Cmder settings here for when VSCode is launched inside Cmder. set verbose_output=1 ) else ( rem Set Cmder settings here for when VSCode is launched from outside Cmder. set verbose_output=1 ) rem Set all required Cmder VSCode terminal environment settings above this line. echo Applying Cmder VSCode settings is complete! ================================================ FILE: vendor/clink.lua ================================================ -- default script for clink, called by init.bat when injecting clink -- !!! THIS FILE IS OVERWRITTEN WHEN CMDER IS UPDATED -- !!! Use "%CMDER_ROOT%\config\.lua" to add your lua startup scripts -- luacheck: globals CMDER_SESSION -- luacheck: globals uah_color cwd_color lamb_color clean_color dirty_color conflict_color unknown_color -- luacheck: globals prompt_homeSymbol prompt_lambSymbol prompt_type prompt_useHomeSymbol prompt_useUserAtHost -- luacheck: globals prompt_singleLine prompt_includeVersionControl -- luacheck: globals prompt_overrideGitStatusOptIn -- luacheck: globals clink io.popenyield os.isdir settings.get -- At first, load the original clink.lua file -- this is needed as we set the script path to this dir and therefore the original -- clink.lua is not loaded. local clink_lua_file = clink.get_env('CMDER_ROOT')..'\\vendor\\clink\\clink.lua' dofile(clink_lua_file) -- now add our own things... local function get_uah_color() return uah_color or "\x1b[1;33;49m" -- Green = uah = [user]@[hostname] end local function get_cwd_color() return cwd_color or "\x1b[1;32;49m" -- Yellow cwd = Current Working Directory end local function get_lamb_color() return lamb_color or "\x1b[1;30;49m" -- Light Grey = Lambda Color end local function get_clean_color() return clean_color or "\x1b[37;1m" -- White, Bold end local function get_dirty_color() return dirty_color or "\x1b[33;3m" -- Yellow, Italic end local function get_conflict_color() return conflict_color or "\x1b[31;1m" -- Red, Bold end local function get_unknown_color() return unknown_color or "\x1b[37;1m" -- White, Bold end --- -- Escapes special characters in a string.gsub `find` parameter, so that it -- can be matched as a literal plain text string, i.e. disable Lua pattern -- matching. See "Patterns" (https://www.lua.org/manual/5.2/manual.html#6.4.1). -- @param {string} text Text to escape -- @returns {string} Escaped text --- local function escape_gsub_find_arg(text) return text and text:gsub("([-+*?.%%()%[%]$^])", "%%%1") or "" end --- -- Escapes special characters in a string.gsub `replace` parameter, so that it -- can be replaced as a literal plain text string, i.e. disable Lua pattern -- matching. See "Patterns" (https://www.lua.org/manual/5.2/manual.html#6.4.1). -- @param {string} text Text to escape -- @returns {string} Escaped text --- local function escape_gsub_replace_arg(text) return text and text:gsub("%%", "%%%%") or "" end --- -- Perform string.sub, but disable Lua pattern matching and just treat both -- the `find` and `replace` parameters as a literal plain text replacement. -- @param {string} str Text in which to perform find and replace -- @param {string} find Text to find (plain text; not a Lua pattern) -- @param {string} replace Replacement text (plain text; not a Lua pattern) -- @returns {string} Copy of the input `str` with `find` replaced by `replace` --- local function gsub_plain(str, find, replace) return string.gsub(str, escape_gsub_find_arg(find), escape_gsub_replace_arg(replace)) end -- Extracts only the folder name from the input Path -- Ex: Input C:\Windows\System32 returns System32 --- local function get_folder_name(path) local reversePath = string.reverse(path) local slashIndex = string.find(reversePath, "\\") return string.sub(path, string.len(path) - slashIndex + 2) end --- -- Forward/backward compatibility for Clink asynchronous prompt filtering. -- With Clink v1.2.10 and higher this lets git status run in the background and -- refresh the prompt when it finishes, to eliminate waits in large git repos. --- local io_popenyield local clink_promptcoroutine local cached_info = {} if clink.promptcoroutine and io.popenyield then io_popenyield = io.popenyield clink_promptcoroutine = clink.promptcoroutine else io_popenyield = io.popen clink_promptcoroutine = function (func) return func(false) end end --- -- Global variable so other Lua scripts can detect whether they're in a Cmder -- shell session. --- CMDER_SESSION = true --- -- Setting the prompt in clink means that commands which rewrite the prompt do -- not destroy our own prompt. It also means that started cmds (or batch files -- which echo) don't get the ugly '{lamb}' shown. --- local function set_prompt_filter() -- get_cwd() is differently encoded than the clink.prompt.value, so everything other than -- pure ASCII will get garbled. So try to parse the current directory from the original prompt -- and only if that doesn't work, use get_cwd() directly. -- The matching relies on the default prompt which ends in X:\PATH\PATH> -- (no network path possible here!) local old_prompt = clink.prompt.value local cwd = old_prompt:match('.*(.:[^>]*)>') if cwd == nil then cwd = clink.get_cwd() end -- environment systems like pythons virtualenv change the PROMPT and usually -- set some variable. But the variables are differently named and we would never -- get them all, so try to parse the env name out of the PROMPT. -- envs are usually put in round or square parentheses and before the old prompt local env = old_prompt:match('.*%(([^%)]+)%).+:') -- also check for square brackets if env == nil then env = old_prompt:match('.*%[([^%]]+)%].+:') end -- Much of the below was 'borrowed' from https://github.com/AmrEldib/cmder-powerline-prompt -- Symbol displayed for the home dir in the prompt. if not prompt_homeSymbol then prompt_homeSymbol = "~" end -- Symbol displayed in the new line below the prompt. if not prompt_lambSymbol then prompt_lambSymbol = "λ" end if not prompt_type then prompt_type = "full" end if prompt_useHomeSymbol == nil then prompt_useHomeSymbol = false end if prompt_useUserAtHost == nil then prompt_useUserAtHost = false end if prompt_singleLine == nil then prompt_singleLine = false end if prompt_includeVersionControl == nil then prompt_includeVersionControl = true end if prompt_type == 'folder' then cwd = get_folder_name(cwd) end if prompt_useHomeSymbol and string.find(cwd, clink.get_env("HOME")) then cwd = gsub_plain(cwd, clink.get_env("HOME"), prompt_homeSymbol) end local uah = '' if prompt_useUserAtHost then uah = clink.get_env("USERNAME") .. "@" .. clink.get_env("COMPUTERNAME") .. ' ' end local cr = "\n" if prompt_singleLine then cr = ' ' end cr = "\x1b[0m" .. cr if env ~= nil then env = "("..env..") " else env = "" end if uah ~= '' then uah = get_uah_color() .. uah end if cwd ~= '' then cwd = get_cwd_color() .. cwd end local version_control = prompt_includeVersionControl and "{git}{hg}{svn}" or "" local prompt = "{uah}{cwd}" .. version_control .. cr .. get_lamb_color() .. "{env}{lamb}\x1b[0m " prompt = gsub_plain(prompt, "{uah}", uah) prompt = gsub_plain(prompt, "{cwd}", cwd) prompt = gsub_plain(prompt, "{env}", env) clink.prompt.value = gsub_plain(prompt, "{lamb}", prompt_lambSymbol) end local function percent_prompt_filter() clink.prompt.value = gsub_plain(clink.prompt.value, "{percent}", "%") end --- -- Resolves closest directory location for specified directory. -- Navigates subsequently up one level and tries to find specified directory -- @param {string} path Path to directory will be checked. If not provided -- current directory will be used -- @param {string} dirname Directory name to search for -- @return {string} Path to specified directory or nil if such dir not found local function get_dir_contains(path, dirname) -- return parent path for specified entry (either file or directory) local function pathname(path) -- luacheck: ignore 432 local prefix = "" local i = path:find("[\\/:][^\\/:]*$") if i then prefix = path:sub(1, i-1) end return prefix end -- Navigates up one level local function up_one_level(path) -- luacheck: ignore 432 if path == nil then path = '.' end if path == '.' then path = clink.get_cwd() end return pathname(path) end -- Checks if provided directory contains git directory local function has_specified_dir(path, specified_dir) -- luacheck: ignore 432 if path == nil then path = '.' end local found_dirs = clink.find_dirs(path..'/'..specified_dir) if #found_dirs > 0 then return true end return false end -- Set default path to current directory if path == nil then path = '.' end -- If we're already have .git directory here, then return current path if has_specified_dir(path, dirname) then return path..'/'..dirname else -- Otherwise go up one level and make a recursive call local parent_path = up_one_level(path) if parent_path == path then return nil else return get_dir_contains(parent_path, dirname) end end end -- adapted from from clink-completions' git.lua local function get_git_dir(path) -- return parent path for specified entry (either file or directory) local function pathname(path) -- luacheck: ignore 432 local prefix = "" local i = path:find("[\\/:][^\\/:]*$") if i then prefix = path:sub(1, i-1) end return prefix end -- Checks if provided directory contains git directory local function has_git_dir(dir) return clink.is_dir(dir..'/.git') and dir..'/.git' end local function has_git_file(dir) local gitfile = io.open(dir..'/.git') if not gitfile then return false end local line = gitfile:read() or '' local git_dir = line:match('gitdir: (.*)') gitfile:close() if os.isdir then -- only available in Clink v1.0.0 and higher if git_dir and os.isdir(git_dir) then return git_dir end end return git_dir and dir..'/'..git_dir end -- Set default path to current directory if not path or path == '.' then path = clink.get_cwd() end -- Calculate parent path now otherwise we won't be -- able to do that inside of logical operator local parent_path = pathname(path) return has_git_dir(path) or has_git_file(path) -- Otherwise go up one level and make a recursive call or (parent_path ~= path and get_git_dir(parent_path) or nil) end local function get_hg_dir(path) return get_dir_contains(path, '.hg') end local function get_svn_dir(path) return get_dir_contains(path, '.svn') end --- -- Find out current branch -- @return {nil|git branch name} --- local function get_git_branch(git_dir, fast) git_dir = git_dir or get_git_dir() -- If git directory not found then we're probably outside of repo -- or something went wrong. The same is when head_file is nil local head_file = git_dir and io.open(git_dir..'/HEAD') if not head_file then return end local HEAD = head_file:read() head_file:close() -- If HEAD is missing, something is wrong. if not HEAD then return end -- if HEAD matches branch expression, then we're on named branch -- otherwise it is a detached commit local branch_name = HEAD:match('ref: refs/heads/(.+)') if os.getenv("CLINK_DEBUG_GIT_REFTABLE") then branch_name = '.invalid' end -- If the branch name is ".invalid" and the fast method wasn't requested, -- then invoke git.exe to get accurate current branch info (slow method). if branch_name == ".invalid" and not fast then local file branch_name = nil -- Handle the most common case first. if not branch_name then file = io_popenyield("git --no-optional-locks branch 2>nul") if file then for line in file:lines() do local b = line:match("^%*%s+(.*)") if b then b = b:match("^%((HEAD detached at .*)%)") or b branch_name = b break end end file:close() end end -- Handle the cases where "git branch" output is empty, but "git -- branch --show-current" shows the branch name (e.g. a new repo). if not branch_name then file = io_popenyield("git --no-optional-locks branch --show-current 2>nul") if file then for line in file:lines() do -- luacheck: ignore 512 branch_name = line break end file:close() end end else branch_name = branch_name or 'HEAD detached at '..HEAD:sub(1, 7) end return branch_name end local function get_git_remote(git_dir, branch) if not git_dir then return nil end if not branch then return nil end local file = io.open(git_dir.."/config", 'r') if not file then return nil end local git_config = {} local function get_git_config_value(section, param) return git_config[section] and git_config[section][param] or nil end local section for line in file:lines() do if (line:sub(1,1) == "[" and line:sub(-1) == "]") then if (line:sub(2,5) == "lfs ") then section = nil -- skip LFS entries as there can be many and we never use them else section = line:sub(2,-2) git_config[section] = git_config[section] or {} end elseif section then local param, value = line:match('^%s-([%w|_]+)%s-=%s+(.+)$') if (param and value ~= nil) then git_config[section][param] = value end end end file:close() local remote_to_push = get_git_config_value('branch "'..branch..'"', 'remote') or '' local remote_ref = get_git_config_value('remote "'..remote_to_push..'"', 'push') or get_git_config_value('push', 'default') local text = remote_to_push if remote_ref then text = text..'/'..remote_ref end return text ~= '' and text or nil end --- -- Find out current branch information -- @return {false|mercurial branch information} --- local function get_hg_branch() -- Return the branch information. local file = io.popen("hg branch 2>nul") if not file then return false end for line in file:lines() do local m = line:match("(.+)$") if m then file:close() return m end end file:close() return false end --- -- Find out current branch -- @return {false|svn branch name} --- local function get_svn_branch() local file = io_popenyield("svn info 2>nul") if not file then return false end for line in file:lines() do local m = line:match("^Relative URL:") if m then file:close() return line:sub(line:find("/")+1,line:len()) end end file:close() return false end --- -- Get the status and conflict status of working dir -- @return {bool , bool } --- local function get_git_status(git_dir) local file = io_popenyield("git --no-optional-locks status --porcelain 2>nul") if not file then return {} end local conflict_found = false local is_status = true for line in file:lines() do local code = line:sub(1, 2) -- print (string.format("code: %s, line: %s", code, line)) if code == "DD" or code == "AU" or code == "UD" or code == "UA" or code == "DU" or code == "AA" or code == "UU" then -- luacheck: no max line length is_status = false conflict_found = true break -- unversioned files are ignored, comment out 'code ~= "!!"' to unignore them elseif code ~= "!!" and code ~= "??" then is_status = false end end file:close() local branch = get_git_branch(git_dir, false--[[fast]]) local remote = get_git_remote(git_dir, branch) return { status = is_status, branch = branch, remote = remote, conflict = conflict_found } end --- -- Get the status of working dir -- @return {bool} --- local function get_hg_status() -- The default is to just use the branch name, but you could e.g. use the -- "hg-prompt" extension to get more information, such as any applied mq -- patches. Here's an example of that: -- "hg prompt \"{branch}{status}{|{patch}}{update}\"" local pipe = io_popenyield("hg status -amrd 2>&1") if not pipe then return { error = true } end local output = pipe:read('*all') pipe:close() local dirty = (output ~= nil and output ~= "") return { clean = not dirty } end --- -- Get the status of working dir -- @return {bool} --- local function get_svn_status() local file = io_popenyield("svn status -q 2>nul") if not file then return { error = true } end for line in file:lines() do -- luacheck: ignore 512, no unused file:close() return { clean = false } end file:close() return { clean = true } end --- -- Get the status of working dir -- @return {bool} --- local last_git_status_time = nil local last_git_status_setting = true local function get_git_status_setting() local time = os.clock() local last_time = last_git_status_time last_git_status_time = time if last_time and time >= 0 and time - last_time < 10 then return last_git_status_setting end -- When async prompt filtering is available, check the -- prompt_overrideGitStatusOptIn config setting for whether to ignore the -- cmder.status and cmder.cmdstatus git config opt-in settings. if clink.promptcoroutine and io.popenyield and settings.get("prompt.async") then if prompt_overrideGitStatusOptIn then last_git_status_setting = true return true end end local gitStatusConfig = io_popenyield("git --no-pager config cmder.status 2>nul") if gitStatusConfig then for line in gitStatusConfig:lines() do if string.match(line, 'false') then gitStatusConfig:close() last_git_status_setting = false return false end end gitStatusConfig:close() end local gitCmdStatusConfig = io_popenyield("git --no-pager config cmder.cmdstatus 2>nul") if gitCmdStatusConfig then for line in gitCmdStatusConfig:lines() do if string.match(line, 'false') then gitCmdStatusConfig:close() last_git_status_setting = false return false end end gitCmdStatusConfig:close() end last_git_status_setting = true return true end --- -- Use a prompt coroutine to get git status in the background. -- Cache the info so we can reuse it next time to reduce flicker. --- local function get_git_info_table(git_dir) local info = clink_promptcoroutine(function () -- Use git status if allowed. local cmderGitStatusOptIn = get_git_status_setting() return cmderGitStatusOptIn and get_git_status(git_dir) or {} end) if not info then info = cached_info.git_info or {} else cached_info.git_info = info end return info end local function git_prompt_filter() -- Don't do any git processing if the prompt doesn't want to show git info. if not clink.prompt.value:find("{git}") then return false end local git_dir = get_git_dir() local color if git_dir then local branch = get_git_branch(git_dir, true--[[fast]]) if branch then -- If in a different repo or branch than last time, discard cached info. if cached_info.git_dir ~= git_dir or (branch ~= ".invalid" and cached_info.git_branch ~= branch) then cached_info.git_info = nil cached_info.git_dir = git_dir cached_info.git_branch = branch end -- If we're inside of git repo then try to detect current branch -- Has branch => therefore it is a git folder, now figure out status local gitInfo = get_git_info_table(git_dir) local gitStatus = gitInfo.status local gitConflict = gitInfo.conflict -- Compensate for git reftables. branch = gitInfo.branch or branch if branch == ".invalid" then branch = "Loading..." elseif gitInfo.remote then branch = branch.." -> "..gitInfo.remote end -- Prevent an older clink-completions git_prompt.lua scripts from -- modifying the prompt. branch = "\x1b[10m"..branch if gitStatus == nil then color = get_unknown_color() elseif gitStatus then color = get_clean_color() else color = get_dirty_color() end if gitConflict then color = get_conflict_color() end local result = " "..color.."("..branch..")" clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", result) return false end end -- No git present or not in git file clink.prompt.value = gsub_plain(clink.prompt.value, "{git}", "") return false end local function get_hg_info_table() local info = clink_promptcoroutine(function () return get_hg_status() or {} end) if not info then info = cached_info.hg_info or {} else cached_info.hg_info = info end return info end local function hg_prompt_filter() -- Don't do any hg processing if the prompt doesn't want to show hg info. if not clink.prompt.value:find("{hg}") then return false end local hg_dir = get_hg_dir() if hg_dir then local branch = get_hg_branch() if branch and string.sub(branch,1,7) ~= "abort: " and -- not an HG working copy (not string.find(branch, "is not recognized")) then -- 'hg' not in path -- If in a different repo or branch than last time, discard cached info if cached_info.hg_dir ~= hg_dir or cached_info.hg_branch ~= branch then cached_info.hg_info = nil cached_info.hg_dir = hg_dir cached_info.hg_branch = branch end local hgInfo = get_hg_info_table() local color if not hgInfo or hgInfo.error then color = get_unknown_color() elseif hgInfo.clean then color = get_clean_color() else color = get_dirty_color() end local result = " "..color.."("..branch..")" clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", result) return false end end -- No hg present or not in hg repo clink.prompt.value = gsub_plain(clink.prompt.value, "{hg}", "") end local function get_svn_info_table() local info = clink_promptcoroutine(function () return get_svn_status() or {} end) if not info then info = cached_info.svn_info or {} else cached_info.svn_info = info end return info end local function svn_prompt_filter() -- Don't do any svn processing if the prompt doesn't want to show svn info. if not clink.prompt.value:find("{svn}") then return false end local svn_dir = get_svn_dir() if svn_dir then -- if we're inside of svn repo then try to detect current branch local branch = get_svn_branch() if branch then -- If in a different repo or branch than last time, discard cached info if cached_info.svn_dir ~= svn_dir or cached_info.svn_branch ~= branch then cached_info.svn_info = nil cached_info.svn_dir = svn_dir cached_info.svn_branch = branch end local svnInfo = get_svn_info_table() local color if not svnInfo or svnInfo.error then color = get_unknown_color() elseif svnInfo.clean then color = get_clean_color() else color = get_dirty_color() end clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", " "..color.."("..branch..")") return false end end -- No svn present or not in svn file clink.prompt.value = gsub_plain(clink.prompt.value, "{svn}", "") return false end -- insert the set_prompt at the very beginning so that it runs first clink.prompt.register_filter(set_prompt_filter, 1) clink.prompt.register_filter(hg_prompt_filter, 50) clink.prompt.register_filter(git_prompt_filter, 50) clink.prompt.register_filter(svn_prompt_filter, 50) clink.prompt.register_filter(percent_prompt_filter, 51) local completions_dir = clink.get_env('CMDER_ROOT')..'/vendor/clink-completions/' -- Execute '.init.lua' first to ensure package.path is set properly dofile(completions_dir..'.init.lua') for _,lua_module in ipairs(clink.find_files(completions_dir..'*.lua')) do -- Skip files that starts with _. This could be useful if some files should be ignored if not string.match(lua_module, '^_.*') then local filename = completions_dir..lua_module -- use dofile instead of require because require caches loaded modules -- so config reloading using Alt-Q won't reload updated modules. dofile(filename) end end if clink.get_env('CMDER_USER_CONFIG') then local cmder_config_dir = clink.get_env('CMDER_ROOT')..'/config/' for _,lua_module in ipairs(clink.find_files(cmder_config_dir..'*.lua')) do local filename = cmder_config_dir..lua_module -- use dofile instead of require because require caches loaded modules -- so config reloading using Alt-Q won't reload updated modules. dofile(filename) end end ================================================ FILE: vendor/clink_settings.default ================================================ # For explanation of these and other settings see: # https://chrisant996.github.io/clink/clink.html # name: Expand envvars when completing # type: boolean match.expand_envvars = True # name: Sets how command history expansion is applied # type: enum # options: off,on,not_squoted,not_dquoted,not_quoted history.expand_mode = not_dquoted # name: Skip adding lines prefixed with whitespace # type: boolean history.ignore_space = True # name: The number of history lines to save # type: integer history.max_lines = 25000 # name: Share history between instances # type: boolean history.shared = False # name: Auto-answer terminate prompt # type: enum # options: off,answer_yes,answer_no cmd.auto_answer = answer_yes # name: Doskey completions # type: color color.doskey = yellow ================================================ FILE: vendor/cmder.sh ================================================ # DO NOT EDIT THIS FILE IT WILL BE OVERWRITTEN ON UPDATE # # Add portable user customizations ${CMDER_ROOT}/config/user_profile.sh, # these customizations will follow Cmder if $CMDER_ROOT is copied # to another machine. # # Add system specific users customizations to $HOME/.bashrc, these # customizations will not follow Cmder to another machine. function runProfiled { unset profile_d_scripts pushd "${1}" >/dev/null profile_d_scripts=$(ls *.sh 2>/dev/null) if [ ! "x${profile_d_scripts}" = "x" ] ; then for x in ${profile_d_scripts} ; do # echo Sourcing "${1}/${x}"... . "${1}/${x}" done fi popd >/dev/null } # We do this for bash as admin sessions since $CMDER_ROOT is not being set if [ "$CMDER_ROOT" == "" ] ; then case "$ConEmuDir" in *\\*) CMDER_ROOT=$( cd "$(cygpath -u "$ConEmuDir")/../.." ; pwd );; esac else case "$CMDER_ROOT" in *\\*) CMDER_ROOT="$(cygpath -u "$CMDER_ROOT")";; esac fi # Remove any trailing '/' CMDER_ROOT=$(echo $CMDER_ROOT | sed 's:/*$::') export CMDER_ROOT if [ -f "/c/Program Files/Git/cmd/git.exe" ] ; then GIT_INSTALL_ROOT="/c/Program Files/Git" elif [ -f "/c/Program Files(x86)/Git/cmd/git.exe" ] ; then GIT_INSTALL_ROOT="/c/Program Files(x86)/Git" elif [ -f "${CMDER_ROOT}/vendor/git-for-windows/cmd/git.exe" ] ; then GIT_INSTALL_ROOT=${CMDER_ROOT}/vendor/git-for-windows fi if [[ ! "$PATH" =~ "${GIT_INSTALL_ROOT}/bin:" ]] ; then PATH="${GIT_INSTALL_ROOT}/bin:$PATH" fi PATH="${CMDER_ROOT}/bin:${CMDER_ROOT}/vendor/bin:$PATH:${CMDER_ROOT}" export PATH # Drop *.sh or *.zsh files into "${CMDER_ROOT}\config\profile.d" # to source them at startup. if [ ! -d "${CMDER_ROOT}/config/profile.d" ] ; then mkdir -p "${CMDER_ROOT}/config/profile.d" fi if [ -d "${CMDER_ROOT}/config/profile.d" ] ; then runProfiled "${CMDER_ROOT}/config/profile.d" fi if [ -d "${CMDER_USER_CONFIG}/profile.d" ] ; then runProfiled "${CMDER_USER_CONFIG}/profile.d" fi # Renaming to "config\user_profile.sh" to "user_profile.sh" for consistency. if [ -f "$CMDER_ROOT/config/user-profile.sh" ] ; then mv "$CMDER_ROOT/config/user-profile.sh" "$CMDER_ROOT/config/user_profile.sh" fi CmderUserProfilePath="${CMDER_ROOT}/config/user_profile.sh" if [ -f "${CMDER_ROOT}/config/user_profile.sh" ] ; then . "${CMDER_ROOT}/config/user_profile.sh" fi if [ "${CMDER_USER_CONFIG}" != "" ] ; then # Renaming to "config\user_profile.sh" to "user_profile.sh" for consistency. if [ -f "$CMDER_USER_CONFIG/user-profile.sh" ] ; then mv "$CMDER_USER_CONFIG/user-profile.sh" "$CMDER_USER_CONFIG/user_profile.sh" fi export PATH="${CMDER_USER_CONFIG}/bin:$PATH" CmderUserProfilePath="${CMDER_USER_CONFIG}/user_profile.sh" if [ -f "${CMDER_USER_CONFIG}/user_profile.sh" ] ; then . "${CMDER_USER_CONFIG}/user_profile.sh" fi fi if [ ! -f "${CmderUserProfilePath}" ] ; then echo Creating user startup file: "${CmderUserProfilePath}" cp "${CMDER_ROOT}/vendor/user_profile.sh.default" "${CmderUserProfilePath}" fi # Source the users .bashrc file if it exists if [ -f "${HOME}/.bashrc" ] ; then . "${HOME}/.bashrc" fi ================================================ FILE: vendor/cmder_exinit ================================================ # Copy this file to your non integrated *nix-like environment, # Cygwin/MSys2/Git for Windows SDK, installs '/etc/profile.d/' # folder to integrate the externally installed Unix like environment # into Cmder so it has access to settings stored in Cmder/config # folder when launched. # # The destination file extension depends on the shell you use. For example: # # bash - Copy to /etc/profile.d/cmder_exinit.sh # zsh - Copy to /etc/profile.d/cmder_exinit.zsh # Add portable user customizations ${CMDER_ROOT}/config/user-profile.sh or # add whole config scripts to ${CMDER_ROOT}/config/profile.d both will be sourced # from this file and be applied to the environment at startup. # # These customizations will follow Cmder if $CMDER_ROOT is copied # to another machine. # # Add system specific users customizations to $HOME/.bashrc, these # customizations will not follow Cmder to another machine. # # Uncomment and edit the CMDER_ROOT line to use Cmder/config even when launched # # from outside Cmder. # CMDER_ROOT=${USERPROFILE}/cmder # This is not required if launched from Cmder. function runProfiled { unset profile_d_scripts pushd "${1}" >/dev/null if [ ! "x${ZSH_VERSION}" = "x" ]; then profile_d_scripts=$(ls *.zsh 2>/dev/null) elif [ ! "x${BASH_VERSION}" = "x" ]; then profile_d_scripts=$(ls *.sh 2>/dev/null) fi if [ ! "x${profile_d_scripts}" = "x" ] ; then for x in ${profile_d_scripts} ; do echo Sourcing "${1}/${x}"... . "${1}/${x}" done fi popd >/dev/null } # Check that we haven't already been sourced. [[ -z ${CMDER_EXINIT} ]] && CMDER_EXINIT="1" || return # We do this for bash as admin sessions since $CMDER_ROOT is not being set if [ "$CMDER_ROOT" = "" -a "$ConEmuDir" != "" ] ; then if [ -d "${ConEmuDir}../../vendor" ] ; then case "$ConEmuDir" in *\\*) CMDER_ROOT=$( cd "$(cygpath -u "$ConEmuDir")/../.." ; pwd );; esac else echo "Running in ConEmu without Cmder, skipping Cmder integration." fi elif [ "$CMDER_ROOT" != "" ] ; then case "$CMDER_ROOT" in *\\*) CMDER_ROOT="$(cygpath -u "$CMDER_ROOT")";; esac fi if [ ! "$CMDER_ROOT" = "" ] ; then # Remove any trailing '/' CMDER_ROOT=$(echo $CMDER_ROOT | sed 's:/*$::') echo "Using \"CMDER_ROOT\" at \"${CMDER_ROOT}\"." export CMDER_ROOT PATH=${CMDER_ROOT}/bin:${CMDER_ROOT}/vendor/bin:$PATH:${CMDER_ROOT} export PATH # Drop *.sh or *.zsh files into "${CMDER_ROOT}\config\profile.d" # to source them at startup. if [ ! -d "${CMDER_ROOT}/config/profile.d" ] ; then mkdir -p "${CMDER_ROOT}/config/profile.d" fi if [ -d "${CMDER_ROOT}/config/profile.d" ] ; then runProfiled "${CMDER_ROOT}/config/profile.d" fi if [ -d "${CMDER_USER_CONFIG}/profile.d" ] ; then runProfiled "${CMDER_USER_CONFIG}/profile.d" fi # Renaming to "config\user_profile.sh" to "user_profile.sh" for consistency. if [ -f "$CMDER_ROOT/config/user-profile.sh" ] ; then mv "$CMDER_ROOT/config/user-profile.sh" "$CMDER_ROOT/config/user_profile.sh" fi CmderUserProfilePath="${CMDER_ROOT}/config/user_profile.sh" if [ -f "${CMDER_ROOT}/config/user_profile.sh" ] ; then . "${CMDER_ROOT}/config/user_profile.sh" fi if [ "${CMDER_USER_CONFIG}" != "" ] ; then # Renaming to "config\user_profile.sh" to "user_profile.sh" for consistency. if [ -f "$CMDER_USER_CONFIG/user-profile.sh" ] ; then mv "$CMDER_USER_CONFIG/user-profile.sh" "$CMDER_USER_CONFIG/user_profile.sh" fi export PATH=${CMDER_USER_CONFIG}/bin:$PATH CmderUserProfilePath="${CMDER_USER_CONFIG}/user_profile.sh" if [ -f "${CMDER_USER_CONFIG}/user_profile.sh" ] ; then . "${CMDER_USER_CONFIG}/user_profile.sh" fi fi if [ ! -f "${CmderUserProfilePath}" ] ; then echo Creating user startup file: "${CmderUserProfilePath}" cp "${CMDER_ROOT}/vendor/user_profile.sh.default" "${CmderUserProfilePath}" fi fi ================================================ FILE: vendor/cmder_prompt_config.lua.default ================================================ -- All of the below was 'borrowed' from https://github.com/AmrEldib/cmder-powerline-prompt --- REQUIRED. config_prompt_type is whether the displayed prompt is the full path or only the folder name -- Use: -- "full" for full path like C:\Windows\System32 -- "folder" for folder name only like System32 -- default is full prompt_type = "full" --- REQUIRED. config_prompt_useHomeSymbol is whether to show ~ instead of the full path to the user's home folder -- Use true or false -- default is false prompt_useHomeSymbol = false -- Symbols -- REQUIRED. Prompt displayed instead of user's home folder e.g. C:\Users\username -- default is '~' prompt_homeSymbol = "~" -- REQUIRED. Symbol displayed in the new line below the prompt. -- default is 'λ' prompt_lambSymbol = "λ" -- REQUIRED. Adds [user]@[host] to the beginning of the prompt like bash -- default is false prompt_useUserAtHost = false -- REQUIRED. If true prompt is a single line instead of default two line prompt. -- default is false prompt_singleLine = false -- OPTIONAL. If true then Cmder includes git, mercurial, and subversion status in the prompt. -- default is true prompt_includeVersionControl = true -- OPTIONAL. If true then always ignore the cmder.status and cmder.cmdstatus git config settings and run the git prompt commands in the background. -- default is false -- NOTE: This only takes effect if using Clink v1.2.10 or higher. prompt_overrideGitStatusOptIn = false -- Prompt Attributes -- -- Colors: https://github.com/cmderdev/cmder/wiki/Customization#list-of-colors -- Effects: https://github.com/cmderdev/cmder/wiki/Customization#list-of-effects -- -- Green: "\x1b[1;32;49m" -- Yellow: "\x1b[1;33;49m" -- Light Grey: "\x1b[1;30;49m" -- Prompt Element Colors uah_color = "\x1b[1;33;49m" -- Yellow uah = [user]@[hostname] cwd_color = "\x1b[1;32;49m" -- Green cwd = Current Working Directory lamb_color = "\x1b[1;30;49m" -- Light Grey = Lambda Color clean_color = "\x1b[37;1m" dirty_color = "\x1b[33;3m" -- Yellow, Italic conflict_color = "\x1b[31;1m" -- Red, Bold unknown_color = "\x1b[37;1m" -- White, Bold = No VCS Status Branch Color ================================================ FILE: vendor/git-prompt.sh ================================================ # Returns 1 if git status for Cmder is disabled, otherwise returns 0 function getGitStatusSetting() { local gitConfig # Get all git config entries for the current repository without pager gitConfig=$(git --no-pager config -l 2>/dev/null) || return 0 # treat failure as enabled # Check if git status display for Cmder is disabled via config # Matches: cmder.status=false or cmder.shstatus=false (Bash-specific) if [[ $gitConfig =~ (^|$'\n')cmder\.(sh)?status=false($|$'\n') ]] then return 1 # disabled fi return 0 } # Prints current branch or detached HEAD short commit hash function getSimpleGitBranch() { local gitDir gitDir=$(git rev-parse --git-dir 2>/dev/null) || return 0 local headFile="$gitDir/HEAD" [ -f "$headFile" ] || return 0 local headContent headContent=$(< "$headFile") if [[ "$headContent" =~ ^ref:\ refs/heads/(.+)$ ]] then echo " (${BASH_REMATCH[1]})" else echo " (HEAD detached at ${headContent:0:7})" fi } if test -f /etc/profile.d/git-sdk.sh then TITLEPREFIX=SDK-${MSYSTEM#MINGW} else TITLEPREFIX=$MSYSTEM fi if test -f ~/.config/git/git-prompt.sh then if getGitStatusSetting then . ~/.config/git/git-prompt.sh fi else # Setup OSC 133 shell integration for Windows Terminal if [ -n "$WT_SESSION" ]; then __cmder_prompt_command() { local exit_code=$? # Emit OSC 133;D to mark the end of command execution with exit code printf '\e]133;D;%s\a' "$exit_code" return $exit_code } __cmder_preexec() { # Emit OSC 133;C to mark the start of command execution printf '\e]133;C\a' } # Append to PROMPT_COMMAND to emit sequences just before each prompt if [ -z "$PROMPT_COMMAND" ]; then PROMPT_COMMAND="__cmder_prompt_command" else PROMPT_COMMAND="__cmder_prompt_command;$PROMPT_COMMAND" fi # Use DEBUG trap to emit OSC 133;C before command execution trap '__cmder_preexec' DEBUG fi # Source: github.com/git-for-windows/build-extra/blob/main/git-extra/git-prompt.sh PS1='\[\033]0;${TITLEPREFIX:+$TITLEPREFIX:}${PWD//[^[:ascii:]]/?}\007\]' # set window title to TITLEPREFIX (if set) and current working directory # PS1="$PS1"'\n' # new line (disabled) if [ -n "$WT_SESSION" ]; then # Emit OSC 133;A to mark the start of prompt PS1="$PS1"'\e]133;A\a' fi PS1="$PS1"'\[\033[32m\]' # change to green and bold PS1="$PS1"'\u@\h ' # user@host PS1="$PS1${MSYSTEM:+\[\033[35m\]$MSYSTEM }" # show MSYSTEM in purple (if set) PS1="$PS1"'\[\033[1;33m\]' # change to dark yellow in bold PS1="$PS1"'\w' # current working directory if test -z "$WINELOADERNOEXEC" then GIT_EXEC_PATH="$(git --exec-path 2>/dev/null)" COMPLETION_PATH="${GIT_EXEC_PATH%/libexec/git-core}" COMPLETION_PATH="${COMPLETION_PATH%/lib/git-core}" COMPLETION_PATH="$COMPLETION_PATH/share/git/completion" if test -f "$COMPLETION_PATH/git-prompt.sh" then . "$COMPLETION_PATH/git-completion.bash" if getGitStatusSetting then . "$COMPLETION_PATH/git-prompt.sh" PS1="$PS1"'\[\033[36m\]' # change color to cyan PS1="$PS1"'`__git_ps1`' # bash function else PS1="$PS1"'\[\033[37;1m\]' # change color to white PS1="$PS1"'`getSimpleGitBranch`' fi fi fi PS1="$PS1"'\[\033[0m\]' # reset color PS1="$PS1"'\n' # new line PS1="$PS1"'\[\033[30;1m\]' # change color to grey in bold PS1="$PS1"'λ ' # prompt: Cmder uses λ PS1="$PS1"'\[\033[0m\]' # reset color if [ -n "$WT_SESSION" ]; then # Emit OSC 133;B to mark the end of prompt PS1="$PS1"'\[\e]133;B\a\]' fi fi MSYS2_PS1="$PS1" # for detection by MSYS2 SDK's bash.basrc # Evaluate all user-specific Bash completion scripts (if any) if test -z "$WINELOADERNOEXEC" then for c in "$HOME"/bash_completion.d/*.bash do # Handle absence of any scripts (or the folder) gracefully test ! -f "$c" || . "$c" done fi ================================================ FILE: vendor/init.bat ================================================ @echo off set CMDER_INIT_START=%time% :: Init Script for cmd.exe shell :: Created as part of cmder project :: !!! THIS FILE IS OVERWRITTEN WHEN CMDER IS UPDATED :: !!! Use "%CMDER_ROOT%\config\user_profile.cmd" to add your own startup commands :: Use /v command line arg or set to > 0 for verbose output to aid in debugging. if not defined verbose_output set verbose_output=0 :: Use /d command line arg or set to 1 for debug output to aid in debugging. if not defined debug_output set debug_output=0 :: Use /t command line arg or set to 1 to display init time. if not defined time_init set time_init=0 :: Use /f command line arg to speed up init at the expense of some functionality. if not defined fast_init set fast_init=0 :: Use /max_depth 1-5 to set max recurse depth for calls to `enhance_path_recursive` if not defined max_depth set max_depth=1 :: Add *nix tools to end of path. 0 turns off *nix tools, 2 adds *nix tools to the front of the path. if not defined nix_tools set nix_tools=1 set "CMDER_USER_FLAGS= " :: Find root dir if not defined CMDER_ROOT ( if defined ConEmuDir ( for /f "delims=" %%i in ("%ConEmuDir%\..\..") do ( set "CMDER_ROOT=%%~fi" ) ) else ( for /f "delims=" %%i in ("%~dp0\..") do ( set "CMDER_ROOT=%%~fi" ) ) ) :: Remove trailing '\' from %CMDER_ROOT% if "%CMDER_ROOT:~-1%" == "\" SET "CMDER_ROOT=%CMDER_ROOT:~0,-1%" :: Include Cmder libraries call "%cmder_root%\vendor\bin\cexec.cmd" /setpath call "%cmder_root%\vendor\lib\lib_console" call "%cmder_root%\vendor\lib\lib_base" call "%cmder_root%\vendor\lib\lib_path" call "%cmder_root%\vendor\lib\lib_git" call "%cmder_root%\vendor\lib\lib_profile" :var_loop if "%~1" == "" ( goto :start ) else if /i "%1" == "/f" ( set fast_init=1 ) else if /i "%1" == "/t" ( set time_init=1 ) else if /i "%1" == "/v" ( set verbose_output=1 ) else if /i "%1" == "/d" ( set debug_output=1 ) else if /i "%1" == "/max_depth" ( if "%~2" geq "1" if "%~2" leq "5" ( set "max_depth=%~2" shift ) else ( %print_error% "'/max_depth' requires a number between 1 and 5!" exit /b ) ) else if /i "%1" == "/c" ( if exist "%~2" ( if not exist "%~2\bin" mkdir "%~2\bin" set "cmder_user_bin=%~2\bin" if not exist "%~2\config\profile.d" mkdir "%~2\config\profile.d" set "cmder_user_config=%~2\config" shift ) ) else if /i "%1" == "/user_aliases" ( if exist "%~2" ( set "user_aliases=%~2" shift ) ) else if /i "%1" == "/git_install_root" ( if exist "%~2" ( set "GIT_INSTALL_ROOT=%~2" shift ) else ( %print_error% "The Git install root folder "%~2" that you specified does not exist!" exit /b ) ) else if /i "%1" == "/nix_tools" ( if "%2" equ "0" ( REM Do not add *nix tools to path set nix_tools=0 shift ) else if "%2" equ "1" ( REM Add *nix tools to end of path set nix_tools=1 shift ) else if "%2" equ "2" ( REM Add *nix tools to front of path set nix_tools=2 shift ) ) else if /i "%1" == "/home" ( if exist "%~2" ( set "HOME=%~2" shift ) else ( %print_error% The home folder "%2" that you specified does not exist! exit /b ) ) else if /i "%1" == "/svn_ssh" ( set SVN_SSH=%2 shift ) else ( set "CMDER_USER_FLAGS=%1 %CMDER_USER_FLAGS%" ) shift goto :var_loop :start :: Enable console related methods if verbose/debug is turned on if %debug_output% gtr 0 (set print_debug=%lib_console% debug_output) if %verbose_output% gtr 0 ( set print_verbose=%lib_console% verbose_output set print_warning=%lib_console% show_warning ) :: Sets CMDER_SHELL, CMDER_CLINK, CMDER_ALIASES variables %lib_base% cmder_shell %print_debug% init.bat "Env Var - CMDER_ROOT=%CMDER_ROOT%" %print_debug% init.bat "Env Var - debug_output=%debug_output%" :: Set the Cmder directory paths set CMDER_CONFIG_DIR=%CMDER_ROOT%\config :: Check if we're using Cmder individual user profile if defined CMDER_USER_CONFIG ( %print_debug% init.bat "CMDER IS ALSO USING INDIVIDUAL USER CONFIG FROM '%CMDER_USER_CONFIG%'!" if not exist "%CMDER_USER_CONFIG%\..\opt" md "%CMDER_USER_CONFIG%\..\opt" set CMDER_CONFIG_DIR=%CMDER_USER_CONFIG% ) if not "%CMDER_SHELL%" == "cmd" ( %print_warning% "Incompatible 'ComSpec/Shell' Detected: %CMDER_SHELL%" set CMDER_CLINK=0 set CMDER_ALIASES=0 ) :: Pick the right version of Clink :: TODO: Support for ARM if "%PROCESSOR_ARCHITECTURE%"=="x86" ( set clink_architecture=x86 set architecture_bits=32 ) else if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( set clink_architecture=x64 set architecture_bits=64 ) else ( %print_warning% "Incompatible Processor Detected: %PROCESSOR_ARCHITECTURE%" set CMDER_CLINK=0 ) if "%CMDER_CLINK%" == "1" ( REM TODO: Detect if clink is already injected, if so goto :CLINK_FINISH goto :INJECT_CLINK ) goto :SKIP_CLINK :INJECT_CLINK %print_verbose% "Injecting Clink!" :: Check if Clink is not present if not exist "%CMDER_ROOT%\vendor\clink\clink_%clink_architecture%.exe" ( %print_error% "Clink executable is not present in 'vendor\clink\clink_%clink_architecture%.exe'" goto :SKIP_CLINK ) :: Run Clink if not exist "%CMDER_CONFIG_DIR%\settings" if not exist "%CMDER_CONFIG_DIR%\clink_settings" ( echo Generating Clink initial settings in "%CMDER_CONFIG_DIR%\clink_settings" copy "%CMDER_ROOT%\vendor\clink_settings.default" "%CMDER_CONFIG_DIR%\clink_settings" echo Additional *.lua files in "%CMDER_CONFIG_DIR%" are loaded on startup. ) if not exist "%CMDER_CONFIG_DIR%\cmder_prompt_config.lua" ( echo Creating Cmder prompt config file: "%CMDER_CONFIG_DIR%\cmder_prompt_config.lua" copy "%CMDER_ROOT%\vendor\cmder_prompt_config.lua.default" "%CMDER_CONFIG_DIR%\cmder_prompt_config.lua" ) :: Cleanup legacy Clink Settings file if exist "%CMDER_CONFIG_DIR%\settings" if exist "%CMDER_CONFIG_DIR%\clink_settings" ( del "%CMDER_CONFIG_DIR%\settings" ) :: Cleanup legacy Clink history file if exist "%CMDER_CONFIG_DIR%\.history" if exist "%CMDER_CONFIG_DIR%\clink_history" ( del "%CMDER_CONFIG_DIR%\.history" ) "%CMDER_ROOT%\vendor\clink\clink_%clink_architecture%.exe" inject --quiet --profile "%CMDER_CONFIG_DIR%" --scripts "%CMDER_ROOT%\vendor" :: Check if a fatal error occurred when trying to inject Clink if errorlevel 2 ( REM %print_error% "Clink injection has failed with error code: %errorlevel%" goto :SKIP_CLINK ) goto :CLINK_FINISH :SKIP_CLINK %print_warning% "Skipping Clink Injection!" for /f "tokens=2 delims=:." %%x in ('chcp') do set cp=%%x chcp 65001>nul :: Revert back to plain cmd.exe prompt without clink prompt $E[1;32;49m$P$S$_$E[1;30;49mλ$S$E[0m :: Add Windows Terminal shell integration support (OSC 133 sequences) if defined WT_SESSION (prompt $e]133;D$e\$e]133;A$e\$e]9;9;$P$e\%PROMPT%$e]133;B$e\) chcp %cp%>nul :CLINK_FINISH if "%CMDER_CONFIGURED%" GTR "1" ( %print_verbose% "Cmder is already configured, skipping Cmder Init!" goto :USER_ALIASES ) else if "%CMDER_CONFIGURED%" == "1" ( %print_verbose% "Cmder is already configured, skipping to Cmder User Init!" goto :USER_CONFIG_START ) :: Prepare for git-for-windows :: Detect which git.exe version to use :: * if the user points to a specific git, use that :: * test if git is in path and if yes, use that :: * last, use our vendored git :: also check that we have a recent enough version of git by examining the version string if defined GIT_INSTALL_ROOT ( if exist "%GIT_INSTALL_ROOT%\cmd\git.exe" goto :SPECIFIED_GIT set GIT_INSTALL_ROOT= ) else if "%fast_init%" == "1" ( if exist "%CMDER_ROOT%\vendor\git-for-windows\cmd\git.exe" ( %print_debug% init.bat "Skipping Git Auto-Detect!" goto :VENDORED_GIT ) %print_debug% init.bat "Fast init is enabled, vendored Git does not exist" for /F "delims=" %%F in ('where git.exe 2^>nul') do ( set "EXT_GIT_EXE=%%~fF" %print_debug% init.bat "Found User installed Git at '%%~fF'. Skipping Git Auto-Detect!" goto :SET_ENV ) ) %print_debug% init.bat "Looking for Git install root..." :: Get the version information for vendored git binary %lib_git% read_version VENDORED "%CMDER_ROOT%\vendor\git-for-windows\cmd" 2>nul %lib_git% validate_version VENDORED %GIT_VERSION_VENDORED% :: Check if git is in path for /F "delims=" %%F in ('where git.exe 2^>nul') do call :check_git "%%~fF" if defined GIT_INSTALL_ROOT ( goto :FOUND_GIT ) else ( goto :VENDORED_GIT ) :check_git set full_path="%~f1" if not defined GIT_INSTALL_ROOT ( if not [\%full_path:\cmd\git.exe=:%]==[\%full_path%] ( :: Get the absolute path to the user provided git binary %lib_git% is_git_shim "%~dp1" %lib_git% get_user_git_version %lib_git% compare_git_versions ) ) exit /b :: Our last hope: use vendored git :VENDORED_GIT if exist "%CMDER_ROOT%\vendor\git-for-windows" ( set "GIT_INSTALL_ROOT=%CMDER_ROOT%\vendor\git-for-windows" %print_debug% init.bat "Using vendored Git '%GIT_VERSION_VENDORED%'..." goto :CONFIGURE_GIT ) else ( goto :NO_GIT ) :SPECIFIED_GIT %print_debug% init.bat "Using /GIT_INSTALL_ROOT..." goto :CONFIGURE_GIT :FOUND_GIT %print_debug% init.bat "Using found Git '%GIT_VERSION_USER%' from '%GIT_INSTALL_ROOT%..." goto :CONFIGURE_GIT :CONFIGURE_GIT %print_debug% init.bat "Using Git from '%GIT_INSTALL_ROOT%..." :: Add git to the path if exist "%GIT_INSTALL_ROOT%\cmd\git.exe" %lib_path% enhance_path "%GIT_INSTALL_ROOT%\cmd" "" :: Add the unix commands at the end to not shadow windows commands like `more` and `find` if %nix_tools% equ 1 ( %print_verbose% "Preferring Windows commands" set "path_position=append" ) else ( %print_verbose% "Preferring *nix commands" set "path_position=" ) if %nix_tools% geq 1 ( if exist "%GIT_INSTALL_ROOT%\mingw32" ( %lib_path% enhance_path "%GIT_INSTALL_ROOT%\mingw32\bin" %path_position% ) else if exist "%GIT_INSTALL_ROOT%\mingw64" ( %lib_path% enhance_path "%GIT_INSTALL_ROOT%\mingw64\bin" %path_position% ) if exist "%GIT_INSTALL_ROOT%\usr\bin" ( %lib_path% enhance_path "%GIT_INSTALL_ROOT%\usr\bin" %path_position% ) ) :SET_ENV :: Plink (PuTTY Link) is a command-line connection tool similar to ssh, setting its protocol to ssh set PLINK_PROTOCOL=ssh :: Define SVN_SSH so we can use git svn with ssh svn repositories if not defined SVN_SSH set "SVN_SSH=%GIT_INSTALL_ROOT:\=\\%\\bin\\ssh.exe" :: Find locale.exe: From the git install root, from the path, using the git installed env, or fallback using the env from the path. setlocal enabledelayedexpansion if not defined git_locale if defined EXT_GIT_EXE ( set "GIT_INSTALL_ROOT=!EXT_GIT_EXE:\cmd\git.exe=!" ) endlocal && set GIT_INSTALL_ROOT=%GIT_INSTALL_ROOT% if not defined git_locale if exist "%GIT_INSTALL_ROOT%\usr\bin\locale.exe" set git_locale="%GIT_INSTALL_ROOT%\usr\bin\locale.exe" if not defined git_locale for /F "tokens=* delims=" %%F in ('where locale.exe 2^>nul') do ( if not defined git_locale set git_locale="%%F" ) if not defined git_locale if exist "%GIT_INSTALL_ROOT%\usr\bin\env.exe" set git_locale="%GIT_INSTALL_ROOT%\usr\bin\env.exe" /usr/bin/locale if not defined git_locale for /F "tokens=* delims=" %%F in ('where env.exe 2^>nul') do ( if not defined git_locale set git_locale="%%F" /usr/bin/locale ) setlocal enabledelayedexpansion if defined git_locale ( REM %print_debug% init.bat "Env Var - git_locale=!git_locale!" if not defined LANG ( for /F "delims=" %%F in ('"!git_locale!" -uU 2') do ( set "LANG=%%F" ) ) ) endlocal && set LANG=%LANG% %print_debug% init.bat "Env Var - GIT_INSTALL_ROOT=%GIT_INSTALL_ROOT%" %print_debug% init.bat "Found Git in: '%GIT_INSTALL_ROOT%'" goto :PATH_ENHANCE :NO_GIT :: Skip this if GIT WAS FOUND else we did 'endlocal' above! endlocal :PATH_ENHANCE %lib_path% enhance_path "%CMDER_ROOT%\vendor\bin" :USER_CONFIG_START %lib_path% enhance_path_recursive "%CMDER_ROOT%\bin" 0 %max_depth% if defined CMDER_USER_BIN ( %lib_path% enhance_path_recursive "%CMDER_USER_BIN%" 0 %max_depth% ) %lib_path% enhance_path "%CMDER_ROOT%" append :: Drop *.bat and *.cmd files into "%CMDER_ROOT%\config\profile.d" :: to run them at startup. %lib_profile% run_profile_d "%CMDER_ROOT%\config\profile.d" if defined CMDER_USER_CONFIG ( %lib_profile% run_profile_d "%CMDER_USER_CONFIG%\profile.d" ) :USER_ALIASES :: Allows user to override default aliases store using profile.d :: scripts run above by setting the 'aliases' env variable. :: :: Note: If overriding default aliases store file the aliases :: must also be self executing, see '.\user_aliases.cmd.default', :: and be in profile.d folder. if not defined user_aliases ( set "user_aliases=%CMDER_CONFIG_DIR%\user_aliases.cmd" ) if "%CMDER_ALIASES%" == "1" ( REM The aliases environment variable is used by alias.bat to id REM the default file to store new aliases in. if not defined aliases ( set "aliases=%user_aliases%" ) REM Make sure we have a self-extracting user_aliases.cmd file if not exist "%user_aliases%" ( echo Creating initial user_aliases store in "%user_aliases%"... copy "%CMDER_ROOT%\vendor\user_aliases.cmd.default" "%user_aliases%" ) else ( %lib_base% update_legacy_aliases ) :: Update old 'user_aliases' to new self executing 'user_aliases.cmd' if exist "%CMDER_ROOT%\config\aliases" ( echo Updating old "%CMDER_ROOT%\config\aliases" to new format... type "%CMDER_ROOT%\config\aliases" >> "%user_aliases%" del "%CMDER_ROOT%\config\aliases" ) else if exist "%user_aliases%.old_format" ( echo Updating old "%user_aliases%" to new format... type "%user_aliases%.old_format" >> "%user_aliases%" del "%user_aliases%.old_format" ) ) :: Add aliases to the environment type "%user_aliases%" | %WINDIR%\System32\findstr /b /l /i "history=cat " >nul if "%ERRORLEVEL%" == "0" ( echo Migrating alias 'history' to new Clink 1.x.x... call "%CMDER_ROOT%\vendor\bin\alias.cmd" /d history echo Restart the session to activate changes! ) call "%user_aliases%" if "%CMDER_CONFIGURED%" gtr "1" goto :CMDER_CONFIGURED :: See vendor\git-for-windows\README.portable for why we do this :: Basically we need to execute this post-install.bat because we are :: manually extracting the archive rather than executing the 7z sfx if exist "%GIT_INSTALL_ROOT%\post-install.bat" ( echo Running Git for Windows one time Post Install.... pushd "%GIT_INSTALL_ROOT%\" "%GIT_INSTALL_ROOT%\git-cmd.exe" --no-needs-console --no-cd --command=post-install.bat popd ) :: Set home path if not defined HOME set "HOME=%USERPROFILE%" %print_debug% init.bat "Env Var - HOME=%HOME%" set "initialConfig=%CMDER_ROOT%\config\user_profile.cmd" if exist "%CMDER_ROOT%\config\user_profile.cmd" ( REM Create this file and place your own command in there %print_debug% init.bat "Calling - %CMDER_ROOT%\config\user_profile.cmd" call "%CMDER_ROOT%\config\user_profile.cmd" ) if defined CMDER_USER_CONFIG ( set "initialConfig=%CMDER_USER_CONFIG%\user_profile.cmd" if exist "%CMDER_USER_CONFIG%\user_profile.cmd" ( REM Create this file and place your own command in there %print_debug% init.bat "Calling - %CMDER_USER_CONFIG%\user_profile.cmd" call "%CMDER_USER_CONFIG%\user_profile.cmd" ) ) if not exist "%initialConfig%" ( echo Creating user startup file: "%initialConfig%" copy "%CMDER_ROOT%\vendor\user_profile.cmd.default" "%initialConfig%" ) if "%CMDER_ALIASES%" == "1" if exist "%CMDER_ROOT%\bin\alias.bat" if exist "%CMDER_ROOT%\vendor\bin\alias.cmd" ( echo Cmder's 'alias' command has been moved into "%CMDER_ROOT%\vendor\bin\alias.cmd" echo to get rid of this message either: echo. echo Delete the file "%CMDER_ROOT%\bin\alias.bat" echo. echo or echo. echo If you have customized it and want to continue using it instead of the included version echo * Rename "%CMDER_ROOT%\bin\alias.bat" to "%CMDER_ROOT%\bin\alias.cmd". echo * Search for 'user-aliases' and replace it with 'user_aliases'. ) set initialConfig= :CMDER_CONFIGURED if not defined CMDER_CONFIGURED set CMDER_CONFIGURED=1 set CMDER_INIT_END=%time% if %time_init% gtr 0 ( "%cmder_root%\vendor\bin\timer.cmd" "%CMDER_INIT_START%" "%CMDER_INIT_END%" ) exit /b ================================================ FILE: vendor/lib/lib_base.cmd ================================================ @echo off set lib_base=call "%~dp0lib_base.cmd" if "%~1" == "/h" ( %lib_base% help "%~0" ) else if "%~1" neq "" ( call :%* ) exit /b :::=============================================================================== :::help - shows all sub routines in a .bat/.cmd file with documentation :::. :::include: :::. ::: call "lib_base.cmd" :::. :::usage: :::. ::: %lib_base% help "file" :::. :::options: :::. ::: file full path to file containing lib_routines to display :::------------------------------------------------------------------------------- :help for /f "tokens=* delims=:" %%a in ('%WINDIR%\System32\findstr /i /r "^:::" "%~1"') do ( if "%%a"=="." ( echo. ) else if /i "%%a" == "usage" ( echo %%a: ) else if /i "%%a" == "options" ( echo %%a: ) else if not "%%a" == "" ( echo %%a ) ) pause exit /b :::=============================================================================== :::cmder_shell - Initializes the Cmder shell environment variables :::. :::description: :::. ::: This routine sets up the Cmder shell environment by detecting the ::: command shell and initializing related variables. :::. :::include: :::. ::: call "lib_base.cmd" :::. :::usage: :::. ::: %lib_base% cmder_shell :::------------------------------------------------------------------------------- :cmder_shell call :detect_comspec %ComSpec% exit /b :::=============================================================================== :::detect_comspec - Detects the command shell being used::: :::. :::description: :::. ::: This function sets the CMDER_SHELL variable to the name of the ::: detected command shell. It also initializes the CMDER_CLINK and ::: CMDER_ALIASES variables if they are not already defined. :::. :::include: :::. ::: call "lib_base.cmd" :::. :::usage: :::. ::: %lib_base% detect_comspec %ComSpec% :::------------------------------------------------------------------------------- :detect_comspec set CMDER_SHELL=%~n1 if not defined CMDER_CLINK ( set CMDER_CLINK=1 ) if not defined CMDER_ALIASES ( set CMDER_ALIASES=1 ) exit /b :::=============================================================================== :::update_legacy_aliases - Updates the legacy alias definitions in the user_aliases file :::. :::description: :::. ::: This function checks if the user_aliases file contains the marker ::: ";= Add aliases below here". If the marker is not found, it creates ::: an initial user_aliases store by copying the default user_aliases file ::: from the CMDER_ROOT directory. If the CMDER_USER_CONFIG environment ::: variable is defined, it creates a backup of the existing user_aliases ::: file before copying the default file. :::. :::include: :::. ::: call "lib_base.cmd" :::. :::usage: :::. ::: %lib_base% update_legacy_aliases :::------------------------------------------------------------------------------- :update_legacy_aliases type "%user_aliases%" | %WINDIR%\System32\findstr /i ";= Add aliases below here" >nul if "%errorlevel%" == "1" ( echo Creating initial user_aliases store in "%user_aliases%"... if defined CMDER_USER_CONFIG ( copy "%user_aliases%" "%user_aliases%.old_format" copy "%CMDER_ROOT%\vendor\user_aliases.cmd.default" "%user_aliases%" ) else ( copy "%user_aliases%" "%user_aliases%.old_format" copy "%CMDER_ROOT%\vendor\user_aliases.cmd.default" "%user_aliases%" ) ) exit /b ================================================ FILE: vendor/lib/lib_console.cmd ================================================ @echo off call "%~dp0lib_base.cmd" set lib_console=call "%~dp0lib_console.cmd" set ESC= :: Much faster than using "%lib_console% debug_output ..." etc. set print_debug=if %debug_output% gtr 0 %lib_console% debug_output set print_verbose=if %verbose_output% gtr 0 %lib_console% verbose_output set print_warning=if %verbose_output% gtr 0 %lib_console% show_warning set print_error=%lib_console% show_error if %fast_init% gtr %verbose_output% if %fast_init% gtr %debug_output% exit /b if "%~1" == "/h" ( %lib_base% help "%~0" ) else if "%~1" neq "" ( call :%* ) exit /b :debug_output :::=============================================================================== :::debug_output - Output a debug message to the console. :::. :::include: :::. ::: call "lib_console.cmd" :::. :::usage: :::. ::: %lib_console% debug_output [caller] [message] :::. :::required: :::. ::: [caller] Script/sub routine name calling debug_output :::. ::: [message] Message text to display. :::. :::------------------------------------------------------------------------------- if %debug_output% gtr 0 echo %time% DEBUG(%~1): %~2 & echo. exit /b :verbose_output :::=============================================================================== :::verbose_output - Output a debug message to the console. :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_console% verbose_output "[message]" :::. :::required: :::. ::: [message] Message text to display. :::. :::------------------------------------------------------------------------------- if %verbose_output% gtr 0 echo %~1 exit /b :show_error :::=============================================================================== :::show_error - Output an error message to the console. :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_console% show_error "[message]" :::. :::required: :::. ::: [message] Message text to display. :::. :::------------------------------------------------------------------------------- echo %ESC%[91;1mERROR:%ESC%[0m %~1 exit /b :show_warning :::=============================================================================== :::show_warning - Output a warning message to the console. :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_console% show_warning "[message]" :::. :::required: :::. ::: [message] Message text to display. :::. :::------------------------------------------------------------------------------- echo %ESC%[93;1mWARNING:%ESC%[0m %~1 exit /b ================================================ FILE: vendor/lib/lib_git.cmd ================================================ @echo off call "%~dp0lib_base.cmd" call "%~dp0lib_console.cmd" set lib_git=call "%~dp0lib_git.cmd" if "%~1" == "/h" ( %lib_base% help "%~0" ) else if "%~1" neq "" ( call :%* ) exit /b :::=============================================================================== :::read_version - Get the git.exe version :::. :::include: :::. ::: call "lib_git.cmd" :::. :::usage: :::. ::: %lib_git% read_version "[dir_path]" :::. :::required: :::. ::: [GIT SCOPE] USER | VENDORED ::: [GIT PATH] Fully qualified path to the Git command root. :::. :::output: :::. ::: GIT_VERSION_[GIT SCOPE] Env variable containing Git semantic version string :::------------------------------------------------------------------------------- :read_version :: clear the variables set GIT_VERSION_%~1= :: set the executable path set "git_executable=%~2\git.exe" %print_debug% :read_version "Env Var - git_executable=%git_executable%" :: check if the executable actually exists if not exist "%git_executable%" ( %print_debug% :read_version "%git_executable% does not exist." exit /b -255 ) :: get the git version in the provided directory "%git_executable%" --version > "%temp%\git_version.txt" setlocal enabledelayedexpansion for /F "tokens=1,2,3 usebackq" %%A in (`type "%temp%\git_version.txt" 2^>nul`) do ( if /i "%%A %%B" == "git version" ( set "GIT_VERSION=%%C" ) else ( echo "'git --version' returned an improper version string!" %print_debug% :read_version "returned string: '%%A %%B %%C' by executable path: %git_executable%" pause exit /b ) ) endlocal & set "GIT_VERSION_%~1=%GIT_VERSION%" & %print_debug% :read_version "Env Var - GIT_VERSION_%~1=%GIT_VERSION%" exit /b :::=============================================================================== :::parse_version - Parse semantic version string 'x.x.x.x' and return the pieces :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_git% parse_version "[VERSION]" :::. :::required: :::. ::: [SCOPE] USER | VENDORED ::: [VERSION] Semantic version String. Ex: 1.2.3.4 :::. :::output: :::. ::: [SCOPE]_MAJOR Scoped Major version. ::: [SCOPE]_MINOR Scoped Minor version. ::: [SCOPE]_PATCH Scoped Patch version. ::: [SCOPE]_BUILD Scoped Build version. :::------------------------------------------------------------------------------- :parse_version :: process a `x.x.x.xxxx.x` formatted string %print_debug% :parse_version "ARGV[1]=%~1, ARGV[2]=%~2" setlocal enabledelayedexpansion for /F "tokens=1-3* delims=.,-" %%A in ("%2") do ( set "%~1_MAJOR=%%A" set "%~1_MINOR=%%B" set "%~1_PATCH=%%C" set "%~1_BUILD=%%D" ) REM endlocal & set "%~1_MAJOR=!%~1_MAJOR!" & set "%~1_MINOR=!%~1_MINOR!" & set "%~1_PATCH=!%~1_PATCH!" & set "%~1_BUILD=!%~1_BUILD!" if "%~1" == "VENDORED" ( endlocal & set "%~1_MAJOR=%VENDORED_MAJOR%" & set "%~1_MINOR=%VENDORED_MINOR%" & set "%~1_PATCH=%VENDORED_PATCH%" & set "%~1_BUILD=%VENDORED_BUILD%" ) else ( endlocal & set "%~1_MAJOR=%USER_MAJOR%" & set "%~1_MINOR=%USER_MINOR%" & set "%~1_PATCH=%USER_PATCH%" & set "%~1_BUILD=%USER_BUILD%" ) exit /b :endlocal_set_git_version :::=============================================================================== :::validate_version - Validate semantic version string 'x.x.x.x' :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_git% validate_version [SCOPE] [VERSION] :::. :::required: :::. ::: [SCOPE] Example: USER | VENDORED ::: [VERSION] Semantic version String. Ex: 1.2.3.4 :::------------------------------------------------------------------------------- :validate_version :: now parse the version information into the corresponding variables %print_debug% :validate_version "ARGV[1]=%~1, ARGV[2]=%~2" call :parse_version %~1 %~2 :: ... and maybe display it, for debugging purposes. REM %print_debug% :validate_version "Found Git Version for %~1: !%~1_MAJOR!.!%~1_MINOR!.!%~1_PATCH!.!%~1_BUILD!" if "%~1" == "VENDORED" ( %print_debug% :validate_version "Found Git Version for %~1: %VENDORED_MAJOR%.%VENDORED_MINOR%.%VENDORED_PATCH%.%VENDORED_BUILD%" ) else ( %print_debug% :validate_version "Found Git Version for %~1: %USER_MAJOR%.%USER_MINOR%.%USER_PATCH%.%USER_BUILD%" ) exit /b :::=============================================================================== :::compare_version - Compare semantic versions and return latest version :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_git% validate_version [SCOPE1] [SCOPE2] :::. :::required: :::. ::: [SCOPE1] Example: USER ::: [SCOPE2] Example: VENDOR :::------------------------------------------------------------------------------- :compare_versions :: checks all major, minor, patch and build variables for the given arguments. :: whichever binary that has the most recent version will be used based on the return code. %print_debug% ":compare_versions" "Comparing:" %print_debug% ":compare_versions" "%~1: %USER_MAJOR%.%USER_MINOR%.%USER_PATCH%.%USER_BUILD%" %print_debug% ":compare_versions" "%~2: %VENDORED_MAJOR%.%VENDORED_MINOR%.%VENDORED_PATCH%.%VENDORED_BUILD%" setlocal enabledelayedexpansion if !%~1_MAJOR! GTR !%~2_MAJOR! (endlocal & exit /b 1) if !%~1_MAJOR! LSS !%~2_MAJOR! (endlocal & exit /b -1) if !%~1_MINOR! GTR !%~2_MINOR! (endlocal & exit /b 1) if !%~1_MINOR! LSS !%~2_MINOR! (endlocal & exit /b -1) if !%~1_PATCH! GTR !%~2_PATCH! (endlocal & exit /b 1) if !%~1_PATCH! LSS !%~2_PATCH! (endlocal & exit /b -1) if !%~1_BUILD! GTR !%~2_BUILD! (endlocal & exit /b 1) if !%~1_BUILD! LSS !%~2_BUILD! (endlocal & exit /b -1) :: looks like we have the same versions. endlocal & exit /b 0 :::=============================================================================== :::is_git_shim - Check if the directory has a git.shim file :::. :::description: :::. ::: Shim is a small helper program for Scoop that calls the executable configured in git.shim file ::: See: github.com/ScoopInstaller/Shim and github.com/cmderdev/cmder/pull/1905 :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_git% is_git_shim [filepath] :::. :::required: :::. ::: [filepath] :::------------------------------------------------------------------------------- :is_git_shim pushd "%~1" :: check if there is a shim file - if yes, read the actual executable path setlocal enabledelayedexpansion if exist git.shim ( for /F "tokens=2 delims== " %%I in (git.shim) do ( pushd %%~dpI set "test_dir=!CD!" popd ) ) else ( set "test_dir=!CD!" ) endlocal & set "test_dir=%test_dir%" popd exit /b :::=============================================================================== :::compare_git_versions - Compare the user git version against the vendored version :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_git% compare_git_versions :::------------------------------------------------------------------------------- :compare_git_versions setlocal enabledelayedexpansion if ERRORLEVEL 0 ( :: compare the user git version against the vendored version %lib_git% compare_versions USER VENDORED set result=!ERRORLEVEL! %print_debug% ":compare_git_versions" "campare versions_result: !result!" :: use the user provided git if its version is greater than, or equal to the vendored git if !result! geq 0 ( if exist "!test_dir:~0,-4!\cmd\git.exe" ( set "GIT_INSTALL_ROOT=!test_dir:~0,-4!" ) else ( set "GIT_INSTALL_ROOT=!test_dir!" ) ) else ( %print_debug% ":compare_git_versions" "Found old !GIT_VERSION_USER! in !test_dir!, but not using..." ) ) else ( :: compare the user git version against the vendored version :: if the user provided git executable is not found IF ERRORLEVEL -255 IF NOT ERRORLEVEL -254 ( :: if not exist "%git_executable%" ( %print_debug% ":compare_git_versions" "No git at '%git_executable%' found." set test_dir= ) ) endlocal && set "GIT_INSTALL_ROOT=%GIT_INSTALL_ROOT%" && set test_dir= exit /b :::=============================================================================== :::get_user_git_version - Get the version information for the user provided git binary :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: %lib_git% get_user_git_version :::------------------------------------------------------------------------------- :get_user_git_version :: get the version information for the user provided git binary %lib_git% read_version USER "%test_dir%" 2>nul %print_debug% ":get_user_git_version" "get_user_git_version GIT_VERSION_USER: %GIT_VERSION_USER%" %lib_git% validate_version USER %GIT_VERSION_USER% exit /b ================================================ FILE: vendor/lib/lib_path.cmd ================================================ @echo off call "%~dp0lib_base.cmd" call "%~dp0lib_console.cmd" set lib_path=call "%~dp0lib_path.cmd" if "%~1" == "/h" ( %lib_base% help "%~0" ) else if "%~1" neq "" ( call :%* ) setlocal enabledelayedexpansion if not defined find_pathext ( set "find_pathext=!PATHEXT:;= !" set "find_pathext=!find_pathext:.=\.!" ) endlocal & set "find_pathext=%find_pathext%" exit /b :enhance_path :::=============================================================================== :::enhance_path - Add a directory to the path env variable if required. ::: :::include: ::: ::: call "lib_path.cmd" ::: :::usage: ::: ::: %lib_path% enhance_path "[dir_path]" [append] ::: :::required: ::: ::: [dir_path] Fully qualified directory path. Ex: "c:\bin" ::: :::options: ::: ::: append Append to the path env variable rather than pre-pend. ::: ::: :::output: ::: ::: path Sets the path env variable if required. :::------------------------------------------------------------------------------- if "%~1" neq "" ( set "add_path=%~1" ) else ( %print_error% "You must specify a directory to add to the path!" exit /b 1 ) if "%~2" neq "" if /i "%~2" == "append" ( set "position=%~2" ) else ( set "position=" ) dir "%add_path%" 2>NUL | findstr -i -e "%find_pathext%" >NUL if "%ERRORLEVEL%" == "0" ( set "add_to_path=%add_path%" ) else ( set "add_to_path=" ) if "%fast_init%" == "1" ( if "%position%" == "append" ( set "PATH=%PATH%;%add_to_path%" ) else ( set "PATH=%add_to_path%;%PATH%" ) goto :end_enhance_path ) else if "%add_to_path%" equ "" ( goto :end_enhance_path ) set found=0 set "find_query=%add_to_path%" set "find_query=%find_query:\=\\%" set "find_query=%find_query: =\ %" set "OLD_PATH=%PATH%" setlocal enabledelayedexpansion if "!found!" == "0" ( echo "!PATH!"|!WINDIR!\System32\findstr >nul /I /R /C:";!find_query!;" call :set_found ) %print_debug% :enhance_path "Env Var INSIDE PATH !find_query! - found=!found!" if /i "!position!" == "append" ( if "!found!" == "0" ( echo "!PATH!"|!WINDIR!\System32\findstr >nul /I /R /C:";!find_query!\"$" call :set_found ) %print_debug% :enhance_path "Env Var END PATH !find_query! - found=!found!" ) else ( if "!found!" == "0" ( echo "!PATH!"|!WINDIR!\System32\findstr >nul /I /R /C:"^\"!find_query!;" call :set_found ) %print_debug% :enhance_path "Env Var BEGIN PATH !find_query! - found=!found!" ) endlocal & set found=%found% if "%found%" == "0" ( if /i "%position%" == "append" ( %print_debug% :enhance_path "Appending '%add_to_path%'" set "PATH=%PATH%;%add_to_path%" ) else ( %print_debug% :enhance_path "Prepending '%add_to_path%'" set "PATH=%add_to_path%;%PATH%" ) set found=1 ) :end_enhance_path set "PATH=%PATH:;;=;%" REM echo %path%|wc -c if "%fast_init%" == "1" exit /b if not "%OLD_PATH:~0,3000%" == "%OLD_PATH:~0,3001%" goto :toolong if not "%OLD_PATH%" == "%PATH%" goto :changed exit /b :toolong set "_rand=%RANDOM%" if exist "%temp%\%_rand%_cmder_lib_pathA" del "%temp%\%_rand%_cmder_lib_pathA" 2>nul 1>nul if exist "%temp%\%_rand%_cmder_lib_pathB" del "%temp%\%_rand%_cmder_lib_pathB" 2>nul 1>nul if exist "%temp%\%_rand%_cmder_lib_pathA" goto :toolong if exist "%temp%\%_rand%_cmder_lib_pathB" goto :toolong echo "%OLD_PATH%">"%temp%\%_rand%_cmder_lib_pathA" if errorlevel 1 ( if exist "%temp%\%_rand%_cmder_lib_pathA" del "%temp%\%_rand%_cmder_lib_pathA" & goto :toolong ) echo "%PATH%">"%temp%\%_rand%_cmder_lib_pathB" if errorlevel 1 ( if exist "%temp%\%_rand%_cmder_lib_pathA" del "%temp%\%_rand%_cmder_lib_pathA" & if exist "%temp%\%_rand%_cmder_lib_pathB" del "%temp%\%_rand%_cmder_lib_pathB" & goto :toolong ) fc /b "%temp%\%_rand%_cmder_lib_pathA" "%temp%\%_rand%_cmder_lib_pathB" 2>nul 1>nul if errorlevel 1 ( del "%temp%\%_rand%_cmder_lib_pathA" & del "%temp%\%_rand%_cmder_lib_pathB" & set "_rand=" & goto :changed ) del "%temp%\%_rand%_cmder_lib_pathA" & del "%temp%\%_rand%_cmder_lib_pathB" & set "_rand=" exit /b :changed %print_debug% :enhance_path "END Env Var - PATH=%PATH%" %print_debug% :enhance_path "Env Var %find_query% - found=%found%" exit /b exit /b :set_found if "%ERRORLEVEL%" == "0" ( set found=1 ) exit /b :enhance_path_recursive :::=============================================================================== :::enhance_path_recursive - Add a directory and subs to the path env variable if ::: required. :::. :::include: :::. ::: call "$0" :::. :::usage: :::. ::: call "%~DP0lib_path" enhance_path_recursive "[dir_path]" [max_depth] [append] :::. :::required: :::. ::: [dir_path] Fully qualified directory path. Ex: "c:\bin" :::. :::options: :::. ::: [max_depth] Max recursion depth. Default: 1 :::. ::: append Append instead to path env variable rather than pre-pend. :::. :::output: :::. ::: path Sets the path env variable if required. :::------------------------------------------------------------------------------- if "%~1" neq "" ( set "add_path=%~1" ) else ( %print_error% "You must specify a directory to add to the path!" exit /b 1 ) set "depth=%~2" set "max_depth=%~3" if "%~4" neq "" if /i "%~4" == "append" ( set "position=%~4" ) else ( set "position=" ) dir "%add_path%" 2>NUL | findstr -i -e "%find_pathext%" >NUL if "%ERRORLEVEL%" == "0" ( set "add_to_path=%add_path%" ) else ( set "add_to_path=" ) if "%fast_init%" == "1" ( if "%add_to_path%" neq "" ( call :enhance_path "%add_to_path%" %position% ) ) set "PATH=%PATH:;;=;%" if "%fast_init%" == "1" ( exit /b ) %print_debug% :enhance_path_recursive "Env Var - add_path=%add_to_path%" %print_debug% :enhance_path_recursive "Env Var - position=%position%" %print_debug% :enhance_path_recursive "Env Var - depth=%depth%" %print_debug% :enhance_path_recursive "Env Var - max_depth=%max_depth%" if %max_depth% gtr %depth% ( if "%add_to_path%" neq "" ( %print_debug% :enhance_path_recursive "Adding parent directory - '%add_to_path%'" call :enhance_path "%add_to_path%" %position% ) call :set_depth call :loop_depth ) set "PATH=%PATH%" exit /b :set_depth set /a "depth=%depth%+1" exit /b :loop_depth if %depth% == %max_depth% ( exit /b ) for /d %%i in ("%add_path%\*") do ( %print_debug% :enhance_path_recursive "Env Var BEFORE - depth=%depth%" %print_debug% :enhance_path_recursive "Found Subdirectory - '%%~fi'" call :enhance_path_recursive "%%~fi" %depth% %max_depth% %position% %print_debug% :enhance_path_recursive "Env Var AFTER- depth=%depth%" ) exit /b ================================================ FILE: vendor/lib/lib_profile.cmd ================================================ @echo off call "%~dp0lib_base.cmd" call "%~dp0lib_console.cmd" set lib_profile=call "%~dp0lib_profile.cmd" if "%~1" == "/h" ( %lib_base% help "%~0" ) else if "%~1" neq "" ( call :%* ) exit /b :::=============================================================================== :::run_profile_d - Run all scripts in the passed directory path :::. :::include: :::. ::: call "lib_profile.cmd" :::. :::usage: :::. ::: %lib_profile% "[dir_path]" :::. :::required: :::. ::: [dir_path] Fully qualified directory path containing init *.cmd|*.bat. ::: Example: "c:\bin" :::. ::: path Sets the path env variable if required. :::------------------------------------------------------------------------------- :run_profile_d if not exist "%~1" ( mkdir "%~1" ) pushd "%~1" for /f "usebackq" %%x in ( `dir /b *.bat *.cmd 2^>nul` ) do ( %print_verbose% "Calling '%~1\%%x'..." call "%~1\%%x" ) popd exit /b ================================================ FILE: vendor/lib/start-ssh-agent.sh ================================================ # Copied from https://help.github.com/articles/working-with-ssh-key-passphrases env=~/.ssh/agent.env agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; } agent_start () { (umask 077; ssh-agent >| "$env") . "$env" >| /dev/null ; } agent_load_env # agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not running agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?) if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then agent_start ssh-add elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then ssh-add fi unset env ================================================ FILE: vendor/profile.ps1 ================================================ # Init Script for PowerShell # Created as part of Cmder project # NOTE: This file must be saved using UTF-8 with BOM encoding for prompt symbol to work correctly. # !!! THIS FILE IS OVERWRITTEN WHEN CMDER IS UPDATED # !!! Use "%CMDER_ROOT%\config\user_profile.ps1" to add your own startup commands $CMDER_INIT_START = Get-Date # Determine the script root if not already set if (!$PSScriptRoot) { $PSScriptRoot = Split-Path $Script:MyInvocation.MyCommand.Path } # We do this for Powershell as Admin Sessions because CMDER_ROOT is not being set. if (!$ENV:CMDER_ROOT) { if ($ENV:ConEmuDir) { $ENV:CMDER_ROOT = Resolve-Path($ENV:ConEmuDir + "\..\..") } else { $ENV:CMDER_ROOT = Resolve-Path($PSScriptRoot + "\..") } } # Remove trailing '\' $ENV:CMDER_ROOT = ($ENV:CMDER_ROOT).TrimEnd("\") # Recent PowerShell versions include PowerShellGet out of the box $moduleInstallerAvailable = [bool](Get-Command -Name 'Install-Module' -ErrorAction SilentlyContinue) # Enable Debug and Verbose output if CMDER_DEBUG environment variable is set to '1' or 'true' if ($env:CMDER_DEBUG -and ($env:CMDER_DEBUG -match '^(1|true)$')) { $DebugPreference = 'Continue' $VerbosePreference = 'Continue' } # Add Cmder modules directory to the autoload path. $CmderModulePath = Join-path $PSScriptRoot "psmodules/" # Import Cmder functions $CmderFunctions = Join-Path $CmderModulePath "Cmder.ps1" . $CmderFunctions # Configure PSModulePath to include Cmder modules if not already present if (-not $moduleInstallerAvailable -and -not $env:PSModulePath.Contains($CmderModulePath) ) { $env:PSModulePath = $env:PSModulePath.Insert(0, "$CmderModulePath;") } if ($env:CMDER_USER_CONFIG) { Write-Verbose "CMDER IS ALSO USING INDIVIDUAL USER CONFIG FROM '$ENV:CMDER_USER_CONFIG'!" } # Read vendored Git Version $gitVendorPath = Join-Path $ENV:CMDER_ROOT 'vendor\git-for-windows\cmd' $gitVersionVendor = Get-GitVersion -GitPath $gitVendorPath if (-not [string]::IsNullOrEmpty($gitVersionVendor)) { Write-Debug "GIT VENDOR: ${gitVersionVendor}" } else { Write-Debug "GIT VENDOR is not present at '$gitVendorPath'" } # Get user installed Git version(s) if found, and compare them with vendored version. foreach ($git in (Get-Command -ErrorAction SilentlyContinue 'git')) { Write-Debug "GIT USER PATH: $($git.Path)" $gitDir = Split-Path -Path $git.Path $gitDir = Get-GitShimPath -GitPath $gitDir $gitVersionUser = Get-GitVersion -GitPath $gitDir Write-Debug "GIT USER VERSION: ${gitVersionUser}" $useGitVersion = Compare-GitVersion -UserVersion $gitVersionUser -VendorVersion $gitVersionVendor Write-Debug "Using Git Version: ${useGitVersion}" # Use user installed Git if ($null -eq $gitPathUser) { Write-Debug "Detected Git from mingw bin directory" Write-Debug "Git Dir: ${gitDir}" if ($gitDir -match '\\mingw32\\bin' -or $gitDir -match '\\mingw64\\bin') { $gitPathUser = $gitDir.subString(0, $gitDir.Length - 12) } else { $gitPathUser = $gitDir.subString(0, $gitDir.Length - 4) } Write-Debug "Git Path User: ${gitDir}" } if ($useGitVersion -eq $gitVersionUser) { Write-Debug "Using Git Dir: ${gitDir}" $ENV:GIT_INSTALL_ROOT = $gitPathUser $ENV:GIT_INSTALL_TYPE = 'USER' break } } # Use vendored Git if no user Git found or user Git is older than vendored Git if ($null -eq $ENV:GIT_INSTALL_ROOT -and $null -ne $gitVersionVendor) { $ENV:GIT_INSTALL_ROOT = "$ENV:CMDER_ROOT\vendor\git-for-windows" $ENV:GIT_INSTALL_TYPE = 'VENDOR' } Write-Debug "GIT_INSTALL_ROOT: ${ENV:GIT_INSTALL_ROOT}" Write-Debug "GIT_INSTALL_TYPE: ${ENV:GIT_INSTALL_TYPE}" if ($null -ne $ENV:GIT_INSTALL_ROOT) { $env:Path = Set-GitPath -GitRoot "$ENV:GIT_INSTALL_ROOT" -GitType $ENV:GIT_INSTALL_TYPE -GitPathUser $gitPathUser } # Create 'vi' alias for 'vim' if vim is available if (Get-Command -Name "vim" -ErrorAction SilentlyContinue) { New-Alias -name "vi" -value vim } # PSReadline configuration if (Get-Module PSReadline -ErrorAction "SilentlyContinue") { # Display an extra prompt line between the prompt and the command input Set-PSReadlineOption -ExtraPromptLineCount 1 # Invoked when Enter is pressed to submit a command if ($env:WT_SESSION) { Set-PSReadLineKeyHandler -Key Enter -ScriptBlock { # Get the current command line $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) # Accept the line first [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() # Emit OSC 133;C to mark start of command output # This is written directly to the console after the command is accepted [Console]::Write("$([char]0x1B)]133;C$([char]7)") } } } # Pre-assign default prompt hooks so the first run of Cmder gets a working prompt $env:gitLoaded = $null [ScriptBlock]$PrePrompt = {} [ScriptBlock]$PostPrompt = {} [ScriptBlock]$CmderPrompt = { # Check if we're currently running under Admin privileges $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal] $identity $adminRole = [Security.Principal.WindowsBuiltInRole]::Administrator $color = "White" if ($principal.IsInRole($adminRole)) { $color = "Red" } $Host.UI.RawUI.ForegroundColor = "White" Microsoft.PowerShell.Utility\Write-Host "PS " -NoNewline -ForegroundColor $color Microsoft.PowerShell.Utility\Write-Host $pwd.ProviderPath -NoNewLine -ForegroundColor Green Show-GitStatus -Path $pwd.ProviderPath Microsoft.PowerShell.Utility\Write-Host "`nλ" -NoNewLine -ForegroundColor "DarkGray" } # Enhance Path $env:Path = "$Env:CMDER_ROOT\bin;$Env:CMDER_ROOT\vendor\bin;$env:Path;$Env:CMDER_ROOT" # Drop *.ps1 files into "$ENV:CMDER_ROOT\config\profile.d" # to source them at startup. if (-not (Test-Path -PathType container "$ENV:CMDER_ROOT\config\profile.d")) { New-Item -ItemType Directory -Path "$ENV:CMDER_ROOT\config\profile.d" } Push-Location $ENV:CMDER_ROOT\config\profile.d foreach ($x in Get-ChildItem *.psm1) { Write-Verbose "Sourcing $x" Import-Module $x } foreach ($x in Get-ChildItem *.ps1) { Write-Verbose "Sourcing $x" . $x } Pop-Location # Drop *.ps1 files into "$ENV:CMDER_USER_CONFIG\config\profile.d" # to source them at startup. Requires using cmder.exe /C [cmder_user_root_path] argument if ($ENV:CMDER_USER_CONFIG -ne "" -and (Test-Path "$ENV:CMDER_USER_CONFIG\profile.d")) { Push-Location $ENV:CMDER_USER_CONFIG\profile.d foreach ($x in Get-ChildItem *.psm1) { Write-Verbose "Sourcing $x" Import-Module $x } foreach ($x in Get-ChildItem *.ps1) { Write-Verbose "Sourcing $x" . $x } Pop-Location } # Renaming to "config\user_profile.ps1" to "user_profile.ps1" for consistency. if (Test-Path "$env:CMDER_ROOT\config\user-profile.ps1") { Rename-Item "$env:CMDER_ROOT\config\user-profile.ps1" user_profile.ps1 } $CmderUserProfilePath = Join-Path $env:CMDER_ROOT "config\user_profile.ps1" if (Test-Path $CmderUserProfilePath) { # Create this file and place your own command in there. . "$CmderUserProfilePath" # user_profile.ps1 is not a module DO NOT USE import-module } if ($ENV:CMDER_USER_CONFIG) { # Renaming to "$env:CMDER_USER_CONFIG\user-profile.ps1" to "user_profile.ps1" for consistency. if (Test-Path "$env:CMDER_USER_CONFIG\user-profile.ps1") { Rename-Item "$env:CMDER_USER_CONFIG\user-profile.ps1" user_profile.ps1 } $env:Path = "$Env:CMDER_USER_CONFIG\bin;$env:Path" $CmderUserProfilePath = Join-Path $ENV:CMDER_USER_CONFIG "user_profile.ps1" if (Test-Path $CmderUserProfilePath) { . "$CmderUserProfilePath" # user_profile.ps1 is not a module DO NOT USE import-module } } if (-not (Test-Path $CmderUserProfilePath)) { $CmderUserProfilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($CmderUserProfilePath) Write-Host -NoNewline "`r" Write-Host -BackgroundColor Green -ForegroundColor Black "First Run: Creating user startup file: $CmderUserProfilePath" Copy-Item "$env:CMDER_ROOT\vendor\user_profile.ps1.default" -Destination $CmderUserProfilePath } # # Prompt Section # Users should modify their user_profile.ps1 as it will be safe from updates. # # Only set the prompt if it is currently set to the default # This allows users to configure the prompt in their user_profile.ps1 or config\profile.d\*.ps1 if ( $(Get-Command prompt).Definition -match 'PS \$\(\$executionContext.SessionState.Path.CurrentLocation\)\$\(' -and ` $(Get-Command prompt).Definition -match '\(\$nestedPromptLevel \+ 1\)\) ";') { <# This scriptblock runs every time the prompt is returned. Explicitly use functions from MS namespace to protect from being overridden in the user session. Custom prompt functions are loaded in as constants to get the same behaviour #> [ScriptBlock]$Prompt = { $lastSUCCESS = $? $realLastExitCode = $LastExitCode # Terminal-specific escape sequences for Windows Terminal and ConEmu if ($env:WT_SESSION -or $env:ConEmuPID) { # Emit OSC 133;D to mark the end of command execution with exit code if ($env:WT_SESSION) { Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]133;D;$realLastExitCode$([char]7)" } # Emit OSC 9;9 to enable directory tracking # Enables "Duplicate Tab" and "Split Pane" to preserve the working directory $loc = $executionContext.SessionState.Path.CurrentLocation if ($loc.Provider.Name -eq "FileSystem") { Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]9;9;`"$($loc.ProviderPath)`"$([char]0x1B)\" } # Emit OSC 133;A to mark the start of the prompt # Enables features like command navigation, selection, and visual separators if ($env:WT_SESSION) { Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]133;A$([char]7)" } } $host.UI.RawUI.WindowTitle = Microsoft.PowerShell.Management\Split-Path $pwd.ProviderPath -Leaf Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x200B)`r$([char]0x1B)[K" if ($lastSUCCESS -or ($LastExitCode -ne 0)) { Microsoft.PowerShell.Utility\Write-Host } PrePrompt | Microsoft.PowerShell.Utility\Write-Host -NoNewline CmderPrompt PostPrompt | Microsoft.PowerShell.Utility\Write-Host -NoNewline # Emit OSC 133;B to mark the start of command input (after prompt, before user types) if ($env:WT_SESSION) { Microsoft.PowerShell.Utility\Write-Host -NoNewline "$([char]0x1B)]133;B$([char]7)" } $global:LastExitCode = $realLastExitCode return " " } # Once Created these code blocks cannot be overwritten # if (-not $(Get-Command PrePrompt).Options -match 'Constant') {Set-Item -Path function:\PrePrompt -Value $PrePrompt -Options Constant} # if (-not $(Get-Command CmderPrompt).Options -match 'Constant') {Set-Item -Path function:\CmderPrompt -Value $CmderPrompt -Options Constant} # if (-not $(Get-Command PostPrompt).Options -match 'Constant') {Set-Item -Path function:\PostPrompt -Value $PostPrompt -Options Constant} Set-Item -Path function:\PrePrompt -Value $PrePrompt -Options Constant Set-Item -Path function:\CmderPrompt -Value $CmderPrompt -Options Constant Set-Item -Path function:\PostPrompt -Value $PostPrompt -Options Constant # Functions can be made constant only at creation time # ReadOnly at least requires `-force` to be overwritten # if (!$(Get-Command Prompt).Options -match 'ReadOnly') {Set-Item -Path function:\prompt -Value $Prompt -Options ReadOnly} Set-Item -Path function:\prompt -Value $Prompt -Options ReadOnly } $CMDER_INIT_END = Get-Date $ElapsedTime = New-TimeSpan -Start $CMDER_INIT_START -End $CMDER_INIT_END Write-Verbose "Elapsed Time: $($ElapsedTime.TotalSeconds) seconds total" ================================================ FILE: vendor/psmodules/Cmder.ps1 ================================================ function Get-GitVersion { param( [Parameter(Mandatory = $true)] [string]$GitPath ) $gitExecutable = Join-Path $GitPath "git.exe" if (-not (Test-Path $gitExecutable)) { return $null } # Execute 'git --version' and capture output $gitVersion = & $gitExecutable --version 2>$null if ($gitVersion -match 'git version\s+(\S+)') { return $Matches[1] } Write-Debug "Git executable path: $gitExecutable" Write-Error "'git --version' returned an improper version string!" Write-Error "Unable to determine Git version from output: $gitVersion" return $null } function Get-GitShimPath { param( [Parameter(Mandatory = $true)] [string]$GitPath ) # Check if there is a shim file - if yes, read the actual executable path # See: github.com/ScoopInstaller/Shim $shimFile = Join-Path $GitPath "git.shim" if (Test-Path $shimFile) { $shimContent = Get-Content $shimFile -Raw if ($shimContent -match '^\s*path\s*=\s*(.+)\s*$') { $GitPath = $Matches[1].Trim().Replace('\git.exe', '') } } return $GitPath } function Compare-Version { param( [Parameter(Mandatory = $false)] [AllowNull()] [string]$UserVersion, [Parameter(Mandatory = $false)] [AllowNull()] [string]$VendorVersion ) if ([string]::IsNullOrEmpty($UserVersion)) { return -1 } if ([string]::IsNullOrEmpty($VendorVersion)) { return 1 } # Split version strings by dots to compare segment by segment # For "2.49.0.windows.1", we get: ["2", "49", "0", "windows", "1"] $userParts = $UserVersion -split '\.' $vendorParts = $VendorVersion -split '\.' $maxLength = [Math]::Max($userParts.Count, $vendorParts.Count) for ($i = 0; $i -lt $maxLength; $i++) { $userPart = if ($i -lt $userParts.Count) { $userParts[$i] } else { '' } $vendorPart = if ($i -lt $vendorParts.Count) { $vendorParts[$i] } else { '' } # Check if both parts are purely numeric $userIsNumeric = $userPart -match '^\d+$' $vendorIsNumeric = $vendorPart -match '^\d+$' if ($userIsNumeric -and $vendorIsNumeric) { # Both numeric: compare as integers (so 49 > 5, not lexicographic) $userNum = [int]$userPart $vendorNum = [int]$vendorPart if ($userNum -gt $vendorNum) { return 1 } if ($userNum -lt $vendorNum) { return -1 } } elseif ($userIsNumeric -and -not $vendorIsNumeric) { # Numeric segment comes before text segment (e.g., "2.0" < "2.0.rc1") return -1 } elseif (-not $userIsNumeric -and $vendorIsNumeric) { # Text segment comes after numeric segment return 1 } else { # Both are text: use case-insensitive lexicographic comparison $cmp = [string]::Compare($userPart, $vendorPart, $true) if ($cmp -ne 0) { return [Math]::Sign($cmp) } } } return 0 } function Compare-GitVersion { param( [Parameter(Mandatory = $false)] [AllowNull()] [string]$UserVersion, [Parameter(Mandatory = $false)] [AllowNull()] [string]$VendorVersion ) $result = Compare-Version -UserVersion $UserVersion -VendorVersion $VendorVersion Write-Debug "Compare Versions Result: $result" if ($result -ge 0) { return $UserVersion } return $VendorVersion } function Set-GitPath { param( [Parameter(Mandatory = $true)] [string]$GitRoot, [Parameter(Mandatory = $true)] [string]$GitType, [Parameter(Mandatory = $false)] [string]$GitPathUser ) # Proposed Behavior # Modify the path if we are using VENDORED Git, do nothing if using USER Git. # If User Git is installed but is older, match its path config adding paths # in the same path positions allowing a user to configure Cmder Git path # using locally installed Git Path Config. if ($GitType -ne 'VENDOR') { return $env:Path } $newPath = $env:Path # Replace user Git path with vendored Git if user path exists if ($GitPathUser) { Write-Verbose "Cmder 'profile.ps1': Replacing older user Git path '$GitPathUser' with newer vendored Git path '$GitRoot' in the system path..." $newPath = $newPath -ireplace [regex]::Escape($GitPathUser), $GitRoot } else { # Add Git cmd directory to the path $gitCmd = Join-Path $GitRoot "cmd" if (-not ($newPath -match [regex]::Escape($gitCmd))) { Write-Debug "Adding $gitCmd to the path" $newPath = "$gitCmd;$newPath" } # Add mingw[32|64]\bin directories to the path, if they exist and not already present # Prefer mingw64 on 64-bit systems, mingw32 on 32-bit systems $is64Bit = [Environment]::Is64BitOperatingSystem $mingwDirs = if ($is64Bit) { @('mingw64', 'mingw32') } else { @('mingw32') } foreach ($mingw in $mingwDirs) { $mingwBin = Join-Path $GitRoot "$mingw\bin" if ((Test-Path $mingwBin) -and -not ($newPath -match [regex]::Escape($mingwBin))) { Write-Debug "Adding $mingwBin to the path" $newPath = "$newPath;$mingwBin" break } } # Add usr\bin directory to the path $usrBin = Join-Path $GitRoot "usr\bin" if ((Test-Path $usrBin) -and -not ($newPath -match [regex]::Escape($usrBin))) { Write-Debug "Adding $usrBin to the path" $newPath = "$newPath;$usrBin" } } return $newPath } function Import-Git { $gitModule = Get-Module -Name Posh-Git -ListAvailable if (-not $gitModule) { Microsoft.PowerShell.Utility\Write-Host -NoNewline "`r`n" Write-Warning "Missing git support, install posh-git with 'Install-Module posh-git' and restart Cmder." Microsoft.PowerShell.Utility\Write-Host -NoNewline "`r$([char]0x1B)[A" return $false } # Import posh-git module (works for all versions) Import-Module Posh-Git -ErrorAction SilentlyContinue | Out-Null # Apply version-specific settings for posh-git 1.0.0+ if (($gitModule.Version -ge [version]"1.0.0") -and (Get-Variable -Name GitPromptSettings -ErrorAction SilentlyContinue)) { $GitPromptSettings.AnsiConsole = $false } return $true } function Show-GitStatus { param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not (Get-Command git -ErrorAction SilentlyContinue)) { return } $gitDir = Join-Path $Path '.git' if (-not (Test-Path $gitDir)) { $parentPath = Split-Path $Path if ($parentPath) { Show-GitStatus -Path $parentPath } return } if (Get-GitStatusSetting) { if ($null -eq $env:gitLoaded) { $env:gitLoaded = Import-Git } if ($env:gitLoaded -eq $true) { Write-VcsStatus } } else { $headFile = Join-Path $gitDir 'HEAD' if (Test-Path $headFile) { $headContent = Get-Content $headFile -Raw if ($headContent -match 'ref: refs/heads/(.+)') { $branchName = $Matches[1].Trim() } else { $shortHash = $headContent.Substring(0, [Math]::Min(7, $headContent.Length)) $branchName = "HEAD detached at $shortHash" } Microsoft.PowerShell.Utility\Write-Host " [$branchName]" -NoNewline -ForegroundColor White } } } function Get-GitStatusSetting { $gitConfig = git --no-pager config -l 2>$null | Out-String # Check if git status display is disabled via config # Matches: cmder.status=false or cmder.psstatus=false (PowerShell-specific) if ($gitConfig -match '(?m)^cmder\.(ps)?status=false$') { return $false } return $true } ================================================ FILE: vendor/sources.json ================================================ [ { "name": "git-for-windows", "version": "2.52.0.windows.1", "url": "https://github.com/git-for-windows/git/releases/download/v2.52.0.windows.1/PortableGit-2.52.0-64-bit.7z.exe" }, { "name": "clink", "version": "1.9.5", "url": "https://github.com/chrisant996/clink/releases/download/v1.9.5/clink.1.9.5.ee6b4f.zip" }, { "name": "conemu-maximus5", "version": "23.07.24", "url": "https://github.com/ConEmu/ConEmu/releases/download/v23.07.24/ConEmuPack.230724.7z" }, { "name": "windows-terminal", "version": "1.23.12811.0", "url": "https://github.com/microsoft/terminal/releases/download/v1.23.12811.0/Microsoft.WindowsTerminal_1.23.12811.0_x64.zip" }, { "name": "clink-completions", "version": "0.6.7", "url": "https://github.com/vladimir-kotikov/clink-completions/archive/v0.6.7.zip" } ] ================================================ FILE: vendor/user_aliases.cmd.default ================================================ ;= @echo off ;= rem Call DOSKEY and use this file as the macrofile ;= %SystemRoot%\system32\doskey /listsize=1000 /macrofile=%0% ;= rem In batch mode, jump to the end of the file ;= goto:eof ;= Add aliases below here e.=explorer . gl=git log --oneline --all --graph --decorate $* l=ls --show-control-chars -CFGNhp --color --ignore={"NTUSER.DAT*","ntuser.dat*"} $* ls=ls --show-control-chars -F --color $* pwd=cd clear=cls unalias=alias /d $1 vi=vim $* cmderr=cd /d "%CMDER_ROOT%" pwsh=%SystemRoot%/System32/WindowsPowerShell/v1.0/powershell.exe -ExecutionPolicy Bypass -NoLogo -NoProfile -NoExit -Command "Invoke-Expression '. ''%CMDER_ROOT%/vendor/profile.ps1'''" ================================================ FILE: vendor/user_profile.cmd.default ================================================ :: use this file to run your own startup commands :: use in front of the command to prevent printing the command :: uncomment this to have the ssh agent load when cmder starts :: call "%GIT_INSTALL_ROOT%/cmd/start-ssh-agent.cmd" /k exit :: uncomment the next two lines to use pageant as the ssh authentication agent :: SET SSH_AUTH_SOCK=/tmp/.ssh-pageant-auth-sock :: call "%GIT_INSTALL_ROOT%/cmd/start-ssh-pageant.cmd" :: you can add your plugins to the cmder path like so :: set "PATH=%CMDER_ROOT%\vendor\whatever;%PATH%" :: arguments in this batch are passed from init.bat, you can quickly parse them like so: :: more usage can be seen by typing "cexec /?" :: %ccall% "/customOption" "command/program" @echo off ================================================ FILE: vendor/user_profile.ps1.default ================================================ # Use this file to run your own startup commands ## Prompt Customization <# .SYNTAX λ .EXAMPLE N:\Documents\src\cmder [master] λ | #> [ScriptBlock]$PrePrompt = { } # Replace the cmder prompt entirely with this. # [ScriptBlock]$CmderPrompt = {} [ScriptBlock]$PostPrompt = { } ## # # Delete default powershell aliases that conflict with bash commands # if (get-command git) { # del -force alias:cat # del -force alias:clear # del -force alias:cp # del -force alias:diff # del -force alias:echo # del -force alias:kill # del -force alias:ls # del -force alias:mv # del -force alias:ps # del -force alias:pwd # del -force alias:rm # del -force alias:sleep # del -force alias:tee # } ================================================ FILE: vendor/user_profile.sh.default ================================================ # use this file to run your own startup commands for msys2 bash' # To add a new vendor to the path, do something like: # export PATH=${CMDER_ROOT}/vendor/whatever:${PATH} # Uncomment this to have the ssh agent load with the first bash terminal # . "${CMDER_ROOT}/vendor/lib/start-ssh-agent.sh"