Error reading many files in a subfolder - c++

I have a problem with read file in the subfolder. I have about 6000 different files in the difference folders. And I will read each file. But if I read about 2000 files, then application is not problem. When I read 6000 files which mean whole subfolders. The application will display problem "can not open file.". But if I only access the folder that is not open, then application is not problem. I don't know what is happen? I think maybe I read many files and memory not enough. Can you edit help me?
//This is code to access subforder
static int
find_directory(
const char *dirname)
{
DIR *dir;
char buffer[PATH_MAX + 2];
char *p = buffer;
const char *src;
const char* folder_dir;
char *end = &buffer[PATH_MAX];
int ok;
/* Copy directory name to buffer */
src = dirname;
printf("src=%s\n",src);
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_REG:
/* Output file name with directory */
{
printf ("FILE=%s\n", buffer);
OFBool check= readfile(buffer)
}
break;
case DT_DIR:
/* Scan sub-directory recursively */
if (strcmp (ent->d_name, ".") != 0
&& strcmp (ent->d_name, "..") != 0) {
find_directory (buffer,opts);
}
break;
default:
/* Do not device entries */
/*NOP*/;
}
}
closedir (dir);
ok = 1;
} else {
/* Could not open directory */
printf ("Cannot open directory %s\n", dirname);
ok = 0;
}
return ok;
}
OFBool readfile(const char* filepath)
{
FILE *f=NULL;
OFBool ok = OFFalse;
if( ( f = fopen( filepath, "rb" ) ) == NULL ) // checks to see if file exists
{
ok = OFFalse;
cout<<"can not read file"<<filepath<<endl;
return ok;
}
else
{
ok = true;
cout<<" reading OK"<<endl;
fclose(f);
return ok;
}
}

How about something like this:
/*
* Recursively walk though a directory tree.
*
* Arguments:
* path - The root directory to start from
* hidden - If non-zero, include hidden files and directories
*/
void recurse_directory(const char *path, const int hidden)
{
DIR *dir = opendir(path);
if (dir == NULL)
{
perror("opendir");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type == DT_DIR)
{
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0 &&
(ent->d_name[0] != '.' || hidden))
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
recurse_directory(newpath, hidden);
free(newpath);
}
}
else if (ent->d_type == DT_REG)
{
if (ent->d_name[0] != '.' || hidden)
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
printf("File %s\n", newpath);
/* readfile(newpath); */
free(newpath);
}
}
}
}

Too add to the answer from Joachim Pileborg. If you are working with a large number of sub-directories you'd need to add closedir() call after while loop. This is because there is a limit on the number of directories you can have open at one time. The above example will work fine when working with a small folder, but when working with 2000+ sub-folders you'd need to close the directory before opening another.
/*
* Recursively walk though a directory tree.
*
* Arguments:
* path - The root directory to start from
* hidden - If non-zero, include hidden files and directories
*/
void recurse_directory(const char *path, const int hidden)
{
DIR *dir = opendir(path);
if (dir == NULL)
{
perror("opendir");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type == DT_DIR)
{
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0 &&
(ent->d_name[0] != '.' || hidden))
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
recurse_directory(newpath, hidden);
free(newpath);
}
}
else if (ent->d_type == DT_REG)
{
if (ent->d_name[0] != '.' || hidden)
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
printf("File %s\n", newpath);
/* readfile(newpath); */
free(newpath);
}
}
}
closedir(dir); /* Add this <--- */
}

Related

Enabling C code to run as C++ code

I have a C program that finds duplicate files within a directory. The program is executed on the command line and passed 2 arguments. One is the parent directory, and argument two is the file name. It is working code in c, but I have a GUI and other files for "microservices" written in c++.
How would one call this C code from a c++ file?
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
//Compile: gcc dreamduplicatefinder.c -o dreamduplicatefinder.exe
//Run: ./dreamduplicateFinder.exe parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//printf(dir); //error could becuase trying to open shortcut or corrupt folder.
printf("%s\n",path);
perror("Failed to open directory");
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("%s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
//Step 2...
//size matches, files can be same; confirm it by matching both file contents...
int fdOriginal = open(originalFile, O_RDONLY);
int fdCurr = open(currFile, O_RDONLY);
if (fdOriginal == -1 || fdCurr == -1)
return false; //error occurred, not sure if file is duplicate...
//we will read file in small chunks and compare...
int chunkSize = 1024, bytesRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
while (true)
{
//read file in chunk...
bytesRead = read(fdOriginal, bufferOriginal, chunkSize);
if (bytesRead <= 0)
break; //end of file...
bytesRead = read(fdCurr, bufferCurr, bytesRead);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
My errors include: (from compareFiles function)
2x 'open' identifier not found
2x 'read' identifier not found
The working code for those curious.
Thank you #MarcusMüller & #JesperJuhl
#include "stdafx.h" //there is nothing in this header
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Compile: gcc <name of this file>.cpp -o <nameOfThisFile>.exe
//Run: <nameOfThisFile> parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//possibly trying to open shortcut or corrupt folder typically.
printf("Failed to open directory %s \n",path);
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("This is a duplicate! %s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
FILE* fdOriginal;
if (fdOriginal = fopen(originalFile, "r")) {
if (fdOriginal == NULL) { fputs("File error", stderr); return false; }
}
else return false; //error occurred, not sure if duplicate
FILE* fdCurr;
if (fdCurr = fopen(currFile, "r")) {
if (fdCurr == NULL) { fputs("File error", stderr); return false; }
}
else return false;
int chunkSize = 1024, objsRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
if (bufferOriginal == NULL) { fputs("Memory error for buff orig", stderr); exit(2); }
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
if (bufferCurr == NULL) { fputs("Memory error for buff curr", stderr); exit(2); }
while (true)
{
//read file in chunk...
//std::size_t fread( void* buffer, std::size_t size, std::size_t count, std::FILE* stream );
objsRead = fread(bufferOriginal, sizeof(char), chunkSize , fdOriginal);
if (objsRead <= 0)
break; //end of file...
objsRead = fread(bufferCurr, sizeof(char), objsRead, fdCurr);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
You usually just wouldn't do that. You'd wrap it in a C function, and compile it to an object file.
Then you'd include your C header with extern "C" {…}, and just call that function from C++.
When building your executable, you'd link in the object file containing your C function. Done!
Note: C isn't C++, and albeit your code not being illegal in C++ (as far as I can instantly tell), it does very "ugly" things (like #defineing true and false – ugh, that would already be a bad idea in C, to be honest). So, deal with it like you would deal with code in Fortran, or Java, or any other language that has a calling convention that you can use from C++ (which, usually, is the C calling convention): Just use it as an extern object.
Using ::open and ::read should cause the functions to be found.
You may also want to replace the C headers (like "string.h") with their C++ equivalent versions (like "cstring").
Your defines for true and false should also go. In C++ those are proper bools, not integers. This means the return type of CompareFiles should be changed to bool.
And you should wrap duplicateCount in an anonymous namespace - or return it from the function that updates it (either by returning a small struct with two ints, or by using a std::pair or std::tuple) - global variables are evil.

C++ read new files added to a directory

I have a C++ program that when starts, gets the list of all files in a directory and does some processing with them (packetizes them for transmission over network). The current version is static and the program only sees the files at the time the program starts. Now I want to change it to a dynamic version where it can continuously see the new files added in the directory (let's assume they have a meaningful incremented name; for instance f1, f2, f3, ... so next one coming would be f4. This way I can use some index).
Right now this is the piece of code (static reading of all files):
DIR *dp;
struct dirent *dirp;
char * dir = new char[inData->dir.length() + 1];
strcpy(dir, inData->dir.c_str());
dp = opendir(dir);
int file_index = 0;
int total_files = DataUtil::count_files(dp);
if (readdir(dp) == NULL)
rewinddir(dp);
//for each file in the directory
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..") { // except . and ..
//Do some processing
my_function(f_name, file_index);
file_index++;
}
}
closedir(dp);
I modified the code using the rewinddir(dp); function and file_index to notice new files. But it doesn't work. Where is the problem?
DIR *dp;
struct dirent *dirp;
char * dir = new char[inData->dir.length() + 1];
strcpy(dir, inData->dir.c_str());
dp = opendir(dir);
int file_index = 0;
int total_files = MyDataUtil::count_files(dp);
if (readdir(dp) == NULL)
rewinddir(dp);
//for each file in the directory
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..") {
//skip files already read
for (int var = 0; var < file_index; var++) {
readdir(dp);
}
//Do some processing
my_function(f_name, file_index);
file_index++;
//reset dir stream, and update total_files
rewinddir(dp);
total_files = MyDataUtil::count_files(dp);
}
}
closedir(dp);
The total_files function:
int MyDataUtil::count_files(DIR *dp){
struct dirent *dirp;
int num = 0;
while ((dirp = readdir( dp ))) {
string f_name = dirp->d_name;
if (f_name != "." && f_name != "..")
num++;
}
return num;
}
Here is the rewinddir documentation. It says ' might or might not be returned'!
Description:
The rewinddir function is used to reinitialize the directory stream dirstream, so that if you call readdir
it returns information about the first entry in the directory again. This function also notices if files
have been added or removed to the directory since it was opened with opendir. (Entries for these files
might or might not be returned by readdir if they were added or removed since you last called opendir
or rewinddir.)
Use inotify on Linux to get asynchronous notifications when files are added / removed / changed in a directory of your choice
http://man7.org/linux/man-pages/man7/inotify.7.html
Here is another way, maybe you can try it.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <list>
#include <Locale.h>
using namespace std;
void ListFile(std::list<std::wstring> &list_Steady)
{
setlocale(LC_ALL, "chs");
WIN32_FIND_DATAW FindFileData;
HANDLE hFind;
LPCWSTR s = L"C:\\Test_Monitor\\*.*";//The path of Directory you want to Monitor
hFind = FindFirstFileW(s, &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFile failed (%d)\n", GetLastError());
return ;
}
else
{
wcout << "The first file found is " << FindFileData.cFileName << endl;
list_Steady.push_back(FindFileData.cFileName);
}
while(FindNextFile(hFind, &FindFileData))
{
wcout << "The next file found is " << FindFileData.cFileName << endl;
list_Steady.push_back(FindFileData.cFileName);
}
FindClose(hFind);
hFind = NULL;
}
void CompareFile(std::list<std::wstring> &new_list , std::list<std::wstring> &steady_list)
{
bool bFound = false;
for (std::list<std::wstring>::iterator it = new_list.begin(); it != new_list.end(); ++it)
{
bFound = false;
for (std::list<std::wstring>::iterator steady_it = steady_list.begin(); steady_it != steady_list.end(); ++steady_it)
{
//Compare to origin list, if file still exist will return true
if(it->compare(steady_it->c_str()) == 0)
{
bFound = true;
}
}
//else this is a new file
if(!bFound)
{
wcout << it->c_str() << L" new file detected" << endl;
}
}
bool bfound2 = false;
for (std::list<std::wstring>::iterator it = steady_list.begin(); it != steady_list.end(); ++it)
{
bfound2 = false;
for (std::list<std::wstring>::iterator new_it = new_list.begin(); new_it != new_list.end(); ++new_it)
{
//Compare to origin list, if file still exist will return true
if(it->compare(new_it->c_str()) == 0)
{
bfound2 = true;
}
}
//else this file has been deleted
if(!bfound2)
{
wcout<< it->c_str() << L" This file has disappeared" << endl;
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
list<wstring> Steady_Directory;
list<wstring> New_Directory;
DWORD WaitStatus = 0;
Steady_Directory.clear(); New_Directory.clear(); //be sure that list is clean
ListFile(Steady_Directory);
HANDLE FileChange = INVALID_HANDLE_VALUE;
FileChange = ::FindFirstChangeNotification(L"C:\\Test_Monitor",FALSE,FILE_NOTIFY_CHANGE_FILE_NAME);
if(FileChange == INVALID_HANDLE_VALUE)
{
cout << "LOG : FAIL to get File Change, With error code: " << ::GetLastError() << endl;
return -1;
}
WaitStatus = ::WaitForSingleObject(FileChange, INFINITE);
if (WaitStatus == WAIT_OBJECT_0)
{
ListFile(New_Directory);
CompareFile(New_Directory, Steady_Directory);
}
system("PAUSE");
return 0;
}
I use two list, one stand for old state, the other is for the next state, and use ::FindFirstChangeNotification to subscribe specific directory's state.
Any file change in the directory will set the HANDLE Filechange, then will call the function CompareFile to make sure which file has been added or deleted.
You can make a for/while loop to make this monitor work constantly

Count files with a specific extension in directories and subdirectories C++

For Example: .txt
C:\Duck\aa.txt
C:\Duck\Dog\Pig\cc.txt
C:\Duck\Dog\kk.txt
C:\Duck\Cat\zz.txt
C:\xx.txt
Return 5
You must use opendir() and readdir().
Example:
DIR *dir = opendir("."); // current dir
if (dir == NULL) {
perror("opendir: .");
return 1;
}
struct dirent *dirent;
while ((dirent = readdir(dir)) != NULL) {
printf("%s\n", dirent->d_name);
}
closedir(dir);
Please try it.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
int count = 0;
void listdir(const char *name, int level)
{
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(name)))
return;
if (!(entry = readdir(dir)))
return;
do {
if (entry->d_type == DT_DIR) {
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", name, entry->d_name);
path[len] = 0;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
listdir(path, level + 1);
}
else {
const char *ext = strrchr(entry->d_name,'.');
if((!ext) || (ext == entry->d_name))
;
else {
if(strcmp(ext, ".txt") == 0) {
printf("%*s- %s\n", level * 2, "", entry->d_name);
count++;
}
}
}
} while (entry = readdir(dir));
closedir(dir);
}
int main(void)
{
listdir(".", 0);
printf("There are %d .txt files in this directory and its subdirectories\n", count);
return 0;
}
Test
./a.out
- hi.txt
- payfile.txt
- csis.txt
- CMakeCache.txt
- text.txt
- sorted.txt
- CMakeLists.txt
- ddd.txt
- duom.txt
- output.txt
- mmap_datafile.txt
- file_datafile.txt
- CMakeLists.txt
- link.txt
- TargetDirectories.txt
There are 15 .txt files in this directory and its subdirectories
Thank you all for the answers, the code you had tried:
WIN32_FIND_DATA fdata;
int counter = 0;
if (HANDLE first = FindFirstFile(L"C:\\duck\\*.txt", &fdata))
{
counter++;
do
{
counter++;
} while (FindNextFile(first, &fdata));
}
But as you can see by the same, I did not get the result I was looking for, which was to include the subdirectories.
Thanks to "Dac Saunders" and to all the others, it helped a lot.

How can I list files in a directory by modification date?

Currently I'm using readdir and it is working fine. Now time has come where my folders are cluttered and searching through a list by alphabetical order (not guaranteed though, it's directory order) can be frustrating. So, how can I modify the code below to sort by modification date rather than it's current order?
static cell AMX_NATIVE_CALL n_dir_list( AMX* amx, cell* params)
{
DIR *dir = (DIR*)params[1];
struct dirent *ent;
if ((ent = readdir (dir)) != NULL)
{
cell *buf, *addr;
amx_GetAddr(amx, params[3], &addr);
switch (ent->d_type)
{
case DT_REG:
*addr = 2;
break;
case DT_DIR:
*addr = 1;
break;
default:
*addr = 0;
}
amx_GetAddr(amx, params[2], &buf);
amx_SetString(buf, ent->d_name, 0, 0, params[4]);
return true;
}
return false;
}
The readdir function is from the dirent header, as follows:
static struct dirent *readdir(DIR *dirp)
{
DWORD attr;
if (dirp == NULL) {
/* directory stream did not open */
DIRENT_SET_ERRNO (EBADF);
return NULL;
}
/* get next directory entry */
if (dirp->cached != 0) {
/* a valid directory entry already in memory */
dirp->cached = 0;
} else {
/* get the next directory entry from stream */
if (dirp->search_handle == INVALID_HANDLE_VALUE) {
return NULL;
}
if (FindNextFileA (dirp->search_handle, &dirp->find_data) == FALSE) {
/* the very last entry has been processed or an error occured */
FindClose (dirp->search_handle);
dirp->search_handle = INVALID_HANDLE_VALUE;
return NULL;
}
}
/* copy as a multibyte character string */
DIRENT_STRNCPY ( dirp->curentry.d_name,
dirp->find_data.cFileName,
sizeof(dirp->curentry.d_name) );
dirp->curentry.d_name[MAX_PATH] = '\0';
/* compute the length of name */
dirp->curentry.d_namlen = strlen (dirp->curentry.d_name);
/* determine file type */
attr = dirp->find_data.dwFileAttributes;
if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) {
dirp->curentry.d_type = DT_CHR;
} else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
dirp->curentry.d_type = DT_DIR;
} else {
dirp->curentry.d_type = DT_REG;
}
return &dirp->curentry;
}
The Microsoft documentation for FindNextFile says, "If the data must be sorted, the application must do the ordering after obtaining all the results." So if you want the directory sorted by modification date you'll have to read it all in and sort it yourself.

recursive file search

I'm trying to figure out how to work this thing out .. For some reason, it ends at a certain point.. I'm not very good at recursion and I'm sure the problem lies somewhere there..
Also, even if I checked for cFileName != "..", it still shows up at the end, not sure why but the "." doesn't show up anymore..
void find_files( wstring wrkdir )
{
wstring temp;
temp = wrkdir + L"\\" + L"*";
fHandle = FindFirstFile( temp.c_str(), &file_data );
if( fHandle == INVALID_HANDLE_VALUE )
{
return;
}
else
{
while( FindNextFile( fHandle, &file_data ) )
{
if( file_data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY &&
wcscmp(file_data.cFileName, L".") != 0 &&
wcscmp(file_data.cFileName, L"..") != 0 )
{
find_files( wrkdir + L"\\" + file_data.cFileName );
}
else if( file_data.dwFileAttributes != FILE_ATTRIBUTE_HIDDEN &&
file_data.dwFileAttributes != FILE_ATTRIBUTE_SYSTEM )
{
results << wrkdir << "\\" << file_data.cFileName << endl;
}
}
}
}
After changing those, the program doesn't enumerate the remaining files left..
For example, if there is a sub folder named test, it enumerates everything inside test but doesn't finish enumerating the files inside the original directory specified.
From the FindFirstFile documentation:
If the function fails or fails to
locate files from the search string in
the lpFileName parameter, the return
value is INVALID_HANDLE_VALUE and the
contents of lpFindFileData are
indeterminate.
You should only exit from the one iteration not the whole program:
if( fHandle == INVALID_HANDLE_VALUE )
{
return;
}
And this may solve your other problem:
else if( file_data.dwFileAttributes != FILE_ATTRIBUTE_HIDDEN &&
file_data.dwFileAttributes != FILE_ATTRIBUTE_SYSTEM &&
wcscmp(file_data.cFileName, L".") != 0 &&
wcscmp(file_data.cFileName, L"..") != 0
)
{
results << wrkdir << "\\" << file_data.cFileName << endl;
}
Also see #fretje's answer as well. It gives another problem that your code has.
Updated new: You need to use fHandle as a local variable as well, not global variable.
Change to:
HANDLE fHandle = FindFirstFile( temp.c_str(), &file_data );
You are changing the value of your local wrkdir variable:
wrkdir = wrkdir + L"\\" + file_data.cFileName;
find_files( wrkdir );
I think you have to call find_files there like this:
find_files( wrkdir + L"\\" + file_data.cFileName );
and not change the value of wrkdir.
There are still several bugs in your code. Try this instead:
void find_files( wstring wrkdir )
{
wstring wrkdirtemp = wrkdir;
if( !wrkdirtemp.empty() && (wrkdirtemp[wrkdirtemp.length()-1] != L'\\') )
{
wrkdirtemp += L"\\";
}
WIN32_FIND_DATA file_data = {0};
HANDLE hFile = FindFirstFile( (wrkdirtemp + L"*").c_str(), &file_data );
if( hFile == INVALID_HANDLE_VALUE )
{
return;
}
do
{
if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if( (wcscmp(file_data.cFileName, L".") != 0) &&
(wcscmp(file_data.cFileName, L"..") != 0) )
{
find_files( wrkdirtemp + file_data.cFileName );
}
}
else
{
if( (file_data.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0 )
{
results << wrkdirtemp << file_data.cFileName << endl;
}
}
}
while( FindNextFile( hFile, &file_data );
FindClose( hFile );
}
Recursive file search with dirent.h
#include <iostream>
#include <dirent.h>
#include <string.h>
bool isUpDirecory(const char* directory) {
if (strcmp(directory, "..") == 0 || strcmp(directory, ".") == 0)
return true;
else
return false;
}
bool findFile(const std::string& fileName, const std::string& path,
std::string& resultPath) {
dirent* entry;
DIR* dir = opendir(path.c_str());
if (dir == NULL)
return false;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_REG) {
if (fileName.compare(entry->d_name) == 0) {
resultPath = path + "/" + entry->d_name;
closedir(dir);
return true;
}
}
}
rewinddir(dir);
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
if (!isUpDirecory(entry->d_name)) {
std::string nextDirectoryPath = path + "/" + entry->d_name;
bool result = findFile(fileName, nextDirectoryPath, resultPath);
if (result == true) {
closedir(dir);
return true;
}
}
}
}
closedir(dir);
return false;
}
int main() {
std::string path;
bool result = findFile("text.txt", "/home/lamerman/", path);
std::cout << path << std::endl;
return 0;
}
Also, check out the implementation of the CFileFind MFC class.
You still have errors in your code:
you ignore the results of the first search. you call FindFirstFile and handle if it fails. But if it succeeds you do not process already fetched file_data and overwrite it with FindNextFile.
You don't close the search handle. Use FindClose for that.
From your existing code it seems that fHandle is global - it shouldn't. It would break your recursion.
Also I think that you can resolve all the issues in your code by paying more attention to MSDN sample provided in FindFirstFile documentation.