Windows 10 and 11 introduce the timeout.exe command. This is a similar program with same argument options
-- v22: timeout: add minimal test suite timeout: Windows 10 introduce the timeout command
From: Michele Dionisio michele.dionisio@powersoft.com
--- configure.ac | 1 + programs/timeout/Makefile.in | 8 ++ programs/timeout/main.c | 188 +++++++++++++++++++++++++++++++++++ programs/timeout/timeout.h | 28 ++++++ programs/timeout/timeout.rc | 33 ++++++ 5 files changed, 258 insertions(+) create mode 100644 programs/timeout/Makefile.in create mode 100644 programs/timeout/main.c create mode 100644 programs/timeout/timeout.h create mode 100644 programs/timeout/timeout.rc
diff --git a/configure.ac b/configure.ac index 3f8be71bd21..0f2bfd93b9b 100644 --- a/configure.ac +++ b/configure.ac @@ -3514,6 +3514,7 @@ WINE_CONFIG_MAKEFILE(programs/tasklist) WINE_CONFIG_MAKEFILE(programs/tasklist/tests) WINE_CONFIG_MAKEFILE(programs/taskmgr) WINE_CONFIG_MAKEFILE(programs/termsv) +WINE_CONFIG_MAKEFILE(programs/timeout) WINE_CONFIG_MAKEFILE(programs/uninstaller) WINE_CONFIG_MAKEFILE(programs/unlodctr) WINE_CONFIG_MAKEFILE(programs/view) diff --git a/programs/timeout/Makefile.in b/programs/timeout/Makefile.in new file mode 100644 index 00000000000..708cca355ff --- /dev/null +++ b/programs/timeout/Makefile.in @@ -0,0 +1,8 @@ +MODULE = timeout.exe +IMPORTS = user32 + +EXTRADLLFLAGS = -mconsole -municode + +SOURCES = \ + timeout.rc \ + main.c diff --git a/programs/timeout/main.c b/programs/timeout/main.c new file mode 100644 index 00000000000..4504b12b3b0 --- /dev/null +++ b/programs/timeout/main.c @@ -0,0 +1,188 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <synchapi.h> +#include <stdbool.h> +#include <wchar.h> +#include <conio.h> +#include "timeout.h" + +static int WINAPIV timeout_error_wprintf(int msg, ...) +{ + WCHAR msg_buffer[MAXSTRING]; + va_list va_args; + int ret; + + LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer)); + va_start(va_args, msg); + ret = vfwprintf(stderr, msg_buffer, va_args); + va_end(va_args); + return ret; +} + +static int WINAPIV timeout_message(int msg) +{ + WCHAR msg_buffer[MAXSTRING]; + + LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer)); + return fwprintf(stdout, msg_buffer); +} + +static int WINAPIV timeout_wprintf(int msg, ...) +{ + WCHAR msg_buffer[MAXSTRING]; + va_list va_args; + int ret; + + LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer)); + va_start(va_args, msg); + ret = vfwprintf(stdout, msg_buffer, va_args); + va_end(va_args); + return ret; +} + +static volatile bool stop = false; + +static BOOL WINAPI ctrl_c_handler(DWORD dwCtrlType) +{ + if (dwCtrlType == CTRL_C_EVENT) + { + stop = true; + return TRUE; + } + return FALSE; +} + +static BOOL WINAPI parseTime(const WCHAR *in, int *out) +{ + WCHAR *endptr = NULL; + long l_wait_time; + l_wait_time = wcstol(in, &endptr, 10); + if (*endptr != L'\0') { + return FALSE; + } + if ((l_wait_time < -1) || (l_wait_time > 99999)) + { + return FALSE; + } + *out = (int)l_wait_time; + return TRUE; +} + +int __cdecl wmain(int argc, WCHAR *argv[]) +{ + int wait_time = 0; + int wait_time_valid = 0; + int nobreak = 0; + int i; + + if (argc <= 1) + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + + for (int i = 1; i < argc; i++) + { + if (wcscmp(argv[i], L"/?") == 0) + { + timeout_message(STRING_USAGE); + return 0; + } + else if ((wcsicmp(argv[i], L"/t") == 0) && (wait_time_valid == 0)) + { + if ((i + 1) < argc) + { + i++; + if (!parseTime(argv[i], &wait_time)) + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + wait_time_valid = 1; + } + else + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + } + else if (wcsicmp(argv[i], L"/nobreak") == 0) + { + nobreak = 1; + } + else if (wait_time_valid == 0) + { + if (!parseTime(argv[i], &wait_time)) + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + wait_time_valid = 1; + } + else + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + } + + if (wait_time_valid == 0) + { + timeout_error_wprintf(STRING_BAD_COMMAND_LINE); + return 1; + } + + if (!VerifyConsoleIoHandle(GetStdHandle(STD_INPUT_HANDLE))) + { + timeout_error_wprintf(STRING_ERROR_REDIRECT); + return 1; + } + + if (nobreak) + { + SetConsoleCtrlHandler(ctrl_c_handler, TRUE); + } + + for (i = 0; (wait_time < 0) || (i < wait_time); i++) + { + timeout_wprintf(STRING_WAITING_SINCE, i); + timeout_message((nobreak == 0) ? STRING_PRESS_KEY : STRING_PRESS_CRTLC); + if (stop) + { + break; + } + if ((nobreak == 0) && _kbhit()) + { + break; + } + Sleep(1000); + } + putc('\n', stdout); + + if (stop) + { + return STATUS_CONTROL_C_EXIT; + } + + return 0; +} diff --git a/programs/timeout/timeout.h b/programs/timeout/timeout.h new file mode 100644 index 00000000000..94320bd26c9 --- /dev/null +++ b/programs/timeout/timeout.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windef.h> + +#define MAXSTRING 8192 + +#define STRING_USAGE 101 +#define STRING_BAD_COMMAND_LINE 102 +#define STRING_WAITING_SINCE 103 +#define STRING_PRESS_CRTLC 104 +#define STRING_PRESS_KEY 105 +#define STRING_ERROR_REDIRECT 106 diff --git a/programs/timeout/timeout.rc b/programs/timeout/timeout.rc new file mode 100644 index 00000000000..bafb961c6c3 --- /dev/null +++ b/programs/timeout/timeout.rc @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "timeout.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ + STRING_USAGE, "Usage: timeout /T <timeout> [</NOBREAK>]\n" + STRING_BAD_COMMAND_LINE, "TIMEOUT: Bad command line\n" + STRING_WAITING_SINCE, "waiting since %d sec. " + STRING_PRESS_CRTLC, "press CTRL+C...\r" + STRING_PRESS_KEY, "press a key to continue...\r" + STRING_ERROR_REDIRECT, "ERROR: not possible to redirect input\n" +}
From: Michele Dionisio michele.dionisio@powersoft.com
--- configure.ac | 1 + programs/timeout/tests/Makefile.in | 4 + programs/timeout/tests/timeout.c | 146 +++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 programs/timeout/tests/Makefile.in create mode 100644 programs/timeout/tests/timeout.c
diff --git a/configure.ac b/configure.ac index 0f2bfd93b9b..5955eb89c78 100644 --- a/configure.ac +++ b/configure.ac @@ -3515,6 +3515,7 @@ WINE_CONFIG_MAKEFILE(programs/tasklist/tests) WINE_CONFIG_MAKEFILE(programs/taskmgr) WINE_CONFIG_MAKEFILE(programs/termsv) WINE_CONFIG_MAKEFILE(programs/timeout) +WINE_CONFIG_MAKEFILE(programs/timeout/tests) WINE_CONFIG_MAKEFILE(programs/uninstaller) WINE_CONFIG_MAKEFILE(programs/unlodctr) WINE_CONFIG_MAKEFILE(programs/view) diff --git a/programs/timeout/tests/Makefile.in b/programs/timeout/tests/Makefile.in new file mode 100644 index 00000000000..a50cdee3062 --- /dev/null +++ b/programs/timeout/tests/Makefile.in @@ -0,0 +1,4 @@ +TESTDLL = timeout.exe + +SOURCES = \ + timeout.c diff --git a/programs/timeout/tests/timeout.c b/programs/timeout/tests/timeout.c new file mode 100644 index 00000000000..11e9814d189 --- /dev/null +++ b/programs/timeout/tests/timeout.c @@ -0,0 +1,146 @@ +/* + * Copyright 2024 Michele Dionisio michele.dionisio@gmail.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include "wine/test.h" + +#define run_timeout_stdin(a, b) _run_timeout_stdin(__FILE__, __LINE__, a, b) +static void _run_timeout_stdin(const char *file, int line, const char *commandline, int exitcode_expected) +{ + PROCESS_INFORMATION process_info = {0}; + STARTUPINFOA startup_info; + char cmd[4096]; + DWORD exitcode; + + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = NULL; + startup_info.hStdError = NULL; + + sprintf(cmd, "timeout.exe %s", commandline); + + trace("Running %s\n", cmd); + + CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info); + + WaitForSingleObject(process_info.hProcess, INFINITE); + GetExitCodeProcess(process_info.hProcess, &exitcode); + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %d, got %ld\n", + exitcode_expected, exitcode); +} + +static void test_basic(void) +{ + /* No options */ + run_timeout_stdin("", 1); + + /* /? */ + run_timeout_stdin("/?", 0); + + if (VerifyConsoleIoHandle(GetStdHandle(STD_INPUT_HANDLE))) + { + /* /T 1 /NOBREAK */ + run_timeout_stdin("/T 1 /NOBREAK", 0); + + /* /T 1 */ + run_timeout_stdin("/T 1", 0); + + /* 1 /NOBREAK */ + run_timeout_stdin("1 /NOBREAK", 0); + } + + /* /T 1ab /NOBREAK */ + run_timeout_stdin("/T 1ab /NOBREAK", 1); + + /* /T -3 /NOBREAK */ + run_timeout_stdin("/T -3 /NOBREAK", 1); + + /* /T 10000000 /NOBREAK */ + run_timeout_stdin("/T 10000000 /NOBREAK", 1); +} + +#define run_timeout_ctrlc(a, b) _run_timeout_ctrlc(__FILE__, __LINE__, a, b) +static void _run_timeout_ctrlc(const char *file, int line, const char *option, DWORD exitcode_expected) +{ + PROCESS_INFORMATION process_info = {0}; + STARTUPINFOA startup_info; + char cmd[4096]; + DWORD status, exitcode; + DWORD64 tick_count; + BOOL ret; + + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = NULL; + startup_info.hStdError = NULL; + + SetConsoleCtrlHandler(NULL, FALSE); + sprintf(cmd, "timeout.exe /T 8 %s", option); + ret = CreateProcessA(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &startup_info, &process_info); + ok(ret, "CreateProcessA failed: %lu\n", GetLastError()); + + /* wait for process to be started */ + status = WaitForSingleObject(process_info.hProcess, 2000); + ok_(file, line)(status == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx (expecting WAIT_TIMEOUT)\n", status); + + tick_count = GetTickCount64(); + + SetConsoleCtrlHandler(NULL, TRUE); + ret = GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + ok_(file, line)(ret, "GenerateConsoleCtrlEvent failed: %lu\n", GetLastError()); + + status = WaitForSingleObject(process_info.hProcess, INFINITE); + ok_(file, line)(status == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx (expecting WAIT_OBJECT_0)\n", status); + + tick_count = GetTickCount64() - tick_count; + ok_(file, line)(tick_count < 2000, "Process has not been stopped by ctrl-c\n"); + + ret = GetExitCodeProcess(process_info.hProcess, &exitcode); + ok_(file, line)(ret, "GetExitCodeProcess failed\n"); + + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); + ok_(file, line)(exitcode == exitcode_expected, "Expected exitcode %ld, got %lx\n", + exitcode_expected, exitcode); +} + +static void test_ctrlc(void) +{ + if (VerifyConsoleIoHandle(GetStdHandle(STD_INPUT_HANDLE))) + { + /* don't temper with winetest (and its parent) */ + FreeConsole(); + AllocConsole(); + + run_timeout_ctrlc("", STATUS_CONTROL_C_EXIT); + run_timeout_ctrlc("/nobreak", 1); + } +} + +START_TEST(timeout) +{ + test_basic(); + /* must be last in list (changes current console) */ + test_ctrlc(); +}
On Mon Nov 25 09:59:11 2024 +0000, Michele Dionisio wrote:
changed this line in [version 22 of the diff](/wine/wine/-/merge_requests/6869/diffs?diff_id=145234&start_sha=259df1a5add58c594582bd55fa8235bdd2509ede#8c3e8649852b7e3812c8229951f288cd6783502f_159_166)
done