Merge remote branch 'gvdb/master'
[platform/upstream/glib.git] / build / win32 / dirent / dirent.c
1 /*\r
2  * dirent.c\r
3  * This file has no copyright assigned and is placed in the Public Domain.\r
4  * This file is a part of the mingw-runtime package.\r
5  * No warranty is given; refer to the file DISCLAIMER within the package.\r
6  *\r
7  * Derived from DIRLIB.C by Matt J. Weinstein\r
8  * This note appears in the DIRLIB.H\r
9  * DIRLIB.H by M. J. Weinstein   Released to public domain 1-Jan-89\r
10  *\r
11  * Updated by Jeremy Bettis <jeremy@hksys.com>\r
12  * Significantly revised and rewinddir, seekdir and telldir added by Colin\r
13  * Peters <colin@fu.is.saga-u.ac.jp>\r
14  *      \r
15  */\r
16 \r
17 #include <stdlib.h>\r
18 #include <errno.h>\r
19 #include <string.h>\r
20 #include <io.h>\r
21 #include <direct.h>\r
22 \r
23 #include "dirent.h"\r
24 \r
25 #define WIN32_LEAN_AND_MEAN\r
26 #include <windows.h> /* for GetFileAttributes */\r
27 \r
28 #include <tchar.h>\r
29 \r
30 #ifdef _UNICODE\r
31 #define _tdirent        _wdirent\r
32 #define _TDIR           _WDIR\r
33 #define _topendir       _wopendir\r
34 #define _tclosedir      _wclosedir\r
35 #define _treaddir       _wreaddir\r
36 #define _trewinddir     _wrewinddir\r
37 #define _ttelldir       _wtelldir\r
38 #define _tseekdir       _wseekdir\r
39 #else\r
40 #define _tdirent        dirent\r
41 #define _TDIR           DIR\r
42 #define _topendir       opendir\r
43 #define _tclosedir      closedir\r
44 #define _treaddir       readdir\r
45 #define _trewinddir     rewinddir\r
46 #define _ttelldir       telldir\r
47 #define _tseekdir       seekdir\r
48 #endif\r
49 \r
50 #define SUFFIX  _T("*")\r
51 #define SLASH   _T("\\")\r
52 \r
53 \r
54 /*\r
55  * opendir\r
56  *\r
57  * Returns a pointer to a DIR structure appropriately filled in to begin\r
58  * searching a directory.\r
59  */\r
60 _TDIR *\r
61 _topendir (const _TCHAR *szPath)\r
62 {\r
63   _TDIR *nd;\r
64   unsigned int rc;\r
65   _TCHAR szFullPath[MAX_PATH];\r
66         \r
67   errno = 0;\r
68 \r
69   if (!szPath)\r
70     {\r
71       errno = EFAULT;\r
72       return (_TDIR *) 0;\r
73     }\r
74 \r
75   if (szPath[0] == _T('\0'))\r
76     {\r
77       errno = ENOTDIR;\r
78       return (_TDIR *) 0;\r
79     }\r
80 \r
81   /* Attempt to determine if the given path really is a directory. */\r
82   rc = GetFileAttributes (szPath);\r
83   if (rc == (unsigned int)-1)\r
84     {\r
85       /* call GetLastError for more error info */\r
86       errno = ENOENT;\r
87       return (_TDIR *) 0;\r
88     }\r
89   if (!(rc & FILE_ATTRIBUTE_DIRECTORY))\r
90     {\r
91       /* Error, entry exists but not a directory. */\r
92       errno = ENOTDIR;\r
93       return (_TDIR *) 0;\r
94     }\r
95 \r
96   /* Make an absolute pathname.  */\r
97   _tfullpath (szFullPath, szPath, MAX_PATH);\r
98 \r
99   /* Allocate enough space to store DIR structure and the complete\r
100    * directory path given. */\r
101   nd = (_TDIR *) malloc (sizeof (_TDIR) + (_tcslen(szFullPath) + _tcslen (SLASH) +\r
102                          _tcslen(SUFFIX) + 1) * sizeof(_TCHAR));\r
103 \r
104   if (!nd)\r
105     {\r
106       /* Error, out of memory. */\r
107       errno = ENOMEM;\r
108       return (_TDIR *) 0;\r
109     }\r
110 \r
111   /* Create the search expression. */\r
112   _tcscpy (nd->dd_name, szFullPath);\r
113 \r
114   /* Add on a slash if the path does not end with one. */\r
115   if (nd->dd_name[0] != _T('\0') &&\r
116       nd->dd_name[_tcslen (nd->dd_name) - 1] != _T('/') &&\r
117       nd->dd_name[_tcslen (nd->dd_name) - 1] != _T('\\'))\r
118     {\r
119       _tcscat (nd->dd_name, SLASH);\r
120     }\r
121 \r
122   /* Add on the search pattern */\r
123   _tcscat (nd->dd_name, SUFFIX);\r
124 \r
125   /* Initialize handle to -1 so that a premature closedir doesn't try\r
126    * to call _findclose on it. */\r
127   nd->dd_handle = -1;\r
128 \r
129   /* Initialize the status. */\r
130   nd->dd_stat = 0;\r
131 \r
132   /* Initialize the dirent structure. ino and reclen are invalid under\r
133    * Win32, and name simply points at the appropriate part of the\r
134    * findfirst_t structure. */\r
135   nd->dd_dir.d_ino = 0;\r
136   nd->dd_dir.d_reclen = 0;\r
137   nd->dd_dir.d_namlen = 0;\r
138   memset (nd->dd_dir.d_name, 0, FILENAME_MAX);\r
139 \r
140   return nd;\r
141 }\r
142 \r
143 \r
144 /*\r
145  * readdir\r
146  *\r
147  * Return a pointer to a dirent structure filled with the information on the\r
148  * next entry in the directory.\r
149  */\r
150 struct _tdirent *\r
151 _treaddir (_TDIR * dirp)\r
152 {\r
153   errno = 0;\r
154 \r
155   /* Check for valid DIR struct. */\r
156   if (!dirp)\r
157     {\r
158       errno = EFAULT;\r
159       return (struct _tdirent *) 0;\r
160     }\r
161 \r
162   if (dirp->dd_stat < 0)\r
163     {\r
164       /* We have already returned all files in the directory\r
165        * (or the structure has an invalid dd_stat). */\r
166       return (struct _tdirent *) 0;\r
167     }\r
168   else if (dirp->dd_stat == 0)\r
169     {\r
170       /* We haven't started the search yet. */\r
171       /* Start the search */\r
172       dirp->dd_handle = _tfindfirst (dirp->dd_name, &(dirp->dd_dta));\r
173 \r
174           if (dirp->dd_handle == -1)\r
175         {\r
176           /* Whoops! Seems there are no files in that\r
177            * directory. */\r
178           dirp->dd_stat = -1;\r
179         }\r
180       else\r
181         {\r
182           dirp->dd_stat = 1;\r
183         }\r
184     }\r
185   else\r
186     {\r
187       /* Get the next search entry. */\r
188       if (_tfindnext (dirp->dd_handle, &(dirp->dd_dta)))\r
189         {\r
190           /* We are off the end or otherwise error.     \r
191              _findnext sets errno to ENOENT if no more file\r
192              Undo this. */\r
193           DWORD winerr = GetLastError();\r
194           if (winerr == ERROR_NO_MORE_FILES)\r
195             errno = 0;  \r
196           _findclose (dirp->dd_handle);\r
197           dirp->dd_handle = -1;\r
198           dirp->dd_stat = -1;\r
199         }\r
200       else\r
201         {\r
202           /* Update the status to indicate the correct\r
203            * number. */\r
204           dirp->dd_stat++;\r
205         }\r
206     }\r
207 \r
208   if (dirp->dd_stat > 0)\r
209     {\r
210       /* Successfully got an entry. Everything about the file is\r
211        * already appropriately filled in except the length of the\r
212        * file name. */\r
213       dirp->dd_dir.d_namlen = _tcslen (dirp->dd_dta.name);\r
214       _tcscpy (dirp->dd_dir.d_name, dirp->dd_dta.name);\r
215       return &dirp->dd_dir;\r
216     }\r
217 \r
218   return (struct _tdirent *) 0;\r
219 }\r
220 \r
221 \r
222 /*\r
223  * closedir\r
224  *\r
225  * Frees up resources allocated by opendir.\r
226  */\r
227 int\r
228 _tclosedir (_TDIR * dirp)\r
229 {\r
230   int rc;\r
231 \r
232   errno = 0;\r
233   rc = 0;\r
234 \r
235   if (!dirp)\r
236     {\r
237       errno = EFAULT;\r
238       return -1;\r
239     }\r
240 \r
241   if (dirp->dd_handle != -1)\r
242     {\r
243       rc = _findclose (dirp->dd_handle);\r
244     }\r
245 \r
246   /* Delete the dir structure. */\r
247   free (dirp);\r
248 \r
249   return rc;\r
250 }\r
251 \r
252 /*\r
253  * rewinddir\r
254  *\r
255  * Return to the beginning of the directory "stream". We simply call findclose\r
256  * and then reset things like an opendir.\r
257  */\r
258 void\r
259 _trewinddir (_TDIR * dirp)\r
260 {\r
261   errno = 0;\r
262 \r
263   if (!dirp)\r
264     {\r
265       errno = EFAULT;\r
266       return;\r
267     }\r
268 \r
269   if (dirp->dd_handle != -1)\r
270     {\r
271       _findclose (dirp->dd_handle);\r
272     }\r
273 \r
274   dirp->dd_handle = -1;\r
275   dirp->dd_stat = 0;\r
276 }\r
277 \r
278 /*\r
279  * telldir\r
280  *\r
281  * Returns the "position" in the "directory stream" which can be used with\r
282  * seekdir to go back to an old entry. We simply return the value in stat.\r
283  */\r
284 long\r
285 _ttelldir (_TDIR * dirp)\r
286 {\r
287   errno = 0;\r
288 \r
289   if (!dirp)\r
290     {\r
291       errno = EFAULT;\r
292       return -1;\r
293     }\r
294   return dirp->dd_stat;\r
295 }\r
296 \r
297 /*\r
298  * seekdir\r
299  *\r
300  * Seek to an entry previously returned by telldir. We rewind the directory\r
301  * and call readdir repeatedly until either dd_stat is the position number\r
302  * or -1 (off the end). This is not perfect, in that the directory may\r
303  * have changed while we weren't looking. But that is probably the case with\r
304  * any such system.\r
305  */\r
306 void\r
307 _tseekdir (_TDIR * dirp, long lPos)\r
308 {\r
309   errno = 0;\r
310 \r
311   if (!dirp)\r
312     {\r
313       errno = EFAULT;\r
314       return;\r
315     }\r
316 \r
317   if (lPos < -1)\r
318     {\r
319       /* Seeking to an invalid position. */\r
320       errno = EINVAL;\r
321       return;\r
322     }\r
323   else if (lPos == -1)\r
324     {\r
325       /* Seek past end. */\r
326       if (dirp->dd_handle != -1)\r
327         {\r
328           _findclose (dirp->dd_handle);\r
329         }\r
330       dirp->dd_handle = -1;\r
331       dirp->dd_stat = -1;\r
332     }\r
333   else\r
334     {\r
335       /* Rewind and read forward to the appropriate index. */\r
336       _trewinddir (dirp);\r
337 \r
338       while ((dirp->dd_stat < lPos) && _treaddir (dirp))\r
339         ;\r
340     }\r
341 }\r