[TIMEOUT]: Implement the TIMEOUT utility (found on Win2k3 and upwards). This is an improved "pause" command, with elapsed time count display.

Based from a patch by Lee Schröder, with modifications by myself.
CORE-10044 #resolve

svn path=/trunk/; revision=75968
This commit is contained in:
Hermès Bélusca-Maïto 2017-09-25 23:31:44 +00:00
parent d2669dc03b
commit 6b45a088f3
7 changed files with 441 additions and 0 deletions

View file

@ -18,6 +18,7 @@ add_subdirectory(reg)
add_subdirectory(schtasks)
add_subdirectory(sort)
add_subdirectory(taskkill)
add_subdirectory(timeout)
add_subdirectory(tree)
add_subdirectory(whoami)
add_subdirectory(wmic)

View file

@ -0,0 +1,8 @@
include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/conutils)
add_executable(timeout timeout.c timeout.rc)
set_module_type(timeout win32cui UNICODE)
target_link_libraries(timeout conutils ${PSEH_LIB})
add_importlibs(timeout msvcrt kernel32)
add_cd_file(TARGET timeout DESTINATION reactos/system32 FOR all)

View file

@ -0,0 +1,33 @@
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
STRINGTABLE
BEGIN
IDS_USAGE "ReactOS Timeout Utility\n\
\n\
TIMEOUT [/?] [/T] delay [/NOBREAK]\n\
\n\
Description:\n\
This tool waits until a specified time period (in seconds) has elapsed,\n\
or until any key is pressed. A parameter to ignore the key press is also\n\
accepted.\n\
\n\
Parameters:\n\
/? Display this help screen.\n\
\n\
/T delay Specify the number of seconds to wait (-1 to 99999).\n\
A value of -1 means the program will wait until a key is pressed.\n\
Note that the ""/T"" specification is optional, you can just\n\
specify the delay value without it.\n\
\n\
/NOBREAK Ignore all keyboard input except for Ctrl+C.\n\
"
IDS_ERROR_OUT_OF_RANGE "ERROR: The timer value must be within range (-1 to 99999).\n"
IDS_ERROR_INVALID_HANDLE_VALUE "ERROR: Unable to get the standard handle for the console (error %lu).\n"
IDS_ERROR_READ_INPUT "ERROR: Unable to read the console input (error %lu).\n"
IDS_ERROR_NO_TIMER_VALUE "ERROR: The timer value must be specified (-1 to 99999).\n"
IDS_ERROR_ONE_TIME "ERROR: Only one timer value is needed.\n"
IDS_NOBREAK_INPUT "Press Ctrl+C to quit..."
IDS_USER_INPUT "Press any key to continue..."
IDS_NOBREAK_INPUT_COUNT "Waiting for %d second(s), press Ctrl+C to quit..."
IDS_USER_INPUT_COUNT "Waiting for %d second(s), press any key to continue..."
END

View file

@ -0,0 +1,34 @@
LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
STRINGTABLE
BEGIN
IDS_USAGE "ReactOS Timeout Utility\n\
\n\
TIMEOUT [/?] [/T] délai [/NOBREAK]\n\
\n\
Description :\n\
Cet outil permet d'attendre jusqu'à ce qu'un délai (en secondes) s'écoule,\n\
ou bien jusqu'à ce qu'une touche de clavier soit pressée. Un paramètre\n\
permettant d'ignorer l'appui de touche de clavier est accepté.\n\
\n\
Liste de paramètres :\n\
/? Affiche cet écran d'aide.\n\
\n\
/T délai Spécifie le délai d'attente en secondes (entre -1 et 99999).\n\
La valeur -1 signifie que le programme attend jusqu'à ce qu'une\n\
touche soit pressée.\n\
Veuillez noter que la spécification ""/T"" est optionnelle, vous\n\
pouvez spécifier la valeur de délai sans celle-ci.\n\
\n\
/NOBREAK Ignore toute frappe de clavier, sauf pour Ctrl+C.\n\
"
IDS_ERROR_OUT_OF_RANGE "ERREUR: Le délai d'attente doit être compris dans la plage (entre -1 et 99999).\n"
IDS_ERROR_INVALID_HANDLE_VALUE "ERREUR: Impossible d'obtenir le handle standard pour la console (erreur %lu).\n"
IDS_ERROR_READ_INPUT "ERREUR: Impossible de lire l'entrée de console (erreur %lu).\n"
IDS_ERROR_NO_TIMER_VALUE "ERREUR: Le délai d'attente doit être spécifié (entre -1 et 99999).\n"
IDS_ERROR_ONE_TIME "ERREUR: Une seule valeur de délai est nécessaire.\n"
IDS_NOBREAK_INPUT "Appuyez sur Ctrl+C pour quitter..."
IDS_USER_INPUT "Appuyez sur une touche pour continuer..."
IDS_NOBREAK_INPUT_COUNT "Attendre %d seconde(s), appuyez sur Ctrl+C pour quitter..."
IDS_USER_INPUT_COUNT "Attendre %d seconde(s), appuyez sur une touche pour continuer..."
END

View file

@ -0,0 +1,12 @@
#pragma once
#define IDS_USAGE 0
#define IDS_ERROR_OUT_OF_RANGE 1
#define IDS_ERROR_INVALID_HANDLE_VALUE 2
#define IDS_ERROR_READ_INPUT 3
#define IDS_ERROR_NO_TIMER_VALUE 4
#define IDS_ERROR_ONE_TIME 5
#define IDS_NOBREAK_INPUT 6
#define IDS_USER_INPUT 7
#define IDS_NOBREAK_INPUT_COUNT 8
#define IDS_USER_INPUT_COUNT 9

View file

@ -0,0 +1,332 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Timeout utility
* FILE: base/applications/cmdutils/timeout/timeout.c
* PURPOSE: An enhanced alternative to the Pause command.
* PROGRAMMERS: Lee Schroeder (spaceseel at gmail dot com)
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
#include <stdio.h>
#include <stdlib.h>
#include <windef.h>
#include <winbase.h>
#include <wincon.h>
#include <winuser.h>
#include <conutils.h>
#include "resource.h"
VOID PrintError(DWORD dwError)
{
if (dwError == ERROR_SUCCESS)
return;
ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwError, LANG_USER_DEFAULT);
ConPuts(StdErr, L"\n");
}
BOOL
WINAPI
CtrlCIntercept(DWORD dwCtrlType)
{
switch (dwCtrlType)
{
case CTRL_C_EVENT:
ConPuts(StdOut, L"\n");
SetConsoleCtrlHandler(NULL, FALSE);
ExitProcess(EXIT_FAILURE);
return TRUE;
}
return FALSE;
}
INT InputWait(BOOL bNoBreak, INT timerValue)
{
INT Status = EXIT_SUCCESS;
HANDLE hInput;
BOOL bUseTimer = (timerValue != -1);
DWORD dwStartTime;
LONG timeElapsed;
DWORD dwWaitState;
INPUT_RECORD InputRecords[5];
ULONG NumRecords, i;
BOOL DisplayMsg = TRUE;
UINT WaitMsgId = (bNoBreak ? IDS_NOBREAK_INPUT : IDS_USER_INPUT);
UINT WaitCountMsgId = (bNoBreak ? IDS_NOBREAK_INPUT_COUNT : IDS_USER_INPUT_COUNT);
/* Retrieve the current input handle */
hInput = ConStreamGetOSHandle(StdIn);
if (hInput == INVALID_HANDLE_VALUE)
{
ConResPrintf(StdErr, IDS_ERROR_INVALID_HANDLE_VALUE, GetLastError());
return EXIT_FAILURE;
}
/* Start a new wait if we use the timer */
if (bUseTimer)
dwStartTime = GetTickCount();
/* If /NOBREAK is used, monitor for Ctrl-C input */
if (bNoBreak)
SetConsoleCtrlHandler(CtrlCIntercept, TRUE);
/* Initially flush the console input queue to remove any pending events */
if (!GetNumberOfConsoleInputEvents(hInput, &NumRecords) ||
!FlushConsoleInputBuffer(hInput))
{
/* A problem happened, bail out */
PrintError(GetLastError());
Status = EXIT_FAILURE;
goto Quit;
}
ConPuts(StdOut, L"\n");
/* If the timer is not used, just show the message */
if (!bUseTimer)
{
ConPuts(StdOut, L"\r");
ConResPuts(StdOut, WaitMsgId);
}
while (TRUE)
{
/* Decrease the timer if we use it */
if (bUseTimer)
{
/*
* Compute how much time the previous operations took.
* This allows us in particular to take account for any time
* elapsed if something slowed down, or if the console has been
* paused in the meantime.
*/
timeElapsed = GetTickCount() - dwStartTime;
if (timeElapsed >= 1000)
{
/* Increase dwStartTime by steps of 1 second */
timeElapsed /= 1000;
dwStartTime += (1000 * timeElapsed);
if (timeElapsed <= timerValue)
timerValue -= timeElapsed;
else
timerValue = 0;
DisplayMsg = TRUE;
}
if (DisplayMsg)
{
ConPuts(StdOut, L"\r");
ConResPrintf(StdOut, WaitCountMsgId, timerValue);
ConPuts(StdOut, L" \b");
DisplayMsg = FALSE;
}
/* Stop when the timer reaches zero */
if (timerValue <= 0)
break;
}
/* If /NOBREAK is used, only allow Ctrl-C input which is handled by the console handler */
if (bNoBreak)
{
if (bUseTimer)
{
/* We use the timer: wait a little bit before updating it */
Sleep(100);
}
else
{
/* No timer is used: wait indefinitely */
Sleep(INFINITE);
}
continue;
}
/* /NOBREAK is not used, check for user key presses */
/*
* If the timer is used, use a passive wait of maximum 1 second
* while monitoring for incoming console input events, so that
* we are still able to display the timing count.
* Indeed, ReadConsoleInputW() indefinitely waits until an input
* event appears. ReadConsoleInputW() is however used to retrieve
* the input events where there are some, as well as for waiting
* indefinitely in case we do not use the timer.
*/
if (bUseTimer)
{
/* Wait a maximum of 1 second for input events */
timeElapsed = GetTickCount() - dwStartTime;
if (timeElapsed < 1000)
dwWaitState = WaitForSingleObject(hInput, 1000 - timeElapsed);
else
dwWaitState = WAIT_TIMEOUT;
/* Check whether the input handle has been signaled, or a timeout happened */
if (dwWaitState == WAIT_TIMEOUT)
continue;
if (dwWaitState != WAIT_OBJECT_0)
{
/* An error happened, bail out */
PrintError(GetLastError());
Status = EXIT_FAILURE;
break;
}
/* Be sure there is someting in the console input queue */
if (!PeekConsoleInputW(hInput, InputRecords, ARRAYSIZE(InputRecords), &NumRecords))
{
/* An error happened, bail out */
ConResPrintf(StdErr, IDS_ERROR_READ_INPUT, GetLastError());
Status = EXIT_FAILURE;
break;
}
if (NumRecords == 0)
continue;
}
/*
* Some events have been detected, pop them out from the input queue.
* In case we do not use the timer, wait indefinitely until an input
* event appears.
*/
if (!ReadConsoleInputW(hInput, InputRecords, ARRAYSIZE(InputRecords), &NumRecords))
{
/* An error happened, bail out */
ConResPrintf(StdErr, IDS_ERROR_READ_INPUT, GetLastError());
Status = EXIT_FAILURE;
break;
}
/* Check the input events for a key press */
for (i = 0; i < NumRecords; ++i)
{
/* Ignore any non-key event */
if (InputRecords[i].EventType != KEY_EVENT)
continue;
/* Ignore any system key event */
if ((InputRecords[i].Event.KeyEvent.wVirtualKeyCode == VK_CONTROL) ||
// (InputRecords[i].Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED )) ||
// (InputRecords[i].Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) ||
(InputRecords[i].Event.KeyEvent.wVirtualKeyCode == VK_MENU))
{
continue;
}
/* This is a non-system key event, stop waiting */
goto Stop;
}
}
Stop:
ConPuts(StdOut, L"\n");
Quit:
if (bNoBreak)
SetConsoleCtrlHandler(NULL, FALSE);
return Status;
}
int wmain(int argc, WCHAR* argv[])
{
INT timerValue = -1;
PWCHAR pszNext;
BOOL bDisableInput = FALSE, fTimerFlags = 0;
int index = 0;
/* Initialize the Console Standard Streams */
ConInitStdStreams();
if (argc == 1)
{
ConResPrintf(StdOut, IDS_USAGE);
return EXIT_SUCCESS;
}
/* Parse the command line for options */
for (index = 1; index < argc; index++)
{
if (argv[index][0] == L'-' || argv[index][0] == L'/')
{
switch (towupper(argv[index][1]))
{
case L'?': /* Help */
{
ConResPrintf(StdOut, IDS_USAGE);
return EXIT_SUCCESS;
}
case L'T': /* Timer */
{
/* Consecutive /T switches are invalid */
if (fTimerFlags & 2)
{
ConResPrintf(StdErr, IDS_ERROR_ONE_TIME);
return EXIT_FAILURE;
}
/* Remember that a /T switch has been encountered */
fTimerFlags |= 2;
/* Go to the next (timer) value */
continue;
}
}
/* This flag is used to ignore any keyboard keys but Ctrl-C */
if (_wcsicmp(&argv[index][1], L"NOBREAK") == 0)
{
bDisableInput = TRUE;
/* Go to next value */
continue;
}
}
/* The timer value can also be specified without the /T switch */
/* Only one timer value is supported */
if (fTimerFlags & 1)
{
ConResPrintf(StdErr, IDS_ERROR_ONE_TIME);
return EXIT_FAILURE;
}
timerValue = wcstol(argv[index], &pszNext, 10);
if (*pszNext)
{
ConResPrintf(StdErr, IDS_ERROR_OUT_OF_RANGE);
return EXIT_FAILURE;
}
/* Remember that the timer value has been set */
fTimerFlags |= 1;
}
/* A timer value is mandatory in order to continue */
if (!(fTimerFlags & 1))
{
ConResPrintf(StdErr, IDS_ERROR_NO_TIMER_VALUE);
return EXIT_FAILURE;
}
/* Make sure the timer value is within range */
if ((timerValue < -1) || (timerValue > 99999))
{
ConResPrintf(StdErr, IDS_ERROR_OUT_OF_RANGE);
return EXIT_FAILURE;
}
return InputWait(bDisableInput, timerValue);
}

View file

@ -0,0 +1,21 @@
#include <windef.h>
// #include <winuser.h>
#include "resource.h"
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Timeout Utility"
#define REACTOS_STR_INTERNAL_NAME "timeout"
#define REACTOS_STR_ORIGINAL_FILENAME "timeout.exe"
#include <reactos/version.rc>
/* UTF-8 */
#pragma code_page(65001)
#ifdef LANGUAGE_EN_US
#include "lang/en-US.rc"
#endif
#ifdef LANGUAGE_FR_FR
#include "lang/fr-FR.rc"
#endif