0c2694f71603acb802f4d6e38cfa4105e4cfe660
[reactos.git] / base / applications / cmdutils / doskey / doskey.c
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 }