<commit_before>/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1998  <NAME>, <NAME> and <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
 */

/* 
 * MT safe for the unix part, FIXME: make the win32 part MT safe as well.
 */

#include "config.h"

#include "gutils.h"

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <ctype.h>		/* For tolower() */
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef G_OS_UNIX
#include <pwd.h>
#include <unistd.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_CRT_EXTERNS_H 
#include <crt_externs.h> /* for _NSGetEnviron */
#endif
#ifdef HAVE_SYS_AUXV_H
#include <sys/auxv.h>
#endif

#include "glib-init.h"
#include "glib-private.h"
#include "genviron.h"
#include "gfileutils.h"
#include "ggettext.h"
#include "ghash.h"
#include "gthread.h"
#include "gtestutils.h"
#include "gunicode.h"
#include "gstrfuncs.h"
#include "garray.h"
#include "glibintl.h"
#include "gstdio.h"

#ifdef G_PLATFORM_WIN32
#include "gconvert.h"
#include "gwin32.h"
#endif


/**
 * SECTION:misc_utils
 * @title: Miscellaneous Utility Functions
 * @short_description: a selection of portable utility functions
 *
 * These are portable utility functions.
 */

#ifdef G_PLATFORM_WIN32
#  include <windows.h>
#  ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
#    define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
#    define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
#  endif
#  include <lmcons.h>		/* For UNLEN */
#endif /* G_PLATFORM_WIN32 */

#ifdef G_OS_WIN32
#  include <direct.h>
#  include <shlobj.h>
   /* older SDK (e.g. msvc 5.0) does not have these*/
#  ifndef CSIDL_MYMUSIC
#    define CSIDL_MYMUSIC 13
#  endif
#  ifndef CSIDL_MYVIDEO
#    define CSIDL_MYVIDEO 14
#  endif
#  ifndef CSIDL_INTERNET_CACHE
#    define CSIDL_INTERNET_CACHE 32
#  endif
#  ifndef CSIDL_COMMON_APPDATA
#    define CSIDL_COMMON_APPDATA 35
#  endif
#  ifndef CSIDL_MYPICTURES
#    define CSIDL_MYPICTURES 0x27
#  endif
#  ifndef CSIDL_COMMON_DOCUMENTS
#    define CSIDL_COMMON_DOCUMENTS 46
#  endif
#  ifndef CSIDL_PROFILE
#    define CSIDL_PROFILE 40
#  endif
#  include <process.h>
#endif

#ifdef HAVE_CARBON
#include <CoreServices/CoreServices.h>
#endif

#ifdef HAVE_CODESET
#include <langinfo.h>
#endif

#ifdef G_PLATFORM_WIN32

gchar *
_glib_get_dll_directory (void)
{
  gchar *retval;
  gchar *p;
  wchar_t wc_fn[MAX_PATH];

#ifdef DLL_EXPORT
  if (glib_dll == NULL)
    return NULL;
#endif

  /* This code is different from that in
   * g_win32_get_package_installation_directory_of_module() in that
   * here we return the actual folder where the GLib DLL is. We don't
   * do the check for it being in a "bin" or "lib" subfolder and then
   * returning the parent of that.
   *
   * In a statically built GLib, glib_dll will be NULL and we will
   * thus look up the application's .exe file's location.
   */
  if (!GetModuleFileNameW (glib_dll, wc_fn, MAX_PATH))
    return NULL;

  retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);

  p = strrchr (retval, G_DIR_SEPARATOR);
  if (p == NULL)
    {
      /* Wtf? */
      return NULL;
    }
  *p = '\0';

  return retval;
}

#endif

/**
 * g_memmove: 
 * @dest: the destination address to copy the bytes to.
 * @src: the source address to copy the bytes from.
 * @len: the number of bytes to copy.
 *
 * Copies a block of memory @len bytes long, from @src to @dest.
 * The source and destination areas may overlap.
 *
 * Deprecated:2.40: Just use memmove().
 */

#ifdef G_OS_WIN32
#undef g_atexit
#endif

/**
 * g_atexit:
 * @func: (scope async): the function to call on normal program termination.
 * 
 * Specifies a function to be called at normal program termination.
 *
 * Since GLib 2.8.2, on Windows g_atexit() actually is a preprocessor
 * macro that maps to a call to the atexit() function in the C
 * library. This means that in case the code that calls g_atexit(),
 * i.e. atexit(), is in a DLL, the function will be called when the
 * DLL is detached from the program. This typically makes more sense
 * than that the function is called when the GLib DLL is detached,
 * which happened earlier when g_atexit() was a function in the GLib
 * DLL.
 *
 * The behaviour of atexit() in the context of dynamically loaded
 * modules is not formally specified and varies wildly.
 *
 * On POSIX systems, calling g_atexit() (or atexit()) in a dynamically
 * loaded module which is unloaded before the program terminates might
 * well cause a crash at program exit.
 *
 * Some POSIX systems implement atexit() like Windows, and have each
 * dynamically loaded module maintain an own atexit chain that is
 * called when the module is unloaded.
 *
 * On other POSIX systems, before a dynamically loaded module is
 * unloaded, the registered atexit functions (if any) residing in that
 * module are called, regardless where the code that registered them
 * resided. This is presumably the most robust approach.
 *
 * As can be seen from the above, for portability it's best to avoid
 * calling g_atexit() (or atexit()) except in the main executable of a
 * program.
 *
 * Deprecated:2.32: It is best to avoid g_atexit().
 */
void
g_atexit (GVoidFunc func)
{
  gint result;
  int errsv;

  result = atexit ((void (*)(void)) func);
  errsv = errno;
  if (result)
    {
      g_error ("Could not register atexit() function: %s",
               g_strerror (errsv));
    }
}

/* Based on execvp() from GNU Libc.
 * Some of this code is cut-and-pasted into gspawn.c
 */

static gchar*
my_strchrnul (const gchar *str, 
	      gchar        c)
{
  gchar *p = (gchar*)str;
  while (*p && (*p != c))
    ++p;

  return p;
}

#ifdef G_OS_WIN32

static gchar *inner_find_program_in_path (const gchar *program);

gchar*
g_find_program_in_path (const gchar *program)
{
  const gchar *last_dot = strrchr (program, '.');

  if (last_dot == NULL ||
      strchr (last_dot, '\\') != NULL ||
      strchr (last_dot, '/') != NULL)
    {
      const gint program_length = strlen (program);
      gchar *pathext = g_build_path (";",
				     ".exe;.cmd;.bat;.com",
				     g_getenv ("PATHEXT"),
				     NULL);
      gchar *p;
      gchar *decorated_program;
      gchar *retval;

      p = pathext;
      do
	{
	  gchar *q = my_strchrnul (p, ';');

	  decorated_program = g_malloc (program_length + (q-p) + 1);
	  memcpy (decorated_program, program, program_length);
	  memcpy (decorated_program+program_length, p, q-p);
	  decorated_program [program_length + (q-p)] = '\0';
	  
	  retval = inner_find_program_in_path (decorated_program);
	  g_free (decorated_program);

	  if (retval != NULL)
	    {
	      g_free (pathext);
	      return retval;
	    }
	  p = q;
	} while (*p++ != '\0');
      g_free (pathext);
      return NULL;
    }
  else
    return inner_find_program_in_path (program);
}

#endif

/**
 * g_find_program_in_path:
 * @program: (type filename): a program name in the GLib file name encoding
 * 
 * Locates the first executable named @program in the user's path, in the
 * same way that execvp() would locate it. Returns an allocated string
 * with the absolute path name, or %NULL if the program is not found in
 * the path. If @program is already an absolute path, returns a copy of
 * @program if @program exists and is executable, and %NULL otherwise.
 *  
 * On Windows, if @program does not have a file type suffix, tries
 * with the suffixes .exe, .cmd, .bat and .com, and the suffixes in
 * the `PATHEXT` environment variable. 
 * 
 * On Windows, it looks for the file in the same way as CreateProcess() 
 * would. This means first in the directory where the executing
 * program was loaded from, then in the current directory, then in the
 * Windows 32-bit system directory, then in the Windows directory, and
 * finally in the directories in the `PATH` environment variable. If
 * the program is found, the return value contains the full name
 * including the type suffix.
 *
 * Returns: (type filename): a newly-allocated string with the absolute path,
 *     or %NULL
 **/
#ifdef G_OS_WIN32
static gchar *
inner_find_program_in_path (const gchar *program)
#else
gchar*
g_find_program_in_path (const gchar *program)
#endif
{
  const gchar *path, *p;
  gchar *name, *freeme;
#ifdef G_OS_WIN32
  const gchar *path_copy;
  gchar *filename = NULL, *appdir = NULL;
  gchar *sysdir = NULL, *windir = NULL;
  int n;
  wchar_t wfilename[MAXPATHLEN], wsysdir[MAXPATHLEN],
    wwindir[MAXPATHLEN];
#endif
  gsize len;
  gsize pathlen;

  g_return_val_if_fail (program != NULL, NULL);

  /* If it is an absolute path, or a relative path including subdirectories,
   * don't look in PATH.
   */
  if (g_path_is_absolute (program)
      || strchr (program, G_DIR_SEPARATOR) != NULL
#ifdef G_OS_WIN32
      || strchr (program, '/') != NULL
#endif
      )
    {
      if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE) &&
	  !g_file_test (program, G_FILE_TEST_IS_DIR))
        return g_strdup (program);
      else
        return NULL;
    }
  
  path = g_getenv ("PATH");
#if defined(G_OS_UNIX)
  if (path == NULL)
    {
      /* There is no 'PATH' in the environment.  The default
       * search path in GNU libc is the current directory followed by
       * the path 'confstr' returns for '_CS_PATH'.
       */
      
      /* In GLib we put . last, for security, and don't use the
       * unportable confstr(); UNIX98 does not actually specify
       * what to search if PATH is unset. POSIX may, dunno.
       */
      
      path = "/bin:/usr/bin:.";
    }
#else
  n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
  if (n > 0 && n < MAXPATHLEN)
    filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
  
  n = GetSystemDirectoryW (wsysdir, MAXPATHLEN);
  if (n > 0 && n < MAXPATHLEN)
    sysdir = g_utf16_to_utf8 (wsysdir, -1, NULL, NULL, NULL);
  
  n = GetWindowsDirectoryW (wwindir, MAXPATHLEN);
  if (n > 0 && n < MAXPATHLEN)
    windir = g_utf16_to_utf8 (wwindir, -1, NULL, NULL, NULL);
  
  if (filename)
    {
      appdir = g_path_get_dirname (filename);
      g_free (filename);
    }
  
  path = g_strdup (path);

  if (windir)
    {
      const gchar *tem = path;
      path = g_strconcat (windir, ";", path, NULL);
      g_free ((gchar *) tem);
      g_free (windir);
    }
  
  if (sysdir)
    {
      const gchar *tem = path;
      path = g_strconcat (sysdir, ";", path, NULL);
      g_free ((gchar *) tem);
      g_free (sysdir);
    }
  
  {
    const gchar *tem = path;
    path = g_strconcat (".;", path, NULL);
    g_free ((gchar *) tem);
  }
  
  if (appdir)
    {
      const gchar *tem = path;
      path = g_strconcat (appdir, ";", path, NULL);
      g_free ((gchar *) tem);
      g_free (appdir);
    }

  path_copy = path;
#endif
  
  len = strlen (program) + 1;
  pathlen = strlen (path);
  freeme = name = g_malloc (pathlen + len + 1);
  
  /* Copy the file name at the top, including '\0'  */
  memcpy (name + pathlen + 1, program, len);
  name = name + pathlen;
  /* And add the slash before the filename  */
  *name = G_DIR_SEPARATOR;
  
  p = path;
  do
    {
      char *startp;

      path = p;
      p = my_strchrnul (path, G_SEARCHPATH_SEPARATOR);

      if (p == path)
        /* Two adjacent colons, or a colon at the beginning or the end
         * of 'PATH' means to search the current directory.
         */
        startp = name + 1;
      else
        startp = memcpy (name - (p - path), path, p - path);

      if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE) &&
	  !g_file_test (startp, G_FILE_TEST_IS_DIR))
        {
          gchar *ret;
          ret = g_strdup (startp);
          g_free (freeme);
#ifdef G_OS_WIN32
	  g_free ((gchar *) path_copy);
#endif
          return ret;
        }
    }
  while (*p++ != '\0');
  
  g_free (freeme);
#ifdef G_OS_WIN32
  g_free ((gchar *) path_copy);
#endif

  return NULL;
}

/* The functions below are defined this way for compatibility reasons.
 * See the note in gutils.h.
 */

/**
 * g_bit_nth_lsf:
 * @mask: a #gulong containing flags
 * @nth_bit: the index of the bit to start the search from
 *
 * Find the position of the first bit set in @mask, searching
 * from (but not including) @nth_bit upwards. Bits are numbered
 * from 0 (least significant) to sizeof(#gulong) * 8 - 1 (31 or 63,
 * usually). To start searching from the 0th bit, set @nth_bit to -1.
 *
 * Returns: the index of the first bit set which is higher than @nth_bit, or -1
 *    if no higher bits are set
 */
gint
(g_bit_nth_lsf) (gulong mask,
                 gint   nth_bit)
{
  return g_bit_nth_lsf_impl (mask, nth_bit);
}

/**
 * g_bit_nth_msf:
 * @mask: a #gulong containing flags
 * @nth_bit: the index of the bit to start the search from
 *
 * Find the position of the first bit set in @mask, searching
 * from (but not including) @nth_bit downwards. Bits are numbered
 * from 0 (least significant) to sizeof(#gulong) * 8 - 1 (31 or 63,
 * usually). To start searching from the last bit, set @nth_bit to
 * -1 or GLIB_SIZEOF_LONG * 8.
 *
 * Returns: the index of the first bit set which is lower than @nth_bit, or -1
 *    if no lower bits are set
 */
gint
(g_bit_nth_msf) (gulong mask,
                 gint   nth_bit)
{
  return g_bit_nth_msf_impl (mask, nth_bit);
}


/**
 * g_bit_storage:
 * @number: a #guint
 *
 * Gets the number of bits used to hold @number,
 * e.g. if @number is 4, 3 bits are needed.
 *
 * Returns: the number of bits used to hold @number
 */
guint
(g_bit_storage) (gulong number)
{
  return g_bit_storage_impl (number);
}

G_LOCK_DEFINE_STATIC (g_utils_global);

typedef struct
{
  gchar *user_name;
  gchar *real_name;
  gchar *home_dir;
} UserDatabaseEntry;

static  gchar   *g_user_data_dir = NULL;
static  gchar  **g_system_data_dirs = NULL;
static  gchar   *g_user_cache_dir = NULL;
static  gchar   *g_user_config_dir = NULL;
static  gchar   *g_user_runtime_dir = NULL;
static  gchar  **g_system_config_dirs = NULL;

static  gchar  **g_user_special_dirs = NULL;

/* fifteen minutes of fame for everybody */
#define G_USER_DIRS_EXPIRE      15 * 60

#ifdef G_OS_WIN32

static gchar *
get_special_folder (int csidl)
{
  wchar_t path[MAX_PATH+1];
  HRESULT hr;
  LPITEMIDLIST pidl = NULL;
  BOOL b;
  gchar *retval = NULL;

  hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
  if (hr == S_OK)
    {
      b = SHGetPathFromIDListW (pidl, path);
      if (b)
	retval = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL);
      CoTaskMemFree (pidl);
    }
  return retval;
}

static char *
get_windows_directory_root (void)
{
  wchar_t wwindowsdir[MAX_PATH];

  if (GetWindowsDirectoryW (wwindowsdir, G_N_ELEMENTS (wwindowsdir)))
    {
      /* Usually X:\Windows, but in terminal server environments
       * might be an UNC path, AFAIK.
       */
      char *windowsdir = g_utf16_to_utf8 (wwindowsdir, -1, NULL, NULL, NULL);
      char *p;

      if (windowsdir == NULL)
	return g_strdup ("C:\\");

      p = (char *) g_path_skip_root (windowsdir);
      if (G_IS_DIR_SEPARATOR (p[-1]) && p[-2] != ':')
	p--;
      *p = '\0';
      return windowsdir;
    }
  else
    return g_strdup ("C:\\");
}

#endif

/* HOLDS: g_utils_global_lock */
static UserDatabaseEntry *
g_get_user_database_entry (void)
{
  static UserDatabaseEntry *entry;

  if (g_once_init_enter (&entry))
    {
      static UserDatabaseEntry e;

#ifdef G_OS_UNIX
      {
        struct passwd *pw = NULL;
        gpointer buffer = NULL;
        gint error;
        gchar *logname;

#  if defined (HAVE_GETPWUID_R)
        struct passwd pwd;
#    ifdef _SC_GETPW_R_SIZE_MAX
        /* This reurns the maximum length */
        glong bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);

        if (bufsize < 0)
          bufsize = 64;
#    else /* _SC_GETPW_R_SIZE_MAX */
        glong bufsize = 64;
#    endif /* _SC_GETPW_R_SIZE_MAX */

        logname = (gchar *) g_getenv ("LOGNAME");

        do
          {
            g_free (buffer);
            /* we allocate 6 extra bytes to work around a bug in
             * Mac OS < 10.3. See #156446
             */
            buffer = g_malloc (bufsize + 6);
            errno = 0;

            if (logname) {
              error = getpwnam_r (logname, &pwd, buffer, bufsize, &pw);
              if (!pw || (pw->pw_uid != getuid ())) {
                /* LOGNAME is lying, fall back to looking up the uid */
                error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
              }
            } else {
              error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
            }
            error = error < 0 ? errno : error;

            if (!pw)
              {
                /* we bail out prematurely if the user id can't be found
                 * (should be pretty rare case actually), or if the buffer
                 * should be sufficiently big and lookups are still not
                 * successful.
                 */
                if (error == 0 || error == ENOENT)
                  {
                    g_warning ("getpwuid_r(): failed due to unknown user id (%lu)",
                               (gulong) getuid ());
                    break;
                  }
                if (bufsize > 32 * 1024)
                  {
                    g_warning ("getpwuid_r(): failed due to: %s.",
                               g_strerror (error));
                    break;
                  }

                bufsize *= 2;
              }
          }
        while (!pw);
#  endif /* HAVE_GETPWUID_R */

        if (!pw)
          {
            pw = getpwuid (getuid ());
          }
        if (pw)
          {
            e.user_name = g_strdup (pw->pw_name);

#ifndef __BIONIC__
            if (pw->pw_gecos && *pw->pw_gecos != '\0')
              {
                gchar **gecos_fields;
                gchar **name_parts;

                /* split the gecos field and substitute '&' */
                gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
                name_parts = g_strsplit (gecos_fields[0], "&", 0);
                pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
                e.real_name = g_strjoinv (pw->pw_name, name_parts);
                g_strfreev (gecos_fields);
                g_strfreev (name_parts);
              }
#endif

            if (!e.home_dir)
              e.home_dir = g_strdup (pw->pw_dir);
          }
        g_free (buffer);
      }

#endif /* G_OS_UNIX */

#ifdef G_OS_WIN32
      {
        guint len = UNLEN+1;
        wchar_t buffer[UNLEN+1];

        if (GetUserNameW (buffer, (LPDWORD) &len))
          {
            e.user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
            e.real_name = g_strdup (e.user_name);
          }
      }
#endif /* G_OS_WIN32 */

      if (!e.user_name)
        e.user_name = g_strdup ("somebody");
      if (!e.real_name)
        e.real_name = g_strdup ("Unknown");

      g_once_init_leave (&entry, &e);
    }

  return entry;
}

/**
 * g_get_user_name:
 *
 * Gets the user name of the current user. The encoding of the returned
 * string is system-defined. On UNIX, it might be the preferred file name
 * encoding, or something else, and there is no guarantee that it is even
 * consistent on a machine. On Windows, it is always UTF-8.
 *
 * Returns: (type filename): the user name of the current user.
 */
const gchar *
g_get_user_name (void)
{
  UserDatabaseEntry *entry;

  entry = g_get_user_database_entry ();

  return entry->user_name;
}

/**
 * g_get_real_name:
 *
 * Gets the real name of the user. This usually comes from the user's
 * entry in the `passwd` file. The encoding of the returned string is
 * system-defined. (On Windows, it is, however, always UTF-8.) If the
 * real user name cannot be determined, the string "Unknown" is 
 * returned.
 *
 * Returns: (type filename): the user's real name.
 */
const gchar *
g_get_real_name (void)
{
  UserDatabaseEntry *entry;

  entry = g_get_user_database_entry ();

  return entry->real_name;
}

/**
 * g_get_home_dir:
 *
 * Gets the current user's home directory.
 *
 * As with most UNIX tools, this function will return the value of the
 * `HOME` environment variable if it is set to an existing absolute path
 * name, falling back to the `passwd` file in the case that it is unset.
 *
 * If the path given in `HOME` is non-absolute, does not exist, or is
 * not a directory, the result is undefined.
 *
 * Before version 2.36 this function would ignore the `HOME` environment
 * variable, taking the value from the `passwd` database instead. This was
 * changed to increase the compatibility of GLib with other programs (and
 * the XDG basedir specification) and to increase testability of programs
 * based on GLib (by making it easier to run them from test frameworks).
 *
 * If your program has a strong requirement for either the new or the
 * old behaviour (and if you don't wish to increase your GLib
 * dependency to ensure that the new behaviour is in effect) then you
 * should either directly check the `HOME` environment variable yourself
 * or unset it before calling any functions in GLib.
 *
 * Returns: (type filename): the current user's home directory
 */
const gchar *
g_get_home_dir (void)
{
  static gchar *home_dir;

  if (g_once_init_enter (&home_dir))
    {
      gchar *tmp;

      /* We first check HOME and use it if it is set */
      tmp = g_strdup (g_getenv ("HOME"));

#ifdef G_OS_WIN32
      /* Only believe HOME if it is an absolute path and exists.
       *
       * We only do this check on Windows for a couple of reasons.
       * Historically, we only did it there because we used to ignore $HOME
       * on UNIX.  There are concerns about enabling it now on UNIX because
       * of things like autofs.  In short, if the user has a bogus value in
       * $HOME then they get what they pay for...
       */
      if (tmp)
        {
          if (!(g_path_is_absolute (tmp) &&
                g_file_test (tmp, G_FILE_TEST_IS_DIR)))
            {
              g_free (tmp);
              tmp = NULL;
            }
        }

      /* In case HOME is Unix-style (it happens), convert it to
       * Windows style.
       */
      if (tmp)
        {
          gchar *p;
          while ((p = strchr (tmp, '/')) != NULL)
            *p = '\\';
        }

      if (!tmp)
        {
          /* USERPROFILE is probably the closest equivalent to $HOME? */
          if (g_getenv ("USERPROFILE") != NULL)
            tmp = g_strdup (g_getenv ("USERPROFILE"));
        }

      if (!tmp)
        tmp = get_special_folder (CSIDL_PROFILE);

      if (!tmp)
        tmp = get_windows_directory_root ();
#endif /* G_OS_WIN32 */

      if (!tmp)
        {
          /* If we didn't get it from any of those methods, we will have
           * to read the user database entry.
           */
          UserDatabaseEntry *entry;

          entry = g_get_user_database_entry ();

          /* Strictly speaking, we should copy this, but we know that
           * neither will ever be freed, so don't bother...
           */
          tmp = entry->home_dir;
        }

      /* If we have been denied access to /etc/passwd (for example, by an
       * overly-zealous LSM), make up a junk value. The return value at this
       * point is explicitly documented as ‘undefined’. Memory management is as
       * immediately above: strictly this should be copied, but we know not
       * copying it is OK. */
      if (tmp == NULL)
        {
          g_warning ("Could not find home directory: $HOME is not set, and "
                     "user database could not be read.");
          tmp = "/";
        }

      g_once_init_leave (&home_dir, tmp);
    }

  return home_dir;
}

/**
 * g_get_tmp_dir:
 *
 * Gets the directory to use for temporary files.
 *
 * On UNIX, this is taken from the `TMPDIR` environment variable.
 * If the variable is not set, `P_tmpdir` is
 * used, as defined by the system C library. Failing that, a
 * hard-coded default of "/tmp" is returned.
 *
 * On Windows, the `TEMP` environment variable is used, with the
 * root directory of the Windows installation (eg: "C:\") used<commit_msg>gutils: Refactor g_get_home_dir() to use a global variable<commit_after>/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1998  <NAME>, <NAME> and <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
 */

/* 
 * MT safe for the unix part, FIXME: make the win32 part MT safe as well.
 */

#include "config.h"

#include "gutils.h"

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <ctype.h>		/* For tolower() */
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef G_OS_UNIX
#include <pwd.h>
#include <unistd.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_CRT_EXTERNS_H 
#include <crt_externs.h> /* for _NSGetEnviron */
#endif
#ifdef HAVE_SYS_AUXV_H
#include <sys/auxv.h>
#endif

#include "glib-init.h"
#include "glib-private.h"
#include "genviron.h"
#include "gfileutils.h"
#include "ggettext.h"
#include "ghash.h"
#include "gthread.h"
#include "gtestutils.h"
#include "gunicode.h"
#include "gstrfuncs.h"
#include "garray.h"
#include "glibintl.h"
#include "gstdio.h"

#ifdef G_PLATFORM_WIN32
#include "gconvert.h"
#include "gwin32.h"
#endif


/**
 * SECTION:misc_utils
 * @title: Miscellaneous Utility Functions
 * @short_description: a selection of portable utility functions
 *
 * These are portable utility functions.
 */

#ifdef G_PLATFORM_WIN32
#  include <windows.h>
#  ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
#    define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
#    define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
#  endif
#  include <lmcons.h>		/* For UNLEN */
#endif /* G_PLATFORM_WIN32 */

#ifdef G_OS_WIN32
#  include <direct.h>
#  include <shlobj.h>
   /* older SDK (e.g. msvc 5.0) does not have these*/
#  ifndef CSIDL_MYMUSIC
#    define CSIDL_MYMUSIC 13
#  endif
#  ifndef CSIDL_MYVIDEO
#    define CSIDL_MYVIDEO 14
#  endif
#  ifndef CSIDL_INTERNET_CACHE
#    define CSIDL_INTERNET_CACHE 32
#  endif
#  ifndef CSIDL_COMMON_APPDATA
#    define CSIDL_COMMON_APPDATA 35
#  endif
#  ifndef CSIDL_MYPICTURES
#    define CSIDL_MYPICTURES 0x27
#  endif
#  ifndef CSIDL_COMMON_DOCUMENTS
#    define CSIDL_COMMON_DOCUMENTS 46
#  endif
#  ifndef CSIDL_PROFILE
#    define CSIDL_PROFILE 40
#  endif
#  include <process.h>
#endif

#ifdef HAVE_CARBON
#include <CoreServices/CoreServices.h>
#endif

#ifdef HAVE_CODESET
#include <langinfo.h>
#endif

#ifdef G_PLATFORM_WIN32

gchar *
_glib_get_dll_directory (void)
{
  gchar *retval;
  gchar *p;
  wchar_t wc_fn[MAX_PATH];

#ifdef DLL_EXPORT
  if (glib_dll == NULL)
    return NULL;
#endif

  /* This code is different from that in
   * g_win32_get_package_installation_directory_of_module() in that
   * here we return the actual folder where the GLib DLL is. We don't
   * do the check for it being in a "bin" or "lib" subfolder and then
   * returning the parent of that.
   *
   * In a statically built GLib, glib_dll will be NULL and we will
   * thus look up the application's .exe file's location.
   */
  if (!GetModuleFileNameW (glib_dll, wc_fn, MAX_PATH))
    return NULL;

  retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);

  p = strrchr (retval, G_DIR_SEPARATOR);
  if (p == NULL)
    {
      /* Wtf? */
      return NULL;
    }
  *p = '\0';

  return retval;
}

#endif

/**
 * g_memmove: 
 * @dest: the destination address to copy the bytes to.
 * @src: the source address to copy the bytes from.
 * @len: the number of bytes to copy.
 *
 * Copies a block of memory @len bytes long, from @src to @dest.
 * The source and destination areas may overlap.
 *
 * Deprecated:2.40: Just use memmove().
 */

#ifdef G_OS_WIN32
#undef g_atexit
#endif

/**
 * g_atexit:
 * @func: (scope async): the function to call on normal program termination.
 * 
 * Specifies a function to be called at normal program termination.
 *
 * Since GLib 2.8.2, on Windows g_atexit() actually is a preprocessor
 * macro that maps to a call to the atexit() function in the C
 * library. This means that in case the code that calls g_atexit(),
 * i.e. atexit(), is in a DLL, the function will be called when the
 * DLL is detached from the program. This typically makes more sense
 * than that the function is called when the GLib DLL is detached,
 * which happened earlier when g_atexit() was a function in the GLib
 * DLL.
 *
 * The behaviour of atexit() in the context of dynamically loaded
 * modules is not formally specified and varies wildly.
 *
 * On POSIX systems, calling g_atexit() (or atexit()) in a dynamically
 * loaded module which is unloaded before the program terminates might
 * well cause a crash at program exit.
 *
 * Some POSIX systems implement atexit() like Windows, and have each
 * dynamically loaded module maintain an own atexit chain that is
 * called when the module is unloaded.
 *
 * On other POSIX systems, before a dynamically loaded module is
 * unloaded, the registered atexit functions (if any) residing in that
 * module are called, regardless where the code that registered them
 * resided. This is presumably the most robust approach.
 *
 * As can be seen from the above, for portability it's best to avoid
 * calling g_atexit() (or atexit()) except in the main executable of a
 * program.
 *
 * Deprecated:2.32: It is best to avoid g_atexit().
 */
void
g_atexit (GVoidFunc func)
{
  gint result;
  int errsv;

  result = atexit ((void (*)(void)) func);
  errsv = errno;
  if (result)
    {
      g_error ("Could not register atexit() function: %s",
               g_strerror (errsv));
    }
}

/* Based on execvp() from GNU Libc.
 * Some of this code is cut-and-pasted into gspawn.c
 */

static gchar*
my_strchrnul (const gchar *str, 
	      gchar        c)
{
  gchar *p = (gchar*)str;
  while (*p && (*p != c))
    ++p;

  return p;
}

#ifdef G_OS_WIN32

static gchar *inner_find_program_in_path (const gchar *program);

gchar*
g_find_program_in_path (const gchar *program)
{
  const gchar *last_dot = strrchr (program, '.');

  if (last_dot == NULL ||
      strchr (last_dot, '\\') != NULL ||
      strchr (last_dot, '/') != NULL)
    {
      const gint program_length = strlen (program);
      gchar *pathext = g_build_path (";",
				     ".exe;.cmd;.bat;.com",
				     g_getenv ("PATHEXT"),
				     NULL);
      gchar *p;
      gchar *decorated_program;
      gchar *retval;

      p = pathext;
      do
	{
	  gchar *q = my_strchrnul (p, ';');

	  decorated_program = g_malloc (program_length + (q-p) + 1);
	  memcpy (decorated_program, program, program_length);
	  memcpy (decorated_program+program_length, p, q-p);
	  decorated_program [program_length + (q-p)] = '\0';
	  
	  retval = inner_find_program_in_path (decorated_program);
	  g_free (decorated_program);

	  if (retval != NULL)
	    {
	      g_free (pathext);
	      return retval;
	    }
	  p = q;
	} while (*p++ != '\0');
      g_free (pathext);
      return NULL;
    }
  else
    return inner_find_program_in_path (program);
}

#endif

/**
 * g_find_program_in_path:
 * @program: (type filename): a program name in the GLib file name encoding
 * 
 * Locates the first executable named @program in the user's path, in the
 * same way that execvp() would locate it. Returns an allocated string
 * with the absolute path name, or %NULL if the program is not found in
 * the path. If @program is already an absolute path, returns a copy of
 * @program if @program exists and is executable, and %NULL otherwise.
 *  
 * On Windows, if @program does not have a file type suffix, tries
 * with the suffixes .exe, .cmd, .bat and .com, and the suffixes in
 * the `PATHEXT` environment variable. 
 * 
 * On Windows, it looks for the file in the same way as CreateProcess() 
 * would. This means first in the directory where the executing
 * program was loaded from, then in the current directory, then in the
 * Windows 32-bit system directory, then in the Windows directory, and
 * finally in the directories in the `PATH` environment variable. If
 * the program is found, the return value contains the full name
 * including the type suffix.
 *
 * Returns: (type filename): a newly-allocated string with the absolute path,
 *     or %NULL
 **/
#ifdef G_OS_WIN32
static gchar *
inner_find_program_in_path (const gchar *program)
#else
gchar*
g_find_program_in_path (const gchar *program)
#endif
{
  const gchar *path, *p;
  gchar *name, *freeme;
#ifdef G_OS_WIN32
  const gchar *path_copy;
  gchar *filename = NULL, *appdir = NULL;
  gchar *sysdir = NULL, *windir = NULL;
  int n;
  wchar_t wfilename[MAXPATHLEN], wsysdir[MAXPATHLEN],
    wwindir[MAXPATHLEN];
#endif
  gsize len;
  gsize pathlen;

  g_return_val_if_fail (program != NULL, NULL);

  /* If it is an absolute path, or a relative path including subdirectories,
   * don't look in PATH.
   */
  if (g_path_is_absolute (program)
      || strchr (program, G_DIR_SEPARATOR) != NULL
#ifdef G_OS_WIN32
      || strchr (program, '/') != NULL
#endif
      )
    {
      if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE) &&
	  !g_file_test (program, G_FILE_TEST_IS_DIR))
        return g_strdup (program);
      else
        return NULL;
    }
  
  path = g_getenv ("PATH");
#if defined(G_OS_UNIX)
  if (path == NULL)
    {
      /* There is no 'PATH' in the environment.  The default
       * search path in GNU libc is the current directory followed by
       * the path 'confstr' returns for '_CS_PATH'.
       */
      
      /* In GLib we put . last, for security, and don't use the
       * unportable confstr(); UNIX98 does not actually specify
       * what to search if PATH is unset. POSIX may, dunno.
       */
      
      path = "/bin:/usr/bin:.";
    }
#else
  n = GetModuleFileNameW (NULL, wfilename, MAXPATHLEN);
  if (n > 0 && n < MAXPATHLEN)
    filename = g_utf16_to_utf8 (wfilename, -1, NULL, NULL, NULL);
  
  n = GetSystemDirectoryW (wsysdir, MAXPATHLEN);
  if (n > 0 && n < MAXPATHLEN)
    sysdir = g_utf16_to_utf8 (wsysdir, -1, NULL, NULL, NULL);
  
  n = GetWindowsDirectoryW (wwindir, MAXPATHLEN);
  if (n > 0 && n < MAXPATHLEN)
    windir = g_utf16_to_utf8 (wwindir, -1, NULL, NULL, NULL);
  
  if (filename)
    {
      appdir = g_path_get_dirname (filename);
      g_free (filename);
    }
  
  path = g_strdup (path);

  if (windir)
    {
      const gchar *tem = path;
      path = g_strconcat (windir, ";", path, NULL);
      g_free ((gchar *) tem);
      g_free (windir);
    }
  
  if (sysdir)
    {
      const gchar *tem = path;
      path = g_strconcat (sysdir, ";", path, NULL);
      g_free ((gchar *) tem);
      g_free (sysdir);
    }
  
  {
    const gchar *tem = path;
    path = g_strconcat (".;", path, NULL);
    g_free ((gchar *) tem);
  }
  
  if (appdir)
    {
      const gchar *tem = path;
      path = g_strconcat (appdir, ";", path, NULL);
      g_free ((gchar *) tem);
      g_free (appdir);
    }

  path_copy = path;
#endif
  
  len = strlen (program) + 1;
  pathlen = strlen (path);
  freeme = name = g_malloc (pathlen + len + 1);
  
  /* Copy the file name at the top, including '\0'  */
  memcpy (name + pathlen + 1, program, len);
  name = name + pathlen;
  /* And add the slash before the filename  */
  *name = G_DIR_SEPARATOR;
  
  p = path;
  do
    {
      char *startp;

      path = p;
      p = my_strchrnul (path, G_SEARCHPATH_SEPARATOR);

      if (p == path)
        /* Two adjacent colons, or a colon at the beginning or the end
         * of 'PATH' means to search the current directory.
         */
        startp = name + 1;
      else
        startp = memcpy (name - (p - path), path, p - path);

      if (g_file_test (startp, G_FILE_TEST_IS_EXECUTABLE) &&
	  !g_file_test (startp, G_FILE_TEST_IS_DIR))
        {
          gchar *ret;
          ret = g_strdup (startp);
          g_free (freeme);
#ifdef G_OS_WIN32
	  g_free ((gchar *) path_copy);
#endif
          return ret;
        }
    }
  while (*p++ != '\0');
  
  g_free (freeme);
#ifdef G_OS_WIN32
  g_free ((gchar *) path_copy);
#endif

  return NULL;
}

/* The functions below are defined this way for compatibility reasons.
 * See the note in gutils.h.
 */

/**
 * g_bit_nth_lsf:
 * @mask: a #gulong containing flags
 * @nth_bit: the index of the bit to start the search from
 *
 * Find the position of the first bit set in @mask, searching
 * from (but not including) @nth_bit upwards. Bits are numbered
 * from 0 (least significant) to sizeof(#gulong) * 8 - 1 (31 or 63,
 * usually). To start searching from the 0th bit, set @nth_bit to -1.
 *
 * Returns: the index of the first bit set which is higher than @nth_bit, or -1
 *    if no higher bits are set
 */
gint
(g_bit_nth_lsf) (gulong mask,
                 gint   nth_bit)
{
  return g_bit_nth_lsf_impl (mask, nth_bit);
}

/**
 * g_bit_nth_msf:
 * @mask: a #gulong containing flags
 * @nth_bit: the index of the bit to start the search from
 *
 * Find the position of the first bit set in @mask, searching
 * from (but not including) @nth_bit downwards. Bits are numbered
 * from 0 (least significant) to sizeof(#gulong) * 8 - 1 (31 or 63,
 * usually). To start searching from the last bit, set @nth_bit to
 * -1 or GLIB_SIZEOF_LONG * 8.
 *
 * Returns: the index of the first bit set which is lower than @nth_bit, or -1
 *    if no lower bits are set
 */
gint
(g_bit_nth_msf) (gulong mask,
                 gint   nth_bit)
{
  return g_bit_nth_msf_impl (mask, nth_bit);
}


/**
 * g_bit_storage:
 * @number: a #guint
 *
 * Gets the number of bits used to hold @number,
 * e.g. if @number is 4, 3 bits are needed.
 *
 * Returns: the number of bits used to hold @number
 */
guint
(g_bit_storage) (gulong number)
{
  return g_bit_storage_impl (number);
}

G_LOCK_DEFINE_STATIC (g_utils_global);

typedef struct
{
  gchar *user_name;
  gchar *real_name;
  gchar *home_dir;
} UserDatabaseEntry;

static  gchar   *g_user_data_dir = NULL;
static  gchar  **g_system_data_dirs = NULL;
static  gchar   *g_user_cache_dir = NULL;
static  gchar   *g_user_config_dir = NULL;
static  gchar   *g_user_runtime_dir = NULL;
static  gchar  **g_system_config_dirs = NULL;

static  gchar  **g_user_special_dirs = NULL;

/* fifteen minutes of fame for everybody */
#define G_USER_DIRS_EXPIRE      15 * 60

#ifdef G_OS_WIN32

static gchar *
get_special_folder (int csidl)
{
  wchar_t path[MAX_PATH+1];
  HRESULT hr;
  LPITEMIDLIST pidl = NULL;
  BOOL b;
  gchar *retval = NULL;

  hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
  if (hr == S_OK)
    {
      b = SHGetPathFromIDListW (pidl, path);
      if (b)
	retval = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL);
      CoTaskMemFree (pidl);
    }
  return retval;
}

static char *
get_windows_directory_root (void)
{
  wchar_t wwindowsdir[MAX_PATH];

  if (GetWindowsDirectoryW (wwindowsdir, G_N_ELEMENTS (wwindowsdir)))
    {
      /* Usually X:\Windows, but in terminal server environments
       * might be an UNC path, AFAIK.
       */
      char *windowsdir = g_utf16_to_utf8 (wwindowsdir, -1, NULL, NULL, NULL);
      char *p;

      if (windowsdir == NULL)
	return g_strdup ("C:\\");

      p = (char *) g_path_skip_root (windowsdir);
      if (G_IS_DIR_SEPARATOR (p[-1]) && p[-2] != ':')
	p--;
      *p = '\0';
      return windowsdir;
    }
  else
    return g_strdup ("C:\\");
}

#endif

/* HOLDS: g_utils_global_lock */
static UserDatabaseEntry *
g_get_user_database_entry (void)
{
  static UserDatabaseEntry *entry;

  if (g_once_init_enter (&entry))
    {
      static UserDatabaseEntry e;

#ifdef G_OS_UNIX
      {
        struct passwd *pw = NULL;
        gpointer buffer = NULL;
        gint error;
        gchar *logname;

#  if defined (HAVE_GETPWUID_R)
        struct passwd pwd;
#    ifdef _SC_GETPW_R_SIZE_MAX
        /* This reurns the maximum length */
        glong bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);

        if (bufsize < 0)
          bufsize = 64;
#    else /* _SC_GETPW_R_SIZE_MAX */
        glong bufsize = 64;
#    endif /* _SC_GETPW_R_SIZE_MAX */

        logname = (gchar *) g_getenv ("LOGNAME");

        do
          {
            g_free (buffer);
            /* we allocate 6 extra bytes to work around a bug in
             * Mac OS < 10.3. See #156446
             */
            buffer = g_malloc (bufsize + 6);
            errno = 0;

            if (logname) {
              error = getpwnam_r (logname, &pwd, buffer, bufsize, &pw);
              if (!pw || (pw->pw_uid != getuid ())) {
                /* LOGNAME is lying, fall back to looking up the uid */
                error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
              }
            } else {
              error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
            }
            error = error < 0 ? errno : error;

            if (!pw)
              {
                /* we bail out prematurely if the user id can't be found
                 * (should be pretty rare case actually), or if the buffer
                 * should be sufficiently big and lookups are still not
                 * successful.
                 */
                if (error == 0 || error == ENOENT)
                  {
                    g_warning ("getpwuid_r(): failed due to unknown user id (%lu)",
                               (gulong) getuid ());
                    break;
                  }
                if (bufsize > 32 * 1024)
                  {
                    g_warning ("getpwuid_r(): failed due to: %s.",
                               g_strerror (error));
                    break;
                  }

                bufsize *= 2;
              }
          }
        while (!pw);
#  endif /* HAVE_GETPWUID_R */

        if (!pw)
          {
            pw = getpwuid (getuid ());
          }
        if (pw)
          {
            e.user_name = g_strdup (pw->pw_name);

#ifndef __BIONIC__
            if (pw->pw_gecos && *pw->pw_gecos != '\0')
              {
                gchar **gecos_fields;
                gchar **name_parts;

                /* split the gecos field and substitute '&' */
                gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
                name_parts = g_strsplit (gecos_fields[0], "&", 0);
                pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
                e.real_name = g_strjoinv (pw->pw_name, name_parts);
                g_strfreev (gecos_fields);
                g_strfreev (name_parts);
              }
#endif

            if (!e.home_dir)
              e.home_dir = g_strdup (pw->pw_dir);
          }
        g_free (buffer);
      }

#endif /* G_OS_UNIX */

#ifdef G_OS_WIN32
      {
        guint len = UNLEN+1;
        wchar_t buffer[UNLEN+1];

        if (GetUserNameW (buffer, (LPDWORD) &len))
          {
            e.user_name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
            e.real_name = g_strdup (e.user_name);
          }
      }
#endif /* G_OS_WIN32 */

      if (!e.user_name)
        e.user_name = g_strdup ("somebody");
      if (!e.real_name)
        e.real_name = g_strdup ("Unknown");

      g_once_init_leave (&entry, &e);
    }

  return entry;
}

/**
 * g_get_user_name:
 *
 * Gets the user name of the current user. The encoding of the returned
 * string is system-defined. On UNIX, it might be the preferred file name
 * encoding, or something else, and there is no guarantee that it is even
 * consistent on a machine. On Windows, it is always UTF-8.
 *
 * Returns: (type filename): the user name of the current user.
 */
const gchar *
g_get_user_name (void)
{
  UserDatabaseEntry *entry;

  entry = g_get_user_database_entry ();

  return entry->user_name;
}

/**
 * g_get_real_name:
 *
 * Gets the real name of the user. This usually comes from the user's
 * entry in the `passwd` file. The encoding of the returned string is
 * system-defined. (On Windows, it is, however, always UTF-8.) If the
 * real user name cannot be determined, the string "Unknown" is 
 * returned.
 *
 * Returns: (type filename): the user's real name.
 */
const gchar *
g_get_real_name (void)
{
  UserDatabaseEntry *entry;

  entry = g_get_user_database_entry ();

  return entry->real_name;
}

/* Protected by @g_utils_global_lock. */
static gchar *g_home_dir = NULL;  /* (owned) (nullable before initialised) */

/**
 * g_get_home_dir:
 *
 * Gets the current user's home directory.
 *
 * As with most UNIX tools, this function will return the value of the
 * `HOME` environment variable if it is set to an existing absolute path
 * name, falling back to the `passwd` file in the case that it is unset.
 *
 * If the path given in `HOME` is non-absolute, does not exist, or is
 * not a directory, the result is undefined.
 *
 * Before version 2.36 this function would ignore the `HOME` environment
 * variable, taking the value from the `passwd` database instead. This was
 * changed to increase the compatibility of GLib with other programs (and
 * the XDG basedir specification) and to increase testability of programs
 * based on GLib (by making it easier to run them from test frameworks).
 *
 * If your program has a strong requirement for either the new or the
 * old behaviour (and if you don't wish to increase your GLib
 * dependency to ensure that the new behaviour is in effect) then you
 * should either directly check the `HOME` environment variable yourself
 * or unset it before calling any functions in GLib.
 *
 * Returns: (type filename): the current user's home directory
 */
const gchar *
g_get_home_dir (void)
{
  G_LOCK (g_utils_global);

  if (g_home_dir == NULL)
    {
      gchar *tmp;

      /* We first check HOME and use it if it is set */
      tmp = g_strdup (g_getenv ("HOME"));

#ifdef G_OS_WIN32
      /* Only believe HOME if it is an absolute path and exists.
       *
       * We only do this check on Windows for a couple of reasons.
       * Historically, we only did it there because we used to ignore $HOME
       * on UNIX.  There are concerns about enabling it now on UNIX because
       * of things like autofs.  In short, if the user has a bogus value in
       * $HOME then they get what they pay for...
       */
      if (tmp)
        {
          if (!(g_path_is_absolute (tmp) &&
                g_file_test (tmp, G_FILE_TEST_IS_DIR)))
            {
              g_free (tmp);
              tmp = NULL;
            }
        }

      /* In case HOME is Unix-style (it happens), convert it to
       * Windows style.
       */
      if (tmp)
        {
          gchar *p;
          while ((p = strchr (tmp, '/')) != NULL)
            *p = '\\';
        }

      if (!tmp)
        {
          /* USERPROFILE is probably the closest equivalent to $HOME? */
          if (g_getenv ("USERPROFILE") != NULL)
            tmp = g_strdup (g_getenv ("USERPROFILE"));
        }

      if (!tmp)
        tmp = get_special_folder (CSIDL_PROFILE);

      if (!tmp)
        tmp = get_windows_directory_root ();
#endif /* G_OS_WIN32 */

      if (!tmp)
        {
          /* If we didn't get it from any of those methods, we will have
           * to read the user database entry.
           */
          UserDatabaseEntry *entry;

          entry = g_get_user_database_entry ();

          /* Strictly speaking, we should copy this, but we know that
           * neither will ever be freed, so don't bother...
           */
          tmp = entry->home_dir;
        }

      /* If we have been denied access to /etc/passwd (for example, by an
       * overly-zealous LSM), make up a junk value. The return value at this
       * point is explicitly documented as ‘undefined’. Memory management is as
       * immediately above: strictly this should be copied, but we know not
       * copying it is OK. */
      if (tmp == NULL)
        {
          g_warning ("Could not find home directory: $HOME is not set, and "
                     "user database could not be read.");
          tmp = "/";
        }

      g_home_dir = g_steal_pointer (&tmp);
    }

  G_UNLOCK (g_utils_global);

  return g_home_dir;
}

/**
 * g_get_tmp_dir:
 *
 * Gets the directory to use for temporary files.
 *
 * On UNIX, this is taken from the `TMPDIR` environment variable.
 * If the variable is not set, `P_tmpdir` is
 * used, as defined by the system C library. Failing that, a
 * hard-coded default of "/tmp" is returned.
 *
 * On Windows, the `TEMP` environment variable is used, with the
 * root directory of the Windows installation (eg: "C:\") used
{'fraction_non_alphanumeric': 0.10336231652839821, 'fraction_numerical': 0.00797702616464582, 'mean_word_length': 2.536568164186473, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6291226', 'n_tokens_mistral': 17212, 'n_tokens_neox': 16463, 'n_words': 5714}