/*
 * StringOps.cs --
 *
 * Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: $
 */

using System;
using System.Collections.Generic;
using System.Globalization;

#if NATIVE && WINDOWS
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#endif

using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Eagle._Attributes;
using Eagle._Components.Private.Delegates;
using Eagle._Components.Public;
using Eagle._Constants;

#if DEAD_CODE
using Eagle._Containers.Private;
#endif

using Eagle._Containers.Public;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("517405a1-bb12-4694-b937-30cb46b7c263")]
    internal static class StringOps
    {
        private const int CharMaxAscii = 0x7F;

        private const int Base26GroupsPerLine = 25;

        internal static readonly StringComparison BinaryComparisonType =
            StringComparison.Ordinal;

        internal static readonly StringComparison BinaryNoCaseComparisonType =
            StringComparison.OrdinalIgnoreCase;

        internal static readonly StringComparison SystemStringComparisonType =
            BinaryComparisonType;

        internal static readonly StringComparison SystemNoCaseStringComparisonType =
            BinaryNoCaseComparisonType;

        internal static readonly StringComparison UserStringComparisonType =
            StringComparison.CurrentCulture;

        internal static readonly StringComparison UserNoCaseStringComparisonType =
            StringComparison.CurrentCultureIgnoreCase;

        private static readonly StringComparer DefaultStringComparer =
            StringComparer.CurrentCulture;

        private static readonly string DefaultHashAlgorithm = "SHA512";
        private static readonly string DefaultEncodingName = "utf-8";

        internal static readonly MatchMode DefaultMatchMode = MatchMode.Glob; // COMPAT: Tcl.
        internal static readonly MatchMode DefaultSwitchMatchMode = MatchMode.Exact; // COMPAT: Tcl.
        internal static readonly MatchMode DefaultResultMatchMode = MatchMode.Exact; // COMPAT: Tcl.

        internal static readonly MatchMode DefaultObjectMatchMode = DefaultMatchMode;
        internal static readonly MatchMode DefaultUnloadMatchMode = MatchMode.Exact;

        internal static readonly RegexOptions DefaultRegExOptions = RegexOptions.Singleline; /* COMPAT: Tcl. */

        private const string mixedXpgError = "cannot mix \"%\" and \"%n$\" conversion specifiers";
        private const string OverflowError = "max size for a Tcl value exceeded";

        private const string BinaryPrefix = "0b";
        private const string OctalPrefix = "0";
        private const string HexadecimalPrefix = "0x";

        private static readonly string[] BadIndexError = {
            "not enough arguments for all format specifiers",
            "\"%n$\" argument index out of range"
        };

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static readonly char[] switchChars = {
            Characters.MinusSign, Characters.Slash
        };

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static readonly char[] SubPatternChars = {
            Characters.OpenBrace, Characters.CloseBrace
        };

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // NOTE: Excludes characters covered by PathOps.HasPathWildcard().
        //
        private static readonly char[] StringMatchWildcardChars = {
            Characters.OpenBracket,
            Characters.Backslash,
            Characters.CloseBracket
        };

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Dead Code
#if DEAD_CODE
        private static readonly CharList switchCharList = new CharList(switchChars);
#endif
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static readonly Regex base26RegEx = new Regex(
            "^[A-Z\\s]*$", RegexOptions.IgnoreCase);

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static readonly Regex base64RegEx = new Regex(
            "^[0-9A-Z+/\\s]*={0,2}$", RegexOptions.IgnoreCase);

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // HACK: Calculate the number of bytes that all CLR objects require,
        //       regardless of any other data (fields) that they may contain.
        //
        //       General equation (based on various Internet sources):
        //
        //       SyncBlock (DWORD) + MethodTable (PTR)
        //
        //       Since, by all reports, the initial DWORD is padded for the
        //       64-bit runtime, just use the size of two IntPtr objects.
        //
        //       Given the nature of the CLR, this number is approximate, at
        //       best (and will likely be wrong in subsequent versions).
        //
        private static int ObjectOverhead = (2 * IntPtr.Size); /* 8 or 16 */

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // HACK: Calculate the number of bytes that all CLR String objects
        //       require, regardless of their actual length.
        //
        //       General equation (based on various Internet sources):
        //
        //       ByteLength (DWORD) + CharLength (DWORD)
        //
        //       Given the nature of the CLR, this number is approximate, at
        //       best (and will likely be wrong in subsequent versions).
        //
        private static int StringOverhead = (2 * sizeof(uint)); /* 8 */

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // HACK: Calculate the number of bytes that all CLR StringBuilder
        //       objects require, regardless of their actual length.
        //
        //       General equation (based on various Internet sources):
        //
        //       Thread (PTR) + String (OBJPTR) + MaxCapacity (DWORD)
        //
        //       Given the nature of the CLR, this number is approximate, at
        //       best (and will likely be wrong in subsequent versions).
        //
        private static int StringBuilderOverhead =
            (2 * IntPtr.Size) + (1 * sizeof(uint)); /* 12 or 20 */

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // HACK: How many StringBuilder objects do we want to try and fit on a
        //       single page in memory.  Given the nature of the CLR, this is 
        //       approximate, at best (and will likely be wrong in subsequent
        //       versions).
        //
        private static int StringBuildersPerPage = 32;

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // BUGBUG: This is the default initial capacity for the StringBuilder
        //         objects that we create.  Changing this value can have a
        //         significant impact on the performance of the entire library;
        //         therefore, we should try to figure out the "optimal" value
        //         for it.  Unfortunately, so far, no value has proven to be
        //         ideal in all circumstances; therefore, this field has been
        //         changed from read-only to read-write so that it can be
        //         overridden at runtime [via reflection] as a last resort.
        //
        private static int DefaultCapacity = GetStringBuilderDefaultCapacity();

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static int GetStringBuilderDefaultCapacity()
        {
            return ((int)PlatformOps.GetPageSize() - (StringBuildersPerPage *
                (ObjectOverhead + StringOverhead + StringBuilderOverhead))) /
                (sizeof(char) * StringBuildersPerPage);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string GetStringFromObject(
            object @object
            )
        {
            if (@object is string)
                return (string)@object;

            if (@object is Argument)
                return ((Argument)@object).String;

            if (@object is Result)
                return ((Result)@object).String;

            if (@object != null)
                return @object.ToString();

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder GetStringBuilderFromObject(
            object @object
            )
        {
        retry:

            if (@object is StringBuilder)
                return (StringBuilder)@object;

            if (@object is string)
                return NewStringBuilder((string)@object);

            if (@object is Argument)
            {
                @object = ((Argument)@object).Value;
                goto retry;
            }

            if (@object is Result)
            {
                @object = ((Result)@object).Value;
                goto retry;
            }

            if (@object != null)
            {
                @object = GetStringFromObject(@object);
                goto retry;
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder()
        {
            return NewStringBuilder((string)null);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder(
            int capacity
            )
        {
            return NewStringBuilder((StringBuilder)null, capacity);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder(
            string value
            )
        {
            return NewStringBuilder(value, DefaultCapacity);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder(
            string value,
            int capacity
            )
        {
            return NewStringBuilder(null, value, Index.Invalid, Length.Invalid, capacity);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Dead Code
#if DEAD_CODE
        public static StringBuilder NewStringBuilder(
            StringBuilder result
            )
        {
            return NewStringBuilder(result, null, Index.Invalid, Length.Invalid);
        }
#endif
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder(
            StringBuilder result,
            int capacity
            )
        {
            if (capacity < DefaultCapacity)
                capacity = DefaultCapacity;

            if (result == null)
                return new StringBuilder(capacity);

            result.Length = 0;
            result.EnsureCapacity(capacity);

            return result;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder(
            string value,
            int startIndex,
            int length
            )
        {
            return NewStringBuilder(null, value, startIndex, length);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static StringBuilder NewStringBuilder(
            StringBuilder result,
            string value,
            int startIndex,
            int length
            )
        {
            return NewStringBuilder(result, value, startIndex, length, DefaultCapacity);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringBuilder NewStringBuilder(
            StringBuilder result,
            string value,
            int startIndex,
            int length,
            int capacity
            )
        {
            if (value == null)
            {
                if (length != Length.Invalid)
                    capacity = Math.Max(length, capacity);

                if (capacity < DefaultCapacity)
                    capacity = DefaultCapacity;

                if (result == null)
                    return new StringBuilder(capacity);

                result.Length = 0;
                result.EnsureCapacity(capacity);

                return result;
            }

            if ((startIndex != Index.Invalid) && (length != Length.Invalid))
            {
                capacity = Math.Max(length, capacity);

                if (capacity < DefaultCapacity)
                    capacity = DefaultCapacity;

                if (result == null)
                    return new StringBuilder(value, startIndex, length, capacity);

                result.Length = 0;
                result.EnsureCapacity(capacity);
                result.Append(value, startIndex, length);

                return result;
            }
            else
            {
                capacity = Math.Max(value.Length, capacity);

                if (capacity < DefaultCapacity)
                    capacity = DefaultCapacity;

                if (result == null)
                    return new StringBuilder(value, capacity);

                result.Length = 0;
                result.EnsureCapacity(capacity);
                result.Append(value);

                return result;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static byte[] ConvertBytes(
            byte[] bytes,
            Encoding inputEncoding,
            Encoding outputEncoding,
            Encoding fallbackEncoding
            )
        {
            if (bytes == null)
                return null;

            if (bytes.Length == 0)
                return new byte[0];

            if ((inputEncoding == null) && (outputEncoding == null))
                return bytes;

            if (((inputEncoding == null) || (outputEncoding == null)) &&
                (fallbackEncoding == null))
            {
                return bytes;
            }

            string text = (inputEncoding != null) ?
                inputEncoding.GetString(bytes) : fallbackEncoding.GetString(bytes);

            return (outputEncoding != null) ?
                outputEncoding.GetBytes(text) : fallbackEncoding.GetBytes(text);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string TrimSwitchChars(
            string text,
            ref int count
            )
        {
            string result = text;

            if (!String.IsNullOrEmpty(result))
            {
                //
                // NOTE: Remove all leading switch chars.
                //
                result = result.TrimStart(switchChars);

                //
                // NOTE: How many chars were removed?
                //
                count = text.Length - result.Length;
            }

            return result;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool MatchSwitch(
            string text,
            string @switch
            )
        {
            if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(@switch))
                return false;

            return String.Compare(text, 0, @switch, 0,
                text.Length, SystemNoCaseStringComparisonType) == 0;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIs(
            char character,
            CharIsCallback callback
            )
        {
            if (callback != null)
                return callback(character);
            else
                return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsWord(
            char character
            )
        {
            return Char.IsLetterOrDigit(character) ||
                (Char.GetUnicodeCategory(character) == UnicodeCategory.ConnectorPunctuation);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsAsciiDigit(
            char character
            )
        {
            return (character >= Characters.Zero) && (character <= Characters.Nine);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsAsciiAlpha(
            char character
            )
        {
            return ((character >= Characters.A) && (character <= Characters.Z)) ||
                ((character >= Characters.a) && (character <= Characters.z));
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsAsciiAlphaOrDigit(
            char character
            )
        {
            return CharIsAsciiAlpha(character) || CharIsAsciiDigit(character);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsAscii(
            char character
            )
        {
            return character <= CharMaxAscii;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsGraph(
            char character
            )
        {
            return CharIsPrint(character) && (character != Characters.Space);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool CharIsPrint(
            char character
            )
        {
            if (Char.IsLetterOrDigit(character) || Char.IsWhiteSpace(character))
            {
                return true;
            }
            else
            {
                switch (Char.GetUnicodeCategory(character))
                {
                    case UnicodeCategory.NonSpacingMark:
                    case UnicodeCategory.SpacingCombiningMark:
                    case UnicodeCategory.EnclosingMark:
                    case UnicodeCategory.LetterNumber:
                    case UnicodeCategory.OtherNumber:
                    case UnicodeCategory.ConnectorPunctuation:
                    case UnicodeCategory.DashPunctuation:
                    case UnicodeCategory.OpenPunctuation:
                    case UnicodeCategory.ClosePunctuation:
                    case UnicodeCategory.InitialQuotePunctuation:
                    case UnicodeCategory.FinalQuotePunctuation:
                    case UnicodeCategory.OtherPunctuation:
                    case UnicodeCategory.MathSymbol:
                    case UnicodeCategory.CurrencySymbol:
                    case UnicodeCategory.ModifierSymbol:
                    case UnicodeCategory.OtherSymbol:
                        return true;
                    default:
                        return false;
                }
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static byte[] FromBase26String(
            string text
            )
        {
            if (!String.IsNullOrEmpty(text))
            {
                //
                // NOTE: Remove all white-space characters from the string.
                //
                text = RemoveWhiteSpace(text);

                //
                // NOTE: Now, get the number of real characters remaining.
                //
                int length = text.Length;

                //
                // NOTE: The number of real characters must be divisible by
                //       two because we need two characters to form one byte.
                //
                if ((length % 2) == 0)
                {
                    byte[] result = new byte[length / 2];

                    for (int index = 0; index < length; index += 2)
                    {
                        long value = 0;

                        if (Parser.ParseHexavigesimal(text, index, 2, ref value) != 2)
                            return null;

                        if ((value < byte.MinValue) || (value > byte.MaxValue))
                            return null;

                        result[index / 2] = ConversionOps.ToByte(value);
                    }

                    return result;
                }
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string ToBase26String(
            byte[] array,
            Base26FormattingOption options /* IGNORED */
            )
        {
            if (array != null)
            {
                int length = array.Length;
                StringBuilder result = NewStringBuilder(length * 2);

                for (int index = 0; index < length; index++)
                {
                    result.Append(FormatOps.Hexavigesimal(array[index], 2));

                    if ((((index + 1) % Base26GroupsPerLine) == 0) &&
                        FlagOps.HasFlags(options,
                            Base26FormattingOption.InsertLineBreaks, true))
                    {
                        result.Append(Environment.NewLine);
                    }
                    else if (((index + 1) < length) &&
                        FlagOps.HasFlags(options,
                            Base26FormattingOption.InsertSpaces, true))
                    {
                        result.Append(Characters.Space);
                    }
                }

                return result.ToString();
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsBase26(
            string text
            )
        {
            if (!String.IsNullOrEmpty(text) && (base26RegEx != null))
            {
                Match match = base26RegEx.Match(text.Trim());

                if ((match != null) && match.Success)
                    return true;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsBase64(
            string text
            )
        {
            if (!String.IsNullOrEmpty(text) && (base64RegEx != null))
            {
                Match match = base64RegEx.Match(text.Trim());

                if ((match != null) && match.Success)
                    return true;
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string ForceCarriageReturns(
            string value
            )
        {
            string result = value;

            if (!String.IsNullOrEmpty(result))
            {
                //
                // NOTE: If the string contains line feeds and no carriage returns,
                //       then it must be modified to include the "missing" carriage
                //       returns.
                //
                if ((result.IndexOf(Characters.CarriageReturn) == Index.Invalid) &&
                    (result.IndexOf(Characters.LineFeed) != Index.Invalid))
                {
                    return result.Replace(Characters.UnixNewLine, Characters.DosNewLine);
                }
            }

            return result;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static byte[] HashString(
            string algorithm,
            string encodingName,
            string text
            )
        {
            try
            {
                if (algorithm == null)
                    algorithm = DefaultHashAlgorithm;

                if (encodingName == null)
                    encodingName = DefaultEncodingName;

                using (HashAlgorithm hashAlgorithm =
                        HashAlgorithm.Create(algorithm))
                {
                    if (hashAlgorithm != null)
                    {
                        return hashAlgorithm.ComputeHash(Encoding.GetEncoding(
                            encodingName).GetBytes(text));
                    }
                }
            }
            catch
            {
                // do nothing.
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode StringToEnumList(
            Interpreter interpreter,
            string text,
            ref Result result
            )
        {
            if (!String.IsNullOrEmpty(text))
            {
                StringList list = null;

                if (Parser.SplitList(
                        interpreter, text, 0, Length.Invalid, true,
                        ref list, ref result) == ReturnCode.Ok)
                {
                    //
                    // NOTE: Make friendly to Enum.Parse for flags fields.
                    //
                    result = list.ToString(
                        Characters.Comma.ToString() + Characters.Space.ToString(),
                        null, false);

                    return ReturnCode.Ok;
                }
                else
                {
                    return ReturnCode.Error;
                }
            }
            else
            {
                //
                // NOTE: Empty list, OK.
                //
                result = String.Empty;

                return ReturnCode.Ok;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Dead Code
#if DEAD_CODE
        private static bool CharIsSwitch( /* NOT USED */
            char character
            )
        {
            return (switchCharList != null) && switchCharList.Contains(character);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool CharIsAlpha( /* NOT USED */
            char character
            )
        {
            switch (Char.GetUnicodeCategory(character))
            {
                case UnicodeCategory.UppercaseLetter:
                case UnicodeCategory.LowercaseLetter:
                case UnicodeCategory.TitlecaseLetter:
                case UnicodeCategory.ModifierLetter:
                case UnicodeCategory.OtherLetter:
                    return true;
                default:
                    return false;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool CharIsDigit( /* NOT USED */
            char character
            )
        {
            return Char.GetUnicodeCategory(character) == UnicodeCategory.DecimalDigitNumber;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static string Slice( /* NOT USED */
            string text,
            int firstIndex,
            int lastIndex
            )
        {
            if (!String.IsNullOrEmpty(text))
            {
                if (firstIndex < 0)
                    firstIndex = 0;

                if (lastIndex >= text.Length)
                    lastIndex = text.Length - 1;

                if (firstIndex > lastIndex)
                {
                    int swap = firstIndex;
                    firstIndex = lastIndex;
                    lastIndex = swap;
                }

                return text.Substring(firstIndex, lastIndex - firstIndex + 1);
            }

            return text;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static string StrReplace(
            string text,
            string oldValue,
            string newValue,
            StringComparison comparisonType,
            int maximum,
            ref int count
            ) /* NOT USED */
        {
            StringBuilder result = NewStringBuilder();

            if (!String.IsNullOrEmpty(text))
            {
                if (!String.IsNullOrEmpty(oldValue))
                {
                    int index = 0;
                    int oldIndex = text.IndexOf(oldValue, index, comparisonType);

                    while ((index < text.Length) || (oldIndex != Index.Invalid))
                    {
                        if (oldIndex != Index.Invalid)
                        {
                            //
                            // NOTE: Did we skip some initial portion of the original
                            //       string that we still need to append to the result?
                            //
                            if (oldIndex > index)
                            {
                                //
                                // NOTE: Append the original characters in the string
                                //       between where the last portion of the original
                                //       string we handled ends and the next substring
                                //       to replace begins.
                                //
                                result.Append(text, index, oldIndex - index);

                                //
                                // NOTE: Advance the origianl string index beyond what
                                //       we just appended.
                                //
                                index += (oldIndex - index);
                            }

                            //
                            // NOTE: Append the new substring to replace the one we
                            //       were looking for.
                            //
                            if (!String.IsNullOrEmpty(newValue))
                                result.Append(newValue);

                            //
                            // NOTE: We replaced another instance of the substring.
                            //
                            count++;

                            //
                            // NOTE: Skip past the substring we replaced in the original
                            //       string.
                            //
                            index += oldValue.Length;

                            //
                            // NOTE: Do we want to replace all instances of the substring
                            //       to replace?
                            //
                            if ((maximum <= 0) || (count < maximum))
                            {
                                //
                                // NOTE: Find the next instance of the substring to
                                //       replace.
                                //
                                oldIndex = text.IndexOf(oldValue, index, comparisonType);
                            }
                            else
                            {
                                //
                                // NOTE: Terminate the loop prematurely because they do
                                //       not want to replace all instances of the
                                //       substring.
                                //
                                oldIndex = Index.Invalid;
                            }

                            //
                            // NOTE: Is there going to be another substring to replace
                            //       (during the next loop iteration)?
                            //
                            if (oldIndex != Index.Invalid)
                            {
                                //
                                // NOTE: Append the original characters in the string
                                //       between where the substring to replace ended
                                //       and the next substring to replace begins.
                                //
                                result.Append(text, index, oldIndex - index);

                                //
                                // NOTE: Advance the origianl string index beyond what
                                //       we just appended.
                                //
                                index += (oldIndex - index);
                            }
                        }
                        else
                        {
                            //
                            // NOTE: We should now be done replacing the substring.
                            //       Append the rest of the original string verbatim.
                            //
                            result.Append(text, index, text.Length - index);

                            //
                            // NOTE: Update the index to reflect the fact that we just
                            //       added the entire remainder of the original string.
                            //
                            index += (text.Length - index);
                        }
                    }
                }
            }

            return result.ToString();
        }
#endif
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool StrInMap(
            string text,
            StringPairList list,
            int startIndex,
            StringComparison comparisonType,
            ref int oldLength,
            ref string oldValue,
            ref string newValue
            )
        {
            if (!String.IsNullOrEmpty(text))
            {
                if ((startIndex >= 0) && (startIndex < text.Length))
                {
                    foreach (StringPair pair in list)
                    {
                        string key = pair.X;

                        if (String.Compare(
                                text, startIndex, key, 0,
                                key.Length, comparisonType) == 0)
                        {
                            string value = pair.Y;

                            oldLength = key.Length;
                            oldValue = key;
                            newValue = value;

                            return true;
                        }
                    }
                }
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string StrMap(
            string text,
            StringPairList list,
            StringComparison comparisonType,
            int maximum,
            ref int count
            )
        {
            StringBuilder result = NewStringBuilder();

            if (!String.IsNullOrEmpty(text))
            {
                if (list != null)
                {
                    if (list.Count > 0)
                    {
                        int index, index2;

                        for (index = 0, index2 = 0; index < text.Length; index++)
                        {
                            int oldLength = 0;
                            string oldValue = null;
                            string newValue = null;

                            //
                            // NOTE: Attempt to match at the current location any of the
                            //       'old' (from) values in our map.
                            //
                            if (StrInMap(text, list, index, comparisonType,
                                    ref oldLength, ref oldValue, ref newValue))
                            {
                                //
                                // NOTE: Have we skipped over anything in the original
                                //       string since we last matched?  If so, we need to
                                //       append it to the result prior to doing anything
                                //       else.
                                //
                                if (index2 != index)
                                {
                                    //
                                    // NOTE: Append the portion or the original string
                                    //       between where we are now and where our last
                                    //       match was to the result.
                                    //
                                    result.Append(text.Substring(index2, index - index2));

                                    //
                                    // NOTE: Advance just beyond this match in the original
                                    //       string.
                                    //
                                    index2 = (index + oldLength);
                                }
                                else
                                {
                                    //
                                    // NOTE: Advance just beyond this match in the original
                                    //       string.
                                    //
                                    index2 += oldLength;
                                }

                                //
                                // NOTE: Make sure we have not met our quota for replacements
                                //       yet.
                                //
                                if ((maximum == Count.Invalid) || (count < maximum))
                                {
                                    //
                                    // NOTE: Append the 'new' (to) value from the map for the
                                    //       'old' (from) value  we just matched to the result.
                                    //
                                    result.Append(newValue);

                                    //
                                    // NOTE: We just replaced another old value with a new value.
                                    //
                                    count++;
                                }
                                else
                                {
                                    //
                                    // NOTE: Append the 'old' (from) value from the map because
                                    //       we have already reached our replacement quota.
                                    //
                                    result.Append(oldValue);
                                }

                                //
                                // NOTE: Set the loop index to be one less than our next index
                                //       to attempt matching at because the outer for loop will
                                //       increment it.  This prevents us from having to use
                                //       specialized logic in the more general case of there
                                //       being no match while still ensuring that we do
                                //       eventually get to the end of the original string.
                                //
                                index = (index2 - 1);
                            }
                        }

                        //
                        // NOTE: Append any final unmapped portion of the original
                        //       string to the result.
                        //
                        if (index2 != index)
                            result.Append(text.Substring(index2, index - index2));
                    }
                    else
                    {
                        //
                        // BUGFIX: This is not an error.  We must return their original
                        //         string verbatim.
                        //
                        result.Append(text);
                    }
                }
            }

            return result.ToString();
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string StrReverse(
            string text
            )
        {
            if (!String.IsNullOrEmpty(text))
            {
                char[] chars = text.ToCharArray();
                Array.Reverse(chars);
                return new string(chars);
            }

            return text;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string StrRepeat(
            int count,
            char character
            )
        {
            StringBuilder result = NewStringBuilder();

            result.EnsureCapacity(count);

            while (count-- > 0)
                result.Append(character);

            return result.ToString();
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string StrRepeat(
            int count,
            string text
            )
        {
            StringBuilder result = NewStringBuilder();

            if (!String.IsNullOrEmpty(text))
            {
                result.EnsureCapacity(count * text.Length);

                while (count-- > 0)
                    result.Append(text);
            }

            return result.ToString();
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string PadCenter(
            string text,
            int length,
            char character
            )
        {
            StringBuilder result = NewStringBuilder();

            if (!String.IsNullOrEmpty(text))
            {
                int textLength = text.Length;
                int halfLength = ((length - textLength) / 2);
                int extraLength = ((length - textLength) % 2);

                result.Append(StrRepeat(halfLength, character));
                result.Append(text);
                result.Append(StrRepeat(halfLength + extraLength, character));
            }
            else
            {
                result.Append(StrRepeat(length, character));
            }

            return result.ToString();
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static StringComparer GetStringComparer(
            StringComparison comparisonType
            )
        {
            switch (comparisonType)
            {
                case StringComparison.CurrentCulture:
                    return StringComparer.CurrentCulture;
                case StringComparison.CurrentCultureIgnoreCase:
                    return StringComparer.CurrentCultureIgnoreCase;
                case StringComparison.InvariantCulture:
                    return StringComparer.InvariantCulture;
                case StringComparison.InvariantCultureIgnoreCase:
                    return StringComparer.InvariantCultureIgnoreCase;
                case StringComparison.Ordinal:
                    return StringComparer.Ordinal;
                case StringComparison.OrdinalIgnoreCase:
                    return StringComparer.OrdinalIgnoreCase;
                default:
                    return DefaultStringComparer;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string ToTitle(
            string text
            )
        {
            string result = text;

            if (!String.IsNullOrEmpty(result))
                result = Char.ToUpper(text[0]) +
                    ((text.Length > 1) ? text.Substring(1).ToLower() : String.Empty);

            return result;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        //
        // NOTE: Excludes characters covered by PathOps.HasPathWildcard().
        //
        public static bool HasStringMatchWildcard(
            string value
            )
        {
            return (value != null) &&
                (StringMatchWildcardChars != null) &&
                (value.IndexOfAny(StringMatchWildcardChars) != Index.Invalid);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool HasStringMatchChar(
            string text
            )
        {
            return (text != null) &&
                (text.IndexOfAny(Characters.StringMatchReservedChars) != Index.Invalid);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static string RemoveWhiteSpace(
            string text
            )
        {
            if (!String.IsNullOrEmpty(text))
            {
                StringBuilder result = NewStringBuilder(text.Length);

                for (int index = 0; index < text.Length; index++)
                {
                    switch (text[index])
                    {
                        case Characters.Bell:
                        case Characters.Backspace:
                        case Characters.HorizontalTab:
                        case Characters.LineFeed:
                        case Characters.VerticalTab:
                        case Characters.FormFeed:
                        case Characters.CarriageReturn:
                        case Characters.Space:
                            {
                                //
                                // NOTE: Do nothing (i.e. skip this character).
                                //
                                break;
                            }
                        default:
                            {
                                result.Append(text[index]);
                                break;
                            }
                    }
                }

                return result.ToString();
            }
            else
            {
                return text;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string NormalizeWhiteSpace(
            string text,
            char fallback,
            bool simple, 
            bool unicode
            )
        {
            //
            // WARNING: This function must not change the length of the string.
            //          Each character that gets replaced must be replaced by
            //          another character, not a string.
            //
            if (!String.IsNullOrEmpty(text))
            {
                //
                // NOTE: This routine was designed primarily with performance
                //       in mind.
                //
                StringBuilder result = NewStringBuilder(text);

                for (int index = 0; index < text.Length; index++)
                {
                    //
                    // NOTE: Make sure that there are no embedded tabs,
                    //       carriage-returns, line-feeds, etc.
                    //
                    if (simple)
                    {
                        //
                        // NOTE: In this mode all white-space characters are
                        //       replaced by the fallback character (which is
                        //       typically a space).
                        //
                        switch (text[index])
                        {
                            case Characters.Bell:
                            case Characters.Backspace:
                            case Characters.HorizontalTab:
                            case Characters.LineFeed:
                            case Characters.VerticalTab:
                            case Characters.FormFeed:
                            case Characters.CarriageReturn:
                                {
                                    result[index] = fallback;
                                    break;
                                }
                        }
                    }
                    else
                    {
                        switch (text[index])
                        {
                            case Characters.Bell:           /* AUDIBLE */
                                {
                                    if (unicode)
                                        result[index] = Characters.BellSymbol;
                                    else
                                        //
                                        // NOTE: There is no real suitable non-Unicode
                                        //       replacement for the bell character;
                                        //       therefore, just use the fallback.
                                        //
                                        result[index] = fallback;

                                    break;
                                }
                            case Characters.Backspace:      /* HORIZONTAL */
                                {
                                    if (unicode)
                                        result[index] = Characters.BackspaceSymbol;
                                    else
                                        //
                                        // NOTE: There is no real suitable non-Unicode
                                        //       replacement (i.e. a character that
                                        //       does not change how the console lays
                                        //       out text) for the backspace character;
                                        //       therefore, just use the fallback.
                                        //
                                        result[index] = fallback;

                                    break;
                                }
                            case Characters.HorizontalTab:  /* HORIZONTAL */
                                {
                                    result[index] = Characters.Space;
                                    break;
                                }
                            case Characters.LineFeed:       /* VERTICAL */
                                {
                                    result[index] = Characters.Pilcrow;
                                    break;
                                }
                            case Characters.VerticalTab:    /* VERTICAL */
                            case Characters.FormFeed:       /* VERTICAL */
                                {
                                    //
                                    // NOTE: These should be fine since their literal
                                    //       inclusion does not seem to change how the
                                    //       console lays out the text.
                                    //
                                    result[index] = text[index];
                                    break;
                                }
                            case Characters.CarriageReturn: /* VERTICAL */
                                {
                                    result[index] = Characters.Pilcrow;
                                    break;
                                }
                        }
                    }
                }

                return result.ToString();
            }
            else
            {
                return text;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static char CharOrNull(
            string value,
            int index
            )
        {
            if (value == null)
                return Characters.Null;

            if ((index >= 0) && (index < value.Length))
                return value[index];

            return Characters.Null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static string NullIfEmpty(
            string value
            )
        {
            if (value == null)
                return null;

            if (value.Length == 0)
                return null;

            return value;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

#if !MONO && NATIVE && WINDOWS
        [MethodImpl(
            MethodImplOptions.NoInlining
#if (NET_20_SP2 || NET_40) && !MONO_LEGACY
            | MethodImplOptions.NoOptimization
#endif
        )]
        public static ReturnCode ZeroString(
            string value,
            ref Result error
            )
        {
            if (value == null)
            {
                error = "invalid string";
                return ReturnCode.Error;
            }

            if (CommonOps.Runtime.IsMono())
            {
                error = "not implemented";
                return ReturnCode.Error;
            }

            GCHandle handle = NativeOps.GetInvalidGCHandle();

            try
            {
                handle = GCHandle.Alloc(value, GCHandleType.Pinned);

                if (handle.IsAllocated)
                {
                    /* m_firstChar */
                    IntPtr pMemory = handle.AddrOfPinnedObject();

                    if (pMemory != IntPtr.Zero)
                    {
                        int length;

                        if (CommonOps.Runtime.IsFramework40())
                        {
                            /* m_stringLength */
                            length = Marshal.ReadInt32(pMemory,
                                -sizeof(int));
                        }
                        else
                        {
                            /* m_arrayLength */
                            length = Marshal.ReadInt32(pMemory,
                                -(sizeof(int) * 2));
                        }

                        return NativeOps.ZeroMemory(
                            pMemory, (uint)(length * sizeof(char)),
                            ref error);
                    }
                    else
                    {
                        error = "could not get address of pinned string";
                    }
                }
                else
                {
                    error = "could not allocate pinned string";
                }
            }
            catch (Exception e)
            {
                error = e;
            }
            finally
            {
                if (handle.IsAllocated)
                    handle.Free();
            }

            return ReturnCode.Error;
        }
#endif

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode AppendWithFormat(
            Interpreter interpreter,
            string format,
            ArgumentList arguments,
            CultureInfo cultureInfo,
            ref StringBuilder result,
            ref Result error
            )
        {
            int spanIndex = 0;
            string message = null;
            int numBytes = 0;
            int argumentIndex = 0;
            bool gotXpg = false;
            bool gotSequential = false;
            int originalLength;
            int limit = 0;

            StringBuilder localResult = NewStringBuilder();

            originalLength = localResult.Length;
            limit = localResult.MaxCapacity - originalLength;

            int index = 0;

            while (index < format.Length)
            {
                int endIndex = 0;
                bool gotMinus;
                bool gotHash;
                bool gotZero;
                bool gotSpace;
                bool gotPlus;
                bool sawFlag;

                int width;
                bool gotPrecision;
                int precision;
                bool useByte;
                bool useShort;
                bool useWide;
                bool useBig;

                bool newXpg;
                int numChars;
                int segmentLimit;
                int segmentNumBytes;

                StringBuilder segment;
                char character;

                character = CharOrNull(format, index);
                index++;

                if (character != Characters.PercentSign)
                {
                    numBytes++;
                    continue;
                }

                if (numBytes > 0)
                {
                    if (numBytes > limit)
                    {
                        message = OverflowError;
                        goto errorMessage;
                    }

                    localResult.Append(format, spanIndex, numBytes);
                    limit -= numBytes;
                    numBytes = 0;
                }

                /*
                 * Saw a % : process the format specifier.
                 *
                 * Step 0. Handle special case of escaped format marker (i.e., %%).
                 */

                character = CharOrNull(format, index);

                if (character == Characters.PercentSign)
                {
                    spanIndex = index;
                    numBytes = 1;
                    index++;
                    continue;
                }

                /*
                 * Step 1. XPG3 position specifier
                 */

                newXpg = false;

                if (Char.IsDigit(character))
                {
                    int position = Parser.ParseInteger(
                        format, index, format.Length, Parser.DecimalRadix,
                        true, true, true, ref endIndex);

                    if (CharOrNull(format, endIndex) == Characters.DollarSign)
                    {
                        newXpg = true;
                        argumentIndex = position - 1;
                        index = endIndex + 1;
                        character = CharOrNull(format, index);
                    }
                }

                if (newXpg)
                {
                    if (gotSequential)
                    {
                        message = mixedXpgError;
                        goto errorMessage;
                    }
                    gotXpg = true;
                }
                else
                {
                    if (gotXpg)
                    {
                        message = mixedXpgError;
                        goto errorMessage;
                    }
                    gotSequential = true;
                }

                if ((argumentIndex < 0) || (argumentIndex >= arguments.Count))
                {
                    message = BadIndexError[ConversionOps.ToInt(gotXpg)];
                    goto errorMessage;
                }

                /*
                 * Step 2. Set of flags.
                 */

                gotMinus = gotHash = gotZero = gotSpace = gotPlus = false;
                sawFlag = true;

                do
                {
                    switch (character)
                    {
                        case Characters.MinusSign:
                            gotMinus = true;
                            break;
                        case Characters.NumberSign:
                            gotHash = true;
                            break;
                        case Characters.Zero:
                            gotZero = true;
                            break;
                        case Characters.Space:
                            gotSpace = true;
                            break;
                        case Characters.PlusSign:
                            gotPlus = true;
                            break;
                        default:
                            sawFlag = false;
                            break;
                    }

                    if (sawFlag)
                    {
                        index++;
                        character = CharOrNull(format, index);
                    }
                } while (sawFlag);

                /*
                 * Step 3. Minimum field width.
                 */

                width = 0;

                if (Char.IsDigit(character))
                {
                    width = Parser.ParseInteger(
                        format, index, format.Length, Parser.DecimalRadix,
                        true, true, true, ref endIndex);

                    index = endIndex;
                    character = CharOrNull(format, index);
                }
                else if (character == Characters.Asterisk)
                {
                    if (argumentIndex >= (arguments.Count - 1))
                    {
                        message = BadIndexError[ConversionOps.ToInt(gotXpg)];
                        goto errorMessage;
                    }

                    if (Value.GetInteger2(
                            (IGetValue)arguments[argumentIndex],
                            ValueFlags.AnyInteger, cultureInfo,
                            ref width, ref error) != ReturnCode.Ok)
                    {
                        goto error;
                    }

                    if (width < 0)
                    {
                        width = -width;
                        gotMinus = true;
                    }

                    argumentIndex++;

                    index++;
                    character = CharOrNull(format, index);
                }

                if (width > limit)
                {
                    message = OverflowError;
                    goto errorMessage;
                }

                /*
                 * Step 4. Precision.
                 */

                gotPrecision = false; precision = 0;

                if (character == Characters.Period)
                {
                    gotPrecision = true;
                    index++;
                    character = CharOrNull(format, index);
                }

                if (Char.IsDigit(character))
                {
                    precision = Parser.ParseInteger(
                        format, index, format.Length, Parser.DecimalRadix,
                        true, true, true, ref endIndex);

                    index = endIndex;
                    character = CharOrNull(format, index);
                }
                else if (character == Characters.Asterisk)
                {
                    if (argumentIndex >= (arguments.Count - 1))
                    {
                        message = BadIndexError[ConversionOps.ToInt(gotXpg)];
                        goto errorMessage;
                    }

                    if (Value.GetInteger2(
                            (IGetValue)arguments[argumentIndex],
                            ValueFlags.AnyInteger, cultureInfo,
                            ref precision, ref error) != ReturnCode.Ok)
                    {
                        goto error;
                    }

                    /*
                     * TODO: Check this truncation logic.
                     */

                    if (precision < 0)
                        precision = 0;

                    argumentIndex++;
                    index++;
                    character = CharOrNull(format, index);
                }

                /*
                 * Step 5. Length modifier.
                 */

                useByte = useShort = useWide = useBig = false;

                if (character == Characters.y) /* BYTE */
                {
                    useByte = true;

                    index++;
                    character = CharOrNull(format, index);
                }
                else if (character == Characters.h) /* SHORT */
                {
                    useShort = true;

                    index++;
                    character = CharOrNull(format, index);
                }
                else if (character == Characters.l) /* LONG */
                {
                    index++;
                    character = CharOrNull(format, index);

                    if (character == Characters.l) /* BIGNUM */
                    {
                        useBig = true;

                        index++;
                        character = CharOrNull(format, index);
                    }
                    else
                    {
                        useWide = true;
                    }
                }

                index++;
                spanIndex = index;

                /*
                 * Step 6. The actual conversion character.
                 */

                segment = NewStringBuilder(arguments[argumentIndex]);

                if (character == Characters.i)
                    character = Characters.d;

                switch (character)
                {
                    case Characters.Null:
                        {
                            message = "format string ended in middle of field specifier";
                            goto errorMessage;
                        }
                    case Characters.s:
                        {
                            numChars = segment.Length;

                            if (gotPrecision && (precision < numChars))
                                segment.Length = precision;

                            break;
                        }
                    case Characters.c:
                        {
                            int code = 0;

                            if (Value.GetInteger2(
                                    segment.ToString(), ValueFlags.AnyInteger,
                                    cultureInfo, ref code, ref error) != ReturnCode.Ok)
                            {
                                goto error;
                            }

                            segment = NewStringBuilder(ConversionOps.ToChar(code).ToString());
                            break;
                        }
                    case Characters.u:
                        {
                            if (useBig)
                            {
                                message = "unsigned bignum format is invalid";
                                goto errorMessage;
                            }

                            goto case Characters.d;
                        }
                    case Characters.d:
                    case Characters.o:
                    case Characters.x:
                    case Characters.X:
                    case Characters.b:
                        {
                            sbyte sbyteValue = 0; /* Silence compiler warning; only defined and
                                                   * used when useByte is true. */
                            short shortValue = 0; /* Silence compiler warning; only defined and
                                                   * used when useShort is true. */
                            int intValue = 0;
                            long longValue = 0;
                            int toAppend;
                            bool isNegative = false;

                            if (useWide)
                            {
                                if (Value.GetWideInteger2(
                                        segment.ToString(), ValueFlags.AnyWideInteger,
                                        cultureInfo, ref longValue, ref error) != ReturnCode.Ok)
                                {
                                    goto error;
                                }

                                isNegative = (longValue < 0);
                            }
                            else if (Value.GetInteger2(
                                    segment.ToString(), ValueFlags.AnyInteger,
                                    cultureInfo, ref intValue, ref error) != ReturnCode.Ok)
                            {
                                if (Value.GetWideInteger2(
                                        segment.ToString(), ValueFlags.AnyWideInteger,
                                        cultureInfo, ref longValue) != ReturnCode.Ok)
                                {
                                    goto error;
                                }
                                else
                                {
                                    intValue = ConversionOps.ToInt(longValue);
                                }

                                if (useByte)
                                {
                                    sbyteValue = ConversionOps.ToSByte(intValue);
                                    isNegative = (sbyteValue < 0);
                                }
                                else if (useShort)
                                {
                                    shortValue = ConversionOps.ToShort(intValue);
                                    isNegative = (shortValue < 0);
                                }
                                else
                                {
                                    isNegative = (intValue < 0);
                                }
                            }
                            else if (useByte)
                            {
                                sbyteValue = ConversionOps.ToSByte(intValue);
                                isNegative = (sbyteValue < 0);
                            }
                            else if (useShort)
                            {
                                shortValue = ConversionOps.ToShort(intValue);
                                isNegative = (shortValue < 0);
                            }
                            else
                            {
                                isNegative = (intValue < 0);
                            }

                            segment = NewStringBuilder();
                            segmentLimit = segment.MaxCapacity;

                            if ((isNegative || gotPlus || gotSpace) &&
                                (useBig || (character == Characters.d)))
                            {
                                segment.Append(isNegative ? Characters.MinusSign :
                                    (gotPlus ? Characters.PlusSign : Characters.Space));

                                segmentLimit--;
                            }

                            if (gotHash)
                            {
                                switch (character)
                                {
                                    case Characters.b:
                                        {
                                            segment.Append(BinaryPrefix);
                                            segmentLimit -= BinaryPrefix.Length;
                                            break;
                                        }
                                    case Characters.o:
                                        {
                                            segment.Append(OctalPrefix);
                                            segmentLimit -= OctalPrefix.Length;
                                            break;
                                        }
                                    case Characters.x:
                                    case Characters.X:
                                        {
                                            segment.Append(HexadecimalPrefix);
                                            segmentLimit -= HexadecimalPrefix.Length;
                                            break;
                                        }
                                }
                            }

                            switch (character)
                            {
                                case Characters.d:
                                    {
                                        int length;
                                        string bytes;
                                        int byteIndex = 0;

                                        if (useByte)
                                            bytes = sbyteValue.ToString();
                                        else if (useShort)
                                            bytes = shortValue.ToString();
                                        else if (useWide)
                                            bytes = longValue.ToString();
                                        else
                                            bytes = intValue.ToString();

                                        length = bytes.Length;

                                        /*
                                         * Already did the sign above.
                                         */

                                        if (bytes[0] == Characters.MinusSign)
                                        {
                                            length--;
                                            byteIndex++;
                                        }

                                        toAppend = length;

                                        /*
                                         * Canonical decimal string reps for integers are composed
                                         * entirely of one-byte encoded characters, so "length" is the
                                         * number of chars.
                                         */

                                        if (gotPrecision)
                                        {
                                            if (length < precision)
                                                segmentLimit -= (precision - length);

                                            while (length < precision)
                                            {
                                                segment.Append(Characters.Zero);
                                                length++;
                                            }
                                            gotZero = false;
                                        }

                                        if (gotZero)
                                        {
                                            length += segment.Length;

                                            if (length < width)
                                                segmentLimit -= (width - length);

                                            while (length < width)
                                            {
                                                segment.Append(Characters.Zero);
                                                length++;
                                            }
                                        }

                                        if (toAppend > segmentLimit)
                                        {
                                            message = OverflowError;
                                            goto errorMessage;
                                        }

                                        segment.Append(bytes, byteIndex, toAppend);
                                        break;
                                    }
                                case Characters.u:
                                case Characters.o:
                                case Characters.x:
                                case Characters.X:
                                case Characters.b:
                                    {
                                        ulong bits = 0;
                                        int numDigits = 0;
                                        int length, radix = Parser.HexadecimalRadix;

                                        if (character == Characters.u)
                                            radix = Parser.DecimalRadix;
                                        else if (character == Characters.o)
                                            radix = Parser.OctalRadix;
                                        else if (character == Characters.b)
                                            radix = Parser.BinaryRadix;

                                        if (useByte)
                                        {
                                            byte byteValue = ConversionOps.ToByte(sbyteValue);

                                            bits = byteValue;
                                            while (byteValue != 0)
                                            {
                                                numDigits++;
                                                byteValue /= (byte)radix;
                                            }
                                        }
                                        else if (useShort)
                                        {
                                            ushort ushortValue = ConversionOps.ToUShort(shortValue);

                                            bits = ushortValue;
                                            while (ushortValue != 0)
                                            {
                                                numDigits++;
                                                ushortValue /= (ushort)radix;
                                            }
                                        }
                                        else if (useWide)
                                        {
                                            ulong ulongValue = ConversionOps.ToULong(longValue);

                                            bits = ulongValue;
                                            while (ulongValue != 0)
                                            {
                                                numDigits++;
                                                ulongValue /= (ulong)radix;
                                            }
                                        }
                                        else
                                        {
                                            uint uintValue = ConversionOps.ToUInt(intValue);

                                            bits = uintValue;
                                            while (uintValue != 0)
                                            {
                                                numDigits++;
                                                uintValue /= (uint)radix;
                                            }
                                        }

                                        /*
                                         * Need to be sure zero becomes "0", not "".
                                         */

                                        if ((numDigits == 0) &&
                                            !((character == Characters.o) && gotHash))
                                        {
                                            numDigits = 1;
                                        }

                                        StringBuilder bytes = NewStringBuilder(numDigits);
                                        bytes.Length = numDigits;

                                        toAppend = length = (int)numDigits;

                                        while (numDigits-- > 0)
                                        {
                                            int digitOffset = (int)(bits % (ulong)radix);

                                            bytes[numDigits] = (digitOffset > 9) ?
                                                (char)(Characters.a + digitOffset - Parser.DecimalRadix) :
                                                (char)(Characters.Zero + digitOffset);

                                            bits /= (ulong)radix;
                                        }

                                        if (gotPrecision)
                                        {
                                            if (length < precision)
                                                segmentLimit -= (precision - length);

                                            while (length < precision)
                                            {
                                                segment.Append(Characters.Zero);
                                                length++;
                                            }
                                            gotZero = false;
                                        }

                                        if (gotZero)
                                        {
                                            length += segment.Length;

                                            if (length < width)
                                                segmentLimit -= (width - length);

                                            while (length < width)
                                            {
                                                segment.Append(Characters.Zero);
                                                length++;
                                            }
                                        }

                                        if (toAppend > segmentLimit)
                                        {
                                            message = OverflowError;
                                            goto errorMessage;
                                        }

                                        segment.Append(bytes);
                                        break;
                                    }
                            }
                            break;
                        }
                    case Characters.e:
                    case Characters.E:
                    case Characters.f:
                    case Characters.g:
                    case Characters.G:
                        {
#if NATIVE
                            double doubleValue = 0;

                            if (Value.GetDouble(
                                    segment.ToString(), cultureInfo, ref doubleValue,
                                    ref error) != ReturnCode.Ok)
                            {
                                /* TODO: Figure out ACCEPT_NAN here */
                                goto error;
                            }

                            StringBuilder spec = NewStringBuilder();

                            spec.Append(Characters.PercentSign);

                            if (gotMinus)
                                spec.Append(Characters.MinusSign);

                            if (gotHash)
                                spec.Append(Characters.NumberSign);

                            if (gotZero)
                                spec.Append(Characters.Zero);

                            if (gotSpace)
                                spec.Append(Characters.Space);

                            if (gotPlus)
                                spec.Append(Characters.PlusSign);

                            if (width > 0)
                                spec.AppendFormat("{0}", width);

                            if (gotPrecision)
                                spec.AppendFormat(".{0}", precision);

                            /*
                             * Don't pass length modifiers!
                             */

                            spec.Append(character);
                            segment = NewStringBuilder();

                            if (NativeOps.PrintDouble(
                                    segment, spec.ToString(), doubleValue,
                                    ref error) != ReturnCode.Ok)
                            {
                                goto error;
                            }
                            break;
#else
                            message = String.Format(
                                "bad field specifier \"{0}\": not implemented",
                                character);

                            goto errorMessage;
#endif
                        }
                    default:
                        {
                            message = String.Format(
                                "bad field specifier \"{0}\"",
                                character);

                            goto errorMessage;
                        }
                }

                switch (character)
                {
                    case Characters.E:
                    case Characters.G:
                    case Characters.X:
                        {
                            segment = NewStringBuilder(segment.ToString().ToUpper());
                            break;
                        }
                }

                numChars = segment.Length;

                if (!gotMinus)
                {
                    if (numChars < width)
                        limit -= (width - numChars);

                    while (numChars < width)
                    {
                        localResult.Append(gotZero ?
                            Characters.Zero : Characters.Space);

                        numChars++;
                    }
                }

                segmentNumBytes = segment.Length;

                if (segmentNumBytes > limit)
                {
                    message = OverflowError;
                    goto errorMessage;
                }

                localResult.Append(segment);
                limit -= segmentNumBytes;

                if (numChars < width)
                    limit -= (width - numChars);

                while (numChars < width)
                {
                    localResult.Append(gotZero ?
                        Characters.Zero : Characters.Space);

                    numChars++;
                }

                argumentIndex += ConversionOps.ToInt(gotSequential);
            }

            if (numBytes > 0)
            {
                if (numBytes > limit)
                {
                    message = OverflowError;
                    goto errorMessage;
                }

                localResult.Append(format, spanIndex, numBytes);
                limit -= numBytes;
                numBytes = 0;
            }

            result = localResult;
            return ReturnCode.Ok;

        errorMessage:
            error = message;

        error:
            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static void FixupSubPatterns(
            IList<StringBuilder> subPatterns,
            string prefix,
            string suffix,
            bool withPrefix,
            bool withSuffix
            )
        {
            //
            // NOTE: Is sub-pattern prefix and/or suffix handling enabled?
            //
            if ((withPrefix && (prefix != null)) ||
                (withSuffix && (suffix != null)))
            {
                //
                // NOTE: If necessary, add the prefix and suffix we found to
                //       each of the sub-patterns.
                //
                if (subPatterns != null)
                {
                    foreach (StringBuilder subPattern in subPatterns)
                    {
                        if (subPattern == null)
                            continue;

                        if (withPrefix && (prefix != null))
                            subPattern.Insert(0, prefix);

                        if (withSuffix && (suffix != null))
                            subPattern.Append(suffix);
                    }
                }
                //
                // NOTE: *SPECIAL* If there are no sub-pattern fragments, make
                //       sure there is at least one sub-pattern, consisting of
                //       the prefix and suffix.
                //
                else
                {
                    StringBuilder subPattern = new StringBuilder();

                    if (withPrefix && (prefix != null))
                        subPattern.Append(prefix);

                    if (withSuffix && (suffix != null))
                        subPattern.Append(suffix);

                    subPatterns = new List<StringBuilder>();
                    subPatterns.Add(subPattern);
                }
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode SplitSubPatterns(
            string pattern,             /* in */
            int startIndex,             /* in */
            bool empty,                 /* in */
            ref StringList subPatterns, /* in, out */
            ref Result error            /* out */
            )
        {
            string prefix = null;
            string suffix = null;
            IList<IList<StringBuilder>> allSubPatterns = null;

            while (true)
            {
                string localPrefix = null;
                string localSuffix = null;
                IList<StringBuilder> localSubPatterns = null;
                int stopIndex = Index.Invalid;

                if (SplitSubPatterns(
                        pattern, startIndex, empty, true, false, false,
                        ref localPrefix, ref localSuffix,
                        ref localSubPatterns, ref stopIndex,
                        ref error) != ReturnCode.Ok)
                {
                    return ReturnCode.Error;
                }

                bool withPrefix = false;

                if (localSubPatterns == null)
                {
                    if (localSuffix != null)
                    {
                        //
                        // NOTE: The suffix was set for this loop iteration and
                        //       takes over the existing master suffix, replace
                        //       it.
                        //
                        suffix = localSuffix;
                    }
                    else if (pattern != null)
                    {
                        //
                        // NOTE: The suffix was not set for this loop iteration;
                        //       however, this is now the final loop iteration
                        //       and there might be additional pattern content,
                        //       use it.
                        //
                        suffix = pattern.Substring(
                            startIndex, pattern.Length - startIndex);
                    }

                    break;
                }

                if (prefix != null)
                {
                    //
                    // NOTE: The prefix was set for this loop iteration, do not
                    //       simply throw it away.
                    //
                    withPrefix = true;
                }
                else if (localPrefix != null)
                {
                    //
                    // NOTE: The prefix was set for this loop iteration and the
                    //       master prefix is not set, replace it.
                    //
                    prefix = localPrefix;
                }

                FixupSubPatterns(
                    localSubPatterns, localPrefix, localSuffix, withPrefix,
                    false);

                if (allSubPatterns == null)
                    allSubPatterns = new List<IList<StringBuilder>>();

                allSubPatterns.Add(localSubPatterns);
                startIndex = stopIndex + 1;
            }

            if (allSubPatterns != null)
            {
                IList<StringBuilder> localSubPatterns = null;

                if (ListOps.Combine(
                        allSubPatterns, ref localSubPatterns,
                        ref error) != ReturnCode.Ok)
                {
                    return ReturnCode.Error;
                }

                FixupSubPatterns(
                    localSubPatterns, prefix, suffix, true, true);

                subPatterns = ListOps.Flatten(localSubPatterns);
            }

            return ReturnCode.Ok;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode SplitSubPatterns(
            string pattern,                       /* in */
            int startIndex,                       /* in */
            bool empty,                           /* in */
            bool firstOnly,                       /* in */
            bool withPrefix,                      /* in */
            bool withSuffix,                      /* in */
            ref string prefix,                    /* in, out */
            ref string suffix,                    /* in, out */
            ref IList<StringBuilder> subPatterns, /* in, out */
            ref int stopIndex,                    /* out */
            ref Result error                      /* out */
            )
        {
            //
            // NOTE: A null pattern is never allowed, return an error.
            //
            if (pattern == null)
            {
                error = "invalid pattern";
                return ReturnCode.Error;
            }

            //
            // NOTE: An empty pattern is OK, do nothing, and let the caller
            //       handle it.
            //
            int length = pattern.Length;

            if (length == 0)
                return ReturnCode.Ok;

            //
            // NOTE: If there are no sub-patterns, skip this method, and let
            //       the caller handle it.
            //
            if (pattern.IndexOfAny(SubPatternChars, startIndex) == Index.Invalid)
                return ReturnCode.Ok;

            //
            // NOTE: The local list of sub-patterns.  This will become the
            //       final result to be returned to the caller upon success.
            //
            IList<StringBuilder> localSubPatterns = null;

            //
            // NOTE: Initialize various state variables used inside (and after)
            //       the loop to assist in keeping track of the sub-patterns.
            //
            StringBuilder subPattern = null; /* Being built, no prefix/suffix. */
            int prefixIndex = Index.Invalid; /* Index of outer open brace. */
            string localPrefix = null;       /* Before first open brace. */
            string localSuffix = null;       /* After last close brace. */
            int levels = 0;                  /* Brace nesting level. */
            bool quoted = false;             /* True when escape active. */

            //
            // NOTE: Process each character in the pattern string, starting at
            //       the specified location.  This loop will terminate early if
            //       an error is found -OR- the "firstOnly" parameter is set.
            //       In that case, this method will return an appropriate error
            //       message to the caller.
            //
            int index = startIndex;

            for (; index < length; index++)
            {
                //
                // NOTE: Grab the current character within the pattern.
                //
                char character = pattern[index];

                //
                // NOTE: Was the previous character the start of an escape?  If
                //       so, treat this character as a normal one, by appending
                //       it verbatim (i.e. at the bottom of the loop body).
                //
                if (quoted)
                {
                    quoted = false;
                }
                //
                // NOTE: Is the current character the start of an escape?  If
                //       so, set the flag and then treat it as a character, by
                //       appending it verbatim (i.e. at the bottom of the loop
                //       body).
                //
                else if (character == Characters.Backslash)
                {
                    quoted = true;
                }
                //
                // NOTE: Is the current character the start of a sub-pattern
                //       list?
                //
                else if (character == Characters.OpenBrace)
                {
                    //
                    // NOTE: Keep track of how many nested sub-pattern lists
                    //       are active by adding one here.
                    //
                    levels++;

                    //
                    // NOTE: The first outermost list is treated specially.
                    //       The starting index for it is saved so that it
                    //       may be used later to calculate the sub-pattern
                    //       prefix.
                    //
                    if (levels == 1)
                    {
                        if (prefixIndex == Index.Invalid)
                            prefixIndex = index;

                        continue;
                    }
                }
                //
                // NOTE: Is the current character the end of a sub-pattern
                //       list?
                //
                else if (character == Characters.CloseBrace)
                {
                    //
                    // NOTE: Keep track of how many nested sub-pattern lists
                    //       are active by removing one here.
                    //
                    levels--;

                    //
                    // NOTE: If the nesting level falls below zero, the pattern
                    //       is malformed.
                    //
                    if (levels < 0)
                    {
                        error = "unmatched close-brace in pattern";
                        return ReturnCode.Error;
                    }
                    //
                    // NOTE: If the nesting level is exactly zero, we are ready
                    //       to calculate the sub-pattern prefix and suffix,
                    //       which must later be applied to every sub-pattern.
                    //
                    else if (levels == 0)
                    {
                        //
                        // NOTE: Grab the entire portion of the pattern prior
                        //       to the first open brace.
                        //
                        if (localPrefix == null)
                        {
                            localPrefix = pattern.Substring(
                                startIndex, prefixIndex - startIndex);
                        }

                        //
                        // NOTE: Grab the entire portion of the pattern after
                        //       what we believe is the last close brace.  If
                        //       an additional close brace is found after this
                        //       point, either an error will be generated and
                        //       this suffix will never be used -OR- that new
                        //       suffix will replace this one.
                        //
                        localSuffix = pattern.Substring(
                            index + 1, length - index - 1);

                        //
                        // NOTE: Has there been any sub-pattern content since
                        //       the last comma or open brace?
                        //
                        if (empty ||
                            ((subPattern != null) && (subPattern.Length > 0)))
                        {
                            if (localSubPatterns == null)
                                localSubPatterns = new List<StringBuilder>();

                            localSubPatterns.Add((subPattern != null) ?
                                subPattern : StringOps.NewStringBuilder());

                            subPattern = StringOps.NewStringBuilder();
                        }

                        //
                        // NOTE: Empty sub-pattern fragment, skip it.
                        //
                        if (firstOnly)
                            break;
                        else
                            continue;
                    }
                }
                //
                // NOTE: When processing the first level of braces, handling
                //       the comma by creating another sub-pattern from all
                //       the content we have seen since the last comma or
                //       open brace.
                //
                else if ((levels == 1) && (character == Characters.Comma))
                {
                    //
                    // NOTE: Has there been any sub-pattern content since
                    //       the last comma or open brace?
                    //
                    if (empty ||
                        ((subPattern != null) && (subPattern.Length > 0)))
                    {
                        if (localSubPatterns == null)
                            localSubPatterns = new List<StringBuilder>();

                        localSubPatterns.Add((subPattern != null) ?
                            subPattern : StringOps.NewStringBuilder());

                        subPattern = StringOps.NewStringBuilder();
                    }

                    continue;
                }

                //
                // NOTE: Skip the entire prefix (i.e. the portion of the
                //       pattern prior to the outer open brace), without
                //       regard to how many levels exist.
                //
                if (prefixIndex != Index.Invalid)
                {
                    if (subPattern == null)
                        subPattern = new StringBuilder();

                    subPattern.Append(character);
                }
            }

            //
            // NOTE: If there are any open braces active, the pattern is
            //       malformed.
            //
            if (levels > 0)
            {
                error = "unmatched open-brace in pattern";
                return ReturnCode.Error;
            }

            //
            // NOTE: Is sub-pattern prefix and/or suffix handling enabled?
            //
            FixupSubPatterns(
                localSubPatterns, localPrefix, localSuffix, withPrefix,
                withSuffix);

            //
            // NOTE: Success, commit changes to the variable provided by
            //       the caller and return.
            //
            prefix = localPrefix;
            suffix = localSuffix;

            if (localSubPatterns != null)
            {
                if (subPatterns != null)
                {
                    GenericOps<StringBuilder>.AddRange(
                        subPatterns, localSubPatterns);
                }
                else
                {
                    subPatterns = new List<StringBuilder>(
                        localSubPatterns);
                }
            }

            stopIndex = index;
            return ReturnCode.Ok;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool Match(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            string pattern,
            bool noCase
            )
        {
            return Match(interpreter, mode, text, pattern, noCase, null);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static bool Match(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            string pattern,
            bool noCase,
            IComparer<string> comparer
            )
        {
            RegexOptions regExOptions = noCase ?
                RegexOptions.IgnoreCase : RegexOptions.None;

            return Match(
                interpreter, mode, text, pattern, noCase, comparer,
                regExOptions);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool Match(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            string pattern,
            bool noCase,
            IComparer<string> comparer,
            RegexOptions regExOptions
            )
        {
            bool match = false;
            Result error = null;

            if (Match(
                    interpreter, mode, text, pattern, noCase, comparer,
                    regExOptions, ref match, ref error) == ReturnCode.Ok)
            {
                return match;
            }
            else
            {
                return false;
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode Match(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            string pattern,
            bool noCase,
            ref bool match,
            ref Result error
            )
        {
            RegexOptions regExOptions = noCase ?
                RegexOptions.IgnoreCase : RegexOptions.None;

            return Match(
                interpreter, mode, text, pattern, noCase, null, regExOptions,
                ref match, ref error);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode Match(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            string pattern,
            bool noCase,
            IComparer<string> comparer,
            RegexOptions regExOptions,
            ref bool match,
            ref Result error
            )
        {
            if (FlagOps.HasFlags(mode, MatchMode.SubPattern, true))
            {
                //
                // NOTE: There may be any number of "sub-patterns" to match
                //       against in an OR-wise fashion.
                //
                bool empty = FlagOps.HasFlags(
                    mode, MatchMode.EmptySubPattern, true);

                StringList subPatterns = null;

                if (SplitSubPatterns(
                        pattern, 0, empty, ref subPatterns,
                        ref error) == ReturnCode.Ok)
                {
                    if (subPatterns != null)
                    {
                        foreach (string subPattern in subPatterns)
                        {
                            if (Match( /* RECURSION */
                                    interpreter, mode, text, subPattern,
                                    noCase, comparer, regExOptions,
                                    ref match, ref error) != ReturnCode.Ok)
                            {
                                return ReturnCode.Error;
                            }

                            if (match)
                                break;
                        }

                        return ReturnCode.Ok;
                    }
                    else
                    {
                        //
                        // NOTE: There is exactly one pattern to match against.
                        //
                        return MatchCore(
                            interpreter, mode, text, pattern, noCase, comparer,
                            regExOptions, ref match, ref error);
                    }
                }
                else
                {
                    //
                    // NOTE: Unable to split the sub-patterns.
                    //
                    return ReturnCode.Error;
                }
            }
            else
            {
                //
                // NOTE: There is exactly one pattern to match against.
                //
                return MatchCore(
                    interpreter, mode, text, pattern, noCase, comparer,
                    regExOptions, ref match, ref error);
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode MatchCore(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            string pattern,
            bool noCase,
            IComparer<string> comparer,
            RegexOptions regExOptions,
            ref bool match,
            ref Result error
            )
        {
            if (FlagOps.HasFlags(mode, MatchMode.ForceCase, true))
                noCase = false;
            else if (FlagOps.HasFlags(mode, MatchMode.NoCase, true))
                noCase = true;

            if (FlagOps.HasFlags(mode, MatchMode.Exact, false))
            {
                if (comparer != null)
                {
                    //
                    // NOTE: This might be case-sensitive.  It depends on the
                    //       exact implementation of the IComparer.
                    //
                    if (comparer.Compare(text, pattern) == 0 /* EQUALS */)
                    {
                        match = true;
                    }
                    else
                    {
                        match = false;
                    }
                }
                else
                {
                    //
                    // NOTE: Exact matching using the String.Compare method
                    //       (may be case-sensitive, depending on the noCase
                    //       parameter value).
                    //
                    if (String.Compare(
                            text, pattern, noCase ?
                                UserNoCaseStringComparisonType :
                                UserStringComparisonType) == 0)
                    {
                        match = true;
                    }
                    else
                    {
                        match = false;
                    }
                }

                return ReturnCode.Ok;
            }
            else if (FlagOps.HasFlags(mode, MatchMode.SubString, false))
            {
                //
                // NOTE: Prefix matching using the String.Compare method
                //       (may be case-sensitive, depending on the noCase
                //       parameter value).
                //
                int length = (pattern != null) ? pattern.Length : 0;

                if (String.Compare(
                        text, 0, pattern, 0, length, noCase ?
                            UserNoCaseStringComparisonType :
                            UserStringComparisonType) == 0)
                {
                    match = true;
                }
                else
                {
                    match = false;
                }

                return ReturnCode.Ok;
            }
            else if (FlagOps.HasFlags(mode, MatchMode.Glob, false))
            {
                //
                // NOTE: Glob matching using the StringMatch method (may be
                //       case-sensitive, depending on the noCase parameter
                //       value).
                //
                if (Parser.StringMatch(
                        interpreter, text, 0, pattern, 0, noCase))
                {
                    match = true;
                }
                else
                {
                    match = false;
                }

                return ReturnCode.Ok;
            }
            else if (FlagOps.HasFlags(mode, MatchMode.RegExp, false))
            {
                try
                {
                    if ((text != null) && (pattern != null) &&
                        Regex.IsMatch(text, pattern, regExOptions))
                    {
                        match = true;
                    }
                    else
                    {
                        match = false;
                    }

                    return ReturnCode.Ok;
                }
                catch (Exception e)
                {
                    error = e;
                }
            }
            else if (FlagOps.HasFlags(mode, MatchMode.Integer, false))
            {
                int textIntValue = 0;
                int patternIntValue = 0;

                if ((Value.GetInteger2(
                        text, ValueFlags.AnyInteger, null,
                        ref textIntValue, ref error) == ReturnCode.Ok) &&
                    (Value.GetInteger2(
                        pattern, ValueFlags.AnyInteger, null,
                        ref patternIntValue, ref error) == ReturnCode.Ok))
                {
                    if (textIntValue == patternIntValue)
                    {
                        match = true;
                    }
                    else
                    {
                        match = false;
                    }

                    return ReturnCode.Ok;
                }
            }
            else
            {
                error = "cannot match, no supported mode found";
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode MatchAnyOrAll(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            StringList patterns,
            bool all,
            bool noCase,
            ref bool match,
            ref Result error
            )
        {
            RegexOptions regExOptions = noCase ?
                RegexOptions.IgnoreCase : RegexOptions.None;

            return MatchAnyOrAll(
                interpreter, mode, text, patterns, all, noCase, null,
                regExOptions, ref match, ref error);
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static ReturnCode MatchAnyOrAll(
            Interpreter interpreter,
            MatchMode mode,
            string text,
            StringList patterns,
            bool all,
            bool noCase,
            IComparer<string> comparer,
            RegexOptions regExOptions,
            ref bool match,
            ref Result error
            )
        {
            try
            {
                if (patterns != null)
                {
                    bool localMatch = false;
                    ReturnCode code = ReturnCode.Ok;

                    foreach (string pattern in patterns)
                    {
                        code = Match(
                            interpreter, mode, text, pattern, noCase,
                            comparer, regExOptions, ref localMatch,
                            ref error);

                        if (code != ReturnCode.Ok)
                            break;

                        if (!all && localMatch)
                            break;
                        else if (all && !localMatch)
                            break;
                    }

                    if (code == ReturnCode.Ok)
                        match = localMatch;

                    return code;
                }
                else
                {
                    error = "invalid pattern list";
                }
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }
    }
}
