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