This question already has answers here:
Why do directory listings contain the current (.) and parent (..) directory?
(8 answers)
Closed 8 years ago.
I have a little problem. I'm reading files from directory and it works, but it read two extra files on the beginning ...what is it?
for example, there is a list of files: "A348", "A348A", "A348B"
and this is what i get: ".", "..", "A348", "A348A", "A348B" ???
DIR *dir;
struct dirent *dp;
char * file_name;
while ((dp=readdir(dir)) != NULL) {
file_name = dp->d_name;
}
. is a directory entry for current directory
.. is a directory entry for the directory one level up in hierarchy
You have to just filter them out using:
if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") )
{
// do nothing (straight logic)
} else {
file_name = dp->d_name; // use it
}
More on using . and .. on Windows:
".\\file" - this is a file named file in current working directory
"..\\file" - this is a file in a parent directory
"..\\otherdir\\file" - this is a file that is in directory named otherdir, that is at the same level as current directory (we don't have to know what directory are we in).
Edit: selfcontained example usage of readdir:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main()
{
DIR *dir;
struct dirent *dp;
char * file_name;
dir = opendir(".");
while ((dp=readdir(dir)) != NULL) {
printf("debug: %s\n", dp->d_name);
if ( !strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") )
{
// do nothing (straight logic)
} else {
file_name = dp->d_name; // use it
printf("file_name: \"%s\"\n",file_name);
}
}
closedir(dir);
return 0;
}
Avoid taking the files whose name . and ..
Related
I'm trying to recursively search a directory for a file and output the file if it matches the given:
static int *search(char *path, const char *request, int depth, bool verbose)
{
DIR *dir;
struct dirent *ent;
char *start_dir = strcmp(path, "/") == 0 ? "root" : path;
printf("\nStarting in '%s' directory..\n\n", start_dir);
if ((dir = opendir(path)) != NULL)
{
while ((ent = readdir(dir)) != NULL)
{
if (verbose == true)
{
printf("Searching directory %s for file %s\n", ent->d_name, request);
}
if (ent->d_type == DT_DIR)
{
if ((strlen(path) + strlen(ent->d_name) + 1) > PATH_MAX)
{
puts("Path to long, cannot continue..");
}
else
{
if (ent->d_name == DT_DIR && strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0 )
{
printf("%s\n", ent->d_name);
}
}
}
}
}
return 0;
}
This will work but it will not output the files or directories inside of the directory. Example:
#ubuntu:~/bin/c/find-files$ ./utilis test / -V
Initialized to search for file: 'test'..
Starting in 'root' directory..
Searching directory vmlinuz.old for file test
Searching directory boot for file test
Searching directory home for file test
Searching directory libx32 for file test
Searching directory lib32 for file test
Searching directory lib64 for file test
Searching directory initrd.img for file test
Searching directory srv for file test
Searching directory usr for file test
Searching directory . for file test
Searching directory cdrom for file test
Searching directory tmp for file test
Searching directory initrd.img.old for file test
Searching directory bin for file test
Searching directory .. for file test
Searching directory proc for file test
Searching directory lib for file test
Searching directory var for file test
Searching directory dev for file test
Searching directory sys for file test
Searching directory media for file test
Searching directory root for file test
Searching directory snap for file test
Searching directory run for file test
Searching directory sbin for file test
Searching directory opt for file test
Searching directory lost+found for file test
Searching directory mnt for file test
Searching directory vmlinuz for file test
Searching directory etc for file test
How can I refactor this function in order to recursively search for the given filename in all directories and sub directories?
As already point out in the comments by many. Your code is not recursive with the missing recursive call (function calling itself). You have a lot of mistake. Not checking for errors with various system calls and forgetting to close the directory stream. I don't know what you want to achieve with your various parameters int depth, bool verbose. But this is another way of looking for a specific file and returning 0 --> EXIT_SUCCESS on success and 1 -->EXIT_FAILURE on failure.
The are various ways things can go wrong when using system calls and some them set errnos, which is important to look at them and print to STDERR
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#define DEBUG 1
static int search(char *path,const char *file){
DIR *dir;
char *slash = "/";
int ret = 1;
struct dirent *entry;
//checking if it failed to open and report errors to STDERR
if((dir = opendir(path)) == NULL){
fprintf(stderr,"opendir: %s\n",strerror(errno));
return EXIT_FAILURE;
}
while ((entry = readdir(dir))){
//if is . or .. we continue to prevent winging back and forth
if(strcmp(entry->d_name,".") == 0 || strcmp(entry->d_name,"..") == 0)
continue;
//we check if the path has already a / if not we add one
int length = strlen(path);
if(path[length-1] != '/'){
slash = "/";
}
length += strlen(entry->d_name)+2;
char *newpath = malloc(length);
if (!newpath){
fprintf(stderr,"malloc: %s\n",strerror(errno));
break;
}
snprintf(newpath,length,"%s%s%s",path,slash,entry->d_name);
if(strcmp(entry->d_name,file) ==0){
#if DEBUG
printf("Was found here %s\n",newpath);
#endif
ret = EXIT_SUCCESS;
break;
}
//checking if is a directory to do a recursive call
// using DT_DIR to avoid the use of lstat or stat
// if not directory we free the memory and move on
if(entry->d_type == DT_DIR)
search(newpath,file);
else{
free(newpath);
continue;
}
free(newpath);
}
if(closedir(dir) != 0){
fprintf(stderr,"closedir: %s\n",strerror(errno));
return EXIT_FAILURE;
}
return ret;
}
int main() {
char *file = "algo.c";
int ret = search("/",file);
printf("%d The file %s was %s\n",ret,file,(ret == 0 ? "found":"not found"));
return 0;
}
With DEBUG set to 1
Was found here /Users/addodennis/CLionProjects/BluePrint/algo.c
The file algo.c was found
With DEBUG set to 0
file algo.c was found
This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I wrote a program that print the directory name or file name. It's easy but I got something trouble.
It couldn't distinguish directory and file type. I know and I used stat.st_mode to finish it. But something is wrong:
When I use gdb to check the st_mode value, I found it was 0, except "." and "..", so here is the question: why st_mode is 0?
and that is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
DIR *pDir = opendir("MyDirectory");
struct dirent *pDirent;
struct stat vStat;
if (pDir == NULL)
{
printf("Can't open the directory \"MyDirectory\"");
exit(1);
}
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name, &vStat);
if (S_ISDIR(vStat.st_mode))
printf("Directory: %s\n", pDirent->d_name);
else
printf("File: %s\n", pDirent->d_name);
}
closedir(pDir);
return 0;
}
Classic readdir mistake: pDirent->d_name is the name of the directory entry, not a path to the file. It's "1", "4-5.c", etc. So your stat calls are looking for a file with that name in the current directory, not under MyDirectory.
Check the return value of stat. You'll see that it's ENOENT — except for . and .., which exist in the current directory as well. When stat fails, the content of the stat structure is undefined.
If you're calling opendir in a directory other than ., then to do pretty much anything useful with the returned names, you need to build a full path. Copy the path you passed to opendir to a buffer with enough room for a slash and file name in addition, and copy each file name to that buffer. Proof-of-concept code (error checking omitted, etc.):
char *directory = "MyDirectory";
size_t directory_length = strlen(directory);
char *path = malloc(directory_length + 1 + NAME_MAX);
strcpy(path, directory);
path[directory_length] = '/';
while ((pDirent = readdir(pDir)) != NULL) {
strcpy(path + directory_length + 1, pDirent->d_name);
if (stat(path, &vStat) == -1) {
perror(path);
continue;
}
…
}
I have a directory say A, into which i have sub-directories aa,bb,cc,dd,ee,ff. Each sub directories have a number of .txt, .bin, .dat files. What I want to do is, check each of the sub-directory to see if it contains a text file, if yes return the sub directory name.
The below c script lists the sub directories, but please assist to check within the sub-directory for a txt file.
I'm trying to do this in windows 7-visual studio 2010
#include <dirent.h>
#include <stdio.h>
int main(void)
{
DIR *d;
DIR *f;
struct dirent *dir;
d = opendir("C:\\Users\\xp\\Desktop\\Star1");
if (d) {
while ((dir = readdir(d)) != NULL) {
if (dir->d_name[0] != '.') {
f=opendir(dir->d_name);
if (strstr(dir->d_name , ".txt")) {
printf("%s\n", dir->d_name);
}
}
}
closedir(d);
}
return(0);
}
You could use a flag. If you find a file ending in ".txt" then you set the flag and exit the loop. After the loop you check the flag.
One way to check if a string ends with a specific sub-string:
static const char string_to_find[] = ".txt";
...
// First make sure the filename is long enough to fit the name-suffix
if (strlen(dir->d_name) > strlen(string_to_find))
{
// +strlen(dir->d_name) to get a pointer to the end of dir->d_name
// -strlen(string_to_find) to get a pointer to where the suffix should start
if (strcmp(dir->d_name + strlen(dir->d_name) - strlen(string_to_find),
string_to_find) == 0)
{
// File-name ends with ".txt"
}
}
As an alternative, lazy and Windows-specific solution, you can just let the job to the windows for command this way:
#include <stdio.h>
#include <string.h>
#define MAX_LENGTH 1024
int main()
{
char buffer[MAX_LENGTH];
FILE *f = _popen("cmd /c #for /R C:\\Users\\xp\\Desktop\\Star1\\ %i in (.) do #if exist \"%~i\"\\*.txt echo %~fi 2> NUL", "r");
if (f != NULL)
{
while (fgets(buffer, MAX_LENGTH, f) != NULL)
{
int len = strlen(buffer);
if (buffer[len - 1] == '\n')
{
buffer[--len] = '\0';
}
printf("Found: %s\n", buffer);
}
_pclose(f);
}
}
Edit: fixed answer to give directory list instead of .txt files.
Instead of printing the directories you could just put it in an if-statement to check if it's the desired file. If it is: return the directory name, else continue. You can put it all in a for-loop so you can check every directory.
For example:
If(!strcmp(filename, filetofind))
Return dirname
This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I wrote a program that print the directory name or file name. It's easy but I got something trouble.
It couldn't distinguish directory and file type. I know and I used stat.st_mode to finish it. But something is wrong:
When I use gdb to check the st_mode value, I found it was 0, except "." and "..", so here is the question: why st_mode is 0?
and that is my code:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
int main(void)
{
DIR *pDir = opendir("MyDirectory");
struct dirent *pDirent;
struct stat vStat;
if (pDir == NULL)
{
printf("Can't open the directory \"MyDirectory\"");
exit(1);
}
while ((pDirent = readdir(pDir)) != NULL)
{
stat(pDirent->d_name, &vStat);
if (S_ISDIR(vStat.st_mode))
printf("Directory: %s\n", pDirent->d_name);
else
printf("File: %s\n", pDirent->d_name);
}
closedir(pDir);
return 0;
}
Classic readdir mistake: pDirent->d_name is the name of the directory entry, not a path to the file. It's "1", "4-5.c", etc. So your stat calls are looking for a file with that name in the current directory, not under MyDirectory.
Check the return value of stat. You'll see that it's ENOENT — except for . and .., which exist in the current directory as well. When stat fails, the content of the stat structure is undefined.
If you're calling opendir in a directory other than ., then to do pretty much anything useful with the returned names, you need to build a full path. Copy the path you passed to opendir to a buffer with enough room for a slash and file name in addition, and copy each file name to that buffer. Proof-of-concept code (error checking omitted, etc.):
char *directory = "MyDirectory";
size_t directory_length = strlen(directory);
char *path = malloc(directory_length + 1 + NAME_MAX);
strcpy(path, directory);
path[directory_length] = '/';
while ((pDirent = readdir(pDir)) != NULL) {
strcpy(path + directory_length + 1, pDirent->d_name);
if (stat(path, &vStat) == -1) {
perror(path);
continue;
}
…
}
I am working on a simple project to implement "ls -R" from scratch. Whenever I run what I have, my program just keeps searching the root directory over and over again. What am I doing wrong?
void lsR(char dirName[]) {
/*
The recursive function call.
*/
DIR *dir;
struct dirent *directory;
struct stat fileStat;
char type;
char **nameList[MAX_RECURSIVE_FILES];
struct passwd *user;
int count = 0;
int i = 0;
printf("\n");
printf("./%s :\n", dirName);
printf("\n");
if ((dir = opendir(dirName)) == NULL) {
perror("opendir error:");
return;
}
while ((directory = readdir(dir)) != NULL) {
if (stat(directory->d_name, &fileStat) < 0) {
perror("fstat error:");
return;
}
if (fileStat.st_uid == 1) {
continue;
}
user = getpwuid(fileStat.st_uid);
printf("%s ", directory->d_name);
fileType(&fileStat, &type);
if ((type == 'd') && (count < MAX_RECURSIVE_FILES)) {
nameList[count] = malloc(sizeof(char)*MAX_STRING_LENGTH);
strncpy(nameList[count++], directory->d_name, MAX_STRING_LENGTH);
}
}
closedir(dir);
printf("\n");
for (i=0; i<count; i++) {
printf("Calling lsR on: %s\n", nameList[i]);
lsR(nameList[i]);
}
}
When it executes, I get the following output:
"./. :
., .., ... all other files in my current working directory ....
./. :
., .., ... all other files in my current working directory...
"
Among the list of files in the current directory you've noticed . and .. The first one is a hardlink to the current directory and the second one to the parent directory. So when you recurse through your dir entries you will want to skip those two. Otherwise the first directory you will recurse into will be ., in other words the directory you've just gone through.
This is the reason of your program current behavior, but once you fix that you will run into the issue lurker mentioned in his answer.
Additional notes :
Are you sure about the char **nameList[MAX_RECURSIVE_FILES]; variable? Seems to me you want an array of char * not an array of char **.
Are you aware you can use the S_ISDIR macro on the st_mode field of your stat struct, in order to check that the current file is not a directory instead of your custom function?
You need to include the path relative to your program's current directory. Each nameList element will need to be dirName + "/" + directory->d_name.
If you started out calling lsR on the local directory, ./foo and foo has directory named bar under it, then to open bar you need to open ./foo/bar since your program is running from the directory represented by ..