Various improvements by Carl Nettelblad.
[reactos.git] / rosapps / cmd / choice.c
1 /*
2 * CHOICE.C - internal command.
3 *
4 *
5 * History:
6 *
7 * 12 Aug 1999 (Eric Kohl)
8 * started.
9 *
10 * 01 Sep 1999 (Eric Kohl)
11 * Fixed help text.
12 *
13 * 26 Sep 1999 (Paolo Pantaleo)
14 * Fixed timeout.
15 */
16
17 #include "config.h"
18
19 #ifdef INCLUDE_CMD_CHOICE
20
21 #include <windows.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <tchar.h>
26
27 #include "cmd.h"
28 #include "batch.h"
29
30
31 #define GC_TIMEOUT -1
32 #define GC_NOKEY 0 //an event occurred but it wasn't a key pressed
33 #define GC_KEYREAD 1 //a key has been read
34
35
36 static INT
37 GetCharacterTimeout (LPTCH ch, DWORD dwMilliseconds)
38 {
39 //--------------------------------------------
40 // Get a character from standard input but with a timeout.
41 // The function will wait a limited amount
42 // of time, then the function returns GC_TIMEOUT.
43 //
44 // dwMilliseconds is the timeout value, that can
45 // be set to INFINITE, so the function works like
46 // stdio.h's getchar()
47
48 HANDLE hInput;
49 DWORD dwRead;
50
51 INPUT_RECORD lpBuffer;
52
53 hInput = GetStdHandle (STD_INPUT_HANDLE);
54
55 //if the timeout experied return GC_TIMEOUT
56 if (WaitForSingleObject (hInput, dwMilliseconds) == WAIT_TIMEOUT)
57 return GC_TIMEOUT;
58
59 //otherwise get the event
60 ReadConsoleInput (hInput, &lpBuffer, 1, &dwRead);
61
62 //if the event is a key pressed
63 if ((lpBuffer.EventType == KEY_EVENT) &&
64 (lpBuffer.Event.KeyEvent.bKeyDown == TRUE))
65 {
66 //read the key
67 #ifdef _UNICODE
68 *ch = lpBuffer.Event.KeyEvent.uChar.UnicodeChar;
69 #else
70 *ch = lpBuffer.Event.KeyEvent.uChar.AsciiChar;
71 #endif
72 return GC_KEYREAD;
73 }
74
75 //else return no key
76 return GC_NOKEY;
77 }
78
79 static INT
80 IsKeyInString (LPTSTR lpString, TCHAR cKey, BOOL bCaseSensitive)
81 {
82 LPTCH p = lpString;
83 INT val = 0;
84
85 while (*p)
86 {
87 if (bCaseSensitive)
88 {
89 if (*p == cKey)
90 return val;
91 }
92 else
93 {
94 if (_totlower (*p) == _totlower (cKey))
95 return val;
96 }
97
98 val++;
99 p++;
100 }
101
102 return -1;
103 }
104
105
106 INT
107 CommandChoice (LPTSTR cmd, LPTSTR param)
108 {
109 LPTSTR lpOptions = "YN";
110 LPTSTR lpText = NULL;
111 BOOL bNoPrompt = FALSE;
112 BOOL bCaseSensitive = FALSE;
113 BOOL bTimeout = FALSE;
114 INT nTimeout = 0;
115 TCHAR cDefault = _T('\0');
116 INPUT_RECORD ir;
117 LPTSTR p, np;
118 LPTSTR *arg;
119 INT argc;
120 INT i;
121 INT val;
122
123 INT GCret;
124 TCHAR Ch;
125 DWORD amount,clk;
126
127 if (_tcsncmp (param, _T("/?"), 2) == 0)
128 {
129 ConOutPuts (_T("Waits for the user to choose one of a set of choices.\n"
130 "\n"
131 "CHOICE [/C[:]choices][/N][/S][/T[:]c,nn][text]\n"
132 "\n"
133 " /C[:]choices Specifies allowable keys. Default is YN.\n"
134 " /N Do not display choices and ? at the end of the prompt string.\n"
135 " /S Treat choice keys as case sensitive.\n"
136 " /T[:]c,nn Default choice to c after nn seconds.\n"
137 " text Prompt string to display.\n"
138 "\n"
139 "ERRORLEVEL is set to offset of key user presses in choices."));
140 return 0;
141 }
142
143 /* retrieve text */
144 p = param;
145
146 while (TRUE)
147 {
148 if (*p == _T('\0'))
149 break;
150
151 if (*p != _T('/'))
152 {
153 lpText = p;
154 break;
155 }
156 np = _tcschr (p, _T(' '));
157 if (!np)
158 break;
159 p = np + 1;
160 }
161
162 /* build parameter array */
163 arg = split (param, &argc);
164
165 /* evaluate arguments */
166 if (argc > 0)
167 {
168 for (i = 0; i < argc; i++)
169 {
170 if (_tcsnicmp (arg[i], _T("/c"), 2) == 0)
171 {
172 if (arg[i][2] == _T(':'))
173 lpOptions = &arg[i][3];
174 else
175 lpOptions = &arg[i][2];
176
177 if (_tcslen (lpOptions) == 0)
178 {
179 ConErrPuts (_T("Invalid option. Expected format: /C[:]options"));
180 freep (arg);
181 return 1;
182 }
183 }
184 else if (_tcsnicmp (arg[i], _T("/n"), 2) == 0)
185 {
186 bNoPrompt = TRUE;
187 }
188 else if (_tcsnicmp (arg[i], _T("/s"), 2) == 0)
189 {
190 bCaseSensitive = TRUE;
191 }
192 else if (_tcsnicmp (arg[i], _T("/t"), 2) == 0)
193 {
194 LPTSTR s;
195
196 if (arg[i][2] == _T(':'))
197 {
198 cDefault = arg[i][3];
199 s = &arg[i][4];
200 }
201 else
202 {
203 cDefault = arg[i][2];
204 s = &arg[i][3];
205 }
206
207 if (*s != _T(','))
208 {
209 ConErrPuts (_T("Invalid option. Expected format: /T[:]c,nn"));
210 freep (arg);
211 return 1;
212 }
213
214 s++;
215 nTimeout = _ttoi(s);
216 bTimeout = TRUE;
217 }
218 else if (arg[i][0] == _T('/'))
219 {
220 ConErrPrintf (_T("Illegal Option: %s"), arg[i]);
221 freep (arg);
222 return 1;
223 }
224 }
225 }
226
227 /* print text */
228 if (lpText)
229 ConOutPrintf (_T("%s"), lpText);
230
231 /* print options */
232 if (bNoPrompt == FALSE)
233 {
234 ConOutPrintf (_T("[%c"), lpOptions[0]);
235
236 for (i = 1; (unsigned)i < _tcslen (lpOptions); i++)
237 ConOutPrintf (_T(",%c"), lpOptions[i]);
238
239 ConOutPrintf (_T("]?"));
240 }
241
242 ConInFlush ();
243
244 if(!bTimeout)
245 {
246 while (TRUE)
247 {
248 ConInKey (&ir);
249
250 val = IsKeyInString (lpOptions,
251 #ifdef _UNICODE
252 ir.Event.KeyEvent.uChar.UnicodeChar,
253 #else
254 ir.Event.KeyEvent.uChar.AsciiChar,
255 #endif /* _UNICODE */
256 bCaseSensitive);
257
258 if (val >= 0)
259 {
260 ConOutPrintf (_T("%c\n"), lpOptions[val]);
261
262 nErrorLevel = val + 1;
263
264 break;
265 }
266
267 Beep (440, 50);
268 }
269
270 freep (arg);
271 return 0;
272 }
273
274 clk = GetTickCount ();
275 amount = nTimeout*1000;
276
277 loop:
278 GCret = GetCharacterTimeout (&Ch, amount - (GetTickCount () - clk));
279
280 switch (GCret)
281 {
282 case GC_TIMEOUT:
283 #ifdef _DEBUG
284 DebugPrintf (_T("GC_TIMEOUT\n"));
285 DebugPrintf (_T("elapsed %d msecs\n"), GetTickCount () - clk);
286 #endif /* _DEBUG */
287 break;
288
289 case GC_NOKEY:
290 #ifdef _DEBUG
291 DebugPrintf(_T("GC_NOKEY\n"));
292 DebugPrintf(_T("elapsed %d msecs\n"), GetTickCount () - clk);
293 #endif /* _DEBUG */
294 goto loop;
295
296 case GC_KEYREAD:
297 #ifdef _DEBUG
298 DebugPrintf(_T("GC_KEYREAD\n"));
299 DebugPrintf(_T("elapsed %d msecs\n"), GetTickCount () - clk);
300 DebugPrintf(_T("read %c"), Ch);
301 #endif /* _DEBUG */
302 if ((val=IsKeyInString(lpOptions,Ch,bCaseSensitive))==-1)
303 {
304 Beep (440, 50);
305 goto loop;
306 }
307 cDefault=Ch;
308 break;
309 }
310
311 #ifdef _DEBUG
312 DebugPrintf(_T("exiting wait loop after %d msecs\n"),
313 GetTickCount () - clk);
314 #endif /* _DEBUG */
315
316 val = IsKeyInString (lpOptions, cDefault, bCaseSensitive);
317 ConOutPrintf (_T("%c\n"), lpOptions[val]);
318
319 nErrorLevel = val + 1;
320
321 freep (arg);
322
323 #ifdef _DEBUG
324 DebugPrintf (_T("ErrorLevel: %d\n"), nErrorLevel);
325 #endif /* _DEBUG */
326
327 return 0;
328 }
329 #endif /* INCLUDE_CMD_CHOICE */
330
331 /* EOF */