Functions/Split-Ini.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Split-Ini
{
    <#
    .SYNOPSIS
    Reads an INI file and returns its contents.
     
    .DESCRIPTION
    A configuration file consists of sections, led by a "[section]" header and followed by "name = value" entries:
 
        [spam]
        eggs=ham
        green=
           eggs
          
        [stars]
        sneetches = belly
          
    By default, the INI file will be returned as `Carbon.Ini.IniNode` objects for each name/value pair. For example, given the INI file above, the following will be returned:
     
        Line FullName Section Name Value
        ---- -------- ------- ---- -----
           2 spam.eggs spam eggs ham
           3 spam.green spam green eggs
           7 stars.sneetches stars sneetches belly
     
    It is sometimes useful to get a hashtable back of the name/values. The `AsHashtable` switch will return a hashtable where the keys are the full names of the name/value pairs. For example, given the INI file above, the following hashtable is returned:
     
        Name Value
        ---- -----
        spam.eggs Carbon.Ini.IniNode;
        spam.green Carbon.Ini.IniNode;
        stars.sneetches Carbon.Ini.IniNode;
        }
 
    Each line of an INI file contains one entry. If the lines that follow are indented, they are treated as continuations of that entry. Leading whitespace is removed from values. Empty lines are skipped. Lines beginning with "#" or ";" are ignored and may be used to provide comments.
 
    Configuration keys can be set multiple times, in which case Split-Ini will use the value that was configured last. As an example:
 
        [spam]
        eggs=large
        ham=serrano
        eggs=small
 
    This would set the configuration key named "eggs" to "small".
 
    It is also possible to define a section multiple times. For example:
 
        [foo]
        eggs=large
        ham=serrano
        eggs=small
 
        [bar]
        eggs=ham
        green=
           eggs
 
        [foo]
        ham=prosciutto
        eggs=medium
        bread=toasted
 
    This would set the "eggs", "ham", and "bread" configuration keys of the "foo" section to "medium", "prosciutto", and "toasted", respectively. As you can see, the only thing that matters is the last value that was set for each of the configuration keys.
 
    Be default, operates on the INI file case-insensitively. If your INI is case-sensitive, use the `-CaseSensitive` switch.
 
    .LINK
    Set-IniEntry
 
    .LINK
    Remove-IniEntry
 
    .EXAMPLE
    Split-Ini -Path C:\Users\rspektor\mercurial.ini
 
    Given this INI file:
 
        [ui]
        username = Regina Spektor <regina@reginaspektor.com>
 
        [extensions]
        share =
        extdiff =
 
    `Split-Ini` returns the following objects to the pipeline:
 
        Line FullName Section Name Value
        ---- -------- ------- ---- -----
           2 ui.username ui username Regina Spektor <regina@reginaspektor.com>
           5 extensions.share extensions share
           6 extensions.extdiff extensions extdiff
 
    .EXAMPLE
    Split-Ini -Path C:\Users\rspektor\mercurial.ini -AsHashtable
 
    Given this INI file:
 
        [ui]
        username = Regina Spektor <regina@reginaspektor.com>
 
        [extensions]
        share =
        extdiff =
 
    `Split-Ini` returns the following hashtable:
 
        @{
            ui.username = Carbon.Ini.IniNode (
                                FullName = 'ui.username';
                                Section = "ui";
                                Name = "username";
                                Value = "Regina Spektor <regina@reginaspektor.com>";
                                LineNumber = 2;
                            );
            extensions.share = Carbon.Ini.IniNode (
                                    FullName = 'extensions.share';
                                    Section = "extensions";
                                    Name = "share"
                                    Value = "";
                                    LineNumber = 5;
                                )
            extensions.extdiff = Carbon.Ini.IniNode (
                                       FullName = 'extensions.extdiff';
                                       Section = "extensions";
                                       Name = "extdiff";
                                       Value = "";
                                       LineNumber = 6;
                                  )
        }
 
    .EXAMPLE
    Split-Ini -Path C:\Users\rspektor\mercurial.ini -AsHashtable -CaseSensitive
 
    Demonstrates how to parse a case-sensitive INI file.
 
        Given this INI file:
 
        [ui]
        username = user@example.com
        USERNAME = user2example.com
 
        [UI]
        username = user3@example.com
 
 
    `Split-Ini -CaseSensitive` returns the following hashtable:
 
        @{
            ui.username = Carbon.Ini.IniNode (
                                FullName = 'ui.username';
                                Section = "ui";
                                Name = "username";
                                Value = "user@example.com";
                                LineNumber = 2;
                            );
            ui.USERNAME = Carbon.Ini.IniNode (
                                FullName = 'ui.USERNAME';
                                Section = "ui";
                                Name = "USERNAME";
                                Value = "user2@example.com";
                                LineNumber = 3;
                            );
            UI.username = Carbon.Ini.IniNode (
                                FullName = 'UI.username';
                                Section = "UI";
                                Name = "username";
                                Value = "user3@example.com";
                                LineNumber = 6;
                            );
        }
 
    #>

    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Mandatory=$true,ParameterSetName='ByPath')]
        [string]
        # The path to the mercurial INI file to read.
        $Path,
        
        [Switch]
        # Pass each parsed setting down the pipeline instead of collecting them all into a hashtable.
        $AsHashtable,

        [Switch]
        # Parses the INI file in a case-sensitive manner.
        $CaseSensitive
    )

    Set-StrictMode -Version 'Latest'

    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    if( -not (Test-Path $Path -PathType Leaf) )
    {
        Write-Error ("INI file '{0}' not found." -f $Path)
        return
    }
    
    $sectionName = ''
    $lineNum = 0
    $lastSetting = $null
    $settings = @{ }
    if( $CaseSensitive )
    {
        $settings = New-Object 'Collections.Hashtable'
    }
    
    Get-Content -Path $Path | ForEach-Object {
        
        $lineNum += 1
        
        if( -not $_ -or $_ -match '^[;#]' )
        {
            if( -not $AsHashtable -and $lastSetting )
            {
                $lastSetting
            }
            $lastSetting = $null
            return
        }
        
        if( $_ -match '^\[([^\]]+)\]' )
        {
            if( -not $AsHashtable -and $lastSetting )
            {
                $lastSetting
            }
            $lastSetting = $null
            $sectionName = $matches[1]
            Write-Debug "Parsed section [$sectionName]"
            return
        }
        
        if( $_ -match '^\s+(.*)$' -and $lastSetting )
        {
            $lastSetting.Value += "`n" + $matches[1]
            return
        }
        
        if( $_ -match '^([^=]*) ?= ?(.*)$' )
        {
            if( -not $AsHashtable -and $lastSetting )
            {
                $lastSetting
            }
            
            $name = $matches[1]
            $value = $matches[2]
            
            $name = $name.Trim()
            $value = $value.TrimStart()
            
            $setting = New-Object Carbon.Ini.IniNode $sectionName,$name,$value,$lineNum
            $settings[$setting.FullName] = $setting
            $lastSetting = $setting
            Write-Debug "Parsed setting '$($setting.FullName)'"
        }
    }
    
    if( $AsHashtable )
    {
        return $settings
    }
    else
    {
        if( $lastSetting )
        {
            $lastSetting
        }
    }
}