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