2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Timeout utility
4 * FILE: base/applications/cmdutils/timeout/timeout.c
5 * PURPOSE: An enhanced alternative to the Pause command.
6 * PROGRAMMERS: Lee Schroeder (spaceseel at gmail dot com)
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
22 VOID
PrintError(DWORD dwError
)
24 if (dwError
== ERROR_SUCCESS
)
27 ConMsgPuts(StdErr
, FORMAT_MESSAGE_FROM_SYSTEM
,
28 NULL
, dwError
, LANG_USER_DEFAULT
);
29 ConPuts(StdErr
, L
"\n");
34 CtrlCIntercept(DWORD dwCtrlType
)
39 ConPuts(StdOut
, L
"\n");
40 SetConsoleCtrlHandler(NULL
, FALSE
);
41 ExitProcess(EXIT_FAILURE
);
47 INT
InputWait(BOOL bNoBreak
, INT timerValue
)
49 INT Status
= EXIT_SUCCESS
;
51 BOOL bUseTimer
= (timerValue
!= -1);
56 INPUT_RECORD InputRecords
[5];
58 BOOL DisplayMsg
= TRUE
;
59 UINT WaitMsgId
= (bNoBreak
? IDS_NOBREAK_INPUT
: IDS_USER_INPUT
);
60 UINT WaitCountMsgId
= (bNoBreak
? IDS_NOBREAK_INPUT_COUNT
: IDS_USER_INPUT_COUNT
);
62 /* Retrieve the current input handle */
63 hInput
= ConStreamGetOSHandle(StdIn
);
64 if (hInput
== INVALID_HANDLE_VALUE
)
66 ConResPrintf(StdErr
, IDS_ERROR_INVALID_HANDLE_VALUE
, GetLastError());
70 /* Start a new wait if we use the timer */
71 if (bNoBreak
&& bUseTimer
)
73 hTimer
= CreateWaitableTimer(NULL
, TRUE
, NULL
);
76 /* A problem happened, bail out */
77 PrintError(GetLastError());
82 dwStartTime
= GetTickCount();
84 /* If /NOBREAK is used, monitor for Ctrl-C input */
86 SetConsoleCtrlHandler(CtrlCIntercept
, TRUE
);
88 /* Initially flush the console input queue to remove any pending events */
89 if (!GetNumberOfConsoleInputEvents(hInput
, &NumRecords
) ||
90 !FlushConsoleInputBuffer(hInput
))
92 /* A problem happened, bail out */
93 PrintError(GetLastError());
94 Status
= EXIT_FAILURE
;
98 ConPuts(StdOut
, L
"\n");
100 /* If the timer is not used, just show the message */
103 ConPuts(StdOut
, L
"\r");
104 ConResPuts(StdOut
, WaitMsgId
);
109 /* Decrease the timer if we use it */
113 * Compute how much time the previous operations took.
114 * This allows us in particular to take account for any time
115 * elapsed if something slowed down, or if the console has been
116 * paused in the meantime.
118 timeElapsed
= GetTickCount() - dwStartTime
;
119 if (timeElapsed
>= 1000)
121 /* Increase dwStartTime by steps of 1 second */
123 dwStartTime
+= (1000 * timeElapsed
);
125 if (timeElapsed
<= timerValue
)
126 timerValue
-= timeElapsed
;
135 ConPuts(StdOut
, L
"\r");
136 ConResPrintf(StdOut
, WaitCountMsgId
, timerValue
);
137 ConPuts(StdOut
, L
" \b");
142 /* Stop when the timer reaches zero */
147 /* If /NOBREAK is used, only allow Ctrl-C input which is handled by the console handler */
152 LARGE_INTEGER DueTime
;
154 /* We use the timer: use a passive wait of maximum 1 second */
155 timeElapsed
= GetTickCount() - dwStartTime
;
156 if (timeElapsed
< 1000)
158 DueTime
.QuadPart
= Int32x32To64(1000 - timeElapsed
, -10000);
159 SetWaitableTimer(hTimer
, &DueTime
, 0, NULL
, NULL
, FALSE
);
160 dwWaitState
= WaitForSingleObject(hTimer
, INFINITE
);
162 /* Check whether the timer has been signaled */
163 if (dwWaitState
!= WAIT_OBJECT_0
)
165 /* An error happened, bail out */
166 PrintError(GetLastError());
167 Status
= EXIT_FAILURE
;
174 /* No timer is used: wait indefinitely */
181 /* /NOBREAK is not used, check for user key presses */
184 * If the timer is used, use a passive wait of maximum 1 second
185 * while monitoring for incoming console input events, so that
186 * we are still able to display the timing count.
187 * Indeed, ReadConsoleInputW() indefinitely waits until an input
188 * event appears. ReadConsoleInputW() is however used to retrieve
189 * the input events where there are some, as well as for waiting
190 * indefinitely in case we do not use the timer.
194 /* Wait a maximum of 1 second for input events */
195 timeElapsed
= GetTickCount() - dwStartTime
;
196 if (timeElapsed
< 1000)
197 dwWaitState
= WaitForSingleObject(hInput
, 1000 - timeElapsed
);
199 dwWaitState
= WAIT_TIMEOUT
;
201 /* Check whether the input event has been signaled, or a timeout happened */
202 if (dwWaitState
== WAIT_TIMEOUT
)
204 if (dwWaitState
!= WAIT_OBJECT_0
)
206 /* An error happened, bail out */
207 PrintError(GetLastError());
208 Status
= EXIT_FAILURE
;
212 /* Be sure there is something in the console input queue */
213 if (!PeekConsoleInputW(hInput
, InputRecords
, ARRAYSIZE(InputRecords
), &NumRecords
))
215 /* An error happened, bail out */
216 ConResPrintf(StdErr
, IDS_ERROR_READ_INPUT
, GetLastError());
217 Status
= EXIT_FAILURE
;
226 * Some events have been detected, pop them out from the input queue.
227 * In case we do not use the timer, wait indefinitely until an input
230 if (!ReadConsoleInputW(hInput
, InputRecords
, ARRAYSIZE(InputRecords
), &NumRecords
))
232 /* An error happened, bail out */
233 ConResPrintf(StdErr
, IDS_ERROR_READ_INPUT
, GetLastError());
234 Status
= EXIT_FAILURE
;
238 /* Check the input events for a key press */
239 for (i
= 0; i
< NumRecords
; ++i
)
241 /* Ignore any non-key event */
242 if (InputRecords
[i
].EventType
!= KEY_EVENT
)
245 /* Ignore any system key event */
246 if ((InputRecords
[i
].Event
.KeyEvent
.wVirtualKeyCode
== VK_CONTROL
) ||
247 // (InputRecords[i].Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED )) ||
248 // (InputRecords[i].Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) ||
249 (InputRecords
[i
].Event
.KeyEvent
.wVirtualKeyCode
== VK_MENU
))
254 /* This is a non-system key event, stop waiting */
260 ConPuts(StdOut
, L
"\n");
264 SetConsoleCtrlHandler(NULL
, FALSE
);
266 if (bNoBreak
&& bUseTimer
)
272 int wmain(int argc
, WCHAR
* argv
[])
276 BOOL bDisableInput
= FALSE
, fTimerFlags
= 0;
279 /* Initialize the Console Standard Streams */
284 ConResPrintf(StdOut
, IDS_USAGE
);
288 /* Parse the command line for options */
289 for (index
= 1; index
< argc
; index
++)
291 if (argv
[index
][0] == L
'-' || argv
[index
][0] == L
'/')
293 switch (towupper(argv
[index
][1]))
295 case L
'?': /* Help */
297 ConResPrintf(StdOut
, IDS_USAGE
);
301 case L
'T': /* Timer */
303 /* Consecutive /T switches are invalid */
306 ConResPrintf(StdErr
, IDS_ERROR_ONE_TIME
);
310 /* Remember that a /T switch has been encountered */
313 /* Go to the next (timer) value */
318 /* This flag is used to ignore any keyboard keys but Ctrl-C */
319 if (_wcsicmp(&argv
[index
][1], L
"NOBREAK") == 0)
321 bDisableInput
= TRUE
;
323 /* Go to next value */
328 /* The timer value can also be specified without the /T switch */
330 /* Only one timer value is supported */
333 ConResPrintf(StdErr
, IDS_ERROR_ONE_TIME
);
337 timerValue
= wcstol(argv
[index
], &pszNext
, 10);
340 ConResPrintf(StdErr
, IDS_ERROR_OUT_OF_RANGE
);
344 /* Remember that the timer value has been set */
348 /* A timer value is mandatory in order to continue */
349 if (!(fTimerFlags
& 1))
351 ConResPrintf(StdErr
, IDS_ERROR_NO_TIMER_VALUE
);
355 /* Make sure the timer value is within range */
356 if ((timerValue
< -1) || (timerValue
> 99999))
358 ConResPrintf(StdErr
, IDS_ERROR_OUT_OF_RANGE
);
362 return InputWait(bDisableInput
, timerValue
);