#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>

#include <sys/types.h>
#include <dirent.h>
#define NAMLEN(dirent) (strlen((dirent)->d_name))

#include "dosfile.h"

#define BOOL  int
#define FALSE  0
#define TRUE  1

// These orphan their strings
char *strupr (const char *a)
{
   int i;
   int j;
   
   j = strlen (a);
   char *k = malloc( j +1 );
   for (i = 0; i < j; i++)
   {
      k[i] = toupper (a[i]);
   }
   k [ j ] = '\0';
   return (k);
}

// Orphans the string
char *strlwr (const char *a)
{
   int i;
   int j;
   
   j = strlen (a);
   char *k = malloc( j +1 );
   for (i = 0; i < j; i++)
   {
      k[i] = tolower (a[i]);
   }
   k [ j ] = '\0';
   return (k);
}

 
// functions for DOS land
int stricmp(const char *s1, const char *s2)
{
   register int c;

   //   printf("Comparing %s to %s\n", s1, s2 );
   
   while ((c = tolower(*s1)) == tolower(*s2)) {      
      if (c == 0)
      {
	 return 0;
      }
      s1++;
      s2++;
     }
   if (c < tolower(*s2))
   {
      return -1;
   }
   return 1;
}
 
int strnicmp(const char *s1, const char *s2, size_t len)
{
   register int c;
   
   if (!len)
      return 0;
   while ((c = tolower(*s1)) == tolower(*s2) && len > 0) {
      if (c == 0 || --len == 0)
	 return 0;
      s1++;
      s2++;
   }
   if (c < tolower(*s2))
      return -1;
   return 1;
}

/* structure for use by the directory scanning routines */
#define FF_MAXPATHLEN 1024

struct FF_DATA
{
   DIR *dir;
   char dirname[FF_MAXPATHLEN];
   char pattern[FF_MAXPATHLEN];
   int attrib;
};

/* ff_get_filename:
 *  When passed a completely specified file path, this returns a pointer
 *  to the filename portion.
 */
static char *ff_get_filename(const char *path)
{
   char *p = (char*)path + strlen(path);

   while ((p > path) && (*(p - 1) != '/'))
      p--;

   return p;
}

/* ff_get_attrib:
 *  Builds up the attribute list of the file pointed to by name and s.
 */
static int ff_get_attrib(char *name, struct stat *s)
{
   int attrib = 0;
   int euid = geteuid();

   if (euid != 0) {
      if (s->st_uid == euid) {
	 if ((s->st_mode & S_IWUSR) == 0)
	    attrib |= FA_RDONLY;
      }
      else if (s->st_gid == getegid()) {
	 if ((s->st_mode & S_IWGRP) == 0)
	    attrib |= FA_RDONLY;
      }
      else if ((s->st_mode & S_IWOTH) == 0) {
	 attrib |= FA_RDONLY;
      }
   }

   if (S_ISDIR(s->st_mode))
      attrib |= FA_DIREC;

   if ((name[0] == '.') && ((name[1] != '.') || (name[2] != '\0')))
      attrib |= FA_HIDDEN;

   return attrib;
}

#define FF_MATCH_TRY 0
#define FF_MATCH_ONE 1
#define FF_MATCH_ANY 2


struct FF_MATCH_DATA
{
   int type;
   const char *s1;
   const char *s2;
};

/* ff_put_backslash:
 *  If the last character of the filename is not a /, this routine will
 *  concatenate a / on to it.
 */
static void ff_put_backslash(char *filename, int size)
{
   int len = strlen(filename);

   if ((len > 0) && (len < (size - 1)) && (filename[len - 1] != '/')) {
      filename[len] = '/';
      filename[len + 1] = 0;
   }
}

/* ff_match:
 *  Matches two strings ('*' matches any number of characters,
 *  '?' matches any character).
 */
static int ff_match(const char *s1, const char *s2)
{
   static unsigned int size = 0;
   static struct FF_MATCH_DATA *data = NULL;
   const char *s1end;
   int index, c1, c2;

   /* handle NULL arguments */
   if ((!s1) && (!s2)) {
      if (data) {
         free(data);
         data = NULL;
      }

      return 0;
   }

   s1end = s1 + strlen(s1);

   /* allocate larger working area if necessary */
   if (data && (size < strlen(s2))) {
      free(data);
      data = NULL;
   }

   if (!data) {
      size = strlen(s2);
      data = malloc(sizeof(struct FF_MATCH_DATA) * size * 2 + 1);
      if (!data)
         return 0;
   }

   index = 0;
   data[0].s1 = s1;
   data[0].s2 = s2;
   data[0].type = FF_MATCH_TRY;

   while (index >= 0) {
      s1 = data[index].s1;
      s2 = data[index].s2;
      c1 = *s1;
      c2 = *s2;

      switch (data[index].type) {

      case FF_MATCH_TRY:
         if (c2 == 0) {
            /* pattern exhausted */
            if (c1 == 0)
               return 1;
            else
               index--;
         }
         else if (c1 == 0) {
            /* string exhausted */
            while (*s2 == '*')
               s2++;
            if (*s2 == 0)
               return 1;
            else
               index--;
         }
         else if (c2 == '*') {
            /* try to match the rest of pattern with empty string */
            data[index++].type = FF_MATCH_ANY;
            data[index].s1 = s1end;
            data[index].s2 = s2 + 1;
            data[index].type = FF_MATCH_TRY;
         }
         else if ((c2 == '?') || (c1 == c2)) {
            /* try to match the rest */
            data[index++].type = FF_MATCH_ONE;
            data[index].s1 = s1 + 1;
            data[index].s2 = s2 + 1;
            data[index].type = FF_MATCH_TRY;
         }
         else
            index--;
         break;

      case FF_MATCH_ONE:
         /* the rest of string did not match, try earlier */
         index--;
         break;

      case FF_MATCH_ANY:
         /* rest of string did not match, try add more chars to string tail */
         if (--data[index + 1].s1 >= s1) {
            data[index + 1].type = FF_MATCH_TRY;
            index++;
         }
         else
            index--;
         break;

      default:
         /* this is a bird? This is a plane? No it's a bug!!! */
         return 0;
      }
   }

   return 0;
}

// NOTE: this orphans new
char *fixpattern( const char* oldpattern )
{
   int len = strlen( oldpattern )+1;
   char *new = malloc( len );
   int i = 0;

   for ( i = 0; i < len; i++ )
   {
      new[ i ] = oldpattern[ i ];

      if ( new[ i ] == '\\' )
      {
	 new[ i ] = '/';
      }
   }
   
   return new;
   
}



/* al_findfirst:
 *  Initiates a directory search.
 */
int findfirst(const char *oldpattern, struct finddata_t *info, int attrib)
{
   struct FF_DATA *ff_data;
   struct stat s;
   int actual_attrib;
   char tmp[1024];
   char *p;

   const char *pattern = fixpattern( oldpattern );

   /* if the pattern contains no wildcard, we use stat() */
   if (!strpbrk(pattern, "?*")) {
      info->ff_data = NULL;

      /* start the search */
      int errno = 0;

      if (stat(pattern, &s) == 0) {
         /* get file attributes */
         actual_attrib = ff_get_attrib(ff_get_filename(pattern), &s);

         /* does it match ? */
         if ( attrib < 0 || (actual_attrib & ~attrib) == 0) {
            info->attrib = actual_attrib;
            info->time = s.st_mtime;
            info->size = s.st_size;
            strncpy(info->name, ff_get_filename(pattern), sizeof(info->name) );
            return 0;
         }
      }
      return -1;
   }

   /* allocate ff_data structure */
   ff_data = malloc(sizeof(struct FF_DATA));

   if (!ff_data) {
      printf("Out of memory to allocate data\n");
      return -1;
   }

   /* attach it to the info structure */
   info->ff_data = (void *) ff_data;

   /* initialize it */
   ff_data->attrib = attrib;

   //   printf("findfirst: pattern = %s\n", pattern );
   strncpy(ff_data->dirname, pattern, sizeof(ff_data->dirname));
   p = ff_get_filename(ff_data->dirname);
   strncpy(ff_data->pattern, p, sizeof(ff_data->pattern));
   if (p == ff_data->dirname)
      strcpy(ff_data->dirname, "./");
   else
      *p = 0;

   /* nasty bodge, but gives better compatibility with DOS programs */
   if (strcmp(ff_data->pattern, "*.*") == 0)
      strcpy(ff_data->pattern, "*");

   /* start the search */
   ff_data->dir = opendir(ff_data->dirname);

   if (!ff_data->dir) {
      //      printf("No entry error in ff_data->dirname %s\n", ff_data->dirname );
      free(ff_data);
      info->ff_data = NULL;
      return -1;
   }

   if (findnext(info) != 0) {
      findclose(info);
      return -1;
   }

   return 0;
}

/* findnext:
 *  Retrieves the next file from a directory search.
 */
int findnext(struct finddata_t *info)
{
   char tempname[FF_MAXPATHLEN];
   char filename[FF_MAXPATHLEN];
   int attrib;
   struct dirent *entry;
   struct stat s;
   struct FF_DATA *ff_data = (struct FF_DATA *) info->ff_data;

   /* if the pattern contained no wildcard */
   if (!ff_data)
   {
      printf("findnext: No wildcard\n");
      return -1;
   }

   while (TRUE) {
      //      printf("Reading directory entry: dir = %s, filename = %s,  pattern = %s\n", ff_data->dirname, filename, ff_data->pattern );

      /* read directory entry */
      entry = readdir(ff_data->dir);
      if (!entry) {
	 //	 printf("Error: No entry for file after %s\n", filename );
         return -1;
      }

      /* try to match file name with pattern */
      tempname[0] = 0;
      if (NAMLEN(entry) >= sizeof(tempname))
         strncat(tempname, entry->d_name, sizeof(tempname) - 1);
      else
         strncat(tempname, entry->d_name, NAMLEN(entry));

      if (ff_match(tempname, ff_data->pattern)) {
         strcpy(filename, ff_data->dirname);
         ff_put_backslash(filename, sizeof(filename));
         strncat(filename, tempname, sizeof(filename) - strlen(filename) - 1);

         /* get file attributes */
         if (stat(filename, &s) == 0) {
            attrib = ff_get_attrib(tempname, &s);

            /* does it match ? */
            if ( ff_data->attrib < 0 || (attrib & ~ff_data->attrib) == 0)
	    {
               break;
	    }
         }
         else {
            /* evil! but no other way to avoid exiting for_each_file() */
	    printf("Errno = 0\n");
         }
      }
   }

   info->attrib = attrib;
   info->time = s.st_mtime;
   info->size = s.st_size;

   strncpy(info->name, tempname, sizeof(info->name));

   //   printf("Found file named %s\n", info->name );

   return 0;
}



/* al_findclose:
 *  Cleans up after a directory search.
 */
void findclose(struct finddata_t *info)
{
   struct FF_DATA *ff_data = (struct FF_DATA *) info->ff_data;

   if (ff_data) {
      closedir(ff_data->dir);
      free(ff_data);
      info->ff_data = NULL;

      /* to avoid leaking memory */
      ff_match(NULL, NULL);
   }
}



/* _al_getdcwd:
 *  Returns the current directory on the specified drive.
 */
void getdcwd(int drive, char *buf, int size)
{
   char tmp[1024];

   if (getcwd(tmp, sizeof(tmp)))
      strncpy(buf, tmp, size);
   else
      buf[ 0 ] = 0;
}



void DeleteFile( const char* name  )
{
   remove( name );
}

/*
**  psplit() - Portable replacement for fnsplit(), _splitpath(), etc.
**
**  Splits a full DOS pathname into drive, path, file, and extension
**  specifications. Works with forward or back slash path separators and
**  network file names, e.g. NET:LOONEY/BIN\WUMPUS.COM, Z:\MYDIR.NEW/NAME.EXT
**
**  Arguments: 1 - Full pathname to split
**             2 - Buffer for drive
**             3 - Buffer for path
**             4 - Buffer for name
**             5 - Buffer for extension
**
**  Returns: Nothing
**
**  public domain by Bob Stout
*/

#include <stdlib.h>
#include <string.h>

#define NUL '\0'

void psplit(char *path, char *drv, char *dir, char *fname, char *ext)
{
      char ch, *ptr, *p;

      /* convert slashes to backslashes for searching       */

      for (ptr = path; *ptr; ++ptr)
      {
            if ('/' == *ptr)
                  *ptr = '\\';
      }

      /* look for drive spec                                */
      // Wolff removed this because there are no drive specs on UNIX
      /*
      if (NULL != (ptr = strchr(path, ':')))
      {
            ++ptr;
            if (drv)
            {
                  strncpy(drv, path, ptr - path);
                  drv[ptr - path] = NUL;
            }
            path = ptr;
      }
      else if (drv)
            *drv = NUL;
	    */
      
      /* find rightmost backslash or leftmost colon         */

      if (NULL == (ptr = strrchr(path, '\\')))
            ptr = (strchr(path, ':'));

      if (!ptr)
      {
            ptr = path;             /* obviously, no path   */
            if (dir)
                  *dir = NUL;
      }
      else
      {
            ++ptr;                  /* skip the delimiter   */
            if (dir)
            {
                  ch = *ptr;
                  *ptr = NUL;
                  strcpy(dir, path);
                  *ptr = ch;
            }
      }

      if (NULL == (p = strrchr(ptr, '.')))
      {
            if (fname)
                  strcpy(fname, ptr);
            if (ext)
                  *ext = NUL;
      }
      else
      {
            *p = NUL;
            if (fname)
                  strcpy(fname, ptr);
            *p = '.';
            if (ext)
                  strcpy(ext, p);
      }
}

#ifdef TEST_FOR //PSPLIT

#include <stdio.h>

int main(int argc, char *argv[])
{
      char drive[10], pathname[FILENAME_MAX], fname[9], ext[5];

      while (--argc)
      {
            psplit(*++argv, drive, pathname, fname, ext);
            printf("psplit(%s) returns:\n drive = %s\n path  = %s\n"
                  " name  = %s\n ext   = %s\n",
                  *argv, drive, pathname, fname, ext);
      }
      return EXIT_SUCCESS;
}

#endif

///////////////////

BOOL CreateDirectory( const char* dir )
{
   char temp[1024];

   sprintf( temp, "mkdir -p %s", dir );
   return system( temp );
}

/* _al_file_time:
 *  Returns the timestamp of the specified file.
 */
time_t getFileTime(const char *filename)
{
   struct stat s;
   char tmp[1024];

   if (stat(filename, &s) != 0) {
      printf("Failed to get file time for file %s\n", filename );
      return 0;
   }

   return s.st_mtime;
}

/* ff_get_attrib:
 *  Builds up the attribute list of the file pointed to by name and s.
 */
static int getAttribHelper(const char *name, struct stat *s)
{
   int attrib = 0;
   int euid = geteuid();

   if (euid != 0) {
      if (s->st_uid == euid) {
	 if ((s->st_mode & S_IWUSR) == 0)
	    attrib |= FA_RDONLY;
      }
      else if (s->st_gid == getegid()) {
	 if ((s->st_mode & S_IWGRP) == 0)
	    attrib |= FA_RDONLY;
      }
      else if ((s->st_mode & S_IWOTH) == 0) {
	 attrib |= FA_RDONLY;
      }
   }

   if (S_ISDIR(s->st_mode))
      attrib |= FA_DIREC;

   if ((name[0] == '.') && ((name[1] != '.') || (name[2] != '\0')))
      attrib |= FA_HIDDEN;

   return attrib;
}

int getAttrib( const char *name )
{
   struct stat s;

   if ( stat( name, &s ) != 0 )
   {
      return -1;
   }
   
   return getAttribHelper( name, &s );
}
   

