//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2006 by PDSoft (Attila Padar)                *
//*                    http://mpxplay.cjb.net                              *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: drive & directory handling

#include <direct.h>
#include <dos.h>
#include <io.h>
#include <stdlib.h>
#include "newfunc.h"

extern volatile unsigned long mpxplay_signal_events;

unsigned int iswin9x,is_lfn_support,uselfn;
dosmem_t dm_int2x_1,dm_int2x_2;

//-------------------------------------------------------------------------
//DOS memory allocations (conventional memory for DOS callings)

unsigned int pds_int2x_dosmems_allocate(void)
{
 if(!dm_int2x_1.linearptr)
  if(!pds_dpmi_dos_allocmem(&dm_int2x_1,PDS_INT2X_DOSMEM_SIZE))
   return 0;
 if(!dm_int2x_2.linearptr)
  if(!pds_dpmi_dos_allocmem(&dm_int2x_2,PDS_INT2X_DOSMEM_SIZE))
   return 0;
 return 1;
}

void pds_int2x_dosmems_free(void)
{
 pds_dpmi_dos_freemem(&dm_int2x_1);
 pds_dpmi_dos_freemem(&dm_int2x_2);
}

//-------------------------------------------------------------------------
void pds_dir_save(unsigned int *savedrive,char *savedir)
{
 if(savedir)
  pds_getcwd(savedir);
 if(savedrive)
  *savedrive=pds_getdrive();
}

void pds_dir_restore(unsigned int savedrive,char *savedir)
{
 pds_setdrive(savedrive);
 pds_chdir(savedir);
}

//-------------------------------------------------------------------------

void pds_check_lfnapi(char *prgname)
{
#ifdef __DOS__
 struct rminfo RMI;

 if(pds_mswin_getver()==1024)
  iswin9x=1;

 if(iswin9x){
  is_lfn_support=1;
 }else{    // detect truename (LFN converter) function
  if(prgname){
   pds_dpmi_rmi_clear(&RMI);
   RMI.EAX=0x00007160;
   RMI.ECX=2;
   RMI.DS =dm_int2x_1.segment;
   RMI.ES =dm_int2x_2.segment;
   funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
   pds_strcpy(dm_int2x_1.linearptr,prgname);
   *((char *)(dm_int2x_2.linearptr))=0;
   pds_dpmi_realmodeint_call(0x21,&RMI);
   if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY) && *((char *)(dm_int2x_2.linearptr))!=0)
    is_lfn_support=1;
  }
 }
#endif
}

void pds_fullpath(char *fullname,char *name)
{
 if(uselfn)
  pds_truename_lfn(fullname,name);
 else
  pds_truename_dos(fullname,name);
}

void pds_getdcwd(int drive,char *path)  // 0:default drive, A=1,B=2,C=3
{
#ifdef __DOS__
 if(pds_filehand_check_entrance()){
  *path=0;
  return;
 }
 pds_filehand_lock_entrance();
 path[0]=0;
 if(is_lfn_support && uselfn){
  struct rminfo RMI;
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x00007147;
  RMI.EDX=drive&0x3f; // 0:default drive, A=1,B=2,C=3
  RMI.DS =dm_int2x_1.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  *((char *)(dm_int2x_1.linearptr))=0;
  pds_dpmi_realmodeint_call(0x21,&RMI);
  *((char *)(dm_int2x_1.linearptr+290))=0;
  //fprintf(stdout,"%c: %s\n",(drive+'A'),dm_int2x_1.linearptr);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)){
   if(dm_int2x_1.linearptr[1]!=':'){
    unsigned int drivenum;
    if(drive<=0){
     pds_filehand_unlock_entrance(); // !!!
     drivenum=pds_getdrive();
     pds_filehand_lock_entrance();   //
     drive=drivenum+1;
    }
    path[0]='A'+drive-1;//num;
    path[1]=':';
    path[2]=PDS_DIRECTORY_SEPARATOR_CHAR;
    pds_strcpy(&path[3],dm_int2x_1.linearptr);
   }else
    pds_strcpy(path,dm_int2x_1.linearptr);
   //fprintf(stdout,"path: %c: %s\n",('A'+drive-1),&path[-3]);
  }
 }
#endif
 if(!path[0])
  _getdcwd(drive&0x3f,path,300); // 0:default drive, A=1,B=2,C=3
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
}

void pds_getcwd(char *path)
{
 pds_getdcwd(0,path);
}

int pds_chdir(char *setpath)
{
 int result=-1;
 if(!setpath || !setpath[0])
  return result;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
 if(is_lfn_support && uselfn){
  struct rminfo RMI;
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x0000713B;
  RMI.DS =dm_int2x_1.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_strcpy(dm_int2x_1.linearptr,setpath);
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
   result=0;
 }else
#endif
  result=chdir(setpath);
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
 return result;
}

int pds_mkdir(char *newdirname)
{
 int result=-1;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
#endif
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
#ifdef __DOS__
 if(is_lfn_support && uselfn){
  struct rminfo RMI;
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x00007139;
  RMI.DS =dm_int2x_1.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_strcpy(dm_int2x_1.linearptr,newdirname);
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
   result=0;
 }else
#endif
  result=mkdir(newdirname);
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
 return result;
}

int pds_rmdir(char *dirname)
{
 int result=-1;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
#endif
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
#ifdef __DOS__
 if(is_lfn_support && uselfn){
  struct rminfo RMI;
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x0000713A;
  RMI.DS =dm_int2x_1.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_strcpy(dm_int2x_1.linearptr,dirname);
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
   result=0;
 }else
#endif
  result=rmdir(dirname);
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
 return result;
}

int pds_rename(char *oldfilename,char *newfilename)
{
 int result=MPXPLAY_ERROR_FILEHAND_RENAME;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
#endif
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
#ifdef __DOS__
 if(is_lfn_support && uselfn){
  struct rminfo RMI;
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x00007156;
  RMI.DS =dm_int2x_1.segment;
  RMI.ES =dm_int2x_2.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_strcpy(dm_int2x_1.linearptr,oldfilename);
  pds_strcpy(dm_int2x_2.linearptr,newfilename);
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
   result=MPXPLAY_ERROR_FILEHAND_OK;
 }else
#endif
  if(rename(oldfilename,newfilename)==0)
   result=MPXPLAY_ERROR_FILEHAND_OK;
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
 return result;
}

int pds_unlink(char *filename)
{
 int result=MPXPLAY_ERROR_FILEHAND_DELETE;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
#endif
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
#ifdef __DOS__
 if(is_lfn_support && uselfn){ // readonly files?
  struct rminfo RMI;
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x00007141;
  RMI.DS =dm_int2x_1.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_strcpy(dm_int2x_1.linearptr,filename);
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
   result=MPXPLAY_ERROR_FILEHAND_OK;
 }else
#endif
 {
  unsigned int attrib;
  if(_dos_getfileattr(filename,&attrib)!=0)
   goto unlink_end;
  if(attrib&(_A_HIDDEN|_A_SYSTEM|_A_VOLID|_A_SUBDIR))
   goto unlink_end;
  attrib&=~_A_RDONLY;
  if(_dos_setfileattr(filename,attrib)!=0)
   goto unlink_end;
  if(unlink(filename)==0)
   result=MPXPLAY_ERROR_FILEHAND_OK;
 }
unlink_end:
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
 return result;
}

//-------------------------------------------------------------------------
#ifdef __DOS__

#define PDS_DTA_SIZE 128 // dos-disk transfer area

typedef struct pds_dos_find_t{
 char *dta_addr;
 char dosdata[43];
}pds_dos_find_t;

static void pds_dta_get(unsigned int *dta_seg,unsigned int *dta_off)
{
 struct rminfo RMI;
 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x2f00;
 pds_dpmi_realmodeint_call(0x21,&RMI);
 *dta_seg=RMI.ES;
 *dta_off=RMI.EBX;
}

static void pds_dta_to_ffblk(struct pds_find_t *ffblk)
{
 struct pds_dos_find_t *ffdos=(struct pds_dos_find_t *)ffblk->ff_data;
 char *dta_addr;

 if(!ffdos)
  return;
 dta_addr=ffdos->dta_addr;
 ffblk->attrib =*((char *)(dta_addr+21+0));
 ffblk->size   =*((unsigned long  *)(dta_addr+21+5));
 pds_memcpy((void *)&ffblk->fdate,(void *)(dta_addr+21+1),sizeof(struct pds_fdate_t));
 pds_strncpy(ffblk->name,(unsigned char *)(dta_addr+21+9),PDS_DTA_SIZE-21-9);

 pds_memcpy(ffdos->dosdata,dta_addr,sizeof(ffdos->dosdata));
}

static unsigned int pds_dos_findfirst(char *path,int attrib,struct pds_find_t *ffblk)
{
 struct pds_dos_find_t *ffdos;
 struct rminfo RMI;
 unsigned int dta_seg,dta_off,result=1;

 ffdos=calloc(1,sizeof(struct pds_dos_find_t));
 if(!ffdos)
  return result;
 ffblk->ff_data=(void *)ffdos;

 pds_dta_get(&dta_seg,&dta_off);
 ffdos->dta_addr=(char *)((dta_seg<<4)+dta_off);
 if(!ffdos->dta_addr)
  goto err_out_ff;

 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x4e00;
 RMI.ECX=attrib;
 RMI.DS =dm_int2x_1.segment;
 pds_strcpy(dm_int2x_1.linearptr,path);
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)){
  result=RMI.EAX;
  goto err_out_ff;
 }

 pds_dta_to_ffblk(ffblk);

 return 0;

err_out_ff:
 pds_dos_findclose(ffblk);
 return result;
}

static unsigned int pds_dos_findnext(struct pds_find_t *ffblk)
{
 struct pds_dos_find_t *ffdos=(struct pds_dos_find_t *)ffblk->ff_data;
 struct rminfo RMI;
 unsigned int result=1;

 if(!ffdos)
  goto end_ff;

 pds_memcpy(ffdos->dta_addr,ffdos->dosdata,sizeof(ffdos->dosdata));

 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x4f00;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)){
  result=RMI.EAX;
  goto end_ff;
 }
 pds_dta_to_ffblk(ffblk);
 result=0;

end_ff:
 return result;
}

static void pds_dos_findclose(struct pds_find_t *ffblk)
{
 struct pds_dos_find_t *ffdos=(struct pds_dos_find_t *)ffblk->ff_data;
 if(ffdos){
  free(ffdos);
  ffblk->ff_data=NULL;
 }
}

#endif

//-------------------------------------------------------------------------
#ifdef __DOS__
struct lfn_find_t
{
 unsigned char ff_attrib[4];  /* Attributes, DOS and extended   */
 unsigned long ff_lctime;     /* Creation time (low)   DOS date */
 unsigned long ff_hctime;     /* Creation time (high)           */
 unsigned long ff_latime;     /* Access time (low)    Date only */
 unsigned long ff_hatime;     /* Access time (high)             */
 unsigned long ff_lmtime;     /* Modification time (low)        */
 unsigned long ff_hmtime;     /* Modification time (high)       */
 unsigned long ff_hsize;      /* File size (high end)           */
 unsigned long ff_lsize;      /* File size (low end)            */
 unsigned char ff_reserved[8];
 unsigned char ff_longname[260];
 unsigned char ff_shortname[14];
};

static unsigned int pds_lfn_findfirst(char *path,int attrib,struct pds_find_t *ffblk)
{
 struct rminfo RMI;
 struct lfn_find_t *flblk;
 unsigned int retry=2; // possible DOSLFN bug (this is not 100% solution)

 do{
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x0000714E;
  RMI.ECX=attrib;
  RMI.ESI=0x0001;
  RMI.DS =dm_int2x_1.segment;
  RMI.ES =dm_int2x_2.segment;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_strcpy(dm_int2x_1.linearptr,path);
  pds_memset(dm_int2x_2.linearptr,0,sizeof(struct lfn_find_t));
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)){
   flblk=(struct lfn_find_t *)dm_int2x_2.linearptr;
   if(flblk->ff_longname[0])
    break;
  }
 }while(--retry);
 if(!retry) //error
  return 1;
 ffblk->attrib =flblk->ff_attrib[0];
 pds_memcpy((void *)(&ffblk->cdate),(void *)(&flblk->ff_lctime),sizeof(struct pds_fdate_t));
 pds_memcpy((void *)(&ffblk->fdate),(void *)(&flblk->ff_lmtime),sizeof(struct pds_fdate_t));
 pds_memcpy((void *)(&ffblk->adate),(void *)(&flblk->ff_latime),sizeof(struct pds_fdate_t));
 ffblk->size=flblk->ff_lsize; // | ((unsigned long long)flblk->ff_hsize<<32);
 pds_strcpy(ffblk->name,flblk->ff_longname);
 ffblk->ff_data=(void *)RMI.EAX;
 return 0;
}

/*static unsigned int pds_lfn_findfirst(char *path,int attrib,struct pds_find_t *ffblk)
{
 struct rminfo RMI;
 struct lfn_find_t *flblk;

 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x0000714E;
 RMI.ECX=attrib;
 RMI.ESI=0x0001;
 RMI.DS =dm_int2x_1.segment;
 RMI.ES =dm_int2x_2.segment;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_strcpy(dm_int2x_1.linearptr,path);
 pds_memset(dm_int2x_2.linearptr,0,sizeof(struct lfn_find_t));
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
  return 1;
 flblk=(struct lfn_find_t *)dm_int2x_2.linearptr;
 if(!flblk->ff_longname[0])
  return 1;
 ffblk->attrib=flblk->ff_attrib[0];
 pds_memcpy((void *)(&ffblk->cdate),(void *)(&flblk->ff_lctime),sizeof(struct pds_fdate_t));
 pds_memcpy((void *)(&ffblk->fdate),(void *)(&flblk->ff_lmtime),sizeof(struct pds_fdate_t));
 pds_memcpy((void *)(&ffblk->adate),(void *)(&flblk->ff_latime),sizeof(struct pds_fdate_t));
 ffblk->size=flblk->ff_lsize; // | ((unsigned long long)flblk->ff_hsize<<32);
 pds_strcpy(ffblk->name,flblk->ff_longname);
 ffblk->ff_data=(void *)RMI.EAX;
 return 0;
}*/

static unsigned int pds_lfn_findnext(struct pds_find_t *ffblk)
{
 struct rminfo RMI;
 struct lfn_find_t *flblk;

 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x0000714F;
 RMI.EBX=(unsigned long)ffblk->ff_data;
 RMI.ESI=0x0001;
 RMI.ES =dm_int2x_2.segment;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_memset(dm_int2x_2.linearptr,0,sizeof(struct lfn_find_t));
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))
  return 1;
 flblk=(struct lfn_find_t *)dm_int2x_2.linearptr;
 ffblk->attrib=flblk->ff_attrib[0];
 pds_memcpy((void *)(&ffblk->cdate),(void *)(&flblk->ff_lctime),sizeof(struct pds_fdate_t));
 pds_memcpy((void *)(&ffblk->fdate),(void *)(&flblk->ff_lmtime),sizeof(struct pds_fdate_t));
 pds_memcpy((void *)(&ffblk->adate),(void *)(&flblk->ff_latime),sizeof(struct pds_fdate_t));
 ffblk->size=flblk->ff_lsize; // | ((unsigned long long)flblk->ff_hsize<<32);
 pds_strcpy(ffblk->name,flblk->ff_longname);
 return 0;
}

static void pds_lfn_findclose(struct pds_find_t *ffblk)
{
 struct rminfo RMI;
 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x000071A1;
 RMI.EBX=(unsigned long)ffblk->ff_data;
 pds_dpmi_realmodeint_call(0x21,&RMI);
}

#endif

unsigned int pds_findfirst(char *path,int attrib,struct pds_find_t *ffblk)
{
 unsigned int result=1;
 ffblk->ff_data=NULL;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
 if(is_lfn_support && uselfn)
  result=pds_lfn_findfirst(path,attrib,ffblk);
 else
  result=pds_dos_findfirst(path,attrib,ffblk);
 pds_filehand_unlock_entrance();
#else
 {
  struct find_t *fsblk=pds_calloc(1,sizeof(struct find_t));
  //struct _finddata_t *fsblk=pds_calloc(1,sizeof(struct _finddata_t));
  if(!fsblk){
   result=5; // ENOMEM
   goto ff_end;
  }
  //ffblk->ff_handler=_findfirst(path,fsblk);
  //if(ffblk->ff_handler>=0){
  result=_dos_findfirst(path,attrib,fsblk);
  if(result==0){
   //result=0;
   ffblk->attrib =fsblk->attrib;
   pds_memcpy((void *)(&ffblk->fdate),(void *)(&fsblk->wr_time),sizeof(struct pds_fdate_t));
   ffblk->size   =fsblk->size;
   pds_strcpy(ffblk->name,fsblk->name);
  }else{
   //result=1;
   pds_free(fsblk);
   fsblk=NULL;
  }
  ffblk->ff_data=fsblk;
 }
#endif
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 return result;
}

unsigned int pds_findnext(struct pds_find_t *ffblk)
{
 unsigned int result=1;
 //long result=1;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return result;
 pds_filehand_lock_entrance();
 if(is_lfn_support && uselfn)
  result=pds_lfn_findnext(ffblk);
 else
  result=pds_dos_findnext(ffblk);
 pds_filehand_unlock_entrance();
#else
 struct find_t *fsblk=(struct find_t *)ffblk->ff_data;
 //struct _finddata_t *fsblk=(struct _finddata_t *)ffblk->ff_data;
 result=_dos_findnext(fsblk);
 if(result==0){
 //result=_findnext(ffblk->ff_handler,fsblk);
 //if(result>=0){
 // result=0;
  ffblk->attrib =fsblk->attrib;
  pds_memcpy((void *)(&ffblk->fdate),(void *)(&fsblk->wr_time),sizeof(struct pds_fdate_t));
  ffblk->size   =fsblk->size;
  pds_strcpy(ffblk->name,fsblk->name);
 }//else
  //result=1;
#endif
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
 return result;
}

void pds_findclose(struct pds_find_t *ffblk)
{
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return;
 pds_filehand_lock_entrance();
 if(is_lfn_support && uselfn)
  pds_lfn_findclose(ffblk);
 else
  pds_dos_findclose(ffblk);
 pds_filehand_unlock_entrance();
#else
  if(ffblk->ff_data){
   #ifdef __WATCOMC__
   _dos_findclose((struct find_t *)ffblk->ff_data);
   //_findclose(ffblk->ff_handler);
   #endif
   pds_free(ffblk->ff_data);
  }
#endif
 ffblk->ff_data=NULL;
 funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS);
}

//----------------------------------------------------------------------
#include "in_file.h"

#define MAX_DIRECTORY_DEPTH  32
#define DIRSCAN_LOCFLAG_SCANSUBDIRS  32 // else scan one directory only
#define DIRSCAN_LOCFLAG_NEXTDIR      64

unsigned int pds_subdirscan_open(char *path_and_filename,unsigned int attrib,struct pds_subdirscan_t *dsi)
{
 unsigned int i;
 char *sd;

 pds_memset(dsi,0,sizeof(*dsi));

 dsi->ff=calloc(1,sizeof(struct pds_find_t));
 if(!dsi->ff)
  goto err_out_open;
 dsi->subdir_ff_datas=calloc(MAX_DIRECTORY_DEPTH,sizeof(struct pds_find_t));
 if(!dsi->subdir_ff_datas)
  goto err_out_open;
 dsi->subdir_names=calloc(MAX_DIRECTORY_DEPTH,sizeof(char *));
 if(!dsi->subdir_names)
  goto err_out_open;
 for(i=0;i<MAX_DIRECTORY_DEPTH;i++){
  dsi->subdir_names[i]=calloc(260,sizeof(char));
  if(!dsi->subdir_names[i])
   goto err_out_open;
 }
 dsi->subdir_masks=calloc(MAX_DIRECTORY_DEPTH,sizeof(char *));
 if(!dsi->subdir_masks)
  goto err_out_open;

 dsi->subdir_level=0;
 dsi->subdir_reach=-1;
 dsi->subfile_reach=-1;

 dsi->scan_attrib=attrib;
 pds_strcpy(dsi->startdir,path_and_filename);

 sd=&dsi->startdir[0];
 if(pds_getdrivenum_from_path(sd)>=0)
  sd+=2;
 if(sd[0]==PDS_DIRECTORY_SEPARATOR_CHAR)
  sd++;

 do{
  char *p=pds_strchr(sd,PDS_DIRECTORY_SEPARATOR_CHAR);
  if(p){
   unsigned int len=p-sd;
   if(len && (dsi->nb_subdirmasks || pds_strnchr(sd,'*',len) || pds_strnchr(sd,'?',len))){
    *p=0;
    dsi->subdir_masks[dsi->nb_subdirmasks++]=sd;
    if(sd[-1]==PDS_DIRECTORY_SEPARATOR_CHAR) // end of startdir
     sd[-1]=0;
   }
   sd=p+1;
  }else{
   pds_strcpy(dsi->scan_names,sd);
   if(sd[-1]==PDS_DIRECTORY_SEPARATOR_CHAR) // begin of scan_name
    sd[-1]=0;
   break;
  }
 }while(1);

 if(dsi->nb_subdirmasks && pds_strcmp(dsi->subdir_masks[dsi->nb_subdirmasks-1],"*.*")==0)
  funcbit_enable(dsi->flags,DIRSCAN_LOCFLAG_SCANSUBDIRS);

 pds_strcpy(dsi->currdir,dsi->startdir);

 return 0;

err_out_open:
 pds_subdirscan_close(dsi);
 return 1;
}

static void subdirscan_rebuild_currdir(struct pds_subdirscan_t *dsi)
{
 unsigned int i,len;

 pds_strcpy(dsi->prevdir,dsi->currdir);
 len=pds_strcpy(dsi->currdir,dsi->startdir);

 for(i=0;i<dsi->subdir_level;i++){
  if(dsi->currdir[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
   len+=pds_strcpy(&dsi->currdir[len],PDS_DIRECTORY_SEPARATOR_STR);
  len+=pds_strcpy(&dsi->currdir[len],dsi->subdir_names[i]);
 }
}

int pds_subdirscan_findnextfile(struct pds_subdirscan_t *dsi)
{
 unsigned int len,fferror;
 char searchfilename[300],searchdirname[300];

 if(!dsi->ff || !dsi->subdir_ff_datas || !dsi->subdir_names)
  return -1;

 funcbit_disable(dsi->flags,(SUBDIRSCAN_FLAG_SUBDIR|SUBDIRSCAN_FLAG_UPDIR));

 if(dsi->flags&DIRSCAN_LOCFLAG_NEXTDIR){ // skip to next directory
  struct pds_find_t *ffd=&dsi->subdir_ff_datas[dsi->subdir_level];
  if(dsi->subdir_reach<dsi->subdir_level){ // we were not here yet (in this directory), begin a new dirname search
   dsi->subdir_reach=dsi->subdir_level;
   len=pds_strcpy(searchdirname,dsi->currdir);
   if(searchdirname[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
    len+=pds_strcpy(&searchdirname[len],PDS_DIRECTORY_SEPARATOR_STR);
   if(dsi->subdir_level<dsi->nb_subdirmasks)
    pds_strcpy(&searchdirname[len],dsi->subdir_masks[dsi->subdir_level]);
   else
    pds_strcpy(&searchdirname[len],"*.*");
   fferror=pds_findfirst(searchdirname,_A_SUBDIR,ffd);
   while( (!(ffd->attrib&_A_SUBDIR)
         || (pds_strcmp("..",ffd->name)==0)
         || (pds_strcmp(".",ffd->name)==0))
    	 && !fferror){
    fferror=pds_findnext(ffd);
   }
  }else{ // continue a dirname search
   do{
    fferror=pds_findnext(ffd);
   }while( (!(ffd->attrib&_A_SUBDIR)
         || (pds_strcmp("..",ffd->name)==0)
         || (pds_strcmp(".",ffd->name)==0))
    	 && !fferror);
  }
  if(!fferror){ // go deeper
   if(dsi->subdir_level<(MAX_DIRECTORY_DEPTH-1)){
    pds_strcpy(dsi->subdir_names[dsi->subdir_level++],ffd->name);
    subdirscan_rebuild_currdir(dsi);
    funcbit_disable(dsi->flags,DIRSCAN_LOCFLAG_NEXTDIR); // read filenames from next dir
    funcbit_enable(dsi->flags,SUBDIRSCAN_FLAG_SUBDIR);
   }
   return 1;
  }else{
   if(dsi->subdir_level>0){ // go higher
    pds_findclose(ffd);
    dsi->subdir_level--;
    dsi->subdir_reach--;
    dsi->subfile_reach=dsi->subdir_reach;
    subdirscan_rebuild_currdir(dsi);
    funcbit_enable(dsi->flags,(DIRSCAN_LOCFLAG_NEXTDIR|SUBDIRSCAN_FLAG_UPDIR));
    return 1;
   }else
    return -1;
  }
 }

 // read filenames from the directory
 if(dsi->subfile_reach<dsi->subdir_level){
  dsi->subfile_reach=dsi->subdir_level;
  len=pds_strcpy(searchfilename,dsi->currdir);
  if(searchfilename[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
   len+=pds_strcpy(&searchfilename[len],PDS_DIRECTORY_SEPARATOR_STR);
  pds_strcpy(&searchfilename[len],dsi->scan_names);
  fferror=pds_findfirst(searchfilename,dsi->scan_attrib,dsi->ff);
 }else
  fferror=pds_findnext(dsi->ff);

 if(fferror)
  goto err_out_nextdir;

 if(dsi->ff->attrib&_A_SUBDIR)
  if(pds_strcmp(dsi->ff->name,"..")==0 || pds_strcmp(dsi->ff->name,".")==0)
   return 1;
 //if(dsi->scan_attrib && !(dsi->ff->attrib&dsi->scan_attrib))
 // return 1;

 len=pds_strcpy(dsi->fullname,dsi->currdir);
 if(dsi->fullname[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
  len+=pds_strcpy(&dsi->fullname[len],PDS_DIRECTORY_SEPARATOR_STR);
 pds_strcpy(&dsi->fullname[len],dsi->ff->name);

 return 0;

err_out_nextdir:
 pds_findclose(dsi->ff);
 if(!funcbit_test(dsi->flags,DIRSCAN_LOCFLAG_SCANSUBDIRS))
  return -1;
 funcbit_enable(dsi->flags,DIRSCAN_LOCFLAG_NEXTDIR);
 return 1;
}

void pds_subdirscan_close(struct pds_subdirscan_t *dsi)
{
 unsigned int i;
 if(dsi->ff){
  free(dsi->ff);
  dsi->ff=NULL;
 }
 if(dsi->subdir_ff_datas){
  for(i=0;i<dsi->subdir_level;i++)
   pds_findclose(&dsi->subdir_ff_datas[i]);
  free(dsi->subdir_ff_datas);
  dsi->subdir_ff_datas=NULL;
 }
 if(dsi->subdir_names){
  for(i=0;i<MAX_DIRECTORY_DEPTH;i++)
   if(dsi->subdir_names[i])
    free(dsi->subdir_names[i]);
  free(dsi->subdir_names);
  dsi->subdir_names=NULL;
 }
 if(dsi->subdir_masks){
  free(dsi->subdir_masks);
  dsi->subdir_masks=NULL;
 }
}

//----------------------------------------------------------------------
mpxp_int64_t pds_getfilesize(char *filename)
{
 mpxp_int64_t filesize;
 struct pds_find_t ffblk;
 if(pds_findfirst(filename,_A_NORMAL,&ffblk)!=0)
  return 0;
 filesize=ffblk.size;
 pds_findclose(&ffblk);
 return filesize;
}

//----------------------------------------------------------------------

static void pds_truename_select(char *fullname,char *name,int mode)
{
#ifdef __DOS__
 struct rminfo RMI;
#endif
 char strtmp[300];
 if(fullname!=NULL){
  if(fullname==name){
   pds_strcpy(strtmp,name);
   name=&strtmp[0];
  }
#ifdef __DOS__
  if(pds_filehand_check_entrance()){
   *name=0;
   return;
  }
  pds_filehand_lock_entrance();
  fullname[0]=0;
  if(is_lfn_support){
   pds_dpmi_rmi_clear(&RMI);
   RMI.EAX=0x00007160;
   RMI.ECX=mode;
   RMI.DS =dm_int2x_1.segment;
   RMI.ES =dm_int2x_2.segment;
   funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
   pds_strcpy(dm_int2x_1.linearptr,name);
   *((char *)(dm_int2x_2.linearptr))=0;
   pds_dpmi_realmodeint_call(0x21,&RMI);
   if(!(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)))
    pds_strcpy(fullname,dm_int2x_2.linearptr);
  }
  if(!fullname[0])
#endif
   _fullpath(fullname,name,270);
#ifdef __DOS__
  pds_filehand_unlock_entrance();
#endif
 }
}

// get long filename
static void pds_truename_lfn(char *longname,char *name)
{
 pds_truename_select(longname,name,2);
}

// get short filename
void pds_truename_dos(char *shortname,char *name)
{
 pds_truename_select(shortname,name,1);
}

//we already have got the fullpath (not in LFN), we just want to convert it to LFN (if possible)
/*void pds_truename(char *longname,char *name)
{
 if(is_lfn_support && uselfn)
  pds_truename_lfn(longname,name);
 else
  pds_strcpy(longname,name);
}*/

//drive functions
unsigned int asm_getdrive(void);

unsigned int pds_getdrive(void) // A=0,B=1,C=2,...
{
 unsigned int drivenum;
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return ('C');
 pds_filehand_lock_entrance();
#endif
#if defined(__DOS__) && defined(NEWFUNC_ASM) && defined(__WATCOMC__)
 #pragma aux asm_getdrive=\
 "mov ax,1900h"\
 "int 21h"\
 "and eax,0x000000ff"\
 value[eax] modify[eax ebx ecx edx edi esi];
 drivenum=asm_getdrive();
#else
 drivenum=_getdrive()-1;
#endif
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
 return drivenum;
}

void asm_setdrive(unsigned int);

void pds_setdrive(unsigned int drivenum) // A=0,B=1,C=2,...
{
#ifdef __DOS__
 if(pds_filehand_check_entrance())
  return;
 pds_filehand_lock_entrance();
#endif
#if defined(__DOS__) && defined(NEWFUNC_ASM) && defined(__WATCOMC__)
 #pragma aux asm_setdrive=\
 "mov edx,eax"\
 "mov ax,0e00h"\
 "int 21h"\
 parm[eax] modify[eax ebx ecx edx edi esi];
 asm_setdrive(drivenum);
#else
 unsigned int lastdrive;
 _dos_setdrive(drivenum+1,&lastdrive);
#endif
#ifdef __DOS__
 pds_filehand_unlock_entrance();
#endif
}

void pds_drive_getvolumelabel(unsigned int drivenum,char *volumename)
{
#ifdef __DOS__
 struct rminfo RMI;
 char lab[12];

 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x440D;
 RMI.EBX=drivenum+1;
 RMI.ECX=0x0866;
 RMI.DS =dm_int2x_1.segment;
 *((char *)(dm_int2x_1.linearptr))=0;
 volumename[0]=0;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)){
  char *vol;
  pds_strncpy(lab,dm_int2x_1.linearptr+6,11);
  lab[11]=0;
  vol=pds_strcutspc(lab);
  pds_strcpy(volumename,vol);
 }
 if(!volumename[0]){
  unsigned int error;
  char path[]="C:\\*.*";
  struct pds_find_t ffblk;

  path[2]=PDS_DIRECTORY_SEPARATOR_CHAR;
  path[0]='A'+drivenum;
  error=pds_findfirst(path,_A_VOLID,&ffblk);
  if(!error && (ffblk.attrib&_A_VOLID)){
   char *ext;
   pds_strcpy(volumename,ffblk.name);
   ext=pds_strrchr(volumename,'.');
   if(ext)
    pds_strcpy(ext,ext+1); // removes dot from disk volume name
  }
  pds_findclose(&ffblk);
 }
#endif
}

#ifndef __DOS__
#include <sys\stat.h>
#endif
extern unsigned int cdw_is_drive_cd(int);

//#define CHKDRIVE_DEBUG 1

unsigned int pds_chkdrive(unsigned int drive) // A=0 B=1 C=2 ...
{
#ifdef __DOS__
 unsigned int drivetype=DRIVE_TYPE_NONE;
 struct rminfo RMI;

 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x4408; // is drive removable (floppy)?
 RMI.EBX=drive+1;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_dpmi_realmodeint_call(0x21,&RMI);
#ifdef CHKDRIVE_DEBUG
 fprintf(stdout,"%c: 4408H:%8.8X flag:%d\n",(drive+'A'),RMI.EAX,((funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))? 1:0));
#endif
 if(!funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)){
  switch(RMI.EAX&0x0f){
   case 0x00:drivetype=DRIVE_TYPE_FLOPPY;break; // removable
   case 0x0f:return DRIVE_TYPE_NONE;            // invalid drive
   default  :drivetype=DRIVE_TYPE_HD;           // fixed
  }
 }

 if(drivetype==DRIVE_TYPE_FLOPPY){
  pds_dpmi_rmi_clear(&RMI);
  RMI.EAX=0x440e; // get logical drive mapping (of local drives)
  RMI.EBX=drive+1;
  funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
  pds_dpmi_realmodeint_call(0x21,&RMI);
  if(!(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))){
   unsigned int mapdrv=(RMI.EAX&0xff);
#ifdef CHKDRIVE_DEBUG
   fprintf(stdout,"%c: 440EH:%8.8X\n",(drive+'A'),RMI.EAX);
#endif
   if(mapdrv && (mapdrv!=(drive+1))) // phantom floppy
    return DRIVE_TYPE_NONE;
  }
 }

 if(cdw_is_drive_cd(drive)){ // is drive CD?
#ifdef CHKDRIVE_DEBUG
  fprintf(stdout,"%c: CD\n",(drive+'A'));
#endif
  return DRIVE_TYPE_CD;
 }
 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x4409; // is drive remote (network) or local?
 RMI.EBX=drive+1;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(!(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY))){
#ifdef CHKDRIVE_DEBUG
  fprintf(stdout,"%c: 4409H:%8.8X\n",(drive+'A'),RMI.EDX);
#endif
  if(RMI.EDX&0x1000)       // remote
   return DRIVE_TYPE_NETWORK;
 }

 return drivetype;

#else
 char path[4]="C:\\";
 struct stat buf;
 path[2]=PDS_DIRECTORY_SEPARATOR_CHAR;
 path[0]='A'+drive;
 buf.st_mode=S_IFDIR;
 if(stat(path,&buf)==0)
  return DRIVE_TYPE_HD;
 //if(access(path,F_OK)==0)
 // return DRIVE_TYPE_HD;
 if(cdw_is_drive_cd(drive))
  return DRIVE_TYPE_CD;
 return DRIVE_TYPE_NONE;
#endif
}

unsigned int pds_chkdir(char *path) // is path a directory?
{
 unsigned int retcode=0;
 char *p;
 struct pds_find_t ffblk;

 if(path[1]==':' && pds_getdrivenum_from_path(path)>=0) // d:
  if(path[2]==0 || (path[2]==PDS_DIRECTORY_SEPARATOR_CHAR && path[3]==0))
   return 1;
 if(path[0]=='.' && path[1]=='.' && path[2]==0) // ..
  return 1;
 if(path[0]=='.' && path[1]==0)                 // .
  return 1;
 if(path[0]==PDS_DIRECTORY_SEPARATOR_CHAR && path[1]==0) // backslash only
  return 1;
 p=pds_strrchr(path,PDS_DIRECTORY_SEPARATOR_CHAR); // backslash at end
 if(p && p[1]==0)
  return 1;

 if(pds_findfirst(path,_A_SUBDIR,&ffblk)==0){
  if(ffblk.attrib&_A_SUBDIR)
   retcode=1;
  pds_findclose(&ffblk);
 }
 return retcode;
}

/*unsigned int pds_abspath_make(char *abspath,char *currdir,char *relpath)
{
 int drive,rootdir;
 char *filename=NULL,rpath[MAX_PATHNAMELEN],cdir[MAX_PATHNAMELEN];

 pds_strcpy(rpath,relpath);
 relpath=&rpath[0];

 drive=pds_getdrivenum_from_path(relpath);
 rootdir=(rpath[(drive>=0) 2:0]==PDS_DIRECTORY_SEPARATOR_CHAR);

 if(drive && !rootdir)
  pds_getdcwd(

 pds_strcpy(abspath,currdir);


 return 1;
}*/

//-------------------------------------------------------------------------
unsigned int pds_network_check(void)
{
#ifdef __DOS__
 struct rminfo RMI;
 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x1100; // is network supported/installed
 pds_dpmi_realmodeint_call(0x2f,&RMI);
 return (RMI.EAX&0xff);
#else
 return 0;
#endif
}

int pds_network_query_assign(unsigned int index,char *drvname,unsigned int maxdrvlen,char *volname,unsigned int maxvollen)
{
#ifdef __DOS__
 struct rminfo RMI;
 pds_dpmi_rmi_clear(&RMI);
 RMI.EAX=0x5f02; // query network assign list entry
 RMI.EBX=index;
 RMI.DS =dm_int2x_1.segment;
 RMI.ES =dm_int2x_2.segment;
 *((char *)(dm_int2x_1.linearptr))=0;
 *((char *)(dm_int2x_2.linearptr))=0;
 funcbit_enable(RMI.flags,RMINFO_FLAG_CARRY);
 pds_dpmi_realmodeint_call(0x21,&RMI);
 if(funcbit_test(RMI.flags,RMINFO_FLAG_CARRY)) // fatal error (?) (or end of assign list)
  return -1;
 //if((RMI.EBX&0xff00)==0x0000) // is entry enabled (accessable)?
 // return RMI.EBX;
 if((RMI.EBX&0x00ff)!=0x0004) // is entry drive?
  return RMI.EBX;
 pds_strncpy(drvname,dm_int2x_1.linearptr,maxdrvlen);
 pds_strncpy(volname,dm_int2x_2.linearptr,maxvollen);
 return 0;
#else
 return 1;
#endif
}

//-------------------------------------------------------------------------
void pds_drives_flush(void)
{
#ifdef __DOS__
 union REGPACK regp;
 pds_newfunc_regp_clear(&regp);
 regp.w.ax=0x0d00;
 intr(0x21,&regp);
#endif
}
