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