/*
* 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);
}