dev-cpp (dbffile) object übersetzen
-
Hallo erstmal!,
ich bekomme es nicht hin diese object für dev-cpp zu übersetzen,
ich habe devc++ wirklich ganz doll lieb aber wir führen eine schwierige beziehung.
ich glaube aber es liegt an den pointern und void`s
könnte mir jemand helfen?
thx ./****************************************************************************/ /********* CDBFile, a dBase III / .DBF file handling object ***********/ /****************************************************************************/ /* Copyright (C) 1997 Hervé GOURMELON, ENSSAT gourmelon@enssat.fr http://www.enssat.fr This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library (COPYING2.TXT); if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ // If you are building a Windoze MFC app under Visual C++, remove the comment // slashes in front of the following line: //#include "stdafx.h" // and turn the following lines into comments: #include <string.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include "cdbfile.h" /* Implementation of the member functions for CField */ // Default constructor CField::CField() { Name[0]='\0'; Type='C'; Length=0; DecCount=0; FieldNumber=0; Offset=0; Next=this; } // Constructor with an initialization of most data members CField::CField(char* NName,char NType,unsigned char NLength, unsigned char NDecCount,unsigned char FieldNum) { strcpy(Name, NName); Type=NType; Length=NLength; DecCount=NDecCount; FieldNumber=FieldNum; } // Destructor CField::~CField() {} unsigned short CField::GetNumberOfFields(CField* Start, unsigned short i) { if (Start==NULL) { Start=this; // NULL is passed for the initial call of i=1; return Next->GetNumberOfFields(Start, i); } // this recursive function in the program else if (Start==this) return i; // We've checked all the elements of else // the dynamic ring: return i */ { i+=1; return Next->GetNumberOfFields(Start, i); /* Proceed to next element */ } } /* Implementation of the member functions of CDBFile */ /* public : */ // Default constructor CDBFile::CDBFile() // Classical init procedure { PathName[0]=0; FileHandle=NULL; HeaderSize=0; FieldCount=0; RecordCount=0; ModifiedFlag=FALSE; FullFileInMemory=FALSE; RecordLength=0; RecordList=NULL; FirstField=NULL; } CDBFile::CDBFile(char* Path) // Creating the object with a path as argument // Automatically opens the file { if (OpenFile(Path)==FALSE) Path[0]=0; } BOOL CDBFile::Clean() // Resets the CDBFile object, regardless of any modifications to be written { CField *Delendum; Record *DelRec; if (FileHandle!=NULL) fclose(FileHandle); // Closes the file by default // Resets all the member data PathName[0]=0; FileHandle=NULL; HeaderSize=0; FieldCount=0; RecordCount=0; ModifiedFlag=FALSE; FullFileInMemory=FALSE; RecordLength=0; if (FirstField!=NULL) // Delete the dynamic list of fields { while (FirstField->GetNext()!=FirstField) { Delendum=FirstField->GetNext(); FirstField->SetNext(Delendum->GetNext()); delete Delendum; } delete FirstField; FirstField=NULL; } if (RecordList!=NULL) // Delete the dynamic list of records { while (RecordList->Next!=NULL) { DelRec=RecordList->Next; RecordList->Next=DelRec->Next; delete[] DelRec->Contents; delete DelRec; } delete[] RecordList->Contents; delete RecordList; RecordList=NULL; } return TRUE; } CDBFile::~CDBFile() // Default destructor { Clean(); } void CDBFile::ClearAllRecords() // Cleaning the contents before rewriting the file (without changing the // data structure) { Record* DelRec; if (FileHandle==NULL) return; RecordCount=0; if (RecordList!=NULL) // Delete the dynamic list of records { while (RecordList->Next!=NULL) { DelRec=RecordList->Next; RecordList->Next=DelRec->Next; delete[] DelRec->Contents; delete DelRec; } delete[] RecordList->Contents; delete RecordList; RecordList=NULL; } } BOOL CDBFile::OpenFile(char* Path) { unsigned char dBaseVersion,NLength,NDecCount; char NType, NName[11]; unsigned short Offst; CField *Tail, *Current; // Please refer to the dBase III file structure to debug // this procedure Clean(); // Closes the previous active file and resets the object if (strlen(Path)>256) { fprintf (stderr, "File path too long : %s \n", Path); return FALSE; } strcpy (PathName, Path); FileHandle = fopen (PathName, "r+"); if (FileHandle==NULL) return FALSE; // error opening the file fseek (FileHandle, 0L, SEEK_SET); fread (&dBaseVersion, 1, sizeof (char), FileHandle); if (dBaseVersion != 3) // typical of the dBase III files; ignores the // case of a DBF file with an associated DBT file { fprintf (stderr, "%s is not a dBASE III file!\n", Path); return FALSE; } // Reading the header: // I ignored the date of last udate. If anybody finds it necessary, just // add it to the member data and read it here... fseek (FileHandle, 4L, SEEK_SET); fread (&RecordCount, sizeof (RecordCount), 1, FileHandle); fseek (FileHandle, 8L, SEEK_SET); fread (&HeaderSize, sizeof (HeaderSize), 1, FileHandle); FieldCount = HeaderSize / 32 - 1; fread (&RecordLength, sizeof (RecordLength), 1, FileHandle); Offst=1; // Data records are preceded by one byte that is 0x20 (space) // Reading the field descriptor arrays: for (unsigned char i=1; i<=FieldCount; i++) { fseek (FileHandle, (long) 32 * i, SEEK_SET); fread (NName, 11, sizeof (char), FileHandle); fread (&NType, 1, sizeof (char), FileHandle); fseek (FileHandle, (long)(32*i+16), SEEK_SET); fread (&NLength, 1, sizeof (unsigned char), FileHandle); fread (&NDecCount, 1, sizeof (unsigned char), FileHandle); Current=new CField(NName,NType,NLength,NDecCount,i); if (FirstField==NULL) // Creating the list of CFields { FirstField=Current; Current->SetNext(FirstField); Tail=FirstField; } else { Tail->SetNext(Current); Tail=Current; Current->SetNext(FirstField); }// end if Current->SetOffset(Offst); Offst+=NLength; }// end for return TRUE; } BOOL CDBFile::CloseFile() // If anybody finds any use to it... { return Clean(); } unsigned long CDBFile::LoadFileToMemory() // Loads all the records to memory. Necessary when you have to sort them or to // delete some of them from the file, or to save it as another file. { unsigned long i; Record *Current; // If the file is closed or the records are already loaded, return. if ((FileHandle==NULL)||(FullFileInMemory==TRUE)) return FALSE; else { for (i=1;i<=RecordCount;i++) { Current=ReadRecord(i); Append(Current); // This will overwrite the records with the // same numbers, unless their ModifFlag is set to TRUE // (see the Append function) if (Current==NULL) return i-1; } FullFileInMemory=TRUE; return i; } } unsigned long CDBFile::WriteAllToFile(char* Path) // Saves the file as "Path". If "Path"==NULL, the current file is overwritten. // Returns the number of records actually written. { unsigned long i; Record *CurRec; // If the file is closed or not all the records are loaded, return. if ((FileHandle==NULL)||(FullFileInMemory==FALSE)) return 0L; else { if (WriteHeader(Path)==TRUE) // Writes the header of the file. { CurRec = RecordList; for (i=1;i<=RecordCount;i++) { if (WriteRecord(CurRec, i)) { if (CurRec->Next==NULL) return i-1; else CurRec=CurRec->Next; } else return i-1; }// end for return i; }//end if WriteHeader else return 0L; }//end if }//end unsigned long CDBFile::WriteModified() { unsigned long i=0; Record* CurRec; if ((FileHandle==NULL)||(FullFileInMemory==TRUE)) return FALSE; else { CurRec=RecordList; do { if(CurRec->ModifFlag==TRUE) { if (WriteRecord(CurRec, CurRec->RecordNumber)) i++; else return i; } CurRec=CurRec->Next; }while(CurRec!=NULL); return i; } } void CDBFile::SortAllRecords(Record *Head, Record *Tail, CField* Criter1 /*, CField* Criter2*/) // Warning : "Tail" must not be the last record in the list. There must be // an empty cell following Tail in the list, which should be non-NULL. // The function below is an implementation of the quick sort algorithm. { Record *i, *j, *s; void *v1, *vj /*, *v2*/; if ((Head->RecordNumber)<(Tail->RecordNumber)) { v1=GetFieldValue(Head, Criter1); i=Head; j=Head->Next; s=Tail->Next; while ((j->RecordNumber)<(s->RecordNumber)) { // Sorting on primary criterium vj = GetFieldValue(j,Criter1); if (IsSmaller(vj, v1, Criter1)) { Swap (i,j); i=i->Next; j=j->Next; } else if (IsBigger(vj, v1, Criter1)) { Swap(j, s->Previous); s=s->Previous; } /* attempt to sort on a secundary criterium. Unfortunately, this method failed. Any idea ? else if (Criter2!=NULL) { v2=GetFieldValue(Head, Criter2); if (IsSmaller(GetFieldValue(j,Criter2), v2, Criter2)) { Swap (i,j); i=i->Next; j=j->Next; } else if (IsBigger(GetFieldValue(j,Criter2), v2, Criter2)) { Swap(j, s->Previous); s=s->Previous; } else j=j->Next; } */ else j=j->Next; DeleteVoidPointer(vj, Criter1); DeleteVoidPointer(v1, Criter1); } SortAllRecords(Head, i->Previous, Criter1/*, Criter2*/); SortAllRecords(s, Tail, Criter1/*, Criter2*/); }//end if } void CDBFile::SortOn(unsigned short Criterium1/* , unsigned short Criterium2*/) // Calls SortAllRecords(); the criterium is specified by the field number { Record *Head, *Tail, *CurRec; CField *Crit1 /*, *Crit2*/; /* Crit2 : optional secundary field */ if (FullFileInMemory!=TRUE) return; CurRec=RecordList; while (CurRec->Next!=NULL) CurRec=CurRec->Next; // Two additional records have to be appended temporarily to the list, // in order to execute the quick sort correctly. Tail=new Record; Head=new Record; Head->Next=RecordList; Tail->Previous=CurRec; RecordList->Previous=Head; CurRec->Next=Tail; Head->Previous=NULL; Tail->Next=NULL; Head->Contents=NULL; Tail->Contents=NULL; Head->RecordNumber=0L; Tail->RecordNumber=RecordCount+1; Crit1=FirstField->GetField(Criterium1); /*Crit2=FirstField->GetField(Criterium2);*/ SortAllRecords(RecordList, CurRec, Crit1/*, Crit2*/); RecordList->Previous=NULL; CurRec->Next=NULL; delete Head; delete Tail; } void* CDBFile::GetFieldValue(Record* Rec, CField* Field) // Returns a pointer to the value of the field *Field in the record *Rec. // Since we do not know the type of the value, we have to return a "void*" // pointer, which has to be deleted by the recipient of the pointer. // See also : SetFieldValue(), DeleteVoidPointer(). { char* Data; char* RecContents; RecContents = &(Rec->Contents[Field->GetOffset()]); // RecContents points at the beginning of the field within the record. Data=new char[Field->GetLength()+1]; // Data is allocated dynamically strncpy(Data, RecContents, Field->GetLength()); // Data now contains the string that has to be converted. switch(Field->GetType()) { case 'N': // The field is of numeric type, either : if (Field->GetDecCount()==0) // an integer (no decimals) { long *Result1; Result1=new long; *Result1=atol(Data); delete []Data; return (void*)Result1; } else // a double { double *Result1; Result1=new double; *Result1=atof(Data); delete []Data; return (void*)Result1; } case 'D': // I won't do any specific conversion for dates // Please feel free to do it if you feel like. case 'C': // Here, we add a Clipper / FoxPro notion: the field decimal // count is considered as an additional field length number. char* Result2; Result2=strncpy(Data, RecContents, Field->GetLength()+256*Field->GetDecCount()+1); Result2[Field->GetLength()+256*Field->GetDecCount()]=0; return (void*)Result2; case 'L': // Here I decided that Logical values should be converted to // booleans. I could have kept it as a single character. char c; BOOL* Result3; Result3=new BOOL; sscanf(Data, "%c", c); *Result3=(BOOL)((c=='Y')||(c=='y')||(c=='T')||(c=='t')); delete []Data; return (void*)Result3; case 'M': default: return NULL; }// end switch; } // Overloaded, public versions of GetFieldValue. : void* CDBFile::GetFieldValue(char* Field) {return GetFieldValue(CurrentRec,FirstField->GetField(Field));} void* CDBFile::GetFieldValue(unsigned short FieldNum) {return GetFieldValue(CurrentRec,FirstField->GetField(FieldNum));} void CDBFile::DeleteVoidPointer(void* Pointer, CField* Field) // This function should be used to delete the void pointers allocated and // returned by GetFieldValue(). It detects the type of the pointers. { switch (Field->GetType()) { case 'N' : case 'L' : delete Pointer; break; case 'D' : case 'C' : default : delete []Pointer; } } // Public overloaded versions of DeleteVoidPointer() void CDBFile::DeleteVoidPointer(void* Pointer, unsigned short Field) {DeleteVoidPointer(Pointer,FirstField->GetField(Field));} void CDBFile::DeleteVoidPointer(void* Pointer, char* Field) {DeleteVoidPointer(Pointer,FirstField->GetField(Field));} Record* CDBFile::GetRecord(unsigned long Number) // This function returns a record identified by its number. This is for // records that are already loaded in memory. { Record* Current; Current=RecordList; if (RecordList==NULL) return NULL; else { if (Current->RecordNumber==Number) return Current; else { while (Current->Next!=NULL) { Current=Current->Next; if (Current->RecordNumber==Number) return Current; } // The matching record was not found in memory : it // must be on the disk... Return the first record. return RecordList; } }//end "if (RecordList==NULL)" } Record* CDBFile::CreateNewRecord() // When an additional record is created, the number of records in the files is // incremented, the file is modified. { Record* Current; Current=new Record; Current->Next=NULL; Current->Previous=NULL; Current->Contents=new char[RecordLength]; for (int i=0; i<RecordLength; i++) Current->Contents[i]=0x20; Current->ModifFlag=FALSE; Current->RecordNumber=RecordCount+1; RecordCount++; ModifiedFlag=TRUE; return Current; } void CDBFile::Append(Record* Rec, Record* Tail) // Inserts Rec after Tail in the list if Tail!=NULL; inserts it in order // of RecordNumber if Tail==NULL (which is the default) and if a record with // the same number is not already present in the list. In that case, the // contents of the former record is replaced by Rec->Contents, unless the // former record has been modified and is not saved yet. { Record* Current; if (Tail==NULL) // by default... { Current=RecordList; if (RecordList==NULL) // The list is empty { RecordList=Rec; // Create a new list return; } while(Current!=NULL) { if (Current->RecordNumber>Rec->RecordNumber) // This is the right place to insert Rec { Rec->Next=Current; if (Current->Previous!=NULL) (Current->Previous)->Next=Rec; else RecordList=Rec; Rec->Previous=Current->Previous; Current->Previous=Rec; ModifiedFlag=TRUE; return; } else if (Current->RecordNumber==Rec->RecordNumber) { if(Current->ModifFlag==TRUE) // Current has been modified, but not saved yet { delete Rec->Contents; delete Rec; return; } else // Current can be replaced by Rec without losing modifications { delete Current->Contents; Current->Contents=Rec->Contents; Current->ModifFlag=Rec->ModifFlag; delete Rec; } // The file has been modified (by adding this record:) ModifiedFlag=TRUE; return; } else if (Current->Next==NULL) // This is the right place to append Rec { Rec->Previous=Current; Current->Next=Rec; Rec->Next=NULL; return; }//end if Current=Current->Next; }// end while }// end if else { // Rec is to be inserted after Tail Current=Tail; Rec->Next=Current->Next; Rec->Previous=Current; if (Current->Next!=NULL) (Current->Next)->Previous=Rec; Current->Next=Rec; } }//end Append void CDBFile::DeleteRecord(Record* Rec) { // This function really deletes a record if all the records are loaded // in memory and if the whole data is saved to a file (current file // or a new one). If only a few records are loaded, it will delete the // pointed record from the memory, discarding any changes. if (Rec==NULL) return; if (Rec==RecordList) { RecordList=RecordList->Next; if (RecordList) RecordList->Previous=NULL; delete(Rec); if (FullFileInMemory==TRUE) RecordCount--; ModifiedFlag=TRUE; return; } else { (Rec->Previous)->Next=Rec->Next; if (Rec->Next) (Rec->Next)->Previous=Rec->Previous; delete(Rec); ModifiedFlag=TRUE; if (FullFileInMemory==TRUE) RecordCount--; } } void CDBFile::AddField(CField* NewField); // Not implemented in this version void CDBFile::SetFieldValue(Record* Rec, CField* Field, void* Value) // Here we have the same problems as in GetFieldValue(), see above. { char* Data; char* RecContents; unsigned short FLength; unsigned short ResLength; if (Field->GetType()=='C') FLength=Field->GetLength()+256*Field->GetDecCount(); else FLength=Field->GetLength(); RecContents = &(Rec->Contents[Field->GetOffset()]); // RecContents points at the beginning of the field within the record. Data=new char[FLength+1]; // Data is allocated dynamically for (int i=0; i<FLength; i++) Data[i]=' '; // Data is blanked Data[FLength]=0; // Last character is set to null, '\0' (end of string) switch(Field->GetType()) { case 'N': // Numeric if (Field->GetDecCount()==0) // Integer (long) { char *Num=new char[FLength+1]; long *Result1; Result1=(long *)Value; ltoa(*Result1, Num, 10); ResLength=strlen(Num); // Text alignment for numbers is flush-right : strncpy(&Data[FLength-ResLength], Num, ResLength); strncpy(RecContents, Data, FLength); delete []Num; break; } else // Float (double) { char *Num=new char[FLength+1]; double *Result1; Result1=(double *)Value; gcvt(*Result1, FLength,Num); ResLength=strlen(Num); // Text alignment for numbers is flush-right : strncpy(&Data[FLength-ResLength], Num, ResLength); strncpy(RecContents, Data, FLength); delete []Num; break; } case 'D': // Date : no specific conversion (for portability) case 'C': // Character : ResLength=strlen((char *)Value); strncpy(Data, (char *)Value, ResLength); strncpy(RecContents, Data, FLength); break; case 'L': // Logical : conversion from 'BOOL' type BOOL* Result3; Result3=(BOOL *)Value; if (*Result3==TRUE) Data[0]='Y'; else Data[0]='N'; strncpy(RecContents, Data, FLength); break; case 'M': default: break; }// end switch; delete []Data; Rec->ModifFlag=TRUE; } // Overloaded, public versions of SetFieldValue. : void CDBFile::SetFieldValue(char* Field, void* Value) { SetFieldValue(CurrentRec,FirstField->GetField(Field), Value);} void CDBFile::SetFieldValue(unsigned short FieldNum, void* Value) { SetFieldValue(CurrentRec,FirstField->GetField(FieldNum), Value);} void CDBFile::DumpCurrentContents(int i, char* String) // Dumps the contents of the current record (raw ascii, no formatting) { if (CurrentRec!=NULL) { strncpy(String, CurrentRec->Contents, i); String[i]=0; } else String[0]=0; } Record* CDBFile::ReadRecord(unsigned long RecNum) // Reads a record from the file, returns a newly allocated *Record pointer // The file offset is determined by RecNum and RecordLength. { unsigned char Res; char* Buffer; Record *NewRec; if ((FileHandle==NULL)||(RecNum>RecordCount)||(RecNum<1)) return NULL; else { Buffer=new char[RecordLength]; fseek (FileHandle, (long)(HeaderSize + (RecNum-1)*RecordLength), SEEK_SET); Res=fread (Buffer, RecordLength, 1, FileHandle); if (Res==0) return NULL; NewRec=new Record; NewRec->Contents=Buffer; NewRec->ModifFlag=FALSE; NewRec->RecordNumber=RecNum; NewRec->Next=NULL; NewRec->Previous=NULL; return NewRec; } } BOOL CDBFile::WriteHeader(char* Path) // Use that function to rewrite or to save the current file under another // name. It writes the header of the current CDBFile. In order to update the // "date of last update" field, I introduced two structures : time_t Date, and // tm *Date_s, which will have to be changed for a port under UNIX. For more // details, have a look at OpenFile() above and the dBase III file structure. { unsigned char NLength,NDecCount; char NType; char Written; CField *Current; char dBaseVersion=0x03; char FieldTerm=0x0D; time_t Date; struct tm *Date_s; char Zero[256]; char Day, Year, Month; // Initialize a zero buffer; for (unsigned short j=0; j<256; j++) Zero[j]=0x00; // Validity of the path name if (Path!=NULL) { if (strlen(Path)>256) { fprintf (stderr, "File path too long : %s \n", Path); return FALSE; } strcpy (PathName, Path); } // Close the currently open file if (FileHandle!=NULL) fclose(FileHandle); FileHandle = fopen (PathName, "w"); if (FileHandle==NULL) return FALSE; fseek (FileHandle, 0L, SEEK_SET); Written=fwrite (&dBaseVersion, 1, sizeof (char), FileHandle); time(&Date); Date_s = gmtime(&Date); Year=(char)(Date_s->tm_year); Month=(char)(Date_s->tm_mon); Day=(char)(Date_s->tm_mday); fseek (FileHandle, 1L, SEEK_SET); Written=fwrite (&Year, sizeof (char), 1, FileHandle); Written=fwrite (&Month, sizeof (char), 1, FileHandle); Written=fwrite (&Day, sizeof (char), 1, FileHandle); fseek (FileHandle, 4L, SEEK_SET); Written=fwrite (&RecordCount, sizeof (RecordCount), 1, FileHandle); fseek (FileHandle, 8L, SEEK_SET); Written=fwrite (&HeaderSize, sizeof (HeaderSize), 1, FileHandle); Written=fwrite (&RecordLength, sizeof (RecordLength), 1, FileHandle); Written=fwrite (&Zero, sizeof(char), 20, FileHandle); for (unsigned short i=1; i<=FieldCount; i++) { Current=FirstField->GetField(i); NLength=Current->GetLength(); NType=Current->GetType(); NDecCount=Current->GetDecCount(); fwrite (Current->GetName(), 11, sizeof (char), FileHandle); fwrite (&NType, sizeof (char), 1, FileHandle); fwrite (&Zero, sizeof(char), 4, FileHandle); fwrite (&NLength, sizeof (unsigned char), 1, FileHandle); fwrite (&NDecCount, sizeof (unsigned char), 1, FileHandle); fwrite (&Zero, sizeof(char), 14, FileHandle); }// end for fwrite (&FieldTerm, sizeof (char), 1, FileHandle); return TRUE; } BOOL CDBFile::WriteRecord(Record* Current, unsigned long RecNum) // Writes a record at the specified place,using RecNum to calculate the offset { size_t Res; if ((FileHandle==NULL)||(RecNum>RecordCount)||(RecNum<1)) return FALSE; else { fseek(FileHandle,(long)(HeaderSize+(RecNum-1)*RecordLength),SEEK_SET); Res=fwrite (Current->Contents, RecordLength, 1, FileHandle); if (Res==1) { Current->ModifFlag=FALSE; // now that it has been written to file, it's no longer modified. return TRUE; } else return FALSE; } } BOOL CDBFile::Swap(Record* Rec1, Record* Rec2) // swaps the contents of Rec1 and Rec2 (used in the SortAll function) { char* BufCont; BOOL BufFlag; BufCont=Rec1->Contents; BufFlag=Rec1->ModifFlag; Rec1->Contents=Rec2->Contents; Rec1->ModifFlag=Rec2->ModifFlag; Rec2->Contents=BufCont; Rec2->ModifFlag=BufFlag; return TRUE; } BOOL CDBFile::IsBigger(void *v1, void *v2, CField* Criterium) // Compares v1 and v2 using Criterium to determine their type. { switch(Criterium->GetType()) { case 'N': // Numeric double *val1, *val2; val1=(double*)v1; val2=(double*)v2; return(BOOL)(*val1>*val2); case 'C': default : // Caracter char *str1, *str2; str1=(char*)v1; str2=(char*)v2; return(BOOL)(strcmp(str1, str2)>0); } } BOOL CDBFile::IsSmaller(void *v1, void *v2, CField* Criterium) // Compares v1 and v2 using Criterium to determine their type. { switch(Criterium->GetType()) { case 'N': // Numeric double *val1, *val2; val1=(double*)v1; val2=(double*)v2; return(BOOL)(*val1<*val2); case 'C': default : // Caracter char *str1, *str2; str1=(char*)v1; str2=(char*)v2; return(BOOL)(strcmp(str2, str1)>0); } }