I've been trying for a few days now to write a code which will inspect all subfolders from C:\Users, and print their files, for example:
C:\Users\Public
files
C:\Users\Somefolder
files
Here is my code:
main() {
DIR *dr,*dirp;
struct dirent *dr_ent,*sec;
char *buf,*baf;
char get[50],x[50];
char folder[] = "C:\\Users\\";
dr = opendir(folder);
if (dr != NULL)
goto next;
else return -1;
next:
while ((dr_ent = readdir(dr))!=NULL) {
buf=dr_ent->d_name;
strcpy(get,buf);
if (strstr(get,".")==NULL && strstr(get,"..")==NULL) {
strcpy(x,folder);
strcat(x,get);
printf("%s\n",x);
continue;
goto read;
Sleep(300);
}
}
read:
dirp = opendir(get);
while ((sec = readdir(dirp))!=NULL) {
baf=sec->d_name;
printf("%s\n",baf);
Sleep(300);
}
system("PAUSE");
return EXIT_SUCCESS;
}`enter code here`
And in this case, only one folder was read. So am I actually making mistake by taking a variable from loop before so only one line was taken? And why is second label completely ignored by program? By the way, I'm sort of beginner in C programming, so don't get surprised by potential errors.
The second label is completely ignored because before the goto read; you use continue; which takes you to the next iteration of the while loop ignoring everything else after it.
Also in your code you don't check if the directory's entry is a file or a directory to handle it appropriately. What I mean is you should descend into a directory but you should print a file's name when you encounter one (as you do).
You can use a recursive function to avoid the use of goto, as goto is a bad practice.
void list_dir(char const* dirName)
{
DIR* dir;
if ((dir = opendir(dirName)) == NULL) {
fprintf(stderr, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
exit(EXIT_FAILURE);
}
struct dirent* entry;
// For every entry in the directory
while ((entry = readdir(dir)) != NULL) {
char* path; // path = dirName/entryName
int pathLength; // length of the path in chars
struct stat entryStats; // info of the entry
char* entryName = entry->d_name; // entry filename
pathLength = strlen(dirName) + 1 + strlen(entryName);
path = malloc((pathLength + 1) * sizeof(char));
strcpy(path, dirName);
strcat(path, "/");
strcat(path, entryName);
if (stat(path, &entryStats) == 0) {
// If the entry is a directory
if (entryStats.st_mode & S_IFDIR) {
// If the directory is not "." or ".." get its entries
if (strcmp(entryName, ".") != 0 &&
strcmp(entryName, "..") != 0) {
list_dir(path);
}
}
// If the entry is a file
else if (entryStats.st_mode & S_IFREG) {
printf("%s\n",path);
}
}
free(path);
}
closedir(dir);
}
The above code works on Unix but on Windows you may have to change stat and struct stat to _stat and struct _stat if you are using Visual Studio.
Related
In this code, I am getting all the subdirectories paths from a directory. It is working fine but I want to add something more and that is to count all the subdirectories and print them. How to do this in this function. I used the count variable to make it work but the result is like this.
Given result:
/home/runner/TestC1/file
15775232
/home/runner/TestC1/dsf
15775233
/home/runner/TestC1/main.c
15775234
/home/runner/TestC1/main
15775235
Expected result:
/home/runner/TestC1/file
/home/runner/TestC1/dsf
/home/runner/TestC1/main.c
/home/runner/TestC1/main
Counted: 4 subdirectories.
Code
void listdir(void){
DIR *dir;
struct dirent *entry;
size_t count;
if (!(dir = opendir(path))) {
perror ("opendir-path not found");
return;
}
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR)
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
snprintf (path1, 100, "%s/%s\n", path, name);
printf("%s", path1);
printf("%zu\n", count);
count++;
}
closedir (dir);
}
There are a few problems with your code:
As Thomas says above:
You didn't initialize count. And you are printing inside the loop
You do not initialize count to zero.
You print inside the loop.
You count everything except . and .., without checking whether it is a file or a directory
Here's a (hopefully) fixed version of your code
void listdir(void){
DIR *dir;
struct dirent *entry;
unsigned count = 0;
if (!(dir = opendir(path))) {
perror ("opendir-path not found");
return;
}
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR) {
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
count++;
snprintf (path1, 100, "%s/%s\n", path, name);
printf("%s", path1);
}
}
printf("\nCounted: %u subdirectories.\n", count);
closedir (dir);
}
Edit:
Edited my code following the comment by chux-ReinstateMonica
Minor: Little reason for size_t count when unsigned count is certainly fine. size_t depends on memory - not file space. Pedantically could use uintmax_t. IAC, "%d" is not the specified matching specifier to go with size_t - a step backwards from OP's matching "%zu"
I want to write a program to list all projects I've got on my hard drive, whatever languages. So for example if I have something like user/projects/C/decompressor/src/... I want the program to ouput decompressor, which is the project root, as it contains src folders and so on. But there are too many case where the parent folder of src is not the real root folder:
projects/C/my-app/release1/src/... or projects/C/another-project-in-c/master/src/...
So before coding I wanted to know what would be the best way to do so ? I have a piece of code that doesn't works as I want:
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(name)))
return;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
char path[1024];
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
//printf("%*s[%s]\n", indent, "", entry->d_name);
listdir(path, indent + 2);
} else {
if (strcmp(get_file_extension(entry->d_name), ".c") == 0) {
char buffer[1024];
realpath(name, buffer);
printf("possible root folder = \"%s\" \n", get_parent(buffer));
}
}
}
closedir(dir);
and:
char* get_parent(char* filename) {
return (dirname(filename));
}
const char* get_file_extension(const char* filename) {
assert(filename != NULL);
char buffer[1024];
char* tmp = strcpy(buffer, filename);
char *ext = strrchr(tmp, '.');
if (ext == NULL)
return (char*) tmp + strlen(tmp);
for (char *iter = ext + 1; *iter != '\0'; iter++) {
if (!isalnum((unsigned char)*iter))
return (char*) tmp + strlen(tmp);
}
return ext;
}
I didn't mention it, but there's also the java project structure (projects/JAVA/MyApp/com/lorem/ipsum/concrete-package) to take in count.
Is this even possible with all the particular cases?
Let projects be the overall projects' root, then you first want to scan all directories inside projects by using a readdir loop (given that each directory inside projects stands for a certain language and contains actual project directories).
While iterating, you want to scan each language folder itself for all d_names that are contained.
To clarify what I'm talking about, I got some pseudo code for you:
dirp = opendir("/user/projects");
while (entp = readdir(dirp)) {
inner_dirp = opendir(entp->d_name);
while (inner_entp = readdir(inner_dirp)) {
print(inner_entp->d_name)
}
}
Note that if your folder structure is prone to change, you might want to go by a recursive approach.
For some reason when I iterate through all the files/folders in a directories, and check my current selection against S_ISDIR it only works on the "." and the ".." directories. Even though I have 3 files (A, B, C) and 2 folders (subdir, seconddir)
code:
//will read all files inside current directory, logic steps down into any sub directory found
int readDir(char * opt_basedir) // opt_basedir is the folder within the root repository of the .exe file, in this case "test"
{
struct dirent *dirr;
DIR *directory; // used to keep track of current directory
struct stat fileStat; //used for lstat to hold stat info for the document
directory = opendir("."); // open root
directory = opendir(opt_basedir); // iterate straight into the selected folder
dirr = readdir(directory);
while(dirr){
lstat(dirr->d_name, &fileStat);
if(S_ISDIR(fileStat.st_mode))
{
printf("only prints of . and .. =>" );
}
//if file (open)
//readCopy(opt_basedir, &block, &offset);
// else folder
//function call
printf("%s\n",dirr->d_name);
dirr = readdir(directory);
}
}
Not sure what I'm doing wrong here... :(
You must make sure first that opt_basedir is your current working directory.
You could do something like
chdir(opt_basedir);
directory = opendir(".");
instead of
directory = opendir(opt_basedir);
Another way is to create an absolute path:
directory = opendir(opt_basedir);
/* error check */
while ((dirr = readdir(directory))) {
char *path = malloc(strlen(opt_basedir) + strlen(dirr->d_name) + 1);
/* error check */
strcpy(path, opt_basedir);
strcat(path, dirr->d_name);
lstat(path, ...);
/* other code */
free(path);
}
Here is a full test program (Note: the output in readDir() only for debugging):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
void
readDir(const char *path)
{
DIR *dp;
struct dirent *dr;
struct stat fs;
dp = opendir(path);
if (dp == NULL) {
perror("opendir");
return;
}
printf("%s:\n", path);
while ((dr = readdir(dp))) {
if (!strcmp(dr->d_name, ".") || !strcmp(dr->d_name, "..")) {
continue;
}
char *abs_path = malloc(strlen(path) + strlen(dr->d_name) + 2);
if (!abs_path) {
perror("malloc");
exit(EXIT_FAILURE);
}
strcpy(abs_path, path);
strcat(abs_path, "/");
strcat(abs_path, dr->d_name);
if (lstat(abs_path, &fs) < 0) {
free(abs_path);
continue;
}
if (S_ISDIR(fs.st_mode)) {
readDir(abs_path);
}
printf("\t%s\n", dr->d_name);
free(abs_path);
}
}
int
main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s dir\n", argv[0]);
exit(EXIT_FAILURE);
}
readDir(argv[1]);
exit(EXIT_SUCCESS);
}
Besides d_name, struct dirent has a member called d_type, too.
Then to fulfill your task, just do sth. like:
struct dirent * dirr;
DIR * directory = opendir(opt_basedir);
if (directory) {
while ((dirr = readdir(directory)) != nullptr) {
if (DT_DIR == dirr->d_type) {
...
}
}
...
}
... is any code you may want to add. And here DT_DIR helps you to check whether it is a directory or not. You don't need to use stat() or S_ISDIR or so on. (Since you already used dirent)
Hope this answer will help you and anybody else who have similar problem.
I'm having issues getting the following code to run to recursively copy sub-folders in C. I saw this in another post but the code seem to not run the if statement to check if the current file is a directory.
void SearchDirectory(const char *name) {
DIR *dir = opendir(name);
if(dir) {
char Path[256], *EndPtr = Path;
struct dirent *e;
strcpy(Path, name);
EndPtr += strlen(name);
while((e = readdir(dir)) != NULL) {
struct stat info;
strcpy(EndPtr, e->d_name);
if(!stat(Path, &info)) { //code stops here and won't check if the current file is a directory or not..
if(S_ISDIR(info.st_mode)) {
SearchDirectory(Path);
} else if(S_ISREG(info.st_mode) {
//Copy routine
}
}
}
}
}
Edit
So I added a slash on the the end of the path and it seem to find the directory but is crashing with stack error on execution. I think it is recursing without end. New code is:
void SearchDirectory(const char *name) {
DIR *dir = opendir(name);
if(dir) {
char Path[256], *EndPtr = Path;
struct dirent *e;
strcpy(Path, name);
strcat(Path, slash);
EndPtr += (strlen(name)+1);
while((e = readdir(dir)) != NULL) {
struct stat info;
strcpy(EndPtr, e->d_name);
if(!stat(Path, &info)) { //code stops here and won't check if the current file is a directory or not..
if(S_ISDIR(info.st_mode)) {
SearchDirectory(Path);
} else if(S_ISREG(info.st_mode) {
//Copy routine
}
}
}
}
}
It looks like it's failing to insert the directory separator character (/) between the base directory and the appended part.
Assuming name = "/home/foo/bar", EndPtr will point at the '\0' at the end, and then e->d_name is copied there, without anything in-between. This is wrong, it will create a mashed-up filename.
I could not test your code myself, I do not have the right libraries installed., but
there is an example that may be helpful here of using opendir et. al.
Platform: Windows XP Service Pack 3
Compiler: Code::Blocks version 12.11
I am currently writing a program that will recursively delete a given directory using the POSIX directory functions. But I am having a problem with readdir() and its counterpart the dirent structure. I read in readdir's documentation that multiple calls to the function will overwrite the data held in the structure returned by the function. So I thought readdir() must allcoate memmory for the struct itself and then simply reassign the pointer address to the structure that captures it's return value. I tested this theory and I was correct readdir() allocated memmory for it's member d_name. The problem I am having is that readdir returns a NULL pointer when the directory stream is empty, so I use a while loop with the condition (dirent_ptr != NULL) to iterate the entire directory. But because readdir() will handle the memmory allocation of the structure I simply declare a dirent structure and let readdir() do its job. Unfourtatnly for some reason dirent structures are initialized to NULL(or it might be my complier) so my loop never starts because it's conditional statement is not initialy true. So I guess my question is what am I doing wrong here?
Here are important variable declarations and the included librarys. Please note that all of these variables are declared globaly.
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
int recursive_delete(const char *path);
int file_delete(const char *path, int file_size);
struct dirent *direntp;
struct stat *statp;
struct switches
{
int verbose;
int no_prompt;
int continue_if_error;
int files_only;
}; struct switches switches;
Rather than resolving relative paths I simply cd to the path given as an argument and then I use the . and .. wildcards to move threw the directorys so that relative paths(d_names) are valid. Also the switches structure simply contains command line switches and should be ignored and I am aware of errors in the following code but unfourtantly I cannot fix them because I cannot get past the above mentioned problem.
int recursive_delete(const char *path)
{
DIR *dirp;
int return_value = 0;
int recursive_return_value = 0;
if((chdir(path)) == -1)
{
perror("ERROR(3)");
return 1;
}
printf("CDED to \"%s\"\n", path);
dirp = opendir(".");
if(dirp == NULL)
{
perror("ERROR(4)");
return 1;
}
printf("OPENED \"%s\"\n", path);
while(direntp != NULL)
{
direntp = readdir(dirp);
if( (direntp == NULL) && (errno != 0) )
{
perror("ERROR(5)");
return 1;
}
printf("READ \"%s\" FROM \"%s\"\n", direntp->d_name, path);
if( (strcmp(direntp->d_name, ".")!=0) && (strcmp(direntp->d_name, "..")!=0) )
{
if((stat(direntp->d_name, statp)) == -1)
{
perror("ERROR(6)");
return 1;
}
printf("STATED \"%s\"\n", direntp->d_name);
if(S_ISREG(statp->st_mode))
{
printf("DELETING \"...\\%s\\%s\"\n", path, direntp->d_name);
return_value += file_delete(direntp->d_name, statp->st_size);
if( (!switches.continue_if_error) && (return_value != 0) )
{
break;
}
}
else if(S_ISDIR(statp->st_mode))
{
printf("\n\n\nCALLING RECURSIVE DELETE with \"%s\"\n", direntp->d_name);
recursive_return_value = recursive_delete(direntp->d_name);
return_value += recursive_return_value;
if( (!switches.continue_if_error) && (recursive_return_value != 0) )
{
break;
}
if( (!switches.files_only) && (recursive_return_value == 0) )
{
if((chdir("..")) == -1)
{
perror("ERROR(6)");
return 1;
}
printf("CDED BACK TO \"%s\" FROM \"%s\"\n", path, direntp->d_name);
if((rmdir(direntp->d_name)) == -1)
{
perror("ERROR(7)");
return 1;
}
if(switches.verbose)
{
printf("DELETED DIRECTORY \"...\\%s\\\"\n\n\n", direntp->d_name);
}
}
}
}
}
return return_value;
}
Your code structure should look something like thhis (with most error checks omitted for clarity):
int recursive_delete(const char *path)
{
DIR* dirp = NULL;
int return_value = 0;
char* initial_cur_dir = malloc(1000);
getcwd(initial_cur_dir, 1000);
chdir(path);
dirp = opendir(".");
while (dirp != NULL)
{
struct dirent* direntp;
struct stat stat;
direntp = readdir(dirp);
if (direntp == NULL)
break;
stat(direntp->d_name, &stat);
if (S_ISDIR(statp->st_mode))
{
if (strcmp(direntp->d_name, ".") && strcmp(direntp->d_name, ".."))
{
return_value += recursive_delete(direntp->d_name);
}
}
else if (S_ISREG(statp->st_mode))
{
unlink(direntp->d_name);
}
}
if (initial_cur_dir != NULL)
{
chdir(initial_cur_dir);
rmdir(path);
}
ErrorLabel: // you should goto here when an error is detected
if (dirp != NULL)
{
closedir(dirp);
}
if (initial_cur_dir != NULL)
{
chdir(initial_cur_dir);
free(initial_cur_dir);
}
return return_value;
}
From the code attached, it's not clear where direntp is being initialized (before the while loop). Possibly try something like:
direntp = readdir(dirp);
while(direntp != NULL)
{
// all your work here
direntp = readdir(dirp);
}
This pattern ensures that direntp is initialized and updated for your while loop. However, on second glance of your code I'm not exactly sure what the while loop is supposed to be doing in the first place. How is direntp or dirp changing in your loop?
It's possible that you can just get away with an if test (instead of the while) and just let the recursive call handle the "looping" effect...