1 /* 2 * dirent.h - dirent API for Microsoft Visual Studio 3 * 4 * Copyright (C) 2006-2012 Toni Ronkko 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * ``Software''), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sublicense, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 * OTHER DEALINGS IN THE SOFTWARE. 24 * 25 * $Id: dirent.h,v 1.20 2014/03/19 17:52:23 tronkko Exp $ 26 */ 27 module libressl_d.compat.dirent_msvc; 28 29 30 private static import core.stdc.config; 31 private static import core.stdc.stddef; 32 private static import core.sys.windows.basetsd; 33 private static import core.sys.windows.winbase; 34 private static import core.sys.windows.windef; 35 private static import core.sys.windows.winnt; 36 private static import libressl_d.compat.limits; 37 public import core.stdc.errno; 38 public import core.stdc.stdarg; 39 public import core.stdc.wchar_; 40 public import core.sys.windows.windows; 41 public import libressl_d.compat.stdio; 42 public import libressl_d.compat.stdlib; 43 public import libressl_d.compat.string; 44 public import libressl_d.compat.sys.stat; 45 public import libressl_d.compat.sys.types; 46 47 version (Windows): 48 extern (C): 49 nothrow @nogc: 50 51 alias errno_t = int; 52 53 .errno_t _set_errno(int); 54 .errno_t mbstowcs_s(size_t*, wchar_t*, size_t, const (char)*, size_t); 55 .errno_t wcstombs_s(size_t*, char*, size_t, const (wchar_t)*, size_t); 56 57 /** 58 * Indicates that d_type field is available in dirent structure 59 */ 60 enum _DIRENT_HAVE_D_TYPE = true; 61 62 /** 63 * Indicates that d_namlen field is available in dirent structure 64 */ 65 enum _DIRENT_HAVE_D_NAMLEN = true; 66 67 /* Maximum length of file name */ 68 enum PATH_MAX = libressl_d.compat.limits.PATH_MAX; 69 70 static if (!__traits(compiles, libressl_d.compat.stdio.FILENAME_MAX)) { 71 enum FILENAME_MAX = core.sys.windows.windef.MAX_PATH; 72 } else { 73 enum FILENAME_MAX = libressl_d.compat.stdio.FILENAME_MAX; 74 } 75 76 enum NAME_MAX = libressl_d.compat.limits.NAME_MAX; 77 78 /** 79 * Return the exact length of d_namlen without zero terminator 80 */ 81 pragma(inline, true) 82 pure nothrow @trusted @nogc @live 83 void _D_EXACT_NAMLEN(P)(P p) 84 85 do 86 { 87 return p.d_namlen; 88 } 89 90 /** 91 * Return number of bytes needed to store d_namlen 92 */ 93 pragma(inline, true) 94 pure nothrow @safe @nogc @live 95 void _D_ALLOC_NAMLEN(P)(P p) 96 97 do 98 { 99 return libressl_d.compat.limits.PATH_MAX; 100 } 101 102 /** 103 * Wide-character version 104 */ 105 struct _wdirent 106 { 107 /** 108 * Always zero 109 */ 110 core.stdc.config.c_long d_ino; 111 112 /** 113 * Structure size 114 */ 115 ushort d_reclen; 116 117 /** 118 * Length of name without \0 119 */ 120 size_t d_namlen; 121 122 /** 123 * File type 124 */ 125 int d_type; 126 127 /** 128 * File name 129 */ 130 core.stdc.stddef.wchar_t[libressl_d.compat.limits.PATH_MAX] d_name = '\0'; 131 } 132 133 struct _WDIR 134 { 135 /** 136 * Current directory entry 137 */ 138 ._wdirent ent; 139 140 /** 141 * Private file data 142 */ 143 core.sys.windows.winbase.WIN32_FIND_DATAW data; 144 145 /** 146 * True if data is valid 147 */ 148 int cached; 149 150 /** 151 * Win32 search handle 152 */ 153 core.sys.windows.basetsd.HANDLE handle; 154 155 /** 156 * Initial directory name 157 */ 158 core.stdc.stddef.wchar_t* patt; 159 } 160 161 /** 162 * Multi-byte character versions 163 */ 164 struct dirent 165 { 166 /** 167 * Always zero 168 */ 169 core.stdc.config.c_long d_ino; 170 171 /** 172 * Structure size 173 */ 174 ushort d_reclen; 175 176 /** 177 * Length of name without \0 178 */ 179 size_t d_namlen; 180 181 /** 182 * File type 183 */ 184 int d_type; 185 186 /** 187 * File name 188 */ 189 char[libressl_d.compat.limits.PATH_MAX] d_name = '\0'; 190 } 191 192 struct DIR 193 { 194 .dirent ent; 195 ._WDIR* wdirp; 196 } 197 198 /* 199 * Open directory stream DIRNAME for read and return a pointer to the 200 * internal working area that is used to retrieve individual directory 201 * entries. 202 */ 203 ._WDIR* _wopendir(const (core.stdc.stddef.wchar_t)* dirname) 204 205 do 206 { 207 /* Must have directory name */ 208 if ((dirname == null) || (dirname[0] == '\0')) { 209 ._set_errno(core.stdc.errno.ENOENT); 210 211 return null; 212 } 213 214 /* Allocate new _WDIR structure */ 215 ._WDIR* dirp = cast(._WDIR*)(libressl_d.compat.stdlib.malloc(._WDIR.sizeof)); 216 217 int error = void; 218 219 if (dirp != null) { 220 /* Reset _WDIR structure */ 221 dirp.handle = core.sys.windows.winbase.INVALID_HANDLE_VALUE; 222 dirp.patt = null; 223 dirp.cached = 0; 224 225 /* Compute the length of full path plus zero terminator */ 226 core.sys.windows.windef.DWORD n = core.sys.windows.winbase.GetFullPathNameW(dirname, 0, null, null); 227 228 /* Allocate room for absolute directory name and search pattern */ 229 dirp.patt = cast(core.stdc.stddef.wchar_t*)(libressl_d.compat.stdlib.malloc((core.stdc.stddef.wchar_t.sizeof * n) + 16)); 230 231 if (dirp.patt != null) { 232 /* 233 * Convert relative directory name to an absolute one. This 234 * allows rewinddir() to function correctly even when current 235 * working directory is changed between opendir() and rewinddir(). 236 */ 237 n = core.sys.windows.winbase.GetFullPathNameW(dirname, n, dirp.patt, null); 238 239 if (n > 0) { 240 /* Append search pattern \* to the directory name */ 241 core.stdc.stddef.wchar_t* p = dirp.patt + n; 242 243 if (dirp.patt < p) { 244 switch (p[-1]) { 245 case '\\': 246 case '/': 247 case ':': 248 /* Directory ends in path separator, e.g. c:\temp\ */ 249 /*NOP*/ 250 //; 251 252 break; 253 254 default: 255 /* Directory name doesn't end in path separator */ 256 *p++ = '\\'; 257 } 258 } 259 260 *p++ = '*'; 261 *p = '\0'; 262 263 /* Open directory stream and retrieve the first entry */ 264 if (.dirent_first(dirp)) { 265 /* Directory stream opened successfully */ 266 error = 0; 267 } else { 268 /* Cannot retrieve first entry */ 269 error = 1; 270 ._set_errno(core.stdc.errno.ENOENT); 271 } 272 } else { 273 /* Cannot retrieve full path name */ 274 ._set_errno(core.stdc.errno.ENOENT); 275 error = 1; 276 } 277 } else { 278 /* Cannot allocate memory for search pattern */ 279 error = 1; 280 } 281 } else { 282 /* Cannot allocate _WDIR structure */ 283 error = 1; 284 } 285 286 /* Clean up in case of error */ 287 if ((error) && (dirp != null)) { 288 ._wclosedir(dirp); 289 dirp = null; 290 } 291 292 return dirp; 293 } 294 295 /* 296 * Read next directory entry. The directory entry is returned in dirent 297 * structure in the d_name field. Individual directory entries returned by 298 * this function include regular files, sub-directories, pseudo-directories 299 * "." and ".." as well as volume labels, hidden files and system files. 300 */ 301 ._wdirent* _wreaddir(._WDIR* dirp) 302 303 in 304 { 305 assert(dirp != null); 306 } 307 308 do 309 { 310 /* Read next directory entry */ 311 core.sys.windows.winbase.WIN32_FIND_DATAW* datap = .dirent_next(dirp); 312 313 ._wdirent* entp = void; 314 315 if (datap != null) { 316 /* Pointer to directory entry to return */ 317 entp = &dirp.ent; 318 319 /* 320 * Copy file name as wide-character string. If the file name is too 321 * core.stdc.config.c_long to fit in to the destination buffer, then truncate file name 322 * to PATH_MAX characters and zero-terminate the buffer. 323 */ 324 size_t n = 0; 325 326 while (((n + 1) < libressl_d.compat.limits.PATH_MAX) && (datap.cFileName[n] != 0)) { 327 entp.d_name[n] = datap.cFileName[n]; 328 n++; 329 } 330 331 dirp.ent.d_name[n] = 0; 332 333 /* Length of file name excluding zero terminator */ 334 entp.d_namlen = n; 335 336 /* File type */ 337 core.sys.windows.windef.DWORD attr = datap.dwFileAttributes; 338 339 if ((attr & core.sys.windows.winnt.FILE_ATTRIBUTE_DEVICE) != 0) { 340 entp.d_type = libressl_d.compat.sys.stat.DT_CHR; 341 } else if ((attr & core.sys.windows.winnt.FILE_ATTRIBUTE_DIRECTORY) != 0) { 342 entp.d_type = libressl_d.compat.sys.stat.DT_DIR; 343 } else { 344 entp.d_type = libressl_d.compat.sys.stat.DT_REG; 345 } 346 347 /* Reset dummy fields */ 348 entp.d_ino = 0; 349 entp.d_reclen = ._wdirent.sizeof; 350 } else { 351 /* Last directory entry read */ 352 entp = null; 353 } 354 355 return entp; 356 } 357 358 /* 359 * Close directory stream opened by opendir() function. This invalidates the 360 * DIR structure as well as any directory entry read previously by 361 * _wreaddir(). 362 */ 363 int _wclosedir(._WDIR* dirp) 364 365 do 366 { 367 int ok = void; 368 369 if (dirp != null) { 370 /* Release search handle */ 371 if (dirp.handle != core.sys.windows.winbase.INVALID_HANDLE_VALUE) { 372 core.sys.windows.winbase.FindClose(dirp.handle); 373 dirp.handle = core.sys.windows.winbase.INVALID_HANDLE_VALUE; 374 } 375 376 /* Release search pattern */ 377 if (dirp.patt != null) { 378 libressl_d.compat.stdlib.free(dirp.patt); 379 dirp.patt = null; 380 } 381 382 /* Release directory structure */ 383 libressl_d.compat.stdlib.free(dirp); 384 385 /*success*/ 386 ok = 0; 387 } else { 388 /* Invalid directory stream */ 389 ._set_errno(core.stdc.errno.EBADF); 390 391 /*failure*/ 392 ok = -1; 393 } 394 395 return ok; 396 } 397 398 /* 399 * Rewind directory stream such that _wreaddir() returns the very first 400 * file name again. 401 */ 402 void _wrewinddir(._WDIR* dirp) 403 404 do 405 { 406 if (dirp != null) { 407 /* Release existing search handle */ 408 if (dirp.handle != core.sys.windows.winbase.INVALID_HANDLE_VALUE) { 409 core.sys.windows.winbase.FindClose(dirp.handle); 410 dirp.handle = core.sys.windows.windef.NULL; 411 } 412 413 /* Open new search handle */ 414 .dirent_first(dirp); 415 } 416 } 417 418 /* Get first directory entry(internal) */ 419 core.sys.windows.winbase.WIN32_FIND_DATAW* dirent_first(._WDIR* dirp) 420 421 in 422 { 423 assert(dirp != null); 424 } 425 426 do 427 { 428 core.sys.windows.winbase.WIN32_FIND_DATAW* datap = void; 429 430 /* Open directory and retrieve the first entry */ 431 dirp.handle = core.sys.windows.winbase.FindFirstFileW(dirp.patt, &dirp.data); 432 433 if (dirp.handle != core.sys.windows.winbase.INVALID_HANDLE_VALUE) { 434 /* a directory entry is now waiting in memory */ 435 datap = &dirp.data; 436 dirp.cached = 1; 437 } else { 438 /* Failed to re-open directory: no directory entry in memory */ 439 dirp.cached = 0; 440 datap = null; 441 } 442 443 return datap; 444 } 445 446 /* Get next directory entry(internal) */ 447 core.sys.windows.winbase.WIN32_FIND_DATAW* dirent_next(._WDIR* dirp) 448 449 in 450 { 451 assert(dirp != null); 452 } 453 454 do 455 { 456 core.sys.windows.winbase.WIN32_FIND_DATAW* p = void; 457 458 /* Get next directory entry */ 459 if (dirp.cached != 0) { 460 /* A valid directory entry already in memory */ 461 p = &dirp.data; 462 dirp.cached = 0; 463 } else if (dirp.handle != core.sys.windows.winbase.INVALID_HANDLE_VALUE) { 464 /* Get the next directory entry from stream */ 465 if (core.sys.windows.winbase.FindNextFileW(dirp.handle, &dirp.data) != core.sys.windows.windef.FALSE) { 466 /* Got a file */ 467 p = &dirp.data; 468 } else { 469 /* The very last entry has been processed or an error occured */ 470 core.sys.windows.winbase.FindClose(dirp.handle); 471 dirp.handle = core.sys.windows.winbase.INVALID_HANDLE_VALUE; 472 p = null; 473 } 474 } else { 475 /* End of directory stream reached */ 476 p = null; 477 } 478 479 return p; 480 } 481 482 /** 483 * Open directory stream using plain old C-string. 484 */ 485 .DIR* opendir(const (char)* dirname) 486 487 do 488 { 489 /* Must have directory name */ 490 if ((dirname == null) || (dirname[0] == '\0')) { 491 ._set_errno(core.stdc.errno.ENOENT); 492 493 return null; 494 } 495 496 /* Allocate memory for DIR structure */ 497 .DIR* dirp = cast(.DIR*)(libressl_d.compat.stdlib.malloc(.DIR.sizeof)); 498 499 int error = void; 500 501 if (dirp != null) { 502 core.stdc.stddef.wchar_t[libressl_d.compat.limits.PATH_MAX] wname = void; 503 size_t n = void; 504 505 /* Convert directory name to wide-character string */ 506 error = .dirent_mbstowcs_s(&n, &(wname[0]), libressl_d.compat.limits.PATH_MAX, dirname, libressl_d.compat.limits.PATH_MAX); 507 508 if (!error) { 509 /* Open directory stream using wide-character name */ 510 dirp.wdirp = ._wopendir(&(wname[0])); 511 512 if (dirp.wdirp) { 513 /* Directory stream opened */ 514 error = 0; 515 } else { 516 /* Failed to open directory stream */ 517 error = 1; 518 } 519 } else { 520 /* 521 * Cannot convert file name to wide-character string. This 522 * occurs if the string contains invalid multi-byte sequences or 523 * the output buffer is too small to contain the resulting 524 * string. 525 */ 526 error = 1; 527 } 528 } else { 529 /* Cannot allocate DIR structure */ 530 error = 1; 531 } 532 533 /* Clean up in case of error */ 534 if ((error) && (dirp != null)) { 535 libressl_d.compat.stdlib.free(dirp); 536 dirp = null; 537 } 538 539 return dirp; 540 } 541 542 /** 543 * Read next directory entry. 544 * 545 * When working with text consoles, please note that file names returned by 546 * readdir() are represented in the default ANSI code page while any output to 547 * console is typically formatted on another code page. Thus, non-ASCII 548 * characters in file names will not usually display correctly on console. The 549 * problem can be fixed in two ways:(1) change the character set of console 550 * to 1252 using chcp utility and use Lucida Console font, or(2) use 551 * _cprintf function when writing to console. The _cprinf() will re-encode 552 * ANSI strings to the console code page so many non-ASCII characters will 553 * display correcly. 554 */ 555 .dirent* readdir(.DIR* dirp) 556 557 in 558 { 559 assert(dirp != null); 560 } 561 562 do 563 { 564 /* Read next directory entry */ 565 core.sys.windows.winbase.WIN32_FIND_DATAW* datap = .dirent_next(dirp.wdirp); 566 567 .dirent* entp = void; 568 569 if (datap != null) { 570 size_t n = void; 571 572 /* Attempt to convert file name to multi-byte string */ 573 int error = .dirent_wcstombs_s(&n, &(dirp.ent.d_name[0]), libressl_d.compat.limits.PATH_MAX, &(datap.cFileName[0]), libressl_d.compat.limits.PATH_MAX); 574 575 /* 576 * If the file name cannot be represented by a multi-byte string, 577 * then attempt to use old 8+3 file name. This allows traditional 578 * Unix-code to access some file names despite of unicode 579 * characters, although file names may seem unfamiliar to the user. 580 * 581 * Be ware that the code below cannot come up with a short file 582 * name unless the file system provides one. At least 583 * VirtualBox shared folders fail to do this. 584 */ 585 if ((error) && (datap.cAlternateFileName[0] != '\0')) { 586 error = .dirent_wcstombs_s(&n, &(dirp.ent.d_name[0]), libressl_d.compat.limits.PATH_MAX, &(datap.cAlternateFileName[0]), libressl_d.compat.limits.PATH_MAX); 587 } 588 589 if (!error) { 590 /* Initialize directory entry for return */ 591 entp = &dirp.ent; 592 593 /* Length of file name excluding zero terminator */ 594 entp.d_namlen = n - 1; 595 596 /* File attributes */ 597 core.sys.windows.windef.DWORD attr = datap.dwFileAttributes; 598 599 if ((attr & core.sys.windows.winnt.FILE_ATTRIBUTE_DEVICE) != 0) { 600 entp.d_type = libressl_d.compat.sys.stat.DT_CHR; 601 } else if ((attr & core.sys.windows.winnt.FILE_ATTRIBUTE_DIRECTORY) != 0) { 602 entp.d_type = libressl_d.compat.sys.stat.DT_DIR; 603 } else { 604 entp.d_type = libressl_d.compat.sys.stat.DT_REG; 605 } 606 607 /* Reset dummy fields */ 608 entp.d_ino = 0; 609 entp.d_reclen = .dirent.sizeof; 610 } else { 611 /* 612 * Cannot convert file name to multi-byte string so construct 613 * an errornous directory entry and return that. Note that 614 * we cannot return null as that would stop the processing 615 * of directory entries completely. 616 */ 617 entp = &dirp.ent; 618 entp.d_name[0] = '?'; 619 entp.d_name[1] = '\0'; 620 entp.d_namlen = 1; 621 entp.d_type = libressl_d.compat.sys.stat.DT_UNKNOWN; 622 entp.d_ino = 0; 623 entp.d_reclen = 0; 624 } 625 } else { 626 /* No more directory entries */ 627 entp = null; 628 } 629 630 return entp; 631 } 632 633 /** 634 * Close directory stream. 635 */ 636 int closedir(.DIR* dirp) 637 638 do 639 { 640 int ok = void; 641 642 if (dirp != null) { 643 /* Close wide-character directory stream */ 644 ok = ._wclosedir(dirp.wdirp); 645 dirp.wdirp = null; 646 647 /* Release multi-byte character version */ 648 libressl_d.compat.stdlib.free(dirp); 649 } else { 650 /* Invalid directory stream */ 651 ._set_errno(core.stdc.errno.EBADF); 652 653 /*failure*/ 654 ok = -1; 655 } 656 657 return ok; 658 } 659 660 /** 661 * Rewind directory stream to beginning. 662 */ 663 void rewinddir(.DIR* dirp) 664 665 in 666 { 667 assert(dirp != null); 668 } 669 670 do 671 { 672 /* Rewind wide-character string directory stream */ 673 ._wrewinddir(dirp.wdirp); 674 } 675 676 /* Convert multi-byte string to wide character string */ 677 int dirent_mbstowcs_s(size_t* pReturnValue, core.stdc.stddef.wchar_t* wcstr, size_t sizeInWords, const (char)* mbstr, size_t count) 678 679 do 680 { 681 return .mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); 682 } 683 684 /* Convert wide-character string to multi-byte string */ 685 int dirent_wcstombs_s(size_t* pReturnValue, char* mbstr, size_t sizeInBytes, /* max size of mbstr */ const (core.stdc.stddef.wchar_t)* wcstr, size_t count) 686 687 do 688 { 689 return .wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); 690 }