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