I'm trying to make a C program that will display all the files and folders containing a given search term. The search term is given as an argument when executing the program. A folder / file is displayed to standard output if its name contains the search term (case insensitive).
The difficulty though is that I do not want to output files and subfolders that are contained in a folder that contains the search term. Here's an example:
Let's assume my search term is docker, this is the current output:
"/Users/me/.docker"
"/Users/me/.docker/contexts"
"/Users/me/.docker/contexts/meta"
"/Users/me/.docker/config.json"
"/Users/me/.docker/scan"
"/Users/me/.docker/scan/config.json"
"/Users/me/.docker/application-template"
"/Users/me/.docker/application-template/logs"
"/Users/me/.docker/application-template/logs/com.docker.log"
"/Users/me/.docker/daemon.json"
"/Users/me/.docker/run"
"/Users/me/Library/Application Support/Docker Desktop"
"/Users/me/Library/Application Support/Docker Desktop/blob_storage"
"/Users/me/Library/Application Support/Docker Desktop/blob_storage/6965e70b-e33a-4415-b9a8-e19996fe221d"
But this is the output I'm trying to achieve:
"/Users/me/.docker"
"/Users/me/Library/Application Support/Docker Desktop"
Here's my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
static int display_info(const char *fpath, const char * fname, const char * term) {
int what_len = strlen(term);
int count = 0;
char *where = fpath;
if (what_len){
while ((where = strcasestr(where, term))) {
where += what_len;
count++;
}
if (count == 1) {
printf("\"%s/%s\"\n", fpath, fname);
}
}
return 0;
}
static void listFilesRecursively(char * basePath, const char * searchTerm) {
char path[1000];
struct dirent * dp;
DIR * dir = opendir(basePath);
// Unable to open directory stream
if (!dir)
return;
while ((dp = readdir(dir)) != NULL) {
if (strcmp(dp -> d_name, ".") != 0 && strcmp(dp -> d_name, "..") != 0) {
// printf("%s %hhu %s\n", basePath, dp->d_type, dp -> d_name);
display_info(basePath, dp->d_name, searchTerm);
// Construct new path from our base path
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp -> d_name);
listFilesRecursively(path, searchTerm);
}
}
closedir(dir);
}
int main(int argc, const char * argv[]) {
char * home = getenv("HOME");
if (argc == 2) {
listFilesRecursively(home, argv[1]);
} else {
printf("Please provide one argument");
}
return 0;
}
Any feedback is greatly appreciated thanks!
I am not sure I understand the logic of display_info()
On listFilesRecursively() you can not reuse path from call to call
main() should the the first function in your code, maybe in a separate file
an alternative
I will add a C example, changing a bit of your listFilesRecursively()...
not using void() so you can return -1 for an error
testing for . and .. at the beginning of the loop and just using continue may lead into code easier to read
path is allocated locally and free() on return
strstr_ign() is a case insensitive version of strstr() for use in the pattern search
code for list_files() after change
int list_files(char* pattern, char* base_path)
{
struct dirent* dp;
DIR* dir = opendir(base_path);
if (!dir) return -1; // Unable to open directory stream
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") == 0) continue;
if (strcmp(dp->d_name, "..") == 0) continue;
if ( strstr_ign((const char*)dp->d_name, pattern) != NULL )
{
display_info(base_path, dp->d_name);
}
else
{
char* path = (char*)malloc(1 + strlen(dp->d_name) + strlen(base_path) + 1);
sprintf(path, "%s/%s", base_path, dp->d_name);
list_files(pattern, path);
free(path); // ok with path
}
}; // while()
closedir(dir);
return 0;
}; // list_files()
code for strstr_ign()
I hate the arguments order for strstr() but kept it here just to have things equal. This way one can use strstr_ign() as a drop-in replacement for strstr() without changing the order of the arguments. I believe needle should come first :) an in the language: search for a needle in a haystack is far more common than search the haystack for a needle but Ken and Dennis had their reasons to write strstr() the way they did it...
//
// strstr() ignoring case
//
char* strstr_ign(const char* haystack, const char* needle)
{
if (needle == NULL) return NULL;
if (haystack == NULL) return NULL;
if (*needle == 0)
{
if (*haystack == 0)
return (char*) haystack;
else
return NULL;
}
int limit = strlen(haystack) - strlen(needle);
for (int x = 0; x <= limit; x += 1)
{ // search for needle at position 'x' of 'haystack'
int here = 1;
for (unsigned y = 0; y < strlen(needle); y += 1)
{
if ( tolower(haystack[x + y]) != tolower(needle[y]) )
{
here = 0; break;
};
};
if ( here == 1) return (char*)(haystack + x);
}
return NULL;
};
a new display_info()
changed to show last access for folders and file size for regular files that match the search pattern (case insensitive). Below is an example of the output for files and folders. Note the '-' and the 'd' as in the ls -l output.
- "./hms.c" [size: 1546]
d "./sub/1/xyzHMSxyz" [last access: Sat Apr 24 12:38:04 2021]
int display_info(const char* base, const char* file)
{
struct stat Stats;
char* path = (char*)malloc(1 + strlen(base) + strlen(file) + 1);
char atime[40];
sprintf(path, "%s/%s", base, file);
if ( stat(path, &Stats) < 0)
{
perror("Inside display_info()");
free(path);
return -1;
}
if ( S_ISDIR(Stats.st_mode) )
{
strftime( atime, sizeof(atime), "%a %b %d %H:%M:%S %Y", localtime(&Stats.st_atime) );
printf("\td \"%s/%s\"\t[last access: %s]\n", base, file, atime );
}
else
{
if ( S_ISREG(Stats.st_mode) )
printf("\t- \"%s/%s\"\t[size: %ld]\n", base, file, Stats.st_size );
else
printf("is somthing else\n");
}
free(path);
return 0;
}
sample output
Search pattern is "hms" (case is ignored)
- "./hms" [size: 16848]
- "./hms-soma.c" [size: 1379]
- "./hms.c" [size: 1546]
d "./sub/1/xyzHMSxyz" [last access: Sat Apr 24 12:38:04 2021]
d "./sub/2/xyzHMS" [last access: Sat Apr 24 12:21:11 2021]
d "./sub/hMs" [last access: Sat Apr 24 12:21:11 2021]
C code for this test
miminally tested :)
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
int display_info(const char*, const char*);
int list_files(char*, char*);
char* strstr_ign(const char*, const char*);
int main(int argc, const char * argv[])
{
char search_term[80];
if (argc >= 2)
{
if ( strlen(argv[1]) > (sizeof(search_term)-1) )
{
printf("Size of substring (%zd) must not be greater than %zd\n",
strlen(argv[1]), sizeof(search_term)-1 );
return -1;
}
for ( int i = 0; i<= strlen(argv[1]); search_term[i] = (char)(tolower(argv[1][i])), i+=1 );
printf("Search pattern is \"%s\" (case is ignored) \n", search_term );
list_files(search_term,".");
} else {
printf("Please provide pattern to search for.\n");
}
return 0;
}; // main()
int display_info(const char* base, const char* file)
{
struct stat Stats;
char* path = (char*)malloc(1 + strlen(base) + strlen(file) + 1);
char atime[40];
sprintf(path, "%s/%s", base, file);
if ( stat(path, &Stats) < 0)
{
perror("Inside display_info()");
free(path);
return -1;
}
if ( S_ISDIR(Stats.st_mode) )
{
strftime( atime, sizeof(atime), "%a %b %d %H:%M:%S %Y", localtime(&Stats.st_atime) );
printf("\td \"%s/%s\"\t[last access: %s]\n", base, file, atime );
}
else
{
if ( S_ISREG(Stats.st_mode) )
printf("\t- \"%s/%s\"\t[size: %ld]\n", base, file, Stats.st_size );
else
printf("is somthing else\n");
}
free(path);
return 0;
}
int list_files(char* pattern, char* base_path)
{
struct dirent* dp;
DIR* dir = opendir(base_path);
if (!dir) return -1; // Unable to open directory stream
while ((dp = readdir(dir)) != NULL)
{
if (strcmp(dp->d_name, ".") == 0) continue;
if (strcmp(dp->d_name, "..") == 0) continue;
if ( strstr_ign((const char*)dp->d_name, pattern) != NULL )
{
display_info(base_path, dp->d_name);
}
else
{
char* path = (char*)malloc(1 + strlen(dp->d_name) + strlen(base_path) + 1);
sprintf(path, "%s/%s", base_path, dp->d_name);
list_files(pattern, path);
free(path); // ok with path
}
}; // while()
closedir(dir);
return 0;
}; // list_files()
//
// strstr() ignoring case
//
char* strstr_ign(const char* haystack, const char* needle)
{
if (needle == NULL) return NULL;
if (haystack == NULL) return NULL;
if (*needle == 0)
{
if (*haystack == 0)
return (char*) haystack;
else
return NULL;
}
int limit = strlen(haystack) - strlen(needle);
for (int x = 0; x <= limit; x += 1)
{ // search for needle at position 'x' of 'haystack'
int here = 1;
for (unsigned y = 0; y < strlen(needle); y += 1)
{
if ( tolower(haystack[x + y]) != tolower(needle[y]) )
{
here = 0; break;
};
};
if ( here == 1) return (char*)(haystack + x);
}
return NULL;
};
Thanks to #KamilCuk I was able to solve my issue. Here's my final listFilesRecursively function:
static void listFilesRecursively(char * basePath, const char * searchTerm) {
char path[1000];
struct dirent * dp;
DIR * dir = opendir(basePath);
// Unable to open directory stream
if (!dir)
return;
while ((dp = readdir(dir)) != NULL) {
if (strcmp(dp -> d_name, ".") != 0 && strcmp(dp -> d_name, "..") != 0) {
if (strcasestr(dp->d_name, searchTerm)) {
printf("%s/%s\n",basePath,dp->d_name);
listFilesRecursively(path, searchTerm);
} else {
// Construct new path from our base path
strcpy(path, basePath);
strcat(path, "/");
strcat(path, dp -> d_name);
listFilesRecursively(path, searchTerm);
}
}
}
closedir(dir);
}
Related
The following C code will list the amount of files and directories and will do it 4 times faster than the linux find command. I need only the count of the folders, not interested in the file count and even listing them. Is there a way to optimize the below code and make it more efficient?
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <dirent.h>
void listdir(char *path, size_t size) {
DIR *dir;
struct dirent *entry;
size_t len = strlen(path);
if (!(dir = opendir(path))) {
fprintf(stderr, "path not found: %s: %s\n",
path, strerror(errno));
return;
}
puts(path);
while ((entry = readdir(dir)) != NULL) {
char *name = entry->d_name;
if (entry->d_type == DT_DIR) {
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
if (len + strlen(name) + 2 > size) {
fprintf(stderr, "path too long: %s/%s\n", path, name);
} else {
path[len] = '/';
strcpy(path + len + 1, name);
listdir(path, size);
path[len] = '\0';
}
} else {
printf("%s/%s\n", path, name);
}
}
closedir(dir);
}
int main( int argc, char *argv[] ) {
if( argc == 2 ) {
printf("Path: %s\n", argv[1]);
}
else if( argc > 2 ) {
printf("Too many arguments supplied.\n");
}
else {
printf("One argument expected.\n");
return 0;
}
char path[1024];
memcpy (path, argv[1],1024);
listdir(path, sizeof path);
return 0;
}
Removing the following lines will of course not display the files , but will not speed up the execution time :
} else {
printf("%s/%s\n", path, name);
}
If you are not interested in printing the filenames, just remove the printf statements.
Note however that there are some problems in the code:
memcpy(path, argv[1], 1024); may read beyond the end of the string pointed to by argv[1], which is undefined behavior, or not produce a proper C string, which leads to undefined behavior in the function listdir.
You could also avoid recomputing the length of the directory name in each recursive call.
Here is a modified version you can try:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
long long countdirs(char *path, size_t size, size_t len) {
DIR *dir;
struct dirent *entry;
long long count;
if (!(dir = opendir(path))) {
fprintf(stderr, "path not found: %s: %s\n",
path, strerror(errno));
return 0;
}
count = 1; // count this directory
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
char *name = entry->d_name;
size_t len1 = strlen(name);
if (*name == '.' && (len1 == 1 || (len1 == 2 && name[1] == '.')))
continue;
if (len + len1 + 2 > size) {
count++;
fprintf(stderr, "path too long: %s/%s\n", path, name);
} else {
path[len] = '/';
memcpy(path + len + 1, name, len1 + 1);
count += countdirs(path, size, len + 1 + len1);
path[len] = '\0';
}
}
}
closedir(dir);
return count;
}
int main(int argc, char *argv[]) {
char buf[4096];
char *path;
size_t len;
if (argc != 2) {
fprintf(stderr, "one argument expected.\n");
return 1;
}
path = argv[1];
len = strlen(path);
if (len >= sizeof(buf)) {
fprintf(stderr, "path too long: %s\n", path);
return 1;
}
memcpy(buf, path, len + 1);
printf("%s: %lld directories\n", path, countdirs(buf, sizeof buf, len));
return 0;
}
Further notes:
The above code might fail if the directory tree is too deep or has loops. Failure may come from running out of handles causing opendir to fail.
You should try an alternative approach using the POSIX standard function nftw() as documented in this answer: https://stackoverflow.com/a/29402705/4593267
As suggested by EOF, since paths are not used, constructing them is not required. It might be safer and more efficient to use openat() and fdopendir(). (documented here: https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html ).
There is little point in optimizing this function as most of the time is spent in the OS or waiting for the storage device. The effect of file system cacheing may be huge: I measured 15x on linux for 133000 directories. Using a different set of system calls may improve or worsen the speed, but small improvements are probably highly system specific.
I'm currently trying to make a c program that counts the total file size in bytes of a given directory, including the directory itself, all files within, and all files and directories in all sub-directories. Essentially, I'm being asked to program a replacement for du -b as a command.
I thought I had a working solution, but after the first directory this program thinks that all entries deeper down are directories, even when they're just regular files. This includes when I give it a directory that's one degree deeper directly, say by giving it the input ./Directory1 instead of just ..
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
int du_function(char direc[]) {
int total = 0;
char str[100];
strcpy(str, direc);
struct stat sfile;
struct dirent *de;
DIR *dr = opendir(direc);
if (dr == NULL) {
printf("Could not open directory\n");
return 0;
}
while ((de = readdir(dr)) != NULL) {
printf("%s\n", de->d_name);
stat(de->d_name, &sfile);
if (S_ISDIR(sfile.st_mode) && strcmp(de->d_name, ".") != 0 &&
strcmp(de->d_name, "..") != 0) {
strcat(str, "/");
strcat(str, de->d_name);
printf("This is a directory called %s\n", str);
total = total + du_function(str);
strcpy(str, direc);
}
if (S_ISREG(sfile.st_mode) || strcmp(de->d_name, ".") == 0) {
printf("Size in bytes = %ld\n", sfile.st_size);
total = total + sfile.st_size;
printf("The total is %d bytes so far.\n", total);
}
}
printf("The total is %d bytes.\n", total);
closedir(dr);
return total;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
char cwd[1] = ".";
du_function(cwd);
} else
du_function(argv[1]);
return 0;
}
I'm at my wit's end here and have tried a variety of solutions, but for some reason S_ISDIR must be identifying things as directories that aren't (or receiving the wrong input from me, more likely.)
char cwd[1] = "."; is too small for a string. No room the the null character.
Use [] and let the compiler determine the needed size for the string.
char cwd[] = ".";
stat() call is based on local name and not full path.
#if 0
stat(de->d_name, &sfile);
#else
char buf[500];
snprintf(buf, sizeof buf, "%s/%s", direc, de->d_name);
stat(buf, &sfile);
#endif
Similar finding in (deleted) #PSkocik
Perhaps other issues exist.
Just like you construct the path for the recursive call, you must construct the filename with the leading directory for the stat system call. Your program only works for the files in the current directory.
An effective way to fix your program is pass a large enough buffer to the recursive function and build the path and filenames incrementally in place:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <dirent.h>
long int du_function(char path[], size_t size) {
size_t len = strlen(path);
long int total = 0;
struct stat sfile;
struct dirent *de;
DIR *dr = opendir(path);
if (dr == NULL) {
printf("Could not open directory %s\n", path);
return 0;
}
while ((de = readdir(dr)) != NULL) {
if (strcmp(de->d_name, "..") == 0)
continue;
//printf("%s\n", de->d_name);
if (snprintf(path + len, size - len, "/%s", de->d_name) > (int)(size - len)) {
path[len] = '\0';
printf("Path too long: %s/%s\n", path, de->d_name);
continue;
}
stat(path, &sfile);
if (S_ISDIR(sfile.st_mode) && strcmp(de->d_name, ".") != 0) {
//printf("This is a directory called %s\n", path);
total = total + du_function(path, size);
} else
if (S_ISREG(sfile.st_mode) || strcmp(de->d_name, ".") == 0) {
//printf("Size in bytes = %ld\n", (long)sfile.st_size);
total = total + sfile.st_size;
//printf("The total is %ld bytes so far.\n", total);
}
}
path[len] = '\0';
printf("%ld\t%s\n", total, path);
closedir(dr);
return total;
}
int main(int argc, char *argv[]) {
char path[1024] = ".";
if (argc > 1) {
strcpy(path, argv[1]);
}
du_function(path, sizeof path);
return 0;
}
I am trying to count only the directories from a path, but it doesn't work. So, i don't want to number both files and directories, i want only the directories. Could you help me, please?
The code:
int listdir(char *dir) {
struct dirent *dp;
struct stat s;
DIR *fd;
int count = 0;
if ((fd = opendir(dir)) == NULL) {
fprintf(stderr, "listdir: can't open %s\n", dir);
}
while ((dp = readdir(fd)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode))
count++;
}
closedir(fd);
return count;
}
your stat() call will fail, since you are not in the correct directory. This you can solve by either changing the current directory, or generate full paths and give to stat as argument.
Some Unixes, you can optimize away the stat call by looking at struct dirent, the d_type field
int listdir(char *dir) {
struct dirent *dp;
struct stat s;
DIR *fd;
int count = 0;
if ((fd = opendir(dir)) == NULL) {
fprintf(stderr, "listdir: can't open %s\n", dir);
}
chdir (dir); /* needed for stat to work */
while ((dp = readdir(fd)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
#ifdef _DIRENT_HAVE_D_TYPE
switch (dp->d_type)
{
case DT_UNKNOWN:
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode)) count++;
break;
case DT_DIR:
count++;
break;
}
#else
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode)) count++;
#endif
}
closedir(fd);
return count;
}
I think you want...
written in C
count the number of directoriesfrom a path.
the counting function will returns int value.
I have no idea about your environment, so it is just a example solution.
If you can use glob, it is so easy to count the number of directories. that is: main.c
#include <stdio.h>
#include <glob.h>
#include <string.h>
#define MAX_PATH 1023
int countDirectories(char* dir)
{
char path[MAX_PATH] = "";
strcat(path, dir);
strcat(path, "/*");
glob_t globbuf;
long i, count = 0;
if (glob(path, GLOB_NOSORT | GLOB_ONLYDIR, NULL, &globbuf) == 0)
{
count = globbuf.gl_pathc;
for (i = 0; i < globbuf.gl_pathc; i++)
{
count += countDirectories(globbuf.gl_pathv[i]);
}
}
globfree(&globbuf);
return count;
}
int main(int argc, char* argv[])
{
int count;
if (argc > 1)
{
count = countDirectories(argv[1]);
}
else
{
count = countDirectories(".");
}
printf("there are %d directories.\n", count);
return 0;
}
and you can try this like:
> gcc main.c -o testglob
> ./testglob /path/to/target/dir
then you'll receive output like this:
path = /path/to/target/dir/*, there are N directories
thanks.
I'm trying to create a function which recursively returns the contents of a folder. While testing it, I encounter some problems. With some folders it works, With others it gives me an EXC_BAD_ACCESS, and sometimes it just stops.
I have been trying to debug it with GDB for a long time, but I just can't find a solution to my problem. The function is not quit short an goes as follows.
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
/* THIS IS AN EXTRACT FROM A LARGER FILE
THERE MAY BE TO MUCH INCLUDE STATEMENTS
*/
struct Directory {
DIR *handle;
const char *filename;
};
typedef struct Directory Directory;
int DirectoryCreate(const char *n, Directory *d) {
DIR *dh;
char *str;
dh = opendir(n);
if(dh == NULL) {
return -1;
}
d->handle = dh;
str = malloc(strlen(n) + 1);
if(str == NULL) {
errno = ENOMEM;
closedir(d->handle);
return -1;
}
strcpy(str, n);
d->filename = (const char *)str;
return 0;
}
void DirectoryFree(Directory *s) {
if(s->handle) {
closedir(s->handle);
}
if(s->filename) {
free((void *)s->filename);
}
}
void FreeDirectoryArray(Directory *array, size_t size) {
register size_t i;
for(i = 0; i < size; i++) {
DirectoryFree(&(array[i]));
}
free(array);
}
Directory *ReadFolders = NULL;
size_t ReadFoldersSize = 0;
const char *ReadFolderFilename = NULL;
const char *ReadNextRecursiveItemInFolder(const char *folder) {
struct dirent *entry;
struct stat fileStatus;
int status;
mode_t mode;
const char *newFilename;
char *fullName;
char *ptr;
size_t strLen;
if(folder == NULL && ReadFolders == NULL) {
errno = 0;
return NULL;
}
if(folder != NULL) {
/* free the previous directory list */
FreeDirectoryArray(ReadFolders, ReadFoldersSize);
ReadFolders = NULL;
ReadFoldersSize = 0;
/* open the new directory */
ReadFolders = (Directory *)realloc(ReadFolders, sizeof(Directory));
ReadFoldersSize++;
status = DirectoryCreate(folder, ReadFolders);
if(status != 0) {
FreeDirectoryArray(ReadFolders, ReadFoldersSize-1);
ReadFolders = NULL;
return NULL;
}
}
entry = readdir(ReadFolders[ReadFoldersSize - 1].handle);
/* If NULL, go to previous folder */
if(entry == NULL) {
DirectoryFree(&(ReadFolders[ReadFoldersSize - 1]));
--ReadFoldersSize;
/* if it's empty, we've reached the end */
if(ReadFoldersSize == 0) {
free(ReadFolders);
ReadFolders = NULL;
errno = 0;
return NULL;
}
newFilename = ReadNextRecursiveItemInFolder(NULL);
return newFilename;
}
/* Make sure the entry name is not . or .. */
if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
newFilename = ReadNextRecursiveItemInFolder(NULL);
return newFilename;
}
/* we've got an entry, now construct the full path */
strLen =
strlen(ReadFolders[ReadFoldersSize - 1].filename) +
1 +
strlen(entry->d_name);
fullName = malloc(strLen + 1);
ptr = fullName;
strcpy(ptr, ReadFolders[ReadFoldersSize - 1].filename);
ptr += strlen(ReadFolders[ReadFoldersSize - 1].filename);
strcpy(ptr, "/");
ptr++;
strcpy(ptr, entry->d_name);
newFilename = fullName;
/* no recurse on symbolic links */
status = lstat(newFilename, &fileStatus);
if(status != 0) {
FreeDirectoryArray(ReadFolders, ReadFoldersSize);
ReadFolders = NULL;
ReadFoldersSize = 0;
return NULL;
}
mode = fileStatus.st_mode;
/* if not readable for file or not searchable for folder, get next */
/* if folder and not link, recursively continue */
/* else return the new name */
if((((mode & S_IFDIR) == S_IFDIR) && (mode & S_IXUSR) != S_IXUSR) ||
(mode & S_IRUSR) != S_IRUSR) {
free((void *)newFilename);
newFilename = ReadNextRecursiveItemInFolder(NULL);
return newFilename;
} else if((mode & S_IFDIR) && (mode & S_IFLNK) != S_IFLNK) {
ReadFolders = realloc(ReadFolders, ReadFoldersSize + 1);
ReadFoldersSize++;
errno = 0;
status = DirectoryCreate(newFilename, &(ReadFolders[ReadFoldersSize - 1]));
if(status != 0) {
FreeDirectoryArray(ReadFolders, ReadFoldersSize - 1);
ReadFolders = NULL;
ReadFoldersSize = 0;
return NULL;
}
if(newFilename != ReadFolderFilename) {
free((void *)ReadFolderFilename);
ReadFolderFilename = newFilename;
}
} else {
if(newFilename != ReadFolderFilename) {
free((void *)ReadFolderFilename);
ReadFolderFilename = newFilename;
}
errno = 0;
}
return ReadFolderFilename;
}
int main() {
const char *filename = "/Users/";
const char *entry;
while(1) {
entry = ReadNextRecursiveItemInFolder(filename);
filename = NULL;
if(entry == NULL) {
if(errno == 0) {
printf("End reached\n");
} else {
printf("Error: %s\n", strerror(errno));
}
break;
}
printf("Entry: %s\n", entry);
}
return 0;
}
I'll give a brief explanation how the code works. To start looping a directory, you have to give the full directory path to the function. All subsequent calls have to pass NULL to get the next item in line, unless they want to process another directory.
The code counts every file and folder in a folder, recursively. It does not follow symbolic links, and it only counts readable files and executable directories. To keep track of its 'flow', the function uses 3 global variables:
ReadFolders: an array of Directory structures used to keep track of different levels of folders. The last one at the back.
ReadFoldersSize: the amount of Directory structures in ReadFolders.
ReadFolderFilename: the string that contains the last item processed.
I hope I can find some help around here,
ief2.
The realloc size is wrong : it not "n" but "n*size".
So the line 153 should be :
ReadFolders = realloc(ReadFolders, (ReadFoldersSize + 1)*sizeof(Directory));
Here is a program I made this program has segmentation fault I checked it in gdb in the second last of code free(somepath); .I do not have any reason for why is this segmentation fault coming?
Some one please suggest some thing.
#include<dirent.h>
#include<unistd.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<stdio.h>
char *directs[20], *files[20];
int i = 0;
int j = 0;
int count = 0;
void printdir(char *);
int count_dirs(char *);
int count_files(char *);
void new_printdir(int ,int ,char *);
int main()
{
char startdir[20];
printf("Scanning user directories\n");
scanf("%s", startdir);
printdir(startdir);
}
void printdir(char *dir)
{
DIR *dp = opendir(dir);
int nDirs, nFiles, nD, nF;
nDirs = 0;
nFiles = 0;
nD = 0;
nF = 0;
if (dp) {
struct dirent *entry = 0;
struct stat statBuf;
nDirs = count_dirs(dir);
nFiles = count_files(dir);
new_printdir(nDirs,nFiles,dir);
while ((entry = readdir(dp)) != 0) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char * filepath = malloc(strlen(dir) + strlen(entry->d_name) + 2);
if (filepath) {
sprintf(filepath, "%s/%s", dir, entry->d_name);
if (lstat(filepath, &statBuf) == 0) {
if (S_ISDIR(statBuf.st_mode)) {
printdir(filepath);
}
else {
}
}
}
free(filepath);
} //2nd while
closedir(dp);
}
else {
fprintf(stderr, "Error, cannot open directory %s\n", dir);
}
} //printdir
int count_dirs(char *dir)
{
DIR *dp = opendir(dir);
int nD;
nD = 0;
if (dp) {
struct dirent *entry = 0;
struct stat statBuf;
while ((entry = readdir(dp)) != 0) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char *filepath = malloc(strlen(dir) + strlen(entry->d_name) + 2);
if (filepath) {
sprintf(filepath, "%s/%s", dir, entry->d_name);
if (lstat(filepath, &statBuf) != 0) {
fprintf(stderr, "File Not found? %s\n",filepath);
}
if (S_ISDIR(statBuf.st_mode)) {
nD++;
} else {
continue;
}
free(filepath);
}
}
closedir(dp);
} else {
fprintf(stderr, "Error, cannot open directory %s\n", dir);
}
return nD;
}
int count_files(char *dir)
{
DIR *dp = opendir(dir);
int nF;
nF = 0;
if (dp) {
struct dirent *entry = 0;
struct stat statBuf;
while ((entry = readdir(dp)) != 0) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char *filepath =
malloc(strlen(dir) + strlen(entry->d_name) + 2);
if (filepath) {
sprintf(filepath, "%s/%s", dir, entry->d_name);
if (lstat(filepath, &statBuf) != 0) {
fprintf(stderr, "File Not found? %s\n", filepath);
}
if (S_ISDIR(statBuf.st_mode)) {
continue;
} else {
nF++;
}
free(filepath);
}
}
closedir(dp);
} else {
fprintf(stderr, "Error, cannot open file %s\n", dir);
}
return nF;
}
void new_printdir(int nDirs,int nFiles,char *dir)
{
struct dirent **namelist;
DIR *dop;
int i, j,t,re,nD,nF;
char userd[20],*somepath;
i = scandir(dir, &namelist, 0, alphasort);
t=0;
if (i < 0)
perror ("Scandir failed to open directory I hope you understand \n");
else {
for (j = 0; j < i; j++) {
if(strcmp(".",namelist[j]->d_name)==0||strcmp("..",namelist[j]->d_name)==0)
continue;
somepath = malloc(strlen(dir)+strlen(namelist[j]->d_name)+2);
sprintf(somepath,"%s/%s",dir,namelist[j]->d_name);
dop=opendir(somepath);
if(dop)
{
nD++;
if ((nDirs-nD)<3)
{printf("%s ",namelist[j]->d_name);}
}
else {
nF++;
if ((nFiles-nF)<3)
printf("%s ",namelist[j]->d_name);
}
closedir(dop);
free(namelist[j]);
}
}
free(namelist);
free(somepath);
}
Why is this segmentation fault happening this is not clear to me.
What can I do to get rid of it?
In your code, you are not guaranteed to allocate memory and assign it to somepath (you also fail to free somepath except for the last iteration of the loop). You should put your free(somepath) statement at the end of the for-loop, and you should initialize somepath to NULL both at the very beginning of the function and at the start of each for loop to avoid similar careless errors.
You're not initializing the somepath variable - it has an undefined value unless the malloc() executes (i.e. for empty folders). You should initialize it to NULL at the place of definition.
You do not initialize somepath to 0 (NULL), but you could under some circumstances release the value. This means you release a random value - which is not a good idea.