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