/* 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,
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
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)
{
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;
+ }
}
}
}
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;
}
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;
}
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