Subversion Repositories NaviCtrl

Rev

Rev 865 | Blame | Last modification | View Log | RSS feed

/*#######################################################################################*/
/* !!! THIS IS NOT FREE SOFTWARE !!!                                                     */
/*#######################################################################################*/
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + www.MikroKopter.com
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Software Nutzungsbedingungen (english version: see below)
// + der Fa. HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland - nachfolgend Lizenzgeber genannt -
// + Der Lizenzgeber räumt dem Kunden ein nicht-ausschließliches, zeitlich und räumlich* unbeschränktes Recht ein, die im den
// + Mikrocontroller verwendete Firmware für die Hardware Flight-Ctrl, Navi-Ctrl, BL-Ctrl, MK3Mag & PC-Programm MikroKopter-Tool
// + - nachfolgend Software genannt - nur für private Zwecke zu nutzen.
// + Der Einsatz dieser Software ist nur auf oder mit Produkten des Lizenzgebers zulässig.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Die vom Lizenzgeber gelieferte Software ist urheberrechtlich geschützt. Alle Rechte an der Software sowie an sonstigen im
// + Rahmen der Vertragsanbahnung und Vertragsdurchführung überlassenen Unterlagen stehen im Verhältnis der Vertragspartner ausschließlich dem Lizenzgeber zu.
// + Die in der Software enthaltenen Copyright-Vermerke, Markenzeichen, andere Rechtsvorbehalte, Seriennummern sowie
// + sonstige der Programmidentifikation dienenden Merkmale dürfen vom Kunden nicht verändert oder unkenntlich gemacht werden.
// + Der Kunde trifft angemessene Vorkehrungen für den sicheren Einsatz der Software. Er wird die Software gründlich auf deren
// + Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
// + Die Haftung des Lizenzgebers wird - soweit gesetzlich zulässig - begrenzt in Höhe des typischen und vorhersehbaren
// + Schadens. Die gesetzliche Haftung bei Personenschäden und nach dem Produkthaftungsgesetz bleibt unberührt. Dem Lizenzgeber steht jedoch der Einwand
// + des Mitverschuldens offen.
// + Der Kunde trifft angemessene Vorkehrungen für den Fall, dass die Software ganz oder teilweise nicht ordnungsgemäß arbeitet.
// + Er wird die Software gründlich auf deren Verwendbarkeit zu dem von ihm beabsichtigten Zweck testen, bevor er diese operativ einsetzt.
// + Der Kunde wird er seine Daten vor Einsatz der Software nach dem Stand der Technik sichern.
// + Der Kunde ist darüber unterrichtet, dass der Lizenzgeber seine Daten im zur Vertragsdurchführung erforderlichen Umfang
// + und auf Grundlage der Datenschutzvorschriften erhebt, speichert, verarbeitet und, sofern notwendig, an Dritte übermittelt.
// + *) Die räumliche Nutzung bezieht sich nur auf den Einsatzort, nicht auf die Reichweite der programmierten Software.
// + #### ENDE DER NUTZUNGSBEDINGUNGEN ####'
// +  Hinweis: Informationen über erweiterte Nutzungsrechte (wie z.B. Nutzung für nicht-private Zwecke) sind auf Anfrage per Email an info(@)hisystems.de verfügbar.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + Software LICENSING TERMS
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// + of HiSystems GmbH, Flachsmeerstrasse 2, 26802 Moormerland, Germany - the Licensor -
// + The Licensor grants the customer a non-exclusive license to use the microcontroller firmware of the Flight-Ctrl, Navi-Ctrl, BL-Ctrl, and MK3Mag hardware
// + (the Software) exclusively for private purposes. The License is unrestricted with respect to time and territory*.
// + The Software may only be used with the Licensor's products.
// + The Software provided by the Licensor is protected by copyright. With respect to the relationship between the parties to this
// + agreement, all rights pertaining to the Software and other documents provided during the preparation and execution of this
// + agreement shall be the property of the Licensor.
// + The information contained in the Software copyright notices, trademarks, other legal reservations, serial numbers and other
// + features that can be used to identify the program may not be altered or defaced by the customer.
// + The customer shall be responsible for taking reasonable precautions
// + for the safe use of the Software. The customer shall test the Software thoroughly regarding its suitability for the
// + intended purpose before implementing it for actual operation. The Licensor's liability shall be limited to the extent of typical and
// + foreseeable damage to the extent permitted by law, notwithstanding statutory liability for bodily injury and product
// + liability. However, the Licensor shall be entitled to the defense of contributory negligence.
// + The customer will take adequate precautions in the case, that the software is not working properly. The customer will test
// + the software for his purpose before any operational usage. The customer will backup his data before using the software.
// + The customer understands that the Licensor collects, stores and processes, and, where required, forwards, customer data
// + to third parties to the extent necessary for executing the agreement, subject to applicable data protection and privacy regulations.
// + *) The territory aspect only refers to the place where the Software is used, not its programmed range.
// + #### END OF LICENSING TERMS ####
// + Note: For information on license extensions (e.g. commercial use), please contact us at info(@)hisystems.de.
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include <stdio.h>
#include <string.h>
#include "91x_lib.h"
#include "timer1.h"
#include "fat16.h"
#include "sdc.h"
#include "ssc.h"
#include "uart1.h"
#include "main.h"
#include "logging.h"
#include "debug.h"


//________________________________________________________________________________________________________________________________________
// Module name:                 fat16.c
// Compiler used:               avr-gcc 3.4.5
// Last Modifikation:   1.12.2012
// Version:                             2.11
// Authors:                             Stephan Busker & Gregor Stobrawa
// Description:                 Source files for FAT16 implementation with read and write-access
//                                              Copyright (C) 2008 Stephan Busker & Gregor Stobrawa
//........................................................................................................................................
// Functions:                   extern s16              Fat16_Init(void);
//                                              extern s16              Fat16_Deinit(void);
//                                              extern s8*              FAT16_GetVolumeLabel(void);
//
//                                              extern File_t * fopen_(const u8 *filename, const s8 mode);
//                                              extern s16              fclose_(File_t *file);
//                                              extern u8               fexist_(s8 * const filename);
//                                              extern s16              fflush_(File_t *file);
//                                              extern s16      fseek_(File_t *file, s32 offset, s16 origin);
//                                              extern s16              fgetc_(File_t *file);
//                                              extern s16              fputc_(u8 c, File_t *file);
//                                              extern u32              fread_(void *buffer, u32 size, u32 count, File_t *file);
//                                              extern u32              fwrite_(void *buffer, u32 size, u32 count, File_t *file);
//                                              extern s16              fputs_(const u8 *string, File_t *File);
//                                              extern u8 *     fgets_(u8 *string, s16 length, File_t *file);
//                                              extern u8               feof_(File_t * const file);
//                                              extern u8               fdelete_(s8* const filepath);
//
//                                              extern s8               *getcwd_(void);
//                                              extern u8               chdir_(s8* const dirpath);
//                                              extern u8               mkdir_(s8* const dirpath);
//                                              extern u8               rmdir_(s8* const dirpath);
//                                              extern u8               findfirst_(const s8* name, u8 attribmask, Find_t *);
//                                              extern u8               findnext_(Find_t *);
//........................................................................................................................................
// ext. functions:              extern SD_Result_t SDC_Init(void;)
//                                              extern SD_Result_t SDC_Deinit(void);
//                      extern SD_Result_t SDC_GetSector (u32,u8 *);
//                                              extern SD_Result_t SDC_PutSector (u32,u8 *);
//........................................................................................................................................
//
// URL:                                 www.Mikro-Control.de
// mailto:                              stephan.busker@mikro-control.de
//________________________________________________________________________________________________________________________________________

/*
FAT16 Drive Layout:
Description                                             Offset
Volume Boot Sector                                      Start of Partition
Fat Tables                                                      Start + # of Reserved Sectors
Root Directory Entry                            Start + # of Reserved + (# of Sectors Per FAT * 2)
Data Area (Starts with Cluster #2)      Start + # of Reserved + (# of Sectors Per FAT * 2) + ((Maximum Root Directory Entries * 32) / Bytes per Sector)
*/




/*
________________________________________________________________________________________________________________________________________

        Structure of an partition entry
________________________________________________________________________________________________________________________________________

        Partition Entry is 16 bytes long
*/

typedef struct
{
        u8      PartitionState;                         // Current State of Partition (00h=Inactive, 80h=Active)
        u8      BeginningHead;                          // Beginning of Partition - Head
        u16     BeginningCylSec;                        // Beginning of Partition - Cylinder/Sector (See Below)
        u8      Type;                                           // Type of Partition (See List Below)
        u8      EndHead;                                        // End of Partition - Head
        u16     EndCylSec;                                      // End of Partition - Cylinder/Sector
        u32     NoSectorsBeforePartition;       // Number of Sectors between the MBR and the First Sector in the Partition
        u32     NoSectorsPartition      ;               // Number of Sectors in the Partition
} __attribute__((packed)) PartitionEntry_t;



/*
Coding of Cylinder/Sector words

Cylinder is 10 bits:  [7:0] at [15:8] and [9:8] at [7:6]
Sector is 5 bits:  [5:0] at [5:0]
*/


// Partition Types:
#define PART_TYPE_UNKNOWN                       0x00
#define PART_TYPE_FAT12                         0x01
#define PART_TYPE_XENIX                         0x02
#define PART_TYPE_FAT16_ST_32_MB        0x04
#define PART_TYPE_EXTDOS                        0x05
#define PART_TYPE_FAT16_LT_32_MB        0x06
#define PART_TYPE_NTFS                          0x07
#define PART_TYPE_FAT32                         0x0B
#define PART_TYPE_FAT32LBA                      0x0C
#define PART_TYPE_FAT16LBA                      0x0E
#define PART_TYPE_EXTDOSLBA                     0x0F
#define PART_TYPE_EISA                          0x12
#define PART_TYPE_ONTRACK                       0x33
#define PART_TYPE_NOVELL                        0x40
#define PART_TYPE_DYNAMIC                       0x42
#define PART_TYPE_PCIX                          0x4B
#define PART_TYPE_LINUX_SWAP            0x82
#define PART_TYPE_LINUX_NATIVE          0x83
#define PART_TYPE_LINUX_LVM                     0x8E
#define PART_TYPE_PHOENIXSAVE           0xA0
#define PART_TYPE_FREEBSD                       0xA5
#define PART_TYPE_OPENBSD                       0xA6
#define PART_TYPE_NETNBSD                       0xA9
#define PART_TYPE_CPM                           0xDB
#define PART_TYPE_DBFS                          0xE0
#define PART_TYPE_BBT                           0xFF


/*
________________________________________________________________________________________________________________________________________

        Structure of the MasterBootRecord
________________________________________________________________________________________________________________________________________

        Master Boot Record is 512 bytes long
        The Master Boot Record is the same for pretty much all Operating Systems.
        It is located on the first Sector of the Hard Drive, at Cylinder 0, Head 0, Sector 1
*/

typedef struct
{
        u8                              ExecutableCode[446];    // 446 bytes for machine start code
        PartitionEntry_t        PartitionEntry1;                // 16 bytes for partition entry 1
        PartitionEntry_t        PartitionEntry2;                // 16 bytes for partition entry 2
        PartitionEntry_t        PartitionEntry3;                // 16 bytes for partition entry 3
        PartitionEntry_t        PartitionEntry4;                // 16 bytes for partition entry 4
        u16                                     ExecutableMarker;               // BIOS-Signature (0x55 0xAA)
} __attribute__((packed)) MBR_Entry_t;


/*
________________________________________________________________________________________________________________________________________

        Structure of the VolumeBootRecord
________________________________________________________________________________________________________________________________________

        The Volume Boot Record is 512 bytes long
        This information is located in the first sector of every partition.
*/

typedef struct
{
        u8  JumpCode[3];                        // Jump Code + NOP
        s8  OEMName[8];                         // OEM Name
        u16 BytesPerSector;                     // Bytes Per Sector
        u8  SectorsPerCluster;          // Sectors Per Cluster
        u16 ReservedSectors;            // Reserved Sectors
        u8  NoFATCopies;                        // Number of Copies of FAT
        u16 MaxRootEntries;                     // Maximum Root Directory Entries
        u16 NoSectorsInPartSml32MB;     // Number of Sectors in Partition Smaller than 32 MB
        u8  MediaDescriptor;            // Media Descriptor (0xF8 for Hard Disks)
        u16 SectorsPerFAT;                      // Sectors Per FAT
        u16 SectorsPerTrack;            // Sectors Per Track
        u16 NoHeads;                            // Number of Heads
        u32 NoHiddenSectors;            // Number of Hidden Sectors     in Partition
        u32 NoSectors;                          // Number of Sectors in Partition
        u16     DriveNo;                                // Logical Drive Number of Partition
        u8  ExtendedSig;                        // Extended Signature (0x29)
        u32 SerialNo;                           // Serial Number of the Partition
        s8  VolumeName[11];                     // Volume Name of the Partititon
        s8  FATName[8];                         // FAT Name (FAT16)
        u8  ExecutableCode[446];        // 446 bytes for machine start code
        u16 ExecutableMarker;           // Executable Marker (0x55 0xAA)
} __attribute__((packed)) VBR_Entry_t;



/*
________________________________________________________________________________________________________________________________________

        Structure of an directory entry
________________________________________________________________________________________________________________________________________

        Directory entry is 32 bytes.
*/

typedef struct
{
        s8      Name[8];                                        // 8 bytes name, padded with spaces.
        u8      Extension[3];                           // 3 bytes extension, padded with spaces.
        u8      Attribute;                                      // attribute of the directory entry (unused,archive,read-only,system,directory,volume)
        u8  Res1;                                               // should be zero
        u8  CreationTime10ms;                   // subsecond resolution of creation time
        u16 CreationTime;                               // Time of creation h:m:s
        u16 CreationDate;                               // Date of creation Y.M.D
        u16 LastAccessDate;             // The date where the file was last accessed
        u8      Res2[2];                                    // should be zero
        u16 ModTime;                                    // date of last write access
        u16 ModDate;                                    // date of last write access to the file or directory.
        u16 StartCluster;                               // first cluster of the file or directory.
        u32 Size;                                               // size of the file or directory in bytes.
}  __attribute__((packed)) DirEntry_t;

#define SLOT_EMPTY              0x00    // slot has never been used
#define SLOT_E5                 0x05    // the real value is 0xE5
#define SLOT_DELETED            0xE5    // file in this slot deleted

/*
________________________________________________________________________________________________________________________________________

        Structure of an entry within the fileallocationtable.
________________________________________________________________________________________________________________________________________
*/

typedef struct
{
        u16  NextCluster;                               // the next cluster of the file.
} __attribute__((packed)) Fat16Entry_t;

// secial fat entries
#define FAT16_CLUSTER_FREE                      0x0000
#define FAT16_CLUSTER_RESERVED          0x0001
#define FAT16_CLUSTER_USED_MIN          0x0002
#define FAT16_CLUSTER_USED_MAX          0xFFEF
#define FAT16_CLUSTER_ROOTDIR_MIN       0xFFF0
#define FAT16_CLUSTER_ROOTDIR_MAX       0xFFF6
#define FAT16_CLUSTER_BAD                       0xFFF7
#define FAT16_CLUSTER_LAST_MIN          0xFFF8
#define FAT16_CLUSTER_LAST_MAX          0xFFFF

/*****************************************************************************************************************************************/
/*                                                                                                                                                                                                                                                                               */
/*      Global variables needed for read- or write-acces to the FAT16- filesystem.                                                                                                                       */
/*                                                                                                                                                                                                                                                                               */
/*****************************************************************************************************************************************/

#define MBR_SECTOR                                      0x00    // the masterboot record is located in sector 0.
#define DIRENTRY_SIZE                           32              //bytes
#define DIRENTRIES_PER_SECTOR           BYTES_PER_SECTOR/DIRENTRY_SIZE
#define FAT16_BYTES                                     2
#define FAT16_ENTRIES_PER_SECTOR        BYTES_PER_SECTOR/FAT16_BYTES

#define SECTOR_UNDEFINED        0x00000000L
#define CLUSTER_UNDEFINED       0x0000

#define FSTATE_UNUSED   0
#define FSTATE_USED             1


Partition_t     Partition;              // Structure holds partition information

File_t FilePointer[FILE_MAX_OPEN];      // Allocate Memmoryspace for each filepointer used.

/********************************************************************************************************************************************/
/*      Function:               s8 *getcwd_(void);                                                                                                                                                                                                              */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function returns a pointer to the absolute path of the active partition                                                                            */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    pointer to the string containing the path to the CWD                                                                                                                                    */
/********************************************************************************************************************************************/

s8 *getcwd_(void)
{
        return(Partition.PathToCwd);
}

/********************************************************************************************************************************************/
/*      Function:               void CWDPath_Push(s8* directory);                                                                                                                                                                               */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function function appends the name of an directory to the path of the CWD                                                                                  */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    none                                                                                                                                                                                                                                    */
/********************************************************************************************************************************************/

void CWDPath_Push(s8* directory)
{
        // append the name of the directory to the path
        strcat(Partition.PathToCwd, directory);
        // append a '/' after the directoryname
        strcat(Partition.PathToCwd, "/");
}

/********************************************************************************************************************************************/
/*      Function:               CWDPath_Pop(void);                                                                                                                                                                                                              */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function removes the last directory from the path to the CWD                                                                                                               */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    none                                                                                                                                                                                                                                    */
/********************************************************************************************************************************************/

void CWDPath_Pop(void)
{
        // a pointer to the beginning of the absolute path to the cwd
        s8 * cptr = Partition.PathToCwd;
        // lets find the end of the path to the cwd
        while(*cptr != 0 ) cptr++;
        // if the path is terminated with an '/' then remove it
        if((*(cptr-1)) == '/') *(cptr-1) = 0;
        // now lets find the beginning of the last directory entry
        while((*cptr != '/' ) && cptr > Partition.PathToCwd) cptr--;
        // is there one subdirectory left within the path?
        if(cptr > Partition.PathToCwd)
        {
                // we delete the direntry by terminating the path with 0
                *(cptr+1) = 0;
        }
        // there is no subdirectory left within the path. Therefore we create the root instead.
        else
        {
                *cptr = '/';
                *(cptr+1) = 0;
        }
}

/****************************************************************************************************************************************/
/*      Function:               FileDate(DateTime_t *);                                                                                                                                                                                         */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function calculates the DOS date from a pointer to a time structure.                                                                                       */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    Returns the DOS date.                                                                                                                                                                                           */
/****************************************************************************************************************************************/
u16 FileDate(DateTime_t * pTimeStruct)
{
        u16 date = 0;
        if(pTimeStruct == NULL)   return date;
        if(!(pTimeStruct->Valid)) return date;

        date |= (0x007F & (u16)(pTimeStruct->Year - 1980))<<9; // set year
        date |= (0x000F & (u16)(pTimeStruct->Month))<<5; // set month
        date |= (0x001F & (u16)(pTimeStruct->Day));
        return date;
}

/****************************************************************************************************************************************/
/*      Function:               FileTime(DateTime_t *);                                                                                                                                                                                         */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function calculates the DOS time from a pointer to a time structure.                                                                                       */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    Returns the DOS time.                                                                                                                                                                                           */
/****************************************************************************************************************************************/

u16 FileTime(DateTime_t * pTimeStruct)
{
        u16 time = 0;
        if(pTimeStruct == NULL)   return time;
        if(!(pTimeStruct->Valid)) return time;

        time |= (0x001F & (u16)(pTimeStruct->Hour))<<11;
        time |= (0x003F & (u16)(pTimeStruct->Min))<<5;
        time |= (0x001F & (u16)(pTimeStruct->Sec/2));
        return time;
}

/****************************************************************************************************************************************/
/*      Function:               LockFilePointer();                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function trys to lock a free file pointer.                                                                                                                                         */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    Returns the Filepointer on success or 0.                                                                                                                                                        */
/****************************************************************************************************************************************/
File_t * LockFilePointer(void)
{
        u8 i;
        File_t * File = 0;
        for(i = 0; i < FILE_MAX_OPEN; i++)
        {
                if(FilePointer[i].State == FSTATE_UNUSED)                                                               // found an unused one
                {
                        File = &FilePointer[i];                                                                                         // set pointer to that entry
                        FilePointer[i].FirstSectorOfFirstCluster = SECTOR_UNDEFINED;            // First sector of the first cluster of the file.
                        FilePointer[i].FirstSectorOfCurrCluster = SECTOR_UNDEFINED;                     // First sector of the cluster which is edited at the moment.
                        FilePointer[i].FirstSectorOfLastCluster = SECTOR_UNDEFINED;                     // First sector of the last cluster of the file
                        FilePointer[i].SectorOfCurrCluster = SECTOR_UNDEFINED;                          // The sector within the current cluster.
                        FilePointer[i].ByteOfCurrSector = 0;                                                            // The byte location within the current sector.
                        FilePointer[i].Mode = 0;                                                                                        // Mode of fileoperation (read,write)
                        FilePointer[i].Size = 0;                                                                                        // The size of the opend file in bytes.
                        FilePointer[i].Position = 0;                                                                            // Pointer to a character within the file 0 < fileposition < filesize
                        FilePointer[i].DirectorySector = SECTOR_UNDEFINED;                                      // the sectorposition where the directoryentry has been made.
                        FilePointer[i].DirectoryIndex = 0;                                                                      // The index to the directoryentry within the specified sector.
                        FilePointer[i].Attribute = 0;                                                                           // The attribute of the file opened.
                        FilePointer[i].SectorInCache = SECTOR_UNDEFINED;                                        // The last sector read, which is still in the sector cache.
                        FilePointer[i].State = FSTATE_USED;                                                                     // mark it as used

                        break;
                }
        }
        return(File);
}

/****************************************************************************************************************************************/
/*      Function:               UnlockFilePointer(file_t *);                                                                                                                                                                            */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function trys to unlock a file pointer.                                                                                                                                            */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    Returns 1 if file pointer was freed else 0.                                                                                                                                                     */
/****************************************************************************************************************************************/
u8 UnlockFilePointer(File_t * file)
{
        u8 cnt;
        if(file == NULL) return(0);
        for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++)
        {
                if(&FilePointer[cnt] == file)                                           // filepointer to be freed found?
                {
                        file->State = FSTATE_UNUSED;
                        file->FirstSectorOfFirstCluster = SECTOR_UNDEFINED;                     // Sectorpointer to the first sector of the first datacluster of the file.
                        file->FirstSectorOfCurrCluster  = SECTOR_UNDEFINED;                     // Pointer to the cluster which is edited at the moment.
                        file->SectorOfCurrCluster               = 0;                    // The sector which is edited at the moment (cluster_pointer + sector_index).
                        file->ByteOfCurrSector                  = 0;                    // The bytelocation within the current sector (cluster_pointer + sector_index + byte_index).
                        file->Mode                                              = 0;                    // mode of fileoperation (read,write)
                        file->Size                                              = 0;                    // the size of the opend file in bytes.
                        file->Position                                  = 0;                    // pointer to a character within the file 0 < fileposition < filesize
                        file->SectorInCache                     = SECTOR_UNDEFINED;                     // the last sector read, wich is still in the sectorbuffer.
                        file->DirectorySector                   = SECTOR_UNDEFINED;                     // the sectorposition where the directoryentry has been made.
                        file->DirectoryIndex                    = 0;                    // the index to the directoryentry within the specified sector.
                        file->Attribute                                 = 0;                    // the attribute of the file opened.
                        file = NULL;
                        return(1);
                }
        }
        return(0);
}

/****************************************************************************************************************************************/
/*      Function:               s8* SeperateFormatedDirName(const s8*, s8*);                                                                                                                                            */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function seperates the first dirname from filepath and brings them                                                                                         */
/*                                      into the needed format ('test.txt' -> 'TEST    TXT')                                                                                                                            */
/*                                      The subpath is the pointer to the remaining substring of the filepath                                                                                           */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    Return NULL on error or pointer to subpath                                                                                                                                                      */
/****************************************************************************************************************************************/
s8* SeperateFormatedDirName(const s8 *filepath, s8 *dirname)
{
        s8* subpath = NULL;
        u8 readpointer  = 0;
        u8 writepointer = 0;

        // search subpath from beginning of filepath
        subpath = NULL;
        readpointer     = 0;
        if(filepath[0] == '/') readpointer = 1; // ignore first '/'
        while(subpath == NULL)  // search the filepath until a subpath was found.
        {
                if(((filepath[readpointer] == 0) || (filepath[readpointer] == '/')))    // if '/' found or end of filepath reached
                {
                        subpath = (s8*)&filepath[readpointer];                          // store the position of the first "/" found after the beginning of the filenpath
                }
                readpointer++;
        }

        // clear dirname with spaces
        dirname[11] = 0; // terminate dirname
        for(writepointer = 0; writepointer < 11; writepointer++) dirname[writepointer] = ' ';

        // handle the special dirnames "." and ".." seperately
        readpointer = 0;
        if(filepath[0] == '/') readpointer++;
        // if we are trying to enter directories "." or ".."
        if(filepath[readpointer] == '.')
        {
                // directory '.'
                if(filepath[readpointer+1] == 0)
                {
                        dirname[0] = '.';
                        return((s8*)&filepath[readpointer]);
                }
                // directory '..'
                if((filepath[readpointer+1] == '.') &&  (filepath[readpointer+2] == 0))
                {
                        dirname[0] = '.';
                        dirname[1] = '.';
                        return((s8*)&filepath[readpointer]);
                }
        }

        writepointer = 0;
        // start seperating the dirname from the filepath.
        readpointer = 0;
        if(filepath[0] == '/') readpointer = 1; // ignore first '/'
        while( &filepath[readpointer] < subpath)
        {
                if(writepointer >= 11) return(NULL);            // dirname to long
                if(filepath[readpointer] == '.')                        // seperating dirname and extension.
                {
                        if(writepointer <= 8)
                        {
                                readpointer++;                                          // next character in filename
                                writepointer = 8;                                       // jump to start of extension
                        }
                        else return(NULL);                                              // dirbasename to long
                }
                else
                {
                        if((0x60 < filepath[readpointer]) && (filepath[readpointer] < 0x7B))
                        {
                                dirname[writepointer] = (filepath[readpointer] - 0x20);                                 // all characters must be upper case.
                        }
                        else
                        {
                                dirname[writepointer] = filepath[readpointer];
                        }
                        readpointer++;
                        writepointer++;
                }
        }
        return(subpath);
}

/**************************************************************************************************************************************+*/
/*      Function:       Fat16ClusterToSector( u16 cluster);                                                                                                                                                                             */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function converts a cluster number given by the fat to the corresponding                                                                           */
/*                                      sector that points to the start of the data area that is represented by the cluster number.                                                     */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue: The sector number with the data area of the given cluster                                                                                                                          */
/****************************************************************************************************************************************/
u32     Fat16ClusterToSector(u16 cluster)
{
        if(!Partition.IsValid) return SECTOR_UNDEFINED;
        if ((cluster < 2) || (cluster == CLUSTER_UNDEFINED))
        {
                return SECTOR_UNDEFINED;
        }
        else
        {
                return ( (cluster - 2) * Partition.SectorsPerCluster) + Partition.FirstDataSector; // the first data sector     is represented by the 2nd cluster
        }
}

/****************************************************************************************************************************************/
/*      Function:       SectorToFat16Cluster( u32 sector);                                                                                                                                                                              */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function converts a given sector number given to the corresponding                                                                                         */
/*                                      cluster number in the fat that represents this data area.                                                                                                                       */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue: The cluster number representing the data area of the sector.                                                                                                                       */
/****************************************************************************************************************************************/
u16     SectorToFat16Cluster(u32 sector)
{
        if(!Partition.IsValid) return CLUSTER_UNDEFINED;
        if((sector == SECTOR_UNDEFINED) || (sector < Partition.FirstDataSector)) return CLUSTER_UNDEFINED;
        else return ((u16)((sector - Partition.FirstDataSector) / Partition.SectorsPerCluster) + 2);
}


/****************************************************************************************************************************************/
/*      Function:       Fat16_IsValid(void);                                                                                                                                                                                                    */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function return the Fat 16 filesystem state                                                                                                                                        */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue: The function returns "1" on success                                                                                                                                                                        */
/****************************************************************************************************************************************/
u8 Fat16_IsValid(void)
{
        return(Partition.IsValid);
}

/****************************************************************************************************************************************/
/*      Function:       Fat16_Deinit(void);                                                                                                                                                                                                             */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function uninitializes the fat 16 api                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue: The function returns "0" on success                                                                                                                                                                        */
/****************************************************************************************************************************************/
u8 Fat16_Deinit(void)
{
        s16 returnvalue = 0;
        u8 cnt;

        UART1_PutString("\r\n FAT16 deinit...");
        // declare the filepointers as unused.
        for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++)
        {
                UnlockFilePointer(&FilePointer[cnt]);
        }
        returnvalue = SDC_Deinit();                     // uninitialize interface to sd-card
        Partition.IsValid = 0;  // mark data in partition structure as invalid
        Partition.VolumeLabel[0]='\0';
        UART1_PutString("ok");
        if(SD_WatchDog > 2000) SD_WatchDog = 2000;
        SD_LoggingError = 100;
        return(returnvalue);
}


/****************************************************************************************************************************************/
/*      Function:               Fat16_Init(void);                                                                                                                                                                                                       */
/*                                                                                                                                                                                                                                                                          */
/*      Description:    This function reads the Masterbootrecord and finds the position of the Volumebootrecord, the FAT and the Rootdir    */
/*                                      and stores the information in global variables.                                                                                                                                     */
/*                                                                                                                                                                                                                                                                          */
/*      Returnvalue:    The function returns "0" if the filesystem is initialized.                                                                                                                      */
/****************************************************************************************************************************************/
u8 Fat16_Init(void)
{
    u8  cnt     = 0;
        u32     partitionfirstsector;
        VBR_Entry_t *VBR;
        MBR_Entry_t *MBR;
        File_t *file;
        u8 result = 0;

        UART1_PutString("\r\n FAT16 init...");
        Partition.IsValid = 0;
        // declare the filepointers as unused.
        for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++)
        {
                UnlockFilePointer(&FilePointer[cnt]);
        }
        // set current file pinter to first position in list
        file = &FilePointer[0];

        // try to initialize the sd-card.
        if(SD_SUCCESS != SDC_Init(1))
        {
                if(!SD_SWITCH) UART1_PutString(" -> No SD-Card");
                else UART1_PutString("ERROR: SD-Card could not be initialized.");

                result = 1;
                goto end;
        }

        // SD-Card is initialized successfully
        if(SD_SUCCESS != SDC_GetSector((u32)MBR_SECTOR,file->Cache))    // Read the MasterBootRecord
        {
                UART1_PutString("Error reading the MBR.");
                result = 2;
                goto end;
        }
        MBR = (MBR_Entry_t *)file->Cache;                                               // Enter the MBR using the structure MBR_Entry_t.
        if((MBR->PartitionEntry1.Type == PART_TYPE_FAT16_ST_32_MB) ||
           (MBR->PartitionEntry1.Type == PART_TYPE_FAT16_LT_32_MB) ||
           (MBR->PartitionEntry1.Type == PART_TYPE_FAT16LBA))
        {
                // get sector offset 1st partition
                partitionfirstsector = MBR->PartitionEntry1.NoSectorsBeforePartition;
                // Start of Partition is the Volume Boot Sector
                if(SD_SUCCESS != SDC_GetSector(partitionfirstsector,file->Cache)) // Read the volume boot record
                {
                        UART1_PutString("Error reading the VBR.");
                        result = 3;
                        goto end;
                }
        }
        else  // maybe the medium has no partition assuming sector 0 is the vbr
        {
                partitionfirstsector = 0;
        }

        VBR = (VBR_Entry_t *) file->Cache;                                              // Enter the VBR using the structure VBR_Entry_t.
        if(VBR->BytesPerSector != BYTES_PER_SECTOR)
        {
                UART1_PutString("VBR: Sector size not supported.");
                result = 4;
                goto end;
        }
        Partition.SectorsPerCluster             = VBR->SectorsPerCluster;                       // Number of sectors per cluster. Depends on the memorysize of the sd-card.
        Partition.FatCopies                     = VBR->NoFATCopies;                                     // Number of fatcopies.
        Partition.MaxRootEntries                = VBR->MaxRootEntries;                          // How many Entries are possible in the rootdirectory (FAT16 allows max. 512 entries).
        Partition.SectorsPerFat                 = VBR->SectorsPerFAT;                           // The number of sectors per FAT                                // copy volume label
        Partition.VolumeLabel[0] = '\0';                                                                        // set string terminator

        /* Calculate the sectorpositon of the FAT, the Rootdirectory and the first Datacluster. */
        // Calculate the position of the FileAllocationTable:
        // Start + # of Reserved Sectors
        Partition.FirstFatSector        =   (u32)(partitionfirstsector + (u32)(VBR->ReservedSectors));
        // Calculate the position of the Rootdirectory:
        // Start + # of Reserved Sectors + (# of Sectors Per FAT * # of FAT Copies)
        Partition.FirstRootDirSector    =   Partition.FirstFatSector + (u32)((u32)Partition.SectorsPerFat*(u32)Partition.FatCopies);
        // Calculate the position of the first datacluster:
        // Start + # of Reserved + (# of Sectors Per FAT * # of FAT Copies) + ((Maximum Root Directory Entries * 32) / Bytes per Sector)
        Partition.FirstDataSector       =   Partition.FirstRootDirSector + (u32)(Partition.MaxRootEntries>>4);  // assuming 512 Byte Per Sector
        // Calculate the last data sector
        if(VBR->NoSectors == 0)
        {
                UART1_PutString("VBR: Bad number of sectors.");
                result = 5;
                goto end;
        }
        Partition.LastDataSector = Partition.FirstDataSector + VBR->NoSectors - 1;
        // check for FAT16 in VBR of first partition
        if(!((VBR->FATName[0]=='F') && (VBR->FATName[1]=='A') && (VBR->FATName[2]=='T') && (VBR->FATName[3]=='1')&&(VBR->FATName[4]=='6')))
        {
                UART1_PutString("VBR: Partition ist not FAT16 type.");
                result = 6;
                goto end;
        }
        Partition.IsValid = 1; // mark data in partition structure as valid
        Partition.CurrentWorkingDirectory = Partition.FirstRootDirSector;
        strcpy(Partition.PathToCwd,"/");
        result = 0;
        end:
        if(result != 0) Fat16_Deinit();
//      else UART1_PutString(" SD card ok");
        return(result);
}



/****************************************************************************************************************************************/
/* Function:    ClearCurrCluster(File_t*);                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                              */
/* Description: This function fills the current cluster with 0.                                                                                                                                                 */
/*                                                                                                                                                                                                                                                                              */
/* Returnvalue: The function returns 1 on success else 0.                                                                                                                                                               */
/****************************************************************************************************************************************/
u8 ClearCurrCluster(File_t * file)
{
        u8 retvalue = 1;
        u32 i;

        if((!Partition.IsValid) || (file == NULL)) return(0);

        for(i = 0; i < BYTES_PER_SECTOR; i++) file->Cache[i] = 0; // clear file cache
        if(file->FirstSectorOfCurrCluster == SECTOR_UNDEFINED) return (0); // nothing to do
        for(i = 0; i < Partition.SectorsPerCluster; i++)
        {
                file->SectorInCache = file->FirstSectorOfCurrCluster + i;
                if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))
                {
                        Fat16_Deinit();
                        return(0);
                }
        }
        return(retvalue);
}

/*****************************************************************************************************************************************/
/* Function:    GetNextCluster(File_t* );                                                                                                                                                                                        */
/*                                                                                                                                                                                                                                                                               */
/* Description: This function finds the next datacluster of the file specified with File *File.                                                                                  */
/*                                                                                                                                                                                                                                                                               */
/* Returnvalue: The function returns the next cluster or 0 if the last cluster has already reached.                                                                                                      */
/*****************************************************************************************************************************************/
u16 GetNextCluster(File_t * file)
{
        u16 cluster = CLUSTER_UNDEFINED;
        u32 fat_byte_offset, sector, byte;
        Fat16Entry_t * fat;

        if((!Partition.IsValid) || (file == NULL)) return(cluster);
        if(file->FirstSectorOfCurrCluster == SECTOR_UNDEFINED) return(cluster);
        // if sector is within the data area
        if((Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)&& (file->FirstSectorOfCurrCluster <= Partition.LastDataSector))
        {
                // determine current file cluster
                cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster);
                // calculate byte offset in the fat for corresponding entry
                fat_byte_offset = ((u32)cluster)<<1; // two FAT bytes (16 bits) for every cluster
                // calculate the sector that contains the current cluster within the fat
                sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
                // calculate byte offset of the current cluster within that fat sector
                byte = fat_byte_offset % BYTES_PER_SECTOR;
                // read this sector to the file cache
                if(file->SectorInCache != sector)
                {
                        file->SectorInCache = sector;                                           // update sector stored in buffer
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))       // read sector from sd-card
                        {
                                Fat16_Deinit();
                                return (CLUSTER_UNDEFINED);
                        }
                }
                // read the next cluster from cache
                fat = (Fat16Entry_t *)(&(file->Cache[byte]));
                cluster = fat->NextCluster;
                // if no next cluster exist
                if(FAT16_CLUSTER_LAST_MIN <= cluster)
                {
                         cluster = CLUSTER_UNDEFINED; // next cluster is undefined
                }
                else
                {
                        file->FirstSectorOfCurrCluster = Fat16ClusterToSector(cluster);
                        file->SectorOfCurrCluster = 0;
                        file->ByteOfCurrSector = 0;
                }
        }
        return(cluster);
}



/****************************************************************************************************************************************/
/* Function:    FindNextFreeCluster(File_t *);                                                                                                                                                                          */
/*                                                                                                                                                                                                                                                                              */
/* Description: This function looks in the fat to find the next free cluster                                                                                                                    */
/*                                                                                                                                                                                                                                                                              */
/* Returnvalue: The function returns the cluster number of the next free cluster found within the fat.                                                                  */
/****************************************************************************************************************************************/
u16 FindNextFreeCluster(File_t *file)
{
        u32 fat_sector;                                 // current sector within the fat relative to the first sector of the fat.
        u32     curr_sector;                            // current sector
        u16     fat_entry;                                      // index to an fatentry within the actual sector (256 fatentries are possible within one sector).
        u16     free_cluster    = CLUSTER_UNDEFINED;    // next free cluster number.
        Fat16Entry_t * fat;

        if((!Partition.IsValid) || (file == NULL)) return(0);

        // start searching for an empty cluster at the beginning of the fat.
        fat_sector = 0;
        do
        {
                curr_sector = Partition.FirstFatSector + fat_sector;    // calculate sector to read
                file->SectorInCache = curr_sector;                                              // upate the sector number of file cache.
                if( SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))              // read sector of fat from sd-card.
                {
                        Fat16_Deinit();
                        return(CLUSTER_UNDEFINED);
                }

                fat = (Fat16Entry_t *)file->Cache;                                              // set fat pointer to file cache

                for(fat_entry = 0; fat_entry < FAT16_ENTRIES_PER_SECTOR; fat_entry++)                                           // look for an free cluster at all entries in this sector of the fat.
                {
                        if(fat[fat_entry].NextCluster == FAT16_CLUSTER_FREE)            // empty cluster found!!
                        {
                                fat[fat_entry].NextCluster = FAT16_CLUSTER_LAST_MAX;    // mark this fat-entry as used
                                if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))               // and save the sector at the sd-card.
                                {
                                        Fat16_Deinit();
                                        return(CLUSTER_UNDEFINED);
                                }
                                free_cluster = (u16)(fat_sector * FAT16_ENTRIES_PER_SECTOR + (u32)fat_entry);
                                fat_entry = FAT16_ENTRIES_PER_SECTOR;                                   // terminate the search for a free cluster in this sector.
                        }
                }
                fat_sector++;                                                                                                   // continue the search in next fat sector
        // repeat until the end of the fat is  reached and no free cluster has been found so far
        }while((fat_sector < Partition.SectorsPerFat) && (!free_cluster));
        return(free_cluster);
}

/****************************************************************************************************************************************/
/* Function:    u8 DeleteClusterChain(File *file);                                                                                                                                                                              */
/*                                                                                                                                                                                                                                                                              */
/* Description: This function trances along a cluster chain in the fat and frees all clusters visited.                                                                  */
/*                                                                                                                                                                                                                                                                              */
/* Returnvalue: returns 1 in success otherwise 0                                                                                                                                                                                */
/****************************************************************************************************************************************/
u8 DeleteClusterChain(u16 StartCluster)
{
        u16 cluster;
        u32 fat_byte_offset, sector, byte;
        Fat16Entry_t * fat;
        u8 buffer[BYTES_PER_SECTOR];
        u32 sector_in_buffer = 0;
        u8 repeat = 0;

        if(!Partition.IsValid) return(0);
        if(StartCluster == CLUSTER_UNDEFINED) return(1);
        cluster = StartCluster; // init chain trace
        // if start cluster is no real cluster
    if(FAT16_CLUSTER_LAST_MIN <= cluster) return(1);

        // calculate byte offset in the fat for corresponding entry
        fat_byte_offset = ((u32)cluster)<<1; // two FAT bytes (16 bits) for every cluster
        // calculate the sector that contains the current cluster within the fat
        sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
        // calculate byte offset of the current cluster within that fat sector
        byte = fat_byte_offset % BYTES_PER_SECTOR;
        do
        {
                if(sector != sector_in_buffer)
                {
                        // read this sector to buffer
                        sector_in_buffer = sector;
                        if(SD_SUCCESS != SDC_GetSector(sector_in_buffer, buffer)) return 0;     // read sector from sd-card
                }
                // read the next cluster from cache
                fat = (Fat16Entry_t *)(&(buffer[byte]));
                cluster = fat->NextCluster;
                fat->NextCluster =      FAT16_CLUSTER_FREE; // mark current cluster as free

                if((FAT16_CLUSTER_USED_MIN <= cluster) && (cluster <= FAT16_CLUSTER_USED_MAX) )
                {
                        repeat = 1;
                        // calculate sector byte and byte offset in the fat for the next cluster
                        fat_byte_offset = ((u32)cluster)<<1; // two FAT bytes (16 bits) for every cluster
                        // calculate the sector that contains the current cluster within the fat
                        sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
                        // calculate byte offset of the current cluster within that fat sector
                        byte = fat_byte_offset % BYTES_PER_SECTOR;
                }
                else repeat = 0;

                // if new sector is not the sector in buffer or the last cluster in the chain was traced
                if((sector != sector_in_buffer) || !repeat)
                {       // write sector in buffer
                        if(SD_SUCCESS != SDC_PutSector(sector_in_buffer,buffer))
                        {
                                Fat16_Deinit();
                                return(0);
                        }
                }
        }
        while(repeat);

        return 1;
}


/****************************************************************************************************************************************/
/* Function:    u16 AppendCluster(File *file);                                                                                                                                                                                  */
/*                                                                                                                                                                                                                                                                              */
/* Description: This function looks in the fat to find the next free cluster and appends it to the file.                                                                */
/*                                                                                                                                                                                                                                                                              */
/* Returnvalue: The function returns the appened cluster number or CLUSTER_UNDEFINED of no cluster was appended.                                                */
/****************************************************************************************************************************************/
u16 AppendCluster(File_t *file)
{
        u16 last_cluster, new_cluster = CLUSTER_UNDEFINED;
        u32 fat_byte_offset, sector, byte;
        Fat16Entry_t * fat;


        if((!Partition.IsValid) || (file == NULL)) return(new_cluster);

        new_cluster = FindNextFreeCluster(file);        // the next free cluster found on the disk.
        if(new_cluster != CLUSTER_UNDEFINED)
        {       // A free cluster was found and can be added to the end of the file.
                // is there at least one cluster appended to the file?
                if(file->FirstSectorOfLastCluster == CLUSTER_UNDEFINED)
                {
                        fseek_(file, 0, SEEK_END);                                                                                                      // jump to the end of the file
                        // remember the first sector of the last cluster
                        file->FirstSectorOfLastCluster = file->FirstSectorOfCurrCluster;
                        last_cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster);            // determine current file cluster
                }
                else
                {
                        last_cluster = SectorToFat16Cluster(file->FirstSectorOfLastCluster);            // determine current file cluster
                }

                if(last_cluster != CLUSTER_UNDEFINED)
                {
                        // update FAT entry of last cluster
                        fat_byte_offset = ((u32)last_cluster)<<1;
                        sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
                        byte = fat_byte_offset % BYTES_PER_SECTOR;
                        // read the sector containing the last cluster of the file
                        if(file->SectorInCache != sector)
                        {
                                file->SectorInCache = sector;   // update sector stored in buffer
                                if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))       // read sector from sd-card
                                {
                                        Fat16_Deinit();
                                        return(0);
                                }
                        }
                        fat = (Fat16Entry_t *)(&(file->Cache[byte]));
                        fat->NextCluster = new_cluster;                                                 // append the free cluster to the end of the file in the FAT.
                        if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))               // save the modified sector to the FAT.
                        {
                                Fat16_Deinit();
                                return(0);
                        }
                        // now the new cluster appended to the fat is the last cluster
                        file->FirstSectorOfLastCluster = Fat16ClusterToSector(new_cluster);
                }
                else // last cluster of the file is undefined
                {   // then the new cluster must be the first one of the file
                    // and its cluster number must be set in the direntry
                        DirEntry_t * dir;
                        file->SectorInCache = file->DirectorySector;                            // update the sector number of file cache.
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector.
                        {
                                Fat16_Deinit();
                                return(CLUSTER_UNDEFINED);
                        }
                        dir = (DirEntry_t *)file->Cache;                                                        // set pointer to directory
                        dir[file->DirectoryIndex].Res1 = 0;
                        dir[file->DirectoryIndex].Res2[0] = 0;
                        dir[file->DirectoryIndex].Res2[1] = 0;
                        dir[file->DirectoryIndex].StartCluster = new_cluster;           // update startcluster
                    dir[file->DirectoryIndex].ModTime   = FileTime(&SystemTime);// set time
                        dir[file->DirectoryIndex].ModDate       = FileDate(&SystemTime);// and date of modification
                        dir[file->DirectoryIndex].LastAccessDate = dir[file->DirectoryIndex].ModDate;
                        dir[file->DirectoryIndex].Size          = 0;
                        // write sector containing the direntry
                        if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))
                        {
                                Fat16_Deinit();
                                return(CLUSTER_UNDEFINED);
                        }
                        // update file info
                        file->FirstSectorOfFirstCluster = Fat16ClusterToSector(new_cluster);
                        file->FirstSectorOfLastCluster = file->FirstSectorOfFirstCluster;
                        file->Size = 0;
                        file->Position = 0;
                }
                // update file pointes
                file->FirstSectorOfCurrCluster = Fat16ClusterToSector(new_cluster);
                file->SectorOfCurrCluster = 0;
                file->ByteOfCurrSector = 0;
        }
        return(new_cluster);
}


/****************************************************************************************************************************************************/
/* Function:    DirectoryEntryExist(s8 *, u8, u8, File_t *)                                                                                                                                                                                     */
/*                                                                                                                                                                                                                                                                                                      */
/* Description: This function searches all possible dir entries until the file or directory is found or the end of the directory is reached                     */
/*                                                                                                                                                                                                                                                                                                      */
/* Returnvalue: This function returns 1 if the directory entry specified was found.                                                                                                                                     */
/****************************************************************************************************************************************************/
u8 DirectoryEntryExist(s8 *dirname, u8 attribfilter, u8 attribmask, File_t *file)
{
        u32             dir_sector, max_dir_sector, curr_sector;
        u16     dir_entry = 0;

        u16     end_of_directory_not_reached = 0;
        u8              i = 0;
        u8      direntry_exist = 0;
        DirEntry_t * dir;

        // incomming pointers are useless?
        if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(direntry_exist);

        // check sector range to be searched

        // no search area defined?
        if(file->FirstSectorOfFirstCluster == SECTOR_UNDEFINED)
        {      
                return(direntry_exist);
        }
        //is search area the root?
        else if(Partition.FirstRootDirSector == file->FirstSectorOfFirstCluster)
        {
                max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; // limit to  size of root
                file->DirectorySector = SECTOR_UNDEFINED; // if we have to search within the root, there is no corresponding directory entry available
        }
        // is search area within the data cluster range?
        else if((Partition.FirstDataSector <= file->FirstSectorOfFirstCluster) && (file->FirstSectorOfFirstCluster <= Partition.LastDataSector))
        {
                max_dir_sector = Partition.SectorsPerCluster;                           // limit max sectors before next cluster
                /*
                //there must be a corresponding directory entry
                if(file->DirectorySector == SECTOR_UNDEFINED)
                {
                        return (direntry_exist);       
                }
                else // check directory entry
                {
                        file->SectorInCache = file->DirectorySector;                            // update the sector number of file cache.
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector.
                        {
                                Fat16_Deinit();
                                return (direntry_exist);
                        }
                        dir = (DirEntry_t *)file->Cache;                                                        // set pointer to directory
                        switch((u8)dir[file->DirectoryIndex].Name[0])                           // check if current directory exist
                        {
                                case SLOT_EMPTY:
                                case SLOT_DELETED:
                                        // the directrory pointer of this file points to a deleted or not existen directory
                                        // therefore no file or subdirectory exist here
                                        return(direntry_exist);
                                        break;
                                default:        // and is a real directory
                                        if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY)
                                        {       // current file is not a directory therefore no file or subdirectory can be searched here
                                                return(direntry_exist);
                                        }
                                        break;
                        }
                        // check that search area matches directory entry
                        if(file->FirstSectorOfFirstCluster != Fat16ClusterToSector(dir[file->DirectoryIndex].StartCluster))
                        {
                                return (direntry_exist);        
                        }
                       
                } */

        }
        else return (direntry_exist); // bad sector range for search area
       
        // update current file data area position to start of first cluster
        file->FirstSectorOfCurrCluster  = file->FirstSectorOfFirstCluster;
        file->SectorOfCurrCluster               = 0;
        file->ByteOfCurrSector                  = 0;

        do // loop over all data clusters of the current directory entry
        {
                dir_sector = 0;
                do // loop over all sectors of a cluster or all sectors of the root directory
                {
                        curr_sector = file->FirstSectorOfCurrCluster + dir_sector;      // calculate sector number
                        file->SectorInCache = curr_sector;                                                      // upate the sector number of file cache.
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector
                        {
                                Fat16_Deinit();
                                return(direntry_exist);
                        }
                        dir = (DirEntry_t *)file->Cache;                                                        // set pointer to directory
                        // search all directory entries within that sector
                        for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++)
                        {   // check for existing dir entry
                                switch((u8)dir[dir_entry].Name[0])
                                {
                                        case SLOT_EMPTY:
                                        case SLOT_DELETED:
                                                // ignore empty or deleted dir entries
                                                break;
                                        default:
                                                // if existing check attributes before names are compared will safe performance
                                                if ((dir[dir_entry].Attribute & attribmask) != attribfilter) break; // attribute must match
                                                // then compare the name to the giveb dirname (first 11 characters include 8 chars of basename and 3 chars extension.)
                                                i = 0;
                                                while((i < 11) && (dir[dir_entry].Name[i] == dirname[i])) i++;
                                                if (i < 10) break; // names does not match
                                                // if dirname and attribute have matched
                                                file->Attribute = dir[dir_entry].Attribute; // store attribute of found dir entry
                                                file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[dir_entry].StartCluster); // set sector of first data cluster
                                                file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
                                                file->SectorOfCurrCluster = 0;
                                                file->ByteOfCurrSector = 0;
                                                file->DirectorySector = curr_sector; // current sector
                                                file->DirectoryIndex  = dir_entry; // current direntry in current sector
                                                file->Size = dir[dir_entry].Size;
                                                direntry_exist = 1; // mark as found
                                                dir_entry = DIRENTRIES_PER_SECTOR;      // stop for-loop
                                } // end of first byte of name check
                        }
                        dir_sector++; // search next sector
                // stop if we reached the end of the cluster or the end of the root dir
                }while((dir_sector < max_dir_sector) && (!direntry_exist));

                // if we are seaching in the data area and the file not found in this cluster so take next cluster.
                if(!direntry_exist && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster))
                {
                        end_of_directory_not_reached = GetNextCluster(file);  // updates File->FirstSectorOfCurrCluster
                }
        }while((end_of_directory_not_reached) && (!direntry_exist)); // repeat until a next cluster exist an no match has been found
        return(direntry_exist);
}


/****************************************************************************************************************************************/
/*      Function:               CreateDirectoryEntry(s8 *, u16, File_t *)                                                                                                                                                       */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function looks for the next free position in the directory and creates an entry.                                                           */
/*                                      The type of an directory entry is specified by the file attribute.                                                                                                      */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    Return 0 on error                                                                                                                                                                                                       */
/****************************************************************************************************************************************/
u8 CreateDirectoryEntry(s8 *dirname, u8 attrib, File_t *file)
{
        u32 dir_sector, max_dir_sector, curr_sector;
        u16 dir_entry   = 0;
        u16 subdircluster, dircluster = 0;
        u16 end_of_directory_not_reached = 0;
        u8      i                       = 0;
        u8      retvalue        = 0;
        DirEntry_t *dir;

        // incomming pointers are useless?
        if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(retvalue);

        // check sector range to be searched

        // no creation area defined?
        if(file->FirstSectorOfFirstCluster == SECTOR_UNDEFINED)
        {      
                return(retvalue);
        }
        //is creation area the root?
        else if(Partition.FirstRootDirSector == file->FirstSectorOfFirstCluster)
        {
                max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR;  // limit to  size of root
                file->DirectorySector = SECTOR_UNDEFINED; // if we have to search within the root, there is no corresponding directory entry available
        }
        // is creation area within the data cluster range?
        else if((Partition.FirstDataSector <= file->FirstSectorOfFirstCluster) && (file->FirstSectorOfFirstCluster <= Partition.LastDataSector))
        {
                max_dir_sector = Partition.SectorsPerCluster;                                   // limit max sectors before next cluster
                // there must be a corresponding directory entry
                if(file->DirectorySector == SECTOR_UNDEFINED)
                {
                        return (retvalue);     
                }
                else // check directory entry
                {
                        file->SectorInCache = file->DirectorySector;                            // update the sector number of file cache.
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read in the sector.
                        {
                                Fat16_Deinit();
                                return (retvalue);
                        }
                        dir = (DirEntry_t *)file->Cache;                                                        // set pointer to directory
                        switch((u8)dir[file->DirectoryIndex].Name[0])                           // check if current directory exist
                        {
                                case SLOT_EMPTY:
                                case SLOT_DELETED:
                                        // the directrory pointer of this file points to a deleted or not existen directory
                                        // therefore no file or subdirectory exist here
                                        return(retvalue);
                                        break;
                                default:        // and is a real directory
                                        if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY)
                                        {       // current file is not a directory therefore no file or subdirectory can be created here
                                                return(retvalue);
                                        }
                                        break;
                        }
                        // check that search area matches directory entry
                        if(file->FirstSectorOfFirstCluster != Fat16ClusterToSector(dir[file->DirectoryIndex].StartCluster))
                        {
                                return (retvalue);      
                        }
                }
        }
        else return(retvalue); // bad sector range for search area

        // if the new directory entry is a subdirectory
        if((attrib & ATTR_SUBDIRECTORY) == ATTR_SUBDIRECTORY)
        {       // get a free cluster for its content
                subdircluster = FindNextFreeCluster(file);      // get the next free cluster on the disk and mark it as used.
        }
        else // a normal file
        {       // has no data cluster after creation
                subdircluster = CLUSTER_UNDEFINED;
        }

        file->FirstSectorOfCurrCluster  = file->FirstSectorOfFirstCluster;
        file->SectorOfCurrCluster               = 0;
        do // loop over all clusters of current directory
        {
                dir_sector = 0; // reset sector counter within a new cluster
                do // loop over all sectors of a cluster or all sectors of the root directory
                {
                        curr_sector = file->FirstSectorOfCurrCluster + dir_sector;      // calculate sector number
                        file->SectorInCache = curr_sector;                                                      // update the sector number of file cache.
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector.
                        {
                                Fat16_Deinit();
                                return(retvalue);
                        }

                        dir = (DirEntry_t *)file->Cache;                                                        // set pointer to directory
                        // search all directory entries of a sector
                        for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++)
                        {       // check if current direntry is available
                                if(((u8)dir[dir_entry].Name[0] == SLOT_EMPTY) || ((u8)dir[dir_entry].Name[0] == SLOT_DELETED))
                                {       // a free direntry was found
                                        for(i = 0; i < 11; i++) dir[dir_entry].Name[i] = dirname[i];            // Set dir name
                                        dir[dir_entry].Attribute    = attrib;
                                        dir[dir_entry].Res1 = 0;
                                        dir[dir_entry].CreationTime10ms = (u8)(SystemTime.mSec/10);
                                        dir[dir_entry].CreationTime     = FileTime(&SystemTime);
                                        dir[dir_entry].CreationDate     = FileDate(&SystemTime);
                                        dir[dir_entry].LastAccessDate = dir[dir_entry].CreationDate;
                                        dir[dir_entry].Res2[0] = 0;
                                        dir[dir_entry].Res2[1] = 0;
                                        dir[dir_entry].ModTime = dir[dir_entry].CreationTime;
                                        dir[dir_entry].ModDate = dir[dir_entry].CreationDate;
                                        // Set the attribute of the new directoryentry.
                                        dir[dir_entry].StartCluster = subdircluster;                                            // copy the location of the first datacluster to the directoryentry.
                                        dir[dir_entry].Size             = 0;                                                                    // the new createted file has no content yet.
                                        if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))       // write back to card
                                        {
                                                Fat16_Deinit();
                                                return(retvalue);
                                        }
                                        file->FirstSectorOfFirstCluster = Fat16ClusterToSector(subdircluster);  // Calculate absolute sectorposition of first datacluster.
                                        file->FirstSectorOfCurrCluster  = file->FirstSectorOfFirstCluster;      // Start reading the file with the first sector of the first datacluster.
                                        file->SectorOfCurrCluster               = 0;                                                            // reset sector of cureen cluster
                                        file->ByteOfCurrSector                  = 0;                                                            // reset the byte location within the current sector
                                        file->Attribute                                 = attrib;                                               // set file attribute to dir attribute
                                        file->Size                                              = 0;                                                        // new file has no size
                                        file->DirectorySector                   = curr_sector;
                                        file->DirectoryIndex                    = dir_entry;
                                        // prepare subdirectory data cluster
                                        if((attrib & ATTR_SUBDIRECTORY) == ATTR_SUBDIRECTORY)                           // if a new directory was created then initilize the data area
                                        {
                                                ClearCurrCluster(file); // fill cluster with zeros
                                                file->SectorInCache = file->FirstSectorOfFirstCluster;
                                                if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector.
                                                {
                                                        Fat16_Deinit();
                                                        return(retvalue);
                                                }
                                                dir = (DirEntry_t *)file->Cache;
                                                // create direntry "." to current dir
                                                dir[0].Name[0] = 0x2E;
                                                for(i = 1; i < 11; i++) dir[0].Name[i] = ' ';
                                                dir[0].Attribute = ATTR_SUBDIRECTORY;
                                                dir[0].StartCluster = subdircluster;
                                                dir[0].Size = 0;
                                                // create direntry ".." to the upper dir
                                                dir[1].Name[0] = 0x2E;
                                                dir[1].Name[1] = 0x2E;
                                                for(i = 2; i < 11; i++) dir[1].Name[i] = ' ';
                                                dir[1].Attribute = ATTR_SUBDIRECTORY;
                                                dir[1].StartCluster = dircluster;
                                                dir[1].Size = 0;
                                                if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// read in the sector.
                                                {
                                                        Fat16_Deinit();
                                                        return(retvalue);
                                                }
                                        }
                                        retvalue = 1;
                                        dir_entry = DIRENTRIES_PER_SECTOR;      // stop for-loop
                                }
                        }
                        dir_sector++; // search next sector
                // stop if we reached the end of the cluster or the end of the root dir
                }while((dir_sector < max_dir_sector) && (!retvalue));

                // if we are seaching in the data area and the file not found in this cluster so take next cluster.
                if(!retvalue && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster))
                {
                        end_of_directory_not_reached = GetNextCluster(file);  // updates File->FirstSectorOfCurrCluster
                }
        }while((end_of_directory_not_reached) && (!retvalue));
        // Perhaps we are at the end of the last cluster of a directory file and have no free direntry found.
        // Then we would need to add a cluster to that file and create the new direntry there.
        // This code is not implemented yet, because its occurs only if more that 32*32=1024 direntries are
        // within a subdirectory of root.

        return(retvalue);       // return 1 if file has been created otherwise return 0.
}

/********************************************************************************************************************************************/
/*      Function:               u8 DeleteDirectoryEntry(File_t *file)                                                                                                                                                                   */
/*      Description:    This function deletes the directoryentry of the file to be deleted from the filesystem                                                                  */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    1 on succes and 0 on error                                                                                                                                                                                              */
/********************************************************************************************************************************************/
u8 DeleteDirectoryEntry(File_t *file)
{
        DirEntry_t *DirectoryEntry = NULL;
        u8 retval = 0;

        if((!Partition.IsValid) || (file == NULL)) return (retval);
        // valid directory sector?
        if((Partition.FirstRootDirSector <= file->DirectorySector) && (file->DirectorySector < Partition.LastDataSector))
        {
                // try to  read the sector containing the directory entry of the file to be deleted
                if(SD_SUCCESS != SDC_GetSector((u32) file->DirectorySector,file->Cache)) return(retval);
                // get access to the elements of the directoryentry
                DirectoryEntry = (DirEntry_t *)file->Cache;
                // delete the directoryentry
                DirectoryEntry[file->DirectoryIndex].Name[0] = SLOT_DELETED;
                DirectoryEntry[file->DirectoryIndex].Attribute = 0;
                DirectoryEntry[file->DirectoryIndex].Size = 0;
                // the file has been deleted from the directory, save the modified sector back to the filesystem
                if(SD_SUCCESS == SDC_PutSector((u32) file->DirectorySector,file->Cache)) retval = 1;
        }
        return(retval);
}

/********************************************************************************************************************************************/
/*      Function:               FileExist(const s8* filename, u8 attribfilter, u8 attribmask, File_t *file);                                                                                    */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function looks for the specified file including its subdirectories.                                                                                                */
/*                  If the file is found the Filepointer properties are updated.                                                                                                                        */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    1 if file is found else 0.                                                                                                                                                                                              */
/********************************************************************************************************************************************/
u8 FileExist(const s8* filename, const u8 attribfilter, const u8 attribmask, File_t *file)
{
        s8* path = 0;
        s8* subpath = 0;
        u8 af, am, file_exist = 0;
        s8 dirname[12]; // 8+3 + temination character

        // if incomming pointers are useless return immediatly
        if ((filename == NULL) || (file == NULL) || (!Partition.IsValid)) return 0;

        // trace along the filepath
        path = (s8*)filename;                                                           // start at the beginning of the filename string
        file->DirectoryIndex = 0;                                                       // start search at first dir entry of cluster
        if(path[0] == '/') // if a path begins  with a '/' the search starts at RootDirectory with file search
        {
                file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector;         // start at RootDirectory with file search     
        }
        else // if a path begins not with a '/' the search starts relative to the CWD
        {
                file->FirstSectorOfFirstCluster = Partition.CurrentWorkingDirectory;
        }
        // as long as the file was not found and the remaining path is not empty
        while((*path != 0) && !file_exist)
        {       // separate dirname and subpath from filepath string
                subpath = SeperateFormatedDirName(path, dirname);
                if(subpath != NULL)
                {
                        if(*subpath == 0)
                        {       // empty subpath indicates last element of dir chain
                                af = attribfilter;
                                am = attribmask;
                        }
                        else  // it must be a subdirectory and no volume label
                        {
                                af = ATTR_SUBDIRECTORY;
                                am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL;
                        }
                        if(!DirectoryEntryExist(dirname, af, am, file))
                        {
                                return(file_exist); // subdirectory does not exist
                        }
                        else
                        {
                                if (*subpath == 0)
                                {
                                        file_exist = 1; // last element of path chain was found with the given attribute filter
                                }
                        }
                }
                else // error seperating the subpath
                {
                        return(file_exist); // bad subdir format
                }
                path = subpath;
                subpath = 0;
        }
        return(file_exist);
}


/********************************************************************************************************************************************/
/*      Function:               FileCreate(const s8* filename, u8 attrib, File_t *file);                                                                                                                                */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function looks for the specified file including its subdirectories beginning                                                                               */
/*                                      in the rootdirectory of the partition. If the file is found the Filepointer properties are                                                              */
/*                                      updated. If file or its subdirectories are not found they will be created                                                                                               */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    1 if file was created else 0.                                                                                                                                                                                   */
/********************************************************************************************************************************************/
u8 FileCreate(const s8* filename, const u8 attrib, File_t *file)
{
        s8 *path = 0;
        s8 *subpath = 0;
        u8 af, am, file_created = 0;
        s8 dirname[12];
        s8 txt[60];
        u8 i=0;

        // if incomming pointers are useless return immediatly
        if ((filename == NULL) || (file == NULL) || (!Partition.IsValid)) return(0);

        // trace along the filepath
        path = (s8*)filename;

        if(path[0] == '/') // if a path begins  with a '/' the search starts at RootDirectory with file search
        {
                file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector;         // start at RootDirectory with file search     
        }
        else // if a path begins not with a '/' the search starts relative to the CWD
        {
                file->FirstSectorOfFirstCluster = Partition.CurrentWorkingDirectory;
        }
        file->DirectoryIndex = 0;

        // as long as the file was not created and the remaining file path is not empty
        while((*path != 0) && !file_created)
        {   // separate dirname and subpath from filepath string
                subpath = SeperateFormatedDirName(path, dirname);
                if(subpath != NULL)
                {
                        if(*subpath == 0)
                        {       // empty subpath indicates last element of dir chain
                                af = ATTR_NONE;
                                am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL;  // any file that is no subdir or volume label
                        }
                        else  // it must be a subdirectory but no volume label
                        {
                                af = ATTR_SUBDIRECTORY;
                                am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL;
                        }
                        if(!DirectoryEntryExist(dirname, af, am, file)) // if subdir or file is not existent
                        {  // try to create subdir or file
                                if(*subpath == 0) af = attrib; // if last element in dir chain take the given attribute
                                if(!CreateDirectoryEntry(dirname, af, file))
                                {       // could not be created
                                        return(file_created);
                                }
                                else if(*subpath == 0) file_created = 1; // last element of path chain was created
                        }
                        sprintf(txt+i, "s:%06d",file->FirstSectorOfFirstCluster);
                        i+=8;
                }
                else // error seperating the subpath
                {
                        return(file_created); // bad subdir format
                }
                path = subpath;
                subpath = NULL;
        }
        Debug(txt);
        return(file_created);
}


/********************************************************************************************************************************************/
/*      Function:               void Slashing_Path(s8*)                                                                                                                                                                                                 */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function replaces all \ by / in the string pointed to by the argument                                                                              */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:                                                                                                                                                                                                                                                    */
/********************************************************************************************************************************************/
void Slashing_Path(s8* path)
{
        s8 *cptr = path;
        while(*cptr != 0 )
        {
                if(*cptr == '\\') *cptr = '/'; // replace \ by /
                cptr++;
        }
}

/********************************************************************************************************************************************/
/*      Function:               File_t * fopen_(s8* filename, s8 mode);                                                                                                                                                                 */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function looks for the specified file in the rootdirectory of the drive. If the file is found the number of the    */
/*                                      corrosponding filepointer is returned. Only modes 'r' (reading), 'w' (writing) and 'a' (append) are implemented yet.    */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    The filepointer to the file or 0 if faild.                                                                                                                                                              */
/********************************************************************************************************************************************/
File_t * fopen_(s8* const filename, const s8 mode)
{
        File_t *file    = 0;
        s8 *cptr;

        if((!Partition.IsValid) || (filename == 0)) return(file);

        // Look for an unused filepointer in the file pointer list?
        file = LockFilePointer();
        // if no unused file pointer was found return 0
        if(file == NULL) return(file);

        // now we have found a free filepointer and claimed it
        file->Mode                                              = mode;         // mode of fileoperation (read,write)
        file->Attribute                                 = 0;            // the attribute of the file opened.

        // bring the path into the correct syntax
        Slashing_Path(filename);
        cptr = filename;

        // check if a real file (no directory) to the given filename exist
        if(FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file))
        {  // file exist
                switch(mode)  // check mode
                {
                        case 'a':       // if mode is: append to file
                                if((file->Attribute & ATTR_READONLY) == ATTR_READONLY)
                                {       // file is marked as readonly --> do not open this file
                                        fclose_(file);
                                        file = NULL;
                                }
                                else
                                {       // file is not marked as read only --> goto end of file
                                        fseek_(file, 0, SEEK_END);              // point to the end of the file
                                }
                                break;
                        case 'w':       // if mode is: write to file
                                if((file->Attribute & ATTR_READONLY) == ATTR_READONLY)
                                {       // file is marked as readonly --> do not open this file
                                        fclose_(file);
                                        file = NULL;
                                }
                                else
                                {       // file is not marked as read only
                                        DirEntry_t * dir;
                                        // free all clusters of that file
                                        DeleteClusterChain(SectorToFat16Cluster(file->FirstSectorOfFirstCluster));
                                        // update directory entry of that file
                                        file->SectorInCache = file->DirectorySector;                            // update the sector number of file cache.
                                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector.
                                        {
                                                Fat16_Deinit();
                                                return(NULL);
                                        }
                                        dir = (DirEntry_t *)file->Cache;                                                                // set pointer to directory
                                    dir[file->DirectoryIndex].ModTime   = FileTime(&SystemTime);        // set modification time
                                        dir[file->DirectoryIndex].ModDate       = FileDate(&SystemTime);        // set modification date
                                        dir[file->DirectoryIndex].LastAccessDate = dir[file->DirectoryIndex].ModDate;
                                        dir[file->DirectoryIndex].StartCluster = CLUSTER_UNDEFINED;             // update startcluster
                                        dir[file->DirectoryIndex].Size          = 0;
                                        // write sector containing the direntry
                                        if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))
                                        {
                                                Fat16_Deinit();
                                                return(NULL);
                                        }
                                        file->FirstSectorOfFirstCluster = SECTOR_UNDEFINED;
                                        file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
                                        file->SectorOfCurrCluster = 0;
                                        file->ByteOfCurrSector = 0;
                                        file->Size = 0;
                                        file->Position = 0;
                                        fseek_(file, 0, SEEK_SET);
                                }
                                break;
                        case 'r':       // if mode is: read from file
                                // goto end of file
                                fseek_(file, 0, SEEK_SET);
                                break;
                        default: // other modes are not supported
                                fclose_(file);
                                file = NULL;
                        break;
                }
                return(file);
        }
        else // file does not exist
        {
                switch(mode)  // check mode
                {
                        case 'a':
                        case 'w': // if mode is write or append
                                // try to create the file
                                if(!FileCreate(filename, ATTR_ARCHIVE, file))
                                { // if it could not be created
                                        fclose_(file);
                                        file = NULL;
                                }
                                break;
                        case 'r': // else opened for 'r'
                        default:  // if unsupported mode
                                fclose_(file);
                                file = NULL;
                                break;
                }
                return(file);
        }
        // we should never come to this point
        fclose_(file);
        file = NULL;
        return(file);
}

/****************************************************************************************************************************************************/
/* Function:    s16 fseek_(File_t *, s32 *, u8)                                                                                                                                                                                                         */
/*                                                                                                                                                                                                                                                                                                      */
/* Description: This function sets the pointer of the stream relative to the position                                                                                                                           */
/*                              specified by origin (SEEK_SET, SEEK_CUR, SEEK_END)                                                                                                                                                                      */
/* Returnvalue: Is 0 if seek was successful                                                                                                                                                                                                                     */
/****************************************************************************************************************************************************/
s16 fseek_(File_t *file, s32 offset, s16 origin)
{
        s32             fposition       = 0;
        s16     retvalue        = 1;
        u32             byte_index       = 0;   // the byteindex within a sector
        u32             sector_index  = 0;      // the  sectorindex within a cluster
        u32     cluster_index = 0;      // the index of the cluster within the clusterchain inside the fat

        // check if the partition is valid
        if((!Partition.IsValid) || (file == NULL)) return(retvalue);
        switch(origin)
        {
                case SEEK_SET:                          // Fileposition relative to the beginning of the file.
                        fposition = 0;
                        break;
                case SEEK_END:                          // Fileposition relative to the end of the file.
                        fposition = (s32)file->Size;
                        break;
                case SEEK_CUR:                          // Fileposition relative to the current position of the file.
                default:
                        fposition = file->Position;
                        break;
        }

        // calculate the specified fileposition according to the selected mode
        fposition += offset;
        // is the fileposition within the file?
        if((fposition >= 0) && (fposition <= (s32)file->Size))
        {
                // initialize the filepointer to start of the file
                file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
                file->SectorOfCurrCluster       = 0;
                file->ByteOfCurrSector          = 0;
                file->Position                          = 0;
                // has the specified file at least one valid sector attached?
                if(file->FirstSectorOfCurrCluster == SECTOR_UNDEFINED) return(retvalue);
                // calculate the absolute number of the sector wich contains the fileposition we are looking for
                sector_index = (u32) ((u32)fposition >> 9);      // 512 bytes per sector
                // calculate the index of the cluster containing the specified sector
                cluster_index = (u32) sector_index / Partition.SectorsPerCluster;
                // the absolute sectornumber becomes relative to the beginning of the specified cluster
                sector_index = (sector_index % Partition.SectorsPerCluster);
                // calculate the index of the byteposition the fileposition points to
                byte_index = (u32) fposition % 512;
                // parse the fat till the calculated cluster has been reached
                while(cluster_index--) GetNextCluster(file);
                // set the filepointer to the specified sector and byteposition
                file->SectorOfCurrCluster = (u8) sector_index;
                file->ByteOfCurrSector = (u16) byte_index;
                // the fileposition now equals the filepointer
                file->Position = (u32)fposition;
                // the specified fileposition has been reached
                retvalue = 0;
        }
        return(retvalue);
}

/****************************************************************************************************************************************************/
/* Function:    fflush_(File *);                                                                                                                                                                                                                                        */
/*                                                                                                                                                                                                                                                                                                      */
/* Description: This function writes the data already in the buffer but not yet written to the file.                                                                                            */
/*                                                                                                                                                                                                                                                                                                      */
/* Returnvalue: 0 on success EOF on error                                                                                                                                                                                                                       */
/****************************************************************************************************************************************************/
s16     fflush_(File_t *file)
{
        DirEntry_t *dir;

        if((file == NULL) || (!Partition.IsValid)) return (EOF);

        switch(file->Mode)
        {
                case 'a':
                case 'w':
                        if(file->ByteOfCurrSector > 0)                                                                          // has data been added to the file?
                        {
                                if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// save the data still in the buffer
                                {
                                        Fat16_Deinit();
                                        return(EOF);
                                }
                        }
                        file->SectorInCache     = file->DirectorySector;
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))                                       // read the directory entry for this file.
                        {
                                Fat16_Deinit();
                                return(EOF);
                        }

                        dir = (DirEntry_t *)file->Cache;
                        // update file size and modification time & date
                        dir[file->DirectoryIndex].ModTime = FileTime(&SystemTime);
                        dir[file->DirectoryIndex].ModDate = FileDate(&SystemTime);
                        dir[file->DirectoryIndex].LastAccessDate = dir[file->DirectoryIndex].ModDate;
                        dir[file->DirectoryIndex].Size = file->Size;                                            // update file size
                        if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))       // write back to sd-card
                        {
                                Fat16_Deinit();
                                return(EOF);
                        }
                        break;
                case 'r':
                default:
                        // do nothing!
                        return(EOF);
                        break;

        }
        return(0);
}

/********************************************************************************************************************************************/
/*      Function:               fgetc_(File *file);                                                                                                                                                                                                             */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function reads and returns one character from the specified file. Is the end of the actual sector reached the              */
/*                                      next sector of the cluster is read. If the last sector of the cluster read the next cluster will be searched in FAT.    */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    The function returns the character read from the specified memorylocation as u8 casted to s16 or EOF.                                   */
/********************************************************************************************************************************************/
s16 fgetc_(File_t *file)
{
        s16 c = EOF;
        u32 curr_sector;

        if( (!Partition.IsValid) || (file == NULL)) return(c);
        // if the end of the file is not reached, get the next character.
        if((0 < file->Size) && ((file->Position) < file->Size) )
        {
                curr_sector  = file->FirstSectorOfCurrCluster;          // calculate the sector of the next character to be read.
                curr_sector += file->SectorOfCurrCluster;

                if(file->SectorInCache != curr_sector)
                {
                        file->SectorInCache = curr_sector;
                        if(SD_SUCCESS != SDC_GetSector(file->SectorInCache,file->Cache))
                        {
                                Fat16_Deinit();
                                return(EOF);
                        }
                }
                c = (s16) file->Cache[file->ByteOfCurrSector];
                file->Position++;                                                                       // increment file position
                file->ByteOfCurrSector++;                                                       // goto next byte in sector
                if(file->ByteOfCurrSector >= BYTES_PER_SECTOR)          // if end of sector
                {
                        file->ByteOfCurrSector = 0;                                             //  reset byte location
                        file->SectorOfCurrCluster++;                                    //      next sector
                        if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster)    // if end of cluster is reached, the next datacluster has to be searched in the FAT.
                        {

                                if(GetNextCluster(file))                                                                                // Sets the clusterpointer of the file to the next datacluster.
                                {
                                        file->SectorOfCurrCluster = 0;                                                          // start reading new cluster at first sector of the cluster.
                                }
                                else // the last cluster was allready reached
                                {
                                        file->SectorOfCurrCluster--;                                                    // jump back to the last sector in the last cluster
                                        file->ByteOfCurrSector = BYTES_PER_SECTOR;                              // set ByteOfCurrSector one byte over sector end
                                }
                        }
                }
        }
        return(c);
}

/********************************************************************************************************************************************/
/*      Function:               fputc_( const s8 c, File *file);                                                                                                                                                                                */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function writes a byte to the specified file and takes care of writing the necessary FAT- Entries.                                 */
/*                                      next sector of the cluster is read. If the last sector of the cluster read the next cluster will be searched in FAT.    */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    The function returns the character written to the stream or EOF on error.                                                                                               */
/********************************************************************************************************************************************/
s16 fputc_(const s8 c, File_t *file)
{
        u32 curr_sector  = 0;

        if((!Partition.IsValid) || (file == NULL)) return(EOF);
        switch(file->Mode)
        {
                case 'w':
                case 'a':
                        // If file position equals to file size, then the end of file has been reached.
                        // In this case it has to be checked that the ByteOfCurrSector is BYTES_PER_SECTOR
                        // and a new cluster should be appended.
                        // If the first sector of first cluster is unvalid, then the file claims no data clusters
                        // and size should be zero, therefore append a new Cluster too.
                        if(((file->Position >= file->Size) && (file->ByteOfCurrSector >= BYTES_PER_SECTOR)) || (file->FirstSectorOfFirstCluster == SECTOR_UNDEFINED))
                        {
                                if(CLUSTER_UNDEFINED == AppendCluster(file)) return(EOF);
                        }

                        curr_sector  = file->FirstSectorOfCurrCluster;
                        curr_sector += file->SectorOfCurrCluster;
                        if(file->SectorInCache != curr_sector)
                        {
                                file->SectorInCache = curr_sector;
                                if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))
                                {
                                        Fat16_Deinit();
                                        return(EOF);
                                }
                        }

                        file->Cache[file->ByteOfCurrSector] = (u8)c;            // write databyte into the buffer. The byte will be written to the device at once
                        if(file->Size == file->Position) file->Size++;          // a character has been written to the file so the size is incremented only when the character has been added at the end of the file.
                        file->Position++;                                                                       // the actual positon within the file.
                        file->ByteOfCurrSector++;                                                       // goto next byte in sector
                        if(file->ByteOfCurrSector >= BYTES_PER_SECTOR)          // if the end of this sector is reached yet
                        {       // save the sector to the sd-card

                                if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))
                                {
                                        Fat16_Deinit();
                                        return(EOF);
                                }
                                file->ByteOfCurrSector = 0;                                             //  reset byte location
                                file->SectorOfCurrCluster++;                                    //      next sector
                                if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster)// if end of cluster is reached, the next datacluster has to be searched in the FAT.
                                {
                                        if(!GetNextCluster(file))                                                               // Sets the clusterpointer of the file to the next datacluster.
                                        { // if current cluster was the last cluster of the file
                                                if(!AppendCluster(file))                                                // append a new and free cluster at the end of the file.
                                                {
                                                        file->SectorOfCurrCluster--;                            // jump back to last sector of last cluster
                                                        file->ByteOfCurrSector = BYTES_PER_SECTOR;      // set byte location to 1 byte over sector len
                                                        return(EOF);
                                                }
                                        }
                                        else // next cluster
                                        {
                                                file->SectorOfCurrCluster = 0;                                                  // start reading new cluster at first sector of the cluster.
                                        }
                                }
                        }
                        break;
                case 'r':
                default:
                        return(EOF);
                        break;
        } // EOF switch(file->Mode)
        return(0);
}


/****************************************************************************************************************************************/
/*      Function:               fread_(void *buffer, s32 size, s32 count, File *File);                                                                                                                          */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function reads count objects of the specified size                                                                                                                         */
/*                                      from the actual position of the file to the specified buffer.                                                                                                           */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    The function returns the number of objects (not bytes) read from the file.                                                                                      */
/****************************************************************************************************************************************/
u32 fread_(void *buffer, u32 size, u32 count, File_t *file)
{
        u32 object_cnt  = 0;                                                                                    // count the number of objects read from the file.
        u32 object_size = 0;                                                                                    // count the number of bytes read from the actual object.
        u8 *pbuff       = 0;                                                                                    // a pointer to the actual bufferposition.
        u8 success      = 1;                                                                                    // no error occured during read operation to the file.
        s16 c;

        if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0);

        pbuff = (u8 *) buffer;                                                                                  // cast the void pointer to an u8 *

        while((object_cnt < count) && success)
        {
                object_size = size;
                while((size > 0) && success)
                {
                        c = fgetc_(file);
                        if(c != EOF)
                        {
                                *pbuff = (u8)c;                                                                         // read a byte from the buffer to the opened file.
                                pbuff++;
                                size--;
                        }
                        else // error or end of file reached
                        {
                                success = 0;
                        }
                }
                if(success) object_cnt++;
        }
        return(object_cnt);                                                                                             // return the number of objects succesfully read from the file
}


/****************************************************************************************************************************************/
/*      Function:               fwrite_(void *buffer, s32 size, s32 count, File *file);                                                                                                                         */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function writes count objects of the specified size                                                                                                                        */
/*                                      from the buffer pointer to the actual position in the file.                                                                                                                     */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    The function returns the number of objects (not bytes) read from the file.                                                                                      */
/****************************************************************************************************************************************/
u32 fwrite_(void *buffer, u32 size, u32 count, File_t *file)
{
        u32 object_cnt  = 0;                                                                                                            // count the number of objects written to the file.
        u32 object_size = 0;                                                                                                            // count the number of bytes written from the actual object.
        u8 *pbuff           = 0;                                                                                                                // a pointer to the actual bufferposition.
        u8 success      = 1;                                                                                                            // no error occured during write operation to the file.
        s16 c;

        if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0);
        if(file->Mode == 'r') return (0); // opened read only
        pbuff = (u8 *) buffer;                                                                                                          // cast the void pointer to an u8 *

        while((object_cnt < count) && success)
        {
                object_size = size;
                while((size > 0) && success)
                {
                        c = fputc_(*pbuff, file);                                                                                       // write a byte from the buffer to the opened file.
                        if(c != EOF)
                        {
                                pbuff++;
                                size--;
                        }
                        else
                        {
                                success = 0;
                        }
                }
                if(success) object_cnt++;
        }

        return(object_cnt);             // return the number of objects succesfully written to the file
}


/****************************************************************************************************************************************/
/*      Function:               fputs_(const s8 *string, File_t *File);                                                                                                                                                         */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function writes a string to the specified file.                                                                                                                            */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    The function returns a no negative value or EOF on error.                                                                                                                       */
/****************************************************************************************************************************************/
s16 fputs_(s8 * const string, File_t * const file)
{
        u8 i=0;
        s16 c = 0;

        if((!Partition.IsValid) || (file == NULL) || (string == NULL)) return(EOF);
        if(file->Mode == 'r') return(EOF);
        while((string[i] != 0)&& (c != EOF))
        {
                c = fputc_(string[i], file);
                i++;
        }
        return(c);
}

/****************************************************************************************************************************************/
/*      Function:               fgets_(s8 *, s16 , File_t *);                                                                                                                                                                           */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function reads a string from the file to the specifies string.                                                                                             */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    A pointer to the string read from the file or 0 on error.                                                                                                                       */
/****************************************************************************************************************************************/
s8 * fgets_(s8 * const string, s16 const length, File_t * const file)
{
        s8 *pbuff;
        s16 c = 0, bytecount;

        if((!Partition.IsValid) || (file == NULL) || (string == NULL) || (length < 1)) return (0);
        bytecount = length;
        pbuff = string;                                                         // set write pointer to start of string
        while(bytecount > 1)                                            // read the length-1 characters from the file to the string.
        {
                c = fgetc_(file);                                               // read a character from the opened file.
                switch(c)
                {
                        case 0x0A:                                                      // new line
                                *pbuff = 0;                                             // set string terminator
                                return(string);                                 // return normal

                        case EOF:
                                *pbuff = 0;                                             // set string terminator
                                return(0);

                        default:
                                *pbuff++ = (s8)c;                               // copy byte to string
                                bytecount--;
                                break;
                }
        }
        *pbuff = 0;     // set string terminator
        return(string);
}

/****************************************************************************************************************************************/
/*      Function:               fclose_(File *file);                                                                                                                                                                                            */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function closes the open file by writing the remaining data                                                                                                        */
/*                                      from the buffer to the device and entering the filesize in the directory entry.                                                                         */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    0 on success EOF on error                                                                                                                                                                                       */
/****************************************************************************************************************************************/
s16 fclose_(File_t *file)
{
        s16 returnvalue = EOF;

        if(file == NULL) return(returnvalue);
        returnvalue = fflush_(file);
        UnlockFilePointer(file);
        return(returnvalue);
}


/****************************************************************************************************************************************/
/*      Function:               fexist_(const u8*);                                                                                                                                                                                                     */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function checks if a file already exist.                                                                                                                                           */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    1 if the file exist else 0.                                                                                                                                                                                     */
/****************************************************************************************************************************************/
u8 fexist_(s8 * const filename)
{
        u8 exist = 0;
        File_t *file = 0;
        file = LockFilePointer();
        exist = FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file);
        UnlockFilePointer(file);
        return(exist);
}

/****************************************************************************************************************************************/
/*      Function:               feof_(File_t *File);                                                                                                                                                                                            */
/*                                                                                                                                                                                                                                                                              */
/*      Description:    This function checks wether the end of the file has been reached.                                                                                                                                               */
/*                                                                                                                                                                                                                                                                              */
/*      Returnvalue:    0 if the end of the file was not reached otherwise 1.                                                                                                                                                                           */
/****************************************************************************************************************************************/
u8 feof_(File_t *file)
{
        if(((file->Position)+1) < (file->Size))
        {
                return(0);
        }
        else
        {
                return(1);
        }
}


/********************************************************************************************************************************************/
/*      Function:               fdelete_(s8* filepath);                                                                                                                                                                                                 */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function deletes the file with the specified filename from the filesystem                                                                                  */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    1 : specified file deleted succesfully 0: specified file not found                                                                                                              */
/********************************************************************************************************************************************/

u8 fdelete_(s8* const filepath)
{
        u8 retvalue = 0;
        File_t file;

        if(filepath == NULL) return(0);
        // correct filepath formatting
        Slashing_Path(filepath);
        file.FirstSectorOfFirstCluster = SECTOR_UNDEFINED;              // First sector of the first cluster of the file.
        file.FirstSectorOfCurrCluster = SECTOR_UNDEFINED;               // First sector of the cluster which is edited at the moment.
        file.FirstSectorOfLastCluster = SECTOR_UNDEFINED;               // First sector of the last cluster of the file
        file.SectorOfCurrCluster = SECTOR_UNDEFINED;                    // The sector within the current cluster.
        file.ByteOfCurrSector = 0;                                                              // The byte location within the current sector.
        file.Mode = 0;                                                                                  // Mode of fileoperation (read,write)
        file.Size = 0;                                                                                  // The size of the opend file in bytes.
        file.Position = 0;                                                                              // Pointer to a character within the file 0 < fileposition < filesize
        file.DirectorySector = SECTOR_UNDEFINED;                                // the sectorposition where the directoryentry has been made.
        file.DirectoryIndex = 0;                                                                // The index to the directoryentry within the specified sector.
        file.Attribute = 0;                                                                             // The attribute of the file opened.
        file.SectorInCache = SECTOR_UNDEFINED;                                  // The last sector read, which is still in the sector cache.
        file.State = FSTATE_USED;                                                               // mark it as used
       
        // if file is existent
        if(FileExist(filepath, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, &file))
        {
                // and is not marked as readonly?
                if((file.Attribute & ATTR_READONLY) != ATTR_READONLY)
                {
                        // try to delete the file content from the filesystem
                        if(DeleteClusterChain(file.FirstSectorOfFirstCluster))
                        {
                                // and try delete the directory entry of the file
                                retvalue = DeleteDirectoryEntry(&file);
                        }      
                }
        }
        return(retvalue);
}

/****************************************************************************************************************************************************/
/* Function:    s8* FAT16_GetVolumeLabel(void)                                                                                                                                                                                                                  */
/*                                                                                                                                                                                                                                                                                                      */
/* Description: This function returns the volume label                                                                                                                                                                                          */
/*                                                                                                                                                                                                                                                                                                      */
/* Returnvalue: This function returns the pointer to the volume label or NULL if not found.                                                                                                                     */
/****************************************************************************************************************************************************/
s8* FAT16_GetVolumeLabel(void)
{
        s8              *pVolumeLabel = NULL;
        u32             dir_sector, max_dir_sector, curr_sector;
        u16     dir_entry = 0;
        u8              i = 0;

        DirEntry_t * dir;
        File_t *file = NULL;

        // if Partition is not valid return NULL
        if(!Partition.IsValid) return(pVolumeLabel);
        // if Volume label was read before return it
        if(Partition.VolumeLabel[0]!= '\0') return (Partition.VolumeLabel);
        // try to catch a file pointer
        file = LockFilePointer();
        if(file == NULL) return(pVolumeLabel);
        // search dir entries direct within the root directory area
        file->DirectorySector = 0;
        max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR;
        file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector;

        // update current file data area position to start of first cluster
        file->FirstSectorOfCurrCluster  = file->FirstSectorOfFirstCluster;
        file->SectorOfCurrCluster               = 0;
        file->ByteOfCurrSector                  = 0;

        dir_sector = 0; // reset sector counter within a new cluster
        do // loop over all sectors of the root directory
        {
                curr_sector = file->FirstSectorOfCurrCluster + dir_sector;      // calculate sector number
                file->SectorInCache = curr_sector;                                                      // upate the sector number of file cache.
                if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector
                {
                        Fat16_Deinit();
                        return(NULL);
                }
                dir = (DirEntry_t *)file->Cache;                                                        // set pointer to directory
                // search all directory entries within that sector
                for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++)
                {   // check for existing dir entry
                        switch((u8)dir[dir_entry].Name[0])
                        {
                                case SLOT_EMPTY:
                                case SLOT_DELETED:
                                        // ignore empty or deleted dir entries
                                        break;
                                default:
                                        // check attributes for volume label
                                        if ((dir[dir_entry].Attribute & ATTR_VOLUMELABEL) != ATTR_VOLUMELABEL) break; // attribute must match
                                        // (first 11 characters include 8 chars of basename and 3 chars extension.)
                                        for(i = 0; i<11;i++) Partition.VolumeLabel[i] = dir[dir_entry].Name[i];
                                        Partition.VolumeLabel[11] = '\0'; // terminate string
                                        file->Attribute = dir[dir_entry].Attribute; // store attribute of found dir entry
                                        file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[dir_entry].StartCluster); // set sector of first data cluster
                                        file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
                                        file->SectorOfCurrCluster = 0;
                                        file->ByteOfCurrSector = 0;
                                        file->DirectorySector = curr_sector; // current sector
                                        file->DirectoryIndex  = dir_entry; // current direntry in current sector
                                        file->Size = dir[dir_entry].Size;
                                        dir_entry = DIRENTRIES_PER_SECTOR;      // stop for-loop
                                        pVolumeLabel =  Partition.VolumeLabel;
                        } // end of first byte of name check
                }
                dir_sector++; // search next sector
        // stop if we reached the end of the cluster or the end of the root dir
        }while((dir_sector < max_dir_sector) && (!pVolumeLabel));

        UnlockFilePointer(file);
        return(pVolumeLabel);
}


/********************************************************************************************************************************************/
/*      Function:               u8 FindItem(Find_t);                                                                                                                                                                                                    */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function looks for the item specified by global structure FindElement in the actual directory                                      */
/*                                                                                                                                                                                                                                                              */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    TRUE if an matching element was found                                                                                                                                                                   */
/********************************************************************************************************************************************/

u8 FindItem(Find_t *findelement)
{
        u16             index = 0;
        u16             max_dir_sector = 0;
        u16     end_of_directory_not_reached = 1;                                                                               // the directory has been read completely without a result.
        u8              i = 0;
        u8              readpointer = 0;
        u8              writepointer = 0;
        u8              retvalue = 0;
        DirEntry_t      *DirectoryEntry;
        File_t  file;
        SD_Result_t res=0;

        file.FirstSectorOfCurrCluster   = findelement->fp.FirstSectorOfCurrCluster;
        file.SectorOfCurrCluster        = findelement->fp.SectorOfCurrCluster;
        index                                                   = findelement->fp.DirectoryIndex;

        // within the root directory area we can read sectors sequentially until the end of this area
        if((Partition.FirstRootDirSector <= file.FirstSectorOfCurrCluster) && (file.FirstSectorOfCurrCluster < Partition.FirstDataSector))
        {
                max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR;
        }
        // within the data clusters we can read sectors sequentially only within the cluster
        else if((Partition.FirstDataSector <= file.FirstSectorOfCurrCluster) && (file.FirstSectorOfCurrCluster <= Partition.LastDataSector))
        {
                max_dir_sector = Partition.SectorsPerCluster;                           // limit max secters before next cluster
        }

        do
        {       // search the next 16 rootentries in this sector of the roordirectory.
                res = SDC_GetSector(((u32) file.FirstSectorOfCurrCluster + (u32)file.SectorOfCurrCluster), file.Cache);                                         // Read the Rootdirectory.
                if(res != SD_SUCCESS)
                {
                        Fat16_Deinit();
                        return(0);
                }

                DirectoryEntry = (DirEntry_t *)file.Cache;

                while((!retvalue)&&(index < 16))
                {
                        i = 0;
                        if( (u8)DirectoryEntry[index].Name[0] != SLOT_DELETED)          // ignore deleted items.
                        {
                                while((i <= 10) && ( (DirectoryEntry[index].Name[i] == findelement->searchstring[i]) || (findelement->searchstring[i] == '*') || findelement->searchstring[i] == '?'))
                                {
                                        i++;
                                }
                        }
                        if((DirectoryEntry[index].Attribute & 0x30) && (DirectoryEntry[index].Attribute & findelement->attribmask) && (i == 11))
                        {
                /* initialize the namestring with 0 */
                        for(i = 0; i < 12; i++) findelement->name[i] = 0;
                for(readpointer = 0; readpointer <= 10; readpointer++)
                                {
                                        if((DirectoryEntry[index].Name[readpointer] != ' ') && (readpointer!=8))
                                        {
                                                findelement->name[writepointer] = DirectoryEntry[index].Name[readpointer];      // copy the name of the item found to the find_structure.
                                                writepointer++;
                                        }
                                        else
                                        {
                                                if(DirectoryEntry[index].Attribute == ATTR_ARCHIVE)
                                                {
                                        if(readpointer < 8) readpointer = 8;
                                        if(DirectoryEntry[index].Name[readpointer] != ' ')
                                        {
                                        findelement->name[writepointer] = '.';                                                  // then seperate the name and the extension by a '.' at index 8.
                                        writepointer++;
                                        findelement->name[writepointer] = DirectoryEntry[index].Name[readpointer];      // copy the name of the item found to the find_structure.
                                        writepointer++;
                                        }
                                            else break;
                                                }
                            else break;
                        }
                                }
                                findelement->fp.FirstSectorOfFirstCluster = (u32) DirectoryEntry[index].StartCluster;
                                findelement->fp.DirectoryIndex   = index;
                                findelement->fp.FirstSectorOfCurrCluster   = file.FirstSectorOfCurrCluster;
                                findelement->fp.DirectorySector  = (file.FirstSectorOfCurrCluster + file.SectorOfCurrCluster);
                                findelement->fp.SectorOfCurrCluster      = file.SectorOfCurrCluster;
                findelement->fp.Size            = DirectoryEntry[index].Size;
                                findelement->fp.Attribute               = DirectoryEntry[index].Attribute;
                retvalue = 1;
                        }
                        /* search the next sector */
                        index++;
                }
                /* this sector has been searched but we havn't found what we are looking for. Therefore we have to find the next sector */
                if(!retvalue)                                                                                                                           // file not found in this sector so take next sector.
                {
                        /* in the next sector we start looking for the specified entry beginning at index 0 */
                        index = 0;
                        /* there are still sectors to be read within the cluster or within the linear addresspace of the rootdirectory */
                        if(file.SectorOfCurrCluster < max_dir_sector-1)  file.SectorOfCurrCluster++;
                        /* if we are looking for an directoryentry outside the rootdirectory and have reached the end of the cluster we have to get the next one */
                        else if(Partition.FirstDataSector <= file.FirstSectorOfCurrCluster)
                        {
                                end_of_directory_not_reached = GetNextCluster(&file);
                        }
                        // within the rootdirectory we abort searching for elements after the maximun number of possible rootentries has beensearched
                        else
                        {
                                end_of_directory_not_reached = 0;
                        }
                }
        }
        while((end_of_directory_not_reached) && (!retvalue) );

        return(retvalue);
}




/********************************************************************************************************************************************/
/*      Function:               findnext_(Find_t *);                                                                                                                                                                                                                    */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function looks for the next item in the specified directory with a matching filename and fileattributes specified  */
/*                  by function findfirst()                                                                                                                                                                                                     */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:                                                                                                                                                                                                                                                    */
/********************************************************************************************************************************************/
u8 findnext_(Find_t * findelement)
{
        u8 itemfound = 0;
        u8 index = 0;

        findelement->fp.DirectoryIndex++;

        /* before we start searching an element we clear the complete namestring within the structure FindElement */
        for(index = 0; index < 11; index++) findelement->name[index] = 0;

        if(FindItem(findelement))
        {
                itemfound = 1;
        }

        return(itemfound);
}



/********************************************************************************************************************************************/
/*      Function:               findfirst_(s8* filename, u8 attribmask, Find_t *);                                                                                                                                              */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function looks for the first item in the specified directory with a matching filename and fileattributes                   */
/*                                      The filename of the element found is transformed from 8.3 to a string                                                                                                   */
/*                                                                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    (1) if Element was found. (0) if no valid element was found                                                                                                                     */
/********************************************************************************************************************************************/
u8 findfirst_(const s8* name, u8 attribmask, Find_t *findelement)
{
        u8 itemfound = 0;
        u8 index = 0;

        /* initialize the FindElement structure */
        findelement->fp.FirstSectorOfFirstCluster = 0;                                  // First sector of the first cluster of the file.
        findelement->fp.FirstSectorOfCurrCluster = Partition.CurrentWorkingDirectory;   // First sector of the cluster which is edited at the moment.
        findelement->fp.SectorOfCurrCluster = 0;                                                // The sector within the current cluster.
        findelement->fp.ByteOfCurrSector = 0;                                                   // The byte location within the current sector.
        findelement->fp.Size = 0;                                                                               // The size of the opend file in bytes.
        findelement->fp.Position = 0;                                                                   // Pointer to a character within the file 0 < fileposition < filesize
        findelement->fp.DirectorySector = 0;                                                    // the sectorposition where the directoryentry has been made.
        findelement->fp.DirectoryIndex = 0;                                                             // The index to the directoryentry within the specified sector.
        findelement->attribfilter = 0;
        findelement->attribmask = attribmask;
        findelement->searchstring[0] = 0;                                                               // terminate string at the begin (=NULL)

        // bring it to the 8.3 format
        if(NULL == SeperateFormatedDirName(name, findelement->searchstring)) return(0);

        //process the wildcards (*). After an * all following character are wildcards to
        for(index = 0;index < 8;index++)
        {
                /* if we find an wildcard within the name of the searchstring all remaining character after the wildcard shall be wildcards also */
                if(findelement->searchstring[index] == '*')
                {
                        /*  */
                        while(++index < 8) findelement->searchstring[index] = '*';
                }
        }
        for(index = 8;index < 11;index++)
        {
                /* if we find an wildcard within the name of the searchstring all remaining character after the wildcard shall be wildcards also */
                if(findelement->searchstring[index] == '*')
                {
                        /*  */
                        while(++index < 11) findelement->searchstring[index] = '*';
                }
        }

        /* the value of ...DirectoryIndex will be incremented in findnext_() thererfore it has to be decremented in findfirst_() */
        findelement->fp.DirectoryIndex--;
        /* now lets search for the item within the direcory */
        itemfound = findnext_(findelement);

        return(itemfound);
}

/********************************************************************************************************************************************/
/* Function:    s8 *GetSubDirectory (s8 *dirpath, s8 *directory)                                                                                                                                                        */
/*                                                                                                                                                                                                                                                                                      */
/* Description: this function parses the dirpath string and copies the first directroy name to the location pointed to by directroy                     */
/*                                                                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/* returnvalue:  a pointer to the beginning of the next subdirectory or NULL                                                                                                                            */
/********************************************************************************************************************************************/
s8 * GetSubDirectory(s8 *dirpath, s8 *directory)
{
   s8 *cptr = dirpath;
   s8 *dptr = directory;
   s8 *remainingpath = NULL;

   /* if the first character of the path is an '/' we go to the next character */
   if(*cptr == '/') cptr++;
   /* search end of path or subdirectory*/
   while((*cptr != 0) && (*cptr != '/'))
   {
      *dptr = *cptr;   // copy to directory string buffer
       dptr++;
       cptr++;
   }
   if(*cptr!=0) remainingpath = ++cptr; // return pointer to remaining path string
   *dptr = 0; // terminate directory name string

   return(remainingpath);
}


/********************************************************************************************************************************************/
/*      Function:               u8 chdir_(s8* const path);                                                                                                                                                                                              */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function changes the current working directory to the directory specified by the filepath                                                  */
/*                  by function findfirst()                                                                                                                                                                                                     */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:                                                                                                                                                                                                                                                    */
/********************************************************************************************************************************************/

u8 chdir_(s8* const path)
{
        u8              retvalue = 0;                                   // the value returned by this function
        u32     ultemp = 0;                                             // temp. variable
        s8      *dirpath = path;                                // pointer to the remaining path string
        s8      dirname[64];                                    // a buffer containing the name of the subdirectory we are actually looking for
        Find_t  fe;                                                             // The findelement needed for function findfirst to find the subdirectory entry
        s8              tp[256];                                                // temporarily we remember the actual path until the operation has finished successfully
        u32     tcwd = 0;                                               // temporarily we remember the actual current working directory

        if(path == NULL) return(0);                             // return immediately of no path is given
        // correct filepath formatting
        Slashing_Path(path);

        // lets remember the actual path and correspondig directory sector
        strcpy(tp, Partition.PathToCwd);
        tcwd = Partition.CurrentWorkingDirectory;

        if(path[0] == '/') // if the path is absolute we begin at the rootdirectory
        {
                strcpy(Partition.PathToCwd, "/");
                Partition.CurrentWorkingDirectory = Partition.FirstRootDirSector;
        }
        // parse through all the subdirectories within the path
        do
        {
                dirpath = GetSubDirectory(dirpath, dirname); // get the name of the subdirectory we are looking for and the remainig path
                if(dirname[0] == 0)             // if no directory name is available
                {
                        retvalue = 1;                   // we are done
                        break;                                  // break the loop
                }      
                // search for the next subdirectory within the path
                if(findfirst_(dirname, ATTR_SUBDIRECTORY, &fe))
                {
                        // we try to change into the directory "..". Now we have to delete the last direntry from the path
                        if(strcmp(dirname, "..") == 0) CWDPath_Pop();
                        // we try to change into the actual directory so there's nothing todo
                        else if(dirname[0] == '.') return(1);
                        // otherwise we append the name of the directory we are changing in to the path
                        else CWDPath_Push(dirname);
                        // The startcluster within an directory entry specifies the position within the fat where the file or directory starts
                        ultemp = (u32) fe.fp.FirstSectorOfFirstCluster;
                        // do we have to change into the rootdirectory?
                        if(ultemp)
                        {
                                // the first 2 entries are reserved for '.' and '..'
                                ultemp -= 2;
                                // now we have to transform the position within the fat into the corrosponding sectoraddress relative to the beginning of the datasection of the active partition
                                ultemp *= Partition.SectorsPerCluster;
                                // at least we make the sectoraddress absolute by adding the relative address to the beginning of the datasection of the active partition
                                ultemp += Partition.FirstDataSector;
                                // the cwd now points to the specified directory
                                Partition.CurrentWorkingDirectory = ultemp;
                        }
                        else // (=0) root directory
                        {
                                // the cwd now points to the rootdirectory
                                Partition.CurrentWorkingDirectory = Partition.FirstRootDirSector;
                        }
                        retvalue = 1;
                }
                else // subdirectory not found
                {
                        retvalue = 0;           // no success tracing along the given directory chain
                        break;                          // break the loop
                }
        }
        while(dirpath != NULL);  // do this until all subdirectories have been found

        // if we could not change to the specified directory we restore the actual path
        if(!retvalue)
        {
                Partition.CurrentWorkingDirectory = tcwd;
                strcpy(Partition.PathToCwd, tp);
        }
        return(retvalue);
}


/********************************************************************************************************************************************/
/*      Function:               u8 rmdir_(s8* dname);                                                                                                                                                                                                           */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function deletes the directory with the specified filename from the filesystem                                                                             */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    1 : specified directory deleted succesfully 0: specified directory not found                                                                                    */
/********************************************************************************************************************************************/

u8 rmdir_(s8* const dirpath)
{
        u8 retvalue = 0;
        Find_t fe;
        u32 cwd;
        s8 path[256];
        s8 *dn;
        s8 *dp;

        if(dirpath == NULL) return(0);
        // correct filepath formatting
        Slashing_Path(dirpath);

    // remember the current workingdirectory
        cwd = Partition.CurrentWorkingDirectory;

        // create local copy of the path
        strcpy(path, dirpath);
        // seperate the filepath from the last directory within the path that shall be deleted
        // start searching for the name of the directory to be deleted from the beginning of the path
        dn = path;
        dp = path;
        // search the complete path until the end of the string is found
        while(*dn != 0) dn++;
        // now check if the last directory whithin the path is terminated with an '/' and remove the '/' from the directoryname
        if(*(dn-1) == '/') *--dn = 0;
        // now find the beginning of the last directoryentry
        while((*dn != '/') && (dn > path)) dn--;
        // beginning of the last directory found
        if(*dn == '/')
        {
                // remove the directoryname from the path by terminating the path with 0
                *dn = 0;
                // the dirname begins 1 character after the terminated path
                dn++;
        }
        // we couldn't find a path before the directory to be deleted or no directoryname at all
        else
        {
                // there is no path before the name of the directory to be deleted
                if(dn == path)
                {
                        // if there is a name of a directory to be deleted?
                        if(*dn == 0) return(0);
                        // the directory to be deleted is within the actual directory therefore we don't have to change the directory before deleting the directory
                        dp = NULL;
                }

        }
        // switch to the directory containing the directory to be deleted if neccessary
        if(dp != NULL)
        {
                if(!chdir_(dp)) return(0);
        }
        // now we have to find the specified directory to be deleted
        retvalue = findfirst_(dn, 0xff, &fe);
        // was the specified directory found?
        if(retvalue)
        {
                // before we delete the directory we check if the directory is empty
                if(chdir_(dn))
                {
                        if(findfirst_("*.*", 0xff, &fe))
                        {
                                // within an directory that shall be deleted only the entries . and .. are allowed
                                if((strncmp(fe.name, ".", 1) != 0) && (strncmp(fe.name, "..", 2) != 0)) retvalue = 0;
                                while(findnext_(&fe))
                                {
                                        if((strncmp(fe.name, ".", 1) != 0) && (strncmp(fe.name, "..", 2) != 0)) retvalue = 0;
                                }
                        }
                        chdir_("..");
                }
                // directory couldn't be entered, is a real file?
                else
                {
                        retvalue = 0;
                }
                // specified directory is empty
                if(retvalue)
                {
                        retvalue = 0;
                        // we have modified the result of the findfirst_ operation therefore we have to redo findfirst_ to get detailed infos about the directory entry to be deleted
                        if(findfirst_(dn, 0xff, &fe))
                        {
                                Debug("found");
                                // try to clear the allocated clusters within the fat
                                if(DeleteClusterChain(fe.fp.FirstSectorOfFirstCluster))
                                {
                                        // delete the directoryentry within the specified directory
                                        if(DeleteDirectoryEntry(&(fe.fp))) retvalue = 1;
                                }
                        }
                }
        }

        // restore the actual path
        Partition.CurrentWorkingDirectory = cwd;

        return(retvalue);
}


/********************************************************************************************************************************************/
/*      Function:               mkdir_(s8* dirpath);                                                                                                                                                                                                    */
/*                                                                                                                                                                                                                                                                                      */
/*      Description:    This function creates the directory with the specified pathname in the filesystem                                                                               */
/*                                                                                                                                                                                                                                      */
/*                                                                                                                                                                                                                                                                                      */
/*      Returnvalue:    1 : specified directory created succesfully 0: specified dir exist already                                                                                              */
/********************************************************************************************************************************************/

u8 mkdir_(s8* const dirpath)
{
        u8 retvalue = 0;
        File_t file;

        if(dirpath == NULL) return(0);
        // correct filepath formatting
        Slashing_Path(dirpath);
        file.FirstSectorOfFirstCluster = SECTOR_UNDEFINED;              // First sector of the first cluster of the file.
        file.FirstSectorOfCurrCluster = SECTOR_UNDEFINED;               // First sector of the cluster which is edited at the moment.
        file.FirstSectorOfLastCluster = SECTOR_UNDEFINED;               // First sector of the last cluster of the file
        file.SectorOfCurrCluster = SECTOR_UNDEFINED;                    // The sector within the current cluster.
        file.ByteOfCurrSector = 0;                                                              // The byte location within the current sector.
        file.Mode = 0;                                                                                  // Mode of fileoperation (read,write)
        file.Size = 0;                                                                                  // The size of the opend file in bytes.
        file.Position = 0;                                                                              // Pointer to a character within the file 0 < fileposition < filesize
        file.DirectorySector = SECTOR_UNDEFINED;                                // the sectorposition where the directoryentry has been made.
        file.DirectoryIndex = 0;                                                                // The index to the directoryentry within the specified sector.
        file.Attribute = 0;                                                                             // The attribute of the file opened.
        file.SectorInCache = SECTOR_UNDEFINED;                                  // The last sector read, which is still in the sector cache.
        file.State = FSTATE_USED;                                                               // mark it as used
        // if directory is not existent
        if(!FileExist(dirpath, ATTR_SUBDIRECTORY, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, &file))
        {
                retvalue = FileCreate(dirpath, ATTR_SUBDIRECTORY, &file);
        }
        return(retvalue);
}