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

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;
}

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 qsort a dirent in C

Brand new to C and finding it confusing. What I really want to know about, is taking two separate pieces of code and getting them to work together.
Here's some code that simply lists the contents of the current directory:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int
main (void)
{
DIR *dp;
struct dirent *ep;
dp = opendir ("./");
if (dp != NULL)
{
while (ep = readdir (dp))
puts (ep->d_name);
(void) closedir (dp);
}
else
perror ("Couldn't open the directory");
return 0;
}
The output is unsorted
Here's some code that sorts an array by length of elements, using a quick sort algorithm:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *array[] = { "XX", "YYY", "Z" };
#define N (sizeof(array) / sizeof(array[0]))
int
cmp(const void *a, const void *b)
{
size_t lena = strlen(*(const char **)a);
size_t lenb = strlen(*(const char **)b);
return lena < lenb ? -1 : lena > lenb;
}
int
main()
{
size_t i;
qsort(array, N, sizeof(array[0]), cmp);
for (i = 0; i < N; i++)
printf("%s\n", array[i]);
}
I'm sure there are better ways to do this, but for purely academic reasons, I'd like to use the output of the first function (directory contents) as an input to the last function (sort by length).
You could store the dirent objects inside an array which you could then pass into qsort.
The function cmp should be modified to compare the d_name element inside the dirent pointers. like so
int cmp(const *a, const void *b)
{
size_t lena = strlen(((struct dirent *) a)->d_name);
size_t lenb = strlen(((struct dirent *) b)->d_name);
return lena < lenb ? -1 : lena > lenb;
}

File copying using ftw()

Below code lists out all files in a directory, can somebody tell me how to copy these files/directory to another location provided using command line argument.
I want to return the name from the list function, the problem is ftw(,list,) takes the return type as int. Since name char, how can i return it?
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include<string.h>
int list(const char *name, const struct stat *status, int type);
int main(int argc, char *argv[]) {
if(argc == 1)
ftw(".", list, 1);
else
ftw(argv[1], list, 1);
return 0;
}
int list(const char *name, const struct stat *status, int type) {
if(type == FTW_NS)
return 0;
if(type == FTW_F)
if(type == FTW_D && strcmp(".", name) != 0)
return 0;
}

C - Scandir only fill namelist with folders

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;
}

How to implement custom compar() for scandir()

I've been reading the man pages of scandir(), alphasort() and have evidently crammed them all.
But still can't figure out how to implement a custom comparision function.
Here's my code:
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
int mySort(char*, char*);
int (*fnPtr)(char*, char*);
int main(){
struct dirent **entryList;
fnPtr = &mySort;
int count = scandir(".",&entryList,NULL,fnptr);
for(count--;count>=0;count--){
printf("%s\n",entryList[count]->d_name);
}
return 0;
}
int mySort(const void* a, const void* b){
char *aNew, *bNew;
if(a[0] == '.'){
*aNew = removeDot(a);
}
else{
aNew = a;
}
if(b[0] == '.'){
*bNew = removeDot(b);
}
else{
bNew = b;
}
return alphasort(aNew, bNew);
}
Easy to see that am trying to alphabetically sort file names irrespective of hidden and normal files (leading '.').
But a computer will always do what you tell it to but not what you want it to.
The sort routine mySort is the issue. This compare function needs to be of type int (*)(const struct dirent **, const struct dirent **). For example:
int mySort(const struct dirent **e1, const struct dirent **e2) {
const char *a = (*e1)->d_name;
const char *b = (*e2)->d_name;
return strcmp(a, b);
}
Recommend changing to
int mySort(const struct dirent **e1, const struct dirent **e2);
int (*fnPtr)(const struct dirent **e1, const struct dirent **e2);

Resources