Don't store address of local variable in hashtable.
[platform/upstream/glib.git] / gutils.c
index 62b4e54..61cdcf5 100644 (file)
--- a/gutils.c
+++ b/gutils.c
@@ -2,23 +2,23 @@
  * Copyright (C) 1995-1998  Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 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
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
 
 /*
- * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
+ * 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/. 
@@ -32,8 +32,6 @@
 #include <config.h>
 #endif
 
-#include "glibconfig.h"
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <sys/param.h>
 #endif
 
-#ifdef G_OS_WIN32
-#  define STRICT                       /* Strict typing, please */
-#  include <windows.h>
-#  include <errno.h>
-#  include <ctype.h>
-#  ifdef _MSC_VER
-#    include <io.h>
-#  endif /* _MSC_VER */
-#endif /* G_OS_WIN32 */
-
 /* implement Glib's inline functions
  */
-#define        G_INLINE_FUNC extern
-#define        G_CAN_INLINE 1
+#define        G_IMPLEMENT_INLINES 1
+#define        __G_UTILS_C__
 #include "glib.h"
 
 #ifdef MAXPATHLEN
-#define        G_PATH_LENGTH   (MAXPATHLEN + 1)
+#define        G_PATH_LENGTH   MAXPATHLEN
 #elif  defined (PATH_MAX)
-#define        G_PATH_LENGTH   (PATH_MAX + 1)
-#else  /* !MAXPATHLEN */
-#define G_PATH_LENGTH   (2048 + 1)
-#endif /* !MAXPATHLEN && !PATH_MAX */
+#define        G_PATH_LENGTH   PATH_MAX
+#elif   defined (_PC_PATH_MAX)
+#define        G_PATH_LENGTH   sysconf(_PC_PATH_MAX)
+#else  
+#define G_PATH_LENGTH   2048
+#endif
+
+#ifdef G_OS_WIN32
+#  define STRICT                       /* Strict typing, please */
+#  include <windows.h>
+#  include <ctype.h>
+#  include <direct.h>
+#endif /* G_OS_WIN32 */
+
+#ifdef HAVE_CODESET
+#include <langinfo.h>
+#endif
 
 const guint glib_major_version = GLIB_MAJOR_VERSION;
 const guint glib_minor_version = GLIB_MINOR_VERSION;
@@ -141,6 +142,106 @@ g_atexit (GVoidFunc func)
     g_error ("Could not register atexit() function: %s", error);
 }
 
+/* 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;
+}
+
+/**
+ * g_find_program_in_path:
+ * @program: a program name
+ * 
+ * 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.
+ * 
+ * Return value: absolute path, or NULL
+ **/
+gchar*
+g_find_program_in_path (const gchar *program)
+{
+  gchar *path, *p, *name, *freeme;
+  size_t len;
+  size_t pathlen;
+
+  g_return_val_if_fail (program != NULL, NULL);
+
+  if (*program == '/')
+    {
+      if (g_file_test (program, G_FILE_TEST_IS_EXECUTABLE))
+        return g_strdup (program);
+      else
+        return NULL;
+    }
+  
+  path = g_getenv ("PATH");
+  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:.";
+    }
+  
+  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 = '/';
+  
+  p = path;
+  do
+    {
+      char *startp;
+
+      path = p;
+      p = my_strchrnul (path, ':');
+
+      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))
+        {
+          gchar *ret;
+          ret = g_strdup (startp);
+          g_free (freeme);
+          return ret;
+        }
+    }
+  while (*p++ != '\0');
+  
+  g_free (freeme);
+
+  return NULL;
+}
+
 gint
 g_snprintf (gchar      *str,
            gulong       n,
@@ -151,6 +252,10 @@ g_snprintf (gchar  *str,
   va_list args;
   gint retval;
   
+  g_return_val_if_fail (str != NULL, 0);
+  g_return_val_if_fail (n > 0, 0);
+  g_return_val_if_fail (fmt != NULL, 0);
+
   va_start (args, fmt);
   retval = vsnprintf (str, n, fmt, args);
   va_end (args);
@@ -166,6 +271,10 @@ g_snprintf (gchar  *str,
   gchar *printed;
   va_list args;
   
+  g_return_val_if_fail (str != NULL, 0);
+  g_return_val_if_fail (n > 0, 0);
+  g_return_val_if_fail (fmt != NULL, 0);
+
   va_start (args, fmt);
   printed = g_strdup_vprintf (fmt, args);
   va_end (args);
@@ -188,6 +297,10 @@ g_vsnprintf (gchar  *str,
 #ifdef HAVE_VSNPRINTF
   gint retval;
   
+  g_return_val_if_fail (str != NULL, 0);
+  g_return_val_if_fail (n > 0, 0);
+  g_return_val_if_fail (fmt != NULL, 0);
+
   retval = vsnprintf (str, n, fmt, args);
   
   if (retval < 0)
@@ -200,6 +313,10 @@ g_vsnprintf (gchar  *str,
 #else  /* !HAVE_VSNPRINTF */
   gchar *printed;
   
+  g_return_val_if_fail (str != NULL, 0);
+  g_return_val_if_fail (n > 0, 0);
+  g_return_val_if_fail (fmt != NULL, 0);
+
   printed = g_strdup_vprintf (fmt, args);
   strncpy (str, printed, n);
   str[n-1] = '\0';
@@ -260,6 +377,17 @@ gchar*
 g_basename (const gchar           *file_name)
 {
   register gchar *base;
+#if defined(G_ENABLE_DEBUG) && !defined(G_OS_WIN32)
+  static gboolean first_call = TRUE;
+
+  if (first_call)
+    {
+      g_message ("g_basename is deprecated. Use g_path_get_basename instead. "
+                "Beware that the string returned by g_path_get_basename() has "
+                " to be g_free()ed.");
+      first_call = FALSE;
+    }
+#endif /* G_ENABLE_DEBUG */
   
   g_return_val_if_fail (file_name != NULL, NULL);
   
@@ -275,6 +403,52 @@ g_basename (const gchar       *file_name)
   return (gchar*) file_name;
 }
 
+gchar*
+g_path_get_basename (const gchar   *file_name)
+{
+  register gint base;
+  register gint last_nonslash;
+  guint len;
+  gchar *retval;
+  g_return_val_if_fail (file_name != NULL, NULL);
+  
+  if (file_name[0] == '\0')
+    /* empty string */
+    return g_strdup (".");
+
+  last_nonslash = strlen (file_name) - 1;
+
+  while (last_nonslash >= 0 && file_name [last_nonslash] == G_DIR_SEPARATOR)
+    last_nonslash--;
+
+  if (last_nonslash == -1)
+    /* string only containing slashes */
+    return g_strdup (G_DIR_SEPARATOR_S);
+
+#ifdef G_OS_WIN32
+  if (last_nonslash == 1 && isalpha (file_name[0]) && file_name[1] == ':')
+    /* string only containing slashes and a drive */
+    return g_strdup (G_DIR_SEPARATOR_S);
+#endif /* G_OS_WIN32 */
+
+  base = last_nonslash;
+
+  while (base >=0 && file_name [base] != G_DIR_SEPARATOR)
+    base--;
+
+#ifdef G_OS_WIN32
+  if (base == -1 && isalpha (file_name[0]) && file_name[1] == ':')
+    base = 1;
+#endif /* G_OS_WIN32 */
+
+  len = last_nonslash - base;
+  retval = g_malloc (len + 1);
+  memcpy (retval, file_name + base + 1, len);
+  retval [len] = '\0';
+  return retval;
+}
+
 gboolean
 g_path_is_absolute (const gchar *file_name)
 {
@@ -296,10 +470,41 @@ g_path_skip_root (gchar *file_name)
 {
   g_return_val_if_fail (file_name != NULL, NULL);
   
+#ifdef G_OS_WIN32
+  /* Skip \\server\share */
+  if (file_name[0] == G_DIR_SEPARATOR &&
+      file_name[1] == G_DIR_SEPARATOR &&
+      file_name[2])
+    {
+      gchar *p, *q;
+
+      if ((p = strchr (file_name + 2, G_DIR_SEPARATOR)) > file_name + 2 &&
+         p[1])
+       {
+         file_name = p + 1;
+
+         while (file_name[0] && file_name[0] != G_DIR_SEPARATOR)
+           file_name++;
+
+         /* Possibly skip a backslash after the share name */
+         if (file_name[0] == G_DIR_SEPARATOR)
+           file_name++;
+
+         return file_name;
+       }
+    }
+#endif
+  
+  /* Skip initial slashes */
   if (file_name[0] == G_DIR_SEPARATOR)
-    return file_name + 1;
+    {
+      while (file_name[0] == G_DIR_SEPARATOR)
+       file_name++;
+      return file_name;
+    }
 
 #ifdef G_OS_WIN32
+  /* Skip X:\ */
   if (isalpha (file_name[0]) && file_name[1] == ':' && file_name[2] == G_DIR_SEPARATOR)
     return file_name + 3;
 #endif
@@ -308,7 +513,7 @@ g_path_skip_root (gchar *file_name)
 }
 
 gchar*
-g_dirname (const gchar    *file_name)
+g_path_get_dirname (const gchar           *file_name)
 {
   register gchar *base;
   register guint len;
@@ -330,22 +535,52 @@ g_dirname (const gchar       *file_name)
 }
 
 gchar*
+g_dirname (const gchar    *file_name)
+{
+#if defined(G_ENABLE_DEBUG) && !defined(G_OS_WIN32)
+  static gboolean first_call = TRUE;
+
+  if (first_call)
+    {
+      g_message ("g_dirname() is deprecated. Use g_path_get_dirname() instead.");
+      first_call = FALSE;
+    }
+#endif /* G_ENABLE_DEBUG */
+
+  return g_path_get_dirname (file_name);
+}
+
+gchar*
 g_get_current_dir (void)
 {
-  gchar *buffer;
-  gchar *dir;
+  gchar *buffer = NULL;
+  gchar *dir = NULL;
+  static gulong max_len = 0;
 
-  buffer = g_new (gchar, G_PATH_LENGTH);
-  *buffer = 0;
+  if (max_len == 0) 
+    max_len = (G_PATH_LENGTH == -1) ? 2048 : G_PATH_LENGTH;
   
   /* We don't use getcwd(3) on SUNOS, because, it does a popen("pwd")
    * and, if that wasn't bad enough, hangs in doing so.
    */
-#if    defined (sun) && !defined (__SVR4)
+#if    (defined (sun) && !defined (__SVR4)) || !defined(HAVE_GETCWD)
+  buffer = g_new (gchar, max_len + 1);
+  *buffer = 0;
   dir = getwd (buffer);
-#else  /* !sun */
-  dir = getcwd (buffer, G_PATH_LENGTH - 1);
-#endif /* !sun */
+#else  /* !sun || !HAVE_GETCWD */
+  while (max_len < G_MAXULONG / 2)
+    {
+      buffer = g_new (gchar, max_len + 1);
+      *buffer = 0;
+      dir = getcwd (buffer, max_len);
+
+      if (dir || errno != ERANGE)
+       break;
+
+      g_free (buffer);
+      max_len *= 2;
+    }
+#endif /* !sun || !HAVE_GETCWD */
   
   if (!dir || !*buffer)
     {
@@ -370,38 +605,71 @@ g_getenv (const gchar *variable)
 
   return getenv (variable);
 #else
-  gchar *v;
-  guint k;
-  static gchar *p = NULL;
-  static gint l;
+  G_LOCK_DEFINE_STATIC (getenv);
+  struct env_struct
+  {
+    gchar *key;
+    gchar *value;
+  } *env;
+  static GArray *environs = NULL;
+  gchar *system_env;
+  guint length, i;
   gchar dummy[2];
 
   g_return_val_if_fail (variable != NULL, NULL);
   
-  v = getenv (variable);
-  if (!v)
-    return NULL;
-  
-  /* On Windows NT, it is relatively typical that environment variables
-   * contain references to other environment variables. Handle that by
-   * calling ExpandEnvironmentStrings.
+  G_LOCK (getenv);
+
+  if (!environs)
+    environs = g_array_new (FALSE, FALSE, sizeof (struct env_struct));
+
+  /* First we try to find the envinronment variable inside the already
+   * found ones.
    */
 
-  /* First check how much space we need */
-  k = ExpandEnvironmentStrings (v, dummy, 2);
-  /* Then allocate that much, and actualy do the expansion */
-  if (p == NULL)
+  for (i = 0; i < environs->len; i++)
     {
-      p = g_malloc (k);
-      l = k;
+      env = &g_array_index (environs, struct env_struct, i);
+      if (strcmp (env->key, variable) == 0)
+       {
+         g_assert (env->value);
+         G_UNLOCK (getenv);
+         return env->value;
+       }
     }
-  else if (k > l)
+
+  /* If not found, we ask the system */
+
+  system_env = getenv (variable);
+  if (!system_env)
     {
-      p = g_realloc (p, k);
-      l = k;
+      G_UNLOCK (getenv);
+      return NULL;
     }
-  ExpandEnvironmentStrings (v, p, k);
-  return p;
+
+  /* On Windows NT, it is relatively typical that environment variables
+   * contain references to other environment variables. Handle that by
+   * calling ExpandEnvironmentStrings.
+   */
+
+  g_array_set_size (environs, environs->len + 1);
+
+  env = &g_array_index (environs, struct env_struct, environs->len - 1);
+
+  /* First check how much space we need */
+  length = ExpandEnvironmentStrings (system_env, dummy, 2);
+
+  /* Then allocate that much, and actualy do the expansion and insert
+   * the new found pair into our buffer 
+   */
+
+  env->value = g_malloc (length);
+  env->key = g_strdup (variable);
+
+  ExpandEnvironmentStrings (system_env, env->value, length);
+
+  G_UNLOCK (getenv);
+  return env->value;
 #endif
 }
 
@@ -449,15 +717,21 @@ g_get_any_init (void)
        g_home_dir = g_strdup (g_getenv ("HOME"));
       
 #ifdef G_OS_WIN32
-      if (!g_home_dir)
+      /* In case HOME is Unix-style (it happens), convert it to
+       * Windows style.
+       */
+      if (g_home_dir)
+       {
+         gchar *p;
+         while ((p = strchr (g_home_dir, '/')) != NULL)
+           *p = '\\';
+       }
+      else
        {
          /* The official way to specify a home directory on NT is
-          * the HOMEDRIVE and HOMEPATH environment variables.
-          *
-          * This is inside #ifdef G_OS_WIN32 because with the cygwin dll,
-          * HOME should be a POSIX style pathname.
+          * the HOMEDRIVE and HOMEPATH environment variables. At least
+          * it was at some time.
           */
-         
          if (getenv ("HOMEDRIVE") != NULL && getenv ("HOMEPATH") != NULL)
            {
              gchar *homedrive, *homepath;
@@ -470,14 +744,14 @@ g_get_any_init (void)
              g_free (homepath);
            }
        }
-#endif /* !G_OS_WIN32 */
+#endif /* G_OS_WIN32 */
       
 #ifdef HAVE_PWD_H
       {
        struct passwd *pw = NULL;
        gpointer buffer = NULL;
        
-#  ifdef HAVE_GETPWUID_R
+#  if defined (HAVE_POSIX_GETPWUID_R) || defined (HAVE_NONPOSIX_GETPWUID_R)
         struct passwd pwd;
 #    ifdef _SC_GETPW_R_SIZE_MAX  
        /* This reurns the maximum length */
@@ -493,10 +767,10 @@ g_get_any_init (void)
             buffer = g_malloc (bufsize);
            errno = 0;
            
-#    ifdef HAVE_GETPWUID_R_POSIX
+#    ifdef HAVE_POSIX_GETPWUID_R
            error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
             error = error < 0 ? errno : error;
-#    else /* !HAVE_GETPWUID_R_POSIX */
+#    else /* HAVE_NONPOSIX_GETPWUID_R */
 #      ifdef _AIX
            error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
            pw = error == 0 ? &pwd : NULL;
@@ -504,7 +778,7 @@ g_get_any_init (void)
             pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
             error = pw ? 0 : errno;
 #      endif /* !_AIX */            
-#    endif /* !HAVE_GETPWUID_R_POSIX */
+#    endif /* HAVE_NONPOSIX_GETPWUID_R */
            
            if (!pw)
              {
@@ -530,7 +804,7 @@ g_get_any_init (void)
              }
          }
        while (!pw);
-#  endif /* !HAVE_GETPWUID_R */
+#  endif /* HAVE_POSIX_GETPWUID_R || HAVE_NONPOSIX_GETPWUID_R */
        
        if (!pw)
          {
@@ -555,7 +829,7 @@ g_get_any_init (void)
        guint len = 17;
        gchar buffer[17];
        
-       if (GetUserName (buffer, &len))
+       if (GetUserName ((LPTSTR) buffer, (LPDWORD) &len))
          {
            g_user_name = g_strdup (buffer);
            g_real_name = g_strdup (buffer);
@@ -679,14 +953,14 @@ g_direct_hash (gconstpointer v)
   return GPOINTER_TO_UINT (v);
 }
 
-gint
+gboolean
 g_direct_equal (gconstpointer v1,
                gconstpointer v2)
 {
   return v1 == v2;
 }
 
-gint
+gboolean
 g_int_equal (gconstpointer v1,
             gconstpointer v2)
 {
@@ -698,3 +972,62 @@ g_int_hash (gconstpointer v)
 {
   return *(const gint*) v;
 }
+
+/**
+ * g_get_codeset:
+ * 
+ * Get the codeset for the current locale.
+ * 
+ * Return value: a newly allocated string containing the name
+ * of the codeset. This string must be freed with g_free().
+ **/
+gchar *
+g_get_codeset (void)
+{
+#ifdef HAVE_CODESET  
+  char *result = nl_langinfo (CODESET);
+  return g_strdup (result);
+#else
+#ifndef G_OS_WIN32
+  /* FIXME: Do something more intelligent based on setlocale (LC_CTYPE, NULL)
+   */
+  return g_strdup ("ISO-8859-1");
+#else
+  return g_strdup_printf ("CP%d", GetACP ());
+#endif
+#endif
+}
+
+#ifdef ENABLE_NLS
+
+#include <libintl.h>
+
+
+#ifdef G_OS_WIN32
+
+#define GLIB_LOCALE_DIR                                                \
+  g_win32_get_package_installation_subdirectory                        \
+  (GETTEXT_PACKAGE, g_strdup_printf ("glib-%d.%d.dll",         \
+                                    GLIB_MAJOR_VERSION,        \
+                                    GLIB_MINOR_VERSION),       \
+   "locale")
+
+#endif /* G_OS_WIN32 */
+
+gchar *
+_glib_gettext (const gchar *str)
+{
+  gboolean _glib_gettext_initialized = FALSE;
+
+  if (!_glib_gettext_initialized)
+    {
+      bindtextdomain(GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+      _glib_gettext_initialized = TRUE;
+    }
+  
+  return dgettext (GETTEXT_PACKAGE, str);
+}
+
+#endif /* ENABLE_NLS */
+
+