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