1 /********************************************************************/
2 /*                                                                  */
3 /*  dir_win.c     Directory functions which call the Windows API.   */
4 /*  Copyright (C) 1989 - 2014  Thomas Mertes                        */
5 /*                                                                  */
6 /*  This file is part of the Seed7 Runtime Library.                 */
7 /*                                                                  */
8 /*  The Seed7 Runtime Library is free software; you can             */
9 /*  redistribute it and/or modify it under the terms of the GNU     */
10 /*  Lesser General Public License as published by the Free Software */
11 /*  Foundation; either version 2.1 of the License, or (at your      */
12 /*  option) any later version.                                      */
13 /*                                                                  */
14 /*  The Seed7 Runtime Library is distributed in the hope that it    */
15 /*  will be useful, but WITHOUT ANY WARRANTY; without even the      */
16 /*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /*  PURPOSE.  See the GNU Lesser General Public License for more    */
18 /*  details.                                                        */
19 /*                                                                  */
20 /*  You should have received a copy of the GNU Lesser General       */
21 /*  Public License along with this program; if not, write to the    */
22 /*  Free Software Foundation, Inc., 51 Franklin Street,             */
23 /*  Fifth Floor, Boston, MA  02110-1301, USA.                       */
24 /*                                                                  */
25 /*  Module: Seed7 Runtime Library                                   */
26 /*  File: seed7/src/dir_win.c                                       */
27 /*  Changes: 1993, 1994, 2007, 2008, 2014  Thomas Mertes            */
28 /*  Content: Directory access using FindFirstFile and FindNextFile. */
29 /*                                                                  */
30 /*  Implements opendir, readdir and closedir in the way it is       */
31 /*  defined in Unix. Additionally wopendir, wreaddir and wclosedir  */
32 /*  are defined which use wchar_t instead of char.                  */
33 /*                                                                  */
34 /********************************************************************/
35 
36 #define LOG_FUNCTIONS 0
37 #define VERBOSE_EXCEPTIONS 0
38 
39 #include "version.h"
40 
41 #if DIR_LIB == DIRWIN_DIRECTORY
42 #include "stdlib.h"
43 #include "stdio.h"
44 #include "string.h"
45 #include "wchar.h"
46 #include "ctype.h"
47 #include "errno.h"
48 
49 #include "common.h"
50 #include "striutl.h"
51 
52 #undef EXTERN
53 #define EXTERN
54 #include "dir_win.h"
55 
56 
57 
58 #ifdef OS_STRI_WCHAR
59 /**
60  *  Opens a directory stream corresponding to the directory dirName.
61  *  The stream is positioned at the first entry in the directory.
62  *  @param dirName Name of the directory to be opened.
63  *  @return a pointer to the directory stream or NULL if the
64  *          directory stream could not be opened.
65  */
wopendir(const wchar_t * dirName)66 WDIR *wopendir (const wchar_t *dirName)
67 
68   {
69     memSizeType nameLen;
70     wchar_t fileNameBuffer[260];
71     wchar_t *fileNamePattern = fileNameBuffer;
72     WIN32_FILE_ATTRIBUTE_DATA fileInfo;
73     WDIR *directory;
74 
75   /* wopendir */
76     logFunction(printf("wopendir(\"%ls\")\n", dirName););
77     nameLen = wcslen(dirName);
78     if (nameLen == 0) {
79       logError(printf("wopendir(\"%ls\"): Name empty.\n", dirName););
80       directory = NULL;
81       errno = ENOENT;
82     } else if (wcspbrk(&dirName[USE_EXTENDED_LENGTH_PATH * PREFIX_LEN], L"?*") != NULL) {
83       logError(printf("wopendir(\"%ls\"): Wildcards in path.\n", dirName););
84       directory = NULL;
85       errno = ENOENT;
86     } else {
87       if (nameLen > sizeof(fileNameBuffer) / sizeof(wchar_t) - 3) {
88         fileNamePattern = (wchar_t *) malloc((nameLen + 3) * sizeof(wchar_t));
89       } /* if */
90       if (unlikely(fileNamePattern == NULL)) {
91         directory = NULL;
92         errno = ENOMEM;
93       } else {
94         directory = (WDIR *) malloc(sizeof(WDIR));
95         if (unlikely(directory == NULL)) {
96           errno = ENOMEM;
97         } else {
98           memcpy(fileNamePattern, dirName, nameLen * sizeof(wchar_t));
99           if (dirName[nameLen - 1] != '/' &&
100               dirName[nameLen - 1] != '\\') {
101             fileNamePattern[nameLen++] = '\\';
102           } /* if */
103           fileNamePattern[nameLen++] = '*';
104           fileNamePattern[nameLen] = '\0';
105           logMessage(printf("wopendir: before FindFirstFileW(\"%ls\", *)\n",
106                             fileNamePattern););
107           directory->dirHandle = FindFirstFileW(fileNamePattern,
108                                                 &directory->findData);
109           if (directory->dirHandle != INVALID_HANDLE_VALUE) {
110             /* printf("--> OK\n");
111             printf(">%ls<\n", directory->findData.cFileName); */
112             directory->firstElement = 1;
113           } else {
114             /* The file referred by dirName does not exist, or */
115             /* it is not a directory, or it is an empty volume */
116             /* respectively directory. All these cases are     */
117             /* checked below.                                  */
118             if (nameLen == PREFIX_LEN + 4 &&
119                 fileNamePattern[PREFIX_LEN + 1] == ':' &&
120                 ((fileNamePattern[PREFIX_LEN] >= 'a' &&
121                   fileNamePattern[PREFIX_LEN] <= 'z') ||
122                  (fileNamePattern[PREFIX_LEN] >= 'A' &&
123                   fileNamePattern[PREFIX_LEN] <= 'Z'))) {
124               /* A volume path needs a trailing backslash. */
125               fileNamePattern[nameLen - 1] = '\0';
126             } else {
127               /* A normal path is not allowed to have a trailing backslash. */
128               fileNamePattern[nameLen - 2] = '\0';
129             } /* if */
130             logMessage(printf("wopendir: "
131                               "before GetFileAttributesExW(\"%ls\", *)\n",
132                               fileNamePattern););
133             if (GetFileAttributesExW(fileNamePattern, GetFileExInfoStandard,
134                                      &fileInfo) != 0) {
135               if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
136                 /* This is an empty directory. Probably an empty    */
137                 /* volume. For nonempty directories FindFirstFileW  */
138                 /* would have succeeded, because normal directories */
139                 /* contain at least the .. and . directories. In    */
140                 /* this special case (no . and .. directories)      */
141                 /* directory->dirHandle has the value               */
142                 /* INVALID_HANDLE_VALUE.                            */
143                 directory->firstElement = 0;
144               } else {
145                 logError(printf("wopendir(\"%ls\"): "
146                                 "GetFileAttributesExW(\"%ls\", *) shows: "
147                                 "The file is not a directory.\n",
148                                 dirName, fileNamePattern););
149                 free(directory);
150                 directory = NULL;
151                 errno = ENOTDIR;
152               } /* if */
153             } else {
154               logError(printf("wopendir(\"%ls\"): "
155                               "GetFileAttributesExW(\"%ls\", *) failed:\n"
156                               "GetLastError=" FMT_U32 "\n",
157                               dirName, fileNamePattern, (uint32Type)
158                               GetLastError()););
159               free(directory);
160               directory = NULL;
161               errno = ENOENT;
162             } /* if */
163           } /* if */
164         } /* if */
165         if (unlikely(fileNamePattern != fileNameBuffer)) {
166           free(fileNamePattern);
167         } /* if */
168       } /* if */
169     } /* if */
170     logFunction(printf("wopendir(\"%ls\") --> " FMT_X_MEM,
171                        dirName, (memSizeType) directory);
172                 if (directory != NULL) {
173                   printf(" {%d, " FMT_X_MEM ", ... }",
174                          directory->firstElement,
175                         (memSizeType) directory->dirHandle);
176                 } else {
177                   printf(" (errno=%d, error=%s)", errno, strerror(errno));
178                 }
179                 printf("\n"););
180     return directory;
181   } /* wopendir */
182 
183 
184 
185 /**
186  *  Determine wdirent structure representing the next directory entry.
187  *  The element d_name in the wdirent structure gets the file name of
188  *  the next directory entry. Besides d_name the wdirent structure
189  *  has no other elements.
190  *  @param directory Directory stream from which the directory entry
191  *         is read.
192  *  @return a pointer to a wdirent structure representing the next
193  *          directory entry. It returns NULL on reaching the end of
194  *          the directory stream or if an error occurred.
195  */
wreaddir(WDIR * directory)196 struct wdirent *wreaddir (WDIR *directory)
197 
198   {
199     struct wdirent *dirEntry;
200 
201   /* wreaddir */
202     logFunction(printf("wreaddir(" FMT_X_MEM ")\n",
203                        (memSizeType) directory););
204     if (directory->firstElement) {
205       /* printf("first\n"); */
206       directory->firstElement = 0;
207       directory->dirEntry.d_name = (wchar_t *) directory->findData.cFileName;
208       dirEntry = &directory->dirEntry;
209       /* printf(">%ls<\n", dirEntry->d_name); */
210     } else if (directory->dirHandle != INVALID_HANDLE_VALUE &&
211         FindNextFileW(directory->dirHandle, &directory->findData) != 0) {
212       directory->dirEntry.d_name = (wchar_t *) directory->findData.cFileName;
213       dirEntry = &directory->dirEntry;
214       /* printf(">%ls<\n", dirEntry->d_name); */
215     } else {
216       /* printf("end\n"); */
217       dirEntry = NULL;
218     } /* if */
219     logFunction(printf("wreaddir --> %ls\n",
220                        dirEntry == NULL ? L"NULL" : dirEntry->d_name););
221     return dirEntry;
222   } /* wreaddir */
223 
224 
225 
226 /**
227  *  Closes the given directory stream.
228  *  @param directory The directory stream to be closed.
229  *  @return 0 on success.
230  */
wclosedir(WDIR * directory)231 int wclosedir (WDIR *directory)
232 
233   { /* wclosedir */
234     logFunction(printf("wclosedir(" FMT_X_MEM ")\n",
235                        (memSizeType) directory););
236     if (directory->dirHandle != INVALID_HANDLE_VALUE) {
237       FindClose(directory->dirHandle);
238     } /* if */
239     free(directory);
240     return 0;
241   } /* wclosedir */
242 
243 #else
244 
245 
246 
247 /**
248  *  Opens a directory stream corresponding to the directory dirName.
249  *  The stream is positioned at the first entry in the directory.
250  *  @param dirName Name of the directory to be opened.
251  *  @return a pointer to the directory stream or NULL if the
252  *          directory stream could not be opened.
253  */
opendir(const char * dirName)254 DIR *opendir (const char *dirName)
255 
256   {
257     memSizeType nameLen;
258     char fileNameBuffer[260];
259     char *fileNamePattern = fileNameBuffer;
260     WIN32_FILE_ATTRIBUTE_DATA fileInfo;
261     DIR *directory;
262 
263   /* opendir */
264     logFunction(printf("opendir(\"%s\")\n", dirName););
265     nameLen = strlen(dirName);
266     if (nameLen == 0) {
267       logError(printf("opendir(\"%s\"): Name empty.\n", dirName););
268       directory = NULL;
269       errno = ENOENT;
270     } else if (strpbrk(&dirName[USE_EXTENDED_LENGTH_PATH * PREFIX_LEN], "?*") != NULL) {
271       logError(printf("opendir(\"%s\"): Wildcards in path.\n", dirName););
272       directory = NULL;
273       errno = ENOENT;
274     } else {
275       if (nameLen > sizeof(fileNameBuffer) - 3) {
276         fileNamePattern = (char *) malloc((nameLen + 3) * sizeof(char));
277       } /* if */
278       if (unlikely(fileNamePattern == NULL)) {
279         directory = NULL;
280         errno = ENOMEM;
281       } else {
282         directory = (DIR *) malloc(sizeof(DIR));
283         if (unlikely(directory == NULL)) {
284           errno = ENOMEM;
285         } else {
286           memcpy(fileNamePattern, dirName, nameLen);
287           if (dirName[nameLen - 1] != '/' &&
288               dirName[nameLen - 1] != '\\') {
289             fileNamePattern[nameLen++] = '\\';
290           } /* if */
291           fileNamePattern[nameLen++] = '*';
292           fileNamePattern[nameLen] = '\0';
293           logMessage(printf("opendir: before FindFirstFileA(\"%s\", *)\n",
294                             fileNamePattern););
295           directory->dirHandle = FindFirstFileA(fileNamePattern, &directory->findData);
296           if (directory->dirHandle != INVALID_HANDLE_VALUE) {
297             /* printf("--> OK\n");
298             printf(">%s<\n", directory->findData.cFileName); */
299             directory->firstElement = 1;
300           } else {
301             /* The file referred by dirName does not exist, or */
302             /* it is not a directory, or it is an empty volume */
303             /* respectively directory. All these cases are     */
304             /* checked below.                                  */
305             if (nameLen == PREFIX_LEN + 4 &&
306                 fileNamePattern[PREFIX_LEN + 1] == ':' &&
307                 isalpha(fileNamePattern[PREFIX_LEN])) {
308               /* A volume path needs a trailing backslash. */
309               fileNamePattern[nameLen - 1] = '\0';
310             } else {
311               /* A normal path is not allowed to have a trailing backslash. */
312               fileNamePattern[nameLen - 2] = '\0';
313             } /* if */
314             logMessage(printf("opendir: "
315                               "before GetFileAttributesExA(\"%s\", *)\n",
316                               fileNamePattern););
317             if (GetFileAttributesExA(fileNamePattern, GetFileExInfoStandard,
318                                      &fileInfo) != 0) {
319               if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
320                 /* This is an empty directory. Probably an empty    */
321                 /* volume. For nonempty directories FindFirstFileA  */
322                 /* would have succeeded, because normal directories */
323                 /* contain at least the .. and . directories. In    */
324                 /* this special case (no . and .. directories)      */
325                 /* directory->dirHandle has the value               */
326                 /* INVALID_HANDLE_VALUE.                            */
327                 directory->firstElement = 0;
328               } else {
329                 logError(printf("opendir(\"%s\"): "
330                                 "GetFileAttributesExA(\"%s\", *) shows: "
331                                 "The file is not a directory.\n",
332                                 dirName, fileNamePattern););
333                 free(directory);
334                 directory = NULL;
335                 errno = ENOTDIR;
336               } /* if */
337             } else {
338               logError(printf("opendir(\"%s\"): "
339                               "GetFileAttributesExA(\"%s\", *) failed:\n"
340                               "GetLastError=" FMT_U32 "\n",
341                               dirName, fileNamePattern, (uint32Type)
342                               GetLastError()););
343               free(directory);
344               directory = NULL;
345               errno = ENOENT;
346             } /* if */
347           } /* if */
348         } /* if */
349         if (unlikely(fileNamePattern != fileNameBuffer)) {
350           free(fileNamePattern);
351         } /* if */
352       } /* if */
353     } /* if */
354     logFunction(printf("opendir(\"%s\") --> " FMT_X_MEM,
355                        dirName, (memSizeType) directory);
356                 if (directory != NULL) {
357                   printf(" {%d, " FMT_X_MEM ", ... }",
358                          directory->firstElement,
359                          (memSizeType) directory->dirHandle);
360                 } else {
361                   printf(" (errno=%d, error=%s)", errno, strerror(errno));
362                 }
363                 printf("\n"););
364     return directory;
365   } /* opendir */
366 
367 
368 
369 /**
370  *  Determine dirent structure representing the next directory entry.
371  *  The element d_name in the dirent structure gets the file name of
372  *  the next directory entry. Besides d_name the dirent structure
373  *  has no other elements.
374  *  @param directory Directory stream from which the directory entry
375  *         is read.
376  *  @return a pointer to a dirent structure representing the next
377  *          directory entry. It returns NULL on reaching the end of
378  *          the directory stream or if an error occurred.
379  */
readdir(DIR * directory)380 struct dirent *readdir (DIR *directory)
381 
382   {
383     struct dirent *dirEntry;
384 
385   /* readdir */
386     logFunction(printf("readdir(" FMT_X_MEM ")\n",
387                        (memSizeType) directory););
388     if (directory->firstElement) {
389       /* printf("first\n"); */
390       directory->firstElement = 0;
391       directory->dirEntry.d_name = (char *) directory->findData.cFileName;
392       dirEntry = &directory->dirEntry;
393       /* printf(">%s<\n", dirEntry->d_name); */
394     } else if (directory->dirHandle != INVALID_HANDLE_VALUE &&
395         FindNextFileA(directory->dirHandle, &directory->findData) != 0) {
396       directory->dirEntry.d_name = (char *) directory->findData.cFileName;
397       dirEntry = &directory->dirEntry;
398       /* printf(">%s<\n", dirEntry->d_name); */
399     } else {
400       /* printf("end\n"); */
401       dirEntry = NULL;
402     } /* if */
403     logFunction(printf("readdir --> %s\n",
404                        dirEntry == NULL ? "NULL" : dirEntry->d_name););
405     return dirEntry;
406   } /* readdir */
407 
408 
409 
410 /**
411  *  Closes the given directory stream.
412  *  @param directory The directory stream to be closed.
413  *  @return 0 on success.
414  */
closedir(DIR * directory)415 int closedir (DIR *directory)
416 
417   { /* closedir */
418     logFunction(printf("closedir(" FMT_X_MEM ")\n",
419                        (memSizeType) directory););
420     if (directory->dirHandle != INVALID_HANDLE_VALUE) {
421       FindClose(directory->dirHandle);
422     } /* if */
423     free(directory);
424     return 0;
425   } /* closedir */
426 
427 #endif
428 
429 
430 
431 #if MAP_ABSOLUTE_PATH_TO_DRIVE_LETTERS
openVolumeList(void)432 volumeListType *openVolumeList (void)
433 
434   {
435     volumeListType *volumeList;
436 
437   /* openVolumeList */
438     logFunction(printf("openVolumeList()\n"););
439     if ((volumeList = (volumeListType *) malloc(sizeof(volumeListType))) != NULL) {
440       volumeList->magicValue = UINT32TYPE_MAX;
441       volumeList->driveBitmask = GetLogicalDrives();
442       volumeList->currentDrive = 0;
443     } /* if */
444     logFunction(printf("openVolumeList --> {0x" F_X32(04) ", %d}\n",
445                        volumeList->driveBitmask, volumeList->currentDrive););
446     return volumeList;
447   } /* openVolumeList */
448 #endif
449 
450 #endif
451