C - Scandir only fill namelist with folders - c

How do I use scandir, so that it only fills my struct dirent ** namelist, with folders?

You can filter the entities to list by providing a filter function, which should return a non-zero value if the file is to be included in the list.
Unfortunately, the members of the dirent struct don't tell you whether you have a directory or not (although your system might include a type field), so you must use some other means to find directories, for example stat:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
int filter_dir(const struct dirent *e)
{
struct stat st;
stat(e->d_name, &st);
return (st.st_mode & S_IFDIR);
}
You could also try to call opendir on the name and check wheter that succeeds. (But don't forget to close it if it is successful.)
This works for the current directory, because the names exclude the path. The filter also doesn't provide a slot for passing additional data, so the best you can do is to define a global variable that holds the path and that you must set beforehand.
Here's an implementation with a wrapper function, scandir_dir:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
static const char *filterdir;
static int filter_dir(const struct dirent *e)
{
char buf[NAME_MAX];
struct stat st;
if (filterdir) {
snprintf(buf, sizeof(buf), "%s/%s", filterdir, e->d_name);
stat(buf, &st);
} else {
stat(e->d_name, &st);
}
return (st.st_mode & S_IFDIR);
}
int scandir_dir(const char *path, struct dirent ***namelist)
{
int n;
filterdir = path;
n = scandir(path, namelist, filter_dir, alphasort);
filterdir = NULL;
return n;
}
int main()
{
struct dirent **namelist;
int n;
n = scandir_dir("/some/dir", &namelist);
if (n < 0) {
perror("scandir");
} else {
int i;
for (i = 0; i < n; i++) {
printf("%s\n", namelist[i]->d_name);
free(namelist[i]);
}
free(namelist);
}
return 0;
}

Related

How can I sort the files read from a directory?

I am trying to write a program that implements somehow the "dir" command that you can use in the Unix shell but I have encountered the following problem. I managed to read the current directory as I will show in the code but I don't know exactly how I am supposed to sort it in order to make it like the dir function which sorts the files from the directory
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
void dirfunction()
{
DIR* directory=opendir(".");
if(directory==NULL)
{
perror("Directory does not exist");
exit(1);
}
struct dirent* p;
p=readdir(directory);
int i=0;
while(p!=NULL)
{
if(strcmp(p->d_name,".")!=0 && strcmp(p->d_name,"..")!=0)
{
printf("%s ",p->d_name);
i++;
}
p=readdir(directory);
}
printf("\n");
closedir(directory);
}
int main(int argc,char** argv[])
{
dirfunction();
}
Should I basically do the normal sorting for an array of character, like adding all file names in an array of string and sort it with selection sort or another sort method? I don't really get how dir command sorts the files before printing them to the terminal.
Because readdir reuses the same buffer for the returned entry, we need a dynamic [growing] array to save/store the struct dirent entries.
Then, we need to sort the stored entries (e.g. use qsort).
Side note: int main(int argc,char **argv[]) is incorrect. It should be: int main(int argc,char **argv) There was one too many levels of indirection.
Below is the updated code with some enhancements. It is annotated:
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
// dynamic array of dirent structs
struct dirlist {
size_t count; // number of entries
struct dirent *base; // pointer to list start
};
// dirload -- load up directory
// RETURNS: directory list
struct dirlist *
dirload(const char *path)
{
size_t capacity = 0;
struct dirlist *list = calloc(1,sizeof(*list));
DIR *directory = opendir(path);
if (directory == NULL) {
perror("Directory does not exist");
exit(1);
}
struct dirent *p;
while (1) {
// get next entry
// this is overwritten, so we need to copy/save it below
p = readdir(directory);
if (p == NULL)
break;
// skip over "." and ".."
if (p->d_name[0] == '.') {
if (p->d_name[1] == 0)
continue;
if ((p->d_name[1] == '.') && (p->d_name[2] == 0))
continue;
}
// enlarge array
if (list->count >= capacity) {
capacity += 10;
list->base = realloc(list->base,sizeof(*list->base) * capacity);
if (list->base == NULL) {
perror("realloc");
exit(1);
}
}
// save dirent entry
list->base[list->count++] = *p;
}
closedir(directory);
// trim list to actual size
list->base = realloc(list->base,sizeof(*list->base) * list->count);
if (list->base == NULL) {
perror("realloc");
exit(1);
}
return list;
}
// dircmp -- compare dirent structs
int
dircmp(const void *lhsp,const void *rhsp)
{
const struct dirent *lhs = lhsp;
const struct dirent *rhs = rhsp;
int cmp;
cmp = strcmp(lhs->d_name,rhs->d_name);
return cmp;
}
// dirsort -- sort directory list
void
dirsort(struct dirlist *list)
{
if (list->count > 0)
qsort(list->base,list->count,sizeof(*list->base),dircmp);
}
// dirprint -- print directory list
void
dirprint(const struct dirlist *list)
{
const struct dirent *p = list->base;
for (size_t idx = 0; idx < list->count; ++idx, ++p)
printf("%s\n",p->d_name);
}
// dirdestroy -- destroy directory list
void
dirdestroy(struct dirlist *list)
{
if (list != NULL)
free(list->base);
free(list);
}
int
main(int argc, char **argv)
{
--argc;
++argv;
const char *dir;
if (argc > 0)
dir = *argv;
else
dir = ".";
struct dirlist *list = dirload(dir);
// sort the list
dirsort(list);
// print the list
dirprint(list);
// destroy the list
dirdestroy(list);
return 0;
}

How to use scandir() in C for case insensitive?

I'm learning C and I have this implementation to sort the files and folders, but this isn't case insensitive:
#include <dirent.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
struct dirent **namelist;
int n;
n = scandir(".", &namelist, NULL, alphasort);
if (n < 0)
perror("scandir");
else {
printf("Inside else, n = %d\n", n);
while (n--) {
printf("%s\n", namelist[n]->d_name);
free(namelist[n]);
}
free(namelist);
}
}
And if I have a.txt, b.txt, C.txt and z.txt it will sort in this order: C.txt, a.txt, b.txt, z.txt. I want this to be sorted case insensitive like this: a.txt, b.txt, C.txt, z.txt
scandir is defined with this prototype:
int scandir(const char *restrict dirp,
struct dirent ***restrict namelist,
int (*filter)(const struct dirent *),
int (*compar)(const struct dirent **,
const struct dirent **));
The function alphasort sorts the filenames in lexicographical order, hence case-sensitive order. If you want case insensitive sorting, use a different comparison function:
int alphasort_no_case(const struct dirent **a, const struct dirent **b) {
return strcasecmp((*a)->d_name, (*b)->d_name);
}
Both scandir and strcasecmp are POSIX functions: strcasecmp is highly likely to be available on systems that support scandir and defined in <strings.h>.
Modifier version:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
int alphasort_no_case(const struct dirent **a, const struct dirent **b) {
return strcasecmp((*a)->d_name, (*b)->d_name);
}
int main(void) {
struct dirent **namelist;
int n;
n = scandir(".", &namelist, NULL, alphasort_no_case);
if (n < 0) {
perror("scandir");
} else {
printf("Inside else, n = %d\n", n);
while (n--) {
printf("%s\n", namelist[n]->d_name);
free(namelist[n]);
}
free(namelist);
}
return 0;
}

Segmentation fault in recursive function, while parsing directories

I am trying to make a program that returns the total size of a directory. The program takes the path to the directory, parses it, if it finds a file(done using a function) it adds the size, else
I recall the function. After some debugging i found out that the line containing the call to the recursive function give a segmentation fault and i have absolutely no idea how to fix it.
my code
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
int isDirectory(const char* fileName) {
DIR* dir=opendir(fileName);
if(dir!=NULL) {
closedir(dir);
return 1;
} else if(errno == ENOTDIR) {
return 0;
} else return -1;
closedir(dir);
}
int getFileSize(char* fileName) {
FILE* file;
file=fopen(fileName,"r");
fseek(file,0,SEEK_END);
int size=ftell(file);
fclose(file);
return size;
}
int getAllSize(char* dirName) {
DIR* dir;
char* name=dirName;
int size=0;
struct dirent *dent;
dir=opendir(dirName);
if(dir!=NULL) {
while((dent=readdir(dir))!=NULL) {
char help[1000000];
memset(help,0,sizeof help);
if(strcmp(dent->d_name,"..") && strcmp(dent->d_name,".")) {
strcat(help,name);
strcat(help,"/");
strcat(help,dent->d_name);
printf("%s\n",help);
if(isDirectory(help)==1) {
getAllSize(name);
} else{
printf("before %d\n",size);
size+=getFileSize(help);
printf("after %d\n",size);
}
}
}
} else {
printf("sal1\n");
}
closedir(dir);
printf("%d",size);
return size;
}
int main(int argc,char** argv) {
getAllSize("/home/rares/Documents/OS/Lab3");
return 0;
}

How to make the c code compiled?

Here is a sample c code related to stat.h. bits/stat.h that mentioned "Never include <bits/stat.h> directly; use <sys/stat.h> instead.". However struct stat is defined in bits/stat.h, and int __xstat (...) is defined in sys/stat.h. The code won't compile with any one of headers or even both of them. How to make it copiled while only changing the #include ... without changing any one of the functions?
#include <stdio.h>
#include <bits/stat.h>
#include <sys/stat.h>
int stat_1(char *filename, struct stat *stat_buf)
{
return __xstat(1, filename, stat_buf); // extern int __xstat (...) defined in sys/stat.h
}
char * test(const char *filename) {
char *result;
stat stat_buf; // struct stat defined in bits/stat.h
printf("DO something here");
if ( stat_1(filename, &sbuf) == -1 ) {
printf("DO something here");
}
return result;
}
int main() {
const char *fileName = "file.txt";
test(fileName);
return 0;
}
You should be calling stat see https://linux.die.net/man/2/stat. Not __xstat.
Interacting with names that start with __ is almost always a sign you are doing something wrong. They are under the hood implementation things
For stat and its associated struct, you should likely be includeing:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

How do I find instances of a file in directory/sub-directories in C?

So I have a program, and with a passed filename, I need to find/open all files with that name that exist in the current directory and all sub-directories.
I do not know the names of the subdirectories. I do not care about their names or any other files, I just need to be able to open all files with the passed name.
Thanks!
Here's an example with ntfw().
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
static int display_info(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf){
char *fileName = "findMe.txt";
/* fpath holds the full path of the file from the specified starting directory */
if ( strstr(fpath, fileName) ){
printf("Match found!\n");
}
return 0;
}
int main(int argc, char **argv){
/* If a starting directory isn't specified, use the current dir */
if (nftw((argc < 2) ? "." : argv[1], display_info, 20, 0) == -1) {
perror("nftw");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

Resources