I've been trying to implement the 'ls' command using sys calls in C, but I've come upon a slight problem. No matter what I try, I just can't seem to sort the directory entries.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <dirent.h>
#include <sys/types.h>
#define BUFF_MAX 1024
int entry_cmp(const void* a, const void* b){
struct dirent* entry_a = (struct dirent*) a;
struct dirent* entry_b = (struct dirent*) b;
return strcasecmp(entry_a->d_name, entry_b->d_name);
}
void ls(const char* dirname){
DIR* dir = opendir(dirname);
struct dirent* entry;
struct dirent* entries[BUFF_MAX];
unsigned entry_count = 0;
while((entry = readdir(dir)) != NULL){
if(entry->d_name[0] == '.')
continue;
entries[entry_count++] = entry;
}
qsort(entries, entry_count, sizeof(struct dirent*), &entry_cmp);
for(unsigned i = 0; i < entry_count; i++){
entry = entries[i];
printf("%s ", entry->d_name);
}
printf("\n");
closedir(dir);
}
I've tried to analyze what's going on under the hood using gdb, but it seems to give me gibberish for the directory entry names.
entry_cmp (a=0x7fffffffb160, b=0x7fffffffb168) at Ex-2/myls.c:89
89 struct dirent* entry_a = (struct dirent*) a;
(gdb) step
90 struct dirent* entry_b = (struct dirent*) b;
(gdb) step
91 return strcasecmp(entry_a->d_name, entry_b->d_name);
(gdb) p entry_a->d_name
$3 = "UUU\000\000\250\223UUUU\000\000\300\223UUUU\000\000ؓUUUU\000\000\360\223UUUU\000\000\b\224UUUU\000\000 \224UUUU\000\000\070\224UUUU\000\000P\224UUUU", '\000' <repeats 188 times>
(gdb) p entry_b->d_name
$4 = "UUU\000\000\300\223UUUU\000\000ؓUUUU\000\000\360\223UUUU\000\000\b\224UUUU\000\000 \224UUUU\000\000\070\224UUUU\000\000P\224UUUU", '\000' <repeats 196 times>
What is the reason for the sorting not occuring?
From the readdir
manual page on the returned pointer:
On success,
readdir()
returns a pointer to adirent
structure. (This structure may be statically allocated; do not attempt tofree(3)
it.)
The important note here is the one about "statically allocated". That means the readdir
function might have only one single dirent
structure, and it continuously return a pointer to this same object. This could be easily checked in the debugger by printing the value of all pointers.
That means you need to copy the structure, not the pointer.
The solution is to change entries
to be an array of structure object, not pointers, and then copy the returned structure:
// ...
struct dirent entries[BUFF_MAX]; // Array of objects, not pointers
// ...
entries[entry_count++] = *entry; // Copy the object, not the pointer
// ...