xref: /reactos/base/shell/cmd/choice.c (revision c2c66aff)
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  *    02 Apr 2005 (Magnus Olsen)
17  *        Remove hardcoded strings so that they can be translated.
18  *
19  */
20 
21 #include "precomp.h"
22 
23 #ifdef INCLUDE_CMD_CHOICE
24 
25 
26 #define GC_TIMEOUT  -1
27 #define GC_NOKEY    0   //an event occurred but it wasn't a key pressed
28 #define GC_KEYREAD  1   //a key has been read
29 
30 
31 static INT
32 GetCharacterTimeout (LPTCH ch, DWORD dwMilliseconds)
33 {
34 //--------------------------------------------
35 //  Get a character from standard input but with a timeout.
36 //  The function will wait a limited amount
37 //  of time, then the function returns GC_TIMEOUT.
38 //
39 //  dwMilliseconds is the timeout value, that can
40 //  be set to INFINITE, so the function works like
41 //  stdio.h's getchar()
42 
43     HANDLE hInput;
44     DWORD  dwRead;
45 
46     INPUT_RECORD lpBuffer;
47 
48     hInput = GetStdHandle (STD_INPUT_HANDLE);
49 
50     //if the timeout expired return GC_TIMEOUT
51     if (WaitForSingleObject (hInput, dwMilliseconds) == WAIT_TIMEOUT)
52         return GC_TIMEOUT;
53 
54     //otherwise get the event
55     ReadConsoleInput (hInput, &lpBuffer, 1, &dwRead);
56 
57     //if the event is a key pressed
58     if ((lpBuffer.EventType == KEY_EVENT) &&
59         (lpBuffer.Event.KeyEvent.bKeyDown != FALSE))
60     {
61         //read the key
62 #ifdef _UNICODE
63         *ch = lpBuffer.Event.KeyEvent.uChar.UnicodeChar;
64 #else
65         *ch = lpBuffer.Event.KeyEvent.uChar.AsciiChar;
66 #endif
67         return GC_KEYREAD;
68     }
69 
70     //else return no key
71     return GC_NOKEY;
72 }
73 
74 static INT
75 IsKeyInString (LPTSTR lpString, TCHAR cKey, BOOL bCaseSensitive)
76 {
77     LPTCH p = lpString;
78     INT val = 0;
79 
80     while (*p)
81     {
82         if (bCaseSensitive)
83         {
84             if (*p == cKey)
85                 return val;
86         }
87         else
88         {
89             if (_totlower (*p) == _totlower (cKey))
90                 return val;
91         }
92 
93         val++;
94         p++;
95     }
96 
97     return -1;
98 }
99 
100 
101 INT
102 CommandChoice (LPTSTR param)
103 {
104     LPTSTR lpOptions;
105     TCHAR Options[6];
106     LPTSTR lpText    = NULL;
107     BOOL   bNoPrompt = FALSE;
108     BOOL   bCaseSensitive = FALSE;
109     BOOL   bTimeout = FALSE;
110     INT    nTimeout = 0;
111     TCHAR  cDefault = _T('\0');
112     INPUT_RECORD ir;
113     LPTSTR p, np;
114     LPTSTR *arg;
115     INT    argc;
116     INT    i;
117     INT    val;
118 
119     INT GCret;
120     TCHAR Ch;
121     DWORD amount,clk;
122 
123     LoadString(CMD_ModuleHandle, STRING_CHOICE_OPTION, Options, 4);
124     lpOptions = Options;
125 
126     if (_tcsncmp (param, _T("/?"), 2) == 0)
127     {
128         ConOutResPaging(TRUE,STRING_CHOICE_HELP);
129         return 0;
130     }
131 
132     /* retrieve text */
133     p = param;
134 
135     while (TRUE)
136     {
137         if (*p == _T('\0'))
138             break;
139 
140         if (*p != _T('/'))
141         {
142             lpText = p;
143                 break;
144         }
145         np = _tcschr (p, _T(' '));
146         if (!np)
147             break;
148         p = np + 1;
149     }
150 
151     /* build parameter array */
152     arg = split (param, &argc, FALSE, FALSE);
153 
154     /* evaluate arguments */
155     if (argc > 0)
156     {
157         for (i = 0; i < argc; i++)
158         {
159             if (_tcsnicmp (arg[i], _T("/c"), 2) == 0)
160             {
161                 if (arg[i][2] == _T(':'))
162                     lpOptions = &arg[i][3];
163                 else
164                     lpOptions = &arg[i][2];
165 
166                 if (_tcslen (lpOptions) == 0)
167                 {
168                     ConErrResPuts(STRING_CHOICE_ERROR);
169                     freep (arg);
170                     return 1;
171                 }
172             }
173             else if (_tcsnicmp (arg[i], _T("/n"), 2) == 0)
174             {
175                 bNoPrompt = TRUE;
176             }
177             else if (_tcsnicmp (arg[i], _T("/s"), 2) == 0)
178             {
179                 bCaseSensitive = TRUE;
180             }
181             else if (_tcsnicmp (arg[i], _T("/t"), 2) == 0)
182             {
183                 LPTSTR s;
184 
185                 if (arg[i][2] == _T(':'))
186                 {
187                     cDefault = arg[i][3];
188                     s = &arg[i][4];
189                 }
190                 else
191                 {
192                     cDefault = arg[i][2];
193                     s = &arg[i][3];
194                 }
195 
196                 if (*s != _T(','))
197                 {
198                     ConErrResPuts(STRING_CHOICE_ERROR_TXT);
199                     freep (arg);
200                     return 1;
201                 }
202 
203                 s++;
204                 nTimeout = _ttoi(s);
205                 bTimeout = TRUE;
206             }
207             else if (arg[i][0] == _T('/'))
208             {
209                 ConErrResPrintf(STRING_CHOICE_ERROR_OPTION, arg[i]);
210                 freep (arg);
211                 return 1;
212             }
213         }
214     }
215 
216     /* print text */
217     if (lpText)
218         ConOutPrintf (_T("%s"), lpText);
219 
220     /* print options */
221     if (bNoPrompt == FALSE)
222     {
223         ConOutPrintf (_T("[%c"), lpOptions[0]);
224 
225         for (i = 1; (unsigned)i < _tcslen (lpOptions); i++)
226             ConOutPrintf (_T(",%c"), lpOptions[i]);
227 
228         ConOutPrintf (_T("]?"));
229     }
230 
231     ConInFlush ();
232 
233     if (!bTimeout)
234     {
235         while (TRUE)
236         {
237             ConInKey (&ir);
238 
239             val = IsKeyInString (lpOptions,
240 #ifdef _UNICODE
241                                  ir.Event.KeyEvent.uChar.UnicodeChar,
242 #else
243                                  ir.Event.KeyEvent.uChar.AsciiChar,
244 #endif
245                                  bCaseSensitive);
246 
247             if (val >= 0)
248             {
249                 ConOutPrintf (_T("%c\n"), lpOptions[val]);
250 
251                 nErrorLevel = val + 1;
252 
253                 break;
254             }
255 
256             Beep (440, 50);
257         }
258 
259         freep (arg);
260         TRACE ("ErrorLevel: %d\n", nErrorLevel);
261         return 0;
262     }
263 
264     clk = GetTickCount ();
265     amount = nTimeout*1000;
266 
267 loop:
268     GCret = GetCharacterTimeout (&Ch, amount - (GetTickCount () - clk));
269 
270     switch (GCret)
271     {
272         case GC_TIMEOUT:
273             TRACE ("GC_TIMEOUT\n");
274             TRACE ("elapsed %d msecs\n", GetTickCount () - clk);
275             break;
276 
277         case GC_NOKEY:
278             TRACE ("GC_NOKEY\n");
279             TRACE ("elapsed %d msecs\n", GetTickCount () - clk);
280             goto loop;
281 
282         case GC_KEYREAD:
283             TRACE ("GC_KEYREAD\n");
284             TRACE ("elapsed %d msecs\n", GetTickCount () - clk);
285             TRACE ("read %c", Ch);
286             if ((val=IsKeyInString(lpOptions,Ch,bCaseSensitive))==-1)
287             {
288                 Beep (440, 50);
289                 goto loop;
290             }
291             cDefault=Ch;
292             break;
293     }
294 
295     TRACE ("exiting wait loop after %d msecs\n",
296                 GetTickCount () - clk);
297 
298     val = IsKeyInString (lpOptions, cDefault, bCaseSensitive);
299     ConOutPrintf(_T("%c\n"), lpOptions[val]);
300 
301     nErrorLevel = val + 1;
302 
303     freep (arg);
304 
305     TRACE ("ErrorLevel: %d\n", nErrorLevel);
306 
307     return 0;
308 }
309 #endif /* INCLUDE_CMD_CHOICE */
310 
311 /* EOF */
312