From: Myah Caron qsniyg@protonmail.com
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=56068 Signed-off-by: Myah Caron qsniyg@protonmail.com --- configure.ac | 1 + programs/timeout/Makefile.in | 8 + programs/timeout/resources.h | 34 ++++ programs/timeout/timeout.c | 327 +++++++++++++++++++++++++++++++++++ programs/timeout/timeout.rc | 57 ++++++ 5 files changed, 427 insertions(+) create mode 100644 programs/timeout/Makefile.in create mode 100644 programs/timeout/resources.h create mode 100644 programs/timeout/timeout.c create mode 100644 programs/timeout/timeout.rc
diff --git a/configure.ac b/configure.ac index 3f54230b46b..3ae541232ca 100644 --- a/configure.ac +++ b/configure.ac @@ -3523,6 +3523,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..84afc8537d8 --- /dev/null +++ b/programs/timeout/Makefile.in @@ -0,0 +1,8 @@ +MODULE = timeout.exe +IMPORTS = user32 + +EXTRADLLFLAGS = -mconsole + +SOURCES = \ + timeout.c\ + timeout.rc diff --git a/programs/timeout/resources.h b/programs/timeout/resources.h new file mode 100644 index 00000000000..7c1b58410c8 --- /dev/null +++ b/programs/timeout/resources.h @@ -0,0 +1,34 @@ +/* + * Resource id definitions + * + * Copyright (C) 2024 Myah Caron + * + * This program 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 program 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 program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windef.h> + +#define STRING_USAGE 101 +#define STRING_INFWAIT_PRESS_ANY_KEY 102 +#define STRING_INFWAIT_PRESS_CTRL_C 103 +#define STRING_WAITING_FOR_SECONDS 104 +#define STRING_WAIT_PRESS_KEY 105 +#define STRING_WAIT_PRESS_CTRL_C 106 +#define STRING_TIMEOUT_ONLY_ONCE 107 +#define STRING_INVALID_TIMEOUT 108 +#define STRING_MISSING_VALUE_FOR_OPTION 109 +#define STRING_INVALID_ARGUMENT 110 +#define STRING_TIMEOUT_RANGE 111 +#define STRING_NO_INPUT_REDIRECTION 112 diff --git a/programs/timeout/timeout.c b/programs/timeout/timeout.c new file mode 100644 index 00000000000..cd734bfd465 --- /dev/null +++ b/programs/timeout/timeout.c @@ -0,0 +1,327 @@ +/* + * timeout program + * + * Copyright (C) 2024 Myah Caron + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <conio.h> +#include <math.h> + +#include <windows.h> + +#include "resources.h" + +#include <wine/debug.h> + +WINE_DEFAULT_DEBUG_CHANNEL(timeout); + +static char* get_string(int which) +{ + char* msg; + int len; + WCHAR wmsg[2048]; + + if (!LoadStringW(GetModuleHandleW(NULL), which, wmsg, ARRAY_SIZE(wmsg))) + { + WINE_ERR("LoadString failed for %d, error %ld\n", which, GetLastError()); + return NULL; + } + + len = WideCharToMultiByte(GetOEMCP(), 0, wmsg, -1, NULL, 0, NULL, NULL); + msg = malloc(len); + if (!msg) + return NULL; + + WideCharToMultiByte(GetOEMCP(), 0, wmsg, -1, msg, len, NULL, NULL); + + return msg; +} + +#define RPRINTF(id) do\ + {\ + char* msg = get_string(id);\ + if (!msg)\ + break;\ + printf(msg);\ + free(msg);\ + } while (0) + +#define RPRINTF_VA(id, ...) do\ + {\ + char* msg = get_string(id);\ + if (!msg)\ + break;\ + printf(msg, __VA_ARGS__);\ + free(msg);\ + } while (0) + +static void usage(void) +{ + RPRINTF(STRING_USAGE); +} + +static BOOL is_piped(void) +{ + DWORD count = 0; + return !GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &count); +} + +static BOOL is_key_hit(void) +{ + INPUT_RECORD *records = NULL; + DWORD count = 0, i; + BOOL retval = FALSE; + + if (!GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &count) || !count) + return FALSE; + + records = malloc(count * sizeof(INPUT_RECORD)); + if (!records) + return FALSE; + + if (!ReadConsoleInputA(GetStdHandle(STD_INPUT_HANDLE), records, count, &count)) + goto cleanup; + + for (i = 0; i < count; i++) + { + if (records[i].EventType == KEY_EVENT && + records[i].Event.KeyEvent.bKeyDown) + { + retval = TRUE; + break; + } + } + +cleanup: + free(records); + return retval; +} + +static BOOL wait_for_keypress(DWORD timeout_millis) +{ + return WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), timeout_millis) == WAIT_OBJECT_0 && is_key_hit(); +} + +static void sleep_without_timeout(BOOL nobreak) +{ + if (!nobreak) + { + RPRINTF(STRING_INFWAIT_PRESS_ANY_KEY); + + for (;;) + { + if (wait_for_keypress(INFINITE)) + break; + } + } + else + { + RPRINTF(STRING_INFWAIT_PRESS_CTRL_C); + + for (;;) + { + Sleep(10000); + } + } +} + +static int int_strlen(int number) +{ + if (number == 0) + return 1; + if (number < 0) + return int_strlen(-number) + 1; + + return (int)log10((float) number) + 1; +} + +static void pad_number(char* out, int number, int pad) +{ + int i, digits = int_strlen(number); + + for (i = 0; i < pad - digits; i++) + { + out[i] = ' '; + } + + sprintf(out + i, "%d", number); +} + +static void sleep_with_timeout(int timeout, BOOL nobreak) +{ + ULONGLONG start_ticks = GetTickCount64(); + ULONGLONG end_ticks = start_ticks + (timeout * 1000); + LONGLONG ticks_remaining, wait_millis; + int digits = int_strlen(timeout); + char padded_timeout[10]; + + pad_number(padded_timeout, timeout, digits); + RPRINTF_VA(STRING_WAITING_FOR_SECONDS, padded_timeout); + + if (!nobreak) + RPRINTF(STRING_WAIT_PRESS_KEY); + else + RPRINTF(STRING_WAIT_PRESS_CTRL_C); + + do + { + ticks_remaining = end_ticks - GetTickCount64(); + + wait_millis = ticks_remaining; + if (wait_millis > 1000) + wait_millis = 1000; + + printf("\r"); + pad_number(padded_timeout, (int)((ticks_remaining + 500) / 1000), digits); + RPRINTF_VA(STRING_WAITING_FOR_SECONDS, padded_timeout); + + if (!nobreak) + { + if (wait_millis > 0 && wait_for_keypress(wait_millis)) + break; + } + else + { + if (wait_millis > 0) + Sleep(wait_millis); + } + } while (ticks_remaining > 0); +} + +static BOOL str_is_number(char* str) +{ + if (str[0] == '-') + str++; + + while (*str) + { + if (!isdigit(*str++)) + return FALSE; + } + + return TRUE; +} + +static BOOL check_timeout_arg(char* str, BOOL timeout_set) +{ + if (timeout_set) + { + RPRINTF(STRING_TIMEOUT_ONLY_ONCE); + return FALSE; + } + + if (!str_is_number(str)) + { + RPRINTF_VA(STRING_INVALID_TIMEOUT, str); + return FALSE; + } + + return TRUE; +} + +int __cdecl main(int argc, char** argv) +{ + int i, timeout = 0; + BOOL timeout_set = FALSE, nobreak = FALSE; + + if (argc == 1) + { + usage(); + return 1; + } + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-' || argv[i][0] == '/') + { + if (!strcmp(argv[i] + 1, "t")) + { + if (i == argc - 1) + { + RPRINTF_VA(STRING_MISSING_VALUE_FOR_OPTION, argv[i]); + exit(1); + } + + if (!check_timeout_arg(argv[++i], timeout_set)) + return 1; + + timeout = atoi(argv[i]); + timeout_set = TRUE; + } + else if (!strcmp(argv[i] + 1, "nobreak")) + { + nobreak = TRUE; + } + else if (!strcmp(argv[i] + 1, "?")) + { + usage(); + return 0; + } + else if (!strcmp(argv[i], "-1")) + { + timeout = -1; + timeout_set = TRUE; + } + else + { + RPRINTF_VA(STRING_INVALID_ARGUMENT, argv[i]); + usage(); + return 1; + } + } + else + { + if (!check_timeout_arg(argv[i], timeout_set)) + return 1; + + timeout = atoi(argv[i]); + timeout_set = TRUE; + } + } + + if (!timeout_set) + { + usage(); + return 1; + } + + if (timeout < -1 || timeout > 99999) + { + RPRINTF(STRING_TIMEOUT_RANGE); + return 1; + } + + if (is_piped()) + { + RPRINTF(STRING_NO_INPUT_REDIRECTION); + return 1; + } + + printf("\n"); + + if (timeout < 0) + sleep_without_timeout(nobreak); + else + sleep_with_timeout(timeout, nobreak); + + printf("\n"); + + return 0; +} diff --git a/programs/timeout/timeout.rc b/programs/timeout/timeout.rc new file mode 100644 index 00000000000..9b7852fbece --- /dev/null +++ b/programs/timeout/timeout.rc @@ -0,0 +1,57 @@ +/* + * timeout program + * + * Copyright (C) 2024 Myah Caron + * + * 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 "resources.h" + +#pragma makedep po + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE +{ +STRING_USAGE, "Usage: timeout [-t] seconds [-nobreak]\n\n\ +Options:\n\ +\ -t Timeout in seconds to wait.\n\ +\ -nobreak Ignore key presses.\n\ +\ -? Display this help message.\n" + +STRING_INFWAIT_PRESS_ANY_KEY, "Press any key to continue ..." + +STRING_INFWAIT_PRESS_CTRL_C, "Press CTRL+C to quit ..." + +STRING_WAITING_FOR_SECONDS, "Waiting for %s seconds, " + +STRING_WAIT_PRESS_KEY, "press a key to continue ..." + +STRING_WAIT_PRESS_CTRL_C, "press CTRL+C to quit ..." + +STRING_TIMEOUT_ONLY_ONCE, "Timeout value cannot be specified more than once.\n" + +STRING_INVALID_TIMEOUT, "Invalid timeout: %s\n" + +STRING_MISSING_VALUE_FOR_OPTION, "Missing value for option %s\n" + +STRING_INVALID_ARGUMENT, "Invalid argument: %s\n" + +STRING_TIMEOUT_RANGE, "Timeout value out of range. Valid range is -1 to 99999.\n" + +STRING_NO_INPUT_REDIRECTION "Input redirection is not supported.\n" + +}