The fts
library can be used to traverse directories recursive.
- fts_open (): Returns the FTS object for the directory passed as argument
- fts_read (): Returns an FTSENT object for a file in an FTS object
Note that i am not familiar with fts apis, there can be something wrong.
Example : Traversing directories
The following example outputs all files for the "./"
directory. The fts library traverses the folder recursively and finds all files.
The example was written referring to Android framework code.
main.cpp
#include <errno.h>
#include <string.h>
#include <fts.h>
#include <iostream>
using namespace std;
int main() {
std::string path("./");
FTS *fts;
char *argv[] = { (char*) path.c_str(), nullptr };
if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
if (errno != ENOENT) {
cout << "Failed to fts_open " << path;
}
return -1;
}
FTSENT *p;
while ((p = fts_read(fts)) != nullptr) {
switch (p->fts_info) {
case FTS_DP:
cout << "Directory: " << p->fts_path << "\n";
break;
case FTS_F:
cout << "File: " << p->fts_path << "\n";
break;
case FTS_SL:
case FTS_SLNONE:
cout << "Symbolic link: " << p->fts_path << "\n";
break;
}
}
fts_close(fts);
return 0;
}
Build & Run
Before running the code, the current directory state is:
$ tree
.
├── aa
│ └── file2.txt
├── bb
│ ├── dd
│ │ ├── file6.txt
│ │ └── file7.txt
│ ├── file3.txt
│ ├── file4.txt
│ └── file5.txt
├── cc
│ ├── file8.txt
│ ├── file9.txt
│ └── symbolic.txt -> file8.txt
├── file1.txt
├── main.cpp
└── main
Build and run with the following command:
$ g++ main.cpp -o main
$ ./main
In the results, all files under "./"
were printed.
File: ./bb/file5.txt
File: ./bb/dd/file7.txt
File: ./bb/dd/file6.txt
Directory: ./bb/dd
File: ./bb/file3.txt
File: ./bb/file4.txt
Directory: ./bb
File: ./aa/file2.txt
Directory: ./aa
Symbolic link: ./cc/symbolic.txt
File: ./cc/file9.txt
File: ./cc/file8.txt
Directory: ./cc
File: ./file1.txt
File: ./main.cpp
File: ./main
Directory: ./
fts_open()
You can get the FTS object using fts_open() as follow. The file path is passed as an argument.
std::string path("./");
char *argv[] = { (char*) path.c_str(), nullptr };
if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
if (errno != ENOENT) {
cout << "Failed to fts_open " << path;
}
return -1;
}
The meanings of the options are as follows. See manpage for more details.
- FTS_PHYSICAL : This option causes the fts routines to return FTSENT structures for symbolic links
- FTS_NOCHDIR : To allow descending to arbitrary depths and improve performance
- FTS_XDEV : This option prevents fts from descending into directories that have a different device number
fts_read()
You can traverse the directory using fts_read() as follow. You can get the file path with a the fts_path
of the FTSENT object.
FTSENT *p;
while ((p = fts_read(fts)) != nullptr) {
switch (p->fts_info) {
case FTS_DP:
cout << "Directory: " << p->fts_path << "\n";
break;
case FTS_F:
cout << "File: " << p->fts_path << "\n";
break;
case FTS_SL:
case FTS_SLNONE:
cout << "Symbolic link: " << p->fts_path << "\n";
break;
}
}
The structure of FTSENT is as follows:
typedef struct _ftsent {
int fts_info; /* status for FTSENT structure */
char *fts_accpath; /* access path */
char *fts_path; /* root path */
size_t fts_pathlen; /* strlen(fts_path) */
char *fts_name; /* file name */
size_t fts_namelen; /* strlen(fts_name) */
long fts_level; /* depth (-1 to N) */
int fts_errno; /* file errno */
long long fts_number; /* local numeric value */
void *fts_pointer; /* local address value */
struct ftsent *fts_parent; /* parent directory */
struct ftsent *fts_link; /* next file structure */
struct ftsent *fts_cycle; /* cycle structure */
struct stat *fts_statp; /* stat(2) information */
} FTSENT;
And the FTS file information used in the example above is as follows. See manpage for more details.
- FTS_DP : A directory
- FTS_F: A regular file
- FTS_SL, FTS_SLNONE: A symbolic link
fts_close()
if you are done using the FTS ojbect, you have to close it as following.
fts_close(fts);