#include <dirent.h>

static inline int int21_emu_ioctl ( void )
{
  switch (AL) {
    case 0  : { /* get device attrs */
      int ret=16384+8129;
      if (BX==0) ret|=1;
      if (BX==1) ret|=2;
      CLC;
      AL=ret;
      return 1;
    }
    
    case 1 : { /* set device attrs */
      CLC; /* ignore */
      return 1;
    }
    
    case 2 : { /* read from device */
      char *p=DOS_PB(DS,DX);
      int ret=read(BX,p,CX);
      if (ret<0) {
        AX=errno_u2d(errno);
	STC;
      } else {
        AX=ret;
	CLC;
      }
      return 1;
    }
    
    case 3 : { /* write to device */
      char *p=DOS_PB(DS,DX);
      int ret=write(BX,p,CX);
      if (ret<0) {
        AX=errno_u2d(errno);
	STC;
      } else {
        AX=ret;
	CLC;
      }
      return 1;
    }
    
    default :
#ifdef CONFIG_DEBUG_UNIMPLEMENTED_FUNC
      fprintf(stderr,"INT21: unimplemented ioctl function (44h, %02Xh).\n",AL);
#endif
      CLC;
      AX=1;
      return 1;
  }
}





struct dta_sys_vars_st {
  DIR *dir;
  char *path;
  char *mask;
  int attr;
};

#define DTA ((struct dta_sys_vars_st*)(dta))


static inline int check_dos_fnamechar ( char c , char d )
{
  if (c>='A'&&c<='Z') c+=32;
  if (d>='A'&&d<='Z') d+=32;
  return c==d;
}


static word attr_u2d ( mode_t m )
{
  word ret=0;
  if (m&S_IWUSR) ret|=1;
  if (S_ISDIR(m)) ret|=16;
  return ret;
}


static void ftime_u2d ( time_t t , word *d_date , word *d_time )
{
  struct tm *unx=localtime(&t);
  *d_date=((unx->tm_year-1980)<<9) | (unx->tm_mon<<5) | unx->tm_mday;
  *d_time=(unx->tm_hour<<11) | (unx->tm_min<<5) | unx->tm_sec>>1;
}



static int check_dos_subfmask ( char *name , char *mask , int max )
{
  while (max--&&*mask&&*name&&(*mask=='?'||check_dos_fnamechar(*mask,*name))) {
    mask++;
    name++;
  }
  return *mask=='*'||max==0||(*mask==0&&*name==0);
}


static int check_dos_fmask ( char *name , char *mask )
{
  char *n=strdup(name);
  char *m=strdup(mask);
  char *nd=strrchr(n,'.');
  char *md=strrchr(m,'.');
  int ok;
//  fprintf(stderr,"INT21: check_dos_fmask %s %s ",name,mask);
  if (md && !nd && md[1]=='*') ok=1; else
  if ((md&&!nd)||(!md&&nd)) ok=0; else {
    if (nd) {
      *nd='\0';
      *md='\0';
      ok=check_dos_subfmask(n,m,256)&&check_dos_subfmask(nd+1,md+1,256);
    } else
      ok=check_dos_subfmask(n,m,256);
  }
  free(n);
  free(m);
//  fprintf(stderr,"%d\n",ok);
  return ok;
}

#define upcase(x) (((x)>='a' && (x)<='z') ? ((x)-32) : (x))

int SHIT_check_dos_fmask(char *name,char *mask)
{
  int n,m,i=0,j=0;
  do {
    n=upcase(name[i++]);
    m=upcase(mask[j++]);
//    printf("n='%c'  m='%c'\n",n,m);
    if ( m=='?' && n && n!='.' ) m=n;
    if ( m=='*' ) {
      while( n && n!='.' ) {
        n=upcase(name[i++]);
//        printf("n='%c'\n",n);
      } /* while */
      m=upcase(mask[j++]);
    } /* if */
    if ( n==0 && m==0 ) return 1;
    if ( n==0 || m==0 ) return 0;
  } while ( n==m ) ;
  return 0;
}




static int check_dos_amask ( mode_t m , int mask )
{
//  if ( (mask&16&&S_ISDIR(m)) || (!(mask&16)&&!S_ISDIR(m))  ) return 1;
  if ( (!mask&16 && S_ISDIR(m)) ) return 0;
  return 1;
}

static char fname_u2d_char ( char c )
{
  if (c>='A'&&c<='Z') return c+32; else return c;
}

static void OLD_fname_u2d ( char *unx , char *dos )
{
  char *q=strrchr(unx,'.');
  if (!q) {
    int max=256;
    while (max--&&*unx) *(dos++)=fname_u2d_char(*(unx++));
  } else {
    int max=256;
    while (max--&&*unx!='.') *(dos++)=fname_u2d_char(*(unx++));
    max=256;
    while (max--&&*q) *(dos++)=fname_u2d_char(*(q++));
  }
  *dos='\0';
}

static void fname_u2d ( char *unx , char *dos )
{
  while (*unx) *(dos++)=fname_u2d_char(*(unx++));
  *dos='\0';
}


static void em_findfile ( char *dta )
{
  int ok=0;
  struct dirent *ent;
  struct stat st;
  do {
    ent=readdir(DTA->dir);
    if (ent) {
      if (check_dos_fmask(ent->d_name,DTA->mask)) {
        char *p=malloc(strlen(ent->d_name)+strlen(DTA->path)+2);
        strcpy(p,DTA->path);
	strcat(p,"/");
	strcat(p,ent->d_name);
	stat(p,&st);
	free(p);
	ok=check_dos_amask(st.st_mode,DTA->attr);
      }
    }
  } while (ent&&!ok);
  if (ok&&ent) {
    fname_u2d(ent->d_name,dta+30);
    *((byte*)(dta+21))=attr_u2d(st.st_mode);
    *((unsigned int *)(dta+26))=st.st_size;
    ftime_u2d(st.st_mtime,(word*)(dta+24),(word*)(dta+22));
//    fprintf(stderr,"INT21: findfile, found %s\n",dta+30);
    CLC;
    return;
  }
  free(DTA->path);
  free(DTA->mask);
  closedir(DTA->dir);
  STC;
  AX=18;
}


static inline void int21_emu_findfirst ( void )
{
  char *dta=DOS_PB(dta_seg,dta_ofs),*p;
  path_d2u(DOS_PB(DS,DX));
  p=strrchr(path,'/');
  if (!p) {
    DTA->mask=strdup(path);
    DTA->path=getcwd(NULL,0);
  } else {
    *p='\0';
    DTA->path=strdup(path);
    DTA->mask=strdup(p+1);
  }
  DTA->attr=CX;
  DTA->dir=opendir(DTA->path);
  if (!DTA->dir) {
    AX=errno_u2d(errno);
    STC;
    free(DTA->mask);
    free(DTA->path);
    return;
  }
//  fprintf(stderr,"INT21: findfirst ATTR=%x (dos=%s (%s), path=%s, fmask=%s)\n",DTA->attr,DOS_PB(DS,DX),newpath,DTA->path,DTA->mask);
  em_findfile(dta);
}



static inline void int21_emu_findnext ( void )
{
  char *dta=DOS_PB(dta_seg,dta_ofs);
//  fprintf(stderr,"INT21: findnext (path=%s, fmask=%s)\n",DTA->path,DTA->mask);
  em_findfile(dta);
//  fprintf(stderr,"ok\n");
}
