INI file operations VBScript Class 
________________________________

  This is a VBS class that can be pasted into any VBS file to provide
INI functions. Class text without comments follows at the end of this file.
See the sample script for class code with comments.

Overview: 
  From outside the class the functions are straightforward - open an
INI file, do various operations, then close the INI file. The way it
works is to use:
  
Boolean = Cls.OpenINIFile(FilePath)
If the return is true the file is ready for editing.

How this class works:

From inside the class what's actually happening is that when OpenINIFile 
is called the file [FilePath] is read line-by-line as a Textstream and a
Dictionary object [Dic] is created. With each line, if the first character 
is "[", denoting a section header, that line is assigned as a Key in Dic.
Any lines read in before the next "[" will be values in that section, so they're
added to a string that's the Item of the Section Key in Dic, interspersed
with bullet characters [Chr(149)]. A bullet is used to avoid any characters
that might appear in an INI file.
Example:

[Section1]
val1=1
val2=2

   When [Section1] is read it gets added to Dic,  with a bullet character as the item:
           Dic.Add "[Section1]", bullet   

   When val1=1 is read there is no "[" at the start of that line so it's added
to the Key.Item:   
           sList = Dic.Item("[Section1]")
           sList = sList & [new line] & bullet

   The entire INI file is thus saved in Dic as a number of Keys representing
sections with each key Item representing the section values:
  val1=redval2=blueval3=yellow

   The various operations can then be done on Dic. For instance, deleting
a section just requires removing a Dic Key; adding a section means adding
a Dic Key. Changing values involves getting the Dic.Key Item string and
inserting or removing the respective value, then re-saving the string as
the Dic.Key Item.

  A private sub, WriteNewINI, is called anytime changes are made.
WriteNewINI gets the Dic Keys, subsitutes VBCrLf for bullets in the
Key Items, and writes a new INI file.

  Notes:

  1) Each operation will involve deleting and re-writing the INI file.
That's fine for private INI files but may present a problem for system INIs
where System File Protection is active. 
  2) Windows generally keeps INI files in memory. When an INI is updated
directly as a text operation Windows may not recognize the change for
seconds or even minutes.

  If either of those factors is a concern the JSSys3.DLL component can
be used to perform INI operations through standard system functions.

________________________________________________________

ClsINI functions:
________________________________________________________

 Ret = OpenINIFile(FilePath)  [ Boolean - returns True If file opened successfully. ]

'   CloseINIFile() [ Sub to close INI file after use. This sets the dictionary to Nothing.
                           The INI file itself is Not actually open so CloseINIFile is Not strictly
                            required. If another file is opened the dictionary will be Set to Nothing
                            before proceeding. If the Class is Set to Nothing the dictionary 
                             will also be cleared.

                          
'---- Get Section Names: ------------------------------
' Ret = GetSectionNames()
'     ex.: R = Cls.GetSectionNames()
' Return: If no file is open or file has no sections, an array is returned
'             with ubound(0) and the value of array(0) is ""
'           If there are sections in the file the names are returned in an array.
                           
                           
'------ Get INI value:   -------------------------------                        
' Ret = GetINIValue(Section, Key, Value)
 '    ex.: R = Cls.GetINIValue("Section1", "val4", s)
 ' on success Ret returns 0 and s returns value of Key.    
 ' Errors: 1 - no such key. 2 - no such section.  3 - no file open. 4 - unexpected error in text of file.
 
'------ GetSectionValues: ------------------------
 '  Ret = GetSectionValues(sSection, sArray)
 ' ex.: R = Cls.GetSectionValues("Section1", A1)
   '      If r = 0 Then ......(A1 holds array of key=value pairs. )
 ' Return: 0 - success. 2 - no such section. 3 - no file open.    
 
'---- Write INI value: -----------------------------
' Ret = WriteINIValue(Section, Key, Value)
'   ex.: R = Cls.WriteINIValue("Section1", "backcolor", "blue")
'  Sets value: backcolor=blue  Key and Section will be created If necessary.
'  Return:  0 - success. 3 - no file open. 4 - unexpected error in text of file. 

'------ Delete INI value: ------------------------
'   Ret = DeleteINIValue(Section, Key)
'  ex.: R = Cls.DeleteINIValue("Section1", "backcolor")
'   Deletes 'backcolor' key.
'  Return: 0 - success. 1 - no such key. 2 - no such section.  3 - no file open. 4 - unexpected error in text of file.
'  For this Function success would be If Ret < 3.    

'-----  Write INI section: ------------------------
'  Ret = WriteINISection(Section)
'  ex.: R = Cls.WriteINISection("Section1")
'   writes a new section to INI file.
' Return: 0 - success. 2 - section already exists. 3 - no file open.
'  For this Function success would be If Ret < 3.  

'---- Delete INI section: -------------------------
' Ret = DeleteINISection(Section)  
'  ex.: R = Cls.DeleteINISection("Section1")
'  Return: 0 - success. 2 - no such section. 3 - no file open.
'  For this Function success would be If Ret < 3.  

NOTE ABOUT ERROR CODES: The error codes are designed to relate
'--to the same thing regardless of the Function. 0 is always success.
'-- 1 always means the key does Not exist, regardless of whether you're
'--trying to read from or delete the key.
'-- 2 always relates to the section. For functions that require a section
'-- it means the section does Not exist. When trying to write a new section
'-- it means the section already exists. 
'-- 3 always means that no file has been opened.
'-- 4 is an unknown error indicating that there was an unexpected problem.



___________________________________________

ClsINI

---------------- SNIP HERE ---------------------------------------->

Class ClsINI
Private FSO, TS, Dic, sFil, sBullet

 Private Sub Class_Initialize()
    Set FSO = CreateObject("Scripting.FileSystemObject")
    sBullet = Chr(149)
 End Sub
          
  Private Sub Class_Terminate()
     Set FSO = Nothing
     Set Dic = Nothing
  End Sub
  
  Public Function OpenINIFile(sFilePath)
    Dim s, sSec, sList
     If FSO.FileExists(sFilePath) = False Then
       OpenINIFile = False
       Exit Function
     End If
      sFil = sFilePath
    Set Dic = Nothing 

   Set Dic = CreateObject("Scripting.Dictionary")
     Dic.Add "Glo_bal", sBullet
       On Error Resume Next 
    Set TS = FSO.OpenTextFile(sFil, 1)  
      Do While TS.AtEndOfStream = False
         s = TS.ReadLine
         s = Trim(s)
         If Len(s) > 0 Then
            If Left(s, 1) = "[" Then
               sSec = s
               Dic.Add sSec, sBullet
            ElseIf Dic.Exists(sSec) Then
               sList = Dic.Item(sSec)
               sList = sList & s & sBullet
               Dic.Item(sSec) = sList
            Else
                sList = Dic.Item("Glo_bal")
                sList = sList & s & sBullet
                Dic.Item("Glo_bal") = sList
            End If    
         End If    
       Loop
      TS.Close  
     Set TS = Nothing
     OpenINIFile = True
  End Function

  Public Sub CloseINIFile()
     Set Dic = Nothing
  End Sub

Public Function GetSectionNames()
  Dim ASec, iK, sTemp, sTemp1
    If IsObject(Dic) = False Then
        ASec = array("")
        GetSectionNames = ASec  '--  no file open.
        Exit Function
    End If
    
    If Dic.Count = 0 Then
        ASec = array("")
        GetSectionNames = ASec  '--  no keys.
        Exit Function
    End If
    
      On Error Resume Next
    ASec = Dic.Keys
     For iK = 0 to UBound(ASec) 
        sTemp = ASec(iK)
          If (sTemp <> "Glo_bal") Then
             sTemp = Mid(sTemp, 2, (len(sTemp) - 2))
             sTemp1 = sTemp1 & (sTemp & sBullet)
         End If   
    Next   
      GetSectionNames = Split(sTemp1, sBullet)
End Function

Public Function GetINIValue(sSection, sKey, sValue)
    Dim sList, pt, pt2, s1, Lens1
      
     Select Case CheckBasics(sSection)
       Case 3
          GetINIValue = 3  '-- return 3: no file open.
          Exit Function
       Case 2
          GetINIValue = 2  '-- return 2: no such section.
         Exit Function
     End Select
     
       sValue = ""
      sList = Dic.Item(sSection)
      s1 = sBullet & sKey & "="
      Lens1 = Len(s1)
     
     pt = InStr(1, sList, s1, 1)
        If pt = 0 Then 
           sValue = ""
           GetINIValue = 1  '-- return 1: no such key.
           Exit Function
        End If
        
     pt2 = InStr((pt + Lens1), sList, sBullet, 1)
       If pt2 = 0 Then 
          GetINIValue = 4 '--error
       ElseIf pt2 = (pt + 1) Then   '-- key exists, value is ""
          GetINIValue = 0
       Else
         sValue = Mid(sList, (pt + Lens1), (pt2 - (pt + Lens1)))
         GetINIValue = 0
       End If  
  End Function

Public Function GetSectionValues(byVal sSection, sArray)
  Dim ATemp, ATemp2(), i, i2
    Select Case CheckBasics(sSection)
       Case 3
          GetSectionValues = 3  '-- return 3: no file open.
          Exit Function
       Case 2
          GetSectionValues = 2  '-- return 2: no such section.
         Exit Function
     End Select
   
  ATemp = Split(Dic.Item(sSection), sBullet)
       i2 = 0
    For i = 0 to UBound(ATemp)
       If Left(ATemp(i), 1) <> ";" Then
          ReDim preserve ATemp2(i2)
          ATemp2(i2) = ATemp(i)
          i2 = (i2 + 1)
       End If
    Next   
    
   sArray = ATemp2      
   GetSectionValues = 0
End Function

  Public Function WriteINIValue(sSection, sKey, sValue)
    Dim sList, pt, pt2, s1, Lens1, sSec
        Select Case CheckBasics(sSection)
          Case 3
             WriteINIValue = 3  '-- return 3: no file open.
              Exit Function
          Case 2
             '--If section does Not exist, write section and key=value, Then quit:
               sList = sBullet & sKey & "=" & sValue & sBullet
               Dic.Add sSection, sList
               Call WriteNewINI
               WriteINIValue = 0
               Exit Function
        End Select

     sList = Dic.Item(sSection)
        s1 = sBullet & sKey & "="
       Lens1 = Len(s1)
        pt = instr(1, sList, s1, 1)
             '--If sKey does Not already exist, write it and quit:
          If pt = 0 Then
             sList = sList & sKey & "=" & sValue & sBullet
             Dic.Item(sSection) = sList
             Call WriteNewINI
             WriteINIValue = 0
          Else   
             pt2 = instr((pt + Lens1), sList, sBullet)
                If pt2 <> 0 Then
                       If (pt2 + 1) < Len(sList) Then 
                           sList = (Left(sList, ((pt + Lens1) - 1))) & sValue & (Right(sList, (Len(sList) - (pt2 - 1))))
                       Else  '--last value in section:
                           sList =  (Left(sList, ((pt + Lens1) - 1))) & sValue & sBullet
                       End If    
                        Dic.Item(sSection) = sList
                        Call WriteNewINI
                        WriteINIValue = 0
                Else
                        WriteINIValue = 4 '--error
                         Exit Function
                End If      
         End If
   End Function

Public Function DeleteINIValue(sSection, sKey)
 Dim sSec, sList, pt, pt2, s1
    Select Case CheckBasics(sSection)
       Case 3
           DeleteINIValue = 3  '-- return 3: no file open.
           Exit Function
       Case 2
           DeleteINIValue = 2  '-- return 2: no such section.
           Exit Function
     End Select
      
  sList = Dic.Item(sSection)
    s1 = sBullet & sKey & "="
    pt = InStr(1, sList, s1, 1)
      If pt = 0 Then
        DeleteINIValue = 1  '--return 1: no such key.
        Exit Function
      End If
    
   pt2 = InStr((pt + 1), sList, sBullet, 1)
     If pt2 = 0 Then  
        DeleteINIValue = 4  '--error.
        Exit Function
     Else
        sList = (Left(sList, (pt))) & (Right(sList, (len(sList) - pt2)))
        Dic.Item(sSection) = sList
        Call WriteNewINI
        DeleteINIValue = 0
     End If
End Function

Public Function WriteINISection(sSection)
    Select Case CheckBasics(sSection)
       Case 3
           WriteINISection = 3  '-- return 3: no file open.
       Case 2
           Dic.Add sSection, sBullet
           Call WriteNewINI
           WriteINISection = 0
        Case Else
           WriteINISection = 2 '-- section already exists.  
     End Select 
End Function   

Public Function DeleteINISection(sSection)
  
  Select Case CheckBasics(sSection)
       Case 3
           DeleteINISection = 3  '-- return 3: no file open.
       Case 2
          DeleteINISection = 2  '-- return 2: no such section.
       Case Else
           Dic.Remove sSection
           Call WriteNewINI
           DeleteINISection = 0
     End Select

End Function    

 Private Sub WriteNewINI()
      Dim Att, oFil, A1, i, s, s1, s2, sG
      On Error Resume Next
        Set oFil = FSO.GetFile(sFil)
          Att = oFil.Attributes
          oFil.Attributes = 0
        Set oFil = Nothing
        
      A1 = Dic.Keys
         For i = 0 to UBound(A1)
            s1 = A1(i)
            If s1 = "Glo_bal" Then
               sG = Dic.Item(s1)
               sG = Replace(sG, sBullet, vbcrlf)
               sG = sG & vbcrlf
            Else   
              s2 = Dic.Item(s1)
              s2 = Replace(s2, sBullet, vbcrlf)
              s = s & s1 & s2 & vbcrlf
            End If   
         Next       
       s = sG & s '--add in global comments section.     
      FSO.DeleteFile sFil, True   
      Set TS = FSO.CreateTextFile(sFil)     
        TS.Write s
        TS.Close
      Set TS = Nothing
      
     Set oFil = FSO.GetFile(sFil)
        oFil.Attributes = Att
     Set oFil = Nothing         
   End Sub
 
Private Function CheckBasics(sSection)
  If IsObject(Dic) = False Then
     CheckBasics = 3  '-- return 3: no file open.
     Exit Function
  End If

   If (Left(sSection, 1) <> "[") Then sSection = "[" & sSection
   If (right(sSection, 1) <> "]") Then sSection = sSection & "]"

 If Dic.Exists(sSection) = False Then
   CheckBasics = 2
 Else
   CheckBasics = 0
 End If
End Function
     
End Class
