LCOV - code coverage report
Current view: top level - glib - gwin32.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 46.9 % 593 278
Test Date: 2026-08-11 05:12:37 Functions: 64.5 % 31 20
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* GLIB - Library of useful routines for C programming
       2                 :             :  * Copyright (C) 1995-1998  Peter Mattis, Spencer Kimball and Josh MacDonald
       3                 :             :  * Copyright (C) 1998-1999  Tor Lillqvist
       4                 :             :  *
       5                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :             :  *
       7                 :             :  * This library is free software; you can redistribute it and/or
       8                 :             :  * modify it under the terms of the GNU Lesser General Public
       9                 :             :  * License as published by the Free Software Foundation; either
      10                 :             :  * version 2.1 of the License, or (at your option) any later version.
      11                 :             :  *
      12                 :             :  * This library is distributed in the hope that it will be useful,
      13                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :             :  * Lesser General Public License for more details.
      16                 :             :  *
      17                 :             :  * You should have received a copy of the GNU Lesser General Public
      18                 :             :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19                 :             :  */
      20                 :             : 
      21                 :             : /*
      22                 :             :  * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
      23                 :             :  * file for a list of people on the GLib Team.  See the ChangeLog
      24                 :             :  * files for a list of changes.  These files are distributed with
      25                 :             :  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
      26                 :             :  */
      27                 :             : 
      28                 :             : /* 
      29                 :             :  * MT safe for the unix part, FIXME: make the win32 part MT safe as well.
      30                 :             :  */
      31                 :             : 
      32                 :             : #include "config.h"
      33                 :             : 
      34                 :             : #include "glibconfig.h"
      35                 :             : 
      36                 :             : #include <glib/gstdio.h>
      37                 :             : #include <stdlib.h>
      38                 :             : #include <stdio.h>
      39                 :             : #include <string.h>
      40                 :             : #include <wchar.h>
      41                 :             : #include <errno.h>
      42                 :             : #include <fcntl.h>
      43                 :             : 
      44                 :             : #include <winsock2.h>
      45                 :             : #include <windows.h>
      46                 :             : #ifndef G_WITH_CYGWIN
      47                 :             : #include <direct.h>
      48                 :             : #endif
      49                 :             : #include <errno.h>
      50                 :             : #include <ctype.h>
      51                 :             : #if defined(_MSC_VER) || defined(__DMC__)
      52                 :             : #  include <io.h>
      53                 :             : #endif /* _MSC_VER || __DMC__ */
      54                 :             : 
      55                 :             : #define MODERN_API_FAMILY 2
      56                 :             : 
      57                 :             : #if WINAPI_FAMILY == MODERN_API_FAMILY
      58                 :             : /* This is for modern UI Builds, where we can't use LoadLibraryW()/GetProcAddress() */
      59                 :             : /* ntddk.h is found in the WDK, and MinGW */
      60                 :             : #include <ntddk.h>
      61                 :             : 
      62                 :             : #ifdef _MSC_VER
      63                 :             : #pragma comment (lib, "ntoskrnl.lib")
      64                 :             : #endif
      65                 :             : #elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
      66                 :             : /* mingw-w64 must use winternl.h, but not MinGW */
      67                 :             : #include <ntdef.h>
      68                 :             : #else
      69                 :             : #include <winternl.h>
      70                 :             : #endif
      71                 :             : 
      72                 :             : #include "glib.h"
      73                 :             : #include "gwin32.h"
      74                 :             : #include "gwin32private.h"
      75                 :             : #include "gthreadprivate.h"
      76                 :             : #include "gwin32private.h"
      77                 :             : #include "glib-init.h"
      78                 :             : 
      79                 :             : #ifdef G_WITH_CYGWIN
      80                 :             : #include <sys/cygwin.h>
      81                 :             : #endif
      82                 :             : 
      83                 :             : #ifndef G_WITH_CYGWIN
      84                 :             : 
      85                 :             : gint
      86                 :          28 : g_win32_ftruncate (gint  fd,
      87                 :             :                    guint size)
      88                 :             : {
      89                 :          28 :   return _chsize (fd, size);
      90                 :             : }
      91                 :             : 
      92                 :             : #endif
      93                 :             : 
      94                 :             : /**
      95                 :             :  * g_win32_getlocale:
      96                 :             :  *
      97                 :             :  * The setlocale() function in the Microsoft C library uses locale
      98                 :             :  * names of the form "English_United States.1252" etc. We want the
      99                 :             :  * UNIXish standard form "en_US", "zh_TW" etc. This function gets the
     100                 :             :  * current thread locale from Windows - without any encoding info -
     101                 :             :  * and returns it as a string of the above form for use in forming
     102                 :             :  * file names etc. The returned string should be deallocated with
     103                 :             :  * g_free().
     104                 :             :  *
     105                 :             :  * Returns: newly-allocated locale name.
     106                 :             :  **/
     107                 :             : 
     108                 :             : #ifndef SUBLANG_SERBIAN_LATIN_BA
     109                 :             : #define SUBLANG_SERBIAN_LATIN_BA 0x06
     110                 :             : #endif
     111                 :             : 
     112                 :             : gchar *
     113                 :        8592 : g_win32_getlocale (void)
     114                 :             : {
     115                 :             :   gchar *result;
     116                 :             :   LCID lcid;
     117                 :             :   LANGID langid;
     118                 :             :   const gchar *ev;
     119                 :             :   gint primary, sub;
     120                 :             :   WCHAR iso639[10];
     121                 :             :   gchar *iso639_utf8;
     122                 :             :   WCHAR iso3166[10];
     123                 :             :   gchar *iso3166_utf8;
     124                 :        8592 :   const gchar *script = NULL;
     125                 :             : 
     126                 :             :   /* Let the user override the system settings through environment
     127                 :             :    * variables, as on POSIX systems. Note that in GTK applications
     128                 :             :    * since GTK 2.10.7 setting either LC_ALL or LANG also sets the
     129                 :             :    * Win32 locale and C library locale through code in gtkmain.c.
     130                 :             :    */
     131                 :        8592 :   if (((ev = g_getenv ("LC_ALL")) != NULL && ev[0] != '\0')
     132                 :        8592 :       || ((ev = g_getenv ("LC_MESSAGES")) != NULL && ev[0] != '\0')
     133                 :        8574 :       || ((ev = g_getenv ("LANG")) != NULL && ev[0] != '\0'))
     134                 :         111 :     return g_strdup (ev);
     135                 :             : 
     136                 :        8481 :   lcid = GetThreadLocale ();
     137                 :             : 
     138                 :        8481 :   if (!GetLocaleInfoW (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) ||
     139                 :        8481 :       !GetLocaleInfoW (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166)))
     140                 :           0 :     return g_strdup ("C");
     141                 :             :   
     142                 :             :   /* Strip off the sorting rules, keep only the language part.  */
     143                 :        8481 :   langid = LANGIDFROMLCID (lcid);
     144                 :             : 
     145                 :             :   /* Split into language and territory part.  */
     146                 :        8481 :   primary = PRIMARYLANGID (langid);
     147                 :        8481 :   sub = SUBLANGID (langid);
     148                 :             : 
     149                 :             :   /* Handle special cases */
     150                 :        8481 :   switch (primary)
     151                 :             :     {
     152                 :             :     case LANG_AZERI:
     153                 :          12 :       switch (sub)
     154                 :             :         {
     155                 :             :         case SUBLANG_AZERI_LATIN:
     156                 :          12 :           script = "@Latn";
     157                 :          12 :           break;
     158                 :             :         case SUBLANG_AZERI_CYRILLIC:
     159                 :           0 :           script = "@Cyrl";
     160                 :           0 :           break;
     161                 :             :         }
     162                 :          12 :       break;
     163                 :             :     case LANG_SERBIAN:          /* LANG_CROATIAN == LANG_SERBIAN */
     164                 :           0 :       switch (sub)
     165                 :             :         {
     166                 :             :         case SUBLANG_SERBIAN_LATIN:
     167                 :             :         case 0x06: /* Serbian (Latin) - Bosnia and Herzegovina */
     168                 :           0 :           script = "@Latn";
     169                 :           0 :           break;
     170                 :             :         }
     171                 :           0 :       break;
     172                 :             :     case LANG_UZBEK:
     173                 :           0 :       switch (sub)
     174                 :             :         {
     175                 :             :         case SUBLANG_UZBEK_LATIN:
     176                 :           0 :           script = "@Latn";
     177                 :           0 :           break;
     178                 :             :         case SUBLANG_UZBEK_CYRILLIC:
     179                 :           0 :           script = "@Cyrl";
     180                 :           0 :           break;
     181                 :             :         }
     182                 :           0 :       break;
     183                 :             :     }
     184                 :             : 
     185                 :        8481 :   iso639_utf8 = g_utf16_to_utf8 (iso639, -1, NULL, NULL, NULL);
     186                 :        8481 :   iso3166_utf8 = g_utf16_to_utf8 (iso3166, -1, NULL, NULL, NULL);
     187                 :             : 
     188                 :        8481 :   result = g_strconcat (iso639_utf8, "_", iso3166_utf8, script, NULL);
     189                 :             : 
     190                 :        8481 :   g_free (iso3166_utf8);
     191                 :        8481 :   g_free (iso639_utf8);
     192                 :             : 
     193                 :        8481 :   return result;
     194                 :        8592 : }
     195                 :             : 
     196                 :             : /**
     197                 :             :  * g_win32_error_message:
     198                 :             :  * @error: Win32 error code
     199                 :             :  *
     200                 :             :  * Translate a Win32 error code into a human readable message.
     201                 :             :  *
     202                 :             :  * The error code could be as returned by
     203                 :             :  * [`GetLastError()`](https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror)
     204                 :             :  * or [`WSAGetLastError()`](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsagetlasterror).
     205                 :             :  *
     206                 :             :  * The message is either language neutral, or in the thread’s language, or the
     207                 :             :  * user’s language, the system’s language, or US English (see documentation for
     208                 :             :  * [`FormatMessage()`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagew)).
     209                 :             :  * The returned string is in UTF-8.
     210                 :             :  *
     211                 :             :  * If a human readable message cannot be found for the given @error, an empty
     212                 :             :  * string is returned.
     213                 :             :  *
     214                 :             :  * Returns: (transfer full) (not nullable): newly-allocated error message
     215                 :             :  **/
     216                 :             : gchar *
     217                 :         161 : g_win32_error_message (gint error)
     218                 :             : {
     219                 :             :   gchar *retval;
     220                 :         161 :   wchar_t *msg = NULL;
     221                 :             :   size_t nchars;
     222                 :             : 
     223                 :         161 :   FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER
     224                 :             :                   |FORMAT_MESSAGE_IGNORE_INSERTS
     225                 :             :                   |FORMAT_MESSAGE_FROM_SYSTEM,
     226                 :         161 :                   NULL, error, 0,
     227                 :             :                   (LPWSTR) &msg, 0, NULL);
     228                 :         161 :   if (msg != NULL)
     229                 :             :     {
     230                 :         161 :       nchars = wcslen (msg);
     231                 :             : 
     232                 :         161 :       if (nchars >= 2 && msg[nchars-1] == L'\n' && msg[nchars-2] == L'\r')
     233                 :         161 :         msg[nchars-2] = L'\0';
     234                 :             : 
     235                 :         161 :       retval = g_utf16_to_utf8 (msg, -1, NULL, NULL, NULL);
     236                 :             : 
     237                 :         161 :       LocalFree (msg);
     238                 :         161 :     }
     239                 :             :   else
     240                 :           0 :     retval = g_strdup ("");
     241                 :             : 
     242                 :         161 :   return retval;
     243                 :             : }
     244                 :             : 
     245                 :             : /**
     246                 :             :  * g_win32_get_package_installation_directory_of_module:
     247                 :             :  * @hmodule: (nullable): The Win32 handle for a DLL loaded into the current process, or %NULL
     248                 :             :  *
     249                 :             :  * This function tries to determine the installation directory of a
     250                 :             :  * software package based on the location of a DLL of the software
     251                 :             :  * package.
     252                 :             :  *
     253                 :             :  * @hmodule should be the handle of a loaded DLL or %NULL. The
     254                 :             :  * function looks up the directory that DLL was loaded from. If
     255                 :             :  * @hmodule is NULL, the directory the main executable of the current
     256                 :             :  * process is looked up. If that directory's last component is "bin"
     257                 :             :  * or "lib", its parent directory is returned, otherwise the directory
     258                 :             :  * itself.
     259                 :             :  *
     260                 :             :  * It thus makes sense to pass only the handle to a "public" DLL of a
     261                 :             :  * software package to this function, as such DLLs typically are known
     262                 :             :  * to be installed in a "bin" or occasionally "lib" subfolder of the
     263                 :             :  * installation folder. DLLs that are of the dynamically loaded module
     264                 :             :  * or plugin variety are often located in more private locations
     265                 :             :  * deeper down in the tree, from which it is impossible for GLib to
     266                 :             :  * deduce the root of the package installation.
     267                 :             :  *
     268                 :             :  * The typical use case for this function is to have a DllMain() that
     269                 :             :  * saves the handle for the DLL. Then when code in the DLL needs to
     270                 :             :  * construct names of files in the installation tree it calls this
     271                 :             :  * function passing the DLL handle.
     272                 :             :  *
     273                 :             :  * Returns: a string containing the guessed installation directory for
     274                 :             :  * the software package @hmodule is from. The string is in the GLib
     275                 :             :  * file name encoding, i.e. UTF-8. The return value should be freed
     276                 :             :  * with g_free() when not needed any longer. If the function fails
     277                 :             :  * %NULL is returned.
     278                 :             :  *
     279                 :             :  * Since: 2.16
     280                 :             :  */
     281                 :             : gchar *
     282                 :         151 : g_win32_get_package_installation_directory_of_module (gpointer hmodule)
     283                 :             : {
     284                 :             :   gchar *filename;
     285                 :             :   gchar *retval;
     286                 :             :   gchar *p;
     287                 :             :   wchar_t wc_fn[MAX_PATH];
     288                 :             : 
     289                 :             :   /* NOTE: it relies that GetModuleFileNameW returns only canonical paths */
     290                 :         151 :   if (!GetModuleFileNameW (hmodule, wc_fn, MAX_PATH))
     291                 :           0 :     return NULL;
     292                 :             : 
     293                 :         151 :   filename = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);
     294                 :             : 
     295                 :         151 :   if ((p = strrchr (filename, G_DIR_SEPARATOR)) != NULL)
     296                 :         151 :     *p = '\0';
     297                 :             : 
     298                 :         151 :   retval = g_strdup (filename);
     299                 :             : 
     300                 :         151 :   do
     301                 :             :     {
     302                 :        1359 :       p = strrchr (retval, G_DIR_SEPARATOR);
     303                 :        1359 :       if (p == NULL)
     304                 :         151 :         break;
     305                 :             : 
     306                 :        1208 :       *p = '\0';
     307                 :             : 
     308                 :        1208 :       if (g_ascii_strcasecmp (p + 1, "bin") == 0 ||
     309                 :        1208 :           g_ascii_strcasecmp (p + 1, "lib") == 0)
     310                 :           0 :         break;
     311                 :        1208 :     }
     312                 :        1208 :   while (p != NULL);
     313                 :             : 
     314                 :         151 :   if (p == NULL)
     315                 :             :     {
     316                 :         151 :       g_free (retval);
     317                 :         151 :       retval = filename;
     318                 :         151 :     }
     319                 :             :   else
     320                 :           0 :     g_free (filename);
     321                 :             : 
     322                 :             : #ifdef G_WITH_CYGWIN
     323                 :             :   /* In Cygwin we need to have POSIX paths */
     324                 :             :   {
     325                 :             :     gchar tmp[MAX_PATH];
     326                 :             : 
     327                 :             :     cygwin_conv_to_posix_path (retval, tmp);
     328                 :             :     g_free (retval);
     329                 :             :     retval = g_strdup (tmp);
     330                 :             :   }
     331                 :             : #endif
     332                 :             : 
     333                 :         151 :   return retval;
     334                 :         151 : }
     335                 :             : 
     336                 :             : static gchar *
     337                 :           0 : get_package_directory_from_module (const gchar *module_name)
     338                 :             : {
     339                 :             :   static GHashTable *module_dirs = NULL;
     340                 :             :   G_LOCK_DEFINE_STATIC (module_dirs);
     341                 :           0 :   HMODULE hmodule = NULL;
     342                 :             :   gchar *fn;
     343                 :             : 
     344                 :           0 :   G_LOCK (module_dirs);
     345                 :             : 
     346                 :           0 :   if (module_dirs == NULL)
     347                 :           0 :     module_dirs = g_hash_table_new (g_str_hash, g_str_equal);
     348                 :             :   
     349                 :           0 :   fn = g_hash_table_lookup (module_dirs, module_name ? module_name : "");
     350                 :             :       
     351                 :           0 :   if (fn)
     352                 :             :     {
     353                 :           0 :       G_UNLOCK (module_dirs);
     354                 :           0 :       return g_strdup (fn);
     355                 :             :     }
     356                 :             : 
     357                 :           0 :   if (module_name)
     358                 :             :     {
     359                 :           0 :       wchar_t *wc_module_name = g_utf8_to_utf16 (module_name, -1, NULL, NULL, NULL);
     360                 :           0 :       hmodule = GetModuleHandleW (wc_module_name);
     361                 :           0 :       g_free (wc_module_name);
     362                 :             : 
     363                 :           0 :       if (!hmodule)
     364                 :             :         {
     365                 :           0 :           G_UNLOCK (module_dirs);
     366                 :           0 :           return NULL;
     367                 :             :         }
     368                 :           0 :     }
     369                 :             : 
     370                 :           0 :   fn = g_win32_get_package_installation_directory_of_module (hmodule);
     371                 :             : 
     372                 :           0 :   if (fn == NULL)
     373                 :             :     {
     374                 :           0 :       G_UNLOCK (module_dirs);
     375                 :           0 :       return NULL;
     376                 :             :     }
     377                 :             :   
     378                 :           0 :   g_hash_table_insert (module_dirs, module_name ? g_strdup (module_name) : "", fn);
     379                 :             : 
     380                 :           0 :   G_UNLOCK (module_dirs);
     381                 :             : 
     382                 :           0 :   return g_strdup (fn);
     383                 :           0 : }
     384                 :             : 
     385                 :             : /**
     386                 :             :  * g_win32_get_package_installation_directory:
     387                 :             :  * @package: (nullable): You should pass %NULL for this.
     388                 :             :  * @dll_name: (nullable): The name of a DLL that a package provides in UTF-8, or %NULL.
     389                 :             :  *
     390                 :             :  * Try to determine the installation directory for a software package.
     391                 :             :  *
     392                 :             :  * This function is deprecated. Use
     393                 :             :  * g_win32_get_package_installation_directory_of_module() instead.
     394                 :             :  *
     395                 :             :  * The use of @package is deprecated. You should always pass %NULL. A
     396                 :             :  * warning is printed if non-NULL is passed as @package.
     397                 :             :  *
     398                 :             :  * The original intended use of @package was for a short identifier of
     399                 :             :  * the package, typically the same identifier as used for
     400                 :             :  * `GETTEXT_PACKAGE` in software configured using GNU
     401                 :             :  * autotools. The function first looks in the Windows Registry for the
     402                 :             :  * value `#InstallationDirectory` in the key
     403                 :             :  * `#HKLM\Software\@package`, and if that value
     404                 :             :  * exists and is a string, returns that.
     405                 :             :  *
     406                 :             :  * It is strongly recommended that packagers of GLib-using libraries
     407                 :             :  * for Windows do not store installation paths in the Registry to be
     408                 :             :  * used by this function as that interferes with having several
     409                 :             :  * parallel installations of the library. Enabling multiple
     410                 :             :  * installations of different versions of some GLib-using library, or
     411                 :             :  * GLib itself, is desirable for various reasons.
     412                 :             :  *
     413                 :             :  * For this reason it is recommended to always pass %NULL as
     414                 :             :  * @package to this function, to avoid the temptation to use the
     415                 :             :  * Registry. In version 2.20 of GLib the @package parameter
     416                 :             :  * will be ignored and this function won't look in the Registry at all.
     417                 :             :  *
     418                 :             :  * If @package is %NULL, or the above value isn't found in the
     419                 :             :  * Registry, but @dll_name is non-%NULL, it should name a DLL loaded
     420                 :             :  * into the current process. Typically that would be the name of the
     421                 :             :  * DLL calling this function, looking for its installation
     422                 :             :  * directory. The function then asks Windows what directory that DLL
     423                 :             :  * was loaded from. If that directory's last component is "bin" or
     424                 :             :  * "lib", the parent directory is returned, otherwise the directory
     425                 :             :  * itself. If that DLL isn't loaded, the function proceeds as if
     426                 :             :  * @dll_name was %NULL.
     427                 :             :  *
     428                 :             :  * If both @package and @dll_name are %NULL, the directory from where
     429                 :             :  * the main executable of the process was loaded is used instead in
     430                 :             :  * the same way as above.
     431                 :             :  *
     432                 :             :  * Returns: a string containing the installation directory for
     433                 :             :  * @package. The string is in the GLib file name encoding,
     434                 :             :  * i.e. UTF-8. The return value should be freed with g_free() when not
     435                 :             :  * needed any longer. If the function fails %NULL is returned.
     436                 :             :  *
     437                 :             :  * Deprecated: 2.18: Pass the HMODULE of a DLL or EXE to
     438                 :             :  * g_win32_get_package_installation_directory_of_module() instead.
     439                 :             :  **/
     440                 :             : 
     441                 :             : gchar *
     442                 :           0 : g_win32_get_package_installation_directory (const gchar *package,
     443                 :             :                                             const gchar *dll_name)
     444                 :             : {
     445                 :           0 :   gchar *result = NULL;
     446                 :             : 
     447                 :           0 :   if (package != NULL)
     448                 :           0 :       g_warning ("Passing a non-NULL package to g_win32_get_package_installation_directory() is deprecated and it is ignored.");
     449                 :             : 
     450                 :           0 :   if (dll_name != NULL)
     451                 :           0 :     result = get_package_directory_from_module (dll_name);
     452                 :             : 
     453                 :           0 :   if (result == NULL)
     454                 :           0 :     result = get_package_directory_from_module (NULL);
     455                 :             : 
     456                 :           0 :   return result;
     457                 :             : }
     458                 :             : 
     459                 :             : /**
     460                 :             :  * g_win32_get_package_installation_subdirectory:
     461                 :             :  * @package: (nullable): You should pass %NULL for this.
     462                 :             :  * @dll_name: (nullable): The name of a DLL that a package provides, in UTF-8, or %NULL.
     463                 :             :  * @subdir: A subdirectory of the package installation directory, also in UTF-8
     464                 :             :  *
     465                 :             :  * This function is deprecated. Use
     466                 :             :  * g_win32_get_package_installation_directory_of_module() and
     467                 :             :  * g_build_filename() instead.
     468                 :             :  *
     469                 :             :  * Returns a newly-allocated string containing the path of the
     470                 :             :  * subdirectory @subdir in the return value from calling
     471                 :             :  * g_win32_get_package_installation_directory() with the @package and
     472                 :             :  * @dll_name parameters. See the documentation for
     473                 :             :  * g_win32_get_package_installation_directory() for more details. In
     474                 :             :  * particular, note that it is deprecated to pass anything except NULL
     475                 :             :  * as @package.
     476                 :             :  *
     477                 :             :  * Returns: a string containing the complete path to @subdir inside
     478                 :             :  * the installation directory of @package. The returned string is in
     479                 :             :  * the GLib file name encoding, i.e. UTF-8. The return value should be
     480                 :             :  * freed with g_free() when no longer needed. If something goes wrong,
     481                 :             :  * %NULL is returned.
     482                 :             :  *
     483                 :             :  * Deprecated: 2.18: Pass the HMODULE of a DLL or EXE to
     484                 :             :  * g_win32_get_package_installation_directory_of_module() instead, and
     485                 :             :  * then construct a subdirectory pathname with g_build_filename().
     486                 :             :  **/
     487                 :             : 
     488                 :             : gchar *
     489                 :           0 : g_win32_get_package_installation_subdirectory (const gchar *package,
     490                 :             :                                                const gchar *dll_name,
     491                 :             :                                                const gchar *subdir)
     492                 :             : {
     493                 :             :   gchar *prefix;
     494                 :             :   gchar *dirname;
     495                 :             : 
     496                 :             : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     497                 :           0 :   prefix = g_win32_get_package_installation_directory (package, dll_name);
     498                 :             : G_GNUC_END_IGNORE_DEPRECATIONS
     499                 :             : 
     500                 :           0 :   dirname = g_build_filename (prefix, subdir, NULL);
     501                 :           0 :   g_free (prefix);
     502                 :             : 
     503                 :           0 :   return dirname;
     504                 :             : }
     505                 :             : 
     506                 :             : /*
     507                 :             :  * private API to call Windows's RtlGetVersion(), which may need to be called
     508                 :             :  * via GetProcAddress()
     509                 :             :  */
     510                 :             : gboolean
     511                 :         147 : _g_win32_call_rtl_version (OSVERSIONINFOEXW *info)
     512                 :             : {
     513                 :             :   static OSVERSIONINFOEXW result;
     514                 :             :   static gsize inited = 0;
     515                 :             : 
     516                 :         147 :   g_return_val_if_fail (info != NULL, FALSE);
     517                 :             : 
     518                 :         147 :   if (g_once_init_enter (&inited))
     519                 :             :     {
     520                 :             : #if WINAPI_FAMILY != MODERN_API_FAMILY
     521                 :             :       /* For non-modern UI Apps, use the LoadLibraryW()/GetProcAddress() thing */
     522                 :             :       typedef NTSTATUS (WINAPI fRtlGetVersion) (PRTL_OSVERSIONINFOEXW);
     523                 :             : 
     524                 :             :       fRtlGetVersion *RtlGetVersion;
     525                 :         147 :       HMODULE hmodule = LoadLibraryW (L"ntdll.dll");
     526                 :         147 :       g_return_val_if_fail (hmodule != NULL, FALSE);
     527                 :             : 
     528                 :         147 :       RtlGetVersion = (fRtlGetVersion *) GetProcAddress (hmodule, "RtlGetVersion");
     529                 :         147 :       g_return_val_if_fail (RtlGetVersion != NULL, FALSE);
     530                 :             : #endif
     531                 :             : 
     532                 :         147 :       memset (&result, 0, sizeof (OSVERSIONINFOEXW));
     533                 :         147 :       result.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEXW);
     534                 :             : 
     535                 :         147 :       RtlGetVersion (&result);
     536                 :             : 
     537                 :             : #if WINAPI_FAMILY != MODERN_API_FAMILY
     538                 :         147 :       FreeLibrary (hmodule);
     539                 :             : #endif
     540                 :         147 :       g_once_init_leave (&inited, TRUE);
     541                 :         147 :     }
     542                 :             : 
     543                 :         147 :   *info = result;
     544                 :             : 
     545                 :         147 :   return TRUE;
     546                 :         147 : }
     547                 :             : 
     548                 :             : /**
     549                 :             :  * g_win32_check_windows_version:
     550                 :             :  * @major: major version of Windows
     551                 :             :  * @minor: minor version of Windows
     552                 :             :  * @spver: Windows Service Pack Level, 0 if none
     553                 :             :  * @os_type: Type of Windows OS
     554                 :             :  *
     555                 :             :  * Returns whether the version of the Windows operating system the
     556                 :             :  * code is running on is at least the specified major, minor and
     557                 :             :  * service pack versions.  See MSDN documentation for the Operating
     558                 :             :  * System Version.  Software that needs even more detailed version and
     559                 :             :  * feature information should use the Win32 API VerifyVersionInfo()
     560                 :             :  * directly.
     561                 :             :  *
     562                 :             :  * Successive calls of this function can be used for enabling or
     563                 :             :  * disabling features at run-time for a range of Windows versions,
     564                 :             :  * as per the VerifyVersionInfo() API documentation.
     565                 :             :  *
     566                 :             :  * Returns: %TRUE if the Windows Version is the same or greater than
     567                 :             :  *          the specified major, minor and service pack versions, and
     568                 :             :  *          whether the running Windows is a workstation or server edition
     569                 :             :  *          of Windows, if specifically specified.
     570                 :             :  *
     571                 :             :  * Since: 2.44
     572                 :             :  **/
     573                 :             : gboolean
     574                 :         147 : g_win32_check_windows_version (const gint major,
     575                 :             :                                const gint minor,
     576                 :             :                                const gint spver,
     577                 :             :                                const GWin32OSType os_type)
     578                 :             : {
     579                 :             :   OSVERSIONINFOEXW osverinfo;
     580                 :         147 :   gboolean is_ver_checked = FALSE;
     581                 :         147 :   gboolean is_type_checked = FALSE;
     582                 :             : 
     583                 :             :   /* We Only Support Checking for XP or later */
     584                 :         147 :   g_return_val_if_fail (major >= 5 && (major <= 6 || major == 10), FALSE);
     585                 :         147 :   g_return_val_if_fail ((major >= 5 && minor >= 1) || major >= 6, FALSE);
     586                 :             : 
     587                 :             :   /* Check for Service Pack Version >= 0 */
     588                 :         147 :   g_return_val_if_fail (spver >= 0, FALSE);
     589                 :             : 
     590                 :         147 :   if (!_g_win32_call_rtl_version (&osverinfo))
     591                 :           0 :     return FALSE;
     592                 :             : 
     593                 :             :   /* check the OS and Service Pack Versions */
     594                 :         147 :   if (osverinfo.dwMajorVersion > (DWORD) major)
     595                 :           0 :     is_ver_checked = TRUE;
     596                 :         147 :   else if (osverinfo.dwMajorVersion == (DWORD) major)
     597                 :             :     {
     598                 :         147 :       if (osverinfo.dwMinorVersion > (DWORD) minor)
     599                 :           0 :         is_ver_checked = TRUE;
     600                 :         147 :       else if (osverinfo.dwMinorVersion == (DWORD) minor)
     601                 :         294 :         if (osverinfo.wServicePackMajor >= (DWORD) spver)
     602                 :         147 :           is_ver_checked = TRUE;
     603                 :         147 :     }
     604                 :             : 
     605                 :             :   /* Check OS Type */
     606                 :         147 :   if (is_ver_checked)
     607                 :             :     {
     608                 :         147 :       switch (os_type)
     609                 :             :         {
     610                 :             :           case G_WIN32_OS_ANY:
     611                 :         147 :             is_type_checked = TRUE;
     612                 :         147 :             break;
     613                 :             :           case G_WIN32_OS_WORKSTATION:
     614                 :           0 :             if (osverinfo.wProductType == VER_NT_WORKSTATION)
     615                 :           0 :               is_type_checked = TRUE;
     616                 :           0 :             break;
     617                 :             :           case G_WIN32_OS_SERVER:
     618                 :           0 :             if (osverinfo.wProductType == VER_NT_SERVER ||
     619                 :           0 :                 osverinfo.wProductType == VER_NT_DOMAIN_CONTROLLER)
     620                 :           0 :               is_type_checked = TRUE;
     621                 :           0 :             break;
     622                 :             :           default:
     623                 :             :             /* shouldn't get here normally */
     624                 :           0 :             g_warning ("Invalid os_type specified");
     625                 :           0 :             break;
     626                 :             :         }
     627                 :         147 :     }
     628                 :             : 
     629                 :         147 :   return is_ver_checked && is_type_checked;
     630                 :         147 : }
     631                 :             : 
     632                 :             : /**
     633                 :             :  * g_win32_get_windows_version:
     634                 :             :  *
     635                 :             :  * This function is deprecated. Use
     636                 :             :  * g_win32_check_windows_version() instead.
     637                 :             :  *
     638                 :             :  * Returns version information for the Windows operating system the
     639                 :             :  * code is running on. See MSDN documentation for the GetVersion()
     640                 :             :  * function. To summarize, the most significant bit is one on Win9x,
     641                 :             :  * and zero on NT-based systems. Since version 2.14, GLib works only
     642                 :             :  * on NT-based systems, so checking whether your are running on Win9x
     643                 :             :  * in your own software is moot. The least significant byte is 4 on
     644                 :             :  * Windows NT 4, and 5 on Windows XP. Software that needs really
     645                 :             :  * detailed version and feature information should use Win32 API like
     646                 :             :  * GetVersionEx() and VerifyVersionInfo().
     647                 :             :  *
     648                 :             :  * Returns: The version information.
     649                 :             :  *
     650                 :             :  * Deprecated: 2.44: Be aware that for Windows 8.1 and Windows Server
     651                 :             :  * 2012 R2 and later, this will return 62 unless the application is
     652                 :             :  * manifested for Windows 8.1/Windows Server 2012 R2, for example.
     653                 :             :  * MSDN stated that GetVersion(), which is used here, is subject to
     654                 :             :  * further change or removal after Windows 8.1.
     655                 :             :  **/
     656                 :             : guint
     657                 :           0 : g_win32_get_windows_version (void)
     658                 :             : {
     659                 :             :   static gsize windows_version;
     660                 :             : 
     661                 :           0 :   if (g_once_init_enter (&windows_version))
     662                 :           0 :     g_once_init_leave (&windows_version, GetVersion ());
     663                 :             : 
     664                 :           0 :   return windows_version;
     665                 :             : }
     666                 :             : 
     667                 :             : /*
     668                 :             :  * Doesn't use gettext (and gconv), preventing recursive calls when
     669                 :             :  * g_win32_locale_filename_from_utf8() is called during
     670                 :             :  * gettext initialization.
     671                 :             :  */
     672                 :             : static gchar *
     673                 :         117 : special_wchar_to_locale_encoding (wchar_t *wstring)
     674                 :             : {
     675                 :             :   int sizeof_output;
     676                 :             :   int wctmb_result;
     677                 :             :   char *result;
     678                 :         117 :   BOOL not_representable = FALSE;
     679                 :             : 
     680                 :         117 :   sizeof_output = WideCharToMultiByte (CP_ACP,
     681                 :             :                                        WC_NO_BEST_FIT_CHARS,
     682                 :         117 :                                        wstring, -1,
     683                 :             :                                        NULL, 0,
     684                 :             :                                        NULL,
     685                 :             :                                        &not_representable);
     686                 :             : 
     687                 :         117 :   if (not_representable ||
     688                 :         117 :       sizeof_output == 0 ||
     689                 :         117 :       sizeof_output > MAX_PATH)
     690                 :           0 :     return NULL;
     691                 :             : 
     692                 :         117 :   result = g_malloc0 (sizeof_output + 1);
     693                 :             : 
     694                 :         117 :   wctmb_result = WideCharToMultiByte (CP_ACP,
     695                 :             :                                       WC_NO_BEST_FIT_CHARS,
     696                 :         117 :                                       wstring, -1,
     697                 :         117 :                                       result, sizeof_output + 1,
     698                 :             :                                       NULL,
     699                 :             :                                       &not_representable);
     700                 :             : 
     701                 :         117 :   if (wctmb_result == sizeof_output &&
     702                 :         117 :       not_representable == FALSE)
     703                 :         117 :     return result;
     704                 :             : 
     705                 :           0 :   g_free (result);
     706                 :             : 
     707                 :           0 :   return NULL;
     708                 :         117 : }
     709                 :             : 
     710                 :             : /**
     711                 :             :  * g_win32_locale_filename_from_utf8:
     712                 :             :  * @utf8filename: a UTF-8 encoded filename.
     713                 :             :  *
     714                 :             :  * Converts a filename from UTF-8 to the system codepage.
     715                 :             :  *
     716                 :             :  * On NT-based Windows, on NTFS file systems, file names are in
     717                 :             :  * Unicode. It is quite possible that Unicode file names contain
     718                 :             :  * characters not representable in the system codepage. (For instance,
     719                 :             :  * Greek or Cyrillic characters on Western European or US Windows
     720                 :             :  * installations, or various less common CJK characters on CJK Windows
     721                 :             :  * installations.)
     722                 :             :  *
     723                 :             :  * In such a case, and if the filename refers to an existing file, and
     724                 :             :  * the file system stores alternate short (8.3) names for directory
     725                 :             :  * entries, the short form of the filename is returned. Note that the
     726                 :             :  * "short" name might in fact be longer than the Unicode name if the
     727                 :             :  * Unicode name has very short pathname components containing
     728                 :             :  * non-ASCII characters. If no system codepage name for the file is
     729                 :             :  * possible, %NULL is returned.
     730                 :             :  *
     731                 :             :  * The return value is dynamically allocated and should be freed with
     732                 :             :  * g_free() when no longer needed.
     733                 :             :  *
     734                 :             :  * Returns: The converted filename, or %NULL on conversion
     735                 :             :  * failure and lack of short names.
     736                 :             :  *
     737                 :             :  * Since: 2.8
     738                 :             :  */
     739                 :             : gchar *
     740                 :         117 : g_win32_locale_filename_from_utf8 (const gchar *utf8filename)
     741                 :             : {
     742                 :             :   gchar *retval;
     743                 :             :   wchar_t *wname;
     744                 :             : 
     745                 :         117 :   wname = g_utf8_to_utf16 (utf8filename, -1, NULL, NULL, NULL);
     746                 :             : 
     747                 :         117 :   if (wname == NULL)
     748                 :           0 :     return NULL;
     749                 :             : 
     750                 :         117 :   retval = special_wchar_to_locale_encoding (wname);
     751                 :             : 
     752                 :         117 :   if (retval == NULL)
     753                 :             :     {
     754                 :             :       /* Conversion failed, so check if there is a 8.3 version, and use that. */
     755                 :             :       wchar_t wshortname[MAX_PATH + 1];
     756                 :             : 
     757                 :           0 :       if (GetShortPathNameW (wname, wshortname, G_N_ELEMENTS (wshortname)))
     758                 :           0 :         retval = special_wchar_to_locale_encoding (wshortname);
     759                 :           0 :     }
     760                 :             : 
     761                 :         117 :   g_free (wname);
     762                 :             : 
     763                 :         117 :   return retval;
     764                 :         117 : }
     765                 :             : 
     766                 :             : /**
     767                 :             :  * g_win32_get_command_line:
     768                 :             :  *
     769                 :             :  * Gets the command line arguments, on Windows, in the GLib filename
     770                 :             :  * encoding (ie: UTF-8).
     771                 :             :  *
     772                 :             :  * Normally, on Windows, the command line arguments are passed to main()
     773                 :             :  * in the system codepage encoding.  This prevents passing filenames as
     774                 :             :  * arguments if the filenames contain characters that fall outside of
     775                 :             :  * this codepage.  If such filenames are passed, then substitutions
     776                 :             :  * will occur (such as replacing some characters with '?').
     777                 :             :  *
     778                 :             :  * GLib's policy of using UTF-8 as a filename encoding on Windows was
     779                 :             :  * designed to localise the pain of dealing with filenames outside of
     780                 :             :  * the system codepage to one area: dealing with commandline arguments
     781                 :             :  * in main().
     782                 :             :  *
     783                 :             :  * As such, most GLib programs should ignore the value of argv passed to
     784                 :             :  * their main() function and call g_win32_get_command_line() instead.
     785                 :             :  * This will get the "full Unicode" commandline arguments using
     786                 :             :  * GetCommandLineW() and convert it to the GLib filename encoding (which
     787                 :             :  * is UTF-8 on Windows).
     788                 :             :  *
     789                 :             :  * The strings returned by this function are suitable for use with
     790                 :             :  * functions such as g_open() and g_file_new_for_commandline_arg() but
     791                 :             :  * are not suitable for use with g_option_context_parse(), which assumes
     792                 :             :  * that its input will be in the system codepage.  The return value is
     793                 :             :  * suitable for use with g_option_context_parse_strv(), however, which
     794                 :             :  * is a better match anyway because it won't leak memory.
     795                 :             :  *
     796                 :             :  * Unlike argv, the returned value is a normal strv and can (and should)
     797                 :             :  * be freed with g_strfreev() when no longer needed.
     798                 :             :  *
     799                 :             :  * Returns: (transfer full): the commandline arguments in the GLib
     800                 :             :  *   filename encoding (ie: UTF-8)
     801                 :             :  *
     802                 :             :  * Since: 2.40
     803                 :             :  **/
     804                 :             : gchar **
     805                 :           8 : g_win32_get_command_line (void)
     806                 :             : {
     807                 :             :   gchar **result;
     808                 :             :   LPWSTR *args;
     809                 :             :   gint i, n;
     810                 :             : 
     811                 :           8 :   args = CommandLineToArgvW (GetCommandLineW(), &n);
     812                 :             : 
     813                 :           8 :   result = g_new (gchar *, n + 1);
     814                 :          78 :   for (i = 0; i < n; i++)
     815                 :          70 :     result[i] = g_utf16_to_utf8 (args[i], -1, NULL, NULL, NULL);
     816                 :           8 :   result[i] = NULL;
     817                 :             : 
     818                 :           8 :   LocalFree (args);
     819                 :           8 :   return result;
     820                 :             : }
     821                 :             : 
     822                 :             : /* Binary compatibility versions. Not for newly compiled code. */
     823                 :             : 
     824                 :             : _GLIB_EXTERN gchar *g_win32_get_package_installation_directory_utf8    (const gchar *package,
     825                 :             :                                                                         const gchar *dll_name);
     826                 :             : 
     827                 :             : _GLIB_EXTERN gchar *g_win32_get_package_installation_subdirectory_utf8 (const gchar *package,
     828                 :             :                                                                         const gchar *dll_name,
     829                 :             :                                                                         const gchar *subdir);
     830                 :             : 
     831                 :             : gchar *
     832                 :           0 : g_win32_get_package_installation_directory_utf8 (const gchar *package,
     833                 :             :                                                  const gchar *dll_name)
     834                 :             : {
     835                 :             : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     836                 :           0 :   return g_win32_get_package_installation_directory (package, dll_name);
     837                 :             : G_GNUC_END_IGNORE_DEPRECATIONS
     838                 :             : }
     839                 :             : 
     840                 :             : gchar *
     841                 :           0 : g_win32_get_package_installation_subdirectory_utf8 (const gchar *package,
     842                 :             :                                                     const gchar *dll_name,
     843                 :             :                                                     const gchar *subdir)
     844                 :             : {
     845                 :             : G_GNUC_BEGIN_IGNORE_DEPRECATIONS
     846                 :           0 :   return g_win32_get_package_installation_subdirectory (package,
     847                 :           0 :                                                         dll_name,
     848                 :           0 :                                                         subdir);
     849                 :             : G_GNUC_END_IGNORE_DEPRECATIONS
     850                 :             : }
     851                 :             : 
     852                 :             : /* This function looks up two environment
     853                 :             :  * variables, G_WIN32_ALLOC_CONSOLE and G_WIN32_ATTACH_CONSOLE.
     854                 :             :  * G_WIN32_ALLOC_CONSOLE, if set to 1, makes the process
     855                 :             :  * call AllocConsole(). This is useful for binaries that
     856                 :             :  * are compiled to run without automatically-allocated console
     857                 :             :  * (like most GUI applications).
     858                 :             :  * G_WIN32_ATTACH_CONSOLE, if set to a comma-separated list
     859                 :             :  * of one or more strings "stdout", "stdin" and "stderr",
     860                 :             :  * makes the process reopen the corresponding standard streams
     861                 :             :  * to ensure that they are attached to the files that
     862                 :             :  * GetStdHandle() returns, which, hopefully, would be
     863                 :             :  * either a file handle or a console handle.
     864                 :             :  *
     865                 :             :  * This function is called automatically when glib DLL is
     866                 :             :  * attached to a process, from DllMain().
     867                 :             :  */
     868                 :             : void
     869                 :        1217 : g_console_win32_init (void)
     870                 :             : {
     871                 :             :   struct
     872                 :             :     {
     873                 :             :       gboolean     redirect;
     874                 :             :       FILE        *stream;
     875                 :             :       const gchar *stream_name;
     876                 :             :       DWORD        std_handle_type;
     877                 :             :       int          flags;
     878                 :             :       const gchar *mode;
     879                 :             :     }
     880                 :             :   streams[] =
     881                 :        2434 :     {
     882                 :        1217 :       { FALSE, stdin, "stdin", STD_INPUT_HANDLE, _O_RDONLY, "rb" },
     883                 :        1217 :       { FALSE, stdout, "stdout", STD_OUTPUT_HANDLE, 0, "wb" },
     884                 :        1217 :       { FALSE, stderr, "stderr", STD_ERROR_HANDLE, 0, "wb" },
     885                 :             :     };
     886                 :             : 
     887                 :             :   const gchar  *attach_envvar;
     888                 :             :   guint         i;
     889                 :             :   gchar       **attach_strs;
     890                 :             : 
     891                 :             :   /* Note: it's not a very good practice to use DllMain()
     892                 :             :    * to call any functions not in Kernel32.dll.
     893                 :             :    * The following only works if there are no weird
     894                 :             :    * circular DLL dependencies that could cause glib DllMain()
     895                 :             :    * to be called before CRT DllMain().
     896                 :             :    */
     897                 :             : 
     898                 :        1217 :   if (g_strcmp0 (g_getenv ("G_WIN32_ALLOC_CONSOLE"), "1") == 0)
     899                 :           0 :     AllocConsole (); /* no error handling, fails if console already exists */
     900                 :             : 
     901                 :        1217 :   attach_envvar = g_getenv ("G_WIN32_ATTACH_CONSOLE");
     902                 :             : 
     903                 :        1217 :   if (attach_envvar == NULL)
     904                 :        1217 :     return;
     905                 :             : 
     906                 :             :   /* Re-use parent console, if we don't have our own.
     907                 :             :    * If we do, it will fail, so just ignore the error.
     908                 :             :    */
     909                 :           0 :   AttachConsole (ATTACH_PARENT_PROCESS);
     910                 :             : 
     911                 :           0 :   attach_strs = g_strsplit (attach_envvar, ",", -1);
     912                 :             : 
     913                 :           0 :   for (i = 0; attach_strs[i]; i++)
     914                 :             :     {
     915                 :           0 :       if (g_strcmp0 (attach_strs[i], "stdout") == 0)
     916                 :           0 :         streams[1].redirect = TRUE;
     917                 :           0 :       else if (g_strcmp0 (attach_strs[i], "stderr") == 0)
     918                 :           0 :         streams[2].redirect = TRUE;
     919                 :           0 :       else if (g_strcmp0 (attach_strs[i], "stdin") == 0)
     920                 :           0 :         streams[0].redirect = TRUE;
     921                 :             :       else
     922                 :           0 :         g_warning ("Unrecognized stream name %s", attach_strs[i]);
     923                 :           0 :     }
     924                 :             : 
     925                 :           0 :   g_strfreev (attach_strs);
     926                 :             : 
     927                 :           0 :   for (i = 0; i < G_N_ELEMENTS (streams); i++)
     928                 :             :     {
     929                 :             :       int          old_fd;
     930                 :             :       int          backup_fd;
     931                 :             :       int          new_fd;
     932                 :           0 :       int          preferred_fd = i;
     933                 :             :       HANDLE       std_handle;
     934                 :           0 :       errno_t      errsv = 0;
     935                 :             : 
     936                 :           0 :       if (!streams[i].redirect)
     937                 :           0 :         continue;
     938                 :             : 
     939                 :           0 :       if (ferror (streams[i].stream) != 0)
     940                 :             :         {
     941                 :           0 :           g_warning ("Stream %s is in error state", streams[i].stream_name);
     942                 :           0 :           continue;
     943                 :             :         }
     944                 :             : 
     945                 :           0 :       std_handle = GetStdHandle (streams[i].std_handle_type);
     946                 :             : 
     947                 :           0 :       if (std_handle == INVALID_HANDLE_VALUE)
     948                 :             :         {
     949                 :           0 :           DWORD gle = GetLastError ();
     950                 :           0 :           g_warning ("Standard handle for %s can't be obtained: %lu",
     951                 :           0 :                      streams[i].stream_name, gle);
     952                 :           0 :           continue;
     953                 :             :         }
     954                 :             : 
     955                 :           0 :       old_fd = fileno (streams[i].stream);
     956                 :             : 
     957                 :             :       /* We need the stream object to be associated with
     958                 :             :        * any valid integer fd for the code to work.
     959                 :             :        * If it isn't, reopen it with NUL (/dev/null) to
     960                 :             :        * ensure that it is.
     961                 :             :        */
     962                 :           0 :       if (old_fd < 0)
     963                 :             :         {
     964                 :           0 :           if (freopen ("NUL", streams[i].mode, streams[i].stream) == NULL)
     965                 :             :             {
     966                 :           0 :               errsv = errno;
     967                 :           0 :               g_warning ("Failed to redirect %s: %d - %s",
     968                 :           0 :                          streams[i].stream_name,
     969                 :           0 :                          errsv,
     970                 :           0 :                          strerror (errsv));
     971                 :           0 :               continue;
     972                 :             :             }
     973                 :             : 
     974                 :           0 :           old_fd = fileno (streams[i].stream);
     975                 :             : 
     976                 :           0 :           if (old_fd < 0)
     977                 :             :             {
     978                 :           0 :               g_warning ("Stream %s does not have a valid fd",
     979                 :           0 :                          streams[i].stream_name);
     980                 :           0 :               continue;
     981                 :             :             }
     982                 :           0 :         }
     983                 :             : 
     984                 :           0 :       new_fd = _open_osfhandle ((intptr_t) std_handle, streams[i].flags);
     985                 :             : 
     986                 :           0 :       if (new_fd < 0)
     987                 :             :         {
     988                 :           0 :           g_warning ("Failed to create new fd for stream %s",
     989                 :           0 :                      streams[i].stream_name);
     990                 :           0 :           continue;
     991                 :             :         }
     992                 :             : 
     993                 :           0 :       backup_fd = dup (old_fd);
     994                 :             : 
     995                 :           0 :       if (backup_fd < 0)
     996                 :           0 :         g_warning ("Failed to backup old fd %d for stream %s",
     997                 :           0 :                    old_fd, streams[i].stream_name);
     998                 :             : 
     999                 :           0 :       errno = 0;
    1000                 :             : 
    1001                 :             :       /* Force old_fd to be associated with the same file
    1002                 :             :        * as new_fd, i.e with the standard handle we need
    1003                 :             :        * (or, rather, with the same kernel object; handle
    1004                 :             :        * value will be different, but the kernel object
    1005                 :             :        * won't be).
    1006                 :             :        */
    1007                 :             :       /* NOTE: MSDN claims that _dup2() returns 0 on success and -1 on error,
    1008                 :             :        * POSIX claims that dup2() reurns new FD on success and -1 on error.
    1009                 :             :        * The "< 0" check satisfies the error condition for either implementation.
    1010                 :             :        */
    1011                 :           0 :       if (_dup2 (new_fd, old_fd) < 0)
    1012                 :             :         {
    1013                 :           0 :           errsv = errno;
    1014                 :           0 :           g_warning ("Failed to substitute fd %d for stream %s: %d : %s",
    1015                 :           0 :                      old_fd, streams[i].stream_name, errsv, strerror (errsv));
    1016                 :             : 
    1017                 :           0 :           _close (new_fd);
    1018                 :             : 
    1019                 :           0 :           if (backup_fd < 0)
    1020                 :           0 :             continue;
    1021                 :             : 
    1022                 :           0 :           errno = 0;
    1023                 :             : 
    1024                 :             :           /* Try to restore old_fd back to its previous
    1025                 :             :            * handle, in case the _dup2() call above succeeded partially.
    1026                 :             :            */
    1027                 :           0 :           if (_dup2 (backup_fd, old_fd) < 0)
    1028                 :             :             {
    1029                 :           0 :               errsv = errno;
    1030                 :           0 :               g_warning ("Failed to restore fd %d for stream %s: %d : %s",
    1031                 :           0 :                          old_fd, streams[i].stream_name, errsv, strerror (errsv));
    1032                 :           0 :             }
    1033                 :             : 
    1034                 :           0 :           _close (backup_fd);
    1035                 :             : 
    1036                 :           0 :           continue;
    1037                 :             :         }
    1038                 :             : 
    1039                 :             :       /* Success, drop the backup */
    1040                 :           0 :       if (backup_fd >= 0)
    1041                 :           0 :         _close (backup_fd);
    1042                 :             : 
    1043                 :             :       /* Sadly, there's no way to check that preferred_fd
    1044                 :             :        * is currently valid, so we can't back it up.
    1045                 :             :        * Doing operations on invalid FDs invokes invalid
    1046                 :             :        * parameter handler, which is bad for us.
    1047                 :             :        */
    1048                 :           0 :       if (old_fd != preferred_fd)
    1049                 :             :         /* This extra code will also try to ensure that
    1050                 :             :          * the expected file descriptors 0, 1 and 2 are
    1051                 :             :          * associated with the appropriate standard
    1052                 :             :          * handles.
    1053                 :             :          */
    1054                 :           0 :         if (_dup2 (new_fd, preferred_fd) < 0)
    1055                 :           0 :           g_warning ("Failed to dup fd %d into fd %d", new_fd, preferred_fd);
    1056                 :             : 
    1057                 :           0 :       _close (new_fd);
    1058                 :           0 :     }
    1059                 :        1217 : }
    1060                 :             : 
    1061                 :             : /* This is a handle to the Vectored Exception Handler that
    1062                 :             :  * we install on library initialization. If installed correctly,
    1063                 :             :  * it will be non-NULL. Only used to later de-install the handler
    1064                 :             :  * on library de-initialization.
    1065                 :             :  */
    1066                 :             : static void        *WinVEH_handle = NULL;
    1067                 :             : 
    1068                 :             : #define             DEBUGGER_BUFFER_SIZE (MAX_PATH + 1)
    1069                 :             : /* This is the debugger that we'll run on crash */
    1070                 :             : static wchar_t      debugger[DEBUGGER_BUFFER_SIZE];
    1071                 :             : 
    1072                 :             : static gsize        number_of_exceptions_to_catch = 0;
    1073                 :             : static DWORD       *exceptions_to_catch = NULL;
    1074                 :             : 
    1075                 :             : static HANDLE       debugger_wakeup_event = 0;
    1076                 :             : static DWORD        debugger_spawn_flags = 0;
    1077                 :             : 
    1078                 :             : /* Copy @cmdline into @debugger, and substitute @pid for `%p`
    1079                 :             :  * and @event for `%e`.
    1080                 :             :  * If @debugger_size (in wchar_ts) is overflowed, return %FALSE.
    1081                 :             :  * Also returns %FALSE when `%` is followed by anything other
    1082                 :             :  * than `e` or `p`.
    1083                 :             :  */
    1084                 :             : bool
    1085                 :        1218 : g_win32_substitute_pid_and_event (wchar_t       *local_debugger,
    1086                 :             :                                   gsize          debugger_size,
    1087                 :             :                                   const wchar_t *cmdline,
    1088                 :             :                                   DWORD          pid,
    1089                 :             :                                   guintptr       event)
    1090                 :             : {
    1091                 :        1218 :   gsize i = 0, dbg_i = 0;
    1092                 :             : /* These are integers, and they can't be longer than 20 characters
    1093                 :             :  * even when they are 64-bit and in decimal notation.
    1094                 :             :  * Use 30 just to be sure.
    1095                 :             :  */
    1096                 :             : #define STR_BUFFER_SIZE 30
    1097                 :        1218 :   wchar_t pid_str[STR_BUFFER_SIZE] = {0};
    1098                 :             :   gsize pid_str_len;
    1099                 :        1218 :   wchar_t event_str[STR_BUFFER_SIZE] = {0};
    1100                 :             :   gsize event_str_len;
    1101                 :             : 
    1102                 :        1218 :   _snwprintf_s (pid_str, STR_BUFFER_SIZE, G_N_ELEMENTS (pid_str), L"%lu", pid);
    1103                 :        1218 :   pid_str[G_N_ELEMENTS (pid_str) - 1] = 0;
    1104                 :        1218 :   pid_str_len = wcslen (pid_str);
    1105                 :        1218 :   _snwprintf_s (event_str, STR_BUFFER_SIZE, G_N_ELEMENTS (pid_str), L"%Iu", event);
    1106                 :        1218 :   event_str[G_N_ELEMENTS (pid_str) - 1] = 0;
    1107                 :        1218 :   event_str_len = wcslen (event_str);
    1108                 :             : #undef STR_BUFFER_SIZE
    1109                 :             : 
    1110                 :      154066 :   while (cmdline[i] != 0 && dbg_i < debugger_size)
    1111                 :             :     {
    1112                 :      152849 :       if (cmdline[i] != L'%')
    1113                 :      150413 :         local_debugger[dbg_i++] = cmdline[i++];
    1114                 :        2436 :       else if (cmdline[i + 1] == L'p')
    1115                 :             :         {
    1116                 :        1217 :           gsize j = 0;
    1117                 :        7242 :           while (j < pid_str_len && dbg_i < debugger_size)
    1118                 :        6025 :             local_debugger[dbg_i++] = pid_str[j++];
    1119                 :        1217 :           i += 2;
    1120                 :        1217 :         }
    1121                 :        1219 :       else if (cmdline[i + 1] == L'e')
    1122                 :             :         {
    1123                 :        1218 :           gsize j = 0;
    1124                 :        4892 :           while (j < event_str_len && dbg_i < debugger_size)
    1125                 :        3674 :             local_debugger[dbg_i++] = event_str[j++];
    1126                 :        1218 :           i += 2;
    1127                 :        1218 :         }
    1128                 :             :       else
    1129                 :           1 :         return FALSE;
    1130                 :             :     }
    1131                 :        1217 :   if (dbg_i < debugger_size)
    1132                 :        1215 :     local_debugger[dbg_i] = 0;
    1133                 :             :   else
    1134                 :           2 :     return FALSE;
    1135                 :             : 
    1136                 :        1215 :   return TRUE;
    1137                 :        1218 : }
    1138                 :             : 
    1139                 :             : static char *
    1140                 :           0 : copy_chars (char       *buffer,
    1141                 :             :             gsize      *buffer_size,
    1142                 :             :             const char *to_copy)
    1143                 :             : {
    1144                 :           0 :   gsize copy_count = MIN (strlen (to_copy), *buffer_size - 1);
    1145                 :           0 :   memset (buffer, 0x20, copy_count);
    1146                 :           0 :   strncpy_s (buffer, *buffer_size, to_copy, _TRUNCATE);
    1147                 :           0 :   *buffer_size -= copy_count;
    1148                 :           0 :   return &buffer[copy_count];
    1149                 :             : }
    1150                 :             : 
    1151                 :             : /* Handles exceptions (useful for debugging).
    1152                 :             :  * Issues a DebugBreak() call if the process is being debugged (not really
    1153                 :             :  * useful - if the process is being debugged, this handler won't be invoked
    1154                 :             :  * anyway). If it is not, runs a debugger from G_DEBUGGER env var,
    1155                 :             :  * substituting first %p in it for PID, and the first %e for the event handle -
    1156                 :             :  * that event should be set once the debugger attaches itself (otherwise the
    1157                 :             :  * only way out of WaitForSingleObject() is to time out after 1 minute).
    1158                 :             :  * For example, G_DEBUGGER can be set to the following command:
    1159                 :             :  * ```
    1160                 :             :  * gdb.exe -ex "attach %p" -ex "signal-event %e" -ex "bt" -ex "c"
    1161                 :             :  * ```
    1162                 :             :  * This will make GDB attach to the process, signal the event (GDB must be
    1163                 :             :  * recent enough for the signal-event command to be available),
    1164                 :             :  * show the backtrace and resume execution, which should make it catch
    1165                 :             :  * the exception when Windows re-raises it again.
    1166                 :             :  * The command line can't be longer than MAX_PATH (260 characters).
    1167                 :             :  *
    1168                 :             :  * This function will only stop (and run a debugger) on the following exceptions:
    1169                 :             :  * * EXCEPTION_ACCESS_VIOLATION
    1170                 :             :  * * EXCEPTION_STACK_OVERFLOW
    1171                 :             :  * * EXCEPTION_ILLEGAL_INSTRUCTION
    1172                 :             :  * To make it stop at other exceptions one should set the G_VEH_CATCH
    1173                 :             :  * environment variable to a list of comma-separated hexadecimal numbers,
    1174                 :             :  * where each number is the code of an exception that should be caught.
    1175                 :             :  * This is done to prevent GLib from breaking when Windows uses
    1176                 :             :  * exceptions to shuttle information (SetThreadName(), OutputDebugString())
    1177                 :             :  * or for control flow.
    1178                 :             :  *
    1179                 :             :  * This function deliberately avoids calling any GLib code.
    1180                 :             :  * This is done on purpose. This function can be called when the program
    1181                 :             :  * is in a bad state (crashing). It can also be called very early, as soon
    1182                 :             :  * as the handler is installed. Therefore, it's imperative that
    1183                 :             :  * it does as little as possible. Preferably, all the work that can be
    1184                 :             :  * done in advance (when the program is not crashing yet) should be done
    1185                 :             :  * in advance.
    1186                 :             :  */
    1187                 :             : static LONG __stdcall
    1188                 :           0 : g_win32_veh_handler (PEXCEPTION_POINTERS ExceptionInfo)
    1189                 :             : {
    1190                 :             :   EXCEPTION_RECORD    *er;
    1191                 :             :   gsize                i;
    1192                 :             :   STARTUPINFOW         si;
    1193                 :             :   PROCESS_INFORMATION  pi;
    1194                 :             : #define ITOA_BUFFER_SIZE 100
    1195                 :             :   char                 itoa_buffer[ITOA_BUFFER_SIZE];
    1196                 :             : #define DEBUG_STRING_SIZE 1024
    1197                 :           0 :   gsize                dbgs = DEBUG_STRING_SIZE;
    1198                 :             :   char                 debug_string[DEBUG_STRING_SIZE];
    1199                 :             :   char                *dbgp;
    1200                 :             : 
    1201                 :           0 :   if (ExceptionInfo == NULL ||
    1202                 :           0 :       ExceptionInfo->ExceptionRecord == NULL ||
    1203                 :           0 :       IsDebuggerPresent () ||
    1204                 :           0 :       debugger[0] == 0)
    1205                 :           0 :     return EXCEPTION_CONTINUE_SEARCH;
    1206                 :             : 
    1207                 :           0 :   er = ExceptionInfo->ExceptionRecord;
    1208                 :             : 
    1209                 :           0 :   switch (er->ExceptionCode)
    1210                 :             :     {
    1211                 :             :     case EXCEPTION_ACCESS_VIOLATION:
    1212                 :             :     case EXCEPTION_STACK_OVERFLOW:
    1213                 :             :     case EXCEPTION_ILLEGAL_INSTRUCTION:
    1214                 :           0 :       break;
    1215                 :             :     default:
    1216                 :           0 :       for (i = 0; i < number_of_exceptions_to_catch; i++)
    1217                 :           0 :         if (exceptions_to_catch[i] == er->ExceptionCode)
    1218                 :           0 :           break;
    1219                 :             : 
    1220                 :           0 :       if (i == number_of_exceptions_to_catch)
    1221                 :           0 :         return EXCEPTION_CONTINUE_SEARCH;
    1222                 :             : 
    1223                 :           0 :       break;
    1224                 :             :     }
    1225                 :             : 
    1226                 :           0 :   memset (&si, 0, sizeof (si));
    1227                 :           0 :   memset (&pi, 0, sizeof (pi));
    1228                 :           0 :   si.cb = sizeof (si);
    1229                 :             : 
    1230                 :             :   /* Run the debugger */
    1231                 :           0 :   if (0 != CreateProcessW (NULL, debugger, NULL, NULL, TRUE, debugger_spawn_flags, NULL, NULL, &si, &pi))
    1232                 :             :     {
    1233                 :           0 :       CloseHandle (pi.hProcess);
    1234                 :           0 :       CloseHandle (pi.hThread);
    1235                 :             :       /* If successful, wait for 60 seconds on the event
    1236                 :             :        * we passed. The debugger should signal that event.
    1237                 :             :        * 60 second limit is here to prevent us from hanging
    1238                 :             :        * up forever in case the debugger does not support
    1239                 :             :        * event signalling.
    1240                 :             :        */
    1241                 :           0 :       WaitForSingleObject (debugger_wakeup_event, 60000);
    1242                 :             : 
    1243                 :           0 :       dbgp = &debug_string[0];
    1244                 :             : 
    1245                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, "Exception code=0x");
    1246                 :           0 :       itoa_buffer[0] = 0;
    1247                 :           0 :       _ui64toa_s (er->ExceptionCode, itoa_buffer, ITOA_BUFFER_SIZE, 16);
    1248                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
    1249                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, " flags=0x");
    1250                 :           0 :       itoa_buffer[0] = 0;
    1251                 :           0 :       _ui64toa_s (er->ExceptionFlags, itoa_buffer, ITOA_BUFFER_SIZE, 16);
    1252                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
    1253                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, " at 0x");
    1254                 :           0 :       itoa_buffer[0] = 0;
    1255                 :           0 :       _ui64toa_s ((guintptr) er->ExceptionAddress, itoa_buffer, ITOA_BUFFER_SIZE, 16);
    1256                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
    1257                 :             : 
    1258                 :           0 :       switch (er->ExceptionCode)
    1259                 :             :         {
    1260                 :             :         case EXCEPTION_ACCESS_VIOLATION:
    1261                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, ". Access violation - attempting to ");
    1262                 :           0 :           if (er->ExceptionInformation[0] == 0)
    1263                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "read data");
    1264                 :           0 :           else if (er->ExceptionInformation[0] == 1)
    1265                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "write data");
    1266                 :           0 :           else if (er->ExceptionInformation[0] == 8)
    1267                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "execute data");
    1268                 :             :           else
    1269                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "do something bad");
    1270                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, " at address 0x");
    1271                 :           0 :           itoa_buffer[0] = 0;
    1272                 :           0 :           _ui64toa_s (er->ExceptionInformation[1], itoa_buffer, ITOA_BUFFER_SIZE, 16);
    1273                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
    1274                 :           0 :           break;
    1275                 :             :         case EXCEPTION_IN_PAGE_ERROR:
    1276                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, ". Page access violation - attempting to ");
    1277                 :           0 :           if (er->ExceptionInformation[0] == 0)
    1278                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "read from an inaccessible page");
    1279                 :           0 :           else if (er->ExceptionInformation[0] == 1)
    1280                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "write to an inaccessible page");
    1281                 :           0 :           else if (er->ExceptionInformation[0] == 8)
    1282                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "execute data in page");
    1283                 :             :           else
    1284                 :           0 :             dbgp = copy_chars (dbgp, &dbgs, "do something bad with a page");
    1285                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, " at address 0x");
    1286                 :           0 :           itoa_buffer[0] = 0;
    1287                 :           0 :           _ui64toa_s (er->ExceptionInformation[1], itoa_buffer, ITOA_BUFFER_SIZE, 16);
    1288                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
    1289                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, " with status ");
    1290                 :           0 :           itoa_buffer[0] = 0;
    1291                 :           0 :           _ui64toa_s (er->ExceptionInformation[2], itoa_buffer, ITOA_BUFFER_SIZE, 16);
    1292                 :           0 :           dbgp = copy_chars (dbgp, &dbgs, itoa_buffer);
    1293                 :           0 :           break;
    1294                 :             :         default:
    1295                 :           0 :           break;
    1296                 :             :         }
    1297                 :             : 
    1298                 :           0 :       dbgp = copy_chars (dbgp, &dbgs, "\n");
    1299                 :           0 :       OutputDebugStringA (debug_string);
    1300                 :           0 :     }
    1301                 :             : 
    1302                 :             :   /* Now the debugger is present, and we can try
    1303                 :             :    * resuming execution, re-triggering the exception,
    1304                 :             :    * which will be caught by debugger this time around.
    1305                 :             :    */
    1306                 :           0 :   if (IsDebuggerPresent ())
    1307                 :           0 :     return EXCEPTION_CONTINUE_EXECUTION;
    1308                 :             : 
    1309                 :           0 :   return EXCEPTION_CONTINUE_SEARCH;
    1310                 :           0 : }
    1311                 :             : 
    1312                 :             : static gsize
    1313                 :           0 : parse_catch_list (const wchar_t *catch_buffer,
    1314                 :             :                   DWORD         *exceptions,
    1315                 :             :                   gsize          num_exceptions)
    1316                 :             : {
    1317                 :           0 :   const wchar_t *catch_list = catch_buffer;
    1318                 :           0 :   gsize          result = 0;
    1319                 :           0 :   gsize          i = 0;
    1320                 :             : 
    1321                 :           0 :   while (catch_list != NULL &&
    1322                 :           0 :          catch_list[0] != 0)
    1323                 :             :     {
    1324                 :             :       unsigned long  catch_code;
    1325                 :             :       wchar_t       *end;
    1326                 :           0 :       errno = 0;
    1327                 :           0 :       catch_code = wcstoul (catch_list, &end, 16);
    1328                 :           0 :       if (errno != NO_ERROR)
    1329                 :           0 :         break;
    1330                 :           0 :       catch_list = end;
    1331                 :           0 :       if (catch_list != NULL && catch_list[0] == L',')
    1332                 :           0 :         catch_list++;
    1333                 :           0 :       if (exceptions && i < num_exceptions)
    1334                 :           0 :         exceptions[i++] = catch_code;
    1335                 :             :     }
    1336                 :             : 
    1337                 :           0 :   return result;
    1338                 :             : }
    1339                 :             : 
    1340                 :             : void
    1341                 :        1217 : g_crash_handler_win32_init (void)
    1342                 :             : {
    1343                 :             :   wchar_t      debugger_env[DEBUGGER_BUFFER_SIZE];
    1344                 :             : #define CATCH_BUFFER_SIZE 1024
    1345                 :             :   wchar_t      catch_buffer[CATCH_BUFFER_SIZE];
    1346                 :             :   SECURITY_ATTRIBUTES  sa;
    1347                 :             : 
    1348                 :        1217 :   if (WinVEH_handle != NULL)
    1349                 :           0 :     return;
    1350                 :             : 
    1351                 :             :   /* Do not register an exception handler if we're not supposed to catch any
    1352                 :             :    * exceptions. Exception handlers are considered dangerous to use, and can
    1353                 :             :    * break advanced exception handling such as in CLRs like C# or other managed
    1354                 :             :    * code. See: http://www.windows-tech.info/13/785f590867bd6316.php
    1355                 :             :    */
    1356                 :        1217 :   debugger_env[0] = 0;
    1357                 :        1217 :   if (!GetEnvironmentVariableW (L"G_DEBUGGER", debugger_env, DEBUGGER_BUFFER_SIZE))
    1358                 :           4 :     return;
    1359                 :             : 
    1360                 :             :   /* Create an inheritable event */
    1361                 :        1213 :   memset (&sa, 0, sizeof (sa));
    1362                 :        1213 :   sa.nLength = sizeof (sa);
    1363                 :        1213 :   sa.bInheritHandle = TRUE;
    1364                 :        1213 :   debugger_wakeup_event = CreateEvent (&sa, FALSE, FALSE, NULL);
    1365                 :             : 
    1366                 :             :   /* Put process ID and event handle into debugger commandline */
    1367                 :        1213 :   if (!g_win32_substitute_pid_and_event (debugger, G_N_ELEMENTS (debugger),
    1368                 :        1213 :                                          debugger_env, GetCurrentProcessId (),
    1369                 :        1213 :                                          (guintptr) debugger_wakeup_event))
    1370                 :             :     {
    1371                 :           0 :       CloseHandle (debugger_wakeup_event);
    1372                 :           0 :       debugger_wakeup_event = 0;
    1373                 :           0 :       debugger[0] = 0;
    1374                 :           0 :       return;
    1375                 :             :     }
    1376                 :        1213 :   debugger[MAX_PATH] = L'\0';
    1377                 :             : 
    1378                 :        1213 :   catch_buffer[0] = 0;
    1379                 :        1213 :   if (GetEnvironmentVariableW (L"G_VEH_CATCH", catch_buffer, CATCH_BUFFER_SIZE))
    1380                 :             :     {
    1381                 :           0 :       number_of_exceptions_to_catch = parse_catch_list (catch_buffer, NULL, 0);
    1382                 :           0 :       if (number_of_exceptions_to_catch > 0)
    1383                 :             :         {
    1384                 :           0 :           exceptions_to_catch = g_new0 (DWORD, number_of_exceptions_to_catch);
    1385                 :           0 :           parse_catch_list (catch_buffer, exceptions_to_catch, number_of_exceptions_to_catch);
    1386                 :           0 :         }
    1387                 :           0 :     }
    1388                 :             : 
    1389                 :        1213 :   if (GetEnvironmentVariableW (L"G_DEBUGGER_OLD_CONSOLE", (wchar_t *) &debugger_spawn_flags, 1))
    1390                 :           2 :     debugger_spawn_flags = 0;
    1391                 :             :   else
    1392                 :        1211 :     debugger_spawn_flags = CREATE_NEW_CONSOLE;
    1393                 :             : 
    1394                 :        1213 :   WinVEH_handle = AddVectoredExceptionHandler (0, &g_win32_veh_handler);
    1395                 :        1217 : }
    1396                 :             : 
    1397                 :             : void
    1398                 :        1214 : g_crash_handler_win32_deinit (void)
    1399                 :             : {
    1400                 :        1214 :   if (WinVEH_handle != NULL)
    1401                 :        1210 :     RemoveVectoredExceptionHandler (WinVEH_handle);
    1402                 :             : 
    1403                 :        1214 :   WinVEH_handle = NULL;
    1404                 :        1214 : }
    1405                 :             : 
    1406                 :             : /**
    1407                 :             :  * g_win32_find_helper_executable_path:
    1408                 :             :  * @executable_name: (transfer none): name of the helper executable to find
    1409                 :             :  * (something like gspawn-win64-helper.exe or gdbus.exe for example).
    1410                 :             :  * @dll_handle: handle of the DLL to use as searching base path. Pass NULL
    1411                 :             :  * to take current process executable as searching base path.
    1412                 :             :  *
    1413                 :             :  * Find an external executable path and name starting in the same folder
    1414                 :             :  * as a specified DLL or current process executable path. Helper executables
    1415                 :             :  * (like gspawn-win64-helper.exe, gspawn-win64-helper-console.exe or
    1416                 :             :  * gdbus.exe for example) are generally installed in the same folder as the
    1417                 :             :  * corresponding DLL file.
    1418                 :             :  *
    1419                 :             :  * So, if package has been correctly installed, with a dynamic build of GLib,
    1420                 :             :  * the helper executable should be in the same directory as the corresponding
    1421                 :             :  * DLL file and searching should be straightforward.
    1422                 :             :  *
    1423                 :             :  * But if built statically, DLL handle is not available and we have to start
    1424                 :             :  * searching from the directory holding current executable. It may be very
    1425                 :             :  * different from the directory containing the helper program. In order to
    1426                 :             :  * find the right helper program automatically in all common situations, we
    1427                 :             :  * use this pattern:
    1428                 :             :  *
    1429                 :             :  * current directory
    1430                 :             :  *             |-- ???
    1431                 :             :  *             |-- bin
    1432                 :             :  *             |    |-- ???
    1433                 :             :  *             |-- lib
    1434                 :             :  *             |    |-- ???
    1435                 :             :  *             |-- glib
    1436                 :             :  *             |    |-- ???
    1437                 :             :  *             |-- gio
    1438                 :             :  *                  |-- ???
    1439                 :             :  *
    1440                 :             :  * starting at base searching path (DLL or current executable directory) and
    1441                 :             :  * getting up until the root path. If we cannot still find the helper program,
    1442                 :             :  * we'll rely on PATH as the last resort.
    1443                 :             :  *
    1444                 :             :  * Returns: (transfer full) (type filename) (nullable): the helper executable
    1445                 :             :  * path and name in the GLib filename encoding or NULL in case of error. It
    1446                 :             :  * should be deallocated with g_free().
    1447                 :             :  */
    1448                 :             : gchar *
    1449                 :         437 : _g_win32_find_helper_walk_up (gchar *base_searching_path,
    1450                 :             :                               const gchar *executable_name)
    1451                 :             : {
    1452                 :             :   static const gchar *const subdirs[] = { "", "bin", "lib", "glib", "gio" };
    1453                 :             :   static const gsize nb_subdirs = G_N_ELEMENTS (subdirs);
    1454                 :             : 
    1455                 :             :   gchar *p;
    1456                 :         437 :   gchar *executable_path = NULL;
    1457                 :             :   gsize i;
    1458                 :             : 
    1459                 :         451 :   for (;;)
    1460                 :             :     {
    1461                 :             :       /* Search in subdirectories */
    1462                 :         562 :       for (i = 0; i < nb_subdirs; ++i)
    1463                 :             :         {
    1464                 :             :           /* As this function is exclusively used on Windows, the
    1465                 :             :            * executable_path is always an absolute path. At worse, when
    1466                 :             :            * reaching the root of the filesystem, base_searching_path may
    1467                 :             :            * equal something like "[Drive letter]:" but never "/" like on
    1468                 :             :            * Linux or Mac.
    1469                 :             :            * For the peace of mind we still assert this, just in case that
    1470                 :             :            * one day someone tries to use this function on Linux or Mac.
    1471                 :             :            */
    1472                 :         540 :           executable_path = g_build_filename (base_searching_path, subdirs[i], executable_name, NULL);
    1473                 :         540 :           g_assert (g_path_is_absolute (executable_path));
    1474                 :         540 :           if (g_file_test (executable_path, G_FILE_TEST_IS_REGULAR))
    1475                 :         429 :             break;
    1476                 :             : 
    1477                 :         111 :           g_free (executable_path);
    1478                 :         111 :           executable_path = NULL;
    1479                 :         111 :         }
    1480                 :             : 
    1481                 :         451 :       if (executable_path != NULL)
    1482                 :         429 :         break;
    1483                 :             : 
    1484                 :             :       /* Let's get one directory level up */
    1485                 :          22 :       p = strrchr (base_searching_path, G_DIR_SEPARATOR);
    1486                 :          22 :       if (p == NULL)
    1487                 :           4 :         break;
    1488                 :             : 
    1489                 :             :       /* Don't strip past the root of a UNC path ("\\\\server\\share").
    1490                 :             :        * For drive-letter paths ("C:") the strrchr() == NULL check above
    1491                 :             :        * already prevents over-stripping. UNC paths start with a
    1492                 :             :        * separator, so without this guard the loop strips past
    1493                 :             :        * "\\server\\share" down to "\\server", "\", and finally "" (empty),
    1494                 :             :        * which makes g_build_filename() below produce a non-absolute path
    1495                 :             :        * and trips the g_assert() above. The separator we're about to strip
    1496                 :             :        * is the first one after the server name, i.e. the one preceding the
    1497                 :             :        * share name.
    1498                 :             :        */
    1499                 :          18 :       if (G_IS_DIR_SEPARATOR (base_searching_path[0]) &&
    1500                 :          18 :           G_IS_DIR_SEPARATOR (base_searching_path[1]) &&
    1501                 :          18 :           strchr (base_searching_path + 2, G_DIR_SEPARATOR) == p)
    1502                 :           4 :         break;
    1503                 :             : 
    1504                 :          14 :       *p = '\0';
    1505                 :             :     }
    1506                 :             : 
    1507                 :         437 :   g_free (base_searching_path);
    1508                 :         437 :   return executable_path;
    1509                 :             : }
    1510                 :             : 
    1511                 :             : gchar *
    1512                 :         428 : g_win32_find_helper_executable_path (const gchar *executable_name, void *dll_handle)
    1513                 :             : {
    1514                 :             :   DWORD module_path_len;
    1515                 :         428 :   wchar_t module_path[MAX_PATH + 2] = { 0 };
    1516                 :             :   gchar *base_searching_path;
    1517                 :             :   gchar *p;
    1518                 :             :   gchar *executable_path;
    1519                 :             : 
    1520                 :         428 :   g_return_val_if_fail (executable_name && *executable_name, NULL);
    1521                 :             : 
    1522                 :         428 :   module_path_len = GetModuleFileNameW (dll_handle, module_path, MAX_PATH + 1);
    1523                 :             :   /* The > MAX_PATH check prevents truncated module path usage */
    1524                 :         428 :   if (module_path_len == 0 || module_path_len > MAX_PATH)
    1525                 :           0 :     return NULL;
    1526                 :             : 
    1527                 :         428 :   base_searching_path = g_utf16_to_utf8 (module_path, -1, NULL, NULL, NULL);
    1528                 :         428 :   if (base_searching_path == NULL)
    1529                 :           0 :     return NULL;
    1530                 :             : 
    1531                 :         428 :   p = strrchr (base_searching_path, G_DIR_SEPARATOR);
    1532                 :         428 :   if (p == NULL)
    1533                 :             :     {
    1534                 :           0 :       g_free (base_searching_path);
    1535                 :           0 :       return NULL;
    1536                 :             :     }
    1537                 :         428 :   *p = '\0';
    1538                 :             : 
    1539                 :         428 :   executable_path = _g_win32_find_helper_walk_up (base_searching_path, executable_name);
    1540                 :             : 
    1541                 :         428 :   if (executable_path == NULL)
    1542                 :             :     {
    1543                 :             :       /* Search in system PATH */
    1544                 :           0 :       executable_path = g_find_program_in_path (executable_name);
    1545                 :           0 :       if (executable_path == NULL)
    1546                 :           0 :         executable_path = g_strdup (executable_name);
    1547                 :           0 :     }
    1548                 :             : 
    1549                 :         428 :   return executable_path;
    1550                 :         428 : }
    1551                 :             : 
    1552                 :             : /** < private >
    1553                 :             :  *
    1554                 :             :  * g_win32_file_stream_is_console_output:
    1555                 :             :  *
    1556                 :             :  * @stream: a FILE stream
    1557                 :             :  *
    1558                 :             :  * Checks if the given FILE stream refers to a Win32 console
    1559                 :             :  * screen buffer.
    1560                 :             :  */
    1561                 :             : bool
    1562                 :       27656 : g_win32_file_stream_is_console_output (FILE *stream)
    1563                 :             : {
    1564                 :       27656 :   int fd = _fileno (stream);
    1565                 :             : 
    1566                 :       27656 :   if (fd < 0)
    1567                 :             :     {
    1568                 :             :       /* On Windows, FILE streams can be 'open' but not associated
    1569                 :             :        * with any file descriptor. As far as I know, that can only
    1570                 :             :        * happen for the standard streams stdin, stdout, and stderr.
    1571                 :             :        * Window processes can have NULL standard HANDLEs, but the
    1572                 :             :        * C standard states that standard streams must be 'open'
    1573                 :             :        * when main is called. So, on Windows, _fileno() is expected
    1574                 :             :        * to return values < 0 even without errors.
    1575                 :             :        */
    1576                 :           0 :       return false;
    1577                 :             :     }
    1578                 :             : 
    1579                 :             :   /* We call _isatty() first because it's a very fast check (just
    1580                 :             :    * checking an internal flag). However, the _isatty check comprehends
    1581                 :             :    * output devices like serial ports, so we check if the output is
    1582                 :             :    * really a win32 console with g_win32_handle_is_console_output.
    1583                 :             :    */
    1584                 :       27723 :   return _isatty (fd) &&
    1585                 :          67 :          g_win32_handle_is_console_output ((HANDLE)_get_osfhandle (fd));
    1586                 :       27656 : }
    1587                 :             : 
    1588                 :             : /** < private >
    1589                 :             :  *
    1590                 :             :  * g_win32_handle_is_console_output:
    1591                 :             :  *
    1592                 :             :  * @handle: the given HANDLE
    1593                 :             :  *
    1594                 :             :  * Checks if the given HANDLE refers to a Win32 console screen
    1595                 :             :  * buffer (output HANDLE).
    1596                 :             :  */
    1597                 :             : bool
    1598                 :          72 : g_win32_handle_is_console_output (HANDLE handle)
    1599                 :             : {
    1600                 :             :   /* MSDN suggests using GetConsoleMode() to check if a HANDLE refers to
    1601                 :             :    * the console. However GetConsoleMode() requires read access rights
    1602                 :             :    * (FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA on Windows 10),
    1603                 :             :    * and output HANDLEs may have been opened with write rights only. To
    1604                 :             :    * overcome that, we use WriteConsole() with a zero characters count.
    1605                 :             :    */
    1606                 :          72 :   const wchar_t *dummy = L"";
    1607                 :          72 :   if (!WriteConsole (handle, dummy, 0, NULL, NULL))
    1608                 :             :     {
    1609                 :          70 :       DWORD code = GetLastError ();
    1610                 :             : 
    1611                 :          70 :       if (code != ERROR_INVALID_FUNCTION && code != ERROR_INVALID_HANDLE)
    1612                 :             :         {
    1613                 :           0 :           WIN32_API_FAILED ("WriteConsole");
    1614                 :           0 :         }
    1615                 :             : 
    1616                 :          70 :       return false;
    1617                 :             :     }
    1618                 :             : 
    1619                 :           2 :   return true;
    1620                 :          72 : }
    1621                 :             : 
    1622                 :             : /*
    1623                 :             :  * g_win32_handle_is_socket:
    1624                 :             :  * @h: a win32 HANDLE
    1625                 :             :  *
    1626                 :             :  * Returns: %TRUE if the handle is a `SOCKET`.
    1627                 :             :  */
    1628                 :             : gboolean
    1629                 :        2164 : g_win32_handle_is_socket (HANDLE h)
    1630                 :             : {
    1631                 :        2164 :   int option = 0;
    1632                 :        2164 :   int optlen = sizeof (option);
    1633                 :             : 
    1634                 :             :   /* according to: https://stackoverflow.com/a/50981652/1277510, this is reasonable */
    1635                 :        2164 :   if (getsockopt ((SOCKET) h, SOL_SOCKET, SO_DEBUG, (char *) &option, &optlen) == SOCKET_ERROR)
    1636                 :        2142 :     return FALSE;
    1637                 :             : 
    1638                 :          22 :   return TRUE;
    1639                 :        2164 : }
    1640                 :             : 
    1641                 :             : /*
    1642                 :             :  * g_win32_reopen_noninherited:
    1643                 :             :  * @fd: (transfer full): A file descriptor
    1644                 :             :  * @mode: _open_osfhandle flags
    1645                 :             :  * @error: A location to return an error of type %G_FILE_ERROR
    1646                 :             :  *
    1647                 :             :  * Reopen the given @fd with `_O_NOINHERIT`.
    1648                 :             :  *
    1649                 :             :  * The @fd is closed on success.
    1650                 :             :  *
    1651                 :             :  * Returns: (transfer full): The new file-descriptor, or -1 on error.
    1652                 :             :  */
    1653                 :             : int
    1654                 :        2138 : g_win32_reopen_noninherited (int fd,
    1655                 :             :                              int mode,
    1656                 :             :                              GError **error)
    1657                 :             : {
    1658                 :             :   HANDLE h;
    1659                 :             :   HANDLE duph;
    1660                 :             :   int dupfd, errsv;
    1661                 :             : 
    1662                 :        2138 :   h = (HANDLE) _get_osfhandle (fd);
    1663                 :        2138 :   errsv = errno;
    1664                 :             : 
    1665                 :        2138 :   if (h == INVALID_HANDLE_VALUE)
    1666                 :             :     {
    1667                 :           0 :       const char *emsg = g_strerror (errsv);
    1668                 :           0 :       g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
    1669                 :           0 :                    "_get_osfhandle() failed: %s", emsg);
    1670                 :           0 :       return -1;
    1671                 :             :     }
    1672                 :             : 
    1673                 :        2138 :   if (g_win32_handle_is_socket (h))
    1674                 :             :     {
    1675                 :             :       WSAPROTOCOL_INFO info;
    1676                 :             : 
    1677                 :           0 :       if (WSADuplicateSocket ((SOCKET) h,
    1678                 :           0 :                               GetCurrentProcessId (),
    1679                 :             :                               &info))
    1680                 :             :         {
    1681                 :           0 :           gchar *emsg = g_win32_error_message (WSAGetLastError ());
    1682                 :           0 :           g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
    1683                 :           0 :                        "WSADuplicateSocket() failed: %s", emsg);
    1684                 :           0 :           g_free (emsg);
    1685                 :           0 :           return -1;
    1686                 :             :         }
    1687                 :             : 
    1688                 :           0 :       duph = (HANDLE) WSASocket (FROM_PROTOCOL_INFO,
    1689                 :             :                                  FROM_PROTOCOL_INFO,
    1690                 :             :                                  FROM_PROTOCOL_INFO,
    1691                 :             :                                  &info, 0, 0);
    1692                 :           0 :       if (duph == (HANDLE) INVALID_SOCKET)
    1693                 :             :         {
    1694                 :           0 :           gchar *emsg = g_win32_error_message (WSAGetLastError ());
    1695                 :           0 :           g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
    1696                 :           0 :                        "WSASocket() failed: %s", emsg);
    1697                 :           0 :           g_free (emsg);
    1698                 :           0 :           return -1;
    1699                 :             :         }
    1700                 :           0 :     }
    1701                 :        4276 :   else if (DuplicateHandle (GetCurrentProcess (), h,
    1702                 :        2138 :                             GetCurrentProcess (), &duph,
    1703                 :        2138 :                             0, FALSE, DUPLICATE_SAME_ACCESS) == 0)
    1704                 :             :     {
    1705                 :           0 :       char *emsg = g_win32_error_message (GetLastError ());
    1706                 :           0 :       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
    1707                 :           0 :                    "DuplicateHandle() failed: %s", emsg);
    1708                 :           0 :       g_free (emsg);
    1709                 :           0 :       return -1;
    1710                 :             :     }
    1711                 :             : 
    1712                 :             :   /* the duph ownership is transferred to dupfd */
    1713                 :        2138 :   dupfd = _open_osfhandle ((gintptr) duph, mode | _O_NOINHERIT);
    1714                 :        2138 :   if (dupfd < 0)
    1715                 :             :     {
    1716                 :           0 :       g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
    1717                 :             :                            "_open_osfhandle() failed");
    1718                 :           0 :       CloseHandle (duph);
    1719                 :           0 :       return -1;
    1720                 :             :     }
    1721                 :             : 
    1722                 :        2138 :   if (!g_close (fd, error))
    1723                 :             :     {
    1724                 :             :       /* ignore extra errors in this case */
    1725                 :           0 :       g_close (dupfd, NULL);
    1726                 :           0 :       return -1;
    1727                 :             :     }
    1728                 :             : 
    1729                 :        2138 :   return dupfd;
    1730                 :        2138 : }
    1731                 :             : 
    1732                 :             : bool
    1733                 :           2 : g_win32_error_message_in_place (DWORD    code,
    1734                 :             :                                 wchar_t *buffer,
    1735                 :             :                                 size_t   wchars_count)
    1736                 :             : {
    1737                 :           2 :   const DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM |
    1738                 :             :                       FORMAT_MESSAGE_IGNORE_INSERTS;
    1739                 :             :   DWORD wchars_written;
    1740                 :             : 
    1741                 :           2 :   g_assert (wchars_count > 0);
    1742                 :             : 
    1743                 :           2 :   wchars_written = FormatMessage (flags, NULL, code, 0, buffer, wchars_count - 1, NULL);
    1744                 :           2 :   if (wchars_written == 0)
    1745                 :           1 :     return false;
    1746                 :             : 
    1747                 :           1 :   g_assert (wchars_written < wchars_count);
    1748                 :             : 
    1749                 :           1 :   if (wchars_written >= 2)
    1750                 :             :     {
    1751                 :           2 :       wchars_written -= (buffer[wchars_written - 1] == L'\n') +
    1752                 :           1 :                         (buffer[wchars_written - 2] == L'\r');
    1753                 :           1 :     }
    1754                 :             : 
    1755                 :           1 :   buffer[wchars_written] = L'\0';
    1756                 :             : 
    1757                 :           1 :   return true;
    1758                 :           2 : }
    1759                 :             : 
    1760                 :             : /** < private >
    1761                 :             :  *
    1762                 :             :  * g_win32_api_failed:
    1763                 :             :  *
    1764                 :             :  * @where: location in the source code
    1765                 :             :  * @api: name of the failing API
    1766                 :             :  * @code: a Windows error code or a failing HRESULT
    1767                 :             :  *
    1768                 :             :  * Prints a warning about the failing API with an extended
    1769                 :             :  * error description (see g_win32_error_message).
    1770                 :             :  */
    1771                 :             : void
    1772                 :           0 : g_win32_api_failed_with_code (const char *where,
    1773                 :             :                               const char *api,
    1774                 :             :                               DWORD       code)
    1775                 :             : {
    1776                 :             :   wchar_t description[500];
    1777                 :             : 
    1778                 :           0 :   if (g_win32_error_message_in_place (code, description, G_N_ELEMENTS (description)))
    1779                 :           0 :     g_warning ("%s failed: %S", api, description);
    1780                 :             :   else
    1781                 :           0 :     g_warning ("%s failed with error code %u", api, (unsigned int) code);
    1782                 :           0 : }
    1783                 :             : 
    1784                 :             : /** < private >
    1785                 :             :  *
    1786                 :             :  * g_win32_api_failed:
    1787                 :             :  *
    1788                 :             :  * @where: location in the source code
    1789                 :             :  * @api: name of the failing API
    1790                 :             :  *
    1791                 :             :  * Prints a warning about the failing API with an extended
    1792                 :             :  * error description (see g_win32_error_message).
    1793                 :             :  */
    1794                 :             : void
    1795                 :           0 : g_win32_api_failed (const char *where,
    1796                 :             :                     const char *api)
    1797                 :             : {
    1798                 :           0 :   g_win32_api_failed_with_code (where, api, GetLastError ());
    1799                 :           0 : }
        

Generated by: LCOV version 2.0-1