Reactos
at master 340 lines 9.1 kB view raw
1/* 2 * PROJECT: ReactOS DosKey Command 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Provides history and command aliases management for 5 * command-line programs. 6 * COPYRIGHT: Copyright 2008 Christoph von Wittich 7 * Copyright 2013-2017 Hermès Bélusca-Maïto 8 */ 9 10#include <stdio.h> 11#include <wchar.h> 12#include <locale.h> 13 14#include <windef.h> 15#include <winbase.h> 16#include <winuser.h> 17#include <wincon.h> 18#include <wincon_undoc.h> 19 20#include "doskey.h" 21 22#define MAX_STRING 2000 23WCHAR szStringBuf[MAX_STRING]; 24LPWSTR pszExeName = L"cmd.exe"; 25 26static VOID SetInsert(DWORD dwFlag) 27{ 28 /* 29 * NOTE: Enabling the ENABLE_INSERT_MODE mode can also be done by calling 30 * kernel32:SetConsoleCommandHistoryMode(CONSOLE_OVERSTRIKE) (deprecated). 31 */ 32 DWORD dwMode; 33 HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE); 34 GetConsoleMode(hConsole, &dwMode); 35 dwMode |= ENABLE_EXTENDED_FLAGS; 36 SetConsoleMode(hConsole, (dwMode & ~ENABLE_INSERT_MODE) | dwFlag); 37} 38 39static VOID PrintHistory(VOID) 40{ 41 DWORD Length = GetConsoleCommandHistoryLengthW(pszExeName); 42 PBYTE HistBuf; 43 WCHAR *Hist; 44 WCHAR *HistEnd; 45 46 HistBuf = HeapAlloc(GetProcessHeap(), 47 HEAP_ZERO_MEMORY, 48 Length); 49 if (!HistBuf) return; 50 Hist = (WCHAR *)HistBuf; 51 HistEnd = (WCHAR *)&HistBuf[Length]; 52 53 if (GetConsoleCommandHistoryW(Hist, Length, pszExeName)) 54 { 55 for (; Hist < HistEnd; Hist += wcslen(Hist) + 1) 56 { 57 wprintf(L"%s\n", Hist); 58 } 59 } 60 61 HeapFree(GetProcessHeap(), 0, HistBuf); 62} 63 64static INT SetMacro(LPWSTR definition) 65{ 66 WCHAR *name, *nameend, *text, temp; 67 68 name = definition; 69 while (*name == L' ') 70 name++; 71 72 /* error if no '=' found */ 73 if ((nameend = wcschr(name, L'=')) != NULL) 74 { 75 text = nameend + 1; 76 while (*text == L' ') 77 text++; 78 79 while (nameend > name && nameend[-1] == L' ') 80 nameend--; 81 82 /* Split rest into name and substitute */ 83 temp = *nameend; 84 *nameend = L'\0'; 85 /* Don't allow spaces in the name, since such a macro would be unusable */ 86 if (!wcschr(name, L' ') && AddConsoleAliasW(name, text, pszExeName)) 87 return 0; 88 *nameend = temp; 89 } 90 91 LoadStringW(GetModuleHandle(NULL), 92 IDS_INVALID_MACRO_DEF, 93 szStringBuf, 94 ARRAYSIZE(szStringBuf)); 95 wprintf(szStringBuf, definition); 96 return 1; 97} 98 99static VOID PrintMacros(LPWSTR pszExeName, LPWSTR Indent) 100{ 101 DWORD Length = GetConsoleAliasesLengthW(pszExeName); 102 PBYTE AliasBuf; 103 WCHAR *Alias; 104 WCHAR *AliasEnd; 105 106 AliasBuf = HeapAlloc(GetProcessHeap(), 107 HEAP_ZERO_MEMORY, 108 Length * sizeof(BYTE)); 109 if (!AliasBuf) return; 110 Alias = (WCHAR *)AliasBuf; 111 AliasEnd = (WCHAR *)&AliasBuf[Length]; 112 113 if (GetConsoleAliasesW(Alias, Length * sizeof(BYTE), pszExeName)) 114 { 115 for (; Alias < AliasEnd; Alias += wcslen(Alias) + 1) 116 { 117 wprintf(L"%s%s\n", Indent, Alias); 118 } 119 } 120 121 HeapFree(GetProcessHeap(), 0, AliasBuf); 122} 123 124static VOID PrintAllMacros(VOID) 125{ 126 DWORD Length = GetConsoleAliasExesLength(); 127 PBYTE ExeNameBuf; 128 WCHAR *ExeName; 129 WCHAR *ExeNameEnd; 130 131 ExeNameBuf = HeapAlloc(GetProcessHeap(), 132 HEAP_ZERO_MEMORY, 133 Length * sizeof(BYTE)); 134 if (!ExeNameBuf) return; 135 ExeName = (WCHAR *)ExeNameBuf; 136 ExeNameEnd = (WCHAR *)&ExeNameBuf[Length]; 137 138 if (GetConsoleAliasExesW(ExeName, Length * sizeof(BYTE))) 139 { 140 for (; ExeName < ExeNameEnd; ExeName += wcslen(ExeName) + 1) 141 { 142 wprintf(L"[%s]\n", ExeName); 143 PrintMacros(ExeName, L" "); 144 wprintf(L"\n"); 145 } 146 } 147 148 HeapFree(GetProcessHeap(), 0, ExeNameBuf); 149} 150 151/* Remove starting and ending quotes from a string, if present */ 152static LPWSTR RemoveQuotes(LPWSTR str) 153{ 154 WCHAR *end; 155 if (*str == L'"' && *(end = str + wcslen(str) - 1) == L'"') 156 { 157 str++; 158 *end = L'\0'; 159 } 160 return str; 161} 162 163static VOID ReadFromFile(LPWSTR FileName) 164{ 165 FILE* fp; 166 WCHAR line[MAX_PATH]; 167 WCHAR ExeNameBuffer[EXENAME_LENGTH]; 168 LPWSTR pszOrgExeName = pszExeName; 169 170 /* Open the file */ 171 fp = _wfopen(FileName, L"rt"); 172 if (!fp) 173 { 174 _wperror(FileName); 175 return; 176 } 177 178 while (fgetws(line, ARRAYSIZE(line), fp) != NULL) 179 { 180 PWCHAR end; 181 182 if (!*line) 183 continue; 184 185 /* Remove trailing newline character */ 186 end = &line[wcslen(line) - 1]; 187 if (*end == L'\n') 188 *end = L'\0'; 189 190 if (!*line) 191 continue; 192 193 /* Check for any section redefining the current executable name */ 194 end = NULL; 195 if (*line == L'[') 196 end = wcschr(line, L']'); 197 198 if (end != NULL) 199 { 200 /* New section: change the current executable name */ 201 202 *end = L'\0'; // NULL-terminate it 203 pszExeName = RemoveQuotes(line + 1); 204 if (*pszExeName) 205 { 206 /* Capture the new executable name and truncate it if needed */ 207 end = &pszExeName[wcslen(pszExeName)]; 208 if (end - pszExeName >= EXENAME_LENGTH) 209 end = &pszExeName[EXENAME_LENGTH - 1]; 210 *end = L'\0'; // Truncate it 211 wcscpy(ExeNameBuffer, pszExeName); 212 pszExeName = ExeNameBuffer; 213 } 214 else 215 { 216 /* Restore the original current executable name */ 217 pszExeName = pszOrgExeName; 218 } 219 } 220 else 221 { 222 /* Set the new macro for the current executable */ 223 SetMacro(line); 224 } 225 } 226 227 /* Restore the original current executable name if it has changed */ 228 pszExeName = pszOrgExeName; 229 230 /* Close the file and return */ 231 fclose(fp); 232 return; 233} 234 235/* Get the start and end of the next command-line argument. */ 236static BOOL GetArg(WCHAR **pStart, WCHAR **pEnd) 237{ 238 BOOL bInQuotes = FALSE; 239 WCHAR *p = *pEnd; 240 p += wcsspn(p, L" \t"); 241 if (!*p) 242 return FALSE; 243 *pStart = p; 244 do 245 { 246 if (!bInQuotes && (*p == L' ' || *p == L'\t')) 247 break; 248 bInQuotes ^= (*p++ == L'"'); 249 } while (*p); 250 *pEnd = p; 251 return TRUE; 252} 253 254int 255wmain(VOID) 256{ 257 LPWSTR pArgStart, pArgEnd; 258 259 setlocale(LC_ALL, ""); 260 261 /* Get the full command line using GetCommandLine(). We can't just use argv, 262 * because then a parameter like "gotoroot=cd \" wouldn't be passed completely. */ 263 pArgEnd = GetCommandLineW(); 264 265 /* Skip the application name */ 266 GetArg(&pArgStart, &pArgEnd); 267 268 while (GetArg(&pArgStart, &pArgEnd)) 269 { 270 /* NULL-terminate this argument to make processing easier */ 271 WCHAR tmp = *pArgEnd; 272 *pArgEnd = L'\0'; 273 274 if (!wcscmp(pArgStart, L"/?")) 275 { 276 LoadStringW(GetModuleHandle(NULL), 277 IDS_HELP, 278 szStringBuf, 279 ARRAYSIZE(szStringBuf)); 280 wprintf(szStringBuf); 281 break; 282 } 283 else if (!_wcsnicmp(pArgStart, L"/EXENAME=", 9)) 284 { 285 pszExeName = RemoveQuotes(pArgStart + 9); 286 } 287 else if (!_wcsicmp(pArgStart, L"/H") || 288 !_wcsicmp(pArgStart, L"/HISTORY")) 289 { 290 PrintHistory(); 291 } 292 else if (!_wcsnicmp(pArgStart, L"/LISTSIZE=", 10)) 293 { 294 SetConsoleNumberOfCommandsW(_wtoi(pArgStart + 10), pszExeName); 295 } 296 else if (!_wcsicmp(pArgStart, L"/REINSTALL")) 297 { 298 ExpungeConsoleCommandHistoryW(pszExeName); 299 } 300 else if (!_wcsicmp(pArgStart, L"/INSERT")) 301 { 302 SetInsert(ENABLE_INSERT_MODE); 303 } 304 else if (!_wcsicmp(pArgStart, L"/OVERSTRIKE")) 305 { 306 SetInsert(0); 307 } 308 else if (!_wcsicmp(pArgStart, L"/M") || 309 !_wcsicmp(pArgStart, L"/MACROS")) 310 { 311 PrintMacros(pszExeName, L""); 312 } 313 else if (!_wcsnicmp(pArgStart, L"/M:", 3) || 314 !_wcsnicmp(pArgStart, L"/MACROS:", 8)) 315 { 316 LPWSTR exe = RemoveQuotes(wcschr(pArgStart, L':') + 1); 317 if (!_wcsicmp(exe, L"ALL")) 318 PrintAllMacros(); 319 else 320 PrintMacros(exe, L""); 321 } 322 else if (!_wcsnicmp(pArgStart, L"/MACROFILE=", 11)) 323 { 324 ReadFromFile(RemoveQuotes(pArgStart + 11)); 325 } 326 else 327 { 328 /* This is the beginning of a macro definition. It includes 329 * the entire remainder of the line, so first put back the 330 * character that we replaced with NULL. */ 331 *pArgEnd = tmp; 332 return SetMacro(pArgStart); 333 } 334 335 if (!tmp) break; 336 pArgEnd++; 337 } 338 339 return 0; 340}