// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
// $Id: filefilter.cc,v 1.4 1999/08/05 03:22:55 jgg Exp $
/* ######################################################################

   File Filter - Regular Expression maching filter
   
   The idea for this was stolen shamelessly from rsync.

   Doesn't work:
    dsync-flist -e binary-alpha -i binary-all -i binary-i386 generate /tmp/listing
   
   And various other incantations like that.
   
   ##################################################################### */
									/*}}}*/
// Include files							/*{{{*/
#ifdef __GNUG__
#pragma implementation "dsync/filefilter.h"
#endif

#include <dsync/filefilter.h>
#include <dsync/error.h>

#include <fnmatch.h>
#include <string.h>
using namespace std;
									/*}}}*/

// FileFilter::dsFileFilter - Constructor				/*{{{*/
// ---------------------------------------------------------------------
/* */
dsFileFilter::dsFileFilter() : List(0)
{
}
									/*}}}*/
// FileFilter::~dsFileFilter - Destructor				/*{{{*/
// ---------------------------------------------------------------------
/* */
dsFileFilter::~dsFileFilter()
{
   while (List != 0)
   {
      Item *Tmp = List;
      List = Tmp->Next;
      delete Tmp;
   }
}
									/*}}}*/
// FileFilter::Test - Test a directory and file				/*{{{*/
// ---------------------------------------------------------------------
/* This will return true if the named entity is included by the filter, false
   otherwise. By default all entries are included. */
bool dsFileFilter::Test(const char *Directory,const char *File)
{
   for (Item *I = List; I != 0; I = I->Next)
   {
      bool Res = I->Test(Directory,File);
      if (Res == false)
	 continue;
      
      if (I->Type == Item::Include)
	 return true;
      
      if (I->Type == Item::Exclude)
	 return false;
   }
   
   return true;
}
									/*}}}*/
// FileFilter::LoadFilter - Load the filter list from the configuration	/*{{{*/
// ---------------------------------------------------------------------
/* When given the root of a configuration tree this will parse that sub-tree
   as an ordered list of include/exclude directives. Each value in the list
   must be prefixed with a + or a - indicating include/exclude */
bool dsFileFilter::LoadFilter(Configuration::Item const *Top)
{
   if (Top != 0)
      Top = Top->Child;
   
   // Advance to the end of the list
   Item **End = &List;
   for (; *End != 0; End = &(*End)->Next);
      
   for (; Top != 0;)
   {
      Item *New = new Item;
      
      // Decode the type
      if (Top->Value[0] == '+')
	 New->Type = Item::Include;
      else
      {
	 if (Top->Value[0] == '-')
	    New->Type = Item::Exclude;
         else
         {
	    delete New;
	    return _error->Error("Malformed filter directive %s",Top->Tag.c_str());
	 }
      }

      // Strip off the +/- indicator
      unsigned int Count = 1;
      for (const char *I = Top->Value.c_str() + 1; I < Top->Value.c_str() + strlen(Top->Value.c_str()) &&
	     isspace(*I); I++)
	 Count++;
      New->Pattern = string(Top->Value,Count);
      
      // Set flags
      New->Flags = 0;
      if (New->Pattern == "*")
	 New->Flags |= Item::MatchAll;
      if (New->Pattern.find('/') != string::npos)
	 New->Flags |= Item::MatchPath;
      
      // Link it into the list
      New->Next = 0;
      *End = New;
      End = &New->Next;
      
      Top = Top->Next;
   }
   return true;
}
									/*}}}*/
// FileFilter::Item::Test - Test a single item				/*{{{*/
// ---------------------------------------------------------------------
/* */
bool dsFileFilter::Item::Test(const char *Directory,const char *File)
{
   // Catch all
   if ((Flags & MatchAll) == MatchAll)
      return true;
 
   // Append the direcotry
   if ((Flags & MatchPath) == MatchPath)
   {
      char S[1024];
      if (strlen(Directory) + strlen(File) > sizeof(S))
	  return _error->Error("File field overflow");
      strcpy(S,Directory);
      strcat(S,File);
      
      return fnmatch(Pattern.c_str(),S,FNM_PATHNAME) == 0;
   }
   
   return fnmatch(Pattern.c_str(),File,FNM_PATHNAME) == 0;
}
									/*}}}*/
