Import-INIToPSObject.ps1

<#PSScriptInfo
 
.VERSION 0.0.0.1
 
.GUID 02dc8320-d51a-41c8-ae60-fb8b58e3f28b
 
.AUTHOR Luke Dalton
 
.COPYRIGHT GNU GPL v3
 
.TAGS INI
 
.LICENSEURI https://www.gnu.org/licenses/gpl-3.0.en.html
 
.PROJECTURI https://psytechnic.blogspot.co.uk
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
  (c) 25 September 2018
  Version History:
  0.0.0.1 - First Write
 
.Description
  Reads lines from .ini files and constructs a custom PSObject which can be modified and exported
#>

Function Import-INIToPSObject{
  [CmdletBinding()]
  [OutputType([System.Management.Automation.PSCustomObject])]
  param(
    # Full path of .ini file to import
    [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)][ValidateNotNullOrEmpty()][ValidateScript({Test-Path -Path $_})][string]$Path,
    # Interpret lines ending with ' \' as line break
    [Parameter(Position=1,Mandatory=$False)][switch]$MultiLine,
    # Ignores inline and full line comments (';')
    [Parameter(Position=2,Mandatory=$False)][switch]$IgnoreComments,
    # Force overwrite of duplicate sections or values
    [Parameter(Position=3,Mandatory=$False)][switch]$Force
  )
  Begin{
    $commandName=$MyInvocation.MyCommand.Name
    Function Process-String([parameter(Mandatory=$True)][string]$string){
      $return=$string.Trim()
      switch -Regex ($return){
        '^\".*\"$'{$return=$return-replace'^\"'-replace'\"$';Continue}
        "^\'.*\'$"{$return=$return-replace"^\'"-replace"\'$";Continue}
      }
      $return
    }
  }
  Process{
    Write-Verbose -Message "$commandName : Creating new PSCustomObject from INI"
    $returnObject=New-Object -TypeName PSObject
    $sectionName=$null
    
    Write-Verbose -Message "$commandName : Reading data from -"
    Write-Verbose -Message "$commandName : $Path"
    Try{$fileLines=[IO.File]::ReadAllLines($Path)}Catch{$_;Return}
    
    Write-Verbose -Message "$commandName : Reading lines"
    For($index=0;$index-lt$fileLines.Count;$index++){
      $fileLine=$fileLines[$index]
      if($previousLine){$fileLine="$previousLine$fileLine"}
      if($MultiLine-and$fileLine-match'\s+\\$'){$previousLine=$fileLine-replace'\s+\\$';Continue}else{$previousLine=$null}
      $lineLabel="Line [$($index+1)\$($fileLines.Count)]: $fileLine"
      
      if($sectionName){$toModify=$returnObject."$sectionName"}else{$toModify=$returnObject}
      switch -Regex ($fileLine){
        "^\s*\[(.+)\]"{
          if($fileLine.IndexOf(';')-ge0){
            if(-not($IgnoreComments)){
              $comment=$fileLine.SubString($fileLine.IndexOf(';')+1).Trim()
              $inlineCommentCount+=1
              $commentName="`;InlineComment$inlineCommentCount"
              $toModify|Add-Member -MemberType NoteProperty -Name $commentName -Value $comment -Force:$Force
              Write-Verbose -Message "$commandName :`r`n$lineLabel`r`nComment : $commentName = $comment"
            }
          }
          $commentCount=$inlineCommentCount=0
          $sectionName=Process-String $Matches[1]
          try{$returnObject|Add-Member -MemberType NoteProperty -Name $sectionName -Value (New-Object -TypeName PSObject) -Force:$Force -ErrorAction Stop}
          catch{
            $message="`r`n$lineLabel`r`nINI Read Error : $($_.Exception.Message)"
            switch($ErrorActionPreference){
              'Stop'{Write-Error -Message $message;Return}
              'Continue'{Write-Warning -Message "$commandName : $message";Continue}
              Default{Write-Verbose -Message "$commandName : $message";Continue}
            }
          }
          Write-Verbose -Message "$commandName :`r`n$lineLabel`r`nSection Name : $sectionName"
          Continue
        }
        "^;(.*)$"{
          if(-not($IgnoreComments)){
            $comment=$Matches[1].Trim()
            $commentCount+=1
            $name="`;Comment$commentCount"
            $toModify|Add-Member -MemberType NoteProperty -Name $name -Value $comment -Force:$Force
            Write-Verbose -Message "$commandName :`r`n$lineLabel`r`nComment : $name = $comment"
          }
          Continue
        }
        "(.+?)\s*=\s*(.*)"{
          $name,$value=$Matches[1..2]
          if($value.IndexOf(';')-ge0){
            if(-not($IgnoreComments)){
              $comment=$value.SubString($value.IndexOf(';')+1).Trim()
              $inlineCommentCount+=1
              $commentName="`;InlineComment$inlineCommentCount"
              $toModify|Add-Member -MemberType NoteProperty -Name $commentName -Value $comment -Force:$Force
              Write-Verbose -Message "$commandName :`r`n$lineLabel`r`nComment : $commentName = $comment"
            }
            $value=$value.SubString(0,$value.IndexOf(';'))
          }
          $name=Process-String $name
          $value=Process-String $value
          Write-Verbose -Message "$commandName :`r`n$lineLabel`r`nProperty : $name = $value"
          try{$ToModify|Add-Member -MemberType NoteProperty -Name $name -Value $value -Force:$Force -ErrorAction Stop}
          catch{
            $message="`r`n$lineLabel`r`nINI Read Error : $($_.Exception.Message)"
            switch($ErrorActionPreference){
              'Stop'{Write-Error -Message $message;Return}
              'Continue'{Write-Warning -Message "$commandName : $message";Continue}
              Default{Write-Verbose -Message "$commandName : $message";Continue}
            }
          }
          Continue
        }
        "^\s*$"{Write-Verbose -Message "$commandName :`r`n$lineLabel`r`nEmpty Line";Continue}
        Default{
          $message="`r`n$lineLabel`r`nINI Read Error : Unrecognised pattern"
          switch($ErrorActionPreference){
            'Stop'{Write-Error -Message $message;Return}
            'Continue'{Write-Warning -Message "$commandName : $message";Continue}
            Default{Write-Verbose -Message "$commandName : $message";Continue}
          }
        }
      }
    }
    Write-Verbose -Message "$commandName : Returning constructed object"
    $returnObject
  }
  End{}
}