< Summary

Information
Class: Ice.Properties.PropertyValue
Assembly: Ice
File(s): /_/csharp/src/Ice/Properties.cs
Tag: 125_37167941578
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 881
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 2
Fully covered methods: 2
Total methods: 2
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Clone()100%11100%

File(s)

/_/csharp/src/Ice/Properties.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3#nullable enable
 4
 5using Ice.Internal;
 6using System.Collections.Immutable;
 7using System.ComponentModel;
 8using System.Globalization;
 9using System.Text;
 10using System.Text.RegularExpressions;
 11
 12namespace Ice;
 13
 14/// <summary>
 15/// Represents a set of properties used to configure Ice and Ice-based applications. A property is a key/value pair,
 16/// where both the key and the value are strings. By convention, property keys should have the form
 17/// <c>application-name[.category[.sub-category]].name</c>.
 18/// </summary>
 19/// <remarks>This class is thread-safe: multiple threads can safely read and write the properties.</remarks>
 20public sealed class Properties
 21{
 22    private readonly Dictionary<string, PropertyValue> _propertySet = [];
 23    private readonly ImmutableList<string> _optInPrefixes = [];
 24    private readonly object _mutex = new(); // protects _propertySet
 25
 26    /// <summary>
 27    /// Initializes a new instance of the <see cref="Properties" /> class. The property set is initially empty.
 28    /// </summary>
 29    public Properties()
 30    {
 31    }
 32
 33    /// <summary>
 34    /// Initializes a new instance of the <see cref="Properties" /> class, loads the configuration files specified by
 35    /// the <c>Ice.Config</c> property or the <c>ICE_CONFIG</c> environment variable, and then parses Ice properties
 36    /// from <paramref name="args" />.
 37    /// </summary>
 38    /// <param name="args">The command-line arguments. This constructor parses arguments starting with <c>--</c> and one
 39    /// of the reserved prefixes (Ice, IceSSL, etc.) as properties and removes these elements from the list. If there is
 40    /// an argument starting with <c>--Ice.Config</c>, this constructor loads the specified configuration file. When the
 41    /// same property is set in a configuration file and through a command-line argument, the command-line setting takes
 42    /// precedence.</param>
 43    /// <param name="defaults">Default values for the new Properties object. Settings in configuration files and the
 44    /// arguments override these defaults.</param>
 45    /// <remarks>This constructor loads properties from files specified by the <c>ICE_CONFIG</c> environment variable
 46    /// when there is no <c>--Ice.Config</c> command-line argument.</remarks>
 47    public Properties(ref string[] args, Properties? defaults = null)
 48        : this(defaults) =>
 49        loadArgs(ref args);
 50
 51    /// <summary>
 52    /// Initializes a new instance of the <see cref="Properties" /> class, loads the configuration files specified by
 53    /// the <c>Ice.Config</c> property or the <c>ICE_CONFIG</c> environment variable, and then parses Ice properties
 54    /// from <paramref name="args" />.
 55    /// </summary>
 56    /// <param name="args">The command-line arguments. This constructor parses arguments starting with <c>--</c> and one
 57    /// of the reserved prefixes (Ice, IceSSL, etc.) as properties and removes these elements from the list. If there is
 58    /// an argument starting with <c>--Ice.Config</c>, this constructor loads the specified configuration file. When the
 59    /// same property is set in a configuration file and through a command-line argument, the command-line setting takes
 60    /// precedence.</param>
 61    /// <param name="optInPrefixes">Optional reserved prefixes to enable in this new Properties object.</param>
 62    /// <remarks>This constructor loads properties from files specified by the <c>ICE_CONFIG</c> environment variable
 63    /// when there is no <c>--Ice.Config</c> command-line argument.</remarks>
 64    [EditorBrowsable(EditorBrowsableState.Never)] // hidden because optInPrefixes is only for internal use in C#
 65    public Properties(ref string[] args, ImmutableList<string> optInPrefixes)
 66    {
 67        _optInPrefixes = optInPrefixes;
 68        loadArgs(ref args);
 69    }
 70
 71    /// <summary>
 72    /// Get a property by key.
 73    /// If the property is not set, an empty string is returned.
 74    /// </summary>
 75    /// <param name="key">The property key.</param>
 76    /// <returns>The property value.</returns>
 77    public string getProperty(string key)
 78    {
 79        lock (_mutex)
 80        {
 81            string result = "";
 82            if (_propertySet.TryGetValue(key, out PropertyValue? pv))
 83            {
 84                pv.used = true;
 85                result = pv.value;
 86            }
 87            return result;
 88        }
 89    }
 90
 91    /// <summary>
 92    /// Get an Ice property by key.
 93    /// If the property is not set, its default value is returned.
 94    /// </summary>
 95    /// <param name="key">The property key.</param>
 96    /// <returns>The property value or the default value.</returns>
 97    /// <exception cref="PropertyException">Thrown when the property is not a known Ice property.</exception>
 98    public string getIceProperty(string key) => getPropertyWithDefault(key, getDefaultProperty(key));
 99
 100    /// <summary>
 101    /// Get a property by key.
 102    /// If the property is not set, the given default value is returned.
 103    /// </summary>
 104    /// <param name="key">The property key.</param>
 105    /// <param name="value">The default value to use if the property does not exist.</param>
 106    /// <returns>The property value or the default value.</returns>
 107    public string getPropertyWithDefault(string key, string value)
 108    {
 109        lock (_mutex)
 110        {
 111            string result = value;
 112            if (_propertySet.TryGetValue(key, out PropertyValue? pv))
 113            {
 114                pv.used = true;
 115                result = pv.value;
 116            }
 117            return result;
 118        }
 119    }
 120
 121    /// <summary>
 122    /// Get a property as an integer.
 123    /// If the property is not set, 0 is returned.
 124    /// </summary>
 125    /// <param name="key">The property key.</param>
 126    /// <returns>The property value interpreted as an integer.</returns>
 127    /// <exception cref="PropertyException">Thrown when the property value is not a valid integer.</exception>
 128    public int getPropertyAsInt(string key) => getPropertyAsIntWithDefault(key, 0);
 129
 130    /// <summary>
 131    /// Get an Ice property as an integer.
 132    /// If the property is not set, its default value is returned.
 133    /// </summary>
 134    /// <param name="key">The property key.</param>
 135    /// <returns>The property value interpreted as an integer, or the default value.</returns>
 136    /// <exception cref="PropertyException">Thrown when the property is not a known Ice property or the value is not a
 137    /// valid integer.</exception>
 138    public int getIcePropertyAsInt(string key)
 139    {
 140        string defaultValueString = getDefaultProperty(key);
 141        int defaultValue = 0;
 142        if (defaultValueString.Length > 0)
 143        {
 144            defaultValue = int.Parse(defaultValueString, CultureInfo.InvariantCulture);
 145        }
 146
 147        return getPropertyAsIntWithDefault(key, defaultValue);
 148    }
 149
 150    /// <summary>
 151    /// Get a property as an integer.
 152    /// If the property is not set, the given default value is returned.
 153    /// </summary>
 154    /// <param name="key">The property key.</param>
 155    /// <param name="value">The default value to use if the property does not exist.</param>
 156    /// <returns>The property value interpreted as an integer, or the default value.</returns>
 157    /// <exception cref="PropertyException">Thrown when the property value is not a valid integer.</exception>
 158    public int getPropertyAsIntWithDefault(string key, int value)
 159    {
 160        lock (_mutex)
 161        {
 162            if (!_propertySet.TryGetValue(key, out PropertyValue? pv))
 163            {
 164                return value;
 165            }
 166            pv.used = true;
 167            try
 168            {
 169                return int.Parse(pv.value, CultureInfo.InvariantCulture);
 170            }
 171            catch (System.Exception ex) when (ex is System.FormatException or System.OverflowException)
 172            {
 173                throw new PropertyException($"property '{key}' has an invalid integer value: '{pv.value}'");
 174            }
 175        }
 176    }
 177
 178    /// <summary>
 179    /// Get a property as a list of strings.
 180    /// The strings must be separated by whitespace or comma. If the property is
 181    /// not set, an empty list is returned. The strings in the list can contain whitespace and commas if they are
 182    /// enclosed in single or double quotes. If quotes are mismatched, an empty list is returned. Within single quotes
 183    /// or double quotes, you can escape the quote in question with a backslash, e.g. O'Reilly can be written as
 184    /// "O'Reilly" or 'O\'Reilly'.
 185    /// </summary>
 186    /// <param name="key">The property key.</param>
 187    /// <returns>The property value interpreted as a list of strings.</returns>
 188    public string[] getPropertyAsList(string key) => getPropertyAsListWithDefault(key, []);
 189
 190    /// <summary>
 191    /// Get an Ice property as a list of strings.
 192    /// The strings must be separated by whitespace or comma. If the property is
 193    /// not set, its default list is returned. The strings in the list can contain whitespace and commas if they are
 194    /// enclosed in single or double quotes. If quotes are mismatched, the default list is returned. Within single
 195    /// quotes or double quotes, you can escape the quote in question with a backslash, e.g. O'Reilly can be written as
 196    /// "O'Reilly" or 'O\'Reilly'.
 197    /// </summary>
 198    /// <param name="key">The property key.</param>
 199    /// <returns>The property value interpreted as list of strings, or the default value.</returns>
 200    /// <exception cref="PropertyException">Thrown when the property is not a known Ice property.</exception>
 201    public string[] getIcePropertyAsList(string key)
 202    {
 203        string[] defaultList = UtilInternal.StringUtil.splitString(getDefaultProperty(key), ", \t\r\n");
 204        return getPropertyAsListWithDefault(key, defaultList);
 205    }
 206
 207    /// <summary>
 208    /// Get a property as a list of strings.
 209    /// The strings must be separated by whitespace or comma. If the property is
 210    /// not set, the default list is returned. The strings in the list can contain whitespace and commas if they are
 211    /// enclosed in single or double quotes. If quotes are mismatched, the default list is returned. Within single
 212    /// quotes or double quotes, you can escape the quote in question with a backslash, e.g. O'Reilly can be written as
 213    /// "O'Reilly" or 'O\'Reilly'.
 214    /// </summary>
 215    /// <param name="key">The property key.</param>
 216    /// <param name="value">The default value to use if the property is not set.</param>
 217    /// <returns>The property value interpreted as list of strings, or the default value.</returns>
 218    public string[] getPropertyAsListWithDefault(string key, string[] value)
 219    {
 220        value ??= [];
 221
 222        lock (_mutex)
 223        {
 224            if (!_propertySet.TryGetValue(key, out PropertyValue? pv))
 225            {
 226                return value;
 227            }
 228
 229            pv.used = true;
 230
 231            string[] result = Ice.UtilInternal.StringUtil.splitString(pv.value, ", \t\r\n");
 232            if (result == null)
 233            {
 234                Util.getProcessLogger().warning(
 235                    $"mismatched quotes in property {key}'s value, returning default value");
 236                return value;
 237            }
 238            return result.Length == 0 ? value : result;
 239        }
 240    }
 241
 242    /// <summary>
 243    /// Get all properties whose keys begin with <paramref name="prefix" />.
 244    /// If <paramref name="prefix" /> is an empty string, then all
 245    /// properties are returned.
 246    /// </summary>
 247    /// <param name="prefix">The prefix to search for (empty string if none).</param>
 248    /// <returns>The matching property set.</returns>
 249    public Dictionary<string, string> getPropertiesForPrefix(string prefix)
 250    {
 251        lock (_mutex)
 252        {
 253            var result = new Dictionary<string, string>();
 254
 255            foreach (string s in _propertySet.Keys)
 256            {
 257                if (prefix.Length == 0 || s.StartsWith(prefix, StringComparison.Ordinal))
 258                {
 259                    PropertyValue pv = _propertySet[s];
 260                    pv.used = true;
 261                    result[s] = pv.value;
 262                }
 263            }
 264            return result;
 265        }
 266    }
 267
 268    /// <summary>
 269    /// Set a property.
 270    /// To unset a property, set it to the empty string.
 271    /// </summary>
 272    /// <param name="key">The property key.</param>
 273    /// <param name="value">The property value.</param>
 274    public void setProperty(string key, string value)
 275    {
 276        //
 277        // Trim whitespace
 278        //
 279        if (key != null)
 280        {
 281            key = key.Trim();
 282        }
 283        if (key == null || key.Length == 0)
 284        {
 285            throw new InitializationException("Attempt to set property with empty key");
 286        }
 287
 288        // Check if the property is in an Ice property prefix. If so, check that it's a valid property.
 289        if (findIcePropertyArray(key) is PropertyArray propertyArray)
 290        {
 291            if (propertyArray.isOptIn && !_optInPrefixes.Contains(propertyArray.name))
 292            {
 293                throw new PropertyException(
 294                    $"Unable to set '{key}': property prefix '{propertyArray.name}' is opt-in and must be explicitly ena
 295            }
 296
 297            Property prop = findProperty(key[(propertyArray.name.Length + 1)..], propertyArray) ??
 298                throw new PropertyException($"unknown Ice property: {key}");
 299
 300            // If the property is deprecated, log a warning.
 301            if (prop.deprecated)
 302            {
 303                Util.getProcessLogger().warning($"setting deprecated property: {key}");
 304            }
 305        }
 306
 307        lock (_mutex)
 308        {
 309            // Set or clear the property.
 310            if (value is not null && value.Length > 0)
 311            {
 312                if (_propertySet.TryGetValue(key, out PropertyValue? pv))
 313                {
 314                    pv.value = value;
 315                }
 316                else
 317                {
 318                    pv = new PropertyValue(value, false);
 319                }
 320                _propertySet[key] = pv;
 321            }
 322            else
 323            {
 324                _propertySet.Remove(key);
 325            }
 326        }
 327    }
 328
 329    /// <summary>
 330    /// Get a sequence of command-line options that is equivalent to this property set.
 331    /// Each element of the returned
 332    /// sequence is a command-line option of the form --key=value.
 333    /// </summary>
 334    /// <returns>The command line options for this property set.</returns>
 335    public string[] getCommandLineOptions()
 336    {
 337        lock (_mutex)
 338        {
 339            string[] result = new string[_propertySet.Count];
 340            int i = 0;
 341            foreach (KeyValuePair<string, PropertyValue> entry in _propertySet)
 342            {
 343                result[i++] = "--" + entry.Key + "=" + entry.Value.value;
 344            }
 345            return result;
 346        }
 347    }
 348
 349    /// <summary>
 350    /// Convert a sequence of command-line options into properties.
 351    /// All options that begin with
 352    /// --prefix. are converted into properties. If the prefix is empty, all options that begin with
 353    /// -- are converted to properties.
 354    /// </summary>
 355    /// <param name="prefix">The property prefix, or an empty string to convert all options starting with --.</param>
 356    /// <param name="options">The command-line options.</param>
 357    /// <returns>The command-line options that do not start with the specified prefix, in their original order.</returns
 358    public string[] parseCommandLineOptions(string prefix, string[] options)
 359    {
 360        if (prefix.Length > 0 && prefix[^1] != '.')
 361        {
 362            prefix += '.';
 363        }
 364        prefix = "--" + prefix;
 365
 366        List<string> result = [];
 367        for (int i = 0; i < options.Length; i++)
 368        {
 369            string opt = options[i];
 370            if (opt.StartsWith(prefix, StringComparison.Ordinal))
 371            {
 372                if (!opt.Contains('=', StringComparison.Ordinal))
 373                {
 374                    opt += "=1";
 375                }
 376
 377                parseLine(opt[2..]);
 378            }
 379            else
 380            {
 381                result.Add(opt);
 382            }
 383        }
 384        string[] arr = new string[result.Count];
 385        if (arr.Length != 0)
 386        {
 387            result.CopyTo(arr);
 388        }
 389        return arr;
 390    }
 391
 392    /// <summary>
 393    /// Convert a sequence of command-line options into properties.
 394    /// All options that begin with one of the reserved Ice prefixes (--Ice, --IceSSL, --IceBox, etc.) are converted
 395    /// into properties.
 396    /// </summary>
 397    /// <param name="options">The command-line options.</param>
 398    /// <returns>The command-line options that do not start with one of the reserved prefixes, in their original
 399    /// order.</returns>
 400    public string[] parseIceCommandLineOptions(string[] options)
 401    {
 402        string[] args = options;
 403        foreach (string? name in PropertyNames.validProps.Select(p => p.name))
 404        {
 405            args = parseCommandLineOptions(name, args);
 406        }
 407        return args;
 408    }
 409
 410    /// <summary>
 411    /// Load properties from a file.
 412    /// </summary>
 413    /// <param name="file">The property file.</param>
 414    public void load(string file)
 415    {
 416        try
 417        {
 418            using var sr = new StreamReader(file);
 419            parse(sr);
 420        }
 421        catch (IOException ex)
 422        {
 423            throw new FileException($"Cannot read '{file}'", ex);
 424        }
 425    }
 426
 427    /// <summary>
 428    /// Create a copy of this property set.
 429    /// </summary>
 430    /// <returns>A copy of this property set.</returns>
 431    public Properties Clone() => new(this);
 432
 433    /// <summary>
 434    /// Alias for <see cref="Clone" />. Provided for source compatibility with Ice 3.7 and earlier versions.
 435    /// </summary>
 436    /// <returns>A copy of this property set.</returns>
 437    [Obsolete("Use Clone instead.")]
 438    public Properties ice_clone_() => Clone();
 439
 440    /// <summary>
 441    /// Get the unused properties in the property set.
 442    /// </summary>
 443    /// <returns>A list containing the names of the unused properties in this property set.</returns>
 444    public List<string> getUnusedProperties()
 445    {
 446        lock (_mutex)
 447        {
 448            var unused = new List<string>();
 449            foreach ((string key, PropertyValue value) in _propertySet)
 450            {
 451                if (!value.used)
 452                {
 453                    unused.Add(key);
 454                }
 455            }
 456            return unused;
 457        }
 458    }
 459
 460    /// <summary>
 461    /// Validate the properties for the given prefix.
 462    /// </summary>
 463    /// <param name="prefix">The property prefix to validate.</param>
 464    /// <param name="properties">The properties to consider. </param>
 465    /// <param name="propertyArray">The property array to search against.</param>
 466    /// <exception cref="PropertyException">Thrown when unknown properties are found.</exception>
 467    internal static void validatePropertiesWithPrefix(
 468        string prefix,
 469        Properties properties,
 470        PropertyArray propertyArray)
 471    {
 472        // Do not check for unknown properties if Ice prefix, ie Ice, Glacier2, etc
 473        foreach (string? name in PropertyNames.validProps.Select(p => p.name))
 474        {
 475            if (prefix.StartsWith($"{name}.", StringComparison.Ordinal))
 476            {
 477                return;
 478            }
 479        }
 480
 481        var unknownProps = new List<string>();
 482        Dictionary<string, string> props = properties.getPropertiesForPrefix($"{prefix}.");
 483
 484        foreach (string p in props.Keys)
 485        {
 486            // Plus one to skip the dot.
 487            if (findProperty(p[(prefix.Length + 1)..], propertyArray) is null)
 488            {
 489                unknownProps.Add(p);
 490            }
 491        }
 492
 493        if (unknownProps.Count > 0)
 494        {
 495            var message = new StringBuilder($"Found unknown properties for {propertyArray.name} : '{prefix}'");
 496            foreach (string s in unknownProps)
 497            {
 498                message.Append("\n    ");
 499                message.Append(s);
 500            }
 501
 502            throw new PropertyException(message.ToString());
 503        }
 504    }
 505
 506    /// <summary>
 507    /// Find the Ice property array for a given property name.
 508    /// </summary>
 509    /// <param name="key">The property key.</param>
 510    /// <returns>The Ice property array if found, else null.</returns>
 511    private static PropertyArray? findIcePropertyArray(string key)
 512    {
 513        int dotPos = key.IndexOf('.', StringComparison.Ordinal);
 514
 515        // If the key doesn't contain a dot, it's not a valid Ice property
 516        if (dotPos == -1)
 517        {
 518            return null;
 519        }
 520
 521        string prefix = key[..dotPos];
 522
 523        // Search for the property list that matches the prefix
 524        return PropertyNames.validProps.FirstOrDefault(properties => prefix == properties.name);
 525    }
 526
 527    /// <summary>
 528    /// Find a property in the property array.
 529    /// </summary>
 530    /// <param name="key">The property key.</param>
 531    /// <param name="propertyArray">The property array to check.</param>
 532    /// <returns>The <see cref="Property" /> if found; otherwise, <see langword="null"/>.</returns>
 533    private static Property? findProperty(string key, PropertyArray propertyArray)
 534    {
 535        foreach (Property prop in propertyArray.properties)
 536        {
 537            // If the key is an exact match, return the property unless it has a property class which is prefix only.
 538            // If the key is a regex match, return the property. A property cannot have a property class and use regex.
 539            if (key == prop.pattern)
 540            {
 541                if (prop.propertyArray is not null && prop.propertyArray.prefixOnly)
 542                {
 543                    return null;
 544                }
 545                return prop;
 546            }
 547            else if (prop.usesRegex && Regex.IsMatch(key, prop.pattern))
 548            {
 549                return prop;
 550            }
 551
 552            // If the property has a property class, check if the key is a prefix of the property.
 553            if (prop.propertyArray is not null)
 554            {
 555                // Check if the key is a prefix of the property.
 556                // The key must be:
 557                // - shorter than the property pattern
 558                // - the property pattern must start with the key
 559                // - the pattern character after the key must be a dot
 560                if (key.Length > prop.pattern.Length && key.StartsWith($"{prop.pattern}.", StringComparison.Ordinal))
 561                {
 562                    // Plus one to skip the dot.
 563                    string substring = key[(prop.pattern.Length + 1)..];
 564
 565                    // Check if the suffix is a valid property. If so, return it. If it's not, continue searching
 566                    // the current property array.
 567                    if (findProperty(substring, prop.propertyArray) is Property subProp)
 568                    {
 569                        return subProp;
 570                    }
 571                }
 572            }
 573        }
 574        return null;
 575    }
 576
 577    /// <summary>
 578    /// Gets the default value for a given Ice property.
 579    /// </summary>
 580    /// <param name="key">The Ice property key.</param>
 581    /// <returns>The default property value, or an empty string if the default is unspecified.</returns>
 582    /// <exception cref="PropertyException">Thrown when the property is not a known Ice property.</exception>
 583    private static string getDefaultProperty(string key)
 584    {
 585        // Find the property, don't log any warnings.
 586        PropertyArray propertyArray = findIcePropertyArray(key) ??
 587            throw new PropertyException($"unknown Ice property: {key}");
 588
 589        // Find the property in the property array.
 590        Property prop = findProperty(key[(propertyArray.name.Length + 1)..], propertyArray) ??
 591            throw new PropertyException($"unknown Ice property: {key}");
 592
 593        return prop.defaultValue;
 594    }
 595
 596    /// <summary>
 597    /// Initializes a new instance of the <see cref="Properties" /> class by copying the given properties.
 598    /// </summary>
 599    /// <param name="defaults">Default values for the new Properties object.</param>
 600    private Properties(Properties? defaults)
 601    {
 602        if (defaults is not null)
 603        {
 604            lock (defaults._mutex)
 605            {
 606                foreach (KeyValuePair<string, PropertyValue> entry in defaults._propertySet)
 607                {
 608                    _propertySet[entry.Key] = entry.Value.Clone();
 609                }
 610            }
 611
 612            _optInPrefixes = defaults._optInPrefixes;
 613        }
 614        // else remains empty
 615    }
 616
 617    // Helper method called exclusively by constructors.
 618    private void loadArgs(ref string[] args)
 619    {
 620        bool loadConfigFiles = false;
 621        List<string> remainingArgs = [];
 622
 623        foreach (string arg in args)
 624        {
 625            if (arg.StartsWith("--Ice.Config", StringComparison.Ordinal))
 626            {
 627                string line = arg;
 628                if (!line.Contains('=', StringComparison.Ordinal))
 629                {
 630                    line += "=1";
 631                }
 632                parseLine(line[2..]);
 633                loadConfigFiles = true;
 634            }
 635            else
 636            {
 637                remainingArgs.Add(arg);
 638            }
 639        }
 640        args = [.. remainingArgs];
 641
 642        if (!loadConfigFiles)
 643        {
 644            // If Ice.Config is not set, load from ICE_CONFIG (if set).
 645            loadConfigFiles = !_propertySet.ContainsKey("Ice.Config");
 646        }
 647
 648        if (loadConfigFiles)
 649        {
 650            loadConfig();
 651        }
 652
 653        args = parseIceCommandLineOptions(args);
 654    }
 655
 656    private void parse(StreamReader input)
 657    {
 658        try
 659        {
 660            string? line;
 661            while ((line = input.ReadLine()) is not null)
 662            {
 663                parseLine(line);
 664            }
 665        }
 666        catch (IOException ex)
 667        {
 668            throw new SyscallException(ex);
 669        }
 670    }
 671
 672    private const int ParseStateKey = 0;
 673    private const int ParseStateValue = 1;
 674
 675    private void parseLine(string line)
 676    {
 677        string key = "";
 678        string val = "";
 679
 680        int state = ParseStateKey;
 681
 682        string whitespace = "";
 683        string escapedSpace = "";
 684        bool finished = false;
 685        for (int i = 0; i < line.Length; ++i)
 686        {
 687            char c = line[i];
 688            switch (state)
 689            {
 690                case ParseStateKey:
 691                {
 692                    switch (c)
 693                    {
 694                        case '\\':
 695                            if (i < line.Length - 1)
 696                            {
 697                                c = line[++i];
 698                                switch (c)
 699                                {
 700                                    case '\\':
 701                                    case '#':
 702                                    case '=':
 703                                        key += whitespace;
 704                                        whitespace = "";
 705                                        key += c;
 706                                        break;
 707
 708                                    case ' ':
 709                                        if (key.Length != 0)
 710                                        {
 711                                            whitespace += c;
 712                                        }
 713                                        break;
 714
 715                                    default:
 716                                        key += whitespace;
 717                                        whitespace = "";
 718                                        key += '\\';
 719                                        key += c;
 720                                        break;
 721                                }
 722                            }
 723                            else
 724                            {
 725                                key += whitespace;
 726                                key += c;
 727                            }
 728                            break;
 729
 730                        case ' ':
 731                        case '\t':
 732                        case '\r':
 733                        case '\n':
 734                            if (key.Length != 0)
 735                            {
 736                                whitespace += c;
 737                            }
 738                            break;
 739
 740                        case '=':
 741                            whitespace = "";
 742                            state = ParseStateValue;
 743                            break;
 744
 745                        case '#':
 746                            finished = true;
 747                            break;
 748
 749                        default:
 750                            key += whitespace;
 751                            whitespace = "";
 752                            key += c;
 753                            break;
 754                    }
 755                    break;
 756                }
 757
 758                case ParseStateValue:
 759                {
 760                    switch (c)
 761                    {
 762                        case '\\':
 763                            if (i < line.Length - 1)
 764                            {
 765                                c = line[++i];
 766                                switch (c)
 767                                {
 768                                    case '\\':
 769                                    case '#':
 770                                    case '=':
 771                                        val += val.Length == 0 ? escapedSpace : whitespace;
 772                                        whitespace = "";
 773                                        escapedSpace = "";
 774                                        val += c;
 775                                        break;
 776
 777                                    case ' ':
 778                                        whitespace += c;
 779                                        escapedSpace += c;
 780                                        break;
 781
 782                                    default:
 783                                        val += val.Length == 0 ? escapedSpace : whitespace;
 784                                        whitespace = "";
 785                                        escapedSpace = "";
 786                                        val += '\\';
 787                                        val += c;
 788                                        break;
 789                                }
 790                            }
 791                            else
 792                            {
 793                                val += val.Length == 0 ? escapedSpace : whitespace;
 794                                val += c;
 795                            }
 796                            break;
 797
 798                        case ' ':
 799                        case '\t':
 800                        case '\r':
 801                        case '\n':
 802                            if (val.Length != 0)
 803                            {
 804                                whitespace += c;
 805                            }
 806                            break;
 807
 808                        case '#':
 809                            finished = true;
 810                            break;
 811
 812                        default:
 813                            val += val.Length == 0 ? escapedSpace : whitespace;
 814                            whitespace = "";
 815                            escapedSpace = "";
 816                            val += c;
 817                            break;
 818                    }
 819                    break;
 820                }
 821            }
 822            if (finished)
 823            {
 824                break;
 825            }
 826        }
 827        val += escapedSpace;
 828
 829        if ((state == ParseStateKey && key.Length != 0) || (state == ParseStateValue && key.Length == 0))
 830        {
 831            Util.getProcessLogger().warning("invalid config file entry: \"" + line + "\"");
 832            return;
 833        }
 834        else if (key.Length == 0)
 835        {
 836            return;
 837        }
 838
 839        setProperty(key, val);
 840    }
 841
 842    private void loadConfig()
 843    {
 844        string val = getIceProperty("Ice.Config");
 845        if (val.Length == 0 || val == "1")
 846        {
 847            string? s = Environment.GetEnvironmentVariable("ICE_CONFIG");
 848            if (s != null && s.Length != 0)
 849            {
 850                val = s;
 851            }
 852        }
 853
 854        if (val.Length > 0)
 855        {
 856            char[] separator = { ',' };
 857            string[] files = val.Split(separator);
 858            for (int i = 0; i < files.Length; i++)
 859            {
 860                load(files[i].Trim());
 861            }
 862
 863            _propertySet["Ice.Config"] = new PropertyValue(val, true);
 864        }
 865    }
 866
 867    private class PropertyValue
 868    {
 869        public string value { get; set; }
 870
 871        public bool used { get; set; }
 872
 1873        public PropertyValue(string value, bool used)
 874        {
 1875            this.value = value;
 1876            this.used = used;
 1877        }
 878
 1879        public PropertyValue Clone() => new(value, used);
 880    }
 881}

Methods/Properties

.ctor(string, bool)
Clone()