C Recursive Opendir Wrapper to Sort Directories First (ascending/descending)












0












$begingroup$


A recent post on StackOverflow about a recursive directory listing which produced an unsorted mixed file/directory list, sparked the stray thought of "What would it take to write a wrapper for the recursive call to produce a sorted listing with directories sorted first?" (this rabbit trail followed...)



In addition to the general code review for any glaring efficiency bottlenecks or obvious mistakes, a specific question would be does it need a check for the number of open file-descriptors, similar to the ftw/nftw parameter nopenfd.



The following example takes the directory path to list as the first argument (defaulting to "." if none given) to provide directory listing sorted ascending with the directories listed first, and optionally a second argument (it doesn't matter what it is) that triggers a descending sort with the directories listed first.



The wrapper function listdir takes the path as its first parameter and a function pointer to the qsort compare function to be used and returns an allocated array of pointers to char with a sentinel NULL marking the end of pointers pointing to an allocated and filled filename. Since the pointer to pointer was declared and initially allocated in the wrapper, calling the actual listdir_read function required just sucking it up and becoming a 3-star programmer (if there is a better way to handle this, that would be another point, but it seemed justified here)



The rest is commented and fairly self-explanatory. The qsort compare functions, just iterate past any leading "." or ".." and checks whether either of the paths being compared contains a second directory separator while the other does not and sorts the directory before the filename. Otherwise it is just a simple strcmp of the adjacent filenames.



The code:



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>

#define INITDSZ 64 /* initial number of pointers to allocate */

#ifndef PATH_MAX /* fallback definition for PATH_MAX */
#define PATH_MAX 4096
#endif

/* file/dir name comparison - ascending (sort dirs first) */
int sort_ascending (const void *a, const void *b) {
const char *pa = *(char * const *)a,
*pb = *(char * const *)b,
*hasdira = NULL,
*hasdirb = NULL;

while (*pa == '.') /* scan past "." and ".." */
pa++;
while (*pb == '.')
pb++;

hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
hasdirb = strchr (pb + 1, '/');

if (hasdira && !hasdirb) /* sort dirs before files */
return -1;
else if (!hasdira && hasdirb)
return 1;

return strcmp (*(char * const *)a, *(char * const *)b);
}

/* file/dir name comparison - descending (sort dirs first) */
int sort_descending (const void *a, const void *b) {
const char *pa = *(char * const *)a,
*pb = *(char * const *)b,
*hasdira = NULL,
*hasdirb = NULL;

while (*pa == '.') /* scan past "." and ".." */
pa++;
while (*pb == '.')
pb++;

hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
hasdirb = strchr (pb + 1, '/');

if (hasdira && !hasdirb) /* sort dirs before files */
return -1;
else if (!hasdira && hasdirb)
return 1;

return strcmp (*(char * const *)b, *(char * const *)a);
}

/* listdir_read - recursive read of directory storing entires in contents.
* listdir_read recursively loops over directory entries beginning with
* the last directory entry in contents. nptrs is a pointer to the currently
* allocated number of pointers in contents and n is the current used number
* of pointers. storage is allocated for each file/directory and each entry
* added to contents to preserve entries for sorting, and each diretory is
* recursed into to gather subdirectory entries. reallocation occurs as
* needed.
*/
void listdir_read (char ***contents, size_t *nptrs, size_t *n)
{
char *path = (*contents)[*n - 1]; /* pointer to current path */
DIR *dir;
struct dirent *entry;

if (!(dir = opendir(path))) { /* open/validate directory */
perror ("opendir-path not found");
return;
}

while ((entry = readdir(dir))) { /* loop over each entry */

char *name = entry->d_name;
size_t len = strlen (name),
pathlen = strlen (path),
entrylen = pathlen + len + 1; /* +1 for '/' */

if (*n + 1 == *nptrs) { /* realloc, preserving sentinel NULL */
void *tmp = realloc (*contents, 2 * *nptrs * sizeof **contents);
if (!tmp) { /* validate */
perror ("listdir_read() realloc-*contents");
return;
}
*contents = tmp; /* assign new block, zero pointers */
memset (*contents + *nptrs, 0, *nptrs * sizeof **contents);
*nptrs *= 2; /* update number of allocated pointers */
}

if (entry->d_type == DT_DIR) /* if "." or ".." skip */
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;

(*contents)[*n] = malloc (entrylen + 1); /* allocate storage */
if (!(*contents)[*n]) { /* validate */
perror ("listdir_read() malloc-(*contents)[*n]");
return;
}

sprintf ((*contents)[(*n)++], "%s/%s", path, name); /* fill entry */

if (entry->d_type == DT_DIR) /* if is directory, recurse */
listdir_read (contents, nptrs, n);
}
closedir (dir); /* close directory */
}

/* wrapper for listdir_read, takes path and qsort compare function.
* returns allocated/sorted pointers to entries on success, NULL otherwise.
*/
char **listdir (char *path, int (*cmp)(const void*, const void*))
{
size_t len, n = 0, nptrs = INITDSZ;
char **contents = calloc (nptrs, sizeof *contents); /* allocate nptrs */

if (!contents) { /* validate */
perror ("listdir() calloc-contents");
return NULL;
}

len = strlen (path);
contents[n] = malloc (len + 1); /* allocate storage for 1st entry */
if (!contents[n]) { /* validate */
perror ("listdir() malloc-contents[n]");
return NULL;
}
strcpy (contents[n++], path); /* copy path as first entry */

listdir_read (&contents, &nptrs, &n); /* call listdir_read */

qsort (contents, n, sizeof *contents, cmp); /* sort contents */

return contents;
}

/* read path provided as argv[1] or present working directory by default.
* sort ascending with directories sorted first by default, or descending
* if any agrv[2] provided.
*/
int main (int argc, char **argv) {

char path[PATH_MAX],
**contents = NULL;

if (argc > 1)
strcpy (path, argv[1]);
else
*path = '.', path[1] = 0;

if (argc > 2)
contents = listdir (path, sort_descending);
else
contents = listdir (path, sort_ascending);

if (contents) {
char **p = contents;
while (*p) {
puts (*p);
free (*p++); /* free entries */
}
free (contents); /* free pointers */
}

return 0;
}


valgrind gives the code a clean bill of health on memory handling (though I've only run it against 15 or so subdirectories with ~5500 files). The memory required was approximately 1.8M for that number of files and directories. Execution time seems quite good.



Look things over and let me have the good, the bad and the ugly.










share|improve this question









$endgroup$

















    0












    $begingroup$


    A recent post on StackOverflow about a recursive directory listing which produced an unsorted mixed file/directory list, sparked the stray thought of "What would it take to write a wrapper for the recursive call to produce a sorted listing with directories sorted first?" (this rabbit trail followed...)



    In addition to the general code review for any glaring efficiency bottlenecks or obvious mistakes, a specific question would be does it need a check for the number of open file-descriptors, similar to the ftw/nftw parameter nopenfd.



    The following example takes the directory path to list as the first argument (defaulting to "." if none given) to provide directory listing sorted ascending with the directories listed first, and optionally a second argument (it doesn't matter what it is) that triggers a descending sort with the directories listed first.



    The wrapper function listdir takes the path as its first parameter and a function pointer to the qsort compare function to be used and returns an allocated array of pointers to char with a sentinel NULL marking the end of pointers pointing to an allocated and filled filename. Since the pointer to pointer was declared and initially allocated in the wrapper, calling the actual listdir_read function required just sucking it up and becoming a 3-star programmer (if there is a better way to handle this, that would be another point, but it seemed justified here)



    The rest is commented and fairly self-explanatory. The qsort compare functions, just iterate past any leading "." or ".." and checks whether either of the paths being compared contains a second directory separator while the other does not and sorts the directory before the filename. Otherwise it is just a simple strcmp of the adjacent filenames.



    The code:



    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dirent.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <unistd.h>

    #define INITDSZ 64 /* initial number of pointers to allocate */

    #ifndef PATH_MAX /* fallback definition for PATH_MAX */
    #define PATH_MAX 4096
    #endif

    /* file/dir name comparison - ascending (sort dirs first) */
    int sort_ascending (const void *a, const void *b) {
    const char *pa = *(char * const *)a,
    *pb = *(char * const *)b,
    *hasdira = NULL,
    *hasdirb = NULL;

    while (*pa == '.') /* scan past "." and ".." */
    pa++;
    while (*pb == '.')
    pb++;

    hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
    hasdirb = strchr (pb + 1, '/');

    if (hasdira && !hasdirb) /* sort dirs before files */
    return -1;
    else if (!hasdira && hasdirb)
    return 1;

    return strcmp (*(char * const *)a, *(char * const *)b);
    }

    /* file/dir name comparison - descending (sort dirs first) */
    int sort_descending (const void *a, const void *b) {
    const char *pa = *(char * const *)a,
    *pb = *(char * const *)b,
    *hasdira = NULL,
    *hasdirb = NULL;

    while (*pa == '.') /* scan past "." and ".." */
    pa++;
    while (*pb == '.')
    pb++;

    hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
    hasdirb = strchr (pb + 1, '/');

    if (hasdira && !hasdirb) /* sort dirs before files */
    return -1;
    else if (!hasdira && hasdirb)
    return 1;

    return strcmp (*(char * const *)b, *(char * const *)a);
    }

    /* listdir_read - recursive read of directory storing entires in contents.
    * listdir_read recursively loops over directory entries beginning with
    * the last directory entry in contents. nptrs is a pointer to the currently
    * allocated number of pointers in contents and n is the current used number
    * of pointers. storage is allocated for each file/directory and each entry
    * added to contents to preserve entries for sorting, and each diretory is
    * recursed into to gather subdirectory entries. reallocation occurs as
    * needed.
    */
    void listdir_read (char ***contents, size_t *nptrs, size_t *n)
    {
    char *path = (*contents)[*n - 1]; /* pointer to current path */
    DIR *dir;
    struct dirent *entry;

    if (!(dir = opendir(path))) { /* open/validate directory */
    perror ("opendir-path not found");
    return;
    }

    while ((entry = readdir(dir))) { /* loop over each entry */

    char *name = entry->d_name;
    size_t len = strlen (name),
    pathlen = strlen (path),
    entrylen = pathlen + len + 1; /* +1 for '/' */

    if (*n + 1 == *nptrs) { /* realloc, preserving sentinel NULL */
    void *tmp = realloc (*contents, 2 * *nptrs * sizeof **contents);
    if (!tmp) { /* validate */
    perror ("listdir_read() realloc-*contents");
    return;
    }
    *contents = tmp; /* assign new block, zero pointers */
    memset (*contents + *nptrs, 0, *nptrs * sizeof **contents);
    *nptrs *= 2; /* update number of allocated pointers */
    }

    if (entry->d_type == DT_DIR) /* if "." or ".." skip */
    if (!strcmp(name, ".") || !strcmp(name, ".."))
    continue;

    (*contents)[*n] = malloc (entrylen + 1); /* allocate storage */
    if (!(*contents)[*n]) { /* validate */
    perror ("listdir_read() malloc-(*contents)[*n]");
    return;
    }

    sprintf ((*contents)[(*n)++], "%s/%s", path, name); /* fill entry */

    if (entry->d_type == DT_DIR) /* if is directory, recurse */
    listdir_read (contents, nptrs, n);
    }
    closedir (dir); /* close directory */
    }

    /* wrapper for listdir_read, takes path and qsort compare function.
    * returns allocated/sorted pointers to entries on success, NULL otherwise.
    */
    char **listdir (char *path, int (*cmp)(const void*, const void*))
    {
    size_t len, n = 0, nptrs = INITDSZ;
    char **contents = calloc (nptrs, sizeof *contents); /* allocate nptrs */

    if (!contents) { /* validate */
    perror ("listdir() calloc-contents");
    return NULL;
    }

    len = strlen (path);
    contents[n] = malloc (len + 1); /* allocate storage for 1st entry */
    if (!contents[n]) { /* validate */
    perror ("listdir() malloc-contents[n]");
    return NULL;
    }
    strcpy (contents[n++], path); /* copy path as first entry */

    listdir_read (&contents, &nptrs, &n); /* call listdir_read */

    qsort (contents, n, sizeof *contents, cmp); /* sort contents */

    return contents;
    }

    /* read path provided as argv[1] or present working directory by default.
    * sort ascending with directories sorted first by default, or descending
    * if any agrv[2] provided.
    */
    int main (int argc, char **argv) {

    char path[PATH_MAX],
    **contents = NULL;

    if (argc > 1)
    strcpy (path, argv[1]);
    else
    *path = '.', path[1] = 0;

    if (argc > 2)
    contents = listdir (path, sort_descending);
    else
    contents = listdir (path, sort_ascending);

    if (contents) {
    char **p = contents;
    while (*p) {
    puts (*p);
    free (*p++); /* free entries */
    }
    free (contents); /* free pointers */
    }

    return 0;
    }


    valgrind gives the code a clean bill of health on memory handling (though I've only run it against 15 or so subdirectories with ~5500 files). The memory required was approximately 1.8M for that number of files and directories. Execution time seems quite good.



    Look things over and let me have the good, the bad and the ugly.










    share|improve this question









    $endgroup$















      0












      0








      0





      $begingroup$


      A recent post on StackOverflow about a recursive directory listing which produced an unsorted mixed file/directory list, sparked the stray thought of "What would it take to write a wrapper for the recursive call to produce a sorted listing with directories sorted first?" (this rabbit trail followed...)



      In addition to the general code review for any glaring efficiency bottlenecks or obvious mistakes, a specific question would be does it need a check for the number of open file-descriptors, similar to the ftw/nftw parameter nopenfd.



      The following example takes the directory path to list as the first argument (defaulting to "." if none given) to provide directory listing sorted ascending with the directories listed first, and optionally a second argument (it doesn't matter what it is) that triggers a descending sort with the directories listed first.



      The wrapper function listdir takes the path as its first parameter and a function pointer to the qsort compare function to be used and returns an allocated array of pointers to char with a sentinel NULL marking the end of pointers pointing to an allocated and filled filename. Since the pointer to pointer was declared and initially allocated in the wrapper, calling the actual listdir_read function required just sucking it up and becoming a 3-star programmer (if there is a better way to handle this, that would be another point, but it seemed justified here)



      The rest is commented and fairly self-explanatory. The qsort compare functions, just iterate past any leading "." or ".." and checks whether either of the paths being compared contains a second directory separator while the other does not and sorts the directory before the filename. Otherwise it is just a simple strcmp of the adjacent filenames.



      The code:



      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <dirent.h>
      #include <errno.h>
      #include <sys/types.h>
      #include <unistd.h>

      #define INITDSZ 64 /* initial number of pointers to allocate */

      #ifndef PATH_MAX /* fallback definition for PATH_MAX */
      #define PATH_MAX 4096
      #endif

      /* file/dir name comparison - ascending (sort dirs first) */
      int sort_ascending (const void *a, const void *b) {
      const char *pa = *(char * const *)a,
      *pb = *(char * const *)b,
      *hasdira = NULL,
      *hasdirb = NULL;

      while (*pa == '.') /* scan past "." and ".." */
      pa++;
      while (*pb == '.')
      pb++;

      hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
      hasdirb = strchr (pb + 1, '/');

      if (hasdira && !hasdirb) /* sort dirs before files */
      return -1;
      else if (!hasdira && hasdirb)
      return 1;

      return strcmp (*(char * const *)a, *(char * const *)b);
      }

      /* file/dir name comparison - descending (sort dirs first) */
      int sort_descending (const void *a, const void *b) {
      const char *pa = *(char * const *)a,
      *pb = *(char * const *)b,
      *hasdira = NULL,
      *hasdirb = NULL;

      while (*pa == '.') /* scan past "." and ".." */
      pa++;
      while (*pb == '.')
      pb++;

      hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
      hasdirb = strchr (pb + 1, '/');

      if (hasdira && !hasdirb) /* sort dirs before files */
      return -1;
      else if (!hasdira && hasdirb)
      return 1;

      return strcmp (*(char * const *)b, *(char * const *)a);
      }

      /* listdir_read - recursive read of directory storing entires in contents.
      * listdir_read recursively loops over directory entries beginning with
      * the last directory entry in contents. nptrs is a pointer to the currently
      * allocated number of pointers in contents and n is the current used number
      * of pointers. storage is allocated for each file/directory and each entry
      * added to contents to preserve entries for sorting, and each diretory is
      * recursed into to gather subdirectory entries. reallocation occurs as
      * needed.
      */
      void listdir_read (char ***contents, size_t *nptrs, size_t *n)
      {
      char *path = (*contents)[*n - 1]; /* pointer to current path */
      DIR *dir;
      struct dirent *entry;

      if (!(dir = opendir(path))) { /* open/validate directory */
      perror ("opendir-path not found");
      return;
      }

      while ((entry = readdir(dir))) { /* loop over each entry */

      char *name = entry->d_name;
      size_t len = strlen (name),
      pathlen = strlen (path),
      entrylen = pathlen + len + 1; /* +1 for '/' */

      if (*n + 1 == *nptrs) { /* realloc, preserving sentinel NULL */
      void *tmp = realloc (*contents, 2 * *nptrs * sizeof **contents);
      if (!tmp) { /* validate */
      perror ("listdir_read() realloc-*contents");
      return;
      }
      *contents = tmp; /* assign new block, zero pointers */
      memset (*contents + *nptrs, 0, *nptrs * sizeof **contents);
      *nptrs *= 2; /* update number of allocated pointers */
      }

      if (entry->d_type == DT_DIR) /* if "." or ".." skip */
      if (!strcmp(name, ".") || !strcmp(name, ".."))
      continue;

      (*contents)[*n] = malloc (entrylen + 1); /* allocate storage */
      if (!(*contents)[*n]) { /* validate */
      perror ("listdir_read() malloc-(*contents)[*n]");
      return;
      }

      sprintf ((*contents)[(*n)++], "%s/%s", path, name); /* fill entry */

      if (entry->d_type == DT_DIR) /* if is directory, recurse */
      listdir_read (contents, nptrs, n);
      }
      closedir (dir); /* close directory */
      }

      /* wrapper for listdir_read, takes path and qsort compare function.
      * returns allocated/sorted pointers to entries on success, NULL otherwise.
      */
      char **listdir (char *path, int (*cmp)(const void*, const void*))
      {
      size_t len, n = 0, nptrs = INITDSZ;
      char **contents = calloc (nptrs, sizeof *contents); /* allocate nptrs */

      if (!contents) { /* validate */
      perror ("listdir() calloc-contents");
      return NULL;
      }

      len = strlen (path);
      contents[n] = malloc (len + 1); /* allocate storage for 1st entry */
      if (!contents[n]) { /* validate */
      perror ("listdir() malloc-contents[n]");
      return NULL;
      }
      strcpy (contents[n++], path); /* copy path as first entry */

      listdir_read (&contents, &nptrs, &n); /* call listdir_read */

      qsort (contents, n, sizeof *contents, cmp); /* sort contents */

      return contents;
      }

      /* read path provided as argv[1] or present working directory by default.
      * sort ascending with directories sorted first by default, or descending
      * if any agrv[2] provided.
      */
      int main (int argc, char **argv) {

      char path[PATH_MAX],
      **contents = NULL;

      if (argc > 1)
      strcpy (path, argv[1]);
      else
      *path = '.', path[1] = 0;

      if (argc > 2)
      contents = listdir (path, sort_descending);
      else
      contents = listdir (path, sort_ascending);

      if (contents) {
      char **p = contents;
      while (*p) {
      puts (*p);
      free (*p++); /* free entries */
      }
      free (contents); /* free pointers */
      }

      return 0;
      }


      valgrind gives the code a clean bill of health on memory handling (though I've only run it against 15 or so subdirectories with ~5500 files). The memory required was approximately 1.8M for that number of files and directories. Execution time seems quite good.



      Look things over and let me have the good, the bad and the ugly.










      share|improve this question









      $endgroup$




      A recent post on StackOverflow about a recursive directory listing which produced an unsorted mixed file/directory list, sparked the stray thought of "What would it take to write a wrapper for the recursive call to produce a sorted listing with directories sorted first?" (this rabbit trail followed...)



      In addition to the general code review for any glaring efficiency bottlenecks or obvious mistakes, a specific question would be does it need a check for the number of open file-descriptors, similar to the ftw/nftw parameter nopenfd.



      The following example takes the directory path to list as the first argument (defaulting to "." if none given) to provide directory listing sorted ascending with the directories listed first, and optionally a second argument (it doesn't matter what it is) that triggers a descending sort with the directories listed first.



      The wrapper function listdir takes the path as its first parameter and a function pointer to the qsort compare function to be used and returns an allocated array of pointers to char with a sentinel NULL marking the end of pointers pointing to an allocated and filled filename. Since the pointer to pointer was declared and initially allocated in the wrapper, calling the actual listdir_read function required just sucking it up and becoming a 3-star programmer (if there is a better way to handle this, that would be another point, but it seemed justified here)



      The rest is commented and fairly self-explanatory. The qsort compare functions, just iterate past any leading "." or ".." and checks whether either of the paths being compared contains a second directory separator while the other does not and sorts the directory before the filename. Otherwise it is just a simple strcmp of the adjacent filenames.



      The code:



      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <dirent.h>
      #include <errno.h>
      #include <sys/types.h>
      #include <unistd.h>

      #define INITDSZ 64 /* initial number of pointers to allocate */

      #ifndef PATH_MAX /* fallback definition for PATH_MAX */
      #define PATH_MAX 4096
      #endif

      /* file/dir name comparison - ascending (sort dirs first) */
      int sort_ascending (const void *a, const void *b) {
      const char *pa = *(char * const *)a,
      *pb = *(char * const *)b,
      *hasdira = NULL,
      *hasdirb = NULL;

      while (*pa == '.') /* scan past "." and ".." */
      pa++;
      while (*pb == '.')
      pb++;

      hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
      hasdirb = strchr (pb + 1, '/');

      if (hasdira && !hasdirb) /* sort dirs before files */
      return -1;
      else if (!hasdira && hasdirb)
      return 1;

      return strcmp (*(char * const *)a, *(char * const *)b);
      }

      /* file/dir name comparison - descending (sort dirs first) */
      int sort_descending (const void *a, const void *b) {
      const char *pa = *(char * const *)a,
      *pb = *(char * const *)b,
      *hasdira = NULL,
      *hasdirb = NULL;

      while (*pa == '.') /* scan past "." and ".." */
      pa++;
      while (*pb == '.')
      pb++;

      hasdira = strchr (pa + 1, '/'); /* check for 2nd '/' */
      hasdirb = strchr (pb + 1, '/');

      if (hasdira && !hasdirb) /* sort dirs before files */
      return -1;
      else if (!hasdira && hasdirb)
      return 1;

      return strcmp (*(char * const *)b, *(char * const *)a);
      }

      /* listdir_read - recursive read of directory storing entires in contents.
      * listdir_read recursively loops over directory entries beginning with
      * the last directory entry in contents. nptrs is a pointer to the currently
      * allocated number of pointers in contents and n is the current used number
      * of pointers. storage is allocated for each file/directory and each entry
      * added to contents to preserve entries for sorting, and each diretory is
      * recursed into to gather subdirectory entries. reallocation occurs as
      * needed.
      */
      void listdir_read (char ***contents, size_t *nptrs, size_t *n)
      {
      char *path = (*contents)[*n - 1]; /* pointer to current path */
      DIR *dir;
      struct dirent *entry;

      if (!(dir = opendir(path))) { /* open/validate directory */
      perror ("opendir-path not found");
      return;
      }

      while ((entry = readdir(dir))) { /* loop over each entry */

      char *name = entry->d_name;
      size_t len = strlen (name),
      pathlen = strlen (path),
      entrylen = pathlen + len + 1; /* +1 for '/' */

      if (*n + 1 == *nptrs) { /* realloc, preserving sentinel NULL */
      void *tmp = realloc (*contents, 2 * *nptrs * sizeof **contents);
      if (!tmp) { /* validate */
      perror ("listdir_read() realloc-*contents");
      return;
      }
      *contents = tmp; /* assign new block, zero pointers */
      memset (*contents + *nptrs, 0, *nptrs * sizeof **contents);
      *nptrs *= 2; /* update number of allocated pointers */
      }

      if (entry->d_type == DT_DIR) /* if "." or ".." skip */
      if (!strcmp(name, ".") || !strcmp(name, ".."))
      continue;

      (*contents)[*n] = malloc (entrylen + 1); /* allocate storage */
      if (!(*contents)[*n]) { /* validate */
      perror ("listdir_read() malloc-(*contents)[*n]");
      return;
      }

      sprintf ((*contents)[(*n)++], "%s/%s", path, name); /* fill entry */

      if (entry->d_type == DT_DIR) /* if is directory, recurse */
      listdir_read (contents, nptrs, n);
      }
      closedir (dir); /* close directory */
      }

      /* wrapper for listdir_read, takes path and qsort compare function.
      * returns allocated/sorted pointers to entries on success, NULL otherwise.
      */
      char **listdir (char *path, int (*cmp)(const void*, const void*))
      {
      size_t len, n = 0, nptrs = INITDSZ;
      char **contents = calloc (nptrs, sizeof *contents); /* allocate nptrs */

      if (!contents) { /* validate */
      perror ("listdir() calloc-contents");
      return NULL;
      }

      len = strlen (path);
      contents[n] = malloc (len + 1); /* allocate storage for 1st entry */
      if (!contents[n]) { /* validate */
      perror ("listdir() malloc-contents[n]");
      return NULL;
      }
      strcpy (contents[n++], path); /* copy path as first entry */

      listdir_read (&contents, &nptrs, &n); /* call listdir_read */

      qsort (contents, n, sizeof *contents, cmp); /* sort contents */

      return contents;
      }

      /* read path provided as argv[1] or present working directory by default.
      * sort ascending with directories sorted first by default, or descending
      * if any agrv[2] provided.
      */
      int main (int argc, char **argv) {

      char path[PATH_MAX],
      **contents = NULL;

      if (argc > 1)
      strcpy (path, argv[1]);
      else
      *path = '.', path[1] = 0;

      if (argc > 2)
      contents = listdir (path, sort_descending);
      else
      contents = listdir (path, sort_ascending);

      if (contents) {
      char **p = contents;
      while (*p) {
      puts (*p);
      free (*p++); /* free entries */
      }
      free (contents); /* free pointers */
      }

      return 0;
      }


      valgrind gives the code a clean bill of health on memory handling (though I've only run it against 15 or so subdirectories with ~5500 files). The memory required was approximately 1.8M for that number of files and directories. Execution time seems quite good.



      Look things over and let me have the good, the bad and the ugly.







      performance c recursion memory-management






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 10 mins ago









      David C. RankinDavid C. Rankin

      23127




      23127






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213435%2fc-recursive-opendir-wrapper-to-sort-directories-first-ascending-descending%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213435%2fc-recursive-opendir-wrapper-to-sort-directories-first-ascending-descending%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Сан-Квентин

          8-я гвардейская общевойсковая армия

          Алькесар