daniel_p
daniel_p

Reputation: 31

Counting files for each subdirectory - program in C (POSIX)

I'm a complete beginner in programming in C using POSIX. I'm trying to write a program which counts files in each subdirectory starting from the directory given in environmental variable. For each subdirectory the program should write to stdout a short message:

Directory: name_of_dir
Files: num_of_files

I've tried to write such a program with nftw().


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define _XOPEN_SOURCE 500
#include <ftw.h>

#define MAXFD 20

int files = 0; // files count for each subdirectory
int files_main = 0; // files count for the directory from env variable DIR
int k = 0;
int l = 0;

char prev_dir[100];
char current_dir[100];

void print_dir() // helper function to print current directory
{
  char current_dir[100];
  getcwd(current_dir, 100);

  printf("Directory: %s \n", current_dir);
}

void print_files() // helper function to print number of files
{
  printf("Files: %d \n", files);
}

int search(const char *name, const struct stat *s, int type, struct FTW *f)
{

  getcwd(current_dir, 100);

  if(strcmp(prev_dir, current_dir) != 0 && l == 1) // check if the directory has changed, l as a flag to omit printing the same directory path more than once
  {
    l = 0;
    print_files();
    files = 0;
    strcpy(prev_dir, current_dir);
  }

  if(strcmp(current_dir, getenv("DIR")) == 0) // counting files in directory from env variable DIR
  {
    switch (type)
    {
      case FTW_F: files_main++; break;
    }
  }

  else // counting files for each subdirectory
  {
    getcwd(prev_dir, 100);
    if(l==0)
    {
      print_dir();
      l = 1;
    }

    switch (type)
    {
      case FTW_F: files++; break;
    }
  }
  return 0;
}

int main(int argc, char ** argv)
{
  strcpy(prev_dir, getenv("DIR")); // getting the dir from env variable

  nftw(getenv("DIR"), search, depth, FTW_CHDIR);
  printf("Directory: %s \n", getenv("DIR"));
  printf("Files: %d \n", files_main);

  return EXIT_SUCCESS;
}

This is the output of "find [...] type -f" for the directory in env variable "DIR":

./.DS_Store
./playground2
./playground3
./playground
./playground.c
./test_dir1/test1.txt
./test_dir1/test2.txt
./Makefile
./playground3.c
./test_file.txt
./playground2.c
./test_dir2/test3.tst
./dir1/p.tet
./dir1/sfsdfsdf/dfsd.txt
./dir1/ofi/test
./dir1/ofi/test2
./link_by_link_function

and this is the output when I run the C program:


$ ./playground2

Directory: /Users/<user>/Desktop/C POSIX/Playground 
Files: 5 
Directory: /Users/<user>/Desktop/C POSIX/Playground/test_dir1 
Files: 2 
Directory: /Users/<user>/C POSIX/Playground 
Files: 4 
Directory: /Users/<user>/C POSIX/Playground/test_dir2 
Files: 1 
Directory: /Users/<user>/C POSIX/Playground/dir1 
Files: 1 
Directory: /Users/<user>/Desktop/C POSIX/Playground/dir1/sfsdfsdf 
Files: 1 
Directory: /Users/<user>/Desktop/C POSIX/Playground/dir1/ofi 
Files: 2 
Directory: /Users/<user>/Desktop/C POSIX/Playground 
Files: 1 
Directory: /Users/<user>/Desktop/C POSIX/test_dir1 
Files: 2 
Directory: /Users/<user>/Desktop/C POSIX/test_dir2 
Files: 1 
Directory: /Users/<user>/Desktop/C POSIX 
Files: 19 

The program doesn't work as intended, which is obvious after looking at the output. The "files_main" counter works also the counters for subdirectories of [...]/Playground/ seem to work fine. The problem is the [...]/Playground/ directory itself. The counter "files" is reset after detecting the change in a directory which is not a good solution when the program enters [...]/Playground directory multiple times (at least this is what I have concluded at this point).

I've tried to use FTW_CHDIR and FTW_DEPTH flags but the output showed only the directory from env variable "DIR" with count == 0;

At this point, I am lost and have no idea how to write this program in a different way to accomplish the task.

Any ideas/help would be appreciated :) .

Upvotes: 0

Views: 310

Answers (1)

dash-o
dash-o

Reputation: 14468

Ideally, you want Breadth-first search (BFS) to do the counting. With BFS, each directory is processed before going to the next directory (down or up). Unfortunately, nftw is implementing depth-search first (DFS).

You can work around the counting limitation using the FTW structure (parameter f to the search function). It includes the member 'level'. Instead of keep a single counter, which will reset to zero on folder change, you can maintain an array of counters, one per each level.

Also note that the search function is passed enough information so that no need to keep state (current_dir, prev_dir, ...), detect changes, etc.

static int file_count[50] ; // Assuming big enough.

static int search(const char *name, const struct stat *s, int type, struct FTW *f)
{
   static int file_count[50] ;
   // Count
   if ( f->level < 50 ) { file_count[f->level]++ ;} ;
   if ( type == FTW_DP ) {
       // Last entry for folder
       printf("Folder: %s files: %d\n", name, file_count[f->level+1]) ;
       file_count[f->level+1] = 0 ;
   } ;
   return 0 ;
}

main (...) 
{
   Invoke nftw with FTW_DP
}

Links:

Upvotes: 1

Related Questions