#import <regex.h>
#import <sys/types.h>
#import <pwd.h>
#import <sys/dir.h> 
#import "StringStorage.h"
#import "FileCollection.h"

@interface FileCollection (PRIVATE)
- bourne: (const char *)pattern toRegex: (char *)reg;
@end

@implementation FileCollection

- init
{
   [super init];
   selectedFiles = [[List alloc] init];
   return self;
}

- free
{
   [selectedFiles free];
   return [super free];
}



- (int) select: (const char *)pattern
{
   int x, cnt;
   char regex[2 * strlen(pattern)];

   [selectedFiles empty];
   [self bourne: pattern toRegex: regex];	     /* convert the bourne shell wild cards to regex */

   re_comp(regex);
   for(x = 0, cnt = [self count]; x < cnt; x++)
   {
      if(re_exec((char *)[[self objectAt:x] stringValue]))
	  [selectedFiles addObject: [self objectAt: x]];
   }

   return [selectedFiles count];
}

- selectedFiles
{
   return selectedFiles;
}


@end


@implementation FileCollection (PRIVATE)

/* 
 * Using regex patterns for file matching is not intuitive,
 * so I take bourne type wild cards and convert them to regex.
 *
 * I support the bourne: *,?,[]
 *
 * To do this convert:
 *
 *    ^ -> \^   
 *
 * And when not in a range:
 *
 *    * -> .*
 *    ? -> .
 *    . -> \.
 *
 * And then wrap the regex in ^$ so that it is an exact match not a substring.
 */

- bourne: (const char *)pattern toRegex: (char *)reg
{
   int x, cnt;
   BOOL inRange = NO;
   
   reg[0] = '^';
   reg++;
   
   for(x = 0, cnt = strlen(pattern); x < cnt; x++)
   {

      if(!inRange)
      {
	 switch(pattern[x])
	 {
	  case '^':
	    reg[0] = '\\';
	    reg[1] = '^';
	    reg += 2;
	    break;

	  case '?':
	    reg[0] = '.';
	    reg++;
	    break;

	  case '.':
	    reg[0] = '\\';
	    reg[1] = '.';
	    reg += 2;
	    break;
	     
	  case '*':
	    reg[0] = '.';
	    reg[1] = '*';
	    reg += 2;
	    break;
	    
  	  case '\\':
	    reg[0] = pattern[x];
	    x++;
	    reg[1] = pattern[x];
	    reg += 2;
	    break;

	  case '[':
	    inRange = YES;
	  default:
	    reg[0] = pattern[x];
	    reg++;
	 }
      }
      else
      {
	 switch(pattern[x])
	 {
	  case '^':
	    reg[0] = '\\';
	    reg[1] = '^';
	    reg += 2;
	    break;

	  case ']':
	    inRange = NO;
	  default:
	    reg[0] = pattern[x];
	    reg++;
	 }
      }
   }

   reg[0] = '$';
   reg[1] = (char)0;

   return self;
}

@end
