When looking for symbols in the "main" module we must search both the main
[platform/upstream/glib.git] / gutils.c
index 80e2ddb..30c3bb7 100644 (file)
--- a/gutils.c
+++ b/gutils.c
@@ -1,5 +1,5 @@
 /* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ * 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
  * 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
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <errno.h>
+#ifdef HAVE_PWD_H
 #include <pwd.h>
+#endif
 #include <sys/types.h>
+#ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
+#endif
+
+/* implement Glib's inline functions
+ */
+#define        G_INLINE_FUNC extern
+#define        G_CAN_INLINE 1
 #include "glib.h"
 
+#ifdef MAXPATHLEN
+#define        G_PATH_LENGTH   MAXPATHLEN
+#elif  defined (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>
+#  include <io.h>
+#endif /* G_OS_WIN32 */
+
 const guint glib_major_version = GLIB_MAJOR_VERSION;
 const guint glib_minor_version = GLIB_MINOR_VERSION;
 const guint glib_micro_version = GLIB_MICRO_VERSION;
+const guint glib_interface_age = GLIB_INTERFACE_AGE;
+const guint glib_binary_age = GLIB_BINARY_AGE;
+
+#if !defined (HAVE_MEMMOVE) && !defined (HAVE_WORKING_BCOPY)
+void 
+g_memmove (gpointer dest, gconstpointer src, gulong len)
+{
+  gchar* destptr = dest;
+  const gchar* srcptr = src;
+  if (src + len < dest || dest + len < src)
+    {
+      bcopy (src, dest, len);
+      return;
+    }
+  else if (dest <= src)
+    {
+      while (len--)
+       *(destptr++) = *(srcptr++);
+    }
+  else
+    {
+      destptr += len;
+      srcptr += len;
+      while (len--)
+       *(--destptr) = *(--srcptr);
+    }
+}
+#endif /* !HAVE_MEMMOVE && !HAVE_WORKING_BCOPY */
 
-extern char* g_vsprintf (const gchar *fmt, va_list *args, va_list *args2);
+void
+g_atexit (GVoidFunc func)
+{
+  gint result;
+  gchar *error = NULL;
+
+  /* keep this in sync with glib.h */
+
+#ifdef G_NATIVE_ATEXIT
+  result = ATEXIT (func);
+  if (result)
+    error = g_strerror (errno);
+#elif defined (HAVE_ATEXIT)
+#  ifdef NeXT /* @#%@! NeXTStep */
+  result = !atexit ((void (*)(void)) func);
+  if (result)
+    error = g_strerror (errno);
+#  else
+  result = atexit ((void (*)(void)) func);
+  if (result)
+    error = g_strerror (errno);
+#  endif /* NeXT */
+#elif defined (HAVE_ON_EXIT)
+  result = on_exit ((void (*)(int, void *)) func, NULL);
+  if (result)
+    error = g_strerror (errno);
+#else
+  result = 0;
+  error = "no implementation";
+#endif /* G_NATIVE_ATEXIT */
+
+  if (error)
+    g_error ("Could not register atexit() function: %s", error);
+}
 
 gint
 g_snprintf (gchar      *str,
@@ -38,33 +145,67 @@ g_snprintf (gchar  *str,
            gchar const *fmt,
            ...)
 {
-#ifdef HAVE_VSNPRINTF
+#ifdef HAVE_VSNPRINTF
   va_list args;
   gint retval;
   
   va_start (args, fmt);
   retval = vsnprintf (str, n, fmt, args);
   va_end (args);
-  
+
+  if (retval < 0)
+    {
+      str[n-1] = '\0';
+      retval = strlen (str);
+    }
+
   return retval;
-  
-#else
+#else  /* !HAVE_VSNPRINTF */
   gchar *printed;
-  va_list args, args2;
+  va_list args;
   
   va_start (args, fmt);
-  va_start (args2, fmt);
+  printed = g_strdup_vprintf (fmt, args);
+  va_end (args);
   
-  printed = g_vsprintf (fmt, &args, &args2);
   strncpy (str, printed, n);
   str[n-1] = '\0';
-  
-  va_end (args2);
-  va_end (args);
+
+  g_free (printed);
   
   return strlen (str);
+#endif /* !HAVE_VSNPRINTF */
+}
+
+gint
+g_vsnprintf (gchar      *str,
+            gulong       n,
+            gchar const *fmt,
+            va_list      args)
+{
+#ifdef HAVE_VSNPRINTF
+  gint retval;
   
-#endif
+  retval = vsnprintf (str, n, fmt, args);
+  
+  if (retval < 0)
+    {
+      str[n-1] = '\0';
+      retval = strlen (str);
+    }
+
+  return retval;
+#else  /* !HAVE_VSNPRINTF */
+  gchar *printed;
+  
+  printed = g_strdup_vprintf (fmt, args);
+  strncpy (str, printed, n);
+  str[n-1] = '\0';
+
+  g_free (printed);
+  
+  return strlen (str);
+#endif /* !HAVE_VSNPRINTF */
 }
 
 guint       
@@ -120,13 +261,50 @@ g_basename (const gchar      *file_name)
   
   g_return_val_if_fail (file_name != NULL, NULL);
   
-  base = strrchr (file_name, '/');
+  base = strrchr (file_name, G_DIR_SEPARATOR);
   if (base)
     return base + 1;
+
+#ifdef G_OS_WIN32
+  if (isalpha (file_name[0]) && file_name[1] == ':')
+    return (gchar*) file_name + 2;
+#endif /* G_OS_WIN32 */
   
   return (gchar*) file_name;
 }
 
+gboolean
+g_path_is_absolute (const gchar *file_name)
+{
+  g_return_val_if_fail (file_name != NULL, FALSE);
+  
+  if (file_name[0] == G_DIR_SEPARATOR)
+    return TRUE;
+
+#ifdef G_OS_WIN32
+  if (isalpha (file_name[0]) && file_name[1] == ':' && file_name[2] == G_DIR_SEPARATOR)
+    return TRUE;
+#endif
+
+  return FALSE;
+}
+
+gchar*
+g_path_skip_root (gchar *file_name)
+{
+  g_return_val_if_fail (file_name != NULL, NULL);
+  
+  if (file_name[0] == G_DIR_SEPARATOR)
+    return file_name + 1;
+
+#ifdef G_OS_WIN32
+  if (isalpha (file_name[0]) && file_name[1] == ':' && file_name[2] == G_DIR_SEPARATOR)
+    return file_name + 3;
+#endif
+
+  return NULL;
+}
+
 gchar*
 g_dirname (const gchar    *file_name)
 {
@@ -135,78 +313,288 @@ g_dirname (const gchar      *file_name)
   
   g_return_val_if_fail (file_name != NULL, NULL);
   
-  base = strrchr (file_name, '/');
+  base = strrchr (file_name, G_DIR_SEPARATOR);
   if (!base)
     return g_strdup (".");
-  while (base > file_name && *base == '/')
+  while (base > file_name && *base == G_DIR_SEPARATOR)
     base--;
   len = (guint) 1 + base - file_name;
-
+  
   base = g_new (gchar, len + 1);
   g_memmove (base, file_name, len);
   base[len] = 0;
-
+  
   return base;
 }
 
 gchar*
-g_getcwd (void)
+g_get_current_dir (void)
 {
-  static gchar g_getcwd_buf[MAXPATHLEN + 1] = { 0 };
-  register gchar *dir;
-  
-  g_getcwd_buf[0] = 0;
+  gchar *buffer = NULL;
+  gchar *dir = NULL;
+  static gulong 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)
-  dir = getwd (g_getcwd_buf);
+  buffer = g_new (gchar, max_len + 1);
+  *buffer = 0;
+  dir = getwd (buffer);
 #else  /* !sun */
-  dir = getcwd (g_getcwd_buf, MAXPATHLEN);
+  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 */
   
-  if (!dir || g_getcwd_buf[0] == 0)
+  if (!dir || !*buffer)
     {
-      /* hm, we should probably g_error() out here...
+      /* hm, should we g_error() out here?
+       * this can happen if e.g. "./" has mode \0000
        */
-      g_getcwd_buf[0] = '/';
-      g_getcwd_buf[1] = 0;
+      buffer[0] = G_DIR_SEPARATOR;
+      buffer[1] = 0;
     }
+
+  dir = g_strdup (buffer);
+  g_free (buffer);
   
-  return g_getcwd_buf;
+  return dir;
+}
+
+gchar*
+g_getenv (const gchar *variable)
+{
+#ifndef G_OS_WIN32
+  g_return_val_if_fail (variable != NULL, NULL);
+
+  return getenv (variable);
+#else
+  gchar *v;
+  guint k;
+  static gchar *p = NULL;
+  static gint l;
+  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.
+   */
+
+  /* First check how much space we need */
+  k = ExpandEnvironmentStrings (v, dummy, 2);
+  /* Then allocate that much, and actualy do the expansion */
+  if (p == NULL)
+    {
+      p = g_malloc (k);
+      l = k;
+    }
+  else if (k > l)
+    {
+      p = g_realloc (p, k);
+      l = k;
+    }
+  ExpandEnvironmentStrings (v, p, k);
+  return p;
+#endif
 }
 
+
+G_LOCK_DEFINE_STATIC (g_utils_global);
+
 static gchar   *g_tmp_dir = NULL;
 static gchar   *g_user_name = NULL;
 static gchar   *g_real_name = NULL;
 static gchar   *g_home_dir = NULL;
 
+/* HOLDS: g_utils_global_lock */
 static void
 g_get_any_init (void)
 {
   if (!g_tmp_dir)
     {
-      struct passwd *pw;
+      g_tmp_dir = g_strdup (g_getenv ("TMPDIR"));
+      if (!g_tmp_dir)
+       g_tmp_dir = g_strdup (g_getenv ("TMP"));
+      if (!g_tmp_dir)
+       g_tmp_dir = g_strdup (g_getenv ("TEMP"));
       
-      g_tmp_dir = g_strdup (getenv ("TMP"));
+#ifdef P_tmpdir
       if (!g_tmp_dir)
-       g_tmp_dir = g_strdup (getenv ("TEMP"));
+       {
+         int k;
+         g_tmp_dir = g_strdup (P_tmpdir);
+         k = strlen (g_tmp_dir);
+         if (g_tmp_dir[k-1] == G_DIR_SEPARATOR)
+           g_tmp_dir[k-1] = '\0';
+       }
+#endif
+      
       if (!g_tmp_dir)
-       g_tmp_dir = g_strdup ("/tmp");
+       {
+#ifndef G_OS_WIN32
+         g_tmp_dir = g_strdup ("/tmp");
+#else /* G_OS_WIN32 */
+         g_tmp_dir = g_strdup ("C:\\");
+#endif /* G_OS_WIN32 */
+       }
+      
+      if (!g_home_dir)
+       g_home_dir = g_strdup (g_getenv ("HOME"));
+      
+#ifdef G_OS_WIN32
+      if (!g_home_dir)
+       {
+         /* 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.
+          */
+         
+         if (getenv ("HOMEDRIVE") != NULL && getenv ("HOMEPATH") != NULL)
+           {
+             gchar *homedrive, *homepath;
+             
+             homedrive = g_strdup (g_getenv ("HOMEDRIVE"));
+             homepath = g_strdup (g_getenv ("HOMEPATH"));
+             
+             g_home_dir = g_strconcat (homedrive, homepath, NULL);
+             g_free (homedrive);
+             g_free (homepath);
+           }
+       }
+#endif /* G_OS_WIN32 */
+      
+#ifdef HAVE_PWD_H
+      {
+       struct passwd *pw = NULL;
+       gpointer buffer = NULL;
+       
+#  ifdef HAVE_GETPWUID_R
+        struct passwd pwd;
+#    ifdef _SC_GETPW_R_SIZE_MAX  
+       /* This reurns the maximum length */
+        guint bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
+#    else /* _SC_GETPW_R_SIZE_MAX */
+        guint bufsize = 64;
+#    endif /* _SC_GETPW_R_SIZE_MAX */
+        gint error;
+       
+        do
+          {
+            g_free (buffer);
+            buffer = g_malloc (bufsize);
+           errno = 0;
+           
+#    ifdef HAVE_GETPWUID_R_POSIX
+           error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
+            error = error < 0 ? errno : error;
+#    else /* !HAVE_GETPWUID_R_POSIX */
+#      ifdef _AIX
+           error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+           pw = error == 0 ? &pwd : NULL;
+#      else /* !_AIX */
+            pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
+            error = pw ? 0 : errno;
+#      endif /* !_AIX */            
+#    endif /* !HAVE_GETPWUID_R_POSIX */
+           
+           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
+                * successfull.
+                */
+               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)
+         {
+           setpwent ();
+           pw = getpwuid (getuid ());
+           endpwent ();
+         }
+       if (pw)
+         {
+           g_user_name = g_strdup (pw->pw_name);
+           g_real_name = g_strdup (pw->pw_gecos);
+           if (!g_home_dir)
+             g_home_dir = g_strdup (pw->pw_dir);
+         }
+       g_free (buffer);
+      }
       
-      g_home_dir = g_strdup (getenv ("HOME"));
+#else /* !HAVE_PWD_H */
       
-      setpwent ();
-      pw = getpwuid (getuid ());
-      endpwent ();
+#  ifdef G_OS_WIN32
+      {
+       guint len = 17;
+       gchar buffer[17];
+       
+       if (GetUserName (buffer, &len))
+         {
+           g_user_name = g_strdup (buffer);
+           g_real_name = g_strdup (buffer);
+         }
+      }
+#  endif /* G_OS_WIN32 */
       
-      if (pw)
+#endif /* !HAVE_PWD_H */
+      
+#ifdef __EMX__
+      /* change '\\' in %HOME% to '/' */
+      g_strdelimit (g_home_dir, "\\",'/');
+#endif
+      if (!g_user_name)
+       g_user_name = g_strdup ("somebody");
+      if (!g_real_name)
+       g_real_name = g_strdup ("Unknown");
+      else
        {
-         g_user_name = g_strdup (pw->pw_name);
-         g_real_name = g_strdup (pw->pw_gecos);
-         if (!g_home_dir)
-           g_home_dir = g_strdup (pw->pw_dir);
+         gchar *p;
+
+         for (p = g_real_name; *p; p++)
+           if (*p == ',')
+             {
+               *p = 0;
+               p = g_strdup (g_real_name);
+               g_free (g_real_name);
+               g_real_name = p;
+               break;
+             }
        }
     }
 }
@@ -214,8 +602,10 @@ g_get_any_init (void)
 gchar*
 g_get_user_name (void)
 {
+  G_LOCK (g_utils_global);
   if (!g_tmp_dir)
     g_get_any_init ();
+  G_UNLOCK (g_utils_global);
   
   return g_user_name;
 }
@@ -223,26 +613,45 @@ g_get_user_name (void)
 gchar*
 g_get_real_name (void)
 {
+  G_LOCK (g_utils_global);
   if (!g_tmp_dir)
     g_get_any_init ();
-  
+  G_UNLOCK (g_utils_global);
   return g_real_name;
 }
 
+/* Return the home directory of the user. If there is a HOME
+ * environment variable, its value is returned, otherwise use some
+ * system-dependent way of finding it out. If no home directory can be
+ * deduced, return NULL.
+ */
+
 gchar*
 g_get_home_dir (void)
 {
+  G_LOCK (g_utils_global);
   if (!g_tmp_dir)
     g_get_any_init ();
+  G_UNLOCK (g_utils_global);
   
   return g_home_dir;
 }
 
+/* Return a directory to be used to store temporary files. This is the
+ * value of the TMPDIR, TMP or TEMP environment variables (they are
+ * checked in that order). If none of those exist, use P_tmpdir from
+ * stdio.h.  If that isn't defined, return "/tmp" on POSIXly systems,
+ * and C:\ on Windows.
+ */
+
 gchar*
 g_get_tmp_dir (void)
 {
+  G_LOCK (g_utils_global);
   if (!g_tmp_dir)
     g_get_any_init ();
+  G_UNLOCK (g_utils_global);
   
   return g_tmp_dir;
 }
@@ -252,34 +661,45 @@ static gchar *g_prgname = NULL;
 gchar*
 g_get_prgname (void)
 {
-  return g_prgname;
+  gchar* retval;
+
+  G_LOCK (g_utils_global);
+  retval = g_prgname;
+  G_UNLOCK (g_utils_global);
+
+  return retval;
 }
 
 void
 g_set_prgname (const gchar *prgname)
 {
-  gchar *c = g_prgname;
-
+  gchar *c;
+    
+  G_LOCK (g_utils_global);
+  c = g_prgname;
   g_prgname = g_strdup (prgname);
   g_free (c);
+  G_UNLOCK (g_utils_global);
 }
 
 guint
-g_direct_hash(gconstpointer v)
+g_direct_hash (gconstpointer v)
 {
   return GPOINTER_TO_UINT (v);
 }
 
 gint
-g_direct_equal(gconstpointer v, gconstpointer v2)
+g_direct_equal (gconstpointer v1,
+               gconstpointer v2)
 {
-  return GPOINTER_TO_UINT (v) == GPOINTER_TO_UINT (v2);
+  return v1 == v2;
 }
 
 gint
-g_int_equal (gconstpointer v, gconstpointer v2)
+g_int_equal (gconstpointer v1,
+            gconstpointer v2)
 {
-  return *((const gint*) v) == *((const gint*) v2);
+  return *((const gint*) v1) == *((const gint*) v2);
 }
 
 guint