1

I'm trying to sort a current working directory, sub-directories, and then display them. My program behavior begins the process and works for the first sub-directory but none after that. A few prints are to check behavior of the code. While there is a complete working version of this on stackoverflow, I just want to better understand where my code logic is failing. Any help is greatly appreciated. Here is the code:

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

void sortArr(char array[256][256], int amt) {

  char temp[1000];
    for(int i=0; i<amt; i++) {
      for(int j=0; j<(amt-1-i); j++) {
    if(strcmp(array[j], array[j+1]) > 0) {
      strcpy(temp, array[j]);
      strcpy(array[j], array[j+1]);
      strcpy(array[j+1], temp);
    }
      }
    }

    printf("SortList: %s\n", array[0]);
}



void build(char *s_path, const int root) {
  char path[1000];
  strcat(path, s_path);
  int amt=0,index;
  struct dirent *dp;
  DIR *dir =opendir(path);
  char list[256][256];
  struct stat statbuf;
  
  if(!dir)
    return;

  while((dp =readdir(dir)) != NULL) {
    if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
      strcpy(list[amt], dp->d_name);
      amt++;
    }
  }
  
  sortArr(list, amt);

  for(int i=0; i <amt;i++) {
     for (index=0; index<root; index++)  {
    if (index%2 == 0 || index == 0)
      printf("%c", ' ');
    else
      printf(" ");
      }
    
    printf("%c %s\n", '-', list[i]);

    strcpy(path, s_path);
    strcat(path, "/");
    strcat(path, list[i]);
    stat(path, &statbuf);
    
    if((S_ISDIR(statbuf.st_mode)) != 0 && (S_ISREG(statbuf.st_mode)) ==0) {
      printf("DIR: %s\n", path);
      build(path,root +2);
    }
  }
  closedir(dir);
}

int main() {

  build(".", 0);
  
};
  • Note that the semicolon after the final `}` of `main()` is unwanted. The code fragment `if (index%2 == 0 || index == 0) printf("%c", ' '); else printf(" "); }` is a long-winded way of writing `putchar(' ');`. – Jonathan Leffler Oct 23 '22 at 04:51
  • You don't check that `stat()` succeeded. You should check that it succeeds. – Jonathan Leffler Oct 23 '22 at 04:57
  • Your first real problem is that you've no idea what's in path when you call build(), so concatenating on the end does nothing reliable. Add path[0] = '\0'; before the strcat(), or use strcpy(). However, that's not the only problem, but I've not yet fully characterized that. However, sometimes the incorrect path gets built and the call to stat() fails. – Jonathan Leffler Oct 23 '22 at 05:14
  • _[…Time passeth…]_ It may simply be that I'm running the code in a directory with too many files — hmmm, yes, the `src` directory has 278 entries, which is too big for the array of 256 entries. All hell would break loose because of that. You need to use dynamic memory allocation so that you can process the information accurately (and you need to free the memory you've allocated). The use of 64 KiB per level of directory is a problem too. (For my test, changing the first dimension of 256 to 356 sufficed — but that's simply a kludge if you need to handle arbitrarily big directories.) – Jonathan Leffler Oct 23 '22 at 05:14

1 Answers1

1

Based on my comments.

The key problem is that you've no idea what's in path when you call build(), so concatenating on the end does nothing reliable. Either add path[0] = '\0'; before the call to strcat(), or (simpler) use strcpy().

That fixes your problems as long as your directories do not have more than 256 entries (other than . and ..) in them.

If you do have bigger directories, you run into problems. Those would be resolved by using dynamic memory allocation — malloc() et al, and using strdup(), probably. That which you allocate you should free, too, of course.

You should check that the calls to stat() succeed, but they won't often fail. One time they could fail is if a file is removed between the when readdir() returns the name and the time when the code calls stat() for the file.

There are POSIX tools for scanning directories. These include:

Amended source code (including some uncommented upon changes):

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

static
void sortArr(char array[356][256], int amt)
{
    char temp[1000];
    for (int i = 0; i < amt; i++)
    {
        for (int j = 0; j < (amt - 1 - i); j++)
        {
            if (strcmp(array[j], array[j + 1]) > 0)
            {
                strcpy(temp, array[j]);
                strcpy(array[j], array[j + 1]);
                strcpy(array[j + 1], temp);
            }
        }
    }

    printf("SortList: %s\n", array[0]);
}

static
void build(const char *s_path, int root)
{
    char path[1000];
    strcpy(path, s_path);
    int amt = 0;
    struct dirent *dp;
    DIR *dir = opendir(path);
    char list[356][256];
    struct stat statbuf;

    if (!dir)
        return;

    while ((dp = readdir(dir)) != NULL)
    {
        if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
        {
            strcpy(list[amt], dp->d_name);
            amt++;
        }
    }

    sortArr(list, amt);

    for (int i = 0; i < amt; i++)
    {
        printf("%*s", root, "");
        /*for (int index = 0; index < root; index++)*/
        /*    putchar(' ');*/

        printf("%c %s\n", '-', list[i]);

        strcpy(path, s_path);
        strcat(path, "/");
        strcat(path, list[i]);
        if (stat(path, &statbuf) != 0)
        {
            fprintf(stderr, "failed to stat name '%s' (%d: %s)\n", path, errno, strerror(errno));
            continue;
        }

        if ((S_ISDIR(statbuf.st_mode)) != 0 && (S_ISREG(statbuf.st_mode)) == 0)
        {
            printf("DIR: %s\n", path);
            build(path, root + 2);
        }
    }
    closedir(dir);
}

int main(void)
{
    build(".", 0);
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278