sylar12
sylar12

Reputation: 107

Recursively search for a file in all subdirectories on Linux

I am trying to search for a file given as parameter through all the sub-directories. The problem with my code is that when it gets to a file which is not a directory it stops with perror("Error opening the directory\n");.

I can't find a way to get over this. I tried with another if(S_ISREG...), but it doesn't work.

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

void check_file_existence(char *dirName,char *file){
    struct stat *metadata;
    char name[1000];
    struct dirent *dirEntry;
    struct stat bufstat;

    DIR *dir;

    dir = opendir(dirName);
    if (dir==NULL)
    {
        perror("Error opening the directory\n");
        exit(1);
    }

    while ((dirEntry = readdir(dir))!=NULL){
        lstat(dirEntry->d_name,&bufstat);

        if(S_ISDIR(bufstat.st_mode)){
            if (strcmp(dirEntry->d_name,".")==0 || strcmp(dirEntry->d_name,"..")==0){
                continue;
            }
            sprintf(name,"%s/%s",dirName,dirEntry->d_name);
            printf("%s\n",name);
            check_file_existence(name,file);
        }       
    }

    closedir(dir);
}

int main(int argc,char **argv){
    if (argc!=3){
        perror("Number of arguments is wrong.\n");
        exit(1);
    }

    check_file_existence(argv[1],argv[2]);  
}

Upvotes: 3

Views: 507

Answers (1)

DrM
DrM

Reputation: 2515

Here is your code with some simplifications that incidentally resolve your bug, and two improvements. It now works to recursively search through a directory tree to find the first occurrence of a specified file.

We use the dirEntry structure to identify the file type and add the strcmp() to check for the specified file. Using d_type in the dirEntry structure is the simplest way to do identify the file type and consequently tends to reduce your bug rate.

We check the input for an extraneous trailing slash. An extra slash on the input will not stop anything but it makes the output less clear.

And, to facilitate debugging, we make copious use of printf() and add a routine to byte-dump the contents of the dirEntry structure to help you see what is happening in a more detailed way as it recurses and loops through the directories and files.

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

void debugbytes( char *s, unsigned short nlen, char dtype, char *nameptr ) {
  int n;
  printf( "\n%d %x ", nlen, dtype );
  switch( dtype )
    {
    case DT_BLK:
      printf( "BLK" );
      break;
    case DT_CHR:
      printf( "CHR" );
      break;
    case DT_DIR:
      printf( "DIR" );
      break;
    case DT_FIFO:
      printf( "FIFO" );
      break;
    case DT_LNK:
      printf( "LNK" );
      break;
    case DT_REG:
      printf( "REG" );
      break;
    case DT_SOCK:
      printf( "SOCK" );
      break;
    case DT_UNKNOWN:
      printf( "UNKOWN" );
      break;
    default:
      printf( "not recognized" );
    }
  printf( " %s :", nameptr );
  for (n = 0; n < nlen; n++ ) {
    printf( "%x", s[n] );
  }
  printf( "\n" );
}

void check_file_existence(char *dirName,char *file){

  DIR *dir;

  struct dirent *dirEntry;

  char name[1000];

  printf( "opening %s\n", dirName );

  dir = opendir(dirName);
  if (dir==NULL)
    {
      perror("Error opening the directory\n");
      exit(1);
    }

    while ((dirEntry = readdir(dir))!=NULL){

      debugbytes( (char *) dirEntry,  dirEntry->d_reclen, dirEntry->d_type, dirEntry->d_name );

      if ( dirEntry->d_type == DT_DIR ) {
        if (strcmp(dirEntry->d_name,".")==0 || strcmp(dirEntry->d_name,"..")==0){
        continue;
      }
      printf( "directory\n" );
      sprintf(name,"%s/%s",dirName,dirEntry->d_name);
      printf("\n%s\n",name);
      check_file_existence(name,file);
    }

    else if ( dirEntry->d_type == DT_REG ) {

      printf( "file %s/%s\n", dirName, (char *)dirEntry->d_name );

      if ( !strcmp( dirEntry->d_name, file ) ) {
        printf( "file found\n" );
        break;
      }
    }
  }

  closedir(dir);
}

int main(int argc,char **argv){
  char dirspec[256] = { 0 };
  int n;

    if (argc!=3){
        perror("Number of arguments is wrong.\n");
        exit(1);
    }

    n = strlen( argv[1] );
    while( (n > 1) && argv[1][n-1] == '/' ) n--; 

    strncpy(dirspec, argv[1], n );

    check_file_existence( dirspec, argv[2] );  
}

And here is a sample output:

$ ./temp1 gEDA/ 1206P.fp
opening gEDA
.
.
.
32 4 DIR Footprints :3b04250000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f2004466f6f747072696e7473004
directory
.
.
.
32 8 REG 1206P.fp :ffffff8084250000ffffffd0ffffffff12ffffffeb7afffffff77052200831323036502e6670054ffffffa7ffffffce8
file gEDA/Footprints/1206P.fp
file found

$

Upvotes: 1

Related Questions