Files
harbour-core/harbour/contrib/htmllib/oini.prg
2001-12-25 16:51:58 +00:00

508 lines
12 KiB
Plaintext

/*
* $Id$
*/
/*
* Harbour Project source code:
* Ini Class
*
* Copyright 2000 Manos Aspradakis <maspr@otenet.gr>
* www - http://www.harbour-project.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version, with one exception:
*
* The exception is that if you link the Harbour Runtime Library (HRL)
* and/or the Harbour Virtual Machine (HVM) with other files to produce
* an executable, this does not by itself cause the resulting executable
* to be covered by the GNU General Public License. Your use of that
* executable is in no way restricted on account of linking the HRL
* and/or HVM code into it.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
* their web site at http://www.gnu.org/).
*
*/
/*
* The following parts are Copyright of the individual authors.
* www - http://www.harbour-project.org
*
* Copyright 2000 Luiz Rafael Culik <culik@sl.conex.net>
* Porting this library to Harbour
*
* See doc/license.txt for licensing terms.
*
*/
/*
**
** oIni.prg
** --------
**
** This implementation of an .ini file reader presents a different
** approach to the problem as it creates a two dimensional array to
** store and analyse data.
**
** The first dimension keeps all sections and the second dimension
** their entries. This way we avoid misinterpretation of entries (an
** entry could be mistaken for section and vice versa) and keep
** a clearer view of the whole structure in memory.
**
** Entries are separated in entry tags, data and comments, e.g.
**
** TestTag="Data" ; comment
**
** Header comments are supported only if preceded with "/*", "*" or "/*"
** and only at the beginning of the .ini file before any sections.
**
**
** Organisation of the file in memory (two dimensional array):
** -----------------------------------------------------------
**
** [section]
** entry=data
** entry=data
** ...
** [section]
** ...
**
**
** organisation of the file on disk:
** ---------------------------------
** /*
** ** Header
** */
**
** [section]
** entry=data ; comment
** entry=data
** [section]
** ...
** ; comment
**
**
*/
#include "hbclass.ch"
#include "default.ch"
#ifdef TEST
PROC TestMe()
LOCAL o := oINI():New( "Test.ini" )
//o:Open()
o:Read()
objectViewer( o )
? "handle",o:handle
? o:Get( "methods", "method45", 45 )
? o:Get( "methods", "method455", .t. )
? o:Put( "methods", "method8", .f. )
? o:Get( "Test", "Test1", 1234567890 )
o:DumpSections()
? "handle",o:handle
o:Save(";"+CRLF()+;
";TEST.INI"+CRLF()+;
";Created on "+DTOC(DATE())+" - " + TIME() +CRLF()+;
";"+CRLF())
o:close()
RETURN
#endif
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
CLASS oIni FROM FileBase
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
DATA aLines INIT {}
DATA aSections INIT {}
DATA lineSize INIT 200
METHOD New( cFile )
METHOD Read()
METHOD Get( cSection, cEntry, uDefault )
METHOD Put( cSection, cEntry, uValue )
METHOD Save(c)
METHOD DumpSections()
ENDCLASS
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
METHOD New( cFile ) CLASS oIni
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Super():New( cFile )
::Name := cFile
::aLines := {}
::aSections := {}
::handle := -1
RETURN Self
/*
** ::Read()
**
** Reads the .ini file in memory, separating [sections] from entries
**
*/
METHOD Read() CLASS oIni
LOCAL nSection := 0
//? "ohandle",::handle
IF ::handle == NIL .OR. ::Handle <= 0
IF ::Open()
::aLines := {} ; ::aSections := {}
::Size()
::GoTop()
WHILE !::EOF()
::ReadLine( ::lineSize )
// ::aLines keep anything before the first section
// like comments, headers etc.
IF !EMPTY( ::Buffer ) .and. nSection == 0
IF ::Buffer = "/*" .or. ;
::Buffer = "**" .or. ;
::Buffer = "*" .or. ;
::Buffer = "*/"
AADD( ::aLines, ::Buffer )
ENDIF
ENDIF
IF LEFT(ALLTRIM( ::Buffer ),1) == "["
nSection++
AADD( ::aSections, { lower(ALLTRIM(::Buffer)), {} } )
ENDIF
if nSection > 0
if !EMPTY( ::Buffer )
AADD( ::aSections[nSection,2], ::buffer )
ENDIF
ENDIF
IF ::EOF()
EXIT
ENDIF
ENDDO
AEVAL( ::aSections, ;
{|e| ADEL(e[2],1), ASIZE( e[2], LEN( e[2] ) -1 ) } )
ENDIF // ::open()
ENDIF // ::handle
RETURN Self
/*
** ::Get( cSection, cEntry, uDefault )
**
** Retrieves an entry from memory. If it doesn't exist it creates it with
** default data. Also it creates the section if it doesn't exist.
*/
METHOD Get( cSection, cEntry, uDefault ) CLASS oIni
LOCAL cRet, nPos, nSection, nEntry
DEFAULT uDefault := ""
cSection := "["+lower( ALLTRIM(cSection) )+"]"
nSection := ASCAN( ::aSections, {|e| e[1] = cSection } )
IF nSection > 0
nEntry := ASCAN( ::aSections[nSection,2], ;
{|e| UPPER(cEntry) == UPPER( ALLTRIM(LEFT( e, AT("=",e)-1 )) ) })
IF nEntry > 0 // Found entry. Get value. Cast to data type
cRet := STR2ANY( GetEntryData( ::aSections[nSection,2,nEntry] ), ;
uDefault )
ELSE // Entry not found. Insert new entry
AADD( ::aSections[nSection,2], cEntry + "=" + ANY2STR( uDefault ) )
cRet := uDefault
ENDIF
ELSE // insert new section and entry...
AADD( ::aSections, { cSection, ;
{ cEntry + "=" + ANY2STR( uDefault ) } } )
cRet := uDefault
ENDIF
RETURN( cRet )
/*
** ::Put()
**
**
**
*/
METHOD Put( cSection, cEntry, uValue ) CLASS oIni
LOCAL cRet := ""
LOCAL cComment, nPos, nSection, nEntry
DEFAULT uValue := ""
cSection := "["+Lower( ALLTRIM(cSection) )+"]"
nSection := ASCAN( ::aSections, {|e| e[1] = cSection }) //$ e[1] } )
IF nSection > 0
nEntry := ASCAN( ::aSections[nSection,2], ;
{|e| UPPER(cEntry) == UPPER( LEFT( e, AT("=",e)-1 ) ) })
IF nEntry > 0 // Found entry. Get value. Cast to data type
// return old value
cRet := STR2ANY( GetEntryData( ::aSections[nSection,2,nEntry] ), ;
uValue )
cComment := GetEntryComment( ::aSections[nSection,2,nEntry] )
// put new value
::aSections[nSection,2,nEntry] := cEntry + "=" + ;
ANY2STR( uValue ) +;
SPACE(2)+;
cComment
ELSE // Entry not found. Insert new entry.
AADD( ::aSections[nSection,2], cEntry + "=" + ANY2STR( uValue ) )
cRet := uValue
ENDIF
ELSE // Section not found. Insert section and entry.
AADD( ::aSections, { cSection, ;
{ cEntry + "=" + ANY2STR( uValue ) } } )
cRet := uValue
ENDIF
RETURN( cRet )
/*
** ::Save()
**
** Stores the .ini file back to disk from memory. All new changes are
** saved, including comments...
*/
METHOD Save( cComment ) CLASS oIni
LOCAL i, j
DEFAULT cComment := ""
::Close()
// Delete and create file
IF ::Create( ::Name )
::Write( cComment + CRLF() )
AEVAL( ::aLines, {|e| FWrite( ::Handle, e + chr(13)+chr(10) ) } )
::Write( CRLF() )
FOR i=1 to LEN(::aSections)
//alert( ::aSections[i,1] )
::Write( ::aSections[i,1] + CRLF() )
FOR j=1 TO LEN( ::aSections[i,2] )
::Write( ::aSections[i,2,j] +CRLF() )
NEXT
::Write( CRLF() )
NEXT
ENDIF
::Close()
RETURN Self
METHOD DumpSections() cLASS OINI
LOCAL i, j
for i=1 to len(::aSections)
outStd( ::aSections[i,1] + CRLF() )
for j=1 to len( ::aSections[i,2] )
OUTSTD( SPACE(5) + ::aSections[i,2,j] +CRLF() )
next
next
RETURN Self
/*
** stripSection()
**
** Removes "[]" characters from a section entry
*/
STATIC FUNCTION StripSection(cSection)
cSection := LOWER( ALLTRIM( cSection ) )
DO WHILE "[" $ cSection
cSection := STUFF(cSection, AT("[", cSection), 1 )
ENDDO
DO WHILE "]" $ cSection
cSection := STUFF(cSection, AT("]", cSection), 1)
ENDDO
RETURN "["+cSection+"]"
/*
** GetEntryData( cEntry )
**
** Retrieves the data part of an .ini entry. Supports comments to the
** right of the data.
*/
STATIC FUNCTION GetEntryData( cEntry )
LOCAL cRet := ""
LOCAL nPos := 0
LOCAL isComment := ( nPos := AT( ";", cEntry ) ) > 0
IF isComment
cRet := SUBSTR( cEntry, AT( "=", cEntry )+1 , LEN(cEntry)-nPos-1 ) //AT( ";", cEntry )-1 )
ELSE
cRet := SUBSTR( cEntry, AT( "=", cEntry )+1 )
ENDIF
RETURN alltrim( cRet )
/*
** GetEntryComment( cEntry )
**
** Retrieves the comment of an .ini entry.
*/
STATIC FUNCTION GetEntryComment( cEntry )
LOCAL cRet := ""
LOCAL nPos := 0
LOCAL isComment := ( nPos := AT( ";", cEntry ) ) > 0
IF isComment
cRet := SUBSTR( cEntry, AT( ";", cEntry ), LEN( cEntry ) )
ELSE
cRet := ""
ENDIF
RETURN alltrim( cRet )
/*
** ::DumpSections()
**
** Display [sections] and entries from memory ( debug method )
**
*/
/****
*
* UTILITIES
* ---------
*
*/
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
STATIC FUNCTION Any2Str( xVal )
LOCAL cRet := ""
LOCAL cType := VALTYPE( xVal )
DO CASE
CASE cType == "D" ; cRet := DTOC( xVal )
CASE cType == "N" ; cRet := ALLTRIM(STR( xVal ))
CASE cType == "L" ; cRet := IF( xVal == .T., ".T.", ".F." )
CASE cType == "B" ; cRet := "{|| ... }"
CASE cType == "O" ; cRet := xVal:ClassName()
CASE cType == "U" ; cRet := ""
OTHERWISE ; cRet := xVal
ENDCASE
RETURN cRet
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
STATIC FUNCTION Str2Any( cVal, uType )
LOCAL bError
LOCAL uRet := ""
LOCAL cType := VALTYPE( uType )
bError:= ERRORBLOCK( {|o| BREAK(o) } )
cVal := ALLTRIM( cVal )
BEGIN SEQUENCE
DO CASE
CASE cType == "D" ; uRet := CTOD( cVal )
CASE cType == "N" ; uRet := IF( !EMPTY(cVal), VAL( cVal ), 0 )
CASE cType == "L" ; uRet := IF( cVal == ".T.", .T., .F. )
CASE cType == "B" ; uRet := {|| .T. }
CASE cType == "O" ; uRet := NIL
CASE cType == "U" ; uRet := NIL
OTHERWISE ; uRet := cVal
ENDCASE
RECOVER
uRet := cVal
END SEQUENCE
ERRORBLOCK( bError )
RETURN uRet
//ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
STATIC FUNCTION Token(cStr, cDelim, nToken)
LOCAL nPos, cToken, nCounter := 1
DEFAULT nToken := 1
WHILE .T.
IF (nPos := AT(cDelim, cStr)) == 0
IF nCounter == nToken
cToken := cStr
endif
EXIT
ENDIF
IF ++nCounter > nToken
cToken := LEFT(cStr, nPos - 1)
EXIT
ENDIF
cStr := SUBSTR(cStr, nPos + 1)
ENDDO
RETURN cToken