Listing 2: CFileList member function definitions

/* cdir.cpp */
/* class to do some operation on all files that match masks */
/* task: maintain '*' and '?' in file masks */
/* author: Michal Niklas mniklas@computer.org */

// those headers include dir and file functions 
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>


#include "sflre.h"
#include "cdir.h"

bool CFileList::DefaultIgnoreCase = false;
/* for MS systems case doesn't matter while others systems
   are more sensitive */

const char DIR_SEPARATOR = '/';
// should work for MS systems and unices 

// I found that Borland C++ 5.02 have some problems with inline
// if you want do use real inline uncomment this:
// #define _inline inline
#define _inline

_inline void CFileList::Init()
  {
  SetIgnoreCase(DefaultIgnoreCase);
  m_recursive = false;
  } // CFileList::Init
  
// construcors 
_inline CFileList::CFileList() { Init();  }

_inline CFileList::CFileList(std::string mask)
  {
  Init();
  AddMask(mask);
  }

_inline CFileList::CFileList(char *masks[], int count)
  {
  Init();
  SetMasks(masks, count);
  }
// end constructors


_inline void CFileList::AddMask(std::string mask)
  {
  m_masks.insert(m_masks.end(), mask);
  } // CFileList::AddMask 

_inline void CFileList::SetMasks(char *masks[], int count)
  {
  for (int i = 0; i < count && masks[i] != NULL; ++i)
    m_masks.insert(m_masks.end(), masks[i]);
  } // CFileList::SetMasks 

// does file_name match any of mask 
_inline bool CFileList::MatchAnyMask(char *file_name)
  {
  for (unsigned int i = 0; i < m_masks.size(); ++i)
    if (m_match_fun(m_masks[i].c_str(), file_name))
      return true;
  return false;
  } // CFileList::MatchAnyMask 



_inline bool CFileList::SetIgnoreCase(bool new_value)
  { // setting this value changes match_fun also
  m_ignore_case = new_value;
  m_match_fun = (IgnoreCase() ? &matchi : &match);
  return m_ignore_case;
  } // SetIgnoreCase 


// check if all chars from src are ch 
_inline int AllCharsAre(char ch, const char *str)
  {
  while (*str)
    if (*str++ != ch)
      return 0;
  return 1;
  } // AllCharsAre 


bool CFileList::CheckMasks()
  {
  return (m_masks.size() > 0);
  } // CFileList::CheckMasks 


unsigned int CFileList::ProcessFiles(std::string dir_name, file_fun_ptr file_fun,
                                     file_fun_ptr dir_fun, void *data)
  {
  if (file_fun == NULL || !CheckMasks())
    return 0;
  DIR *dir;
  std::string sError;

  dir = opendir(dir_name.c_str());
  if (dir == NULL)
    {
    sError = "Error during opendir(\"" + dir_name + "\"): ";
    perror(sError.c_str());
    return 0;
    }
  dirent *direntry;       // info about what does dir contains 
  struct stat stat_buf;   // info about particular file (or dir) 
  std::vector <std::string> Files;
  std::vector <std::string> Dirs;
  std::string full_file_name;   // file name with full path 
  while ((direntry = readdir(dir)) != NULL)
    {
    full_file_name = dir_name;
    full_file_name.append(DIR_SEPARATOR);
    full_file_name.append(static_cast<char *>(direntry->d_name));
    // unfortunately more natural
    //    full_file_name = dir_name + DIR_SEPARATOR + static_cast<char *>(direntry->d_name);
    // doesn't work with Borland C 5.02

    // getting info about file
    if (stat(full_file_name.c_str(), &stat_buf) != 0)
      {
      sError = "Error during stat(\"" + full_file_name + "\"): ";
      perror(sError.c_str());
      continue;
      }
    if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
      {                  // our direntry->d_name contains directory 
      if (Recursive())   // we must avoid '.' and '..' dirs 
        if (!AllCharsAre('.', direntry->d_name))
          Dirs.insert(Dirs.end(), full_file_name);
      }
    else
      if (MatchAnyMask(direntry->d_name))
        Files.insert(Files.end(), full_file_name);
    } // while direntry 
  closedir(dir);

  unsigned int i, count = 0;
  for (i = 0; i < Files.size(); ++i)
    count += file_fun((Files[i]).c_str(), data); // process all files
  for (i = 0; i < Dirs.size(); ++i)
    {
    if (dir_fun != NULL)
      dir_fun((Dirs[i]).c_str(), data);
    count += ProcessFiles(Dirs[i], file_fun, dir_fun, data); // process all dirs
    }
  return count;
  } // CFileList::ProcessFiles 


bool IsOptionOn(char arg[], char opt)
  {
  if (arg[0] == '-' && arg[1] == opt && arg[2] == '\0')
    return true;
  return false;
  } // IsOptionOn 

_inline bool CFileList::ParseArgs(int argc, char *argv[])
  {
  for (int i = 0; i < argc; ++i)
    {
    if (IsOptionOn(argv[i], 'h') || IsOptionOn(argv[i], '?'))
      return false;  // show help only
    if (IsOptionOn(argv[i], 'r'))
      m_recursive = true;
    if (IsOptionOn(argv[i], 'i'))
      m_ignore_case = true;
    }
  return true;
  } // CFileList::ParseArgs