Index: base/applications/cmdutils/CMakeLists.txt =================================================================== --- base/applications/cmdutils/CMakeLists.txt (revision 75846) +++ base/applications/cmdutils/CMakeLists.txt (working copy) @@ -17,6 +17,7 @@ add_subdirectory(schtasks) add_subdirectory(sort) add_subdirectory(taskkill) +add_subdirectory(timeout) add_subdirectory(tree) add_subdirectory(whoami) add_subdirectory(wmic) Index: base/applications/cmdutils/timeout/CMakeLists.txt =================================================================== --- base/applications/cmdutils/timeout/CMakeLists.txt (nonexistent) +++ base/applications/cmdutils/timeout/CMakeLists.txt (working copy) @@ -0,0 +1,9 @@ + +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 user32 kernel32) +add_importlibs(timeout msvcrt kernel32) +add_cd_file(TARGET timeout DESTINATION reactos/system32 FOR all) Index: base/applications/cmdutils/timeout/lang/en-US.rc =================================================================== --- base/applications/cmdutils/timeout/lang/en-US.rc (nonexistent) +++ base/applications/cmdutils/timeout/lang/en-US.rc (working copy) @@ -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 user 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 Index: base/applications/cmdutils/timeout/resource.h =================================================================== --- base/applications/cmdutils/timeout/resource.h (nonexistent) +++ base/applications/cmdutils/timeout/resource.h (working copy) @@ -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 Index: base/applications/cmdutils/timeout/timeout.c =================================================================== --- base/applications/cmdutils/timeout/timeout.c (nonexistent) +++ base/applications/cmdutils/timeout/timeout.c (working copy) @@ -0,0 +1,307 @@ +/* + * 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) + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#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(1); + return TRUE; + } + return FALSE; +} + +INT InputWait(BOOL NoBreak, INT timing) +{ + INT Status = EXIT_SUCCESS; + HANDLE hInput; + BOOL useNoTimer = (timing == -1); + DWORD dwStartTime; + LONG timeElapsed; + DWORD dwWaitState; + INPUT_RECORD InputRecords[5]; + ULONG NumRecords, i; + BOOL DisplayMsg = TRUE; + UINT WaitMsgId = (NoBreak ? IDS_NOBREAK_INPUT : IDS_USER_INPUT); + UINT WaitCountMsgId = (NoBreak ? 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 (timing != -1) + dwStartTime = GetTickCount(); + + /* If /nobreak is used, monitor for Ctrl-C input */ + if (NoBreak) + 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 there is no timer, just show the message */ + if (timing == -1) // if (useNoTimer) + { + ConPuts(StdOut, L"\r"); + ConResPuts(StdOut, WaitMsgId); + } + + while (TRUE) + { + /* Decrease the timer if we use it */ + if (timing != -1) + { + /* Compute how much time the previous operations took */ + timeElapsed = GetTickCount() - dwStartTime; + if (timeElapsed >= 1000) + { + /* Increase dwStartTime by steps of 1 second */ + timeElapsed /= 1000; + dwStartTime += (1000 * timeElapsed); + + if (timeElapsed <= timing) + timing -= timeElapsed; + else + timing = 0; + + DisplayMsg = TRUE; + } + + if (DisplayMsg) + { + ConPuts(StdOut, L"\r"); + ConResPrintf(StdOut, WaitCountMsgId, timing); + ConPuts(StdOut, L" \b"); + + DisplayMsg = FALSE; + } + + if (timing <= 0) + break; + } + + /* If /nobreak is used, only allow Ctrl-C input */ + if (NoBreak) + { + if (timing != -1) + { + /* 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 (timing != -1) + { + /* 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; + } + + 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 */ + 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 (NoBreak) + SetConsoleCtrlHandler(NULL, FALSE); + + return Status; +} + +int wmain(int argc, WCHAR* argv[]) +{ + INT timerValue = 1; + PWCHAR pszNext; + BOOL disableInput = FALSE, isTimed = FALSE; + int index = 0; + + /* Initialize the Console Standard Streams */ + ConInitStdStreams(); + + if (argc == 1) + { + ConResPrintf(StdOut, IDS_USAGE); + return EXIT_SUCCESS; + } + + /* Parse command line for options */ + for (index = 1; index < argc; index++) + { + if (argv[index][0] == L'-' || argv[index][0] == L'/') + { + switch (towlower(argv[index][1])) + { + case L'?': /* Help */ + { + ConResPrintf(StdOut, IDS_USAGE); + return EXIT_SUCCESS; + } + + case L't': /* Timer */ + { + /* Only one timer value is supported */ + if (isTimed == TRUE) + { + ConResPrintf(StdErr, IDS_ERROR_ONE_TIME); + return EXIT_FAILURE; + } + + /* 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) + { + disableInput = TRUE; + + /* Go to next value */ + continue; + } + } + + /* A timer value can be specified without the /T switch */ + + /* Only one timer value is supported */ + if (isTimed == TRUE) + { + 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; + } + + isTimed = TRUE; + } + + /* The program needs a timer value in order to continue */ + if (isTimed == FALSE) + { + 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(disableInput, timerValue); +} Index: base/applications/cmdutils/timeout/timeout.rc =================================================================== --- base/applications/cmdutils/timeout/timeout.rc (nonexistent) +++ base/applications/cmdutils/timeout/timeout.rc (working copy) @@ -0,0 +1,18 @@ +#include +// #include + +#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 + +/* UTF-8 */ +#pragma code_page(65001) + +#ifdef LANGUAGE_EN_US + #include "lang/en-US.rc" +#endif