Support added for building using a GNU toolchain on Win32,
[platform/upstream/glib.git] / 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             g_real_name = g_strdup (pw->pw_gecos);
508             if (!g_home_dir)
509               g_home_dir = g_strdup (pw->pw_dir);
510           }
511         g_free (buffer);
512       }
513       
514 #else /* !HAVE_PWD_H */
515       
516 #  ifdef NATIVE_WIN32
517       {
518         guint len = 17;
519         gchar buffer[17];
520         
521         if (GetUserName (buffer, &len))
522           {
523             g_user_name = g_strdup (buffer);
524             g_real_name = g_strdup (buffer);
525           }
526       }
527 #  endif /* NATIVE_WIN32 */
528       
529 #endif /* !HAVE_PWD_H */
530       
531 #ifdef __EMX__
532       /* change '\\' in %HOME% to '/' */
533       g_strdelimit (g_home_dir, "\\",'/');
534 #endif
535       if (!g_user_name)
536         g_user_name = g_strdup ("somebody");
537       if (!g_real_name)
538         g_real_name = g_strdup ("Unknown");
539       else
540         {
541           gchar *p;
542
543           for (p = g_real_name; *p; p++)
544             if (*p == ',')
545               {
546                 *p = 0;
547                 p = g_strdup (g_real_name);
548                 g_free (g_real_name);
549                 g_real_name = p;
550                 break;
551               }
552         }
553     }
554 }
555
556 gchar*
557 g_get_user_name (void)
558 {
559   G_LOCK (g_utils_global);
560   if (!g_tmp_dir)
561     g_get_any_init ();
562   G_UNLOCK (g_utils_global);
563   
564   return g_user_name;
565 }
566
567 gchar*
568 g_get_real_name (void)
569 {
570   G_LOCK (g_utils_global);
571   if (!g_tmp_dir)
572     g_get_any_init ();
573   G_UNLOCK (g_utils_global);
574  
575   return g_real_name;
576 }
577
578 /* Return the home directory of the user. If there is a HOME
579  * environment variable, its value is returned, otherwise use some
580  * system-dependent way of finding it out. If no home directory can be
581  * deduced, return NULL.
582  */
583
584 gchar*
585 g_get_home_dir (void)
586 {
587   G_LOCK (g_utils_global);
588   if (!g_tmp_dir)
589     g_get_any_init ();
590   G_UNLOCK (g_utils_global);
591   
592   return g_home_dir;
593 }
594
595 /* Return a directory to be used to store temporary files. This is the
596  * value of the TMPDIR, TMP or TEMP environment variables (they are
597  * checked in that order). If none of those exist, use P_tmpdir from
598  * stdio.h.  If that isn't defined, return "/tmp" on POSIXly systems,
599  * and C:\ on Windows.
600  */
601
602 gchar*
603 g_get_tmp_dir (void)
604 {
605   G_LOCK (g_utils_global);
606   if (!g_tmp_dir)
607     g_get_any_init ();
608   G_UNLOCK (g_utils_global);
609   
610   return g_tmp_dir;
611 }
612
613 static gchar *g_prgname = NULL;
614
615 gchar*
616 g_get_prgname (void)
617 {
618   gchar* retval;
619
620   G_LOCK (g_utils_global);
621   retval = g_prgname;
622   G_UNLOCK (g_utils_global);
623
624   return retval;
625 }
626
627 void
628 g_set_prgname (const gchar *prgname)
629 {
630   gchar *c;
631     
632   G_LOCK (g_utils_global);
633   c = g_prgname;
634   g_prgname = g_strdup (prgname);
635   g_free (c);
636   G_UNLOCK (g_utils_global);
637 }
638
639 guint
640 g_direct_hash (gconstpointer v)
641 {
642   return GPOINTER_TO_UINT (v);
643 }
644
645 gint
646 g_direct_equal (gconstpointer v1,
647                 gconstpointer v2)
648 {
649   return v1 == v2;
650 }
651
652 gint
653 g_int_equal (gconstpointer v1,
654              gconstpointer v2)
655 {
656   return *((const gint*) v1) == *((const gint*) v2);
657 }
658
659 guint
660 g_int_hash (gconstpointer v)
661 {
662   return *(const gint*) v;
663 }
664
665 #if 0 /* Old IO Channels */
666
667 GIOChannel*
668 g_iochannel_new (gint fd)
669 {
670   GIOChannel *channel = g_new (GIOChannel, 1);
671
672   channel->fd = fd;
673
674 #ifdef NATIVE_WIN32
675   channel->peer = 0;
676   channel->peer_fd = 0;
677   channel->offset = 0;
678   channel->need_wakeups = 0;
679 #endif /* NATIVE_WIN32 */
680
681   return channel;
682 }
683
684 void
685 g_iochannel_free (GIOChannel *channel)
686 {
687   g_return_if_fail (channel != NULL);
688
689   g_free (channel);
690 }
691
692 void
693 g_iochannel_close_and_free (GIOChannel *channel)
694 {
695   g_return_if_fail (channel != NULL);
696
697   close (channel->fd);
698
699   g_iochannel_free (channel);
700 }
701
702 #undef g_iochannel_wakeup_peer
703
704 void
705 g_iochannel_wakeup_peer (GIOChannel *channel)
706 {
707 #ifdef NATIVE_WIN32
708   static guint message = 0;
709 #endif
710
711   g_return_if_fail (channel != NULL);
712
713 #ifdef NATIVE_WIN32
714   if (message == 0)
715     message = RegisterWindowMessage ("gdk-pipe-readable");
716
717 #  if 0
718   g_print ("g_iochannel_wakeup_peer: calling PostThreadMessage (%#x, %d, %d, %d)\n",
719            channel->peer, message, channel->peer_fd, channel->offset);
720 #  endif
721   PostThreadMessage (channel->peer, message,
722                      channel->peer_fd, channel->offset);
723 #endif /* NATIVE_WIN32 */
724 }
725
726 #endif /* Old IO Channels */
727
728 #ifdef NATIVE_WIN32
729
730 int
731 gwin_ftruncate (gint  fd,
732                 guint size)
733 {
734   HANDLE hfile;
735   guint curpos;
736
737   g_return_val_if_fail (fd >= 0, -1);
738   
739   hfile = (HANDLE) _get_osfhandle (fd);
740   curpos = SetFilePointer (hfile, 0, NULL, FILE_CURRENT);
741   if (curpos == 0xFFFFFFFF
742       || SetFilePointer (hfile, size, NULL, FILE_BEGIN) == 0xFFFFFFFF
743       || !SetEndOfFile (hfile))
744     {
745       gint error = GetLastError ();
746
747       switch (error)
748         {
749         case ERROR_INVALID_HANDLE:
750           errno = EBADF;
751           break;
752         default:
753           errno = EIO;
754           break;
755         }
756
757       return -1;
758     }
759
760   return 0;
761 }
762
763 DIR*
764 gwin_opendir (const char *dirname)
765 {
766   DIR *result;
767   gchar *mask;
768   guint k;
769
770   g_return_val_if_fail (dirname != NULL, NULL);
771
772   result = g_new0 (DIR, 1);
773   result->find_file_data = g_new0 (WIN32_FIND_DATA, 1);
774   result->dir_name = g_strdup (dirname);
775   
776   k = strlen (result->dir_name);
777   if (k && result->dir_name[k - 1] == '\\')
778     {
779       result->dir_name[k - 1] = '\0';
780       k--;
781     }
782   mask = g_strdup_printf ("%s\\*", result->dir_name);
783
784   result->find_file_handle = (guint) FindFirstFile (mask,
785                                              (LPWIN32_FIND_DATA) result->find_file_data);
786   g_free (mask);
787
788   if (result->find_file_handle == (guint) INVALID_HANDLE_VALUE)
789     {
790       int error = GetLastError ();
791
792       g_free (result->dir_name);
793       g_free (result->find_file_data);
794       g_free (result);
795       switch (error)
796         {
797         default:
798           errno = EIO;
799           return NULL;
800         }
801     }
802   result->just_opened = TRUE;
803
804   return result;
805 }
806
807 struct dirent*
808 gwin_readdir (DIR *dir)
809 {
810   static struct dirent result;
811
812   g_return_val_if_fail (dir != NULL, NULL);
813
814   if (dir->just_opened)
815     dir->just_opened = FALSE;
816   else
817     {
818       if (!FindNextFile ((HANDLE) dir->find_file_handle,
819                          (LPWIN32_FIND_DATA) dir->find_file_data))
820         {
821           int error = GetLastError ();
822
823           switch (error)
824             {
825             case ERROR_NO_MORE_FILES:
826               return NULL;
827             default:
828               errno = EIO;
829               return NULL;
830             }
831         }
832     }
833   strcpy (result.d_name, g_basename (((LPWIN32_FIND_DATA) dir->find_file_data)->cFileName));
834       
835   return &result;
836 }
837
838 void
839 gwin_rewinddir (DIR *dir)
840 {
841   gchar *mask;
842
843   g_return_if_fail (dir != NULL);
844
845   if (!FindClose ((HANDLE) dir->find_file_handle))
846     g_warning ("gwin_rewinddir(): FindClose() failed\n");
847
848   mask = g_strdup_printf ("%s\\*", dir->dir_name);
849   dir->find_file_handle = (guint) FindFirstFile (mask,
850                                           (LPWIN32_FIND_DATA) dir->find_file_data);
851   g_free (mask);
852
853   if (dir->find_file_handle == (guint) INVALID_HANDLE_VALUE)
854     {
855       int error = GetLastError ();
856
857       switch (error)
858         {
859         default:
860           errno = EIO;
861           return;
862         }
863     }
864   dir->just_opened = TRUE;
865 }  
866
867 gint
868 gwin_closedir (DIR *dir)
869 {
870   g_return_val_if_fail (dir != NULL, -1);
871
872   if (!FindClose ((HANDLE) dir->find_file_handle))
873     {
874       int error = GetLastError ();
875
876       switch (error)
877         {
878         default:
879           errno = EIO; return -1;
880         }
881     }
882
883   g_free (dir->dir_name);
884   g_free (dir->find_file_data);
885   g_free (dir);
886
887   return 0;
888 }
889
890 #endif /* NATIVE_WIN32 */