< Summary

Information
Class: Ice.UtilInternal.Options
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/UtilInternal/Options.cs
Tag: 71_18251537082
Line coverage
74%
Covered lines: 90
Uncovered lines: 31
Coverable lines: 121
Total lines: 404
Line coverage: 74.3%
Branch coverage
83%
Covered branches: 112
Total branches: 134
Branch coverage: 83.5%
Method coverage
100%
Covered methods: 1
Total methods: 1
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
split(...)83.58%435.9613474.38%

File(s)

/home/runner/work/ice/ice/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                                    c = l[++i];
 1317                                    if (
 1318                                        (char.ToUpper(c, CultureInfo.InvariantCulture) >= 'A' &&
 1319                                        char.ToUpper(c, CultureInfo.InvariantCulture) <= 'Z') ||
 1320                                       c == '@' ||
 1321                                       (c >= '[' && c <= '_'))
 322                                    {
 1323                                        arg += (char)(char.ToUpper(c, CultureInfo.InvariantCulture) - '@');
 324                                    }
 325                                    else
 326                                    {
 327                                        //
 328                                        // Bash does not define what should happen if a \c
 329                                        // is not followed by a recognized control character.
 330                                        // We simply treat this case like other unrecognized
 331                                        // escape sequences, that is, we preserve the escape
 332                                        // sequence unchanged.
 333                                        //
 0334                                        arg += '\\';
 0335                                        arg += 'c';
 0336                                        arg += c;
 337                                    }
 0338                                    break;
 339                                }
 340
 341                                //
 342                                // If inside an ANSI-quoted string, a backslash isn't followed by
 343                                // one of the recognized characters, both the backslash and the
 344                                // character are preserved.
 345                                //
 346                                default:
 347                                {
 1348                                    arg += '\\';
 1349                                    arg += c;
 1350                                    break;
 351                                }
 352                            }
 353                            break;
 354                        }
 355                        case '\'': // End of ANSI-quote mode.
 356                        {
 1357                            state = State.Normal;
 1358                            break;
 359                        }
 360                        default:
 361                        {
 1362                            arg += c; // Everything else is taken literally.
 363                            break;
 364                        }
 365                    }
 366                    break;
 367                }
 368                default:
 369                {
 370                    Debug.Assert(false);
 371                    break;
 372                }
 373            }
 374        }
 375
 376        switch (state)
 377        {
 378            case State.Normal:
 379            {
 1380                vec.Add(arg);
 1381                break;
 382            }
 383            case State.SingleQuote:
 384            {
 1385                throw new ParseException("missing closing single quote");
 386            }
 387            case State.DoubleQuote:
 388            {
 1389                throw new ParseException("missing closing double quote");
 390            }
 391            case State.ANSIQuote:
 392            {
 1393                throw new ParseException("unterminated $' quote");
 394            }
 395            default:
 396            {
 397                Debug.Assert(false);
 398                break;
 399            }
 400        }
 401
 1402        return vec.ToArray();
 403    }
 404}

Methods/Properties

split(string)