< Summary

Information
Class: Ice.UtilInternal.Options
Assembly: Ice
File(s): /_/csharp/src/Ice/UtilInternal/Options.cs
Tag: 125_37167941578
Line coverage
75%
Covered lines: 93
Uncovered lines: 31
Coverable lines: 124
Total lines: 412
Line coverage: 75%
Branch coverage
83%
Covered branches: 114
Total branches: 136
Branch coverage: 83.8%
Method coverage
100%
Covered methods: 1
Fully covered methods: 0
Total methods: 1
Method coverage: 100%
Full method coverage: 0%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
split(...)83.82%42513675%

File(s)

/_/csharp/src/Ice/UtilInternal/Options.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4using System.Globalization;
 5
 6namespace Ice.UtilInternal;
 7
 8public sealed class Options
 9{
 10    private enum State
 11    {
 12        Normal,
 13        DoubleQuote,
 14        SingleQuote,
 15        ANSIQuote
 16    }
 17
 18    public static string[]
 19    split(string line)
 20    {
 121        string IFS = " \t\n";
 22
 123        string l = line.Trim();
 124        if (l.Length == 0)
 25        {
 126            return [];
 27        }
 28
 129        State state = State.Normal;
 30
 131        string arg = "";
 132        var vec = new List<string>();
 133        for (int i = 0; i < l.Length; ++i)
 34        {
 135            char c = l[i];
 36            switch (state)
 37            {
 38                case State.Normal:
 39                {
 40                    switch (c)
 41                    {
 42                        case '\\':
 43                        {
 44                            //
 45                            // Ignore a backslash at the end of the string,
 46                            // and strip backslash-newline pairs. If a
 47                            // backslash is followed by a space, single quote,
 48                            // double quote, or dollar sign, we drop the backslash
 49                            // and write the space, single quote, double quote,
 50                            // or dollar sign. This is necessary to allow quotes
 51                            // to be escaped. Dropping the backslash preceding a
 52                            // space deviates from bash quoting rules, but is
 53                            // necessary so we don't drop backslashes from Windows
 54                            // path names.)
 55                            //
 156                            if (i < l.Length - 1 && l[++i] != '\n')
 57                            {
 158                                switch (l[i])
 59                                {
 60                                    case ' ':
 61                                    case '$':
 62                                    case '\'':
 63                                    case '"':
 64                                    {
 165                                        arg += l[i];
 166                                        break;
 67                                    }
 68                                    default:
 69                                    {
 070                                        arg += '\\';
 071                                        arg += l[i];
 072                                        break;
 73                                    }
 74                                }
 75                            }
 76                            break;
 77                        }
 78                        case '\'':
 79                        {
 180                            state = State.SingleQuote;
 181                            break;
 82                        }
 83                        case '"':
 84                        {
 185                            state = State.DoubleQuote;
 186                            break;
 87                        }
 88                        case '$':
 89                        {
 190                            if (i < l.Length - 1 && l[i + 1] == '\'')
 91                            {
 192                                state = State.ANSIQuote; // Bash uses $'<text>' to allow ANSI escape sequences
 93                                                         // within <text>.
 194                                ++i;
 95                            }
 96                            else
 97                            {
 098                                arg += '$';
 99                            }
 0100                            break;
 101                        }
 102                        default:
 103                        {
 1104                            if (IFS.Contains(l[i], StringComparison.Ordinal))
 105                            {
 1106                                vec.Add(arg);
 1107                                arg = "";
 108
 109                                //
 110                                // Move to start of next argument.
 111                                //
 1112                                while (++i < l.Length && IFS.Contains(l[i], StringComparison.Ordinal))
 113                                {
 114                                }
 1115                                --i;
 116                            }
 117                            else
 118                            {
 1119                                arg += l[i];
 120                            }
 1121                            break;
 122                        }
 123                    }
 124                    break;
 125                }
 126                case State.DoubleQuote:
 127                {
 128                    //
 129                    // Within double quotes, only backslash retains its special
 130                    // meaning, and only if followed by double quote, backslash,
 131                    // or newline. If not followed by one of these characters,
 132                    // both the backslash and the character are preserved.
 133                    //
 1134                    if (c == '\\' && i < l.Length - 1)
 135                    {
 1136                        switch (c = l[++i])
 137                        {
 138                            case '"':
 139                            case '\\':
 140                            case '\n':
 141                            {
 1142                                arg += c;
 1143                                break;
 144                            }
 145                            default:
 146                            {
 1147                                arg += '\\';
 1148                                arg += c;
 1149                                break;
 150                            }
 151                        }
 152                    }
 1153                    else if (c == '"') // End of double-quote mode.
 154                    {
 1155                        state = State.Normal;
 156                    }
 157                    else
 158                    {
 1159                        arg += c; // Everything else is taken literally.
 160                    }
 1161                    break;
 162                }
 163                case State.SingleQuote:
 164                {
 1165                    if (c == '\'') // End of single-quote mode.
 166                    {
 1167                        state = State.Normal;
 168                    }
 169                    else
 170                    {
 1171                        arg += c; // Everything else is taken literally.
 172                    }
 1173                    break;
 174                }
 175                case State.ANSIQuote:
 176                {
 177                    switch (c)
 178                    {
 179                        case '\\':
 180                        {
 1181                            if (i == l.Length - 1)
 182                            {
 183                                break;
 184                            }
 1185                            switch (c = l[++i])
 186                            {
 187                                //
 188                                // Single-letter escape sequences.
 189                                //
 190                                case 'a':
 191                                {
 0192                                    arg += '\a';
 0193                                    break;
 194                                }
 195                                case 'b':
 196                                {
 0197                                    arg += '\b';
 0198                                    break;
 199                                }
 200                                case 'f':
 201                                {
 0202                                    arg += '\f';
 0203                                    break;
 204                                }
 205                                case 'n':
 206                                {
 0207                                    arg += '\n';
 0208                                    break;
 209                                }
 210                                case 'r':
 211                                {
 0212                                    arg += '\r';
 0213                                    break;
 214                                }
 215                                case 't':
 216                                {
 0217                                    arg += '\t';
 0218                                    break;
 219                                }
 220                                case 'v':
 221                                {
 0222                                    arg += '\v';
 0223                                    break;
 224                                }
 225                                case '\\':
 226                                {
 1227                                    arg += '\\';
 1228                                    break;
 229                                }
 230                                case '\'':
 231                                {
 0232                                    arg += '\'';
 0233                                    break;
 234                                }
 235                                case 'e': // Not ANSI-C, but used by bash.
 236                                {
 0237                                    arg += '\u001B';
 0238                                    break;
 239                                }
 240
 241                                //
 242                                // Process up to three octal digits.
 243                                //
 244                                case '0':
 245                                case '1':
 246                                case '2':
 247                                case '3':
 248                                case '4':
 249                                case '5':
 250                                case '6':
 251                                case '7':
 252                                {
 253                                    const string octalDigits = "01234567";
 1254                                    short s = 0;
 255                                    int j;
 256                                    for (
 1257                                        j = i;
 1258                                        j < i + 3 &&
 1259                                            j < l.Length &&
 1260                                            octalDigits.Contains(c = l[j], StringComparison.Ordinal);
 1261                                        ++j)
 262                                    {
 1263                                        s = (short)((s * 8) + c - '0');
 264                                    }
 1265                                    i = j - 1;
 1266                                    arg += (char)s;
 1267                                    break;
 268                                }
 269
 270                                //
 271                                // Process up to two hex digits.
 272                                //
 273                                case 'x':
 274                                {
 275                                    const string hexDigits = "0123456789abcdefABCDEF";
 1276                                    if (i < l.Length - 1 && !hexDigits.Contains(l[i + 1], StringComparison.Ordinal))
 277                                    {
 0278                                        arg += '\\';
 0279                                        arg += 'x';
 0280                                        break;
 281                                    }
 282
 1283                                    short s = 0;
 284                                    int j;
 285                                    for (
 1286                                        j = i + 1;
 1287                                        j < i + 3 &&
 1288                                            j < l.Length &&
 1289                                            hexDigits.Contains(c = l[j], StringComparison.Ordinal);
 1290                                        ++j)
 291                                    {
 1292                                        s *= 16;
 1293                                        if (char.IsDigit(c))
 294                                        {
 1295                                            s += (short)(c - '0');
 296                                        }
 1297                                        else if (char.IsLower(c))
 298                                        {
 0299                                            s += (short)(c - 'a' + 10);
 300                                        }
 301                                        else
 302                                        {
 1303                                            s += (short)(c - 'A' + 10);
 304                                        }
 305                                    }
 1306                                    i = j - 1;
 1307                                    arg += (char)s;
 1308                                    break;
 309                                }
 310
 311                                //
 312                                // Process control-chars.
 313                                //
 314                                case 'c':
 315                                {
 1316                                    if (i == l.Length - 1)
 317                                    {
 318                                        // Nothing after the \c: keep it unchanged. The missing closing quote is
 319                                        // reported after the loop.
 1320                                        arg += "\\c";
 1321                                        break;
 322                                    }
 323
 1324                                    c = l[++i];
 1325                                    if (
 1326                                        (char.ToUpper(c, CultureInfo.InvariantCulture) >= 'A' &&
 1327                                        char.ToUpper(c, CultureInfo.InvariantCulture) <= 'Z') ||
 1328                                       c == '@' ||
 1329                                       (c >= '[' && c <= '_'))
 330                                    {
 1331                                        arg += (char)(char.ToUpper(c, CultureInfo.InvariantCulture) - '@');
 332                                    }
 333                                    else
 334                                    {
 335                                        //
 336                                        // Bash does not define what should happen if a \c
 337                                        // is not followed by a recognized control character.
 338                                        // We simply treat this case like other unrecognized
 339                                        // escape sequences, that is, we preserve the escape
 340                                        // sequence unchanged.
 341                                        //
 0342                                        arg += '\\';
 0343                                        arg += 'c';
 0344                                        arg += c;
 345                                    }
 0346                                    break;
 347                                }
 348
 349                                //
 350                                // If inside an ANSI-quoted string, a backslash isn't followed by
 351                                // one of the recognized characters, both the backslash and the
 352                                // character are preserved.
 353                                //
 354                                default:
 355                                {
 1356                                    arg += '\\';
 1357                                    arg += c;
 1358                                    break;
 359                                }
 360                            }
 361                            break;
 362                        }
 363                        case '\'': // End of ANSI-quote mode.
 364                        {
 1365                            state = State.Normal;
 1366                            break;
 367                        }
 368                        default:
 369                        {
 1370                            arg += c; // Everything else is taken literally.
 371                            break;
 372                        }
 373                    }
 374                    break;
 375                }
 376                default:
 377                {
 378                    Debug.Assert(false);
 379                    break;
 380                }
 381            }
 382        }
 383
 384        switch (state)
 385        {
 386            case State.Normal:
 387            {
 1388                vec.Add(arg);
 1389                break;
 390            }
 391            case State.SingleQuote:
 392            {
 1393                throw new ParseException("missing closing single quote");
 394            }
 395            case State.DoubleQuote:
 396            {
 1397                throw new ParseException("missing closing double quote");
 398            }
 399            case State.ANSIQuote:
 400            {
 1401                throw new ParseException("unterminated $' quote");
 402            }
 403            default:
 404            {
 405                Debug.Assert(false);
 406                break;
 407            }
 408        }
 409
 1410        return vec.ToArray();
 411    }
 412}

Methods/Properties

split(string)