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