diff options
Diffstat (limited to 'userland/libc')
| -rw-r--r-- | userland/libc/dirent/scandir.c | 60 | ||||
| -rw-r--r-- | userland/libc/include/stdlib.h | 2 | 
2 files changed, 62 insertions, 0 deletions
| diff --git a/userland/libc/dirent/scandir.c b/userland/libc/dirent/scandir.c index 564cbc8..3ac040a 100644 --- a/userland/libc/dirent/scandir.c +++ b/userland/libc/dirent/scandir.c @@ -1,12 +1,72 @@  #include <dirent.h> +#include <fcntl.h>  #include <stdlib.h>  #include <string.h> +#include <unistd.h>  int nop_compar(const struct dirent **d1, const struct dirent **d2) {    *d2 = *d1;    return 0;  } +// Differs from scandir in the sense that the namelist has its own way +// of allocating its entries(in this case it allocates it as a large +// chunk). As a result the usual free() operation can not be ran on +// the elements. +// +// Instead scandir_sane_free() should be used to deallocate the +// namelist. +int scandir_sane(const char *dir, struct dirent ***namelist, +                 int (*sel)(const struct dirent *), +                 int (*compar)(const struct dirent **, +                               const struct dirent **)) { +  if (!compar) { +    compar = nop_compar; +  } + +  int fd = open(dir, O_RDONLY); +  if (-1 == fd) { +    // TODO: Figure out what to set errno to +    return -1; +  } + +  size_t capacity = 128; +  size_t num_entries; +  struct dirent *chunk = NULL; +  for (;;) { +    chunk = realloc(chunk, sizeof(struct dirent) * capacity); +    int rc = pread(fd, chunk, sizeof(struct dirent) * capacity, 0); +    if (-1 == rc) { +      free(chunk); +      // TODO: Figure out what to set errno to +      return -1; +    } +    if (sizeof(struct dirent) * capacity == (size_t)rc) { +      // The directory **may** contain more entries. +      // Redo +      capacity *= 2; +      continue; +    } +    num_entries = rc / (sizeof(struct dirent)); +    break; +  } + +  *namelist = allocarray(num_entries, sizeof(struct dirent *)); + +  for (size_t i = 0; i < num_entries; i++) { +    (*namelist)[i] = &chunk[i]; +  } + +  close(fd); +  return num_entries; +} + +void scandir_sane_free(struct dirent **namelist) { +  free(namelist[0]); // First pointer is the start of the chunk in the +                     // current implementation +  free(namelist); +} +  int scandir(const char *dir, struct dirent ***namelist,              int (*sel)(const struct dirent *),              int (*compar)(const struct dirent **, const struct dirent **)) { diff --git a/userland/libc/include/stdlib.h b/userland/libc/include/stdlib.h index 7e23035..a389e8c 100644 --- a/userland/libc/include/stdlib.h +++ b/userland/libc/include/stdlib.h @@ -10,6 +10,8 @@ typedef size_t size_t; // only for 32 bit  void *malloc(size_t s);  void *calloc(size_t nelem, size_t elsize); +void *allocarray(size_t nmemb, size_t size); +void *reallocarray(void *ptr, size_t nmemb, size_t size);  void *realloc(void *ptr, size_t size);  void free(void *p);  char *getenv(const char *name); |