1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-29 19:30:36 +02:00
guile/libguile/posix-w32.c
Michael Gran af96820e07 Windows 11: for fport input from the console, ignore terminal returns
There is an apparent bug in Windows 11 (not Windows 10) where,
when reading from an fd backed by the Console, a single
return character will always be available.

* libguile/posix-w32.c (console_has_return_keyevent_w32): new procedure
* libguile/posix-w32.h: declare console_has_return_keyevent_w32
* libguile/fports.c [__MINGW32__](fport_input_waiting): ignore return keyevent
2025-03-23 10:26:40 -07:00

344 lines
9.1 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright 2001,2006,2008,2016,2018,2021,2025
Free Software Foundation, Inc.
This file is part of Guile.
Guile 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 3 of the License, or
(at your option) any later version.
Guile 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 Guile. If not, see
<https://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <c-strcase.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <io.h>
#include <fcntl.h>
#include "gc.h" /* for scm_*alloc, scm_strdup */
#include "threads.h" /* for scm_i_scm_pthread_mutex_lock */
#include "posix-w32.h"
/*
* Get name and information about current kernel.
*/
int
uname (struct utsname *uts)
{
enum { WinNT, Win95, Win98, WinUnknown };
OSVERSIONINFO osver;
SYSTEM_INFO sysinfo;
DWORD sLength;
DWORD os = WinUnknown;
memset (uts, 0, sizeof (*uts));
osver.dwOSVersionInfoSize = sizeof (osver);
GetVersionEx (&osver);
GetSystemInfo (&sysinfo);
switch (osver.dwPlatformId)
{
case VER_PLATFORM_WIN32_NT: /* NT, Windows 2000 or Windows XP */
if (osver.dwMajorVersion == 4)
strcpy (uts->sysname, "Windows NT4x"); /* NT4x */
else if (osver.dwMajorVersion <= 3)
strcpy (uts->sysname, "Windows NT3x"); /* NT3x */
else if (osver.dwMajorVersion == 5 && osver.dwMinorVersion < 1)
strcpy (uts->sysname, "Windows 2000"); /* 2k */
else if (osver.dwMajorVersion < 6)
strcpy (uts->sysname, "Windows XP"); /* XP */
else if (osver.dwMajorVersion == 6)
{
if (osver.dwMinorVersion < 1)
strcpy (uts->sysname, "Windows Vista"); /* Vista */
else if (osver.dwMinorVersion < 2)
strcpy (uts->sysname, "Windows 7"); /* Windows 7 */
else if (osver.dwMinorVersion < 3)
strcpy (uts->sysname, "Windows 8"); /* Windows 8 */
else if (osver.dwMinorVersion < 4)
strcpy (uts->sysname, "Windows 8.1"); /* Windows 8.1 */
}
else if (osver.dwMajorVersion >= 10)
strcpy (uts->sysname, "Windows 10 or later"); /* Windows 10 and later */
os = WinNT;
break;
case VER_PLATFORM_WIN32_WINDOWS: /* Win95, Win98 or WinME */
if ((osver.dwMajorVersion > 4) ||
((osver.dwMajorVersion == 4) && (osver.dwMinorVersion > 0)))
{
if (osver.dwMinorVersion >= 90)
strcpy (uts->sysname, "Windows ME"); /* ME */
else
strcpy (uts->sysname, "Windows 98"); /* 98 */
os = Win98;
}
else
{
strcpy (uts->sysname, "Windows 95"); /* 95 */
os = Win95;
}
break;
case VER_PLATFORM_WIN32s: /* Windows 3.x */
strcpy (uts->sysname, "Windows");
break;
}
sprintf (uts->version, "%ld.%02ld",
osver.dwMajorVersion, osver.dwMinorVersion);
if (osver.szCSDVersion[0] != '\0' &&
(strlen (osver.szCSDVersion) + strlen (uts->version) + 1) <
sizeof (uts->version))
{
strcat (uts->version, " ");
strcat (uts->version, osver.szCSDVersion);
}
sprintf (uts->release, "build %ld", osver.dwBuildNumber & 0xFFFF);
switch (sysinfo.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_PPC:
strcpy (uts->machine, "ppc");
break;
case PROCESSOR_ARCHITECTURE_ALPHA:
strcpy (uts->machine, "alpha");
break;
case PROCESSOR_ARCHITECTURE_MIPS:
strcpy (uts->machine, "mips");
break;
case PROCESSOR_ARCHITECTURE_IA64:
strcpy (uts->machine, "ia64");
break;
case PROCESSOR_ARCHITECTURE_INTEL:
/*
* dwProcessorType is only valid in Win95 and Win98 and WinME
* wProcessorLevel is only valid in WinNT
*/
switch (os)
{
case Win95:
case Win98:
switch (sysinfo.dwProcessorType)
{
case PROCESSOR_INTEL_386:
case PROCESSOR_INTEL_486:
case PROCESSOR_INTEL_PENTIUM:
sprintf (uts->machine, "i%ld", sysinfo.dwProcessorType);
break;
default:
strcpy (uts->machine, "i386");
break;
}
break;
case WinNT:
sprintf (uts->machine, "i%d86", sysinfo.wProcessorLevel);
break;
default:
strcpy (uts->machine, "unknown");
break;
}
break;
case PROCESSOR_ARCHITECTURE_AMD64:
strcpy (uts->machine, "x86_64");
break;
default:
strcpy (uts->machine, "unknown");
break;
}
sLength = sizeof (uts->nodename) - 1;
GetComputerName (uts->nodename, &sLength);
return 0;
}
/* Translate abnormal exit status of Windows programs into the signal
that terminated the program. This is required to support scm_kill
and WTERMSIG. */
struct signal_and_status {
int sig;
DWORD status;
};
static const struct signal_and_status sigtbl[] = {
{SIGSEGV, 0xC0000005}, /* access to invalid address */
{SIGSEGV, 0xC0000008}, /* invalid handle */
{SIGILL, 0xC000001D}, /* illegal instruction */
{SIGILL, 0xC0000025}, /* non-continuable instruction */
{SIGSEGV, 0xC000008C}, /* array bounds exceeded */
{SIGFPE, 0xC000008D}, /* float denormal */
{SIGFPE, 0xC000008E}, /* float divide by zero */
{SIGFPE, 0xC000008F}, /* float inexact */
{SIGFPE, 0xC0000090}, /* float invalid operation */
{SIGFPE, 0xC0000091}, /* float overflow */
{SIGFPE, 0xC0000092}, /* float stack check */
{SIGFPE, 0xC0000093}, /* float underflow */
{SIGFPE, 0xC0000094}, /* integer divide by zero */
{SIGFPE, 0xC0000095}, /* integer overflow */
{SIGILL, 0xC0000096}, /* privileged instruction */
{SIGSEGV, 0xC00000FD}, /* stack overflow */
{SIGTERM, 0xC000013A}, /* Ctrl-C exit */
{SIGINT, 0xC000013A}
};
static int
w32_signal_to_status (int sig)
{
int i;
for (i = 0; i < sizeof (sigtbl) / sizeof (sigtbl[0]); i++)
if (sig == sigtbl[i].sig)
return sigtbl[i].status;
return (int)0xC000013A;
}
int
waitpid (pid_t pid, int *status, int options)
{
/* Not supported on MS-Windows. */
if (pid <= 0 || options != 0)
{
errno = ENOSYS;
return -1;
}
return _cwait (status, pid, WAIT_CHILD);
}
int
w32_status_to_termsig (DWORD status)
{
int i;
for (i = 0; i < sizeof (sigtbl) / sizeof (sigtbl[0]); i++)
if (status == sigtbl[i].status)
return sigtbl[i].sig;
return SIGTERM;
}
/* This only implements the absolute minimum features for
foreign-library.scm. */
void *
dlopen_w32 (const char *name, int flags)
{
void *ret = NULL;
if (name == NULL || *name == '\0')
return (void *) GetModuleHandle (NULL);
ret = (void *) LoadLibrary (name);
GetModuleHandleEx (0, name, (HMODULE *) & ret);
return ret;
}
void *
dlsym_w32 (void *handle, const char *name)
{
return (void *) GetProcAddress ((HMODULE) handle, name);
}
int
dlclose_w32 (void *handle)
{
FreeLibrary ((HMODULE) handle);
return 0;
}
#define DLERROR_LEN 80
static char dlerror_str[DLERROR_LEN + 1];
char *
dlerror_w32 ()
{
char *msg_buf;
DWORD dw = GetLastError ();
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) & msg_buf, 0, NULL);
if (dw == 0)
snprintf (dlerror_str, DLERROR_LEN, "No error");
else
snprintf (dlerror_str, DLERROR_LEN, "error %ld: %s", (long) dw, msg_buf);
return dlerror_str;
}
/* Check for the Windows 11 bug where there's a return character in the
* console input queue despite draining the input. */
int
console_has_return_keyevent_w32 (int fdes)
{
HANDLE h;
DWORD mode;
h = (HANDLE) _get_osfhandle (fdes);
if (h == -1)
return 0;
if (GetConsoleMode (h, &mode) == 0)
return 0;
// Rarely need more than 1 INPUT_RECORD for this test, but just in
// case there is a mouse event in the queue.
#define NBUFFER 8
INPUT_RECORD irbuffer[NBUFFER];
BOOL bRet;
DWORD avail;
int i;
int n_chars = 0;
int n_returns = 0;
while (1)
{
bRet = PeekConsoleInput (h, irbuffer, NBUFFER, &avail);
if (!bRet || avail == 0)
break;
for (i = 0; i < avail; i++)
if (irbuffer[i].EventType == KEY_EVENT)
{
n_chars ++;
if (irbuffer[i].Event.KeyEvent.uChar.AsciiChar == 13)
n_returns ++;
}
if (avail < NBUFFER)
break;
}
if (n_chars == 1 && n_returns == 1)
return 1;
return 0;
#undef NBUFFER
}
int
getpagesize_w32 (void)
{
return 4 * 1024;
}