Port to BeOS by myself and Richard Offer.
[platform/upstream/glib.git] / glib / gutils.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1998  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /* 
28  * MT safe for the unix part, FIXME: make the win32 part MT safe as well.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include "glibconfig.h"
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <errno.h>
45 #ifdef HAVE_PWD_H
46 #include <pwd.h>
47 #endif
48 #include <sys/types.h>
49 #ifdef HAVE_SYS_PARAM_H
50 #include <sys/param.h>
51 #endif
52
53 #ifdef NATIVE_WIN32
54 #  define STRICT                        /* Strict typing, please */
55 #  include <windows.h>
56 #  include <direct.h>
57 #  include <errno.h>
58 #  include <ctype.h>
59 #  ifdef _MSC_VER
60 #    include <io.h>
61 #  endif /* _MSC_VER */
62 #endif /* NATIVE_WIN32 */
63
64 /* implement Glib's inline functions
65  */
66 #define G_INLINE_FUNC extern
67 #define G_CAN_INLINE 1
68 #include "glib.h"
69
70 #ifdef  MAXPATHLEN
71 #define G_PATH_LENGTH   (MAXPATHLEN + 1)
72 #elif   defined (PATH_MAX)
73 #define G_PATH_LENGTH   (PATH_MAX + 1)
74 #else   /* !MAXPATHLEN */
75 #define G_PATH_LENGTH   (2048 + 1)
76 #endif  /* !MAXPATHLEN && !PATH_MAX */
77
78 const guint glib_major_version = GLIB_MAJOR_VERSION;
79 const guint glib_minor_version = GLIB_MINOR_VERSION;
80 const guint glib_micro_version = GLIB_MICRO_VERSION;
81 const guint glib_interface_age = GLIB_INTERFACE_AGE;
82 const guint glib_binary_age = GLIB_BINARY_AGE;
83
84 void
85 g_atexit (GVoidFunc func)
86 {
87   gint result;
88   gchar *error = NULL;
89
90   /* keep this in sync with glib.h */
91
92 #ifdef  G_NATIVE_ATEXIT
93   result = ATEXIT (func);
94   if (result)
95     error = g_strerror (errno);
96 #elif defined (HAVE_ATEXIT)
97 #  ifdef NeXT /* @#%@! NeXTStep */
98   result = !atexit ((void (*)(void)) func);
99   if (result)
100     error = g_strerror (errno);
101 #  else
102   result = atexit ((void (*)(void)) func);
103   if (result)
104     error = g_strerror (errno);
105 #  endif /* NeXT */
106 #elif defined (HAVE_ON_EXIT)
107   result = on_exit ((void (*)(int, void *)) func, NULL);
108   if (result)
109     error = g_strerror (errno);
110 #else
111   result = 0;
112   error = "no implementation";
113 #endif /* G_NATIVE_ATEXIT */
114
115   if (error)
116     g_error ("Could not register atexit() function: %s", error);
117 }
118
119 gint
120 g_snprintf (gchar       *str,
121             gulong       n,
122             gchar const *fmt,
123             ...)
124 {
125 #ifdef  HAVE_VSNPRINTF
126   va_list args;
127   gint retval;
128   
129   va_start (args, fmt);
130   retval = vsnprintf (str, n, fmt, args);
131   va_end (args);
132   
133   return retval;
134 #else   /* !HAVE_VSNPRINTF */
135   gchar *printed;
136   va_list args;
137   
138   va_start (args, fmt);
139   printed = g_strdup_vprintf (fmt, args);
140   va_end (args);
141   
142   strncpy (str, printed, n);
143   str[n-1] = '\0';
144
145   g_free (printed);
146   
147   return strlen (str);
148 #endif  /* !HAVE_VSNPRINTF */
149 }
150
151 gint
152 g_vsnprintf (gchar       *str,
153              gulong       n,
154              gchar const *fmt,
155              va_list      args)
156 {
157 #ifdef  HAVE_VSNPRINTF
158   gint retval;
159   
160   retval = vsnprintf (str, n, fmt, args);
161   
162   return retval;
163 #else   /* !HAVE_VSNPRINTF */
164   gchar *printed;
165   
166   printed = g_strdup_vprintf (fmt, args);
167   strncpy (str, printed, n);
168   str[n-1] = '\0';
169
170   g_free (printed);
171   
172   return strlen (str);
173 #endif /* !HAVE_VSNPRINTF */
174 }
175
176 guint        
177 g_parse_debug_string  (const gchar *string, 
178                        GDebugKey   *keys, 
179                        guint        nkeys)
180 {
181   guint i;
182   guint result = 0;
183   
184   g_return_val_if_fail (string != NULL, 0);
185   
186   if (!g_strcasecmp (string, "all"))
187     {
188       for (i=0; i<nkeys; i++)
189         result |= keys[i].value;
190     }
191   else
192     {
193       gchar *str = g_strdup (string);
194       gchar *p = str;
195       gchar *q;
196       gboolean done = FALSE;
197       
198       while (*p && !done)
199         {
200           q = strchr (p, ':');
201           if (!q)
202             {
203               q = p + strlen(p);
204               done = TRUE;
205             }
206           
207           *q = 0;
208           
209           for (i=0; i<nkeys; i++)
210             if (!g_strcasecmp(keys[i].key, p))
211               result |= keys[i].value;
212           
213           p = q+1;
214         }
215       
216       g_free (str);
217     }
218   
219   return result;
220 }
221
222 gchar*
223 g_basename (const gchar    *file_name)
224 {
225   register gchar *base;
226   
227   g_return_val_if_fail (file_name != NULL, NULL);
228   
229   base = strrchr (file_name, G_DIR_SEPARATOR);
230   if (base)
231     return base + 1;
232
233 #ifdef NATIVE_WIN32
234   if (isalpha (file_name[0]) && file_name[1] == ':')
235     return (gchar*) file_name + 2;
236 #endif /* NATIVE_WIN32 */
237   
238   return (gchar*) file_name;
239 }
240
241 gboolean
242 g_path_is_absolute (const gchar *file_name)
243 {
244   g_return_val_if_fail (file_name != NULL, FALSE);
245   
246   if (file_name[0] == G_DIR_SEPARATOR)
247     return TRUE;
248
249 #ifdef NATIVE_WIN32
250   if (isalpha (file_name[0]) && file_name[1] == ':' && file_name[2] == G_DIR_SEPARATOR)
251     return TRUE;
252 #endif
253
254   return FALSE;
255 }
256
257 gchar*
258 g_path_skip_root (gchar *file_name)
259 {
260   g_return_val_if_fail (file_name != NULL, NULL);
261   
262   if (file_name[0] == G_DIR_SEPARATOR)
263     return file_name + 1;
264
265 #ifdef NATIVE_WIN32
266   if (isalpha (file_name[0]) && file_name[1] == ':' && file_name[2] == G_DIR_SEPARATOR)
267     return file_name + 3;
268 #endif
269
270   return NULL;
271 }
272
273 gchar*
274 g_dirname (const gchar     *file_name)
275 {
276   register gchar *base;
277   register guint len;
278   
279   g_return_val_if_fail (file_name != NULL, NULL);
280   
281   base = strrchr (file_name, G_DIR_SEPARATOR);
282   if (!base)
283     return g_strdup (".");
284   while (base > file_name && *base == G_DIR_SEPARATOR)
285     base--;
286   len = (guint) 1 + base - file_name;
287   
288   base = g_new (gchar, len + 1);
289   g_memmove (base, file_name, len);
290   base[len] = 0;
291   
292   return base;
293 }
294
295 gchar*
296 g_get_current_dir (void)
297 {
298   gchar *buffer;
299   gchar *dir;
300
301   buffer = g_new (gchar, G_PATH_LENGTH);
302   *buffer = 0;
303   
304   /* We don't use getcwd(3) on SUNOS, because, it does a popen("pwd")
305    * and, if that wasn't bad enough, hangs in doing so.
306    */
307 #if     defined (sun) && !defined (__SVR4)
308   dir = getwd (buffer);
309 #else   /* !sun */
310   dir = getcwd (buffer, G_PATH_LENGTH - 1);
311 #endif  /* !sun */
312   
313   if (!dir || !*buffer)
314     {
315       /* hm, should we g_error() out here?
316        * this can happen if e.g. "./" has mode \0000
317        */
318       buffer[0] = G_DIR_SEPARATOR;
319       buffer[1] = 0;
320     }
321
322   dir = g_strdup (buffer);
323   g_free (buffer);
324   
325   return dir;
326 }
327
328 gchar*
329 g_getenv (const gchar *variable)
330 {
331 #ifndef NATIVE_WIN32
332   g_return_val_if_fail (variable != NULL, NULL);
333
334   return getenv (variable);
335 #else
336   gchar *v;
337   guint k;
338   static gchar *p = NULL;
339   static gint l;
340   gchar dummy[2];
341
342   g_return_val_if_fail (variable != NULL, NULL);
343   
344   v = getenv (variable);
345   if (!v)
346     return NULL;
347   
348   /* On Windows NT, it is relatively typical that environment variables
349    * contain references to other environment variables. Handle that by
350    * calling ExpandEnvironmentStrings.
351    */
352
353   /* First check how much space we need */
354   k = ExpandEnvironmentStrings (v, dummy, 2);
355   /* Then allocate that much, and actualy do the expansion */
356   if (p == NULL)
357     {
358       p = g_malloc (k);
359       l = k;
360     }
361   else if (k > l)
362     {
363       p = g_realloc (p, k);
364       l = k;
365     }
366   ExpandEnvironmentStrings (v, p, k);
367   return p;
368 #endif
369 }
370
371
372 G_LOCK_DEFINE_STATIC (g_utils_global);
373
374 static  gchar   *g_tmp_dir = NULL;
375 static  gchar   *g_user_name = NULL;
376 static  gchar   *g_real_name = NULL;
377 static  gchar   *g_home_dir = NULL;
378
379 /* HOLDS: g_utils_global_lock */
380 static void
381 g_get_any_init (void)
382 {
383   if (!g_tmp_dir)
384     {
385       g_tmp_dir = g_strdup (g_getenv ("TMPDIR"));
386       if (!g_tmp_dir)
387         g_tmp_dir = g_strdup (g_getenv ("TMP"));
388       if (!g_tmp_dir)
389         g_tmp_dir = g_strdup (g_getenv ("TEMP"));
390       
391 #ifdef P_tmpdir
392       if (!g_tmp_dir)
393         {
394           int k;
395           g_tmp_dir = g_strdup (P_tmpdir);
396           k = strlen (g_tmp_dir);
397           if (g_tmp_dir[k-1] == G_DIR_SEPARATOR)
398             g_tmp_dir[k-1] = '\0';
399         }
400 #endif
401       
402       if (!g_tmp_dir)
403         {
404 #ifndef NATIVE_WIN32
405           g_tmp_dir = g_strdup ("/tmp");
406 #else /* NATIVE_WIN32 */
407           g_tmp_dir = g_strdup ("C:\\");
408 #endif /* NATIVE_WIN32 */
409         }
410       
411       if (!g_home_dir)
412         g_home_dir = g_strdup (g_getenv ("HOME"));
413       
414 #ifdef NATIVE_WIN32
415       if (!g_home_dir)
416         {
417           /* The official way to specify a home directory on NT is
418            * the HOMEDRIVE and HOMEPATH environment variables.
419            *
420            * This is inside #ifdef NATIVE_WIN32 because with the cygwin dll,
421            * HOME should be a POSIX style pathname.
422            */
423           
424           if (getenv ("HOMEDRIVE") != NULL && getenv ("HOMEPATH") != NULL)
425             {
426               gchar *homedrive, *homepath;
427               
428               homedrive = g_strdup (g_getenv ("HOMEDRIVE"));
429               homepath = g_strdup (g_getenv ("HOMEPATH"));
430               
431               g_home_dir = g_strconcat (homedrive, homepath, NULL);
432               g_free (homedrive);
433               g_free (homepath);
434             }
435         }
436 #endif /* !NATIVE_WIN32 */
437       
438 #ifdef HAVE_PWD_H
439       {
440         struct passwd *pw = NULL;
441         gpointer buffer = NULL;
442         
443 #  ifdef HAVE_GETPWUID_R
444         struct passwd pwd;
445 #    ifdef _SC_GETPW_R_SIZE_MAX  
446         /* This reurns the maximum length */
447         guint bufsize = sysconf (_SC_GETPW_R_SIZE_MAX);
448 #    else /* _SC_GETPW_R_SIZE_MAX */
449         guint bufsize = 64;
450 #    endif /* _SC_GETPW_R_SIZE_MAX */
451         gint error;
452         
453         do
454           {
455             g_free (buffer);
456             buffer = g_malloc (bufsize);
457             errno = 0;
458             
459 #    ifdef HAVE_GETPWUID_R_POSIX
460             error = getpwuid_r (getuid (), &pwd, buffer, bufsize, &pw);
461             error = error < 0 ? errno : error;
462 #    else /* !HAVE_GETPWUID_R_POSIX */
463 #      ifdef _AIX
464             error = getpwuid_r (getuid (), &pwd, buffer, bufsize);
465             pw = error == 0 ? &pwd : NULL;
466 #      else /* !_AIX */
467             pw = getpwuid_r (getuid (), &pwd, buffer, bufsize);
468             error = pw ? 0 : errno;
469 #      endif /* !_AIX */            
470 #    endif /* !HAVE_GETPWUID_R_POSIX */
471             
472             if (!pw)
473               {
474                 /* we bail out prematurely if the user id can't be found
475                  * (should be pretty rare case actually), or if the buffer
476                  * should be sufficiently big and lookups are still not
477                  * successfull.
478                  */
479                 if (error == 0 || error == ENOENT)
480                   {
481                     g_warning ("getpwuid_r(): failed due to: No such user %d.",
482                                getuid ());
483                     break;
484                   }
485                 if (bufsize > 32 * 1024)
486                   {
487                     g_warning ("getpwuid_r(): failed due to: %s.",
488                                g_strerror (error));
489                     break;
490                   }
491                 
492                 bufsize *= 2;
493               }
494           }
495         while (!pw);
496 #  endif /* !HAVE_GETPWUID_R */
497         
498         if (!pw)
499           {
500             setpwent ();
501             pw = getpwuid (getuid ());
502             endpwent ();
503           }
504         if (pw)
505           {
506             g_user_name = g_strdup (pw->pw_name);
507 #ifdef HAVE_PW_GECOS
508             g_real_name = g_strdup (pw->pw_gecos);
509 #else
510             g_real_name = g_strdup (g_user_name);
511 #endif
512             if (!g_home_dir)
513               g_home_dir = g_strdup (pw->pw_dir);
514           }
515         g_free (buffer);
516       }
517       
518 #else /* !HAVE_PWD_H */
519       
520 #  ifdef NATIVE_WIN32
521       {
522         guint len = 17;
523         gchar buffer[17];
524         
525         if (GetUserName (buffer, &len))
526           {
527             g_user_name = g_strdup (buffer);
528             g_real_name = g_strdup (buffer);
529           }
530       }
531 #  endif /* NATIVE_WIN32 */
532       
533 #endif /* !HAVE_PWD_H */
534       
535 #ifdef __EMX__
536       /* change '\\' in %HOME% to '/' */
537       g_strdelimit (g_home_dir, "\\",'/');
538 #endif
539       if (!g_user_name)
540         g_user_name = g_strdup ("somebody");
541       if (!g_real_name)
542         g_real_name = g_strdup ("Unknown");
543       else
544         {
545           gchar *p;
546
547           for (p = g_real_name; *p; p++)
548             if (*p == ',')
549               {
550                 *p = 0;
551                 p = g_strdup (g_real_name);
552                 g_free (g_real_name);
553                 g_real_name = p;
554                 break;
555               }
556         }
557     }
558 }
559
560 gchar*
561 g_get_user_name (void)
562 {
563   G_LOCK (g_utils_global);
564   if (!g_tmp_dir)
565     g_get_any_init ();
566   G_UNLOCK (g_utils_global);
567   
568   return g_user_name;
569 }
570
571 gchar*
572 g_get_real_name (void)
573 {
574   G_LOCK (g_utils_global);
575   if (!g_tmp_dir)
576     g_get_any_init ();
577   G_UNLOCK (g_utils_global);
578  
579   return g_real_name;
580 }
581
582 /* Return the home directory of the user. If there is a HOME
583  * environment variable, its value is returned, otherwise use some
584  * system-dependent way of finding it out. If no home directory can be
585  * deduced, return NULL.
586  */
587
588 gchar*
589 g_get_home_dir (void)
590 {
591   G_LOCK (g_utils_global);
592   if (!g_tmp_dir)
593     g_get_any_init ();
594   G_UNLOCK (g_utils_global);
595   
596   return g_home_dir;
597 }
598
599 /* Return a directory to be used to store temporary files. This is the
600  * value of the TMPDIR, TMP or TEMP environment variables (they are
601  * checked in that order). If none of those exist, use P_tmpdir from
602  * stdio.h.  If that isn't defined, return "/tmp" on POSIXly systems,
603  * and C:\ on Windows.
604  */
605
606 gchar*
607 g_get_tmp_dir (void)
608 {
609   G_LOCK (g_utils_global);
610   if (!g_tmp_dir)
611     g_get_any_init ();
612   G_UNLOCK (g_utils_global);
613   
614   return g_tmp_dir;
615 }
616
617 static gchar *g_prgname = NULL;
618
619 gchar*
620 g_get_prgname (void)
621 {
622   gchar* retval;
623
624   G_LOCK (g_utils_global);
625   retval = g_prgname;
626   G_UNLOCK (g_utils_global);
627
628   return retval;
629 }
630
631 void
632 g_set_prgname (const gchar *prgname)
633 {
634   gchar *c;
635     
636   G_LOCK (g_utils_global);
637   c = g_prgname;
638   g_prgname = g_strdup (prgname);
639   g_free (c);
640   G_UNLOCK (g_utils_global);
641 }
642
643 guint
644 g_direct_hash (gconstpointer v)
645 {
646   return GPOINTER_TO_UINT (v);
647 }
648
649 gint
650 g_direct_equal (gconstpointer v1,
651                 gconstpointer v2)
652 {
653   return v1 == v2;
654 }
655
656 gint
657 g_int_equal (gconstpointer v1,
658              gconstpointer v2)
659 {
660   return *((const gint*) v1) == *((const gint*) v2);
661 }
662
663 guint
664 g_int_hash (gconstpointer v)
665 {
666   return *(const gint*) v;
667 }
668
669 #ifdef NATIVE_WIN32
670
671 int
672 gwin_ftruncate (gint  fd,
673                 guint size)
674 {
675   HANDLE hfile;
676   guint curpos;
677
678   g_return_val_if_fail (fd >= 0, -1);
679   
680   hfile = (HANDLE) _get_osfhandle (fd);
681   curpos = SetFilePointer (hfile, 0, NULL, FILE_CURRENT);
682   if (curpos == 0xFFFFFFFF
683       || SetFilePointer (hfile, size, NULL, FILE_BEGIN) == 0xFFFFFFFF
684       || !SetEndOfFile (hfile))
685     {
686       gint error = GetLastError ();
687
688       switch (error)
689         {
690         case ERROR_INVALID_HANDLE:
691           errno = EBADF;
692           break;
693         default:
694           errno = EIO;
695           break;
696         }
697
698       return -1;
699     }
700
701   return 0;
702 }
703
704 DIR*
705 gwin_opendir (const char *dirname)
706 {
707   DIR *result;
708   gchar *mask;
709   guint k;
710
711   g_return_val_if_fail (dirname != NULL, NULL);
712
713   result = g_new0 (DIR, 1);
714   result->find_file_data = g_new0 (WIN32_FIND_DATA, 1);
715   result->dir_name = g_strdup (dirname);
716   
717   k = strlen (result->dir_name);
718   if (k && result->dir_name[k - 1] == '\\')
719     {
720       result->dir_name[k - 1] = '\0';
721       k--;
722     }
723   mask = g_strdup_printf ("%s\\*", result->dir_name);
724
725   result->find_file_handle = (guint) FindFirstFile (mask,
726                                              (LPWIN32_FIND_DATA) result->find_file_data);
727   g_free (mask);
728
729   if (result->find_file_handle == (guint) INVALID_HANDLE_VALUE)
730     {
731       int error = GetLastError ();
732
733       g_free (result->dir_name);
734       g_free (result->find_file_data);
735       g_free (result);
736       switch (error)
737         {
738         default:
739           errno = EIO;
740           return NULL;
741         }
742     }
743   result->just_opened = TRUE;
744
745   return result;
746 }
747
748 struct dirent*
749 gwin_readdir (DIR *dir)
750 {
751   static struct dirent result;
752
753   g_return_val_if_fail (dir != NULL, NULL);
754
755   if (dir->just_opened)
756     dir->just_opened = FALSE;
757   else
758     {
759       if (!FindNextFile ((HANDLE) dir->find_file_handle,
760                          (LPWIN32_FIND_DATA) dir->find_file_data))
761         {
762           int error = GetLastError ();
763
764           switch (error)
765             {
766             case ERROR_NO_MORE_FILES:
767               return NULL;
768             default:
769               errno = EIO;
770               return NULL;
771             }
772         }
773     }
774   strcpy (result.d_name, g_basename (((LPWIN32_FIND_DATA) dir->find_file_data)->cFileName));
775       
776   return &result;
777 }
778
779 void
780 gwin_rewinddir (DIR *dir)
781 {
782   gchar *mask;
783
784   g_return_if_fail (dir != NULL);
785
786   if (!FindClose ((HANDLE) dir->find_file_handle))
787     g_warning ("gwin_rewinddir(): FindClose() failed\n");
788
789   mask = g_strdup_printf ("%s\\*", dir->dir_name);
790   dir->find_file_handle = (guint) FindFirstFile (mask,
791                                           (LPWIN32_FIND_DATA) dir->find_file_data);
792   g_free (mask);
793
794   if (dir->find_file_handle == (guint) INVALID_HANDLE_VALUE)
795     {
796       int error = GetLastError ();
797
798       switch (error)
799         {
800         default:
801           errno = EIO;
802           return;
803         }
804     }
805   dir->just_opened = TRUE;
806 }  
807
808 gint
809 gwin_closedir (DIR *dir)
810 {
811   g_return_val_if_fail (dir != NULL, -1);
812
813   if (!FindClose ((HANDLE) dir->find_file_handle))
814     {
815       int error = GetLastError ();
816
817       switch (error)
818         {
819         default:
820           errno = EIO; return -1;
821         }
822     }
823
824   g_free (dir->dir_name);
825   g_free (dir->find_file_data);
826   g_free (dir);
827
828   return 0;
829 }
830
831 #endif /* NATIVE_WIN32 */