update for HEAD-2003050101
[reactos.git] / subsys / system / 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, FALSE);
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 */