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