/* Copyright (C) 1999 Beau Kuiper.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this program; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "ftpd.h"

/* My attempt at creating a fnmatch funciton. This actually isn't bad for 
   my first attempt, It seems to beat the library equivelent in the 
   GNU library for everything but complicated expressions with many
   star tokens. */

/* this matches square bracket stuff */

int bracket_match(unsigned char letter, unsigned char *pattern)
{
	unsigned fchar = 0;
	int partdone;
	int inverse = FALSE;
	
	/* see if the pattern is inversed */
	if ((*pattern == '!') || (*pattern == '^'))
	{
		inverse = TRUE;
		pattern++;
	}
	
	while (*pattern != 0)
	{
		fchar = *pattern;
		partdone = FALSE;
		/* search for character range */
		
		if (*(pattern + 1) != 0)
			if ((*(pattern + 1) == '-') && (*(pattern + 2) != 0))
			{
				/* found an a-z thingy */
				if ((letter >= fchar) && (letter <= *(pattern + 2)))
					return(!inverse);
				partdone = TRUE;
				pattern += 3;
			}
		if (!partdone)
		{
			/* match as a single character */
			
			if (letter == *pattern)
				return(!inverse);
			pattern++;
		}
	}
	return(inverse);
}

int my_dofnmatch(char *pattern, char *string, char *endstr)
{
	unsigned char *ppos, *spos;

	ppos = pattern;
	spos = string;

	while(*ppos != 0)
	{
		switch(*ppos)
		{
			/* this is simple too */
			case '\\' :
			{
				ppos++;
				if ((*ppos == 0) || (*spos == 0)) 
					return(-1);
				if (*spos != *ppos)
					return(-1);
				spos++;
				break;
			}
			/* this one is easy, one character */
			case '?' :
			{
				if (*spos != 0)
					spos++;
				else
					return(-1);
				break;
			}
			/* this one is a hard one! */
			case '[' :
			{
				char *start = ppos + 1;
				char oldchar;

				while((*ppos != 0) && (*ppos != ']'))
					ppos++;
				if (*ppos == 0)
					return(-1);
				if (*(ppos + 1) == ']')
					ppos++;
				oldchar = *(ppos + 1);
				*(ppos + 1) = 0;
				if (!bracket_match(*spos, start))
					return(-1);
				*(ppos + 1) = oldchar;
				spos++;
				break;
			}
			/* this one sucks bigtime */
			case '*' :
			{
				unsigned char *subpat;
				 
				/* this is a fast track for common patterns
				   like '*' */
				
				/* since * matches any string, if everything
				   has matched so far, and no more info pattern
				   is after the star, then the match is
				   successful */
				
				if (*(ppos + 1) == 0)
					return(0);
				
				/* since there is data after the *, we have to
				   greedyly (by starting at the end of the
				   string) match the * */
				
				subpat = endstr;
				while(subpat != spos)
				{
					subpat--;
					if (my_dofnmatch(ppos + 1, subpat, endstr) == 0)
							return(0);
				}
				
				/* if we cannot match the subpattern, then
				   it is not going to work no matter what */
				
				return(-1);
			}
			/* this is trivial */
			default:
			{
				if (*spos != *ppos)
					return(-1);
				spos++;
			}
		}
		ppos++;
	}
	
	/* if we reach the end of the pattern, then we must also have nothing
	   left in the string */
	  
	if (*spos == 0)
		return(0);
	return(-1);
}

int my_dofnmatchpath(unsigned char *pattern, unsigned char *string, unsigned char *endstr)
{
	unsigned char *ppos, *spos;

	ppos = pattern;
	spos = string;
	
	while(*ppos != 0)
	{
		switch(*ppos)
		{
			/* this is simple too */
			case '\\' :
			{
				ppos++;
				if ((*ppos == 0) || (*spos == 0)) 
					return(-1);
				if (*spos != *ppos)
					return(-1);
				spos++;
				break;
			}
			/* this one is easy, one character */
			case '?' :
			{
				if ((*spos != 0) && (*spos != '/'))
					spos++;
				else
					return(-1);
				break;
			}
			/* this one is a hard one! */
			case '[' :
			{
				char *start = ppos + 1;
				char oldchar;

				if ((*spos == '/') || (*spos == 0))
					return(-1);
				while((*ppos != 0) && (*ppos != ']'))
					ppos++;
				if (*ppos == 0)
					return(-1);
				if (*(ppos + 1) == ']')
					ppos++;
				oldchar = *(ppos + 1);
				*(ppos + 1) = 0;
				if (!bracket_match(*spos, start))
					return(-1);
				*(ppos + 1) = oldchar;
				spos++;
				break;
			}
			/* this one sucks bigtime */
			case '*' :
			{
				unsigned char *subpat;
				 
				/* this is a fast track for common patterns
				   like '*' disabled for path work since *
				   means everything except / */
				
				/* start from curret pos and attempt to find
				   first /, since * cannot match any further
				   than that! */

				subpat = spos;
				while((*subpat != '/') && (subpat != endstr))
					subpat++;

				/* since there is data after the *, we have to
				   greedyly (by starting at the end of the
				   string) match the * */
					
				while(subpat != spos)
				{
					subpat--;
					if (my_dofnmatchpath(ppos + 1, subpat, endstr) == 0)
							return(0);
				}
				
				/* if we cannot match the subpattern, then
				   it is not going to work no matter what */
				
				return(-1);
			}
			/* this is trivial */
			default:
			{
				if (*spos != *ppos)
					return(-1);
				spos++;
			}
		}
		ppos++;
	}
	
	/* if we reach the end of the pattern, then we must also have nothing
	   left in the string */
	  
	if (*spos == 0)
		return(0);
	return(-1);
}

/* this is a wrapper for the recursive function about, giving the correct
   parameters. */

int my_fnmatch(char *pattern, char *string, int flags)
{
	if (flags != FNM_PATHNAME)
	{
		if ((pattern[0] == '*') && (pattern[1] == 0))
			return(0);
		return(my_dofnmatch(pattern, string, strchr(string, 0)));
	}
	else
		return(my_dofnmatchpath(pattern, string, strchr(string, 0)));
}

void qsortarray(char **l, int start, int end)
{
	int left = start;
	int right = end;
	int pivot = left;
	char *temp;

	if (left == right)
		return;
			
	while(left != right)
	{
		if (strcmp(l[left], l[right]) > 0)
		{
			/* swap left and right */
			temp = l[left];
			l[left] = l[right];
			l[right] = temp;
		
			if (pivot == left)
			{
				pivot = right;
				left++;
			}
			else
			{
				pivot = left;	
				right--;
			}
		}
		else
		{
			if (pivot == left)
				right--;
			else			
				left++;
		}
	}

	if (pivot > start)
		qsortarray(l, start, pivot - 1);
	if (pivot < end)
		qsortarray(l, pivot + 1, end);
}

MYGLOBDATA *myglob (char *dir, char *pattern, int allfiles, int sortforward)
{
	MYGLOBDATA *g = NULL;
	int error = 0;
	DIR *globdir = NULL;
	int count;
	struct dirent *filedat;
	
	error = chdir(dir);
	
	if (!error)
		error = ((globdir = opendir(".")) == NULL);
	
	if (!error)
	{
		g = mallocwrapper(sizeof(MYGLOBDATA));
		g->namecount = 0;
		g->name = NULL;
		g->stat_buf = NULL;
		g->blocks = 0;
	
		while((filedat = readdir(globdir)) != NULL)
		{
			if ((filedat->d_name[0] != '.') || (allfiles))
			{
				if (my_fnmatch(pattern, filedat->d_name, 0) == 0)
				{
					(g->namecount)++;
					reallocwrapper(sizeof(char **) * g->namecount, (void *)&(g->name));
					g->name[g->namecount - 1] = strdupwrapper(filedat->d_name);
				}
			}
		}
		
		reallocwrapper(sizeof(char **) * (g->namecount + 1), (void *)&(g->name));
		g->name[g->namecount] = NULL;
		
		if (g->namecount > 0)
		{
			qsortarray(g->name, 0, g->namecount-1);
			
			g->stat_buf = mallocwrapper(sizeof(struct stat) * (g->namecount));
	
			for(count = 0; count < g->namecount; count++)
			{
				if (lstat(g->name[count], g->stat_buf + count) == -1)
				{
					printf("stat error\n");

					memset(g->stat_buf + count, 0, sizeof(struct stat));
				}
				else
					g->blocks += g->stat_buf[count].st_blocks / 2;
			}
		}
	}
	
	if (globdir)
		closedir(globdir);
	
	return(g);
}	

void myglobfree (MYGLOBDATA *g)
{
	int count;
	if (g->name)
	{ 
		for (count = 0; count < g->namecount; count++)
			freewrapper(g->name[count]);
			
		freewrapper(g->name);
		freeifnotnull(g->stat_buf);
	}
	freewrapper(g);
}
