ReactOS  0.4.12-dev-685-gf36cbf7
timeout.c
Go to the documentation of this file.
1 /*
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)
8  */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 
13 #include <windef.h>
14 #include <winbase.h>
15 #include <wincon.h>
16 #include <winuser.h>
17 
18 #include <conutils.h>
19 
20 #include "resource.h"
21 
23 {
24  if (dwError == ERROR_SUCCESS)
25  return;
26 
28  NULL, dwError, LANG_USER_DEFAULT);
29  ConPuts(StdErr, L"\n");
30 }
31 
32 BOOL
33 WINAPI
34 CtrlCIntercept(DWORD dwCtrlType)
35 {
36  switch (dwCtrlType)
37  {
38  case CTRL_C_EVENT:
39  ConPuts(StdOut, L"\n");
42  return TRUE;
43  }
44  return FALSE;
45 }
46 
47 INT InputWait(BOOL bNoBreak, INT timerValue)
48 {
50  HANDLE hInput;
51  BOOL bUseTimer = (timerValue != -1);
52  HANDLE hTimer = NULL;
53  DWORD dwStartTime;
54  LONG timeElapsed;
55  DWORD dwWaitState;
56  INPUT_RECORD InputRecords[5];
57  ULONG NumRecords, i;
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);
61 
62  /* Retrieve the current input handle */
63  hInput = ConStreamGetOSHandle(StdIn);
64  if (hInput == INVALID_HANDLE_VALUE)
65  {
67  return EXIT_FAILURE;
68  }
69 
70  /* Start a new wait if we use the timer */
71  if (bNoBreak && bUseTimer)
72  {
73  hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
74  if (hTimer == NULL)
75  {
76  /* A problem happened, bail out */
78  return EXIT_FAILURE;
79  }
80  }
81  if (bUseTimer)
82  dwStartTime = GetTickCount();
83 
84  /* If /NOBREAK is used, monitor for Ctrl-C input */
85  if (bNoBreak)
87 
88  /* Initially flush the console input queue to remove any pending events */
89  if (!GetNumberOfConsoleInputEvents(hInput, &NumRecords) ||
90  !FlushConsoleInputBuffer(hInput))
91  {
92  /* A problem happened, bail out */
95  goto Quit;
96  }
97 
98  ConPuts(StdOut, L"\n");
99 
100  /* If the timer is not used, just show the message */
101  if (!bUseTimer)
102  {
103  ConPuts(StdOut, L"\r");
104  ConResPuts(StdOut, WaitMsgId);
105  }
106 
107  while (TRUE)
108  {
109  /* Decrease the timer if we use it */
110  if (bUseTimer)
111  {
112  /*
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.
117  */
118  timeElapsed = GetTickCount() - dwStartTime;
119  if (timeElapsed >= 1000)
120  {
121  /* Increase dwStartTime by steps of 1 second */
122  timeElapsed /= 1000;
123  dwStartTime += (1000 * timeElapsed);
124 
125  if (timeElapsed <= timerValue)
126  timerValue -= timeElapsed;
127  else
128  timerValue = 0;
129 
130  DisplayMsg = TRUE;
131  }
132 
133  if (DisplayMsg)
134  {
135  ConPuts(StdOut, L"\r");
136  ConResPrintf(StdOut, WaitCountMsgId, timerValue);
137  ConPuts(StdOut, L" \b");
138 
139  DisplayMsg = FALSE;
140  }
141 
142  /* Stop when the timer reaches zero */
143  if (timerValue <= 0)
144  break;
145  }
146 
147  /* If /NOBREAK is used, only allow Ctrl-C input which is handled by the console handler */
148  if (bNoBreak)
149  {
150  if (bUseTimer)
151  {
153 
154  /* We use the timer: use a passive wait of maximum 1 second */
155  timeElapsed = GetTickCount() - dwStartTime;
156  if (timeElapsed < 1000)
157  {
158  /*
159  * For whatever reason, x86 MSVC generates a ntdll!_allmul
160  * call when using Int32x32To64(), instead of an imul
161  * instruction. This leads the linker to error that _allmul
162  * is missing, since we do not link against ntdll.
163  * Everything is however OK with GCC...
164  * We therefore use the __emul() intrinsic which does
165  * the correct job.
166  */
167  DueTime.QuadPart = __emul(1000 - timeElapsed, -10000);
168  SetWaitableTimer(hTimer, &DueTime, 0, NULL, NULL, FALSE);
169  dwWaitState = WaitForSingleObject(hTimer, INFINITE);
170 
171  /* Check whether the timer has been signaled */
172  if (dwWaitState != WAIT_OBJECT_0)
173  {
174  /* An error happened, bail out */
177  break;
178  }
179  }
180  }
181  else
182  {
183  /* No timer is used: wait indefinitely */
184  Sleep(INFINITE);
185  }
186 
187  continue;
188  }
189 
190  /* /NOBREAK is not used, check for user key presses */
191 
192  /*
193  * If the timer is used, use a passive wait of maximum 1 second
194  * while monitoring for incoming console input events, so that
195  * we are still able to display the timing count.
196  * Indeed, ReadConsoleInputW() indefinitely waits until an input
197  * event appears. ReadConsoleInputW() is however used to retrieve
198  * the input events where there are some, as well as for waiting
199  * indefinitely in case we do not use the timer.
200  */
201  if (bUseTimer)
202  {
203  /* Wait a maximum of 1 second for input events */
204  timeElapsed = GetTickCount() - dwStartTime;
205  if (timeElapsed < 1000)
206  dwWaitState = WaitForSingleObject(hInput, 1000 - timeElapsed);
207  else
208  dwWaitState = WAIT_TIMEOUT;
209 
210  /* Check whether the input event has been signaled, or a timeout happened */
211  if (dwWaitState == WAIT_TIMEOUT)
212  continue;
213  if (dwWaitState != WAIT_OBJECT_0)
214  {
215  /* An error happened, bail out */
218  break;
219  }
220 
221  /* Be sure there is something in the console input queue */
222  if (!PeekConsoleInputW(hInput, InputRecords, ARRAYSIZE(InputRecords), &NumRecords))
223  {
224  /* An error happened, bail out */
227  break;
228  }
229 
230  if (NumRecords == 0)
231  continue;
232  }
233 
234  /*
235  * Some events have been detected, pop them out from the input queue.
236  * In case we do not use the timer, wait indefinitely until an input
237  * event appears.
238  */
239  if (!ReadConsoleInputW(hInput, InputRecords, ARRAYSIZE(InputRecords), &NumRecords))
240  {
241  /* An error happened, bail out */
244  break;
245  }
246 
247  /* Check the input events for a key press */
248  for (i = 0; i < NumRecords; ++i)
249  {
250  /* Ignore any non-key event */
251  if (InputRecords[i].EventType != KEY_EVENT)
252  continue;
253 
254  /* Ignore any system key event */
255  if ((InputRecords[i].Event.KeyEvent.wVirtualKeyCode == VK_CONTROL) ||
256  // (InputRecords[i].Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED )) ||
257  // (InputRecords[i].Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) ||
258  (InputRecords[i].Event.KeyEvent.wVirtualKeyCode == VK_MENU))
259  {
260  continue;
261  }
262 
263  /* This is a non-system key event, stop waiting */
264  goto Stop;
265  }
266  }
267 
268 Stop:
269  ConPuts(StdOut, L"\n");
270 
271 Quit:
272  if (bNoBreak)
274 
275  if (bNoBreak && bUseTimer)
276  CloseHandle(hTimer);
277 
278  return Status;
279 }
280 
281 int wmain(int argc, WCHAR* argv[])
282 {
283  INT timerValue = -1;
284  PWCHAR pszNext;
285  BOOL bDisableInput = FALSE, fTimerFlags = 0;
286  int index = 0;
287 
288  /* Initialize the Console Standard Streams */
290 
291  if (argc == 1)
292  {
294  return EXIT_SUCCESS;
295  }
296 
297  /* Parse the command line for options */
298  for (index = 1; index < argc; index++)
299  {
300  if (argv[index][0] == L'-' || argv[index][0] == L'/')
301  {
302  switch (towupper(argv[index][1]))
303  {
304  case L'?': /* Help */
305  {
307  return EXIT_SUCCESS;
308  }
309 
310  case L'T': /* Timer */
311  {
312  /* Consecutive /T switches are invalid */
313  if (fTimerFlags & 2)
314  {
316  return EXIT_FAILURE;
317  }
318 
319  /* Remember that a /T switch has been encountered */
320  fTimerFlags |= 2;
321 
322  /* Go to the next (timer) value */
323  continue;
324  }
325  }
326 
327  /* This flag is used to ignore any keyboard keys but Ctrl-C */
328  if (_wcsicmp(&argv[index][1], L"NOBREAK") == 0)
329  {
330  bDisableInput = TRUE;
331 
332  /* Go to next value */
333  continue;
334  }
335  }
336 
337  /* The timer value can also be specified without the /T switch */
338 
339  /* Only one timer value is supported */
340  if (fTimerFlags & 1)
341  {
343  return EXIT_FAILURE;
344  }
345 
346  timerValue = wcstol(argv[index], &pszNext, 10);
347  if (*pszNext)
348  {
350  return EXIT_FAILURE;
351  }
352 
353  /* Remember that the timer value has been set */
354  fTimerFlags |= 1;
355  }
356 
357  /* A timer value is mandatory in order to continue */
358  if (!(fTimerFlags & 1))
359  {
361  return EXIT_FAILURE;
362  }
363 
364  /* Make sure the timer value is within range */
365  if ((timerValue < -1) || (timerValue > 99999))
366  {
368  return EXIT_FAILURE;
369  }
370 
371  return InputWait(bDisableInput, timerValue);
372 }
#define IDS_ERROR_NO_TIMER_VALUE
Definition: resource.h:7
VOID PrintError(DWORD dwError)
Definition: timeout.c:22
static int argc
Definition: ServiceArgs.c:12
#define TRUE
Definition: types.h:120
#define CloseHandle
Definition: compat.h:398
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:736
#define ERROR_SUCCESS
Definition: deptool.c:10
#define IDS_ERROR_ONE_TIME
Definition: resource.h:8
__wchar_t WCHAR
Definition: xmlstorage.h:180
_In_ LARGE_INTEGER DueTime
Definition: kefuncs.h:524
DWORD WINAPI GetTickCount(VOID)
Definition: time.c:445
#define CTRL_C_EVENT
Definition: wincon.h:65
#define ARRAYSIZE(array)
Definition: filtermapper.c:47
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
VOID WINAPI ExitProcess(IN UINT uExitCode)
Definition: proc.c:1517
BOOL WINAPI FlushConsoleInputBuffer(IN HANDLE hConsoleInput)
Definition: console.c:169
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
#define IDS_USAGE
Definition: resource.h:3
_Check_return_ long __cdecl wcstol(_In_z_ const wchar_t *_Str, _Out_opt_ _Deref_post_z_ wchar_t **_EndPtr, _In_ int _Radix)
#define VK_MENU
Definition: winuser.h:2158
uint16_t * PWCHAR
Definition: typedefs.h:54
#define IDS_USER_INPUT
Definition: resource.h:10
#define IDS_NOBREAK_INPUT
Definition: resource.h:9
#define argv
Definition: mplay32.c:18
int32_t INT
Definition: typedefs.h:56
_In_ PVOID _Out_ BOOLEAN * Stop
Definition: ldrtypes.h:239
WORD wVirtualKeyCode
Definition: wincon.h:227
int wmain(int argc, WCHAR *argv[])
Definition: timeout.c:281
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
#define EXIT_SUCCESS
Definition: rdjpgcom.c:55
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define ConInitStdStreams()
Definition: stream.h:122
long LONG
Definition: pedump.c:60
BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add)
Definition: console.c:2111
#define KEY_EVENT
Definition: wincon.h:122
smooth NULL
Definition: ftsmooth.c:416
GLuint index
Definition: glext.h:6031
#define IDS_USER_INPUT_COUNT
Definition: resource.h:12
#define FORMAT_MESSAGE_FROM_SYSTEM
Definition: winbase.h:404
INT __cdecl ConResPrintf(IN PCON_STREAM Stream, IN UINT uID,...)
Definition: outstream.c:781
HANDLE ConStreamGetOSHandle(IN PCON_STREAM Stream)
Definition: stream.c:240
unsigned int BOOL
Definition: ntddk_ex.h:94
#define WAIT_OBJECT_0
Definition: winbase.h:387
BOOL WINAPI DECLSPEC_HOTPATCH PeekConsoleInputW(IN HANDLE hConsoleInput, OUT PINPUT_RECORD lpBuffer, IN DWORD nLength, OUT LPDWORD lpNumberOfEventsRead)
Definition: readwrite.c:1218
#define IDS_ERROR_INVALID_HANDLE_VALUE
Definition: resource.h:5
unsigned int UINT
Definition: ndis.h:50
KEY_EVENT_RECORD KeyEvent
Definition: wincon.h:263
INT InputWait(BOOL bNoBreak, INT timerValue)
Definition: timeout.c:47
unsigned long DWORD
Definition: ntddk_ex.h:95
#define EXIT_FAILURE
Definition: jerror.c:33
INT ConMsgPuts(IN PCON_STREAM Stream, IN DWORD dwFlags, IN LPCVOID lpSource OPTIONAL, IN DWORD dwMessageId, IN DWORD dwLanguageId)
Definition: outstream.c:837
#define StdErr
Definition: stream.h:77
#define IDS_NOBREAK_INPUT_COUNT
Definition: resource.h:11
#define WAIT_TIMEOUT
Definition: dderror.h:14
INT ConResPuts(IN PCON_STREAM Stream, IN UINT uID)
Definition: outstream.c:610
static const WCHAR L[]
Definition: oid.c:1250
BOOL WINAPI GetNumberOfConsoleInputEvents(HANDLE hConsoleInput, LPDWORD lpNumberOfEvents)
Definition: console.c:1637
#define IDS_ERROR_READ_INPUT
Definition: resource.h:6
#define IDS_ERROR_OUT_OF_RANGE
Definition: resource.h:4
#define WINAPI
Definition: msvc.h:20
BOOL WINAPI CtrlCIntercept(DWORD dwCtrlType)
Definition: timeout.c:34
Status
Definition: gdiplustypes.h:24
INT ConPuts(IN PCON_STREAM Stream, IN LPWSTR szStr)
Definition: outstream.c:427
#define VK_CONTROL
Definition: winuser.h:2157
#define StdOut
Definition: stream.h:76
PPC_QUAL long long __emul(const int a, const int b)
Definition: intrin_ppc.h:527
unsigned int ULONG
Definition: retypes.h:1
_In_ ACCESS_MASK _In_opt_ POBJECT_ATTRIBUTES _In_ EVENT_TYPE EventType
Definition: exfuncs.h:165
#define CreateWaitableTimer
Definition: winbase.h:3575
#define towupper(c)
Definition: wctype.h:99
#define INFINITE
Definition: serial.h:102
BOOL WINAPI SetWaitableTimer(IN HANDLE hTimer, IN const LARGE_INTEGER *pDueTime, IN LONG lPeriod, IN PTIMERAPCROUTINE pfnCompletionRoutine OPTIONAL, IN OPTIONAL LPVOID lpArgToCompletionRoutine, IN BOOL fResume)
Definition: synch.c:382
union _INPUT_RECORD::@3143 Event
_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
#define StdIn
Definition: stream.h:75
#define LANG_USER_DEFAULT
Definition: tnerror.cpp:50
BOOL WINAPI DECLSPEC_HOTPATCH ReadConsoleInputW(IN HANDLE hConsoleInput, OUT PINPUT_RECORD lpBuffer, IN DWORD nLength, OUT LPDWORD lpNumberOfEventsRead)
Definition: readwrite.c:1258
LONGLONG QuadPart
Definition: typedefs.h:112