From b9bb2048d26b194391ca05b6e67dd73fd92cb85e Mon Sep 17 00:00:00 2001 From: Hui Li Date: Wed, 27 May 2020 14:25:04 +0800 Subject: [PATCH] [sync master] --- deps/dirent/CMakeLists.txt | 53 ++ deps/dirent/ChangeLog | 129 +++ deps/dirent/LICENSE | 21 + deps/dirent/README.md | 96 ++ deps/dirent/distclean.cmake | 62 ++ deps/dirent/examples/cat.c | 85 ++ deps/dirent/examples/find.c | 147 +++ deps/dirent/examples/locate.c | 280 ++++++ deps/dirent/examples/ls.c | 103 +++ deps/dirent/examples/scandir.c | 110 +++ deps/dirent/examples/updatedb.c | 232 +++++ deps/dirent/include/dirent.h | 1160 ++++++++++++++++++++++++ deps/dirent/tests/1/dir/readme.txt | 3 + deps/dirent/tests/1/file | 0 deps/dirent/tests/2/Testfile-1.2.3.dat | 0 deps/dirent/tests/2/file.txt | 1 + deps/dirent/tests/3/3zero.dat | 0 deps/dirent/tests/3/666.dat | 0 deps/dirent/tests/3/Qwerty-my-aunt.dat | 0 deps/dirent/tests/3/README.txt | 2 + deps/dirent/tests/3/aaa.dat | 0 deps/dirent/tests/3/dirent.dat | 0 deps/dirent/tests/3/empty.dat | 0 deps/dirent/tests/3/sane-1.12.0.dat | 0 deps/dirent/tests/3/sane-1.2.3.dat | 0 deps/dirent/tests/3/sane-1.2.4.dat | 0 deps/dirent/tests/3/zebra.dat | 0 deps/dirent/tests/t-compile.c | 47 + deps/dirent/tests/t-cplusplus.cpp | 160 ++++ deps/dirent/tests/t-dirent.c | 633 +++++++++++++ deps/dirent/tests/t-scandir.c | 264 ++++++ deps/dirent/tests/t-unicode.c | 401 ++++++++ 32 files changed, 3989 insertions(+) create mode 100644 deps/dirent/CMakeLists.txt create mode 100644 deps/dirent/ChangeLog create mode 100644 deps/dirent/LICENSE create mode 100644 deps/dirent/README.md create mode 100644 deps/dirent/distclean.cmake create mode 100644 deps/dirent/examples/cat.c create mode 100644 deps/dirent/examples/find.c create mode 100644 deps/dirent/examples/locate.c create mode 100644 deps/dirent/examples/ls.c create mode 100644 deps/dirent/examples/scandir.c create mode 100644 deps/dirent/examples/updatedb.c create mode 100644 deps/dirent/include/dirent.h create mode 100644 deps/dirent/tests/1/dir/readme.txt create mode 100644 deps/dirent/tests/1/file create mode 100644 deps/dirent/tests/2/Testfile-1.2.3.dat create mode 100644 deps/dirent/tests/2/file.txt create mode 100644 deps/dirent/tests/3/3zero.dat create mode 100644 deps/dirent/tests/3/666.dat create mode 100644 deps/dirent/tests/3/Qwerty-my-aunt.dat create mode 100644 deps/dirent/tests/3/README.txt create mode 100644 deps/dirent/tests/3/aaa.dat create mode 100644 deps/dirent/tests/3/dirent.dat create mode 100644 deps/dirent/tests/3/empty.dat create mode 100644 deps/dirent/tests/3/sane-1.12.0.dat create mode 100644 deps/dirent/tests/3/sane-1.2.3.dat create mode 100644 deps/dirent/tests/3/sane-1.2.4.dat create mode 100644 deps/dirent/tests/3/zebra.dat create mode 100644 deps/dirent/tests/t-compile.c create mode 100644 deps/dirent/tests/t-cplusplus.cpp create mode 100644 deps/dirent/tests/t-dirent.c create mode 100644 deps/dirent/tests/t-scandir.c create mode 100644 deps/dirent/tests/t-unicode.c diff --git a/deps/dirent/CMakeLists.txt b/deps/dirent/CMakeLists.txt new file mode 100644 index 0000000000..d4f8b6baf5 --- /dev/null +++ b/deps/dirent/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required (VERSION 2.8.11) +project (dirent LANGUAGES C CXX) + +# Initialize C and C++ compilers +enable_language (C CXX) + +# Compile in debug mode by default +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Debug + CACHE STRING + "Type of build: None Debug Release RelWithDebInfo MinSizeRel." + FORCE + ) +endif (NOT CMAKE_BUILD_TYPE) + +# Use the include directory only on Windows +if (WIN32) + include_directories (${CMAKE_SOURCE_DIR}/include) +endif (WIN32) + +# Install dirent.h +if (WIN32) + install (FILES include/dirent.h DESTINATION include) +endif (WIN32) + +# Add distclean target +add_custom_target (distclean + COMMAND ${CMAKE_BUILD_TOOL} clean + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/distclean.cmake +) + +# Build example programs +add_executable (find examples/find.c) +add_executable (ls examples/ls.c) +add_executable (locate examples/locate.c) +add_executable (updatedb examples/updatedb.c) +add_executable (scandir examples/scandir.c) +add_executable (cat examples/cat.c) + +# Build test programs +include (CTest) +add_custom_target (check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C ${CMAKE_CFG_INTDIR}) +function (add_test_executable TEST_NAME) + add_executable (${TEST_NAME} EXCLUDE_FROM_ALL ${ARGN}) + add_test (NAME ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMAND $) + add_dependencies (check ${TEST_NAME}) +endfunction (add_test_executable) + +add_test_executable (t-compile tests/t-compile.c) +add_test_executable (t-dirent tests/t-dirent.c) +add_test_executable (t-scandir tests/t-scandir.c) +add_test_executable (t-unicode tests/t-unicode.c) +add_test_executable (t-cplusplus tests/t-cplusplus.cpp) diff --git a/deps/dirent/ChangeLog b/deps/dirent/ChangeLog new file mode 100644 index 0000000000..ce7975ec5e --- /dev/null +++ b/deps/dirent/ChangeLog @@ -0,0 +1,129 @@ +2018-05-08 Toni Rönkkö + + * Version 1.23.2: fixes bad scandir prototype. + +2017-08-27 Toni Rönkkö + + * Version 1.23: support readdir_r and scandir functions. + +2017-07-18 Toni Rönkkö + + * Created release branches v1.22 and v1.21 to Git. Published version + 1.22 at softagalleria.net. + +2016-09-11 Toni Rönkkö + + * Version 1.22: added support for CMake. Thanks to Paul Fultz II. + +2014-09-25 Toni Rönkkö + + * Version 1.21: compiles correctly under Open Watcom. Thanks to + Virgil Banowetz for a patch! + +2014-04-07 Toni Rönkkö + + * Version 1.20.1: the zip file from the previous version did not open + correctly with Microsoft's compressed folders. Thanks to Alexandre + for info! + +2014-03-17 Toni Ronkko + + * Version 1.20: dirent.h compiles correctly in 64-bit architecture. + Thanks to Aaron Simmons! + +2014-03-03 Toni Ronkko + + * Version 1.13.2: define DT_LNK for compatibility with Unix + programs. Thanks to Joel Bruick for suggestion! + +2013-01-27 Toni Ronkko + + * Version 1.13.1: patch from Edward Berner fixes set_errno() on + Windows NT 4.0. + + * Revised wcstombs() and mbstowcs() wrappers to make sure that they do + not write past their target string. + + * PATH_MAX from windows.h includes zero terminator so there is no + need to add one extra byte to variables and structures. + +2012-12-12 Toni Ronkko + + * Version 1.13: use the traditional 8+3 file naming scheme if a file + name cannot be represented in the default ANSI code page. Now + compiles again with MSVC 6.0. Thanks to Konstantin Khomoutov for + testing. + +2012-10-01 Toni Ronkko + + * Version 1.12.1: renamed wide-character DIR structure _wDIR to + _WDIR (with capital W) in order to maintain compatibility with MingW. + +2012-09-30 Toni Ronkko + + * Version 1.12: define PATH_MAX and NAME_MAX. Added wide-character + variants _wDIR, _wdirent, _wopendir(), _wreaddir(), _wclosedir() and + _wrewinddir(). Thanks to Edgar Buerkle and Jan Nijtmans for ideas + and code. + + * Now avoiding windows.h. This allows dirent.h to be integrated + more easily into programs using winsock. Thanks to Fernando + Azaldegui. + +2011-03-15 Toni Ronkko + + * Version 1.11: defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0. + +2010-08-11 Toni Ronkko + + * Version 1.10: added d_type and d_namlen fields to dirent structure. + The former is especially useful for determining whether directory + entry represents a file or a directory. For more information, see + http://www.delorie.com/gnu/docs/glibc/libc_270.html + + * Improved conformance to the standards. For example, errno is now + set properly on failure and assert() is never used. Thanks to Peter + Brockam for suggestions. + + * Fixed a bug in rewinddir(): when using relative directory names, + change of working directory no longer causes rewinddir() to fail. + +2009-12-15 John Cunningham + + * Version 1.9: added rewinddir member function + +2008-01-18 Toni Ronkko + + * Version 1.8: Using FindFirstFileA and WIN32_FIND_DATAA to avoid + converting string between multi-byte and unicode representations. + This makes the code simpler and also allows the code to be compiled + under MingW. Thanks to Azriel Fasten for the suggestion. + +2007-03-04 Toni Ronkko + + * Bug fix: due to the strncpy_s() function this file only compiled in + Visual Studio 2005. Using the new string functions only when the + compiler version allows. + +2006-11-02 Toni Ronkko + + * Major update: removed support for Watcom C, MS-DOS and Turbo C to + simplify the file, updated the code to compile cleanly on Visual + Studio 2005 with both unicode and multi-byte character strings, + removed rewinddir() as it had a bug. + +2006-08-20 Toni Ronkko + + * Removed all remarks about MSVC 1.0, which is antiqued now. + Simplified comments by removing SGML tags. + +2002-05-14 Toni Ronkko + + * Embedded the function definitions directly to the header so that no + source modules need to be included in the Visual Studio project. + Removed all the dependencies to other projects so that this header + file can be used independently. + +1998-05-28 Toni Ronkko + + * First version. diff --git a/deps/dirent/LICENSE b/deps/dirent/LICENSE new file mode 100644 index 0000000000..c5740cd7ec --- /dev/null +++ b/deps/dirent/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 1998-2019 Toni Ronkko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/dirent/README.md b/deps/dirent/README.md new file mode 100644 index 0000000000..c90647347b --- /dev/null +++ b/deps/dirent/README.md @@ -0,0 +1,96 @@ +# Dirent +Dirent is a C/C++ programming interface that allows programmers to retrieve +information about files and directories under Linux/UNIX. This project +provides Linux compatible Dirent interface for Microsoft Windows. + + +# Installation + +Download the latest Dirent installation package from +[GitHub](https://github.com/tronkko/dirent/releases) and +unpack the installation file with 7-zip, for example. The installation +package contains dirent.h file as well as a few example programs and +tests. + + +## Install Dirent for All Programs + +To make dirent.h available for all C/C++ programs, simply copy the +``include/dirent.h`` file to the system include directory. System include +directory contains header files such as assert.h and windows.h. In Visual +Studio 2008, for example, the system include may be found at +``C:\Program Files\Microsoft Visual Studio 9.0\VC\include``. + +Everything you need is included in the single dirent.h file, and you can +start using Dirent immediately -- there is no need to add files to your +Visual Studio project. + + +## Embed Dirent into Your Own Project + +If you wish to distribute dirent.h alongside with your own source code, then +copy ``include/dirent.h`` file to a new sub-directory within your project and +add that directory to include path on Windows while omitting the directory +under Linux/UNIX. This allows your project to be compiled against native +dirent.h on Linux/UNIX while substituting the functionality on Microsoft +Windows. + + +## Examples + +The installation package contains four example programs: + +Program | Purpose +-------- | ----------------------------------------------------------------- +ls | List files in a directory, e.g. ls "c:\Program Files" +find | Find files in subdirectories, e.g. find "c:\Program Files\CMake" +updatedb | Build database of files in a drive, e.g. updatedb c:\ +locate | Locate a file from database, e.g. locate notepad.exe + +To build the example programs, first install [CMake](https://cmake.org/). +Then, with CMake installed, open command prompt and create a temporary +directory ``c:\temp\dirent`` for the build files as + +``` +c:\ +mkdir temp +mkdir temp\dirent +cd temp\dirent +``` + +Generate build files as + +``` +cmake d:\dirent +``` + +where ``d:\dirent`` is the root directory of the Dirent package (containing +this README.md and LICENSE file). + +Once CMake is finished, open Visual Studio, load the generated dirent.sln file +from the build directory and build the solution. Once the build completes, run +the example programs from the command prompt as + +``` +cd Debug +ls . +find . +updatedb c:\ +locate cmd.exe +``` + + +# Copying + +Dirent may be freely distributed under the MIT license. See the +[LICENSE](LICENSE) file for details. + + +# Alternatives to Dirent + +I ported Dirent to Microsoft Windows in 1998 when only a few alternatives +were available. However, the situation has changed since then and nowadays +both [Cygwin](http://www.cygwin.com) and [MingW](http://www.mingw.org) +allow you to compile a great number of UNIX programs in Microsoft Windows. +They both provide a full dirent API as well as many other UNIX APIs. MingW +can even be used for commercial applications! diff --git a/deps/dirent/distclean.cmake b/deps/dirent/distclean.cmake new file mode 100644 index 0000000000..e6bde39227 --- /dev/null +++ b/deps/dirent/distclean.cmake @@ -0,0 +1,62 @@ +# Remove CMake generated temporary files +set (cmake_generated + ${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj + ${CMAKE_BINARY_DIR}/ALL_BUILD.vcxproj.filters + ${CMAKE_BINARY_DIR}/CMakeCache.txt + ${CMAKE_BINARY_DIR}/CMakeFiles + ${CMAKE_BINARY_DIR}/CTestTestfile.cmake + ${CMAKE_BINARY_DIR}/Continuous.vcxproj + ${CMAKE_BINARY_DIR}/Continuous.vcxproj.filters + ${CMAKE_BINARY_DIR}/DartConfiguration.tcl + ${CMAKE_BINARY_DIR}/Debug + ${CMAKE_BINARY_DIR}/Experimental.vcxproj + ${CMAKE_BINARY_DIR}/Experimental.vcxproj.filters + ${CMAKE_BINARY_DIR}/INSTALL.vcxproj + ${CMAKE_BINARY_DIR}/INSTALL.vcxproj.filters + ${CMAKE_BINARY_DIR}/Makefile + ${CMAKE_BINARY_DIR}/Nightly.vcxproj + ${CMAKE_BINARY_DIR}/Nightly.vcxproj.filters + ${CMAKE_BINARY_DIR}/NightlyMemoryCheck.vcxproj + ${CMAKE_BINARY_DIR}/NightlyMemoryCheck.vcxproj.filters + ${CMAKE_BINARY_DIR}/RUN_TESTS.vcxproj + ${CMAKE_BINARY_DIR}/RUN_TESTS.vcxproj.filters + ${CMAKE_BINARY_DIR}/Testing + ${CMAKE_BINARY_DIR}/Win32 + ${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj + ${CMAKE_BINARY_DIR}/ZERO_CHECK.vcxproj.filters + ${CMAKE_BINARY_DIR}/check.vcxproj + ${CMAKE_BINARY_DIR}/check.vcxproj.filters + ${CMAKE_BINARY_DIR}/cmake_install.cmake + ${CMAKE_BINARY_DIR}/dirent.sln + ${CMAKE_BINARY_DIR}/distclean.vcxproj + ${CMAKE_BINARY_DIR}/distclean.vcxproj.filters + ${CMAKE_BINARY_DIR}/find + ${CMAKE_BINARY_DIR}/find.dir + ${CMAKE_BINARY_DIR}/find.vcxproj + ${CMAKE_BINARY_DIR}/find.vcxproj.filters + ${CMAKE_BINARY_DIR}/locate + ${CMAKE_BINARY_DIR}/locate.dir + ${CMAKE_BINARY_DIR}/locate.vcxproj + ${CMAKE_BINARY_DIR}/locate.vcxproj.filters + ${CMAKE_BINARY_DIR}/ls + ${CMAKE_BINARY_DIR}/ls.dir + ${CMAKE_BINARY_DIR}/ls.vcxproj + ${CMAKE_BINARY_DIR}/ls.vcxproj.filters + ${CMAKE_BINARY_DIR}/t-compile + ${CMAKE_BINARY_DIR}/t-compile.dir + ${CMAKE_BINARY_DIR}/t-compile.vcxproj + ${CMAKE_BINARY_DIR}/t-compile.vcxproj.filters + ${CMAKE_BINARY_DIR}/t-dirent + ${CMAKE_BINARY_DIR}/t-dirent.dir + ${CMAKE_BINARY_DIR}/t-dirent.vcxproj + ${CMAKE_BINARY_DIR}/t-dirent.vcxproj.filters + ${CMAKE_BINARY_DIR}/updatedb + ${CMAKE_BINARY_DIR}/updatedb.dir + ${CMAKE_BINARY_DIR}/updatedb.vcxproj + ${CMAKE_BINARY_DIR}/updatedb.vcxproj.filters +) +foreach (file ${cmake_generated}) + if (EXISTS ${file}) + file (REMOVE_RECURSE ${file}) + endif() +endforeach (file) diff --git a/deps/dirent/examples/cat.c b/deps/dirent/examples/cat.c new file mode 100644 index 0000000000..7067bfcbde --- /dev/null +++ b/deps/dirent/examples/cat.c @@ -0,0 +1,85 @@ +/* + * Output contents of a file. + * + * Compile this file with Visual Studio and run the produced command in + * console with a file name argument. For example, command + * + * cat include\dirent.h + * + * will output the dirent.h to screen. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include + +static void output_file (const char *fn); + + +int +main( + int argc, char *argv[]) +{ + int i; + + /* Select default locale */ + setlocale (LC_ALL, ""); + + /* Require at least one file */ + if (argc == 1) { + fprintf (stderr, "Usage: cat filename\n"); + return EXIT_FAILURE; + } + + /* For each file name argument in command line */ + i = 1; + while (i < argc) { + output_file (argv[i]); + i++; + } + return EXIT_SUCCESS; +} + +/* + * Output file to screen + */ +static void +output_file( + const char *fn) +{ + FILE *fp; + + /* Open file */ + fp = fopen (fn, "r"); + if (fp != NULL) { + size_t n; + char buffer[4096]; + + /* Output file to screen */ + do { + + /* Read some bytes from file */ + n = fread (buffer, 1, 4096, fp); + + /* Output bytes to screen */ + fwrite (buffer, 1, n, stdout); + + } while (n != 0); + + /* Close file */ + fclose (fp); + + } else { + /* Could not open directory */ + fprintf (stderr, "Cannot open %s (%s)\n", fn, strerror (errno)); + exit (EXIT_FAILURE); + } +} diff --git a/deps/dirent/examples/find.c b/deps/dirent/examples/find.c new file mode 100644 index 0000000000..8073d05018 --- /dev/null +++ b/deps/dirent/examples/find.c @@ -0,0 +1,147 @@ +/* + * An example demonstrating recursive directory traversal. + * + * Compile this file with Visual Studio and run the produced command in + * console with a directory name argument. For example, command + * + * find "C:\Program Files" + * + * will output thousands of file names such as + * + * c:\Program Files/7-Zip/7-zip.chm + * c:\Program Files/7-Zip/7-zip.dll + * c:\Program Files/7-Zip/7z.dll + * c:\Program Files/Adobe/Reader 10.0/Reader/logsession.dll + * c:\Program Files/Adobe/Reader 10.0/Reader/LogTransport2.exe + * c:\Program Files/Windows NT/Accessories/wordpad.exe + * c:\Program Files/Windows NT/Accessories/write.wpc + * + * The find command provided by this file is only an example: the command does + * not provide options to restrict the output to certain files as the Linux + * version does. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include + +static int find_directory (const char *dirname); + + +int +main( + int argc, char *argv[]) +{ + int i; + int ok; + + /* Select default locale */ + setlocale (LC_ALL, ""); + + /* For each directory in command line */ + i = 1; + while (i < argc) { + ok = find_directory (argv[i]); + if (!ok) { + exit (EXIT_FAILURE); + } + i++; + } + + /* List current working directory if no arguments on command line */ + if (argc == 1) { + find_directory ("."); + } + return EXIT_SUCCESS; +} + +/* Find files and subdirectories recursively */ +static int +find_directory( + const char *dirname) +{ + DIR *dir; + char buffer[PATH_MAX + 2]; + char *p = buffer; + const char *src; + char *end = &buffer[PATH_MAX]; + int ok; + + /* Copy directory name to buffer */ + src = dirname; + while (p < end && *src != '\0') { + *p++ = *src++; + } + *p = '\0'; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir != NULL) { + struct dirent *ent; + + /* Print all files and directories within the directory */ + while ((ent = readdir (dir)) != NULL) { + char *q = p; + char c; + + /* Get final character of directory name */ + if (buffer < q) { + c = q[-1]; + } else { + c = ':'; + } + + /* Append directory separator if not already there */ + if (c != ':' && c != '/' && c != '\\') { + *q++ = '/'; + } + + /* Append file name */ + src = ent->d_name; + while (q < end && *src != '\0') { + *q++ = *src++; + } + *q = '\0'; + + /* Decide what to do with the directory entry */ + switch (ent->d_type) { + case DT_LNK: + case DT_REG: + /* Output file name with directory */ + printf ("%s\n", buffer); + break; + + case DT_DIR: + /* Scan sub-directory recursively */ + if (strcmp (ent->d_name, ".") != 0 + && strcmp (ent->d_name, "..") != 0) { + find_directory (buffer); + } + break; + + default: + /* Ignore device entries */ + /*NOP*/; + } + + } + + closedir (dir); + ok = 1; + + } else { + /* Could not open directory */ + fprintf (stderr, "Cannot open %s (%s)\n", dirname, strerror (errno)); + ok = 0; + } + + return ok; +} diff --git a/deps/dirent/examples/locate.c b/deps/dirent/examples/locate.c new file mode 100644 index 0000000000..c5cf0f471f --- /dev/null +++ b/deps/dirent/examples/locate.c @@ -0,0 +1,280 @@ +/* + * A file look-up utility to complement updatedb + * + * Compile and run updatedb command first to create locate.db file. Then, + * compile this program with Visual Studio and run the program in console with + * a file name argument. For example, the command + * + * locate autoexec + * + * might output something like + * + * c:/AUTOEXEC.BAT + * c:/WINDOWS/repair/autoexec.nt + * c:/WINDOWS/system32/AUTOEXEC.NT + * + * Be ware that this file uses wide-character API which is not compatible + * with Linux or other major Unixes. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#include +#include +#include +#include +#ifdef WIN32 +# include +# include +#endif +#include + +/* File name and location of database file */ +#define DB_LOCATION L"locate.db" + + +/* Forward-decl */ +static int db_locate (const wchar_t *pattern); +static int db_match (const wchar_t *fn, const wchar_t *pattern); +static void db_open (void); +static void db_close (void); +static int db_read (wchar_t *buffer, size_t max); + + +/* Module local variables */ +static FILE *db = NULL; + + +int +main( + int argc, char *argv[]) +{ +#ifdef WIN32 + int i; + + /* Prepare for unicode output */ + _setmode (_fileno (stdout), _O_U16TEXT); + + /* For each pattern in command line */ + i = 1; + while (i < argc) { + wchar_t buffer[PATH_MAX + 1]; + errno_t error; + size_t n; + int count = 0; + + /* Convert ith argument to wide-character string */ + error = mbstowcs_s (&n, buffer, PATH_MAX, argv[i], _TRUNCATE); + if (!error) { + /* Find files matching pattern */ + count = db_locate (buffer); + + /* Output warning if string is not found */ + if (count == 0) { + wprintf (L"%s not found\n", buffer); + } + } + + + i++; + } + + if (argc < 2) { + wprintf (L"Usage: locate pattern\n"); + exit (EXIT_FAILURE); + } +#else + printf ("locate only works on Microsoft Windows\n"); +#endif + + return EXIT_SUCCESS; +} + +/* Match pattern against files in locate.db file */ +static int +db_locate( + const wchar_t *pattern) +{ + int count = 0; + +#ifdef WIN32 + wchar_t buffer[PATH_MAX + 1]; + + /* Open locate.db for read */ + db_open (); + + /* Read one directory and file name at a time from database file */ + while (db_read (buffer, PATH_MAX)) { + /* See if file name in buffer matches the search pattern */ + if (db_match (buffer, pattern)) { + /* Match found => output file name and path */ + wprintf (L"%s\n", buffer); + count++; + } + + } + + db_close (); +#endif + + return count; +} + +/* Match pattern against file name */ +static int +db_match( + const wchar_t *fn, const wchar_t *pattern) +{ + int found = 0; + +#ifdef WIN32 + wchar_t *p; + wchar_t base[PATH_MAX + 1]; + wchar_t patt[PATH_MAX + 1]; + int i; + int done = 0; + + /* Locate zero-terminator from fn */ + p = wcschr (fn, '\0'); + + /* Find base name from buffer */ + while (fn < p && !done) { + switch (p[-1]) { + case ':': + case '/': + case '\\': + /* Final path separator found */ + done = 1; + break; + + default: + /* No path separator yet */ + p--; + } + } + + /* Convert base name to lower case */ + i = 0; + while (i < PATH_MAX && p[i] != '\0') { + base[i] = towlower (p[i]); + i++; + } + base[i] = '\0'; + + /* Convert search pattern to lower case */ + i = 0; + while (i < PATH_MAX && pattern[i] != '\0') { + patt[i] = towlower (pattern[i]); + i++; + } + patt[i] = '\0'; + + /* See if file name matches pattern */ + if (wcsstr (base, patt) != NULL) { + found = 1; + } else { + found = 0; + } +#endif + + return found; +} + +/* + * Read line from locate.db. This function is same as fgetws() except + * that new-line at the end of line is not included. + */ +static int +db_read( + wchar_t *buffer, size_t max) +{ + int ok = 0; + +#ifdef WIN32 + size_t i = 0; + wchar_t c; + int done = 0; + + do { + /* Read wide-character from stream */ + if (db) { + c = fgetwc (db); + } else { + wprintf (L"Database not open\n"); + exit (EXIT_SUCCESS); + } + + /* Determine how to process character */ + switch (c) { + case '\r': + /* Ignore, should be handled by run-time libraries */ + /*NOP*/; + break; + + case '\n': + /* End of string => return file name and true */ + done = 1; + ok = 1; + break; + + case /*EOF*/WEOF: + /* End of file */ + done = 1; + if (i == 0) { + /* No data in buffer => return false to indicate EOF */ + ok = 0; + } else { + /* Data in buffer => return true */ + ok = 1; + } + break; + + default: + /* Store character */ + if (i < max - 1) { + buffer[i++] = c; + } else { + wprintf (L"Buffer too small"); + exit (EXIT_FAILURE); + } + } + } while (!done); + + /* Zero-terminate buffer */ + buffer[i] = '\0'; +#endif + + return ok; +} + +/* Open database file locate.db */ +static void +db_open( + void) +{ +#ifdef WIN32 + if (db == NULL) { + errno_t error; + + /* Open file for writing */ + error = _wfopen_s (&db, DB_LOCATION, L"rt, ccs=UNICODE"); + if (error) { + wprintf (L"Cannot open %s\n", DB_LOCATION); + exit (EXIT_FAILURE); + } + } +#endif +} + +/* Close database file */ +static void +db_close( + void) +{ + if (db) { + fclose (db); + db = NULL; + } +} diff --git a/deps/dirent/examples/ls.c b/deps/dirent/examples/ls.c new file mode 100644 index 0000000000..d0dcee12f0 --- /dev/null +++ b/deps/dirent/examples/ls.c @@ -0,0 +1,103 @@ +/* + * An example demonstrating basic directory listing. + * + * Compile this file with Visual Studio and run the produced command in + * console with a directory name argument. For example, command + * + * ls "c:\Program Files" + * + * might output something like + * + * ./ + * ../ + * 7-Zip/ + * Internet Explorer/ + * Microsoft Visual Studio 9.0/ + * Microsoft.NET/ + * Mozilla Firefox/ + * + * The ls command provided by this file is only an example: the command does + * not have any fancy options like "ls -al" in Linux and the command does not + * support file name matching like "ls *.c". + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include + +static void list_directory (const char *dirname); + + +int +main( + int argc, char *argv[]) +{ + int i; + + /* Select default locale */ + setlocale (LC_ALL, ""); + + /* For each directory in command line */ + i = 1; + while (i < argc) { + list_directory (argv[i]); + i++; + } + + /* List current working directory if no arguments on command line */ + if (argc == 1) { + list_directory ("."); + } + return EXIT_SUCCESS; +} + +/* + * List files and directories within a directory. + */ +static void +list_directory( + const char *dirname) +{ + DIR *dir; + struct dirent *ent; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir != NULL) { + + /* Print all files and directories within the directory */ + while ((ent = readdir (dir)) != NULL) { + switch (ent->d_type) { + case DT_REG: + printf ("%s\n", ent->d_name); + break; + + case DT_DIR: + printf ("%s/\n", ent->d_name); + break; + + case DT_LNK: + printf ("%s@\n", ent->d_name); + break; + + default: + printf ("%s*\n", ent->d_name); + } + } + + closedir (dir); + + } else { + /* Could not open directory */ + fprintf (stderr, "Cannot open %s (%s)\n", dirname, strerror (errno)); + exit (EXIT_FAILURE); + } +} diff --git a/deps/dirent/examples/scandir.c b/deps/dirent/examples/scandir.c new file mode 100644 index 0000000000..5c783e9527 --- /dev/null +++ b/deps/dirent/examples/scandir.c @@ -0,0 +1,110 @@ +/* + * Example program demonstrating the use of scandir function. + * + * Compile this file with Visual Studio and run the produced command in + * console with a directory name argument. For example, command + * + * scandir "c:\Program Files" + * + * might output something like + * + * ./ + * ../ + * 7-Zip/ + * Internet Explorer/ + * Microsoft Visual Studio 9.0/ + * Microsoft.NET/ + * Mozilla Firefox/ + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#define _CRT_SECURE_NO_WARNINGS +#include +#include +#include +#include +#include +#include + +static void list_directory (const char *dirname); + + +int +main( + int argc, char *argv[]) +{ + int i; + + /* Select default locale */ + setlocale (LC_ALL, ""); + + /* For each directory in command line */ + i = 1; + while (i < argc) { + list_directory (argv[i]); + i++; + } + + /* List current working directory if no arguments on command line */ + if (argc == 1) { + list_directory ("."); + } + return EXIT_SUCCESS; +} + +/* + * List files and directories within a directory. + */ +static void +list_directory( + const char *dirname) +{ + struct dirent **files; + int i; + int n; + + /* Scan files in directory */ + n = scandir (dirname, &files, NULL, alphasort); + if (n >= 0) { + + /* Loop through file names */ + for (i = 0; i < n; i++) { + struct dirent *ent; + + /* Get pointer to file entry */ + ent = files[i]; + + /* Output file name */ + switch (ent->d_type) { + case DT_REG: + printf ("%s\n", ent->d_name); + break; + + case DT_DIR: + printf ("%s/\n", ent->d_name); + break; + + case DT_LNK: + printf ("%s@\n", ent->d_name); + break; + + default: + printf ("%s*\n", ent->d_name); + } + + } + + /* Release file names */ + for (i = 0; i < n; i++) { + free (files[i]); + } + free (files); + + } else { + fprintf (stderr, "Cannot open %s (%s)\n", dirname, strerror (errno)); + exit (EXIT_FAILURE); + } +} diff --git a/deps/dirent/examples/updatedb.c b/deps/dirent/examples/updatedb.c new file mode 100644 index 0000000000..1642178b9c --- /dev/null +++ b/deps/dirent/examples/updatedb.c @@ -0,0 +1,232 @@ +/* + * An example demonstrating wide-character functions + * + * Compile this file with Visual Studio and run the produced command in + * console with a directory name argument. For example, command + * + * updatedb C:\ + * + * will produce the file locate.db with one file name per line such as + * + * c:\Program Files/7-Zip/7-zip.chm + * c:\Program Files/7-Zip/7-zip.dll + * c:\Program Files/7-Zip/7z.dll + * c:\Program Files/Adobe/Reader 10.0/Reader/logsession.dll + * c:\Program Files/Adobe/Reader 10.0/Reader/LogTransport2.exe + * c:\Program Files/Windows NT/Accessories/wordpad.exe + * c:\Program Files/Windows NT/Accessories/write.wpc + * + * Be ware that this file uses wide-character API which is not compatible + * with Linux or other major Unixes. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#include +#include +#include +#include +#ifdef WIN32 +# include +# include +#endif +#include + +/* File name and location of database file */ +#define DB_LOCATION L"locate.db" + + +/* Forward-decl */ +static int update_directory (const wchar_t *dirname); +static void db_open (void); +static void db_close (void); +static void db_store (const wchar_t *dirname); + + +/* Module local variables */ +static FILE *db = NULL; + + +int +main( + int argc, char *argv[]) +{ +#ifdef WIN32 + int i; + int ok; + + /* Prepare for unicode output */ + _setmode (_fileno (stdout), _O_U16TEXT); + + /* Open locate.db */ + db_open (); + + /* For each directory in command line */ + i = 1; + while (i < argc) { + wchar_t buffer[PATH_MAX + 1]; + errno_t error; + size_t n; + + /* Convert ith argument to wide-character string */ + error = mbstowcs_s (&n, buffer, PATH_MAX, argv[i], _TRUNCATE); + if (!error) { + + /* Scan directory for files */ + ok = update_directory (buffer); + if (!ok) { + wprintf (L"Cannot open directory %s\n", buffer); + exit (EXIT_FAILURE); + } + + } + + i++; + } + + /* Use current working directory if no arguments on command line */ + if (argc == 1) { + update_directory (L"."); + } + + db_close (); +#else + printf ("updatedb only works on Microsoft Windows\n"); +#endif + + return EXIT_SUCCESS; +} + +/* Find files recursively */ +static int +update_directory( + const wchar_t *dirname) +{ + int ok = 0; + +#ifdef WIN32 + _WDIR *dir; + wchar_t buffer[PATH_MAX + 2]; + wchar_t *p = buffer; + const wchar_t *src; + wchar_t *end = &buffer[PATH_MAX]; + + /* Copy directory name to buffer */ + src = dirname; + while (p < end && *src != '\0') { + *p++ = *src++; + } + *p = '\0'; + + /* Open directory stream */ + dir = _wopendir (dirname); + if (dir != NULL) { + struct _wdirent *ent; + + /* Print all files and directories within the directory */ + while ((ent = _wreaddir (dir)) != NULL) { + wchar_t *q = p; + wchar_t c; + + /* Get final character of directory name */ + if (buffer < q) { + c = q[-1]; + } else { + c = ':'; + } + + /* Append directory separator if not already there */ + if (c != ':' && c != '/' && c != '\\') { + *q++ = '/'; + } + + /* Append file name */ + src = ent->d_name; + while (q < end && *src != '\0') { + *q++ = *src++; + } + *q = '\0'; + + /* Decide what to do with the directory entry */ + switch (ent->d_type) { + case DT_REG: + /* Store file name */ + db_store (buffer); + break; + + case DT_DIR: + /* Scan sub-directory recursively */ + if (wcscmp (ent->d_name, L".") != 0 + && wcscmp (ent->d_name, L"..") != 0) { + update_directory (buffer); + } + break; + + default: + /* Do not device entries */ + /*NOP*/; + } + + } + + wclosedir (dir); + ok = 1; + + } else { + + /* Cannot open directory */ + ok = 0; + + } +#endif + + return ok; +} + +/* Store file name to locate.db */ +static void +db_store( + const wchar_t *dirname) +{ +#ifdef WIN32 + if (db) { + /* Output line to file */ + fwprintf (db, L"%s\n", dirname); + } else { + wprintf (L"Database not open\n"); + exit (EXIT_FAILURE); + } +#endif +} + +/* Open database file locate.db */ +static void +db_open( + void) +{ +#ifdef WIN32 + if (db == NULL) { + errno_t error; + + /* Open file for writing */ + error = _wfopen_s (&db, DB_LOCATION, L"wt, ccs=UNICODE"); + if (error) { + wprintf (L"Cannot open %s\n", DB_LOCATION); + exit (EXIT_FAILURE); + } + } +#endif +} + +/* Close database file */ +static void +db_close( + void) +{ + if (db) { + fclose (db); + db = NULL; + } +} diff --git a/deps/dirent/include/dirent.h b/deps/dirent/include/dirent.h new file mode 100644 index 0000000000..c885b398a7 --- /dev/null +++ b/deps/dirent/include/dirent.h @@ -0,0 +1,1160 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; + DWORD n; + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/deps/dirent/tests/1/dir/readme.txt b/deps/dirent/tests/1/dir/readme.txt new file mode 100644 index 0000000000..0c582df36a --- /dev/null +++ b/deps/dirent/tests/1/dir/readme.txt @@ -0,0 +1,3 @@ +This file ensures that the directory dir will be created accordingly when +you unzip dirent to your computer. The directory itself is needed by the +test program t-dirent. diff --git a/deps/dirent/tests/1/file b/deps/dirent/tests/1/file new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/2/Testfile-1.2.3.dat b/deps/dirent/tests/2/Testfile-1.2.3.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/2/file.txt b/deps/dirent/tests/2/file.txt new file mode 100644 index 0000000000..980283b0a9 --- /dev/null +++ b/deps/dirent/tests/2/file.txt @@ -0,0 +1 @@ +This dummy file is needed by the test program t-dirent. diff --git a/deps/dirent/tests/3/3zero.dat b/deps/dirent/tests/3/3zero.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/666.dat b/deps/dirent/tests/3/666.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/Qwerty-my-aunt.dat b/deps/dirent/tests/3/Qwerty-my-aunt.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/README.txt b/deps/dirent/tests/3/README.txt new file mode 100644 index 0000000000..e93188e235 --- /dev/null +++ b/deps/dirent/tests/3/README.txt @@ -0,0 +1,2 @@ +This directory contains some random files for the t-scandir test program. The +files are empty and only the file names matter. diff --git a/deps/dirent/tests/3/aaa.dat b/deps/dirent/tests/3/aaa.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/dirent.dat b/deps/dirent/tests/3/dirent.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/empty.dat b/deps/dirent/tests/3/empty.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/sane-1.12.0.dat b/deps/dirent/tests/3/sane-1.12.0.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/sane-1.2.3.dat b/deps/dirent/tests/3/sane-1.2.3.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/sane-1.2.4.dat b/deps/dirent/tests/3/sane-1.2.4.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/3/zebra.dat b/deps/dirent/tests/3/zebra.dat new file mode 100644 index 0000000000..e69de29bb2 diff --git a/deps/dirent/tests/t-compile.c b/deps/dirent/tests/t-compile.c new file mode 100644 index 0000000000..e45d52f332 --- /dev/null +++ b/deps/dirent/tests/t-compile.c @@ -0,0 +1,47 @@ +/* + * Test program to make sure that dirent compiles cleanly with winsock. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#include +#ifdef WIN32 +# include +# include +#endif +#include +#include +#include + +int +main( + int argc, char *argv[]) +{ + struct dirent *dirp = NULL; + + (void) argc; + (void) argv; + +#ifdef _DIRENT_HAVE_D_TYPE + printf ("Has d_type\n"); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + printf ("Has d_namlen\n"); +#endif +#ifdef _D_EXACT_NAMLEN + printf ("Has _D_EXACT_NAMLEN\n"); +#endif +#ifdef _D_ALLOC_NAMLEN + printf ("Has _D_ALLOC_NAMLEN\n"); +#endif +#ifdef _D_ALLOC_NAMLEN + printf ("Has _D_ALLOC_NAMLEN\n"); +#endif + printf ("Length of d_name with terminator: %d\n", + (int) sizeof (dirp->d_name)); + + printf ("OK\n"); + return EXIT_SUCCESS; +} diff --git a/deps/dirent/tests/t-cplusplus.cpp b/deps/dirent/tests/t-cplusplus.cpp new file mode 100644 index 0000000000..81d04d0bc2 --- /dev/null +++ b/deps/dirent/tests/t-cplusplus.cpp @@ -0,0 +1,160 @@ +/* + * Test program to make sure that dirent compiles cleanly with C++ + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#include +#include +#include +#include +using namespace std; + + +/* Filter and sort functions */ +static int only_readme (const struct dirent *entry); + + +int +main( + int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + /* Basic directory retrieval */ + { + DIR *dir; + struct dirent *ent; + int found = 0; + + /* Open directory */ + dir = opendir ("tests/1"); + if (dir == NULL) { + cerr << "Directory tests/1 not found" << endl; + abort (); + } + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 1); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 1); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 1); +#endif + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 2); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 2); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 2); +#endif + found += 2; + + } else if (strcmp (ent->d_name, "file") == 0) { + /* Regular file */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_REG); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 4); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 4); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 4); +#endif + found += 4; + + } else if (strcmp (ent->d_name, "dir") == 0) { + /* Just a directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 3); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 3); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 3); +#endif + found += 8; + + } else { + /* Other file */ + cerr << "Unexpected file " << ent->d_name << endl; + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + closedir (dir); + } + + /* Basic scan with simple filter function */ + { + struct dirent **files; + int n; + int i; + + /* Read directory entries */ + n = scandir ("tests/3", &files, only_readme, alphasort); + assert (n == 1); + + /* Make sure that the filter works */ + assert (strcmp (files[0]->d_name, "README.txt") == 0); + + /* Release file names */ + for (i = 0; i < n; i++) { + free (files[i]); + } + free (files); + } + + + cout << "OK" << endl; + return EXIT_SUCCESS; +} + +/* Only pass README.txt file */ +static int +only_readme (const struct dirent *entry) +{ + int pass; + + if (strcmp (entry->d_name, "README.txt") == 0) { + pass = 1; + } else { + pass = 0; + } + + return pass; +} diff --git a/deps/dirent/tests/t-dirent.c b/deps/dirent/tests/t-dirent.c new file mode 100644 index 0000000000..053c93a6de --- /dev/null +++ b/deps/dirent/tests/t-dirent.c @@ -0,0 +1,633 @@ +/* + * A test program to make sure that dirent works correctly. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#include +#include +#include +#ifdef _MSC_VER +# include +# define chdir(x) _chdir(x) +#else +# include +#endif +#include +#include +#include + +#undef NDEBUG +#include + + +int +main( + int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + /* File type macros */ + { + assert (DTTOIF(DT_REG) == S_IFREG); + assert (DTTOIF(DT_DIR) == S_IFDIR); + assert (DTTOIF(DT_FIFO) == S_IFIFO); + assert (DTTOIF(DT_SOCK) == S_IFSOCK); + assert (DTTOIF(DT_CHR) == S_IFCHR); + assert (DTTOIF(DT_BLK) == S_IFBLK); + + assert (IFTODT(S_IFREG) == DT_REG); + assert (IFTODT(S_IFDIR) == DT_DIR); + assert (IFTODT(S_IFIFO) == DT_FIFO); + assert (IFTODT(S_IFSOCK) == DT_SOCK); + assert (IFTODT(S_IFCHR) == DT_CHR); + assert (IFTODT(S_IFBLK) == DT_BLK); + } + + /* Basic directory retrieval */ + { + DIR *dir; + struct dirent *ent; + int found = 0; + + /* Open directory */ + dir = opendir ("tests/1"); + if (dir == NULL) { + fprintf (stderr, "Directory tests/1 not found\n"); + abort (); + } + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 1); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 1); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 1); +#endif + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 2); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 2); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 2); +#endif + found += 2; + + } else if (strcmp (ent->d_name, "file") == 0) { + /* Regular file */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_REG); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 4); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 4); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 4); +#endif + found += 4; + + } else if (strcmp (ent->d_name, "dir") == 0) { + /* Just a directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 3); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 3); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 3); +#endif + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", ent->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + closedir (dir); + } + + /* Function opendir() fails if directory doesn't exist */ + { + DIR *dir; + + /* Open directory */ + dir = opendir ("tests/invalid"); + assert (dir == NULL); + assert (errno == ENOENT); + } + + /* Function opendir() fails if pathname is really a file */ + { + DIR *dir; + + /* Open directory */ + dir = opendir ("tests/1/file"); + assert (dir == NULL); + assert (errno == ENOTDIR); + } + + /* Function opendir() fails if pathname is a zero-length string */ + { + DIR *dir; + + /* Open directory */ + dir = opendir (""); + assert (dir == NULL); + assert (errno == ENOENT); + } + + /* Rewind of directory stream */ + { + DIR *dir; + struct dirent *ent; + int found = 0; + + /* Open directory */ + dir = opendir ("tests/1"); + assert (dir != NULL); + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ + found += 2; + + } else if (strcmp (ent->d_name, "file") == 0) { + /* Regular file */ + found += 4; + + } else if (strcmp (ent->d_name, "dir") == 0) { + /* Just a directory */ + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", ent->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + /* Rewind stream and read entries again */ + rewinddir (dir); + found = 0; + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ + found += 2; + + } else if (strcmp (ent->d_name, "file") == 0) { + /* Regular file */ + found += 4; + + } else if (strcmp (ent->d_name, "dir") == 0) { + /* Just a directory */ + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", ent->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + closedir (dir); + } + + /* Rewind with intervening change of working directory */ + { + DIR *dir; + struct dirent *ent; + int found = 0; + int errorcode; + + /* Open directory */ + dir = opendir ("tests/1"); + assert (dir != NULL); + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ + found += 2; + + } else if (strcmp (ent->d_name, "file") == 0) { + /* Regular file */ + found += 4; + + } else if (strcmp (ent->d_name, "dir") == 0) { + /* Just a directory */ + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", ent->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + /* Change working directory */ + errorcode = chdir ("tests"); + assert (errorcode == 0); + + /* Rewind stream and read entries again */ + rewinddir (dir); + found = 0; + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ + found += 2; + + } else if (strcmp (ent->d_name, "file") == 0) { + /* Regular file */ + found += 4; + + } else if (strcmp (ent->d_name, "dir") == 0) { + /* Just a directory */ + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", ent->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + /* Restore working directory */ + errorcode = chdir (".."); + assert (errorcode == 0); + + closedir (dir); + } + + /* Long file name */ + { + DIR *dir; + struct dirent *ent; + int found = 0; + + /* Open directory */ + dir = opendir ("tests/2"); + if (dir == NULL) { + fprintf (stderr, "Directory tests/2 not found\n"); + abort (); + } + + /* Read entries */ + while ((ent = readdir (dir)) != NULL) { + + /* Check each file */ + if (strcmp (ent->d_name, ".") == 0) { + /* Directory itself */ + found += 1; + + } else if (strcmp (ent->d_name, "..") == 0) { + /* Parent directory */ + found += 2; + + } else if (strcmp (ent->d_name, "file.txt") == 0) { + /* Regular 8+3 filename */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_REG); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 8); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 8); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 8); +#endif + found += 4; + + } else if (strcmp (ent->d_name, "Testfile-1.2.3.dat") == 0) { + /* Long file name with multiple dots */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (ent->d_type == DT_REG); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (ent->d_namlen == 18); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(ent) == 18); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(ent) > 18); +#endif + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", ent->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + closedir (dir); + } + + /* Basic directory retrieval with readdir_r */ + { + DIR *dir; + struct dirent ent[10]; + struct dirent *entry; + size_t i = 0; + size_t n = 0; + int found = 0; + + /* Open directory */ + dir = opendir ("tests/1"); + if (dir == NULL) { + fprintf (stderr, "Directory tests/1 not found\n"); + abort (); + } + + /* Read entries to table */ + while (readdir_r (dir, &ent[n], &entry) == /*OK*/0 && entry != 0) { + n++; + assert (n <= 4); + } + + /* Make sure that we got all the files from directory */ + assert (n == 4); + + /* Check entries in memory */ + for (i = 0; i < 4; i++) { + entry = &ent[i]; + + /* Check each file */ + if (strcmp (entry->d_name, ".") == 0) { + /* Directory itself */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 1); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 1); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 1); +#endif + found += 1; + + } else if (strcmp (entry->d_name, "..") == 0) { + /* Parent directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 2); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 2); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 2); +#endif + found += 2; + + } else if (strcmp (entry->d_name, "file") == 0) { + /* Regular file */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_REG); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 4); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 4); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 4); +#endif + found += 4; + + } else if (strcmp (entry->d_name, "dir") == 0) { + /* Just a directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 3); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 3); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 3); +#endif + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file %s\n", entry->d_name); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + closedir (dir); + } + + /* Basic directory retrieval with _wreaddir_r */ +#ifdef WIN32 + { + _WDIR *dir; + struct _wdirent ent[10]; + struct _wdirent *entry; + size_t i = 0; + size_t n = 0; + int found = 0; + + /* Open directory */ + dir = _wopendir (L"tests/1"); + if (dir == NULL) { + fprintf (stderr, "Directory tests/1 not found\n"); + abort (); + } + + /* Read entries to table */ + while (_wreaddir_r (dir, &ent[n], &entry) == /*OK*/0 && entry != 0) { + n++; + assert (n <= 4); + } + + /* Make sure that we got all the files from directory */ + assert (n == 4); + + /* Check entries in memory */ + for (i = 0; i < 4; i++) { + entry = &ent[i]; + + /* Check each file */ + if (wcscmp (entry->d_name, L".") == 0) { + /* Directory itself */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 1); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 1); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 1); +#endif + found += 1; + + } else if (wcscmp (entry->d_name, L"..") == 0) { + /* Parent directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 2); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 2); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 2); +#endif + found += 2; + + } else if (wcscmp (entry->d_name, L"file") == 0) { + /* Regular file */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_REG); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 4); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 4); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 4); +#endif + found += 4; + + } else if (wcscmp (entry->d_name, L"dir") == 0) { + /* Just a directory */ +#ifdef _DIRENT_HAVE_D_TYPE + assert (entry->d_type == DT_DIR); +#endif +#ifdef _DIRENT_HAVE_D_NAMLEN + assert (entry->d_namlen == 3); +#endif +#ifdef _D_EXACT_NAMLEN + assert (_D_EXACT_NAMLEN(entry) == 3); +#endif +#ifdef _D_ALLOC_NAMLEN + assert (_D_ALLOC_NAMLEN(entry) > 3); +#endif + found += 8; + + } else { + /* Other file */ + fprintf (stderr, "Unexpected file\n"); + abort (); + } + + } + + /* Make sure that all files were found */ + assert (found == 0xf); + + _wclosedir (dir); + } +#endif + + printf ("OK\n"); + return EXIT_SUCCESS; +} diff --git a/deps/dirent/tests/t-scandir.c b/deps/dirent/tests/t-scandir.c new file mode 100644 index 0000000000..491c962fe5 --- /dev/null +++ b/deps/dirent/tests/t-scandir.c @@ -0,0 +1,264 @@ +/* + * Make sure that scandir function works OK. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ + +/* Silence warning about fopen being insecure */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef NDEBUG +#include + + +/* Filter and sort functions */ +static int only_readme (const struct dirent *entry); +static int no_directories (const struct dirent *entry); +static int reverse_alpha (const struct dirent **a, const struct dirent **b); + + +int +main( + int argc, char *argv[]) +{ + struct dirent **files; + int i; + int n; + + (void) argc; + (void) argv; + + /* Initialize random number generator */ + srand ((unsigned) time (NULL)); + + /* Basic scan with simple filter function */ + { + /* Read directory entries */ + n = scandir ("tests/3", &files, only_readme, alphasort); + assert (n == 1); + + /* Make sure that the filter works */ + assert (strcmp (files[0]->d_name, "README.txt") == 0); + + /* Release file names */ + for (i = 0; i < n; i++) { + free (files[i]); + } + free (files); + } + + /* Basic scan with default sorting function */ + { + /* Read directory entries in alphabetic order */ + n = scandir ("tests/3", &files, NULL, alphasort); + assert (n == 13); + + /* Make sure that we got all the file names in the proper order */ + assert (strcmp (files[0]->d_name, ".") == 0); + assert (strcmp (files[1]->d_name, "..") == 0); + assert (strcmp (files[2]->d_name, "3zero.dat") == 0); + assert (strcmp (files[3]->d_name, "666.dat") == 0); + assert (strcmp (files[4]->d_name, "Qwerty-my-aunt.dat") == 0); + assert (strcmp (files[5]->d_name, "README.txt") == 0); + assert (strcmp (files[6]->d_name, "aaa.dat") == 0); + assert (strcmp (files[7]->d_name, "dirent.dat") == 0); + assert (strcmp (files[8]->d_name, "empty.dat") == 0); + assert (strcmp (files[9]->d_name, "sane-1.12.0.dat") == 0); + assert (strcmp (files[10]->d_name, "sane-1.2.3.dat") == 0); + assert (strcmp (files[11]->d_name, "sane-1.2.4.dat") == 0); + assert (strcmp (files[12]->d_name, "zebra.dat") == 0); + + /* Release file names */ + for (i = 0; i < n; i++) { + free (files[i]); + } + free (files); + } + + /* Custom filter AND sort function */ + { + /* Read directory entries in alphabetic order */ + n = scandir ("tests/3", &files, no_directories, reverse_alpha); + assert (n == 11); + + /* Make sure that we got all the FILE names in the REVERSE order */ + assert (strcmp (files[0]->d_name, "zebra.dat") == 0); + assert (strcmp (files[1]->d_name, "sane-1.2.4.dat") == 0); + assert (strcmp (files[2]->d_name, "sane-1.2.3.dat") == 0); + assert (strcmp (files[3]->d_name, "sane-1.12.0.dat") == 0); + assert (strcmp (files[4]->d_name, "empty.dat") == 0); + assert (strcmp (files[5]->d_name, "dirent.dat") == 0); + assert (strcmp (files[6]->d_name, "aaa.dat") == 0); + assert (strcmp (files[7]->d_name, "README.txt") == 0); + assert (strcmp (files[8]->d_name, "Qwerty-my-aunt.dat") == 0); + assert (strcmp (files[9]->d_name, "666.dat") == 0); + assert (strcmp (files[10]->d_name, "3zero.dat") == 0); + + /* Release file names */ + for (i = 0; i < n; i++) { + free (files[i]); + } + free (files); + } + + /* Trying to read from non-existent directory leads to an error */ + { + files = NULL; + n = scandir ("tests/invalid", &files, NULL, alphasort); + assert (n == -1); + assert (files == NULL); + assert (errno == ENOENT); + } + + /* Trying to open file as a directory produces ENOTDIR error */ + { + files = NULL; + n = scandir ("tests/3/666.dat", &files, NULL, alphasort); + assert (n == -1); + assert (files == NULL); + assert (errno == ENOTDIR); + } + + /* Scan large directory */ + { + char dirname[PATH_MAX+1]; + int i, j; + int ok; + + /* Copy name of temporary directory to variable dirname */ +#ifdef WIN32 + i = GetTempPathA (PATH_MAX, dirname); + assert (i > 0); +#else + strcpy (dirname, "/tmp/"); + i = strlen (dirname); +#endif + + /* Append random characters to dirname */ + for (j = 0; j < 10; j++) { + char c; + + /* Generate random character */ + c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26]; + + /* Append character to dirname */ + assert (i < PATH_MAX); + dirname[i++] = c; + } + + /* Terminate directory name */ + assert (i < PATH_MAX); + dirname[i] = '\0'; + + /* Create directory */ +#ifdef WIN32 + ok = CreateDirectoryA (dirname, NULL); + assert (ok); +#else + ok = mkdir (dirname, 0700); + assert (ok == /*success*/0); +#endif + + /* Create one thousand files */ + assert (i + 5 < PATH_MAX); + for (j = 0; j < 1000; j++) { + FILE *fp; + + /* Construct file name */ + dirname[i] = '/'; + dirname[i+1] = 'z'; + dirname[i+2] = '0' + ((j / 100) % 10); + dirname[i+3] = '0' + ((j / 10) % 10); + dirname[i+4] = '0' + (j % 10); + dirname[i+5] = '\0'; + + /* Create file */ + fp = fopen (dirname, "w"); + assert (fp != NULL); + fclose (fp); + + } + + /* Cut out the file name part */ + dirname[i] = '\0'; + + /* Scan directory */ + n = scandir (dirname, &files, no_directories, alphasort); + assert (n == 1000); + + /* Make sure that all 1000 files are read back */ + for (j = 0; j < n; j++) { + char match[100]; + + /* Construct file name */ + match[0] = 'z'; + match[1] = '0' + ((j / 100) % 10); + match[2] = '0' + ((j / 10) % 10); + match[3] = '0' + (j % 10); + match[4] = '\0'; + + /* Make sure that file name matches that on the disk */ + assert (strcmp (files[j]->d_name, match) == 0); + + } + + /* Release file names */ + for (j = 0; j < n; j++) { + free (files[j]); + } + free (files); + } + + printf ("OK\n"); + return EXIT_SUCCESS; +} + +/* Only pass README.txt file */ +static int +only_readme (const struct dirent *entry) +{ + int pass; + + if (strcmp (entry->d_name, "README.txt") == 0) { + pass = 1; + } else { + pass = 0; + } + + return pass; +} + +/* Filter out directories */ +static int +no_directories (const struct dirent *entry) +{ + int pass; + + if (entry->d_type != DT_DIR) { + pass = 1; + } else { + pass = 0; + } + + return pass; +} + +/* Sort in reverse direction */ +static int +reverse_alpha( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*b)->d_name, (*a)->d_name); +} diff --git a/deps/dirent/tests/t-unicode.c b/deps/dirent/tests/t-unicode.c new file mode 100644 index 0000000000..1a65d0bb27 --- /dev/null +++ b/deps/dirent/tests/t-unicode.c @@ -0,0 +1,401 @@ +/* + * Test program to try unicode file names. + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ + +/* Silence warning about fopen being insecure */ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include + +#undef NDEBUG +#include + + +int +main( + int argc, char *argv[]) +{ +#ifdef WIN32 + wchar_t wpath[MAX_PATH+1]; + char path[MAX_PATH+1]; + DWORD i, j, k, x; + BOOL ok; + HANDLE fh; + _WDIR *wdir; + struct _wdirent *wentry; + DIR *dir; + struct dirent *entry; + char buffer[100]; + FILE *fp; + int counter = 0; + + (void) argc; + (void) argv; + + + /* Initialize random number generator */ + srand (((int) time (NULL)) * 257 + ((int) GetCurrentProcessId ())); + + /* Set current locale */ + if (argc > 1) { + printf ("Locale %s\n", argv[1]); + setlocale (LC_ALL, argv[1]); + } else { + setlocale (LC_ALL, ""); + } + + + /****** CREATE FILE WITH UNICODE FILE NAME ******/ + + /* Get path to temporary directory (wide-character and ascii) */ + i = GetTempPathW (MAX_PATH, wpath); + assert (i > 0); + j = GetTempPathA (MAX_PATH, path); + assert (j > 0); + + /* Append random directory name */ + for (k = 0; k < 10; k++) { + char c; + + /* Generate random character */ + c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26]; + + /* Append character to paths */ + assert (i < MAX_PATH && j < MAX_PATH); + wpath[i++] = c; + path[j++] = c; + } + + /* Terminate paths */ + assert (i < MAX_PATH && j < MAX_PATH); + wpath[i] = '\0'; + path[j] = '\0'; + + /* Remember the end of directory name */ + k = i; + + /* Create directory using unicode */ + ok = CreateDirectoryW (wpath, NULL); + if (!ok) { + DWORD e = GetLastError (); + wprintf (L"Cannot create directory %ls (code %u)\n", wpath, e); + abort (); + } + + /* Overwrite zero terminator with path separator */ + assert (i < MAX_PATH && j < MAX_PATH); + wpath[i++] = '\\'; + + /* Append a few unicode characters */ + assert (i < MAX_PATH); + wpath[i++] = 0x6d4b; + assert (i < MAX_PATH); + wpath[i++] = 0x8bd5; + + /* Terminate string */ + assert (i < MAX_PATH); + wpath[i] = '\0'; + + /* Create file with unicode */ + fh = CreateFileW( + wpath, + /* Access */ GENERIC_READ | GENERIC_WRITE, + /* Share mode */ 0, + /* Security attributes */ NULL, + /* Creation disposition */ CREATE_NEW, + /* Attributes */ FILE_ATTRIBUTE_NORMAL, + /* Template files */ NULL + ); + assert (fh != INVALID_HANDLE_VALUE); + + /* Write some data to file */ + ok = WriteFile( + /* File handle */ fh, + /* Pointer to data */ "hep\n", + /* Number of bytes to write */ 4, + /* Number of bytes written */ NULL, + /* Overlapped */ NULL + ); + assert (ok); + + /* Close file */ + ok = CloseHandle (fh); + assert (ok); + + + /****** MAKE SURE THAT UNICODE FILE NAME CAN BE READ BY _WREADDIR ******/ + + /* Zero terminate wide-character path and open directory stream */ + wpath[k] = '\0'; + wdir = _wopendir (wpath); + if (wdir == NULL) { + wprintf (L"Cannot open directory %ls\n", wpath); + abort (); + } + + /* Read through entries */ + counter = 0; + while ((wentry = _wreaddir (wdir)) != NULL) { + + /* Skip pseudo directories */ + if (wcscmp (wentry->d_name, L".") == 0) { + continue; + } + if (wcscmp (wentry->d_name, L"..") == 0) { + continue; + } + + /* Found a file */ + counter++; + assert (wentry->d_type == DT_REG); + + /* Append file name to path */ + i = k; + assert (i < MAX_PATH); + wpath[i++] = '\\'; + x = 0; + while (wentry->d_name[x] != '\0') { + assert (i < MAX_PATH); + wpath[i++] = wentry->d_name[x++]; + } + assert (i < MAX_PATH); + wpath[i] = '\0'; + + /* Open file for read */ + fh = CreateFileW( + wpath, + /* Access */ GENERIC_READ, + /* Share mode */ 0, + /* Security attributes */ NULL, + /* Creation disposition */ OPEN_EXISTING, + /* Attributes */ FILE_ATTRIBUTE_NORMAL, + /* Template files */ NULL + ); + assert (fh != INVALID_HANDLE_VALUE); + + /* Read data from file */ + ok = ReadFile( + /* File handle */ fh, + /* Output buffer */ buffer, + /* Maximum number of bytes to read */ sizeof (buffer) - 1, + /* Number of bytes actually read */ &x, + /* Overlapped */ NULL + ); + assert (ok); + + /* Make sure that we got the file contents right */ + assert (x == 4); + assert (buffer[0] == 'h'); + assert (buffer[1] == 'e'); + assert (buffer[2] == 'p'); + assert (buffer[3] == '\n'); + + /* Close file */ + ok = CloseHandle (fh); + assert (ok); + + } + assert (counter == 1); + + /* Close directory */ + _wclosedir (wdir); + + + /****** MAKE SURE THAT UNICODE FILE NAME CAN BE READ BY READDIR ******/ + + /* Zero terminate ascii path and open directory stream */ + k = j; + path[k] = '\0'; + dir = opendir (path); + if (dir == NULL) { + fprintf (stderr, "Cannot open directory %s\n", path); + abort (); + } + + /* Read through entries */ + counter = 0; + while ((entry = readdir (dir)) != NULL) { + + /* Skip pseudo directories */ + if (strcmp (entry->d_name, ".") == 0) { + continue; + } + if (strcmp (entry->d_name, "..") == 0) { + continue; + } + + /* Found a file */ + counter++; + assert (entry->d_type == DT_REG); + + /* Append file name to path */ + j = k; + assert (j < MAX_PATH); + path[j++] = '\\'; + x = 0; + while (entry->d_name[x] != '\0') { + assert (j < MAX_PATH); + path[j++] = entry->d_name[x++]; + } + assert (j < MAX_PATH); + path[j] = '\0'; + + /* Open file for read */ + fp = fopen (path, "r"); + if (!fp) { + fprintf (stderr, "Cannot open file %s\n", path); + abort (); + } + + /* Read data from file */ + if (fgets (buffer, sizeof (buffer), fp) == NULL) { + fprintf (stderr, "Cannot read file %s\n", path); + abort (); + } + + /* Make sure that we got the file contents right */ + assert (buffer[0] == 'h'); + assert (buffer[1] == 'e'); + assert (buffer[2] == 'p'); + assert (buffer[3] == '\n'); + assert (buffer[4] == '\0'); + + /* Close file */ + fclose (fp); + + } + assert (counter == 1); + + /* Close directory */ + closedir (dir); + + + /****** CREATE FILE WITH UTF-8 ******/ + + /* Append UTF-8 file name (åäö.txt) to path */ + j = k; + path[j++] = '\\'; + path[j++] = 0xc3; + path[j++] = 0xa5; + path[j++] = 0xc3; + path[j++] = 0xa4; + path[j++] = 0xc3; + path[j++] = 0xb6; + path[j++] = 0x2e; + path[j++] = 0x74; + path[j++] = 0x78; + path[j++] = 0x74; + assert (j < MAX_PATH); + path[j] = '\0'; + + /* + * Create file. + * + * Be ware that the code below creates a different file depending on the + * current locale! For example, if the current locale is + * english_us.65001, then the file name will be "åäö.txt" (7 characters). + * However, if the current locale is english_us.1252, then the file name + * will be "ÃċÃĊö.txt" (10 characters). + */ + printf ("Creating %s\n", path); + fp = fopen (path, "w"); + if (!fp) { + fprintf (stderr, "Cannot open file %s\n", path); + abort (); + } + fputs ("hep\n", fp); + fclose (fp); + + /* Open directory again */ + path[k] = '\0'; + dir = opendir (path); + if (dir == NULL) { + fprintf (stderr, "Cannot open directory %s\n", path); + abort (); + } + + /* Read through entries */ + counter = 0; + while ((entry = readdir (dir)) != NULL) { + + /* Skip pseudo directories */ + if (strcmp (entry->d_name, ".") == 0) { + continue; + } + if (strcmp (entry->d_name, "..") == 0) { + continue; + } + + /* Found a file */ + counter++; + assert (entry->d_type == DT_REG); + + /* Append file name to path */ + j = k; + assert (j < MAX_PATH); + path[j++] = '\\'; + x = 0; + while (entry->d_name[x] != '\0') { + assert (j < MAX_PATH); + path[j++] = entry->d_name[x++]; + } + assert (j < MAX_PATH); + path[j] = '\0'; + + /* Print file name for debugging */ + printf ("Opening \"%s\" hex ", path + k + 1); + x = 0; + while (entry->d_name[x] != '\0') { + printf ("0x%02x ", (unsigned) (entry->d_name[x++] & 0xff)); + } + printf ("\n"); + + /* Open file for read */ + fp = fopen (path, "r"); + if (!fp) { + fprintf (stderr, "Cannot open file %s\n", path); + abort (); + } + + /* Read data from file */ + if (fgets (buffer, sizeof (buffer), fp) == NULL) { + fprintf (stderr, "Cannot read file %s\n", path); + abort (); + } + + /* Make sure that we got the file contents right */ + assert (buffer[0] == 'h'); + assert (buffer[1] == 'e'); + assert (buffer[2] == 'p'); + assert (buffer[3] == '\n'); + assert (buffer[4] == '\0'); + + /* Close file */ + fclose (fp); + + } + assert (counter == 2); + + /* Close directory */ + closedir (dir); + +#else + + /* Linux */ + (void) argc; + (void) argv; + +#endif + return EXIT_SUCCESS; +} -- GitLab