text: /*++
  
  Copyright (c) Microsoft Corporation
  
  Module Name:
  
      Timeout.c
  
  Abstract:
  
      This file implements the parsing of the command line for the arguments and also the
      wait functionality.
  
  Author:
  
      EricB
  
  Revision History:
  
      26-Aug-1991 Created by EricB.
      10-Mar-1992 Added the _getch() call to flush the hit key.
      17-Apr-1992 Added countdown display.
      03-Oct-1992 Ported to NT/Win32
      23-May-1995 Added Sleep call
      14-Jun-2001 localization added by Wipro Technologies
      01-Aug-2001 Added /nobreak option by Wipro Technologies
  --*/
  
  #include "pch.h"
  #include "Timeout.h"
  #include "Resource.h"
  
  DWORD _cdecl
  wmain(
      IN DWORD argc,
      IN  LPCWSTR argv[]
      )
  /*++
  Routine Description:
    This is main entry point to this utility. Different function calls are made
    from here to achieve the required functionality.
  
  Arguments:
      [in] argc  : Number of Command line arguments.
      [in] argv  : Pointer to Command line arguments.
  
  Return Value:
      0 on success
      1 on failure.
  --*/
  {
      //local variables
      CONSOLE_SCREEN_BUFFER_INFO          csbi;
  
      time_t                              tWait = 0L;
      time_t                              tLast = 0L;
      time_t                              tNow = 0L;
      time_t                              tEnd = 0L;
  
      DWORD                               dwTimeActuals = 0;
      BOOL                                bNBActuals = 0;
      BOOL                                bUsage = FALSE;
      BOOL                                bResult = FALSE;
      BOOL                                bStatus = FALSE;
  
      WCHAR                               wszProgressMsg[ MAX_STRING_LENGTH ] = NULL_U_STRING ;
      DWORD                               dwWidth = 0;
      WCHAR                               wszBackup[12] = NULL_U_STRING;
      WCHAR                               wszTimeout[ MAX_RES_STRING] = NULL_U_STRING;
      LPWSTR                              pszStopString =  NULL;
      COORD                               coord = {0};
      HANDLE                              hOutput = NULL;
      HANDLE                              hStdIn = NULL;
  
      DWORD                               dwMode = 0L;
      DWORD                               dwRead = 0L;
  
      INPUT_RECORD                        InputBuffer[ MAX_NUM_RECS ] = {0};
      HRESULT                             hr = S_OK;
  
      if ( NULL == argv )
      {
          SetLastError (ERROR_INVALID_PARAMETER );
          SaveLastError();
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_INTERNAL );
          return EXIT_FAILURE;
      }
  
      // Parse the command line and get the actual values
      bResult = ProcessOptions( argc, argv, &bUsage, &dwTimeActuals, wszTimeout , &bNBActuals );
      if( FALSE == bResult )
      {
          // display an error message with respect to the GetReason()
          ShowLastErrorEx ( stderr, SLE_TYPE_ERROR | SLE_INTERNAL );
          ReleaseGlobals();
          return( EXIT_FAILURE );
      }
  
      // check for invalid syntax
      // 1. check for the case c:\>timeout.exe
      // 2. check for the case c:\>timeout.exe /nobreak
      // 3. check for the case c:\>timeout.exe /? /?
      if ( ( ( 0 == dwTimeActuals ) && ( FALSE == bNBActuals ) && ( FALSE == bUsage ) ) ||
          ( ( 0 == dwTimeActuals ) && ( TRUE == bNBActuals ) ) || ( ( TRUE  == bUsage ) && (argc > 2 )) )
      {
          // display an error message as specified syntax is invalid
          ShowMessage ( stderr, GetResString (IDS_INVALID_SYNTAX) );
          ReleaseGlobals();
          return( EXIT_FAILURE );
      }
  
      // Check whether the usage(/?) is specified
      if( TRUE == bUsage )
      {
          // Display the help/usage for the tool
          DisplayUsage();
          ReleaseGlobals();
          return( EXIT_SUCCESS );
      }
  
      // get the value for timeout (/T)
      tWait = ( time_t ) wcstol(wszTimeout,&pszStopString,BASE_TEN);
  
      //check whether any non-numeric value specified for timeout value
      // if so, display appropriate error message as invalid timeout value specified.
      // Also, check for overflow and underflow conds
      if( ((NULL != pszStopString) && ( StringLength( pszStopString, 0 ) != 0 )) ||
          (errno == ERANGE) ||
          ( tWait < MIN_TIME_VAL ) || ( tWait >= MAX_TIME_VAL ) )
      {
          ShowMessage ( stderr, GetResString (IDS_ERROR_TIME_VALUE) );
          ReleaseGlobals();
          return( EXIT_FAILURE );
      }
  
      // Get the time elapsed in secs since midnight (00:00:00), January 1, 1970
      bResult = GetTimeInSecs( &tNow );
      if( FALSE == bResult )
      {
          ReleaseGlobals();
          // could not get the time so exit...
          return( EXIT_FAILURE );
      }
  
      // check for /nobreak option is specified
      if ( TRUE == bNBActuals )
      {
          // set console handler to capture the keys like CTRL+BREAK or CRTL+C
          bStatus = SetConsoleCtrlHandler( &HandlerRoutine, TRUE );
          if ( FALSE == bStatus )
          {
              // Format an error message accroding to GetLastError() return by API.
              ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
              ReleaseGlobals();
              // to remove the handle
              SetConsoleCtrlHandler( NULL, FALSE );
              return( EXIT_FAILURE );
          }
      }
  
      hStdIn = GetStdHandle( STD_INPUT_HANDLE );
      // check for the input redirection on console and telnet session
      if( ( hStdIn != (HANDLE)0x0000000F ) &&
          ( hStdIn != (HANDLE)0x00000003 ) &&
          ( hStdIn != INVALID_HANDLE_VALUE ) )
      {
         ShowMessage( stderr, GetResString (IDS_INVALID_INPUT_REDIRECT) );
         ReleaseGlobals();
         // to remove the handle
         SetConsoleCtrlHandler( NULL, FALSE );
         return EXIT_FAILURE;
      }
  
  #ifdef WIN32
  
      // Set input mode so that a single key hit can be detected.
      if ( GetConsoleMode( hStdIn, &dwMode ) == FALSE )
      {
          // Format an error message accroding to GetLastError() return by API.
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
          ReleaseGlobals();
          // to remove the handle
          SetConsoleCtrlHandler( NULL, FALSE );
          return( EXIT_FAILURE );
      }
  
      // Turn off the following modes:
      dwMode &= ~( ENABLE_LINE_INPUT   |   // Don't wait for CR.
                   ENABLE_ECHO_INPUT   |   // Don't echo input.
                   ENABLE_WINDOW_INPUT |   // Don't record window events.
                   ENABLE_MOUSE_INPUT      // Don't record mouse events.
                 );
  
      // set the input mode of a console's input buffer
      if ( SetConsoleMode( hStdIn, dwMode ) == FALSE )
      {
          // Format an error message accroding to GetLastError() return by API.
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
          ReleaseGlobals();
          // to remove the handle
          SetConsoleCtrlHandler( NULL, FALSE );
          return( EXIT_FAILURE );
      }
  
      // retrieve number of unread input records in the console's input buffer.
      if( GetNumberOfConsoleInputEvents( hStdIn, &dwRead ) == FALSE )
      {
          // Format an error message accroding to GetLastError() return by API.
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
          ReleaseGlobals();
          // to remove the handle
          SetConsoleCtrlHandler( NULL, FALSE );
          return( EXIT_FAILURE );
      }
  
  
      // clear the console input buffer
      if ( FALSE == FlushConsoleInputBuffer (hStdIn))
      {
          // Format an error message accroding to GetLastError() return by API.
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
          ReleaseGlobals();
          // to remove the handle
          SetConsoleCtrlHandler( NULL, FALSE );
          return( EXIT_FAILURE );
      }
  
  #endif
  
      // check whether /T value is -1. If so, need to wait indefinitely for a key press..
      if( -1 == tWait )
      {
          // check whether /nobreak is specified
          if ( FALSE == bNBActuals )
          {
              // Wait until a key hit.
              ShowMessage( stdout, GetResString( IDS_WAIT_MSG ) );
          }
          else // /nobreak option is not specified
          {
              // Wait until a CTRL+C key hit.
              ShowMessage( stdout, GetResString( IDS_NO_BREAK_MSG ) );
          }
  
          // Ensure infinite do loop.
          tEnd = tNow + 1;
      }
      else
      {
          // Wait with a timeout period.
          // Compute end time.
          tEnd = tNow + tWait;
  
           // Figure the time display dwWidth.
           // Need to decrement the time factor (/T) specified at a specified
           // location. To get the position where it needs to be decremented,
           // get the width of the /T value
  
          if (tWait < 10)
          {
              // if the /T value is less than 10 then set the width as 1
              dwWidth = 1;
          }
          else
          {
              if (tWait < 100)
              {
                  // if the /T value is less than 100 then set width as 2
                  dwWidth = 2;
              }
              else
              {
                  if (tWait < 1000)
                  {
                      // if the /T value is less than 1000 then set width as 3
                      dwWidth = 3;
                  }
                  else
                  {
                      if (tWait < 10000)
                      {
                          // if the /T value is less than 10000 then set width as 4
                          dwWidth = 4;
                      }
                      else
                      {
                          // if the /T value is less than 100000 then set width as 5
                          dwWidth = 5;
                      }
                  }
              }
          }
  
          if ( FALSE == Replicate ( wszBackup, ONE_BACK_SPACE, dwWidth, SIZE_OF_ARRAY(wszBackup)))
          {
              // Format an error message accroding to GetLastError() return by API.
              ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_INTERNAL );
              ReleaseGlobals();
              // to remove the handle
              SetConsoleCtrlHandler( NULL, FALSE );
              return( EXIT_FAILURE );
          }
  
          //
          // Initialize the console screen buffer structure to zero's
          // and then get the console handle and screen buffer information
          //
          SecureZeroMemory( wszProgressMsg, sizeof( WCHAR ) * MAX_STRING_LENGTH );
  
          // display the message as ..Waiting for...
          //ShowMessage ( stdout, GetResString (IDS_WAIT_MSG_TIME1 ) );
  
          //format the message as .. n seconds...for wait time
          //_snwprintf( wszProgressMsg, SIZE_OF_ARRAY(wszProgressMsg), WAIT_TIME , dwWidth, tWait );
          hr = StringCchPrintf( wszProgressMsg, SIZE_OF_ARRAY(wszProgressMsg), GetResString(IDS_WAIT_MSG_TIME1) , dwWidth, tWait );
          if ( FAILED (hr))
          {
              SetLastError (HRESULT_CODE (hr));
              SaveLastError();
              ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
              ReleaseGlobals();
              // to remove the handle
              SetConsoleCtrlHandler( NULL, FALSE );
              return( EXIT_FAILURE );
          }
  
          // print the message.
          ShowMessage ( stdout, wszProgressMsg );
  
          //
          // Initialize the console screen buffer structure to zero's
          // and then get the console handle and screen buffer information
          //
          SecureZeroMemory( &csbi, sizeof( CONSOLE_SCREEN_BUFFER_INFO ) );
          hOutput = GetStdHandle( STD_OUTPUT_HANDLE );
          if ( NULL != hOutput )
          {
              // Get the info of screen buffer.
              GetConsoleScreenBufferInfo( hOutput, &csbi );
          }
  
           // set the cursor position
          coord.X = csbi.dwCursorPosition.X;
          coord.Y = csbi.dwCursorPosition.Y;
  
          // check whether /nobreak is specified or not
          if ( FALSE == bNBActuals )
          {
              // display the message as ...press a key to continue ...
              ShowMessage ( stdout, GetResString (IDS_WAIT_MSG_TIME2) );
          }
          else
          {
              // display the message as ...press CTRL+C to quit...
              ShowMessage ( stdout, GetResString (IDS_NB_MSG_TIME) );
          }
  
      }
  
      do
      {
          // Break out if a key is pressed.
  #ifdef WIN32
          //reads data from the specified console input buffer without removing it from the buffer.
          if( PeekConsoleInput( hStdIn, InputBuffer, MAX_NUM_RECS, &dwRead ) == FALSE )
          {
              // Format an error message accroding to GetLastError() return by API.
              ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
              ReleaseGlobals();
              // to remove the handle
              SetConsoleCtrlHandler( NULL, FALSE );
              return( EXIT_FAILURE );
          }
  
          if (dwRead > 0)
          {
              //reads data from a console input buffer and removes it from the buffer
              if( ReadConsoleInput(hStdIn, InputBuffer, MAX_NUM_RECS, &dwRead ) == FALSE )
              {
                  // Format an error message accroding to GetLastError() return by API.
                  ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
                  ReleaseGlobals();
                  // to remove the handle
                  SetConsoleCtrlHandler( NULL, FALSE );
                  return( EXIT_FAILURE );
              }
  
              // Filter the input so that ctrl-c can be generated and passed on.
              // Also, ignore alt key presses and window focus events.
              if( (FOCUS_EVENT != InputBuffer[0].EventType)
                  && (VK_CONTROL != InputBuffer[0].Event.KeyEvent.wVirtualKeyCode)
                  && (VK_CONTROL != InputBuffer[0].Event.KeyEvent.wVirtualScanCode)
                  && (MOUSE_MOVED != InputBuffer[0].Event.MouseEvent.dwEventFlags)
                  && (MOUSE_WHEELED != InputBuffer[0].Event.MouseEvent.dwEventFlags)
                  && (FALSE != InputBuffer[0].Event.KeyEvent.bKeyDown)
                  && (VK_MENU != InputBuffer[0].Event.KeyEvent.wVirtualKeyCode)
                  && ( FALSE == bNBActuals ) )
              {
                  // exit from the loop
                  break;
              }
  
          }
  #else
          //Checks the console for keyboard input.
          if( ( _kbhit() ) && ( FALSE == bNBActuals ) )
          {
              // get characters from the console without echo
              _getch();
  
              // exit from the loop
              break;
          }
  #endif
  
          // check if /T value is other than -1
          if( -1 != tWait )
          {
              // Update the time and time value display.
              tLast = tNow;
  
              // call the function GetTimeInSecs to get the current time in seconds
              bResult = GetTimeInSecs( &tNow );
              if( FALSE == bResult )
              {
                  // Format an error message accroding to GetLastError() return by API.
                  ReleaseGlobals();
                  // to remove the handle
                  SetConsoleCtrlHandler( NULL, FALSE );
                  // could not get the time so exit...
                  return( EXIT_FAILURE );
              }
  
              // check if tLast value is same as tNow.. if not, display the
              // message with tEnd-tNow as a wait value
              if (tLast != tNow)
              {
  
                  // Print the message.
                  SecureZeroMemory( wszProgressMsg, sizeof( WCHAR ) * MAX_STRING_LENGTH );
  
                  // fromat the message
                  //_snwprintf( wszProgressMsg, SIZE_OF_ARRAY(wszProgressMsg), STRING_FORMAT2 , wszBackup, dwWidth, tEnd - tNow );
                  hr = StringCchPrintf( wszProgressMsg, SIZE_OF_ARRAY(wszProgressMsg), STRING_FORMAT2 , wszBackup, dwWidth, tEnd - tNow );
                  if ( FAILED (hr))
                  {
                      SetLastError (HRESULT_CODE (hr));
                      SaveLastError();
                      ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
                      ReleaseGlobals();
                      // to remove the handle
                      SetConsoleCtrlHandler( NULL, FALSE );
                      return( EXIT_FAILURE );
  
                  }
  
                  // set the cursor position
                  SetConsoleCursorPosition( hOutput, coord );
  
                  // display the message as ..tEnd - tNow seconds.. at the current cursor location
                  ShowMessage ( stdout, wszProgressMsg );
              }
          }
  
  #ifdef WIN32
          // Sleep for sometime...
          Sleep( 100 );
  #endif
  
      }while( tNow < tEnd ); //check till tNow is less than tEnd value
  
      ShowMessage ( stdout, L"\n" );
      // release global variables
      ReleaseGlobals();
  
      // to remove the handle
      SetConsoleCtrlHandler( NULL, FALSE );
  
      // return 0
      return( EXIT_SUCCESS );
  }
  
  BOOL
  GetTimeInSecs(
      OUT time_t *ptTime
      )
  /*++
  Routine Description
      This function calculates the time elapsed in secs since
      midnight (00:00:00), January 1, 1970
  
  Arguments:
      [out] time_t ptTime  :  variable to hold the time in secs
  
  Return Value
      TRUE on success
      FALSE on failure
  --*/
  {
  #ifdef YANK
      //local variables
      SYSTEMTIME  st = {0};
      FILETIME    ft = {0};
  
      // check for NULL
      if ( NULL == ptTime )
      {
          SetLastError (ERROR_INVALID_PARAMETER );
          SaveLastError();
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_INTERNAL );
          return FALSE;
      }
  
      // get the system time
      GetSystemTime( &st );
  
      // convert system time to file time
      if( SystemTimeToFileTime( &st, &ft ) == FALSE )
      {
          // Format an error message accroding to GetLastError() return by API.
          ShowLastErrorEx( stderr, SLE_TYPE_ERROR| SLE_SYSTEM );
          return( FALSE );
      }
  
      // need to check for LowDateTime rollover...
      *ptTime = ft.dwLowDateTime / LOW_DATE_TIME_ROLL_OVER;
  #else
      // This function returns the time elapsed in secs since midnight (00:00:00), January 1, 1970
      time( ptTime );
  #endif
  
      // return 0
      return( TRUE );
  }
  
  VOID
  DisplayUsage(
      VOID
      )
  /*++
  Routine Description
      This function displays the usage of this utility
  
  Arguments:
      NONE
  
  Return Value
      NONE
  --*/
  {
      // local variable
      DWORD dwIndex = 0;
  
      // Displaying main usage
      for( dwIndex = IDS_HELP_START; dwIndex <= IDS_HELP_END; dwIndex++ )
      {
          ShowMessage( stdout, GetResString( dwIndex ) );
      }
  
      return;
  }
  
  BOOL ProcessOptions(
      IN DWORD argc,
      IN LPCWSTR argv[],
      OUT BOOL *pbUsage,
      OUT DWORD *pwTimeActuals,
      OUT LPWSTR wszTimeout,
      OUT BOOL *pbNBActuals
      )
  /*++
  Routine Description
      This function processes the command line for the main options
  
  Arguments:
      [in]  argc      : Number of Command line arguments.
      [in]  argv      : Pointer to Command line arguments.
      [out] pbUsage   : Flag that indicates whether the usage is to be displayed or not.
      [out] plTimeoutVal : contains the timeout value specified on the command line
  
  Return Value
      TRUE on success
      FALSE on failure
  --*/
  {
  
      // sub-local variables
      TCMDPARSER2*  pcmdParser = NULL;
      TCMDPARSER2 cmdParserOptions[MAX_COMMANDLINE_OPTIONS];
      BOOL bReturn = FALSE;
  
      // command line options
      const WCHAR szTimeoutOpt[] = L"t";
      const WCHAR szNoBreakOpt[] = L"nobreak";
      const WCHAR szHelpOpt[] = L"?";
  
      // -? help/usage
      pcmdParser = cmdParserOptions + OI_USAGE;
  
      StringCopyA( pcmdParser->szSignature, "PARSER2\0", 8 );
  
      pcmdParser->dwType = CP_TYPE_BOOLEAN;
      pcmdParser->pwszOptions = szHelpOpt;
      pcmdParser->pwszFriendlyName = NULL;
      pcmdParser->pwszValues = NULL;
      pcmdParser->dwFlags    = CP2_USAGE;
      pcmdParser->dwCount    = 1;
      pcmdParser->dwActuals  = 0;
      pcmdParser->pValue     = pbUsage;
      pcmdParser->dwLength    = MAX_STRING_LENGTH;
      pcmdParser->pFunction     = NULL;
      pcmdParser->pFunctionData = NULL;
      pcmdParser->dwReserved = 0;
      pcmdParser->pReserved1 = NULL;
      pcmdParser->pReserved2 = NULL;
      pcmdParser->pReserved3 = NULL;
  
      // -T <timeout>
      pcmdParser = cmdParserOptions + OI_TIME_OUT;
  
      StringCopyA( pcmdParser->szSignature, "PARSER2\0", 8 );
  
      pcmdParser->dwType = CP_TYPE_TEXT;
      pcmdParser->pwszOptions = szTimeoutOpt;
      pcmdParser->pwszFriendlyName = NULL;
      pcmdParser->pwszValues = NULL;
      pcmdParser->dwFlags    = CP2_DEFAULT|CP2_VALUE_TRIMINPUT|CP2_VALUE_NONULL;
      pcmdParser->dwCount    = 1;
      pcmdParser->dwActuals  = 0;
      pcmdParser->pValue     = wszTimeout;
      pcmdParser->dwLength    = MAX_STRING_LENGTH;
      pcmdParser->pFunction     = NULL;
      pcmdParser->pFunctionData = NULL;
      pcmdParser->dwReserved = 0;
      pcmdParser->pReserved1 = NULL;
      pcmdParser->pReserved2 = NULL;
      pcmdParser->pReserved3 = NULL;
  
      // -NOBREAK
      pcmdParser = cmdParserOptions + OI_NB_OUT;
  
      StringCopyA( pcmdParser->szSignature, "PARSER2\0", 8 );
  
      pcmdParser->dwType = CP_TYPE_BOOLEAN;
      pcmdParser->pwszOptions = szNoBreakOpt;
      pcmdParser->pwszFriendlyName = NULL;
      pcmdParser->pwszValues = NULL;
      pcmdParser->dwFlags    = 0;
      pcmdParser->dwCount    = 1;
      pcmdParser->dwActuals  = 0;
      pcmdParser->pValue     = pbNBActuals;
      pcmdParser->dwLength    = MAX_STRING_LENGTH;
      pcmdParser->pFunction     = NULL;
      pcmdParser->pFunctionData = NULL;
      pcmdParser->dwReserved = 0;
      pcmdParser->pReserved1 = NULL;
      pcmdParser->pReserved2 = NULL;
      pcmdParser->pReserved3 = NULL;
  
  
      //
      // do the command line parsing and get the appropriate values
      //
  
      bReturn = DoParseParam2( argc, argv, -1, SIZE_OF_ARRAY(cmdParserOptions), cmdParserOptions, 0);
      if( FALSE == bReturn) // Invalid commandline
      {
          // Reason is already set by DoParseParam2
          return FALSE;
      }
  
      pcmdParser = cmdParserOptions + OI_TIME_OUT;
      // get the value for /T value
      *pwTimeActuals = pcmdParser->dwActuals;
  
      //return 0
      return TRUE;
  }
  
  
  
  BOOL WINAPI
  HandlerRoutine(
    IN DWORD dwCtrlType   //  control signal type
  )
  /*++
     Routine Description:
      This function handles the control key CTRL+C.
  
     Arguments:
          IN dwCtrlType : Error code value
  
     Return Value:
         TRUE on success and FALSE on failure
  --*/
  {
      // check for CTRL+C key
      if ( dwCtrlType == CTRL_C_EVENT )
      {
          ShowMessage ( stdout, L"\n" );
          // release globals
          ReleaseGlobals ();
  
          // to remove the handle
          SetConsoleCtrlHandler( NULL, FALSE );
  
          // exit 0
          ExitProcess ( TRUE );
      }
  
      // for remaining keys return false
      return FALSE;
  }
  
  
  