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