/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



#define INCL_DOSEXCEPTIONS

#include <stdlib.h>

#ifdef __BORLANDC__
#include <alloc.h>
#else
#include <malloc.h>
#endif
#include <tools/debug.hxx>
#include <tools/list.hxx>
#include <tools/bigint.hxx>
#include <tools/fsys.hxx>
#include "comdep.hxx"

#ifdef OS2
#ifndef _VOS_MUTEX_HXX //autogen
#include <vos/mutex.hxx>
#endif
#endif

int Sys2SolarError_Impl( int nSysErr );

DECLARE_LIST( DirEntryList, DirEntry* );
DECLARE_LIST( FSysSortList, FSysSort* );
DECLARE_LIST( FileStatList, FileStat* );

static char sCaseMap[256];
static sal_Bool bCaseMap = FALSE;
static sal_Bool bDriveMap = FALSE;

struct DriveMapItem
{
    DirEntryKind    nKind;
    char            cName;
    FSysPathStyle   nStyle;
};

void CreateCaseMapImpl();
void CreateDriveMapImpl();

static DriveMapItem aDriveMap[26];

static sal_Bool   bLastCaseSensitive    = FALSE;

//====================================================================

int ApiRet2ToSolarError_Impl( int nApiRet )
{
    switch ( nApiRet )
    {
        case NO_ERROR:                      return ERRCODE_NONE;
        case ERROR_FILE_NOT_FOUND:          return ERRCODE_IO_NOTEXISTS;
        case ERROR_PATH_NOT_FOUND:          return ERRCODE_IO_NOTEXISTSPATH;
        case ERROR_TOO_MANY_OPEN_FILES:     return ERRCODE_IO_TOOMANYOPENFILES;
        case ERROR_ACCESS_DENIED:           return ERRCODE_IO_ACCESSDENIED;
        case ERROR_NOT_ENOUGH_MEMORY:       return ERRCODE_IO_OUTOFMEMORY;
        case ERROR_BAD_FORMAT:              return ERRCODE_IO_WRONGFORMAT;
        case ERROR_NOT_SAME_DEVICE:         return ERRCODE_IO_INVALIDDEVICE;
        case ERROR_WRITE_PROTECT:           return ERRCODE_IO_INVALIDDEVICE;
        case ERROR_BAD_UNIT:                return ERRCODE_IO_INVALIDDEVICE;
        case ERROR_CRC:                     return ERRCODE_IO_INVALIDDEVICE;
        case ERROR_NOT_DOS_DISK:            return ERRCODE_IO_INVALIDDEVICE;
        case ERROR_WRITE_FAULT:             return ERRCODE_IO_CANTWRITE;
        case ERROR_READ_FAULT:              return ERRCODE_IO_CANTREAD;
        case ERROR_SHARING_VIOLATION:       return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_LOCK_VIOLATION:          return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_WRONG_DISK:              return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_HANDLE_DISK_FULL:        return ERRCODE_IO_OUTOFSPACE;
        case ERROR_NOT_SUPPORTED:           return ERRCODE_IO_NOTSUPPORTED;
        case ERROR_DUP_NAME:                return ERRCODE_IO_ALREADYEXISTS;
        case ERROR_BAD_NETPATH:             return ERRCODE_IO_NOTEXISTSPATH;
        case ERROR_DEV_NOT_EXIST:           return ERRCODE_IO_NOTEXISTS;
        case ERROR_NETWORK_ACCESS_DENIED:   return ERRCODE_IO_ACCESSDENIED;
        case ERROR_INVALID_PARAMETER:       return ERRCODE_IO_INVALIDPARAMETER;
        case ERROR_NET_WRITE_FAULT:         return ERRCODE_IO_CANTWRITE;
        case ERROR_DEVICE_IN_USE:           return ERRCODE_IO_INVALIDPARAMETER;
        case ERROR_DISK_FULL:               return ERRCODE_IO_OUTOFSPACE;
        case ERROR_BAD_ARGUMENTS:           return ERRCODE_IO_INVALIDPARAMETER;
        case ERROR_BAD_PATHNAME:            return ERRCODE_IO_NOTEXISTSPATH;
        case ERROR_LOCK_FAILED:             return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_LOCKED:                  return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_DUPLICATE_NAME:          return ERRCODE_IO_ALREADYEXISTS;
        case ERROR_DIRECTORY_IN_CDS:        return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_CURRENT_DIRECTORY:       return ERRCODE_IO_LOCKVIOLATION;
        case ERROR_FILENAME_EXCED_RANGE:    return ERRCODE_IO_NAMETOOLONG;
    }

    DBG_TRACE1( "FSys: unknown apiret error %d occurred", nApiRet );
    return FSYS_ERR_UNKNOWN;
}

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

char* volumeid( const char* pPfad )
{
    static FSINFO   aFSInfoBuf;
    ULONG           ulFSInfoLevel = FSIL_VOLSER;
    ULONG           nDriveNumber;

    nDriveNumber = toupper(*pPfad) - 'A' + 1;

    if ( nDriveNumber >= 3 )
    {
        APIRET rc = DosQueryFSInfo(
            nDriveNumber, ulFSInfoLevel, &aFSInfoBuf, sizeof(FSINFO) );
        if ( rc )
            return 0;
        return (char*) aFSInfoBuf.vol.szVolLabel;
    }
    return 0;
}

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

/*************************************************************************
|*
|*    DirEntry::ToAbs()
|*
|*    Beschreibung      FSYS.SDW
|*    Ersterstellung    MI 26.04.91
|*    Letzte Aenderung  MA 02.12.91 13:30
|*
*************************************************************************/

sal_Bool DirEntry::ToAbs()
{
    DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );

    if ( FSYS_FLAG_VOLUME == eFlag )
    {
        eFlag = FSYS_FLAG_ABSROOT;
        return TRUE;
    }

    if ( IsAbs() )
        return TRUE;

	char sBuf[_MAX_PATH + 1];
	*this = DirEntry( String( getcwd( sBuf, _MAX_PATH ), osl_getThreadTextEncoding() ) ) + *this;

    return IsAbs();
}

/*************************************************************************
|*
|*    DirEntry::GetVolume()
|*
|*    Beschreibung      FSYS.SDW
|*    Ersterstellung    MI 04.03.92
|*    Letzte Aenderung  MI 04.03.92
|*
*************************************************************************/

String DirEntry::GetVolume() const
{
    DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
	
    String aRet;
    const DirEntry *pTop = ImpGetTopPtr();
    ByteString aName = ByteString( pTop->aName ).ToLowerAscii();

    if ( ( pTop->eFlag == FSYS_FLAG_ABSROOT ||
           pTop->eFlag == FSYS_FLAG_RELROOT ||
           pTop->eFlag == FSYS_FLAG_VOLUME )
         && aName != "a:" && aName != "b:" && Exists() )
    {
        const char *pVol;
        pVol = volumeid( (char*) pTop->aName.GetBuffer() );
		if (pVol)
			aRet = String( pVol, osl_getThreadTextEncoding());
    }

    return aRet;
}

/*************************************************************************
|*
|*    DirEntry::SetCWD()
|*
|*    Beschreibung      FSYS.SDW
|*    Ersterstellung    MI 26.04.91
|*    Letzte Aenderung  MI 21.05.92
|*
*************************************************************************/

sal_Bool DirEntry::SetCWD( sal_Bool bSloppy ) const
{
    DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );

    if ( eFlag == FSYS_FLAG_CURRENT && !aName.Len() )
        return TRUE;

    if ( !chdir(ByteString(GetFull(), osl_getThreadTextEncoding()).GetBuffer()) )
    {
        //nError = FSYS_ERR_OK;
        return TRUE;
    }

    if ( bSloppy && pParent &&
         !chdir(ByteString(pParent->GetFull(), osl_getThreadTextEncoding()).GetBuffer()) )
    {
        //nError = FSYS_ERR_OK;
        return TRUE;
    }

    //nError = FSYS_ERR_NOTADIRECTORY;
    return FALSE;
}

/*************************************************************************
|*
|*    DirEntry::MoveTo()
|*
|*    Beschreibung      FSYS.SDW
|*    Ersterstellung    MI 26.04.91
|*    Letzte Aenderung  MA 02.12.91 14:07
|*
*************************************************************************/

#if 0 // YD see dirent.cxx
sal_Bool createLongNameEA( const PCSZ pszPath, ULONG ulAttributes, const String& aLongName );

FSysError DirEntry::MoveTo( const DirEntry& rDest ) const
{
    DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );

    DirEntry aTmpDest(rDest);
    FileStat aTmpStat(aTmpDest);
    if ( aTmpStat.IsKind(FSYS_KIND_DIR) )
        aTmpDest += DirEntry( GetName() );

    String aSource( GetFull() );
    String aDest( aTmpDest.GetFull() );
    String aShortSource("");
    String aShortDest("");

    if (Folder::IsAvailable())
    {
        if  (IsLongNameOnFAT())
        {
            // in kurzen Pfad wandeln
            ItemIDPath      aItemIDPath(aSource);
            aShortSource = aItemIDPath.GetHostNotationPath();
        }
        if  (rDest.IsLongNameOnFAT())
        {
            // in kurzen Pfad wandeln
            ItemIDPath      aItemIDPath(aDest);
            aShortDest = aItemIDPath.GetHostNotationPath();
        }
    }

    APIRET nRet = DosMove( aShortSource.Len()>0?(PSZ)aShortSource.GetStr():(PSZ)aSource.GetStr(),
                           aShortDest.Len()>0?(PSZ)aShortDest.GetStr():(PSZ)aDest.GetStr());

    if ( nRet == ERROR_DIRECTORY_IN_CDS ||
         nRet == ERROR_CURRENT_DIRECTORY )
    {
        // 2nd chance with modified CWD
        DosSetCurrentDir( (PSZ) "\\" );
        nRet = DosMove( aShortSource.Len()>0?(PSZ)aShortSource.GetStr():(PSZ)aSource.GetStr(),
                        aShortDest.Len()>0?(PSZ)aShortDest.GetStr():(PSZ)aDest.GetStr());
    }
    else if ( nRet == ERROR_NOT_SAME_DEVICE )
    {
        // other volume => copy+delete
        FileCopier aMover( *this, rDest );
        nRet = aMover.Execute( FSYS_ACTION_MOVE|FSYS_ACTION_RECURSIVE );
        return nRet;
    }

    if ( (nRet==NO_ERROR) && aShortDest.Len()>0)
    {
	createLongNameEA((const char*)aShortDest,  FILE_NORMAL, rDest.GetName());
    }

    return ApiRet2ToSolarError_Impl( nRet );
}
#endif // 0

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

USHORT DirReader_Impl::Init()
{
    // Block-Devices auflisten?
    if ( pDir->eAttrMask & FSYS_KIND_BLOCK )
    {
        CreateDriveMapImpl();
        // CWD merken
        DirEntry aCurrentDir;
        aCurrentDir.ToAbs();

        // einzeln auf Existenz und Masken-konformit"at pr"ufen
        USHORT nRead = 0;
        char sDrive[3] = { '?', ':', 0 };
        char sRoot[4] = { '?', ':', '\\', 0 };
        for ( char c = 'a'; c <= 'z'; c++ )
        {
            sDrive[0] = c;
            sRoot[0] = c;
            DirEntry* pDrive = new DirEntry( sDrive, FSYS_FLAG_VOLUME, FSYS_STYLE_HOST );
            if ( pDir->aNameMask.Matches( String( ByteString(sDrive), osl_getThreadTextEncoding())) 
				&& aDriveMap[c-'a'].nKind != FSYS_KIND_UNKNOWN )
            {
                if ( pDir->pStatLst ) //Status fuer Sort gewuenscht?
                {
                    FileStat *pNewStat = new FileStat( *pDrive );
                    pDir->ImpSortedInsert( pDrive, pNewStat );
                }
                else
                    pDir->ImpSortedInsert( pDrive, NULL );
                ++nRead;
            }
            else
                delete pDrive;
        }

        // CWD restaurieren
        aCurrentDir.SetCWD();
        return nRead;
    }

    return 0;
}

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

USHORT DirReader_Impl::Read()
{
	if (!pDosDir)
	{
		pDosDir = opendir( (char*) ByteString(aPath, osl_getThreadTextEncoding()).GetBuffer() );
	}

	if (!pDosDir)
	{
		bReady = TRUE;
		return 0;
	}

    // Directories und Files auflisten?
	if ( ( pDir->eAttrMask & FSYS_KIND_DIR || pDir->eAttrMask & FSYS_KIND_FILE ) &&
		 ( ( pDosEntry = readdir( pDosDir ) ) != NULL ) )
	{
		String aD_Name(pDosEntry->d_name, osl_getThreadTextEncoding());
        if ( pDir->aNameMask.Matches( aD_Name  ) )
        {
			DirEntryFlag eFlag =
					0 == strcmp( pDosEntry->d_name, "." ) ? FSYS_FLAG_CURRENT
				:	0 == strcmp( pDosEntry->d_name, ".." ) ? FSYS_FLAG_PARENT
				:	FSYS_FLAG_NORMAL;
            DirEntry *pTemp = new DirEntry( ByteString(pDosEntry->d_name), eFlag, FSYS_STYLE_UNX );
            if ( pParent )
                pTemp->ImpChangeParent( new DirEntry( *pParent ), FALSE);
            FileStat aStat( *pTemp );
            if ( ( ( ( pDir->eAttrMask & FSYS_KIND_DIR ) &&
					 ( aStat.IsKind( FSYS_KIND_DIR ) ) ) ||
				   ( ( pDir->eAttrMask & FSYS_KIND_FILE ) &&
					 !( aStat.IsKind( FSYS_KIND_DIR ) ) ) ) &&
				 !( pDir->eAttrMask & FSYS_KIND_VISIBLE &&
					pDosEntry->d_name[0] == '.' ) )
            {
                if ( pDir->pStatLst ) //Status fuer Sort gewuenscht?
                    pDir->ImpSortedInsert( pTemp, new FileStat( aStat ) );
                else
                    pDir->ImpSortedInsert( pTemp, NULL );;
				return 1;
            }
            else
                delete pTemp;
        }
	}
	else
		bReady = TRUE;
	return 0;
}

/*************************************************************************
|*
|*    FileStat::FileStat()
|*
|*    Beschreibung      FSYS.SDW
|*    Ersterstellung    MA 05.11.91
|*    Letzte Aenderung  MA 07.11.91
|*
*************************************************************************/

FileStat::FileStat( const void *pInfo,      // struct dirent
                    const void * ):         // dummy
        aDateCreated(0),
        aTimeCreated(0),
        aDateModified(0),
        aTimeModified(0),
        aDateAccessed(0),
        aTimeAccessed(0)
{
    struct dirent *pDirent = (struct dirent*) pInfo;

    nSize = pDirent->d_size;

    aDateCreated  = MsDos2Date( (FDATE*) &pDirent->d_date );
    aTimeCreated  = MsDos2Time( (FTIME*) &pDirent->d_time );
    aDateModified = aDateModified;
    aTimeModified = aTimeModified;
    aDateAccessed = aDateModified;
    aTimeAccessed = aTimeModified;

    nKindFlags = FSYS_KIND_FILE;
    if ( pDirent->d_type & DOS_DIRECT )
        nKindFlags = FSYS_KIND_DIR;
}

/*************************************************************************
|*
|*    FileStat::Update()
|*
|*    Beschreibung      FSYS.SDW
|*    Ersterstellung    MI 11.06.91
|*    Letzte Aenderung  MA 07.11.91
|*
*************************************************************************/

struct _FSYS_FSQBUFFER
{
    FSQBUFFER2  aBuf;
    UCHAR       sBuf[256];
};

sal_Bool FileStat::Update( const DirEntry& rDirEntry, sal_Bool bAccessRemovableDevice )
{
    nSize = 0;
    FSysPathStyle eStyle = FSYS_STYLE_UNKNOWN;
    aCreator.Erase();
    aType.Erase();
    aDateCreated = Date(0);
    aTimeCreated = Time(0);
    aDateModified = Date(0);
    aTimeModified = Time(0);
    aDateAccessed = Date(0);
    aTimeAccessed = Time(0);

	if ( !rDirEntry.IsValid() )
	{
		nError = FSYS_ERR_NOTEXISTS;
		return FALSE;
	}

	// Sonderbehandlung falls es sich um eine Root ohne Laufwerk handelt
	if ( !rDirEntry.aName.Len() && rDirEntry.eFlag == FSYS_FLAG_ABSROOT )
	{
		nKindFlags = FSYS_KIND_DIR;
		nError = FSYS_ERR_OK;
		return TRUE;
	}

    // Sonderbehandlung falls es sich um eine Wildcard handelt
    ByteString aTempName( rDirEntry.GetName(), osl_getThreadTextEncoding() );
    if ( strchr( aTempName.GetBuffer(), '?' ) ||
         strchr( aTempName.GetBuffer(), '*' ) ||
         strchr( aTempName.GetBuffer(), ';' ) )
    {
        nKindFlags = FSYS_KIND_WILD;
        nError = FSYS_ERR_OK;
        return TRUE;
    }

    // Sonderbehandlung falls es sich um eine Root handelt
    if ( rDirEntry.eFlag == FSYS_FLAG_VOLUME ||
         rDirEntry.eFlag == FSYS_FLAG_ABSROOT )
    {
        if ( rDirEntry.eFlag == FSYS_FLAG_VOLUME )
            nKindFlags = FSYS_KIND_DEV;
        else
            nKindFlags = FSYS_KIND_DIR;

        if ( rDirEntry.aName.Len() == 2 )
        {
            if ( !bDriveMap )
                CreateDriveMapImpl();

			ByteString rDirEntryUpperCase = ByteString( rDirEntry.aName ).ToUpperAscii();
            DriveMapItem &rItem = aDriveMap[rDirEntryUpperCase.GetChar(0) - 'A'];
            if ( !rItem.nKind )
            {
                nError = ERRCODE_IO_INVALIDDEVICE;
                                nKindFlags = FSYS_KIND_UNKNOWN;
                return FALSE;
            }
            else
            {
                if ( rDirEntry.eFlag == FSYS_FLAG_VOLUME )
                    nKindFlags |= FSYS_KIND_BLOCK | rItem.nKind;
                eStyle = rItem.nStyle;
            }
        }

        nError = FSYS_ERR_OK;
        return TRUE;
    }

    // disable error-boxes for hard-errors
    DosError(FERR_DISABLEHARDERR);

    // Statusinformation vom Betriebssystem holen
    DirEntry    aTempDirEntry( rDirEntry );
    char*       p;

    aTempDirEntry.ToAbs();
    ByteString aFullName( aTempDirEntry.GetFull(), osl_getThreadTextEncoding() );

#if 0 // YD
	if (Folder::IsAvailable() && aTempDirEntry.IsLongNameOnFAT())
	{
		// in String mit kurzem Pfad wandeln
		ItemIDPath aItemIDPath(aTempDirEntry.GetFull());
		aFullName = ByteString( aItemIDPath.GetHostNotationPath(), osl_getThreadTextEncoding() );
	}
#endif

    p = (char *) aFullName.GetBuffer();

    FILESTATUS3 filestat;
    memset( &filestat, 0, sizeof( filestat ) );
    if( DosQueryPathInfo( (PSZ)p, 1, &filestat, sizeof( filestat ) ) )
    {
        nError = FSYS_ERR_NOTEXISTS;
        nKindFlags = FSYS_KIND_UNKNOWN;
        return FALSE;
    }

    nError = FSYS_ERR_OK;
    nSize = filestat.cbFile;

    nKindFlags = FSYS_KIND_UNKNOWN;
    if( filestat.attrFile & FILE_DIRECTORY )
        nKindFlags |= FSYS_KIND_DIR;
    if ( nKindFlags == FSYS_KIND_UNKNOWN )
        nKindFlags = nKindFlags | FSYS_KIND_FILE;

    aDateModified = Date( filestat.fdateLastWrite.day,
                          filestat.fdateLastWrite.month,
                          filestat.fdateLastWrite.year + 1980 );

    aTimeModified = Time( filestat.ftimeLastWrite.hours,
                          filestat.ftimeLastWrite.minutes,
                          filestat.ftimeLastWrite.twosecs*2 );

    if ( filestat.fdateCreation.day )
    {
        aDateCreated  = Date( filestat.fdateCreation.day,
                              filestat.fdateCreation.month,
                              filestat.fdateCreation.year + 1980 );

        aTimeCreated  = Time( filestat.ftimeCreation.hours,
                              filestat.ftimeCreation.minutes,
                              filestat.ftimeCreation.twosecs*2 );
    }
    else
    {
        aDateCreated = aDateModified;
        aTimeCreated = aTimeModified;
    }

    if ( filestat.fdateLastAccess.day > 0 )
    {
        aDateAccessed = Date( filestat.fdateLastAccess.day,
                              filestat.fdateLastAccess.month,
                              filestat.fdateLastAccess.year + 1980 );

        aTimeAccessed = Time( filestat.ftimeLastAccess.hours,
                                filestat.ftimeLastAccess.minutes,
                                filestat.ftimeLastAccess.twosecs*2 );
    }
    else
    {
        aDateAccessed = aDateModified;
        aTimeAccessed = aTimeModified;
    }

    return TRUE;
}

sal_Bool IsRedirectable_Impl( const ByteString &rPath )
{
    if ( rPath.Len() >= 3 && ':' == rPath.GetBuffer()[1] )
    {
        ByteString aVolume = rPath.Copy( 0, 3 );
        DriveMapItem &rItem = aDriveMap[toupper(aVolume.GetChar(0)) - 'A'];
        return FSYS_KIND_FIXED != rItem.nKind;
    }
    return FALSE;
}

#if 0
sal_Bool IsRedirectable_Impl( const String &rPath )
{
    if ( rPath.Len() >= 3 && ':' == rPath.GetStr()[1] )
    {
        DriveMapItem &rItem = aDriveMap[toupper(rPath[0]) - 'A'];
        return FSYS_KIND_FIXED != rItem.nKind;
    }
    return FALSE;
}
#endif


/*************************************************************************
|*
|*    TempDirImpl()
|*
|*    Beschreibung      liefert den Namens des Directories fuer temporaere
|*                      Dateien
|*    Ersterstellung    MI 16.03.94
|*    Letzte Aenderung  MI 16.03.94
|*
*************************************************************************/

const char* TempDirImpl( char *pBuf )
{
    PSZ         pVar;
    USHORT      nRet;
    sal_Bool        bAppendTemp = FALSE; // mu\s noch \\temp angeh"angt werden

    // Erstmal sehen, ob TEMP oder TMP gesetzt sind
    nRet = DosScanEnv( (PSZ)"TEMP", &pVar );
    if( nRet )
        nRet = DosScanEnv( (PSZ)"temp", &pVar );
    if( nRet )
        nRet = DosScanEnv( (PSZ)"TMP", &pVar );
    if( nRet )
        nRet = DosScanEnv( (PSZ)"tmp", &pVar );
    if( nRet )
        nRet = DosScanEnv( (PSZ)"TMPDIR", &pVar );

    // falls das geklappt hat, und ein Backslash dranhaengt,
    // oder falls es bisher nicht geklappt hat,
    // muessen wir nachher einen Backslash entfernen
    sal_Bool bRemoveBS = nRet || *(pVar+strlen(pVar)-1) == '\\';

    // Keine temp-Variable gefunden, dann gehen wir mal auf die Suche
    // nach dem System-Laufwerk
    if( nRet )
    {
        nRet = DosScanEnv( (PSZ)"USER_INI",&pVar );
        bAppendTemp = (0 == nRet);
    }
    if( nRet )
    {
        nRet = DosScanEnv( (PSZ)"SYSTEM_INI", &pVar );
        bAppendTemp = (0 == nRet);
    }
    if( nRet )
        // Wenn das immer noch nicht reicht nehmen wir eben die Root von C:
#ifdef __BORLANDC__
        pVar = (PSZ)"c:\\temp\\";
#else
        pVar = (PCSZ)"c:\\temp\\";
#endif
    strcpy( pBuf, (const char*)pVar );

    // jetzt haengt ggf. ein Backlash dran, den wir abschneiden,
    // ggf. inklusive dahinter haengendem Dateinamen
    if ( bRemoveBS )
    {
        char *pTail = pBuf + strlen(pBuf) - 1;
        for ( char cLast = *pTail; cLast != '\\'; cLast = *(--pTail) )
            *pTail = 0;
    }

    if ( bAppendTemp )
        strcat( pBuf, "\\temp" );
    DirEntry( pBuf ).MakeDir();

    return pBuf;
}

#define CURRENT_COUNTRY 0
#define NLS_CODEPAGE 850

/*====================================================================
 * CreateCaseMapImpl()
 * creates a map of each character to convert to lower
 *--------------------------------------------------------------------*/

#if 0
void CreateCaseMapImpl()
{
    // build a string starting with code 0 as first character up to 255
    char sTemp[256];
	USHORT n;
	
    for ( n = 0; n < 256; ++n )
        sTemp[n] = (char) n;

    // convert string to upper case
    COUNTRYCODE aCountry;
    aCountry.country = CURRENT_COUNTRY;   /* Country code */
    aCountry.codepage = NLS_CODEPAGE;     /* Code page */
    DosMapCase( 255, &aCountry, sTemp+1 );

    // fill a global buffer starting with code 0 as first character up to 255
    for ( n = 0; n < 256; ++n )
        sCaseMap[n] = (char) n;

    // reorder by upper-code and store in a global buffer
    for ( n = 255; n > 0; --n )
        // was this character converted?
        if ( sTemp[n] != (char) n )
            // we found a conversion from upper to lower
            sCaseMap[ (unsigned char) sTemp[n] ] = (char) n;

    bCaseMap = TRUE;
}

String ToLowerImpl( const String& rSource )
{
    if ( !bCaseMap )
        CreateCaseMapImpl();

    // TH sagt: International ist zu langsam, also mit einer eigenen Map
    ByteString aLower( rSource );
    for ( USHORT n = 0; n < aLower.Len(); ++n )
        aLower[n] = sCaseMap[ (unsigned char) aLower[n] ];
    return aLower;
}
#endif // 0

/*====================================================================
 * CreateDriveMapImpl()
 * creates a map of drive-infos like FileSystem (style) and Kind (remote)
 *--------------------------------------------------------------------*/
typedef struct _FSQBUFFER_
{
    FSQBUFFER2  aBuf;
    UCHAR       sBuf[64];
} FSQBUFFER_;

void CreateDriveMapImpl()
{
#ifdef POWERPC
    // !!!!! Hack, da der untere Teil mit Beta 2 noch abstuertzt !!!!!
    BYTE nFloppies = 1;
    for ( USHORT nDrive = 0; nDrive < 26; ++nDrive )
    {
        if ( nDrive < nFloppies )
        {
            aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE;
            aDriveMap[nDrive].nStyle = FSYS_STYLE_FAT;
        }
        else
        {
            aDriveMap[nDrive].nKind = FSYS_KIND_UNKNOWN;
            aDriveMap[nDrive].nStyle = FSYS_STYLE_UNKNOWN;
        }
    }

    aDriveMap[2].nKind = FSYS_KIND_FIXED;
    aDriveMap[2].nStyle = FSYS_STYLE_FAT;
#else
    FSQBUFFER_	aBuf;
    ULONG       nBufLen;
    APIRET      nRet;
	USHORT 		nDrive;

    // disable error-boxes for hard-errors
    DosError(FERR_DISABLEHARDERR);

    // determine number of floppy-drives
    PM_BYTE nFloppies;
    nRet = DosDevConfig( (void*) &nFloppies, DEVINFO_FLOPPY );

    // reset the map
    for ( nDrive = 0; nDrive < 26; ++nDrive )
    {
        if ( nDrive < nFloppies )
        {
            aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE;
            aDriveMap[nDrive].nStyle = FSYS_STYLE_FAT;
        }
        else
        {
            aDriveMap[nDrive].nKind = FSYS_KIND_UNKNOWN;
            aDriveMap[nDrive].nStyle = FSYS_STYLE_UNKNOWN;
        }
    }

    // determine file-system via DosOpen/DocDevIOCtrl
    for ( nDrive = 2; nDrive < 26; ++nDrive )
    {
        // open drive
        sal_Bool bFixed;
        HFILE nDevHandle;
        char pDriveName[3] = "#:";
        pDriveName[0] = nDrive+'a';
        ULONG nAction;
        nRet = DosOpen( (PSZ) pDriveName, &nDevHandle,
            &nAction, 0, 0, OPEN_ACTION_OPEN_IF_EXISTS,
            OPEN_FLAGS_DASD|OPEN_SHARE_DENYNONE|OPEN_ACCESS_READONLY,
            0 );

        // exists?
        if ( !nRet )
        {
            // removeable?
            PM_BYTE nDriveId = nDrive;
            ULONG nParaOutLen, nDataOutLen;
            nRet = DosDevIOCtl(nDevHandle, 8, 0x20,
                &nDriveId, sizeof(nDriveId), &nParaOutLen,
                &bFixed, sizeof(bFixed), &nDataOutLen );

            // prepare the drive-map
            if ( !nRet && !bFixed )
                aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE;

            // close drive
            DosClose(nDevHandle);
        }
        else if ( nRet == ERROR_NOT_READY )
            aDriveMap[nDrive].nKind = FSYS_KIND_REMOVEABLE | FSYS_KIND_CDROM;
    }

    // determine file-system via FSAttach
    nRet = 0;
    for ( USHORT n = 3; nRet != ERROR_NO_MORE_ITEMS; ++n )
    {
        nBufLen = sizeof( aBuf );
        nRet = DosQueryFSAttach( 0, n, FSAIL_DRVNUMBER,
            (_FSQBUFFER2*) &aBuf, &nBufLen );
        if ( !nRet )
        {
            nDrive = toupper(aBuf.aBuf.szName[0]) - 'A';

            if ( aDriveMap[nDrive].nKind == FSYS_KIND_UNKNOWN )
                aDriveMap[nDrive].nKind =
                    aBuf.aBuf.iType == 3 ? FSYS_KIND_FIXED :
                    aBuf.aBuf.iType == 4 ? FSYS_KIND_REMOTE :
                    FSYS_KIND_UNKNOWN;

            char *pType = (char*)(aBuf.aBuf.szName + aBuf.aBuf.cbName + 1);
            aDriveMap[nDrive].nStyle =
                strcmp( pType, "FAT" ) == 0 ? FSYS_STYLE_FAT :
                strcmp( pType, "FAT32" ) == 0 ? FSYS_STYLE_VFAT :
                strcmp( pType, "NTFS" ) == 0 ? FSYS_STYLE_NTFS :
                strcmp( pType, "HPFS" ) == 0 ? FSYS_STYLE_HPFS :
                strcmp( pType, "JFS" ) == 0 ? FSYS_STYLE_HPFS :
                strcmp( pType, "RAMFS" ) == 0 ? FSYS_STYLE_HPFS :
                strcmp( pType, "NDFS32" ) == 0 ? FSYS_STYLE_HPFS :
                strcmp( pType, "NWFS" ) == 0 ? FSYS_STYLE_NWFS :
				strcmp( pType, "EXT2" ) == 0 ? FSYS_STYLE_UNX :
				strcmp( pType, "NFS" ) == 0 ? FSYS_STYLE_UNX :
                FSYS_STYLE_UNKNOWN;
            if ( strcmp( pType, "CDFS" ) == 0 )
                aDriveMap[nDrive].nKind = FSYS_KIND_CDROM|FSYS_KIND_REMOVEABLE;
        }
    }
#endif

    bDriveMap = TRUE;
}

Time MsDos2Time( const time_t *pTimeT )
{
    tm *pTm = localtime( pTimeT );
    if ( pTm )
        return Time( pTm->tm_hour, pTm->tm_min, pTm->tm_sec );
    else
        return Time(0);
}

Date MsDos2Date( const time_t *pTimeT )
{
    tm *pTm = localtime( pTimeT );
    if ( pTm )
        return Date( pTm->tm_mday, pTm->tm_mon + 1, pTm->tm_year );
    else
        return Date(0);
}

/*************************************************************************
|*
|*    DirEntry::GetPathStyle() const
|*
|*    Beschreibung
|*    Ersterstellung    MI 11.05.95
|*    Letzte Aenderung  MI 11.05.95
|*
*************************************************************************/

FSysPathStyle DirEntry::GetPathStyle( const String &rDevice )
{
    ByteString aRootDir(rDevice, osl_getThreadTextEncoding());
    // UNC-Pathname?
    if ( aRootDir.Len()==0 || ( aRootDir.Len() > 1 && aRootDir.GetChar(1) != ':' ) )
        return FSYS_STYLE_UNKNOWN;

    if ( !bDriveMap )
        CreateDriveMapImpl();
    return aDriveMap[toupper(aRootDir.GetChar(0)) - 'A'].nStyle;
}

/*************************************************************************
|*
|*    DirEntry::IsCaseSensitive() const
|*
|*    Beschreibung
|*    Ersterstellung    TPF 26.02.1999
|*    Letzte Aenderung  
|*
*************************************************************************/

sal_Bool DirEntry::IsCaseSensitive( FSysPathStyle eFormatter ) const
{
	if (eFormatter==FSYS_STYLE_HOST)
	{
		if 	(GetPathStyle(GetDevice().GetName()) == FSYS_STYLE_UNX)
		{
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}
	else
	{
		sal_Bool isCaseSensitive = FALSE;			// ich bin unter OS2, also ist der default im Zweifelsfall case insensitiv
		switch ( eFormatter )
		{
			case FSYS_STYLE_MAC:
			case FSYS_STYLE_FAT:
			case FSYS_STYLE_VFAT:
			case FSYS_STYLE_NTFS:
			case FSYS_STYLE_NWFS:
			case FSYS_STYLE_HPFS:
			case FSYS_STYLE_DETECT:
				{
					isCaseSensitive = FALSE;
					break;
				}
			case FSYS_STYLE_SYSV:
			case FSYS_STYLE_BSD:
				{
					isCaseSensitive = TRUE;
					break;
				}
			default:
				{
					isCaseSensitive = FALSE;	// ich bin unter OS2, also ist der default im Zweifelsfall case insensitiv
					break;
				}
		}
		return isCaseSensitive;
	}
}




//=========================================================================

ErrCode FileStat::QueryDiskSpace( const String &rPath,
                                  BigInt &rFreeBytes, BigInt &rTotalBytes )
{
    FSALLOCATE aFSInfoBuf;
    ByteString aVol( DirEntry(rPath).ImpGetTopPtr()->GetName(), osl_getThreadTextEncoding());
    ULONG nDriveNumber = toupper( aVol.GetChar(0) ) - 'A' + 1;

    APIRET rc = DosQueryFSInfo( nDriveNumber, FSIL_ALLOC,
                                &aFSInfoBuf, sizeof(aFSInfoBuf) );
    if ( rc )
        return Sys2SolarError_Impl( rc );

    BigInt aBytesPerCluster( BigInt(aFSInfoBuf.cbSector) *
                             BigInt(aFSInfoBuf.cSectorUnit) );
    rFreeBytes = aBytesPerCluster * BigInt(aFSInfoBuf.cUnitAvail);
    rTotalBytes = aBytesPerCluster * BigInt(aFSInfoBuf.cUnit);
    return 0;
}

//=========================================================================

void FSysEnableSysErrorBox( sal_Bool bEnable )
{
    DosError( bEnable ? 0 : FERR_DISABLEHARDERR );
}

