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