|  | /* Host support routines for MinGW, for GDB, the GNU debugger. | 
|  |  | 
|  | Copyright (C) 2006-2021 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 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 General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include "main.h" | 
|  | #include "serial.h" | 
|  | #include "gdbsupport/event-loop.h" | 
|  |  | 
|  | #include "gdbsupport/gdb_select.h" | 
|  |  | 
|  | #include <windows.h> | 
|  |  | 
|  | /* Return an absolute file name of the running GDB, if possible, or | 
|  | ARGV0 if not.  The return value is in malloc'ed storage.  */ | 
|  |  | 
|  | char * | 
|  | windows_get_absolute_argv0 (const char *argv0) | 
|  | { | 
|  | char full_name[PATH_MAX]; | 
|  |  | 
|  | if (GetModuleFileName (NULL, full_name, PATH_MAX)) | 
|  | return xstrdup (full_name); | 
|  | return xstrdup (argv0); | 
|  | } | 
|  |  | 
|  | /* Wrapper for select.  On Windows systems, where the select interface | 
|  | only works for sockets, this uses the GDB serial abstraction to | 
|  | handle sockets, consoles, pipes, and serial ports. | 
|  |  | 
|  | The arguments to this function are the same as the traditional | 
|  | arguments to select on POSIX platforms.  */ | 
|  |  | 
|  | int | 
|  | gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, | 
|  | struct timeval *timeout) | 
|  | { | 
|  | static HANDLE never_handle; | 
|  | HANDLE handles[MAXIMUM_WAIT_OBJECTS]; | 
|  | HANDLE h; | 
|  | DWORD event; | 
|  | DWORD num_handles; | 
|  | /* SCBS contains serial control objects corresponding to file | 
|  | descriptors in READFDS and WRITEFDS.  */ | 
|  | struct serial *scbs[MAXIMUM_WAIT_OBJECTS]; | 
|  | /* The number of valid entries in SCBS.  */ | 
|  | size_t num_scbs; | 
|  | int fd; | 
|  | int num_ready; | 
|  | size_t indx; | 
|  |  | 
|  | if (n == 0) | 
|  | { | 
|  | /* The MS API says that the first argument to | 
|  | WaitForMultipleObjects cannot be zero.  That's why we just | 
|  | use a regular Sleep here.  */ | 
|  | if (timeout != NULL) | 
|  | Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | num_ready = 0; | 
|  | num_handles = 0; | 
|  | num_scbs = 0; | 
|  | for (fd = 0; fd < n; ++fd) | 
|  | { | 
|  | HANDLE read = NULL, except = NULL; | 
|  | struct serial *scb; | 
|  |  | 
|  | /* There is no support yet for WRITEFDS.  At present, this isn't | 
|  | used by GDB -- but we do not want to silently ignore WRITEFDS | 
|  | if something starts using it.  */ | 
|  | gdb_assert (!writefds || !FD_ISSET (fd, writefds)); | 
|  |  | 
|  | if ((!readfds || !FD_ISSET (fd, readfds)) | 
|  | && (!exceptfds || !FD_ISSET (fd, exceptfds))) | 
|  | continue; | 
|  |  | 
|  | scb = serial_for_fd (fd); | 
|  | if (scb) | 
|  | { | 
|  | serial_wait_handle (scb, &read, &except); | 
|  | scbs[num_scbs++] = scb; | 
|  | } | 
|  |  | 
|  | if (read == NULL) | 
|  | read = (HANDLE) _get_osfhandle (fd); | 
|  | if (except == NULL) | 
|  | { | 
|  | if (!never_handle) | 
|  | never_handle = CreateEvent (0, FALSE, FALSE, 0); | 
|  |  | 
|  | except = never_handle; | 
|  | } | 
|  |  | 
|  | if (readfds && FD_ISSET (fd, readfds)) | 
|  | { | 
|  | gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); | 
|  | handles[num_handles++] = read; | 
|  | } | 
|  |  | 
|  | if (exceptfds && FD_ISSET (fd, exceptfds)) | 
|  | { | 
|  | gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); | 
|  | handles[num_handles++] = except; | 
|  | } | 
|  | } | 
|  |  | 
|  | gdb_assert (num_handles <= MAXIMUM_WAIT_OBJECTS); | 
|  |  | 
|  | event = WaitForMultipleObjects (num_handles, | 
|  | handles, | 
|  | FALSE, | 
|  | timeout | 
|  | ? (timeout->tv_sec * 1000 | 
|  | + timeout->tv_usec / 1000) | 
|  | : INFINITE); | 
|  | /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the | 
|  | HANDLES included an abandoned mutex.  Since GDB doesn't use | 
|  | mutexes, that should never occur.  */ | 
|  | gdb_assert (!(WAIT_ABANDONED_0 <= event | 
|  | && event < WAIT_ABANDONED_0 + num_handles)); | 
|  | /* We no longer need the helper threads to check for activity.  */ | 
|  | for (indx = 0; indx < num_scbs; ++indx) | 
|  | serial_done_wait_handle (scbs[indx]); | 
|  | if (event == WAIT_FAILED) | 
|  | return -1; | 
|  | if (event == WAIT_TIMEOUT) | 
|  | return 0; | 
|  | /* Run through the READFDS, clearing bits corresponding to descriptors | 
|  | for which input is unavailable.  */ | 
|  | h = handles[event - WAIT_OBJECT_0]; | 
|  | for (fd = 0, indx = 0; fd < n; ++fd) | 
|  | { | 
|  | HANDLE fd_h; | 
|  |  | 
|  | if ((!readfds || !FD_ISSET (fd, readfds)) | 
|  | && (!exceptfds || !FD_ISSET (fd, exceptfds))) | 
|  | continue; | 
|  |  | 
|  | if (readfds && FD_ISSET (fd, readfds)) | 
|  | { | 
|  | fd_h = handles[indx++]; | 
|  | /* This handle might be ready, even though it wasn't the handle | 
|  | returned by WaitForMultipleObjects.  */ | 
|  | if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) | 
|  | FD_CLR (fd, readfds); | 
|  | else | 
|  | num_ready++; | 
|  | } | 
|  |  | 
|  | if (exceptfds && FD_ISSET (fd, exceptfds)) | 
|  | { | 
|  | fd_h = handles[indx++]; | 
|  | /* This handle might be ready, even though it wasn't the handle | 
|  | returned by WaitForMultipleObjects.  */ | 
|  | if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0) | 
|  | FD_CLR (fd, exceptfds); | 
|  | else | 
|  | num_ready++; | 
|  | } | 
|  | } | 
|  |  | 
|  | return num_ready; | 
|  | } | 
|  |  | 
|  | /* Map COLOR's RGB triplet, with 8 bits per component, into 16 Windows | 
|  | console colors, where each component has just 1 bit, plus a single | 
|  | intensity bit which affects all 3 components.  */ | 
|  | static int | 
|  | rgb_to_16colors (const ui_file_style::color &color) | 
|  | { | 
|  | uint8_t rgb[3]; | 
|  | color.get_rgb (rgb); | 
|  |  | 
|  | int retval = 0; | 
|  | for (int i = 0; i < 3; i++) | 
|  | { | 
|  | /* Subdivide 256 possible values of each RGB component into 3 | 
|  | regions: no color, normal color, bright color.  256 / 3 = 85, | 
|  | but ui-style.c follows xterm and uses 92 for R and G | 
|  | components of the bright-blue color, so we bias the divisor a | 
|  | bit to have the bright colors between 9 and 15 identical to | 
|  | what ui-style.c expects.  */ | 
|  | int bits = rgb[i] / 93; | 
|  | retval |= ((bits > 0) << (2 - i)) | ((bits > 1) << 3); | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* Zero if not yet initialized, 1 if stdout is a console device, else -1.  */ | 
|  | static int mingw_console_initialized; | 
|  |  | 
|  | /* Handle to stdout . */ | 
|  | static HANDLE hstdout = INVALID_HANDLE_VALUE; | 
|  |  | 
|  | /* Text attribute to use for normal text (the "none" pseudo-color).  */ | 
|  | static SHORT  norm_attr; | 
|  |  | 
|  | /* The most recently applied style.  */ | 
|  | static ui_file_style last_style; | 
|  |  | 
|  | /* Alternative for the libc 'fputs' which handles embedded SGR | 
|  | sequences in support of styling.  */ | 
|  |  | 
|  | int | 
|  | gdb_console_fputs (const char *linebuf, FILE *fstream) | 
|  | { | 
|  | if (!mingw_console_initialized) | 
|  | { | 
|  | hstdout = (HANDLE)_get_osfhandle (fileno (fstream)); | 
|  | DWORD cmode; | 
|  | CONSOLE_SCREEN_BUFFER_INFO csbi; | 
|  |  | 
|  | if (hstdout != INVALID_HANDLE_VALUE | 
|  | && GetConsoleMode (hstdout, &cmode) != 0 | 
|  | && GetConsoleScreenBufferInfo (hstdout, &csbi)) | 
|  | { | 
|  | norm_attr = csbi.wAttributes; | 
|  | mingw_console_initialized = 1; | 
|  | } | 
|  | else if (hstdout != INVALID_HANDLE_VALUE) | 
|  | mingw_console_initialized = -1; /* valid, but not a console device */ | 
|  | } | 
|  | /* If our stdout is not a console device, let the default 'fputs' | 
|  | handle the task. */ | 
|  | if (mingw_console_initialized <= 0) | 
|  | return 0; | 
|  |  | 
|  | /* Mapping between 8 ANSI colors and Windows console attributes.  */ | 
|  | static int fg_color[] = { | 
|  | 0,					/* black */ | 
|  | FOREGROUND_RED,			/* red */ | 
|  | FOREGROUND_GREEN,			/* green */ | 
|  | FOREGROUND_GREEN | FOREGROUND_RED,	/* yellow */ | 
|  | FOREGROUND_BLUE,			/* blue */ | 
|  | FOREGROUND_BLUE | FOREGROUND_RED,	/* magenta */ | 
|  | FOREGROUND_BLUE | FOREGROUND_GREEN, /* cyan */ | 
|  | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE /* gray */ | 
|  | }; | 
|  | static int bg_color[] = { | 
|  | 0,					/* black */ | 
|  | BACKGROUND_RED,			/* red */ | 
|  | BACKGROUND_GREEN,			/* green */ | 
|  | BACKGROUND_GREEN | BACKGROUND_RED,	/* yellow */ | 
|  | BACKGROUND_BLUE,			/* blue */ | 
|  | BACKGROUND_BLUE | BACKGROUND_RED,	/* magenta */ | 
|  | BACKGROUND_BLUE | BACKGROUND_GREEN, /* cyan */ | 
|  | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE /* gray */ | 
|  | }; | 
|  |  | 
|  | ui_file_style style = last_style; | 
|  | unsigned char c; | 
|  | size_t n_read; | 
|  |  | 
|  | for ( ; (c = *linebuf) != 0; linebuf += n_read) | 
|  | { | 
|  | if (c == '\033') | 
|  | { | 
|  | fflush (fstream); | 
|  | bool parsed = style.parse (linebuf, &n_read); | 
|  | if (n_read <= 0)	/* should never happen */ | 
|  | n_read = 1; | 
|  | if (!parsed) | 
|  | { | 
|  | /* This means we silently swallow SGR sequences we | 
|  | cannot parse.  */ | 
|  | continue; | 
|  | } | 
|  | /* Colors.  */ | 
|  | const ui_file_style::color &fg = style.get_foreground (); | 
|  | const ui_file_style::color &bg = style.get_background (); | 
|  | int fgcolor, bgcolor, bright, inverse; | 
|  | if (fg.is_none ()) | 
|  | fgcolor = norm_attr & 15; | 
|  | else if (fg.is_basic ()) | 
|  | fgcolor = fg_color[fg.get_value () & 15]; | 
|  | else | 
|  | fgcolor = rgb_to_16colors (fg); | 
|  | if (bg.is_none ()) | 
|  | bgcolor = norm_attr & (15 << 4); | 
|  | else if (bg.is_basic ()) | 
|  | bgcolor = bg_color[bg.get_value () & 15]; | 
|  | else | 
|  | bgcolor = rgb_to_16colors (bg) << 4; | 
|  |  | 
|  | /* Intensity.  */ | 
|  | switch (style.get_intensity ()) | 
|  | { | 
|  | case ui_file_style::NORMAL: | 
|  | case ui_file_style::DIM: | 
|  | bright = 0; | 
|  | break; | 
|  | case ui_file_style::BOLD: | 
|  | bright = 1; | 
|  | break; | 
|  | default: | 
|  | gdb_assert_not_reached ("invalid intensity"); | 
|  | } | 
|  |  | 
|  | /* Inverse video.  */ | 
|  | if (style.is_reverse ()) | 
|  | inverse = 1; | 
|  | else | 
|  | inverse = 0; | 
|  |  | 
|  | /* Construct the attribute.  */ | 
|  | if (inverse) | 
|  | { | 
|  | int t = fgcolor; | 
|  | fgcolor = (bgcolor >> 4); | 
|  | bgcolor = (t << 4); | 
|  | } | 
|  | if (bright) | 
|  | fgcolor |= FOREGROUND_INTENSITY; | 
|  |  | 
|  | SHORT attr = (bgcolor & (15 << 4)) | (fgcolor & 15); | 
|  |  | 
|  | /* Apply the attribute.  */ | 
|  | SetConsoleTextAttribute (hstdout, attr); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* When we are about to write newline, we need to clear to | 
|  | EOL with the normal attribute, to avoid spilling the | 
|  | colors to the next screen line.  We assume here that no | 
|  | non-default attribute extends beyond the newline.  */ | 
|  | if (c == '\n') | 
|  | { | 
|  | DWORD nchars; | 
|  | COORD start_pos; | 
|  | DWORD written; | 
|  | CONSOLE_SCREEN_BUFFER_INFO csbi; | 
|  |  | 
|  | fflush (fstream); | 
|  | GetConsoleScreenBufferInfo (hstdout, &csbi); | 
|  |  | 
|  | if (csbi.wAttributes != norm_attr) | 
|  | { | 
|  | start_pos = csbi.dwCursorPosition; | 
|  | nchars = csbi.dwSize.X - start_pos.X; | 
|  |  | 
|  | FillConsoleOutputAttribute (hstdout, norm_attr, nchars, | 
|  | start_pos, &written); | 
|  | FillConsoleOutputCharacter (hstdout, ' ', nchars, | 
|  | start_pos, &written); | 
|  | } | 
|  | } | 
|  | fputc (c, fstream); | 
|  | n_read = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | last_style = style; | 
|  | return 1; | 
|  | } |