~ubuntu-branches/ubuntu/saucy/monodevelop/saucy

« back to all changes in this revision

Viewing changes to external/ngit/NGit/NGit/Config.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2012-05-27 18:08:20 UTC
  • mfrom: (1.8.5) (1.5.8 sid)
  • Revision ID: package-import@ubuntu.com-20120527180820-f1ub6lhg0s50wci1
Tags: 3.0.2+dfsg-3
* [fcecfe7] Fix monodevelop-core-addins.pc.in to point to actual 
  installed location of assemblies.
* [26e1a07] DebSrc 3.0 does not support Quilt's -p parameter, so 
  manually adjust the path in the patch file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using System;
 
45
using System.Collections.Generic;
 
46
using System.Text;
 
47
using NGit;
 
48
using NGit.Errors;
 
49
using NGit.Events;
 
50
using NGit.Internal;
 
51
using NGit.Util;
 
52
using Sharpen;
 
53
 
 
54
namespace NGit
 
55
{
 
56
        /// <summary>
 
57
        /// Git style
 
58
        /// <code>.config</code>
 
59
        /// ,
 
60
        /// <code>.gitconfig</code>
 
61
        /// ,
 
62
        /// <code>.gitmodules</code>
 
63
        /// file.
 
64
        /// </summary>
 
65
        public class Config
 
66
        {
 
67
                private static readonly string[] EMPTY_STRING_ARRAY = new string[] {  };
 
68
 
 
69
                private const long KiB = 1024;
 
70
 
 
71
                private const long MiB = 1024 * KiB;
 
72
 
 
73
                private const long GiB = 1024 * MiB;
 
74
 
 
75
                /// <summary>the change listeners</summary>
 
76
                private readonly ListenerList listeners = new ListenerList();
 
77
 
 
78
                /// <summary>Immutable current state of the configuration data.</summary>
 
79
                /// <remarks>
 
80
                /// Immutable current state of the configuration data.
 
81
                /// <p>
 
82
                /// This state is copy-on-write. It should always contain an immutable list
 
83
                /// of the configuration keys/values.
 
84
                /// </remarks>
 
85
                private readonly AtomicReference<ConfigSnapshot> state;
 
86
 
 
87
                private readonly NGit.Config baseConfig;
 
88
 
 
89
                /// <summary>Magic value indicating a missing entry.</summary>
 
90
                /// <remarks>
 
91
                /// Magic value indicating a missing entry.
 
92
                /// <p>
 
93
                /// This value is tested for reference equality in some contexts, so we
 
94
                /// must ensure it is a special copy of the empty string.  It also must
 
95
                /// be treated like the empty string.
 
96
                /// </remarks>
 
97
                private static readonly string MAGIC_EMPTY_VALUE = string.Empty;
 
98
 
 
99
                /// <summary>Create a configuration with no default fallback.</summary>
 
100
                /// <remarks>Create a configuration with no default fallback.</remarks>
 
101
                public Config() : this(null)
 
102
                {
 
103
                }
 
104
 
 
105
                /// <summary>Create an empty configuration with a fallback for missing keys.</summary>
 
106
                /// <remarks>Create an empty configuration with a fallback for missing keys.</remarks>
 
107
                /// <param name="defaultConfig">
 
108
                /// the base configuration to be consulted when a key is missing
 
109
                /// from this configuration instance.
 
110
                /// </param>
 
111
                public Config(NGit.Config defaultConfig)
 
112
                {
 
113
                        baseConfig = defaultConfig;
 
114
                        state = new AtomicReference<ConfigSnapshot>(NewState());
 
115
                }
 
116
 
 
117
                /// <summary>Escape the value before saving</summary>
 
118
                /// <param name="x">the value to escape</param>
 
119
                /// <returns>the escaped value</returns>
 
120
                private static string EscapeValue(string x)
 
121
                {
 
122
                        bool inquote = false;
 
123
                        int lineStart = 0;
 
124
                        StringBuilder r = new StringBuilder(x.Length);
 
125
                        for (int k = 0; k < x.Length; k++)
 
126
                        {
 
127
                                char c = x[k];
 
128
                                switch (c)
 
129
                                {
 
130
                                        case '\n':
 
131
                                        {
 
132
                                                if (inquote)
 
133
                                                {
 
134
                                                        r.Append('"');
 
135
                                                        inquote = false;
 
136
                                                }
 
137
                                                r.Append("\\n\\\n");
 
138
                                                lineStart = r.Length;
 
139
                                                break;
 
140
                                        }
 
141
 
 
142
                                        case '\t':
 
143
                                        {
 
144
                                                r.Append("\\t");
 
145
                                                break;
 
146
                                        }
 
147
 
 
148
                                        case '\b':
 
149
                                        {
 
150
                                                r.Append("\\b");
 
151
                                                break;
 
152
                                        }
 
153
 
 
154
                                        case '\\':
 
155
                                        {
 
156
                                                r.Append("\\\\");
 
157
                                                break;
 
158
                                        }
 
159
 
 
160
                                        case '"':
 
161
                                        {
 
162
                                                r.Append("\\\"");
 
163
                                                break;
 
164
                                        }
 
165
 
 
166
                                        case ';':
 
167
                                        case '#':
 
168
                                        {
 
169
                                                if (!inquote)
 
170
                                                {
 
171
                                                        r.Insert(lineStart, '"');
 
172
                                                        inquote = true;
 
173
                                                }
 
174
                                                r.Append(c);
 
175
                                                break;
 
176
                                        }
 
177
 
 
178
                                        case ' ':
 
179
                                        {
 
180
                                                if (!inquote && r.Length > 0 && r[r.Length - 1] == ' ')
 
181
                                                {
 
182
                                                        r.Insert(lineStart, '"');
 
183
                                                        inquote = true;
 
184
                                                }
 
185
                                                r.Append(' ');
 
186
                                                break;
 
187
                                        }
 
188
 
 
189
                                        default:
 
190
                                        {
 
191
                                                r.Append(c);
 
192
                                                break;
 
193
                                                break;
 
194
                                        }
 
195
                                }
 
196
                        }
 
197
                        if (inquote)
 
198
                        {
 
199
                                r.Append('"');
 
200
                        }
 
201
                        return r.ToString();
 
202
                }
 
203
 
 
204
                /// <summary>Obtain an integer value from the configuration.</summary>
 
205
                /// <remarks>Obtain an integer value from the configuration.</remarks>
 
206
                /// <param name="section">section the key is grouped within.</param>
 
207
                /// <param name="name">name of the key to get.</param>
 
208
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
209
                /// <returns>an integer value from the configuration, or defaultValue.</returns>
 
210
                public virtual int GetInt(string section, string name, int defaultValue)
 
211
                {
 
212
                        return GetInt(section, null, name, defaultValue);
 
213
                }
 
214
 
 
215
                /// <summary>Obtain an integer value from the configuration.</summary>
 
216
                /// <remarks>Obtain an integer value from the configuration.</remarks>
 
217
                /// <param name="section">section the key is grouped within.</param>
 
218
                /// <param name="subsection">subsection name, such a remote or branch name.</param>
 
219
                /// <param name="name">name of the key to get.</param>
 
220
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
221
                /// <returns>an integer value from the configuration, or defaultValue.</returns>
 
222
                public virtual int GetInt(string section, string subsection, string name, int defaultValue
 
223
                        )
 
224
                {
 
225
                        long val = GetLong(section, subsection, name, defaultValue);
 
226
                        if (int.MinValue <= val && val <= int.MaxValue)
 
227
                        {
 
228
                                return (int)val;
 
229
                        }
 
230
                        throw new ArgumentException(MessageFormat.Format(JGitText.Get().integerValueOutOfRange
 
231
                                , section, name));
 
232
                }
 
233
 
 
234
                /// <summary>Obtain an integer value from the configuration.</summary>
 
235
                /// <remarks>Obtain an integer value from the configuration.</remarks>
 
236
                /// <param name="section">section the key is grouped within.</param>
 
237
                /// <param name="name">name of the key to get.</param>
 
238
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
239
                /// <returns>an integer value from the configuration, or defaultValue.</returns>
 
240
                public virtual long GetLong(string section, string name, long defaultValue)
 
241
                {
 
242
                        return GetLong(section, null, name, defaultValue);
 
243
                }
 
244
 
 
245
                /// <summary>Obtain an integer value from the configuration.</summary>
 
246
                /// <remarks>Obtain an integer value from the configuration.</remarks>
 
247
                /// <param name="section">section the key is grouped within.</param>
 
248
                /// <param name="subsection">subsection name, such a remote or branch name.</param>
 
249
                /// <param name="name">name of the key to get.</param>
 
250
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
251
                /// <returns>an integer value from the configuration, or defaultValue.</returns>
 
252
                public virtual long GetLong(string section, string subsection, string name, long 
 
253
                        defaultValue)
 
254
                {
 
255
                        string str = GetString(section, subsection, name);
 
256
                        if (str == null)
 
257
                        {
 
258
                                return defaultValue;
 
259
                        }
 
260
                        string n = str.Trim();
 
261
                        if (n.Length == 0)
 
262
                        {
 
263
                                return defaultValue;
 
264
                        }
 
265
                        long mul = 1;
 
266
                        switch (StringUtils.ToLowerCase(n[n.Length - 1]))
 
267
                        {
 
268
                                case 'g':
 
269
                                {
 
270
                                        mul = GiB;
 
271
                                        break;
 
272
                                }
 
273
 
 
274
                                case 'm':
 
275
                                {
 
276
                                        mul = MiB;
 
277
                                        break;
 
278
                                }
 
279
 
 
280
                                case 'k':
 
281
                                {
 
282
                                        mul = KiB;
 
283
                                        break;
 
284
                                }
 
285
                        }
 
286
                        if (mul > 1)
 
287
                        {
 
288
                                n = Sharpen.Runtime.Substring(n, 0, n.Length - 1).Trim();
 
289
                        }
 
290
                        if (n.Length == 0)
 
291
                        {
 
292
                                return defaultValue;
 
293
                        }
 
294
                        try
 
295
                        {
 
296
                                return mul * long.Parse(n);
 
297
                        }
 
298
                        catch (FormatException)
 
299
                        {
 
300
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().invalidIntegerValue
 
301
                                        , section, name, str));
 
302
                        }
 
303
                }
 
304
 
 
305
                /// <summary>Get a boolean value from the git config</summary>
 
306
                /// <param name="section">section the key is grouped within.</param>
 
307
                /// <param name="name">name of the key to get.</param>
 
308
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
309
                /// <returns>
 
310
                /// true if any value or defaultValue is true, false for missing or
 
311
                /// explicit false
 
312
                /// </returns>
 
313
                public virtual bool GetBoolean(string section, string name, bool defaultValue)
 
314
                {
 
315
                        return GetBoolean(section, null, name, defaultValue);
 
316
                }
 
317
 
 
318
                /// <summary>Get a boolean value from the git config</summary>
 
319
                /// <param name="section">section the key is grouped within.</param>
 
320
                /// <param name="subsection">subsection name, such a remote or branch name.</param>
 
321
                /// <param name="name">name of the key to get.</param>
 
322
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
323
                /// <returns>
 
324
                /// true if any value or defaultValue is true, false for missing or
 
325
                /// explicit false
 
326
                /// </returns>
 
327
                public virtual bool GetBoolean(string section, string subsection, string name, bool
 
328
                         defaultValue)
 
329
                {
 
330
                        string n = GetRawString(section, subsection, name);
 
331
                        if (n == null)
 
332
                        {
 
333
                                return defaultValue;
 
334
                        }
 
335
                        if (MAGIC_EMPTY_VALUE == n)
 
336
                        {
 
337
                                return true;
 
338
                        }
 
339
                        try
 
340
                        {
 
341
                                return StringUtils.ToBoolean(n);
 
342
                        }
 
343
                        catch (ArgumentException)
 
344
                        {
 
345
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().invalidBooleanValue
 
346
                                        , section, name, n));
 
347
                        }
 
348
                }
 
349
 
 
350
                /// <summary>Parse an enumeration from the configuration.</summary>
 
351
                /// <remarks>Parse an enumeration from the configuration.</remarks>
 
352
                /// <?></?>
 
353
                /// <param name="section">section the key is grouped within.</param>
 
354
                /// <param name="subsection">subsection name, such a remote or branch name.</param>
 
355
                /// <param name="name">name of the key to get.</param>
 
356
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
357
                /// <returns>
 
358
                /// the selected enumeration value, or
 
359
                /// <code>defaultValue</code>
 
360
                /// .
 
361
                /// </returns>
 
362
                public virtual T GetEnum<T>(string section, string subsection, string name, T defaultValue
 
363
                        )
 
364
                {
 
365
                        Array all = AllValuesOf(defaultValue);
 
366
                        return GetEnum(all, section, subsection, name, defaultValue);
 
367
                }
 
368
 
 
369
                private static Array AllValuesOf<T>(T value)
 
370
                {
 
371
                        try
 
372
                        {
 
373
                                return Enum.GetValues (typeof(T));
 
374
                        }
 
375
                        catch (Exception err)
 
376
                        {
 
377
                                string typeName = value.GetType().FullName;
 
378
                                string msg = MessageFormat.Format(JGitText.Get().enumValuesNotAvailable, typeName
 
379
                                        );
 
380
                                throw new ArgumentException(msg, err);
 
381
                        }
 
382
                }
 
383
 
 
384
                /// <summary>Parse an enumeration from the configuration.</summary>
 
385
                /// <remarks>Parse an enumeration from the configuration.</remarks>
 
386
                /// <?></?>
 
387
                /// <param name="all">
 
388
                /// all possible values in the enumeration which should be
 
389
                /// recognized. Typically
 
390
                /// <code>EnumType.values()</code>
 
391
                /// .
 
392
                /// </param>
 
393
                /// <param name="section">section the key is grouped within.</param>
 
394
                /// <param name="subsection">subsection name, such a remote or branch name.</param>
 
395
                /// <param name="name">name of the key to get.</param>
 
396
                /// <param name="defaultValue">default value to return if no value was present.</param>
 
397
                /// <returns>
 
398
                /// the selected enumeration value, or
 
399
                /// <code>defaultValue</code>
 
400
                /// .
 
401
                /// </returns>
 
402
                public virtual T GetEnum<T>(Array all, string section, string subsection, string name
 
403
                        , T defaultValue)
 
404
                {
 
405
                        string value = GetString(section, subsection, name);
 
406
                        if (value == null)
 
407
                        {
 
408
                                return defaultValue;
 
409
                        }
 
410
                        string n = value.Replace(' ', '_');
 
411
                        object trueState = null;
 
412
                        object falseState = null;
 
413
                        foreach (object e in all)
 
414
                        {
 
415
                                if (StringUtils.EqualsIgnoreCase(e.ToString(), n))
 
416
                                {
 
417
                                        return (T)e;
 
418
                                }
 
419
                                else
 
420
                                {
 
421
                                        if (StringUtils.EqualsIgnoreCase(e.ToString(), "TRUE"))
 
422
                                        {
 
423
                                                trueState = e;
 
424
                                        }
 
425
                                        else
 
426
                                        {
 
427
                                                if (StringUtils.EqualsIgnoreCase(e.ToString(), "FALSE"))
 
428
                                                {
 
429
                                                        falseState = e;
 
430
                                                }
 
431
                                        }
 
432
                                }
 
433
                        }
 
434
                        // This is an odd little fallback. C Git sometimes allows boolean
 
435
                        // values in a tri-state with other things. If we have both a true
 
436
                        // and a false value in our enumeration, assume its one of those.
 
437
                        //
 
438
                        if (trueState != null && falseState != null)
 
439
                        {
 
440
                                try
 
441
                                {
 
442
                                        return StringUtils.ToBoolean(n) ? (T)trueState : (T)falseState;
 
443
                                }
 
444
                                catch (ArgumentException)
 
445
                                {
 
446
                                }
 
447
                        }
 
448
                        // Fall through and use our custom error below.
 
449
                        if (subsection != null)
 
450
                        {
 
451
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().enumValueNotSupported3
 
452
                                        , section, name, value));
 
453
                        }
 
454
                        else
 
455
                        {
 
456
                                throw new ArgumentException(MessageFormat.Format(JGitText.Get().enumValueNotSupported2
 
457
                                        , section, name, value));
 
458
                        }
 
459
                }
 
460
 
 
461
                /// <summary>Get string value</summary>
 
462
                /// <param name="section">the section</param>
 
463
                /// <param name="subsection">the subsection for the value</param>
 
464
                /// <param name="name">the key name</param>
 
465
                /// <returns>a String value from git config.</returns>
 
466
                public virtual string GetString(string section, string subsection, string name)
 
467
                {
 
468
                        return GetRawString(section, subsection, name);
 
469
                }
 
470
 
 
471
                /// <summary>
 
472
                /// Get a list of string values
 
473
                /// <p>
 
474
                /// If this instance was created with a base, the base's values are returned
 
475
                /// first (if any).
 
476
                /// </summary>
 
477
                /// <remarks>
 
478
                /// Get a list of string values
 
479
                /// <p>
 
480
                /// If this instance was created with a base, the base's values are returned
 
481
                /// first (if any).
 
482
                /// </remarks>
 
483
                /// <param name="section">the section</param>
 
484
                /// <param name="subsection">the subsection for the value</param>
 
485
                /// <param name="name">the key name</param>
 
486
                /// <returns>array of zero or more values from the configuration.</returns>
 
487
                public virtual string[] GetStringList(string section, string subsection, string name
 
488
                        )
 
489
                {
 
490
                        string[] @base;
 
491
                        if (baseConfig != null)
 
492
                        {
 
493
                                @base = baseConfig.GetStringList(section, subsection, name);
 
494
                        }
 
495
                        else
 
496
                        {
 
497
                                @base = EMPTY_STRING_ARRAY;
 
498
                        }
 
499
                        string[] self = GetRawStringList(section, subsection, name);
 
500
                        if (self == null)
 
501
                        {
 
502
                                return @base;
 
503
                        }
 
504
                        if (@base.Length == 0)
 
505
                        {
 
506
                                return self;
 
507
                        }
 
508
                        string[] res = new string[@base.Length + self.Length];
 
509
                        int n = @base.Length;
 
510
                        System.Array.Copy(@base, 0, res, 0, n);
 
511
                        System.Array.Copy(self, 0, res, n, self.Length);
 
512
                        return res;
 
513
                }
 
514
 
 
515
                /// <param name="section">section to search for.</param>
 
516
                /// <returns>
 
517
                /// set of all subsections of specified section within this
 
518
                /// configuration and its base configuration; may be empty if no
 
519
                /// subsection exists. The set's iterator returns sections in the
 
520
                /// order they are declared by the configuration starting from this
 
521
                /// instance and progressing through the base.
 
522
                /// </returns>
 
523
                public virtual ICollection<string> GetSubsections(string section)
 
524
                {
 
525
                        return GetState().GetSubsections(section);
 
526
                }
 
527
 
 
528
                /// <returns>
 
529
                /// the sections defined in this
 
530
                /// <see cref="Config">Config</see>
 
531
                /// . The set's iterator
 
532
                /// returns sections in the order they are declared by the
 
533
                /// configuration starting from this instance and progressing through
 
534
                /// the base.
 
535
                /// </returns>
 
536
                public virtual ICollection<string> GetSections()
 
537
                {
 
538
                        return GetState().GetSections();
 
539
                }
 
540
 
 
541
                /// <param name="section">the section</param>
 
542
                /// <returns>the list of names defined for this section</returns>
 
543
                public virtual ICollection<string> GetNames(string section)
 
544
                {
 
545
                        return GetNames(section, null);
 
546
                }
 
547
 
 
548
                /// <param name="section">the section</param>
 
549
                /// <param name="subsection">the subsection</param>
 
550
                /// <returns>the list of names defined for this subsection</returns>
 
551
                public virtual ICollection<string> GetNames(string section, string subsection)
 
552
                {
 
553
                        return GetState().GetNames(section, subsection);
 
554
                }
 
555
 
 
556
                /// <summary>Obtain a handle to a parsed set of configuration values.</summary>
 
557
                /// <remarks>Obtain a handle to a parsed set of configuration values.</remarks>
 
558
                /// <?></?>
 
559
                /// <param name="parser">
 
560
                /// parser which can create the model if it is not already
 
561
                /// available in this configuration file. The parser is also used
 
562
                /// as the key into a cache and must obey the hashCode and equals
 
563
                /// contract in order to reuse a parsed model.
 
564
                /// </param>
 
565
                /// <returns>the parsed object instance, which is cached inside this config.</returns>
 
566
                public virtual T Get<T>(Config.SectionParser<T> parser)
 
567
                {
 
568
                        ConfigSnapshot myState = GetState();
 
569
                        T obj = (T)myState.cache.Get(parser);
 
570
                        if (obj == null)
 
571
                        {
 
572
                                obj = parser.Parse(this);
 
573
                                myState.cache.Put(parser, obj);
 
574
                        }
 
575
                        return obj;
 
576
                }
 
577
 
 
578
                /// <summary>Remove a cached configuration object.</summary>
 
579
                /// <remarks>
 
580
                /// Remove a cached configuration object.
 
581
                /// <p>
 
582
                /// If the associated configuration object has not yet been cached, this
 
583
                /// method has no effect.
 
584
                /// </remarks>
 
585
                /// <param name="parser">parser used to obtain the configuration object.</param>
 
586
                /// <seealso cref="Get{T}(SectionParser{T})">Get&lt;T&gt;(SectionParser&lt;T&gt;)</seealso>
 
587
                public virtual void Uncache<_T0>(Config.SectionParser<_T0> parser)
 
588
                {
 
589
                        Sharpen.Collections.Remove(state.Get().cache, parser);
 
590
                }
 
591
 
 
592
                /// <summary>Adds a listener to be notified about changes.</summary>
 
593
                /// <remarks>
 
594
                /// Adds a listener to be notified about changes.
 
595
                /// <p>
 
596
                /// Clients are supposed to remove the listeners after they are done with
 
597
                /// them using the
 
598
                /// <see cref="NGit.Events.ListenerHandle.Remove()">NGit.Events.ListenerHandle.Remove()
 
599
                ///     </see>
 
600
                /// method
 
601
                /// </remarks>
 
602
                /// <param name="listener">the listener</param>
 
603
                /// <returns>the handle to the registered listener</returns>
 
604
                public virtual ListenerHandle AddChangeListener(ConfigChangedListener listener)
 
605
                {
 
606
                        return listeners.AddConfigChangedListener(listener);
 
607
                }
 
608
 
 
609
                /// <summary>Determine whether to issue change events for transient changes.</summary>
 
610
                /// <remarks>
 
611
                /// Determine whether to issue change events for transient changes.
 
612
                /// <p>
 
613
                /// If <code>true</code> is returned (which is the default behavior),
 
614
                /// <see cref="FireConfigChangedEvent()">FireConfigChangedEvent()</see>
 
615
                /// will be called upon each change.
 
616
                /// <p>
 
617
                /// Subclasses that override this to return <code>false</code> are
 
618
                /// responsible for issuing
 
619
                /// <see cref="FireConfigChangedEvent()">FireConfigChangedEvent()</see>
 
620
                /// calls
 
621
                /// themselves.
 
622
                /// </remarks>
 
623
                /// <returns><code></code></returns>
 
624
                protected internal virtual bool NotifyUponTransientChanges()
 
625
                {
 
626
                        return true;
 
627
                }
 
628
 
 
629
                /// <summary>Notifies the listeners</summary>
 
630
                protected internal virtual void FireConfigChangedEvent()
 
631
                {
 
632
                        listeners.Dispatch(new ConfigChangedEvent());
 
633
                }
 
634
 
 
635
                private string GetRawString(string section, string subsection, string name)
 
636
                {
 
637
                        string[] lst = GetRawStringList(section, subsection, name);
 
638
                        if (lst != null)
 
639
                        {
 
640
                                return lst[0];
 
641
                        }
 
642
                        else
 
643
                        {
 
644
                                if (baseConfig != null)
 
645
                                {
 
646
                                        return baseConfig.GetRawString(section, subsection, name);
 
647
                                }
 
648
                                else
 
649
                                {
 
650
                                        return null;
 
651
                                }
 
652
                        }
 
653
                }
 
654
 
 
655
                private string[] GetRawStringList(string section, string subsection, string name)
 
656
                {
 
657
                        return state.Get().Get(section, subsection, name);
 
658
                }
 
659
 
 
660
                private ConfigSnapshot GetState()
 
661
                {
 
662
                        ConfigSnapshot cur;
 
663
                        ConfigSnapshot upd;
 
664
                        do
 
665
                        {
 
666
                                cur = state.Get();
 
667
                                ConfigSnapshot @base = GetBaseState();
 
668
                                if (cur.baseState == @base)
 
669
                                {
 
670
                                        return cur;
 
671
                                }
 
672
                                upd = new ConfigSnapshot(cur.entryList, @base);
 
673
                        }
 
674
                        while (!state.CompareAndSet(cur, upd));
 
675
                        return upd;
 
676
                }
 
677
 
 
678
                private ConfigSnapshot GetBaseState()
 
679
                {
 
680
                        return baseConfig != null ? baseConfig.GetState() : null;
 
681
                }
 
682
 
 
683
                /// <summary>Add or modify a configuration value.</summary>
 
684
                /// <remarks>
 
685
                /// Add or modify a configuration value. The parameters will result in a
 
686
                /// configuration entry like this.
 
687
                /// <pre>
 
688
                /// [section &quot;subsection&quot;]
 
689
                /// name = value
 
690
                /// </pre>
 
691
                /// </remarks>
 
692
                /// <param name="section">section name, e.g "branch"</param>
 
693
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
694
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
695
                /// <param name="value">parameter value</param>
 
696
                public virtual void SetInt(string section, string subsection, string name, int value
 
697
                        )
 
698
                {
 
699
                        SetLong(section, subsection, name, value);
 
700
                }
 
701
 
 
702
                /// <summary>Add or modify a configuration value.</summary>
 
703
                /// <remarks>
 
704
                /// Add or modify a configuration value. The parameters will result in a
 
705
                /// configuration entry like this.
 
706
                /// <pre>
 
707
                /// [section &quot;subsection&quot;]
 
708
                /// name = value
 
709
                /// </pre>
 
710
                /// </remarks>
 
711
                /// <param name="section">section name, e.g "branch"</param>
 
712
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
713
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
714
                /// <param name="value">parameter value</param>
 
715
                public virtual void SetLong(string section, string subsection, string name, long 
 
716
                        value)
 
717
                {
 
718
                        string s;
 
719
                        if (value >= GiB && (value % GiB) == 0)
 
720
                        {
 
721
                                s = (value / GiB).ToString() + " g";
 
722
                        }
 
723
                        else
 
724
                        {
 
725
                                if (value >= MiB && (value % MiB) == 0)
 
726
                                {
 
727
                                        s = (value / MiB).ToString() + " m";
 
728
                                }
 
729
                                else
 
730
                                {
 
731
                                        if (value >= KiB && (value % KiB) == 0)
 
732
                                        {
 
733
                                                s = (value / KiB).ToString() + " k";
 
734
                                        }
 
735
                                        else
 
736
                                        {
 
737
                                                s = value.ToString();
 
738
                                        }
 
739
                                }
 
740
                        }
 
741
                        SetString(section, subsection, name, s);
 
742
                }
 
743
 
 
744
                /// <summary>Add or modify a configuration value.</summary>
 
745
                /// <remarks>
 
746
                /// Add or modify a configuration value. The parameters will result in a
 
747
                /// configuration entry like this.
 
748
                /// <pre>
 
749
                /// [section &quot;subsection&quot;]
 
750
                /// name = value
 
751
                /// </pre>
 
752
                /// </remarks>
 
753
                /// <param name="section">section name, e.g "branch"</param>
 
754
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
755
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
756
                /// <param name="value">parameter value</param>
 
757
                public virtual void SetBoolean(string section, string subsection, string name, bool
 
758
                         value)
 
759
                {
 
760
                        SetString(section, subsection, name, value ? "true" : "false");
 
761
                }
 
762
 
 
763
                /// <summary>Add or modify a configuration value.</summary>
 
764
                /// <remarks>
 
765
                /// Add or modify a configuration value. The parameters will result in a
 
766
                /// configuration entry like this.
 
767
                /// <pre>
 
768
                /// [section &quot;subsection&quot;]
 
769
                /// name = value
 
770
                /// </pre>
 
771
                /// </remarks>
 
772
                /// <?></?>
 
773
                /// <param name="section">section name, e.g "branch"</param>
 
774
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
775
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
776
                /// <param name="value">parameter value</param>
 
777
                public virtual void SetEnum<T>(string section, string subsection, string name, T 
 
778
                        value)
 
779
                {
 
780
                        string n = value.ToString().ToLower().Replace('_', ' ');
 
781
                        SetString(section, subsection, name, n);
 
782
                }
 
783
 
 
784
                /// <summary>Add or modify a configuration value.</summary>
 
785
                /// <remarks>
 
786
                /// Add or modify a configuration value. The parameters will result in a
 
787
                /// configuration entry like this.
 
788
                /// <pre>
 
789
                /// [section &quot;subsection&quot;]
 
790
                /// name = value
 
791
                /// </pre>
 
792
                /// </remarks>
 
793
                /// <param name="section">section name, e.g "branch"</param>
 
794
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
795
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
796
                /// <param name="value">parameter value, e.g. "true"</param>
 
797
                public virtual void SetString(string section, string subsection, string name, string
 
798
                         value)
 
799
                {
 
800
                        SetStringList(section, subsection, name, Sharpen.Collections.SingletonList(value)
 
801
                                );
 
802
                }
 
803
 
 
804
                /// <summary>Remove a configuration value.</summary>
 
805
                /// <remarks>Remove a configuration value.</remarks>
 
806
                /// <param name="section">section name, e.g "branch"</param>
 
807
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
808
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
809
                public virtual void Unset(string section, string subsection, string name)
 
810
                {
 
811
                        SetStringList(section, subsection, name, Sharpen.Collections.EmptyList<string>());
 
812
                }
 
813
 
 
814
                /// <summary>Remove all configuration values under a single section.</summary>
 
815
                /// <remarks>Remove all configuration values under a single section.</remarks>
 
816
                /// <param name="section">section name, e.g "branch"</param>
 
817
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
818
                public virtual void UnsetSection(string section, string subsection)
 
819
                {
 
820
                        ConfigSnapshot src;
 
821
                        ConfigSnapshot res;
 
822
                        do
 
823
                        {
 
824
                                src = state.Get();
 
825
                                res = UnsetSection(src, section, subsection);
 
826
                        }
 
827
                        while (!state.CompareAndSet(src, res));
 
828
                }
 
829
 
 
830
                private ConfigSnapshot UnsetSection(ConfigSnapshot srcState, string section, string
 
831
                         subsection)
 
832
                {
 
833
                        int max = srcState.entryList.Count;
 
834
                        AList<ConfigLine> r = new AList<ConfigLine>(max);
 
835
                        bool lastWasMatch = false;
 
836
                        foreach (ConfigLine e in srcState.entryList)
 
837
                        {
 
838
                                if (e.Match(section, subsection))
 
839
                                {
 
840
                                        // Skip this record, it's for the section we are removing.
 
841
                                        lastWasMatch = true;
 
842
                                        continue;
 
843
                                }
 
844
                                if (lastWasMatch && e.section == null && e.subsection == null)
 
845
                                {
 
846
                                        continue;
 
847
                                }
 
848
                                // skip this padding line in the section.
 
849
                                r.AddItem(e);
 
850
                        }
 
851
                        return NewState(r);
 
852
                }
 
853
 
 
854
                /// <summary>Set a configuration value.</summary>
 
855
                /// <remarks>
 
856
                /// Set a configuration value.
 
857
                /// <pre>
 
858
                /// [section &quot;subsection&quot;]
 
859
                /// name = value
 
860
                /// </pre>
 
861
                /// </remarks>
 
862
                /// <param name="section">section name, e.g "branch"</param>
 
863
                /// <param name="subsection">optional subsection value, e.g. a branch name</param>
 
864
                /// <param name="name">parameter name, e.g. "filemode"</param>
 
865
                /// <param name="values">list of zero or more values for this key.</param>
 
866
                public virtual void SetStringList(string section, string subsection, string name, 
 
867
                        IList<string> values)
 
868
                {
 
869
                        ConfigSnapshot src;
 
870
                        ConfigSnapshot res;
 
871
                        do
 
872
                        {
 
873
                                src = state.Get();
 
874
                                res = ReplaceStringList(src, section, subsection, name, values);
 
875
                        }
 
876
                        while (!state.CompareAndSet(src, res));
 
877
                        if (NotifyUponTransientChanges())
 
878
                        {
 
879
                                FireConfigChangedEvent();
 
880
                        }
 
881
                }
 
882
 
 
883
                private ConfigSnapshot ReplaceStringList(ConfigSnapshot srcState, string section, 
 
884
                        string subsection, string name, IList<string> values)
 
885
                {
 
886
                        IList<ConfigLine> entries = Copy(srcState, values);
 
887
                        int entryIndex = 0;
 
888
                        int valueIndex = 0;
 
889
                        int insertPosition = -1;
 
890
                        // Reset the first n Entry objects that match this input name.
 
891
                        //
 
892
                        while (entryIndex < entries.Count && valueIndex < values.Count)
 
893
                        {
 
894
                                ConfigLine e = entries[entryIndex];
 
895
                                if (e.Match(section, subsection, name))
 
896
                                {
 
897
                                        entries.Set(entryIndex, e.ForValue(values[valueIndex++]));
 
898
                                        insertPosition = entryIndex + 1;
 
899
                                }
 
900
                                entryIndex++;
 
901
                        }
 
902
                        // Remove any extra Entry objects that we no longer need.
 
903
                        //
 
904
                        if (valueIndex == values.Count && entryIndex < entries.Count)
 
905
                        {
 
906
                                while (entryIndex < entries.Count)
 
907
                                {
 
908
                                        ConfigLine e = entries[entryIndex++];
 
909
                                        if (e.Match(section, subsection, name))
 
910
                                        {
 
911
                                                entries.Remove(--entryIndex);
 
912
                                        }
 
913
                                }
 
914
                        }
 
915
                        // Insert new Entry objects for additional/new values.
 
916
                        //
 
917
                        if (valueIndex < values.Count && entryIndex == entries.Count)
 
918
                        {
 
919
                                if (insertPosition < 0)
 
920
                                {
 
921
                                        // We didn't find a matching key above, but maybe there
 
922
                                        // is already a section available that matches. Insert
 
923
                                        // after the last key of that section.
 
924
                                        //
 
925
                                        insertPosition = FindSectionEnd(entries, section, subsection);
 
926
                                }
 
927
                                if (insertPosition < 0)
 
928
                                {
 
929
                                        // We didn't find any matching section header for this key,
 
930
                                        // so we must create a new section header at the end.
 
931
                                        //
 
932
                                        ConfigLine e = new ConfigLine();
 
933
                                        e.section = section;
 
934
                                        e.subsection = subsection;
 
935
                                        entries.AddItem(e);
 
936
                                        insertPosition = entries.Count;
 
937
                                }
 
938
                                while (valueIndex < values.Count)
 
939
                                {
 
940
                                        ConfigLine e = new ConfigLine();
 
941
                                        e.section = section;
 
942
                                        e.subsection = subsection;
 
943
                                        e.name = name;
 
944
                                        e.value = values[valueIndex++];
 
945
                                        entries.Add(insertPosition++, e);
 
946
                                }
 
947
                        }
 
948
                        return NewState(entries);
 
949
                }
 
950
 
 
951
                private static IList<ConfigLine> Copy(ConfigSnapshot src, IList<string> values)
 
952
                {
 
953
                        // At worst we need to insert 1 line for each value, plus 1 line
 
954
                        // for a new section header. Assume that and allocate the space.
 
955
                        //
 
956
                        int max = src.entryList.Count + values.Count + 1;
 
957
                        AList<ConfigLine> r = new AList<ConfigLine>(max);
 
958
                        Sharpen.Collections.AddAll(r, src.entryList);
 
959
                        return r;
 
960
                }
 
961
 
 
962
                private static int FindSectionEnd(IList<ConfigLine> entries, string section, string
 
963
                         subsection)
 
964
                {
 
965
                        for (int i = 0; i < entries.Count; i++)
 
966
                        {
 
967
                                ConfigLine e = entries[i];
 
968
                                if (e.Match(section, subsection, null))
 
969
                                {
 
970
                                        i++;
 
971
                                        while (i < entries.Count)
 
972
                                        {
 
973
                                                e = entries[i];
 
974
                                                if (e.Match(section, subsection, e.name))
 
975
                                                {
 
976
                                                        i++;
 
977
                                                }
 
978
                                                else
 
979
                                                {
 
980
                                                        break;
 
981
                                                }
 
982
                                        }
 
983
                                        return i;
 
984
                                }
 
985
                        }
 
986
                        return -1;
 
987
                }
 
988
 
 
989
                /// <returns>this configuration, formatted as a Git style text file.</returns>
 
990
                public virtual string ToText()
 
991
                {
 
992
                        StringBuilder @out = new StringBuilder();
 
993
                        foreach (ConfigLine e in state.Get().entryList)
 
994
                        {
 
995
                                if (e.prefix != null)
 
996
                                {
 
997
                                        @out.Append(e.prefix);
 
998
                                }
 
999
                                if (e.section != null && e.name == null)
 
1000
                                {
 
1001
                                        @out.Append('[');
 
1002
                                        @out.Append(e.section);
 
1003
                                        if (e.subsection != null)
 
1004
                                        {
 
1005
                                                @out.Append(' ');
 
1006
                                                string escaped = EscapeValue(e.subsection);
 
1007
                                                // make sure to avoid double quotes here
 
1008
                                                bool quoted = escaped.StartsWith("\"") && escaped.EndsWith("\"");
 
1009
                                                if (!quoted)
 
1010
                                                {
 
1011
                                                        @out.Append('"');
 
1012
                                                }
 
1013
                                                @out.Append(escaped);
 
1014
                                                if (!quoted)
 
1015
                                                {
 
1016
                                                        @out.Append('"');
 
1017
                                                }
 
1018
                                        }
 
1019
                                        @out.Append(']');
 
1020
                                }
 
1021
                                else
 
1022
                                {
 
1023
                                        if (e.section != null && e.name != null)
 
1024
                                        {
 
1025
                                                if (e.prefix == null || string.Empty.Equals(e.prefix))
 
1026
                                                {
 
1027
                                                        @out.Append('\t');
 
1028
                                                }
 
1029
                                                @out.Append(e.name);
 
1030
                                                if (MAGIC_EMPTY_VALUE != e.value)
 
1031
                                                {
 
1032
                                                        @out.Append(" =");
 
1033
                                                        if (e.value != null)
 
1034
                                                        {
 
1035
                                                                @out.Append(' ');
 
1036
                                                                @out.Append(EscapeValue(e.value));
 
1037
                                                        }
 
1038
                                                }
 
1039
                                                if (e.suffix != null)
 
1040
                                                {
 
1041
                                                        @out.Append(' ');
 
1042
                                                }
 
1043
                                        }
 
1044
                                }
 
1045
                                if (e.suffix != null)
 
1046
                                {
 
1047
                                        @out.Append(e.suffix);
 
1048
                                }
 
1049
                                @out.Append('\n');
 
1050
                        }
 
1051
                        return @out.ToString();
 
1052
                }
 
1053
 
 
1054
                /// <summary>Clear this configuration and reset to the contents of the parsed string.
 
1055
                ///     </summary>
 
1056
                /// <remarks>Clear this configuration and reset to the contents of the parsed string.
 
1057
                ///     </remarks>
 
1058
                /// <param name="text">Git style text file listing configuration properties.</param>
 
1059
                /// <exception cref="NGit.Errors.ConfigInvalidException">
 
1060
                /// the text supplied is not formatted correctly. No changes were
 
1061
                /// made to
 
1062
                /// <code>this</code>
 
1063
                /// .
 
1064
                /// </exception>
 
1065
                public virtual void FromText(string text)
 
1066
                {
 
1067
                        IList<ConfigLine> newEntries = new AList<ConfigLine>();
 
1068
                        Config.StringReader @in = new Config.StringReader(text);
 
1069
                        ConfigLine last = null;
 
1070
                        ConfigLine e = new ConfigLine();
 
1071
                        for (; ; )
 
1072
                        {
 
1073
                                int input = @in.Read();
 
1074
                                if (-1 == input)
 
1075
                                {
 
1076
                                        break;
 
1077
                                }
 
1078
                                char c = (char)input;
 
1079
                                if ('\n' == c)
 
1080
                                {
 
1081
                                        // End of this entry.
 
1082
                                        newEntries.AddItem(e);
 
1083
                                        if (e.section != null)
 
1084
                                        {
 
1085
                                                last = e;
 
1086
                                        }
 
1087
                                        e = new ConfigLine();
 
1088
                                }
 
1089
                                else
 
1090
                                {
 
1091
                                        if (e.suffix != null)
 
1092
                                        {
 
1093
                                                // Everything up until the end-of-line is in the suffix.
 
1094
                                                e.suffix += c;
 
1095
                                        }
 
1096
                                        else
 
1097
                                        {
 
1098
                                                if (';' == c || '#' == c)
 
1099
                                                {
 
1100
                                                        // The rest of this line is a comment; put into suffix.
 
1101
                                                        e.suffix = c.ToString();
 
1102
                                                }
 
1103
                                                else
 
1104
                                                {
 
1105
                                                        if (e.section == null && char.IsWhiteSpace(c))
 
1106
                                                        {
 
1107
                                                                // Save the leading whitespace (if any).
 
1108
                                                                if (e.prefix == null)
 
1109
                                                                {
 
1110
                                                                        e.prefix = string.Empty;
 
1111
                                                                }
 
1112
                                                                e.prefix += c;
 
1113
                                                        }
 
1114
                                                        else
 
1115
                                                        {
 
1116
                                                                if ('[' == c)
 
1117
                                                                {
 
1118
                                                                        // This is a section header.
 
1119
                                                                        e.section = ReadSectionName(@in);
 
1120
                                                                        input = @in.Read();
 
1121
                                                                        if ('"' == input)
 
1122
                                                                        {
 
1123
                                                                                e.subsection = ReadValue(@in, true, '"');
 
1124
                                                                                input = @in.Read();
 
1125
                                                                        }
 
1126
                                                                        if (']' != input)
 
1127
                                                                        {
 
1128
                                                                                throw new ConfigInvalidException(JGitText.Get().badGroupHeader);
 
1129
                                                                        }
 
1130
                                                                        e.suffix = string.Empty;
 
1131
                                                                }
 
1132
                                                                else
 
1133
                                                                {
 
1134
                                                                        if (last != null)
 
1135
                                                                        {
 
1136
                                                                                // Read a value.
 
1137
                                                                                e.section = last.section;
 
1138
                                                                                e.subsection = last.subsection;
 
1139
                                                                                @in.Reset();
 
1140
                                                                                e.name = ReadKeyName(@in);
 
1141
                                                                                if (e.name.EndsWith("\n"))
 
1142
                                                                                {
 
1143
                                                                                        e.name = Sharpen.Runtime.Substring(e.name, 0, e.name.Length - 1);
 
1144
                                                                                        e.value = MAGIC_EMPTY_VALUE;
 
1145
                                                                                }
 
1146
                                                                                else
 
1147
                                                                                {
 
1148
                                                                                        e.value = ReadValue(@in, false, -1);
 
1149
                                                                                }
 
1150
                                                                        }
 
1151
                                                                        else
 
1152
                                                                        {
 
1153
                                                                                throw new ConfigInvalidException(JGitText.Get().invalidLineInConfigFile);
 
1154
                                                                        }
 
1155
                                                                }
 
1156
                                                        }
 
1157
                                                }
 
1158
                                        }
 
1159
                                }
 
1160
                        }
 
1161
                        state.Set(NewState(newEntries));
 
1162
                }
 
1163
 
 
1164
                private ConfigSnapshot NewState()
 
1165
                {
 
1166
                        return new ConfigSnapshot(Sharpen.Collections.EmptyList<ConfigLine>(), GetBaseState
 
1167
                                ());
 
1168
                }
 
1169
 
 
1170
                private ConfigSnapshot NewState(IList<ConfigLine> entries)
 
1171
                {
 
1172
                        return new ConfigSnapshot(Sharpen.Collections.UnmodifiableList(entries), GetBaseState
 
1173
                                ());
 
1174
                }
 
1175
 
 
1176
                /// <summary>Clear the configuration file</summary>
 
1177
                protected internal virtual void Clear()
 
1178
                {
 
1179
                        state.Set(NewState());
 
1180
                }
 
1181
 
 
1182
                /// <exception cref="NGit.Errors.ConfigInvalidException"></exception>
 
1183
                private static string ReadSectionName(Config.StringReader @in)
 
1184
                {
 
1185
                        StringBuilder name = new StringBuilder();
 
1186
                        for (; ; )
 
1187
                        {
 
1188
                                int c = @in.Read();
 
1189
                                if (c < 0)
 
1190
                                {
 
1191
                                        throw new ConfigInvalidException(JGitText.Get().unexpectedEndOfConfigFile);
 
1192
                                }
 
1193
                                if (']' == c)
 
1194
                                {
 
1195
                                        @in.Reset();
 
1196
                                        break;
 
1197
                                }
 
1198
                                if (' ' == c || '\t' == c)
 
1199
                                {
 
1200
                                        for (; ; )
 
1201
                                        {
 
1202
                                                c = @in.Read();
 
1203
                                                if (c < 0)
 
1204
                                                {
 
1205
                                                        throw new ConfigInvalidException(JGitText.Get().unexpectedEndOfConfigFile);
 
1206
                                                }
 
1207
                                                if ('"' == c)
 
1208
                                                {
 
1209
                                                        @in.Reset();
 
1210
                                                        break;
 
1211
                                                }
 
1212
                                                if (' ' == c || '\t' == c)
 
1213
                                                {
 
1214
                                                        continue;
 
1215
                                                }
 
1216
                                                // Skipped...
 
1217
                                                throw new ConfigInvalidException(MessageFormat.Format(JGitText.Get().badSectionEntry
 
1218
                                                        , name));
 
1219
                                        }
 
1220
                                        break;
 
1221
                                }
 
1222
                                if (char.IsLetterOrDigit((char)c) || '.' == c || '-' == c)
 
1223
                                {
 
1224
                                        name.Append((char)c);
 
1225
                                }
 
1226
                                else
 
1227
                                {
 
1228
                                        throw new ConfigInvalidException(MessageFormat.Format(JGitText.Get().badSectionEntry
 
1229
                                                , name));
 
1230
                                }
 
1231
                        }
 
1232
                        return name.ToString();
 
1233
                }
 
1234
 
 
1235
                /// <exception cref="NGit.Errors.ConfigInvalidException"></exception>
 
1236
                private static string ReadKeyName(Config.StringReader @in)
 
1237
                {
 
1238
                        StringBuilder name = new StringBuilder();
 
1239
                        for (; ; )
 
1240
                        {
 
1241
                                int c = @in.Read();
 
1242
                                if (c < 0)
 
1243
                                {
 
1244
                                        throw new ConfigInvalidException(JGitText.Get().unexpectedEndOfConfigFile);
 
1245
                                }
 
1246
                                if ('=' == c)
 
1247
                                {
 
1248
                                        break;
 
1249
                                }
 
1250
                                if (' ' == c || '\t' == c)
 
1251
                                {
 
1252
                                        for (; ; )
 
1253
                                        {
 
1254
                                                c = @in.Read();
 
1255
                                                if (c < 0)
 
1256
                                                {
 
1257
                                                        throw new ConfigInvalidException(JGitText.Get().unexpectedEndOfConfigFile);
 
1258
                                                }
 
1259
                                                if ('=' == c)
 
1260
                                                {
 
1261
                                                        break;
 
1262
                                                }
 
1263
                                                if (';' == c || '#' == c || '\n' == c)
 
1264
                                                {
 
1265
                                                        @in.Reset();
 
1266
                                                        break;
 
1267
                                                }
 
1268
                                                if (' ' == c || '\t' == c)
 
1269
                                                {
 
1270
                                                        continue;
 
1271
                                                }
 
1272
                                                // Skipped...
 
1273
                                                throw new ConfigInvalidException(JGitText.Get().badEntryDelimiter);
 
1274
                                        }
 
1275
                                        break;
 
1276
                                }
 
1277
                                if (char.IsLetterOrDigit((char)c) || c == '-')
 
1278
                                {
 
1279
                                        // From the git-config man page:
 
1280
                                        // The variable names are case-insensitive and only
 
1281
                                        // alphanumeric characters and - are allowed.
 
1282
                                        name.Append((char)c);
 
1283
                                }
 
1284
                                else
 
1285
                                {
 
1286
                                        if ('\n' == c)
 
1287
                                        {
 
1288
                                                @in.Reset();
 
1289
                                                name.Append((char)c);
 
1290
                                                break;
 
1291
                                        }
 
1292
                                        else
 
1293
                                        {
 
1294
                                                throw new ConfigInvalidException(MessageFormat.Format(JGitText.Get().badEntryName
 
1295
                                                        , name));
 
1296
                                        }
 
1297
                                }
 
1298
                        }
 
1299
                        return name.ToString();
 
1300
                }
 
1301
 
 
1302
                /// <exception cref="NGit.Errors.ConfigInvalidException"></exception>
 
1303
                private static string ReadValue(Config.StringReader @in, bool quote, int eol)
 
1304
                {
 
1305
                        StringBuilder value = new StringBuilder();
 
1306
                        bool space = false;
 
1307
                        for (; ; )
 
1308
                        {
 
1309
                                int c = @in.Read();
 
1310
                                if (c < 0)
 
1311
                                {
 
1312
                                        if (value.Length == 0)
 
1313
                                        {
 
1314
                                                throw new ConfigInvalidException(JGitText.Get().unexpectedEndOfConfigFile);
 
1315
                                        }
 
1316
                                        break;
 
1317
                                }
 
1318
                                if ('\n' == c)
 
1319
                                {
 
1320
                                        if (quote)
 
1321
                                        {
 
1322
                                                throw new ConfigInvalidException(JGitText.Get().newlineInQuotesNotAllowed);
 
1323
                                        }
 
1324
                                        @in.Reset();
 
1325
                                        break;
 
1326
                                }
 
1327
                                if (eol == c)
 
1328
                                {
 
1329
                                        break;
 
1330
                                }
 
1331
                                if (!quote)
 
1332
                                {
 
1333
                                        if (char.IsWhiteSpace((char)c))
 
1334
                                        {
 
1335
                                                space = true;
 
1336
                                                continue;
 
1337
                                        }
 
1338
                                        if (';' == c || '#' == c)
 
1339
                                        {
 
1340
                                                @in.Reset();
 
1341
                                                break;
 
1342
                                        }
 
1343
                                }
 
1344
                                if (space)
 
1345
                                {
 
1346
                                        if (value.Length > 0)
 
1347
                                        {
 
1348
                                                value.Append(' ');
 
1349
                                        }
 
1350
                                        space = false;
 
1351
                                }
 
1352
                                if ('\\' == c)
 
1353
                                {
 
1354
                                        c = @in.Read();
 
1355
                                        switch (c)
 
1356
                                        {
 
1357
                                                case -1:
 
1358
                                                {
 
1359
                                                        throw new ConfigInvalidException(JGitText.Get().endOfFileInEscape);
 
1360
                                                }
 
1361
 
 
1362
                                                case '\n':
 
1363
                                                {
 
1364
                                                        continue;
 
1365
                                                        goto case 't';
 
1366
                                                }
 
1367
 
 
1368
                                                case 't':
 
1369
                                                {
 
1370
                                                        value.Append('\t');
 
1371
                                                        continue;
 
1372
                                                        goto case 'b';
 
1373
                                                }
 
1374
 
 
1375
                                                case 'b':
 
1376
                                                {
 
1377
                                                        value.Append('\b');
 
1378
                                                        continue;
 
1379
                                                        goto case 'n';
 
1380
                                                }
 
1381
 
 
1382
                                                case 'n':
 
1383
                                                {
 
1384
                                                        value.Append('\n');
 
1385
                                                        continue;
 
1386
                                                        goto case '\\';
 
1387
                                                }
 
1388
 
 
1389
                                                case '\\':
 
1390
                                                {
 
1391
                                                        value.Append('\\');
 
1392
                                                        continue;
 
1393
                                                        goto case '"';
 
1394
                                                }
 
1395
 
 
1396
                                                case '"':
 
1397
                                                {
 
1398
                                                        value.Append('"');
 
1399
                                                        continue;
 
1400
                                                        goto default;
 
1401
                                                }
 
1402
 
 
1403
                                                default:
 
1404
                                                {
 
1405
                                                        throw new ConfigInvalidException(MessageFormat.Format(JGitText.Get().badEscape, (
 
1406
                                                                (char)c)));
 
1407
                                                }
 
1408
                                        }
 
1409
                                }
 
1410
                                if ('"' == c)
 
1411
                                {
 
1412
                                        quote = !quote;
 
1413
                                        continue;
 
1414
                                }
 
1415
                                value.Append((char)c);
 
1416
                        }
 
1417
                        return value.Length > 0 ? value.ToString() : null;
 
1418
                }
 
1419
 
 
1420
                /// <summary>Parses a section of the configuration into an application model object.</summary>
 
1421
                /// <remarks>
 
1422
                /// Parses a section of the configuration into an application model object.
 
1423
                /// <p>
 
1424
                /// Instances must implement hashCode and equals such that model objects can
 
1425
                /// be cached by using the
 
1426
                /// <code>SectionParser</code>
 
1427
                /// as a key of a HashMap.
 
1428
                /// <p>
 
1429
                /// As the
 
1430
                /// <code>SectionParser</code>
 
1431
                /// itself is used as the key of the internal
 
1432
                /// HashMap applications should be careful to ensure the SectionParser key
 
1433
                /// does not retain unnecessary application state which may cause memory to
 
1434
                /// be held longer than expected.
 
1435
                /// </remarks>
 
1436
                /// <?></?>
 
1437
                public interface SectionParser<T>
 
1438
                {
 
1439
                        /// <summary>Create a model object from a configuration.</summary>
 
1440
                        /// <remarks>Create a model object from a configuration.</remarks>
 
1441
                        /// <param name="cfg">the configuration to read values from.</param>
 
1442
                        /// <returns>the application model instance.</returns>
 
1443
                        T Parse(Config cfg);
 
1444
                }
 
1445
 
 
1446
                private class StringReader
 
1447
                {
 
1448
                        private readonly char[] buf;
 
1449
 
 
1450
                        private int pos;
 
1451
 
 
1452
                        internal StringReader(string @in)
 
1453
                        {
 
1454
                                buf = @in.ToCharArray();
 
1455
                        }
 
1456
 
 
1457
                        internal virtual int Read()
 
1458
                        {
 
1459
                                try
 
1460
                                {
 
1461
                                        return buf[pos++];
 
1462
                                }
 
1463
                                catch (IndexOutOfRangeException)
 
1464
                                {
 
1465
                                        pos = buf.Length;
 
1466
                                        return -1;
 
1467
                                }
 
1468
                        }
 
1469
 
 
1470
                        internal virtual void Reset()
 
1471
                        {
 
1472
                                pos--;
 
1473
                        }
 
1474
                }
 
1475
        }
 
1476
}