update for HEAD-2003050101
[reactos.git] / subsys / system / cmd / choice.c
diff --git a/subsys/system/cmd/choice.c b/subsys/system/cmd/choice.c
new file mode 100644 (file)
index 0000000..97f70f2
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ *  CHOICE.C - internal command.
+ *
+ *
+ *  History:
+ *
+ *    12 Aug 1999 (Eric Kohl)
+ *        started.
+ *
+ *    01 Sep 1999 (Eric Kohl)
+ *        Fixed help text.
+ *
+ *    26 Sep 1999 (Paolo Pantaleo)
+ *        Fixed timeout.
+ */
+
+#include "config.h"
+
+#ifdef INCLUDE_CMD_CHOICE
+
+#include <windows.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <tchar.h>
+
+#include "cmd.h"
+#include "batch.h"
+
+
+#define GC_TIMEOUT     -1
+#define GC_NOKEY       0       //an event occurred but it wasn't a key pressed
+#define GC_KEYREAD     1       //a key has been read
+
+
+static INT
+GetCharacterTimeout (LPTCH ch, DWORD dwMilliseconds)
+{
+//--------------------------------------------
+//  Get a character from standard input but with a timeout.
+//  The function will wait a limited amount
+//  of time, then the function returns GC_TIMEOUT.
+//
+//     dwMilliseconds is the timeout value, that can
+//     be set to INFINITE, so the function works like
+//     stdio.h's getchar()
+
+       HANDLE hInput;
+       DWORD  dwRead;
+
+       INPUT_RECORD lpBuffer;
+
+       hInput = GetStdHandle (STD_INPUT_HANDLE);
+
+       //if the timeout experied return GC_TIMEOUT
+       if (WaitForSingleObject (hInput, dwMilliseconds) == WAIT_TIMEOUT)
+               return GC_TIMEOUT;
+
+       //otherwise get the event
+       ReadConsoleInput (hInput, &lpBuffer, 1, &dwRead);
+
+       //if the event is a key pressed
+       if ((lpBuffer.EventType == KEY_EVENT) &&
+               (lpBuffer.Event.KeyEvent.bKeyDown == TRUE))
+       {
+               //read the key
+#ifdef _UNICODE
+               *ch = lpBuffer.Event.KeyEvent.uChar.UnicodeChar;
+#else
+               *ch = lpBuffer.Event.KeyEvent.uChar.AsciiChar;
+#endif
+               return GC_KEYREAD;
+       }
+
+       //else return no key
+       return GC_NOKEY;
+}
+
+static INT
+IsKeyInString (LPTSTR lpString, TCHAR cKey, BOOL bCaseSensitive)
+{
+       LPTCH p = lpString;
+       INT val = 0;
+
+       while (*p)
+       {
+               if (bCaseSensitive)
+               {
+                       if (*p == cKey)
+                               return val;
+               }
+               else
+               {
+                       if (_totlower (*p) == _totlower (cKey))
+                               return val;
+               }
+
+               val++;
+               p++;
+       }
+
+       return -1;
+}
+
+
+INT
+CommandChoice (LPTSTR cmd, LPTSTR param)
+{
+       LPTSTR lpOptions = "YN";
+       LPTSTR lpText    = NULL;
+       BOOL   bNoPrompt = FALSE;
+       BOOL   bCaseSensitive = FALSE;
+       BOOL   bTimeout = FALSE;
+       INT    nTimeout = 0;
+       TCHAR  cDefault = _T('\0');
+       INPUT_RECORD ir;
+       LPTSTR p, np;
+       LPTSTR *arg;
+       INT    argc;
+       INT    i;
+       INT    val;
+
+       INT GCret;
+       TCHAR Ch;
+       DWORD amount,clk;
+
+       if (_tcsncmp (param, _T("/?"), 2) == 0)
+       {
+               ConOutPuts (_T("Waits for the user to choose one of a set of choices.\n"
+                              "\n"
+                              "CHOICE  [/C[:]choices][/N][/S][/T[:]c,nn][text]\n"
+                              "\n"
+                              "  /C[:]choices  Specifies allowable keys. Default is YN.\n"
+                              "  /N            Do not display choices and ? at the end of the prompt string.\n"
+                              "  /S            Treat choice keys as case sensitive.\n"
+                              "  /T[:]c,nn     Default choice to c after nn seconds.\n"
+                              "  text          Prompt string to display.\n"
+                              "\n"
+                              "ERRORLEVEL is set to offset of key user presses in choices."));
+               return 0;
+       }
+
+       /* retrieve text */
+       p = param;
+
+       while (TRUE)
+       {
+               if (*p == _T('\0'))
+                       break;
+
+               if (*p != _T('/'))
+               {
+                       lpText = p;
+                               break;
+               }
+               np = _tcschr (p, _T(' '));
+               if (!np)
+                       break;
+               p = np + 1;
+       }
+
+       /* build parameter array */
+       arg = split (param, &argc, FALSE);
+
+       /* evaluate arguments */
+       if (argc > 0)
+       {
+               for (i = 0; i < argc; i++)
+               {
+                       if (_tcsnicmp (arg[i], _T("/c"), 2) == 0)
+                       {
+                               if (arg[i][2] == _T(':'))
+                                       lpOptions = &arg[i][3];
+                               else
+                                       lpOptions = &arg[i][2];
+
+                               if (_tcslen (lpOptions) == 0)
+                               {
+                                       ConErrPuts (_T("Invalid option. Expected format: /C[:]options"));
+                                       freep (arg);
+                                       return 1;
+                               }
+                       }
+                       else if (_tcsnicmp (arg[i], _T("/n"), 2) == 0)
+                       {
+                               bNoPrompt = TRUE;
+                       }
+                       else if (_tcsnicmp (arg[i], _T("/s"), 2) == 0)
+                       {
+                               bCaseSensitive = TRUE;
+                       }
+                       else if (_tcsnicmp (arg[i], _T("/t"), 2) == 0)
+                       {
+                               LPTSTR s;
+
+                               if (arg[i][2] == _T(':'))
+                               {
+                                       cDefault = arg[i][3];
+                                       s = &arg[i][4];
+                               }
+                               else
+                               {
+                                       cDefault = arg[i][2];
+                                       s = &arg[i][3];
+                               }
+
+                               if (*s != _T(','))
+                               {
+                                       ConErrPuts (_T("Invalid option. Expected format: /T[:]c,nn"));
+                                       freep (arg);
+                                       return 1;
+                               }
+
+                               s++;
+                               nTimeout = _ttoi(s);
+                               bTimeout = TRUE;
+                       }
+                       else if (arg[i][0] == _T('/'))
+                       {
+                               ConErrPrintf (_T("Illegal Option: %s"), arg[i]);
+                               freep (arg);
+                               return 1;
+                       }
+               }
+       }
+
+       /* print text */
+       if (lpText)
+               ConOutPrintf (_T("%s"), lpText);
+
+       /* print options */
+       if (bNoPrompt == FALSE)
+       {
+               ConOutPrintf (_T("[%c"), lpOptions[0]);
+
+               for (i = 1; (unsigned)i < _tcslen (lpOptions); i++)
+                       ConOutPrintf (_T(",%c"), lpOptions[i]);
+
+               ConOutPrintf (_T("]?"));
+       }
+
+       ConInFlush ();
+
+       if(!bTimeout)
+       {
+               while (TRUE)
+               {
+                       ConInKey (&ir);
+
+                       val = IsKeyInString (lpOptions,
+#ifdef _UNICODE
+                                            ir.Event.KeyEvent.uChar.UnicodeChar,
+#else
+                                            ir.Event.KeyEvent.uChar.AsciiChar,
+#endif /* _UNICODE */
+                                            bCaseSensitive);
+
+                       if (val >= 0)
+                       {
+                               ConOutPrintf (_T("%c\n"), lpOptions[val]);
+
+                               nErrorLevel = val + 1;
+
+                               break;
+                       }
+
+                       Beep (440, 50);
+               }
+
+               freep (arg);
+               return 0;
+       }
+
+       clk = GetTickCount ();
+       amount = nTimeout*1000;
+
+loop:
+       GCret = GetCharacterTimeout (&Ch, amount - (GetTickCount () - clk));
+
+       switch (GCret)
+       {
+               case GC_TIMEOUT:
+#ifdef _DEBUG
+                       DebugPrintf (_T("GC_TIMEOUT\n"));
+                       DebugPrintf (_T("elapsed %d msecs\n"), GetTickCount () - clk);
+#endif /* _DEBUG */
+                       break;
+
+               case GC_NOKEY:
+#ifdef _DEBUG
+                       DebugPrintf(_T("GC_NOKEY\n"));
+                       DebugPrintf(_T("elapsed %d msecs\n"), GetTickCount () - clk);
+#endif /* _DEBUG */
+                       goto loop;
+
+               case GC_KEYREAD:
+#ifdef _DEBUG
+                       DebugPrintf(_T("GC_KEYREAD\n"));
+                       DebugPrintf(_T("elapsed %d msecs\n"), GetTickCount () - clk);
+                       DebugPrintf(_T("read %c"), Ch);
+#endif /* _DEBUG */
+                       if ((val=IsKeyInString(lpOptions,Ch,bCaseSensitive))==-1)
+                       {
+                               Beep (440, 50);
+                               goto loop;
+                       }
+                       cDefault=Ch;
+                       break;
+       }
+
+#ifdef _DEBUG
+       DebugPrintf(_T("exiting wait loop after %d msecs\n"),
+                   GetTickCount () - clk);
+#endif /* _DEBUG */
+
+       val = IsKeyInString (lpOptions, cDefault, bCaseSensitive);
+       ConOutPrintf (_T("%c\n"), lpOptions[val]);
+
+       nErrorLevel = val + 1;
+
+       freep (arg);
+
+#ifdef _DEBUG
+       DebugPrintf (_T("ErrorLevel: %d\n"), nErrorLevel);
+#endif /* _DEBUG */
+
+       return 0;
+}
+#endif /* INCLUDE_CMD_CHOICE */
+
+/* EOF */