diff options
Diffstat (limited to 'dirent/tests')
26 files changed, 2081 insertions, 0 deletions
diff --git a/dirent/tests/1/dir/readme.txt b/dirent/tests/1/dir/readme.txt new file mode 100644 index 0000000..e59af2f --- /dev/null +++ b/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/dirent/tests/1/file b/dirent/tests/1/file new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/1/file diff --git a/dirent/tests/2/Testfile-1.2.3.dat b/dirent/tests/2/Testfile-1.2.3.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/2/Testfile-1.2.3.dat diff --git a/dirent/tests/2/file.txt b/dirent/tests/2/file.txt new file mode 100644 index 0000000..d32e004 --- /dev/null +++ b/dirent/tests/2/file.txt @@ -0,0 +1 @@ +This dummy file is needed by the test program t-dirent. diff --git a/dirent/tests/3/3zero.dat b/dirent/tests/3/3zero.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/3zero.dat diff --git a/dirent/tests/3/666.dat b/dirent/tests/3/666.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/666.dat diff --git a/dirent/tests/3/Qwerty-my-aunt.dat b/dirent/tests/3/Qwerty-my-aunt.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/Qwerty-my-aunt.dat diff --git a/dirent/tests/3/README.txt b/dirent/tests/3/README.txt new file mode 100644 index 0000000..a07591c --- /dev/null +++ b/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/dirent/tests/3/aaa.dat b/dirent/tests/3/aaa.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/aaa.dat diff --git a/dirent/tests/3/dirent.dat b/dirent/tests/3/dirent.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/dirent.dat diff --git a/dirent/tests/3/empty.dat b/dirent/tests/3/empty.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/empty.dat diff --git a/dirent/tests/3/sane-1.12.0.dat b/dirent/tests/3/sane-1.12.0.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/sane-1.12.0.dat diff --git a/dirent/tests/3/sane-1.2.30.dat b/dirent/tests/3/sane-1.2.30.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/sane-1.2.30.dat diff --git a/dirent/tests/3/sane-1.2.4.dat b/dirent/tests/3/sane-1.2.4.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/sane-1.2.4.dat diff --git a/dirent/tests/3/zebra.dat b/dirent/tests/3/zebra.dat new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/3/zebra.dat diff --git a/dirent/tests/4/asidofilusphosphorus b/dirent/tests/4/asidofilusphosphorus new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/4/asidofilusphosphorus diff --git a/dirent/tests/4/bubblebob b/dirent/tests/4/bubblebob new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/4/bubblebob diff --git a/dirent/tests/4/celsiusfortissimo b/dirent/tests/4/celsiusfortissimo new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dirent/tests/4/celsiusfortissimo diff --git a/dirent/tests/t-compile.c b/dirent/tests/t-compile.c new file mode 100644 index 0000000..90d8e50 --- /dev/null +++ b/dirent/tests/t-compile.c @@ -0,0 +1,46 @@ +/* + * 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 <dirent.h> +#ifdef WIN32 +# include <winsock2.h> +# include <ws2tcpip.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +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/dirent/tests/t-cplusplus.cpp b/dirent/tests/t-cplusplus.cpp new file mode 100644 index 0000000..8a8bad4 --- /dev/null +++ b/dirent/tests/t-cplusplus.cpp @@ -0,0 +1,155 @@ +/* + * 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 <iostream> +#include <string.h> +#include <dirent.h> +#include <assert.h> +using namespace std; + +/* Filter and sort functions */ +static int only_readme(const struct dirent *entry); +static void test_retrieval(void); +static void test_scan(void); + +int +main(int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + test_retrieval(); + test_scan(); + + cout << "OK" << endl; + return EXIT_SUCCESS; +} + +/* Test basic directory retrieval */ +static void +test_retrieval(void) +{ + /* Open directory */ + DIR *dir = opendir("tests/1"); + if (dir == NULL) { + cerr << "Directory tests/1 not found" << endl; + abort(); + } + + /* Read directory entries */ + struct dirent *ent; + int found = 0; + 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); +} + +/* Text basic scan with simple filter function */ +static void +test_scan(void) +{ + struct dirent **files; + + /* Read directory entries */ + int 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 (int i = 0; i < n; i++) { + free(files[i]); + } + free(files); +} + +/* 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/dirent/tests/t-dirent.c b/dirent/tests/t-dirent.c new file mode 100644 index 0000000..95f151a --- /dev/null +++ b/dirent/tests/t-dirent.c @@ -0,0 +1,608 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _MSC_VER +# include <direct.h> +# define chdir(x) _chdir(x) +#else +# include <unistd.h> +#endif +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> + +#undef NDEBUG +#include <assert.h> + +static void test_macros(void); +static void test_retrieval(void); +static void test_nonexistent(void); +static void test_isfile(void); +static void test_zero(void); +static void test_rewind(void); +static void test_chdir(void); +static void test_filename(void); +static void test_readdir(void); +static void test_wreaddir(void); + +int +main(int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + /* Execute tests */ + test_macros(); + test_retrieval(); + test_nonexistent(); + test_isfile(); + test_zero(); + test_rewind(); + test_chdir(); + test_filename(); + test_readdir(); + test_wreaddir(); + + printf("OK\n"); + return EXIT_SUCCESS; +} + +/* Test file type macros */ +static void +test_macros(void) +{ + 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); +} + +/* Test basic directory retrieval */ +static void +test_retrieval(void) +{ + /* Open directory */ + DIR *dir = opendir("tests/1"); + if (dir == NULL) { + fprintf(stderr, "Directory tests/1 not found\n"); + abort(); + } + + /* Read entries */ + struct dirent *ent; + int found = 0; + 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 */ +static void +test_nonexistent(void) +{ + DIR *dir = opendir("tests/invalid"); + assert(dir == NULL); + assert(errno == ENOENT); +} + +/* Function opendir() fails if pathname is really a file */ +static void +test_isfile(void) +{ + DIR *dir = opendir("tests/1/file"); + assert(dir == NULL); + assert(errno == ENOTDIR); +} + +/* Function opendir() fails if pathname is a zero-length string */ +static void +test_zero(void) +{ + DIR *dir = opendir(""); + assert(dir == NULL); + assert(errno == ENOENT); +} + +/* Test rewind of directory stream */ +static void +test_rewind(void) +{ + /* Open directory */ + DIR *dir = opendir("tests/1"); + assert(dir != NULL); + + /* Read entries */ + int found = 0; + struct dirent *ent; + 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); +} + +/* Test rewind with intervening change of working directory */ +static void +test_chdir(void) +{ + /* Open directory */ + DIR *dir = opendir("tests/1"); + assert(dir != NULL); + + /* Read entries */ + struct dirent *ent; + int found = 0; + 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 */ + int 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); +} + +/* Test long file name */ +static void +test_filename(void) +{ + /* Open directory */ + DIR *dir = opendir("tests/2"); + if (dir == NULL) { + fprintf(stderr, "Directory tests/2 not found\n"); + abort(); + } + + /* Read entries */ + struct dirent *ent; + int found = 0; + 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); +} + +/* Test basic directory retrieval with readdir_r */ +static void +test_readdir(void) +{ + /* Open directory */ + DIR *dir = opendir("tests/1"); + if (dir == NULL) { + fprintf(stderr, "Directory tests/1 not found\n"); + abort(); + } + + /* Read entries to table */ + struct dirent ent[10]; + struct dirent *entry; + size_t i = 0; + size_t n = 0; + 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 */ + int found = 0; + 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 */ +static void +test_wreaddir(void) +{ +#ifdef WIN32 + /* Open directory */ + _WDIR *dir = _wopendir(L"tests/1"); + if (dir == NULL) { + fprintf(stderr, "Directory tests/1 not found\n"); + abort(); + } + + /* Read entries to table */ + struct _wdirent ent[10]; + struct _wdirent *entry; + size_t i = 0; + size_t n = 0; + 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 */ + int found = 0; + 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 +} diff --git a/dirent/tests/t-scandir.c b/dirent/tests/t-scandir.c new file mode 100644 index 0000000..c463f9b --- /dev/null +++ b/dirent/tests/t-scandir.c @@ -0,0 +1,276 @@ +/* + * 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 (MS Visual Studio) */ +#define _CRT_SECURE_NO_WARNINGS + +/* Include prototype for versionsort (Linux) */ +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <time.h> +#include <limits.h> + +#undef NDEBUG +#include <assert.h> + +/* 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 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.30.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 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.30.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); + } + + /* Sort files using versionsort() */ + { + files = NULL; + n = scandir("tests/3", &files, no_directories, versionsort); + assert(n == 11); + + /* + * Make sure that we got all the file names in the proper order: + * 1.2.4 < 1.2.30 < 1.12.0 + */ + assert(strcmp(files[0]->d_name, "3zero.dat") == 0); + assert(strcmp(files[1]->d_name, "666.dat") == 0); + assert(strcmp(files[2]->d_name, "Qwerty-my-aunt.dat") == 0); + assert(strcmp(files[3]->d_name, "README.txt") == 0); + assert(strcmp(files[4]->d_name, "aaa.dat") == 0); + assert(strcmp(files[5]->d_name, "dirent.dat") == 0); + assert(strcmp(files[6]->d_name, "empty.dat") == 0); + assert(strcmp(files[7]->d_name, "sane-1.2.4.dat") == 0); + assert(strcmp(files[8]->d_name, "sane-1.2.30.dat") == 0); + assert(strcmp(files[9]->d_name, "sane-1.12.0.dat") == 0); + assert(strcmp(files[10]->d_name, "zebra.dat") == 0); + + /* Release file names */ + for (i = 0; i < n; i++) { + free(files[i]); + } + free(files); + } + + /* Scan large directory */ + { + char dirname[PATH_MAX+1]; + int i; + 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 (int 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 (int 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 (int 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 (int 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) +{ + return strcmp(entry->d_name, "README.txt") == 0; +} + +/* Filter out directories */ +static int +no_directories(const struct dirent *entry) +{ + return entry->d_type != DT_DIR; +} + +/* 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/dirent/tests/t-strverscmp.c b/dirent/tests/t-strverscmp.c new file mode 100644 index 0000000..fcb044f --- /dev/null +++ b/dirent/tests/t-strverscmp.c @@ -0,0 +1,206 @@ +/* + * Test program to make sure that strverscmp 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 prototype for strverscmp */ +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <dirent.h> +#include <ctype.h> + +int +main( + int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + /* Strings without digits are compared as in strcmp() */ + assert(strverscmp("", "") == 0); + assert(strverscmp("abc", "abc") == 0); + assert(strverscmp("a", "b") < 0); + assert(strverscmp("b", "a") > 0); + + /* Shorter string is smaller, other things being equal */ + assert(strverscmp("a", "aa") < 0); + assert(strverscmp("aa", "a") > 0); + assert(strverscmp("abcdef", "abcdefg") < 0); + assert(strverscmp("abcdefg", "abcdef") > 0); + + /* Integers with equal length are compared as in strcmp() */ + assert(strverscmp("0", "0") == 0); + assert(strverscmp("000", "000") == 0); + assert(strverscmp("1", "2") < 0); + assert(strverscmp("2", "1") > 0); + assert(strverscmp("001", "100") < 0); + assert(strverscmp("100", "001") > 0); + assert(strverscmp("2020-07-01", "2020-07-02") < 0); + assert(strverscmp("2020-07-02", "2020-07-01") > 0); + assert(strverscmp("jan999", "jan999") == 0); + + /* Integers of different length are compared as numbers */ + assert(strverscmp("jan9", "jan10") < 0); + assert(strverscmp("jan10", "jan9") > 0); + assert(strverscmp("999", "1000") < 0); + assert(strverscmp("1000", "999") > 0); + assert(strverscmp("t12-1000", "t12-9999") < 0); + assert(strverscmp("t12-9999", "t12-1000") > 0); + assert(strverscmp("1000", "10001") < 0); + assert(strverscmp("10001", "1000") > 0); + assert(strverscmp("1000!", "10001") < 0); + assert(strverscmp("10001", "1000!") > 0); + assert(strverscmp("1000Z", "10001") < 0); + assert(strverscmp("10001", "1000Z") > 0); + + /* If numbers starts with zero, then longer number is smaller */ + assert(strverscmp("00", "0") < 0); + assert(strverscmp("0", "00") > 0); + assert(strverscmp("a000", "a00") < 0); + assert(strverscmp("a00", "a000") > 0); + assert(strverscmp("0000", "000") < 0); + assert(strverscmp("000", "0000") > 0); + assert(strverscmp("0000", "000!") < 0); + assert(strverscmp("000!", "0000") > 0); + assert(strverscmp("0000", "000Z") < 0); + assert(strverscmp("000Z", "0000") > 0); + assert(strverscmp("0000", "000Z") < 0); + assert(strverscmp("000Z", "0000") > 0); + assert(strverscmp("1.01", "1.0") < 0); + assert(strverscmp("1.0", "1.01") > 0); + assert(strverscmp("1.01", "1.0!") < 0); + assert(strverscmp("1.0!", "1.01") > 0); + assert(strverscmp("1.01", "1.0~") < 0); + assert(strverscmp("1.0~", "1.01") > 0); + + /* Number having more leading zeros is considered smaller */ + assert(strverscmp("item-0001", "item-001") < 0); + assert(strverscmp("item-001", "item-0001") > 0); + assert(strverscmp("item-001", "item-01") < 0); + assert(strverscmp("item-01", "item-001") > 0); + assert(strverscmp(".0001000", ".001") < 0); + assert(strverscmp(".001", ".0001000") > 0); + assert(strverscmp(".0001000", ".01") < 0); + assert(strverscmp(".01", ".0001000") > 0); + assert(strverscmp(".0001000", ".1") < 0); + assert(strverscmp(".1", ".0001000") > 0); + assert(strverscmp("1.0002", "1.0010000") < 0); + assert(strverscmp("1.0010000", "1.0002") > 0); + + /* Number starting with zero is smaller than any number */ + assert(strverscmp("item-009", "item-1") < 0); + assert(strverscmp("item-1", "item-009") > 0); + assert(strverscmp("item-099", "item-2") < 0); + assert(strverscmp("item-2", "item-099") > 0); + + /* Number vs alphabetical comparison */ + assert(strverscmp("1.001", "1.00!") < 0); + assert(strverscmp("1.00!", "1.001") > 0); + assert(strverscmp("1.001", "1.00x") < 0); + assert(strverscmp("1.00x", "1.001") > 0); + assert(strverscmp("1", "x") < 0); + assert(strverscmp("x", "1") > 0); + assert(strverscmp("1", "!") > 0); + assert(strverscmp("!", "1") < 0); + + /* Handling the end of string */ + assert(strverscmp("01", "011") < 0); + assert(strverscmp("011", "01") > 0); + assert(strverscmp("0100", "01000") < 0); + assert(strverscmp("01000", "0100") > 0); + assert(strverscmp("1", "1!") < 0); + assert(strverscmp("1!", "1") > 0); + assert(strverscmp("1", "1z") < 0); + assert(strverscmp("1z", "1") > 0); + + /* Ordering 000 < 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10 */ + assert(strverscmp("000", "00") < 0); + assert(strverscmp("000", "01") < 0); + assert(strverscmp("000", "010") < 0); + assert(strverscmp("000", "09") < 0); + assert(strverscmp("000", "0") < 0); + assert(strverscmp("000", "1") < 0); + assert(strverscmp("000", "9") < 0); + assert(strverscmp("000", "10") < 0); + + assert(strverscmp("00", "01") < 0); + assert(strverscmp("00", "010") < 0); + assert(strverscmp("00", "09") < 0); + assert(strverscmp("00", "0") < 0); + assert(strverscmp("00", "1") < 0); + assert(strverscmp("00", "9") < 0); + assert(strverscmp("00", "10") < 0); + + assert(strverscmp("01", "010") < 0); + assert(strverscmp("01", "09") < 0); + assert(strverscmp("01", "0") < 0); + assert(strverscmp("01", "1") < 0); + assert(strverscmp("01", "9") < 0); + assert(strverscmp("01", "10") < 0); + + assert(strverscmp("010", "09") < 0); + assert(strverscmp("010", "0") < 0); + assert(strverscmp("010", "1") < 0); + assert(strverscmp("010", "9") < 0); + assert(strverscmp("010", "10") < 0); + + assert(strverscmp("09", "0") < 0); + assert(strverscmp("09", "1") < 0); + assert(strverscmp("09", "9") < 0); + assert(strverscmp("09", "10") < 0); + + assert(strverscmp("0", "1") < 0); + assert(strverscmp("0", "9") < 0); + assert(strverscmp("0", "10") < 0); + + assert(strverscmp("1", "9") < 0); + assert(strverscmp("1", "10") < 0); + + assert(strverscmp("9", "10") < 0); + + /* Compare speed */ + { +#define LENGTH 100 +#define REPEAT 1000000 + char a[LENGTH+1]; + char b[LENGTH+1]; + size_t i; + size_t j; + char letters[] = "01234567890123456789abdefghjkpqrtwxyz-/."; + size_t n = strlen(letters); + + /* Repeat test */ + for(i = 0; i < REPEAT; i++) { + int diff1; + int diff2; + + /* Generate two random strings of LENGTH characters */ + for(j = 0; j < LENGTH; j++) { + a[j] = letters[rand() % n]; + b[j] = letters[rand() % n]; + } + a[j] = '\0'; + b[j] = '\0'; + + /* Compare strings in both directions */ + diff1 = strverscmp(a, b); + diff2 = strverscmp(b, a); + + /* Must give identical result in both directions */ + assert((diff1 < 0 && diff2 > 0) + || (diff1 == 0 && diff2 == 0) + || (diff1 > 0 && diff2 < 0)); + } + } + + printf("OK\n"); + return EXIT_SUCCESS; +} diff --git a/dirent/tests/t-telldir.c b/dirent/tests/t-telldir.c new file mode 100644 index 0000000..e2a1763 --- /dev/null +++ b/dirent/tests/t-telldir.c @@ -0,0 +1,165 @@ +/* + * 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 + */ + +/* Silence warning about strcmp being insecure (MS Visual Studio) */ +#define _CRT_SECURE_NO_WARNINGS + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> + +#undef NDEBUG +#include <assert.h> + +static void test_telldir(void); + +int +main(int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + test_telldir(); + + return EXIT_SUCCESS; +} + +static void +test_telldir(void) +{ + DIR *dir = opendir("tests/4"); + if (dir == NULL) { + fprintf(stderr, "Directory tests/4 not found\n"); + abort(); + } + + /* Get position of first file */ + long pos1 = telldir(dir); + printf("pos1: %lx\n", (long) pos1); + assert(pos1 >= 0); + + /* Read first file */ + struct dirent *ent = readdir(dir); + assert(ent != NULL); + struct dirent ent1 = *ent; + printf("ent1: %s %lx\n", ent->d_name, (long) ent->d_off); + + /* Seek back to the first position */ + seekdir(dir, pos1); + + /* Re-read the first entry */ + ent = readdir(dir); + assert(ent != NULL); + assert(strcmp(ent->d_name, ent1.d_name) == 0); + + /* Get position to second file */ + long pos2 = telldir(dir); + printf("pos2: %lx\n", (long) pos2); + assert(pos2 >= 0); + + /* Read second file */ + ent = readdir(dir); + assert(ent != NULL); + struct dirent ent2 = *ent; + printf("ent2: %s %lx\n", ent->d_name, (long) ent->d_off); + + /* Get position to third file */ + long pos3 = telldir(dir); + printf("pos3: %lx\n", (long) pos3); + assert(pos3 >= 0); + + /* Read third file */ + ent = readdir(dir); + assert(ent != NULL); + struct dirent ent3 = *ent; + printf("ent3: %s %lx\n", ent->d_name, (long) ent->d_off); + + /* Get position to fourth file */ + long pos4 = telldir(dir); + printf("pos4: %lx\n", (long) pos4); + assert(pos4 >= 0); + + /* Read fourth file */ + ent = readdir(dir); + assert(ent != NULL); + struct dirent ent4 = *ent; + printf("ent4: %s %lx\n", ent->d_name, (long) ent->d_off); + + /* Get position to fifth file */ + long pos5 = telldir(dir); + printf("pos5: %lx\n", (long) pos5); + assert(pos5 >= 0); + + /* Read fifth file */ + ent = readdir(dir); + assert(ent != NULL); + struct dirent ent5 = *ent; + printf("ent5: %s %lx\n", ent->d_name, (long) ent->d_off); + + /* Read position at the end of directory stream */ + long posx = telldir(dir); + assert(posx >= 0); + printf("posx: %lx\n", (long) posx); + + /* End of directory stream has been reached */ + ent = readdir(dir); + assert(ent == NULL); + + /* Seek back to position just before the end of stream */ + seekdir(dir, posx); + + /* Function telldir returns the same position when asked again */ + assert(telldir(dir) == posx); + assert(telldir(dir) == posx); + assert(telldir(dir) == posx); + + /* Read end of stream again */ + ent = readdir(dir); + assert(ent == NULL); + + /* Seek back to fifth file and read it again */ + seekdir(dir, pos5); + assert(telldir(dir) == pos5); + ent = readdir(dir); + assert(ent != NULL); + assert(strcmp(ent->d_name, ent5.d_name) == 0); + + /* Seek back to second file and read it again */ + seekdir(dir, pos2); + assert(telldir(dir) == pos2); + ent = readdir(dir); + assert(ent != NULL); + assert(strcmp(ent->d_name, ent2.d_name) == 0); + + /* Continue reading from the third file without a seek in between */ + assert(telldir(dir) == pos3); + ent = readdir(dir); + assert(ent != NULL); + assert(strcmp(ent->d_name, ent3.d_name) == 0); + + /* Read fourth position again */ + assert(telldir(dir) == pos4); + ent = readdir(dir); + assert(ent != NULL); + assert(strcmp(ent->d_name, ent4.d_name) == 0); + + /* Read fifth position again */ + assert(telldir(dir) == pos5); + ent = readdir(dir); + assert(ent != NULL); + assert(strcmp(ent->d_name, ent5.d_name) == 0); + + /* Read end of stream again */ + assert(telldir(dir) == posx); + ent = readdir(dir); + assert(ent == NULL); +} + diff --git a/dirent/tests/t-unicode.c b/dirent/tests/t-unicode.c new file mode 100644 index 0000000..8239120 --- /dev/null +++ b/dirent/tests/t-unicode.c @@ -0,0 +1,381 @@ +/* + * 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 <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <time.h> +#include <locale.h> + +#undef NDEBUG +#include <assert.h> + +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; + + /* 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++) { + /* Generate random character */ + char 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 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, + /* Max 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; +} diff --git a/dirent/tests/t-utf8.c b/dirent/tests/t-utf8.c new file mode 100644 index 0000000..fdc31fa --- /dev/null +++ b/dirent/tests/t-utf8.c @@ -0,0 +1,238 @@ +/* + * Test program to try UTF-8 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 <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <time.h> +#include <locale.h> + +#undef NDEBUG +#include <assert.h> + +int +main(int argc, char *argv[]) +{ +#ifdef WIN32 + /* + * Select UTF-8 locale. This will change the way how C runtime + * functions such as fopen() and mkdir() handle character strings. + * For more information, please see: + * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale?view=msvc-160#utf-8-support + */ + setlocale(LC_ALL, "LC_CTYPE=.utf8"); + + /* Initialize random number generator */ + srand(((int) time(NULL)) * 257 + ((int) GetCurrentProcessId())); + + /* Get path to temporary directory */ + wchar_t wpath[MAX_PATH+1]; + DWORD i = GetTempPathW(MAX_PATH, wpath); + assert(i > 0); + + /* Ensure that path name ends in directory separator */ + assert(wpath[i - 1] == '\\'); + + /* Append random prefix */ + DWORD k; + for (k = 0; k < 8; k++) { + /* Generate random character */ + char c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26]; + + /* Append character to path */ + assert(i < MAX_PATH); + wpath[i++] = c; + } + + /* Append a wide character to the path name */ + wpath[i++] = 0x00c4; + + /* Terminate the path name */ + assert(i < MAX_PATH); + wpath[i] = '\0'; + + /* Create directory with unicode name */ + BOOL 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); + wpath[i++] = '\\'; + + /* Append a few unicode characters */ + assert(i < MAX_PATH); + wpath[i++] = 0x00f6; + assert(i < MAX_PATH); + wpath[i++] = 0x00e4; + + /* Terminate string */ + assert(i < MAX_PATH); + wpath[i] = '\0'; + + /* Create file with unicode name */ + HANDLE 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); + + /* Convert file name to UTF-8 */ + char path[MAX_PATH+1]; + int n = WideCharToMultiByte( + /* Code page to use in conversion */ CP_UTF8, + /* Flags */ 0, + /* Pointer to unicode string */ wpath, + /* Length of unicode string in characters */ i, + /* Pointer to output buffer */ path, + /* Size of output buffer in bytes */ MAX_PATH, + /* Pointer to default character */ NULL, + /* Pointer to boolean variable */ NULL + ); + assert(n > 0); + + /* Zero-terminate path */ + path[(size_t) n] = '\0'; + + /* Make sure that fopen() can open the file with UTF-8 file name */ + FILE *fp = fopen(path, "r"); + if (!fp) { + fprintf(stderr, "Cannot open file %s\n", path); + abort(); + } + + /* Read data from file */ + char buffer[100]; + 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); + + /* Truncate path name to the last directory separator */ + i = 0; + k = 0; + while (path[k] != '\0') { + if (path[k] == '\\' || path[k] == '/') { + i = k; + } + k++; + } + path[i] = '\0'; + + /* Ensure that opendir() can open the directory with UTF-8 name */ + DIR *dir = opendir(path); + if (dir == NULL) { + fprintf(stderr, "Cannot open directory %s\n", path); + abort(); + } + + /* Read entries */ + int counter = 0; + struct dirent *entry; + 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 */ + k = i; + assert(i < MAX_PATH); + path[i++] = '\\'; + DWORD x = 0; + while (entry->d_name[x] != '\0') { + assert(i < MAX_PATH); + path[i++] = entry->d_name[x++]; + } + assert(i < MAX_PATH); + path[i] = '\0'; + + /* Reset buffer */ + for (x = 0; x < sizeof(buffer); x++) { + buffer[x] = '\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); +#else + /* Linux */ + (void) argc; + (void) argv; +#endif + return EXIT_SUCCESS; +} |