예제 #1
0
        private const char FORMAT_END    = ' '; // End of format was found.

        public TCL.CompletionCode CmdProc(Interp interp, TclObject[] argv)
        {
            int arg;                         // Index of next argument to consume.

            char[] format = null;            // User specified format string.
            char   cmd;                      // Current format character.
            int    cursor;                   // Current position within result buffer.
            int    maxPos;                   // Greatest position within result buffer that cursor has visited.
            int    value = 0;                // Current integer value to be packed.
            int    offset, size = 0, length; // Initialized to avoid compiler warning.

            if (argv.Length < 2)
            {
                throw new TclNumArgsException(interp, 1, argv, "option ?arg arg ...?");
            }
            int cmdIndex = TclIndex.Get(interp, argv[1], _validCmds, "option", 0);

            switch (cmdIndex)
            {
            case CMD_FORMAT:
            {
                if (argv.Length < 3)
                {
                    throw new TclNumArgsException(interp, 2, argv, "formatString ?arg arg ...?");
                }
                // To avoid copying the data, we format the string in two passes. The first pass computes the size of the output buffer.  The
                // second pass places the formatted data into the buffer.
                format = argv[2].ToString().ToCharArray();
                arg    = 3;
                length = 0;
                offset = 0;
                int parsePos = 0;
                while ((cmd = GetFormatSpec(format, ref parsePos)) != FORMAT_END)
                {
                    int count = GetFormatCount(format, ref parsePos);
                    switch (cmd)
                    {
                    case 'a':
                    case 'A':
                    case 'b':
                    case 'B':
                    case 'h':
                    case 'H':
                    {
                        // For string-type specifiers, the count corresponds to the number of bytes in a single argument.
                        if (arg >= argv.Length)
                        {
                            MissingArg(interp);
                        }
                        if (count == BINARY_ALL)
                        {
                            count = TclByteArray.getLength(interp, argv[arg]);
                        }
                        else if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        arg++;
                        switch (cmd)
                        {
                        case 'a':
                        case 'A':
                            offset += count;
                            break;

                        case 'b':
                        case 'B':
                            offset += (count + 7) / 8;
                            break;

                        case 'h':
                        case 'H':
                            offset += (count + 1) / 2;
                            break;
                        }
                        break;
                    }

                    case 'c':
                    case 's':
                    case 'S':
                    case 'i':
                    case 'I':
                    case 'f':
                    case 'd':
                    {
                        if (arg >= argv.Length)
                        {
                            MissingArg(interp);
                        }
                        switch (cmd)
                        {
                        case 'c':
                            size = 1;
                            break;

                        case 's':
                        case 'S':
                            size = 2;
                            break;

                        case 'i':
                        case 'I':
                            size = 4;
                            break;

                        case 'f':
                            size = 4;
                            break;

                        case 'd':
                            size = 8;
                            break;
                        }
                        // For number-type specifiers, the count corresponds to the number of elements in the list stored in
                        // a single argument.  If no count is specified, then the argument is taken as a single non-list value.
                        if (count == BINARY_NOCOUNT)
                        {
                            arg++;
                            count = 1;
                        }
                        else
                        {
                            int listc = TclList.getLength(interp, argv[arg++]);
                            if (count == BINARY_ALL)
                            {
                                count = listc;
                            }
                            else if (count > listc)
                            {
                                throw new TclException(interp, "number of elements in list" + " does not match count");
                            }
                        }
                        offset += count * size;
                        break;
                    }

                    case 'x':
                    {
                        if (count == BINARY_ALL)
                        {
                            throw new TclException(interp, "cannot use \"*\"" + " in format string with \"x\"");
                        }
                        if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        offset += count;
                        break;
                    }

                    case 'X':
                    {
                        if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        if (count > offset || count == BINARY_ALL)
                        {
                            count = offset;
                        }
                        if (offset > length)
                        {
                            length = offset;
                        }
                        offset -= count;
                        break;
                    }

                    case '@':
                    {
                        if (offset > length)
                        {
                            length = offset;
                        }
                        if (count == BINARY_ALL)
                        {
                            offset = length;
                        }
                        else if (count == BINARY_NOCOUNT)
                        {
                            AlephWithoutCount(interp);
                        }
                        else
                        {
                            offset = count;
                        }
                        break;
                    }

                    default:
                    {
                        BadField(interp, cmd);
                    }
                    break;
                    }
                }
                if (offset > length)
                {
                    length = offset;
                }
                if (length == 0)
                {
                    return(TCL.CompletionCode.RETURN);
                }

                // Prepare the result object by preallocating the calculated number of bytes and filling with nulls.
                TclObject resultObj   = TclByteArray.NewInstance();
                byte[]    resultBytes = TclByteArray.SetLength(interp, resultObj, length);
                interp.SetResult(resultObj);
                // Pack the data into the result object.  Note that we can skip the error checking during this pass, since we have already parsed the string once.
                arg      = 3;
                cursor   = 0;
                maxPos   = cursor;
                parsePos = 0;
                while ((cmd = GetFormatSpec(format, ref parsePos)) != FORMAT_END)
                {
                    int count = GetFormatCount(format, ref parsePos);
                    if (count == 0 && cmd != '@')
                    {
                        arg++;
                        continue;
                    }
                    switch (cmd)
                    {
                    case 'a':
                    case 'A':
                    {
                        byte   pad   = (cmd == 'a' ? (byte)0 : (byte)SupportClass.Identity(' '));
                        byte[] bytes = TclByteArray.getBytes(interp, argv[arg++]);
                        length = bytes.Length;
                        if (count == BINARY_ALL)
                        {
                            count = length;
                        }
                        else if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        if (length >= count)
                        {
                            Array.Copy(bytes, 0, resultBytes, cursor, count);
                        }
                        else
                        {
                            Array.Copy(bytes, 0, resultBytes, cursor, length);
                            for (int ix = 0; ix < count - length; ix++)
                            {
                                resultBytes[cursor + length + ix] = pad;
                            }
                        }
                        cursor += count;
                        break;
                    }

                    case 'b':
                    case 'B':
                    {
                        char[] str = argv[arg++].ToString().ToCharArray();
                        if (count == BINARY_ALL)
                        {
                            count = str.Length;
                        }
                        else if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        int last = cursor + ((count + 7) / 8);
                        if (count > str.Length)
                        {
                            count = str.Length;
                        }
                        if (cmd == 'B')
                        {
                            for (offset = 0; offset < count; offset++)
                            {
                                value <<= 1;
                                if (str[offset] == '1')
                                {
                                    value |= 1;
                                }
                                else if (str[offset] != '0')
                                {
                                    ExpectedButGot(interp, "binary", new string(str));
                                }
                                if (((offset + 1) % 8) == 0)
                                {
                                    resultBytes[cursor++] = (byte)value;
                                    value = 0;
                                }
                            }
                        }
                        else
                        {
                            for (offset = 0; offset < count; offset++)
                            {
                                value >>= 1;
                                if (str[offset] == '1')
                                {
                                    value |= 128;
                                }
                                else if (str[offset] != '0')
                                {
                                    ExpectedButGot(interp, "binary", new string(str));
                                }
                                if (((offset + 1) % 8) == 0)
                                {
                                    resultBytes[cursor++] = (byte)value;
                                    value = 0;
                                }
                            }
                        }
                        if ((offset % 8) != 0)
                        {
                            if (cmd == 'B')
                            {
                                value <<= 8 - (offset % 8);
                            }
                            else
                            {
                                value >>= 8 - (offset % 8);
                            }
                            resultBytes[cursor++] = (byte)value;
                        }
                        while (cursor < last)
                        {
                            resultBytes[cursor++] = 0;
                        }
                        break;
                    }

                    case 'h':
                    case 'H':
                    {
                        char[] str = argv[arg++].ToString().ToCharArray();
                        if (count == BINARY_ALL)
                        {
                            count = str.Length;
                        }
                        else if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        int last = cursor + ((count + 1) / 2);
                        if (count > str.Length)
                        {
                            count = str.Length;
                        }
                        if (cmd == 'H')
                        {
                            for (offset = 0; offset < count; offset++)
                            {
                                value <<= 4;
                                int c = HEXDIGITS.IndexOf(Char.ToLower(str[offset]));
                                if (c < 0)
                                {
                                    ExpectedButGot(interp, "hexadecimal", new string(str));
                                }
                                value |= (c & 0xf);
                                if ((offset % 2) != 0)
                                {
                                    resultBytes[cursor++] = (byte)value;
                                    value = 0;
                                }
                            }
                        }
                        else
                        {
                            for (offset = 0; offset < count; offset++)
                            {
                                value >>= 4;
                                int c = HEXDIGITS.IndexOf(Char.ToLower(str[offset]));
                                if (c < 0)
                                {
                                    ExpectedButGot(interp, "hexadecimal", new string(str));
                                }
                                value |= ((c << 4) & 0xf0);
                                if ((offset % 2) != 0)
                                {
                                    resultBytes[cursor++] = (byte)value;
                                    value = 0;
                                }
                            }
                        }
                        if ((offset % 2) != 0)
                        {
                            if (cmd == 'H')
                            {
                                value <<= 4;
                            }
                            else
                            {
                                value >>= 4;
                            }
                            resultBytes[cursor++] = (byte)value;
                        }
                        while (cursor < last)
                        {
                            resultBytes[cursor++] = 0;
                        }
                        break;
                    }

                    case 'c':
                    case 's':
                    case 'S':
                    case 'i':
                    case 'I':
                    case 'f':
                    case 'd':
                    {
                        TclObject[] listv;
                        if (count == BINARY_NOCOUNT)
                        {
                            listv    = new TclObject[1];
                            listv[0] = argv[arg++];
                            count    = 1;
                        }
                        else
                        {
                            listv = TclList.getElements(interp, argv[arg++]);
                            if (count == BINARY_ALL)
                            {
                                count = listv.Length;
                            }
                        }
                        for (int ix = 0; ix < count; ix++)
                        {
                            cursor = FormatNumber(interp, cmd, listv[ix], resultBytes, cursor);
                        }
                        break;
                    }

                    case 'x':
                    {
                        if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        for (int ix = 0; ix < count; ix++)
                        {
                            resultBytes[cursor++] = 0;
                        }
                        break;
                    }

                    case 'X':
                    {
                        if (cursor > maxPos)
                        {
                            maxPos = cursor;
                        }
                        if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        if (count == BINARY_ALL || count > cursor)
                        {
                            cursor = 0;
                        }
                        else
                        {
                            cursor -= count;
                        }
                        break;
                    }

                    case '@':
                    {
                        if (cursor > maxPos)
                        {
                            maxPos = cursor;
                        }
                        if (count == BINARY_ALL)
                        {
                            cursor = maxPos;
                        }
                        else
                        {
                            cursor = count;
                        }
                        break;
                    }
                    }
                }
                break;
            }

            case CMD_SCAN:
            {
                if (argv.Length < 4)
                {
                    throw new TclNumArgsException(interp, 2, argv, "value formatString ?varName varName ...?");
                }
                byte[] src = TclByteArray.getBytes(interp, argv[2]);
                length = src.Length;
                format = argv[3].ToString().ToCharArray();
                arg    = 4;
                cursor = 0;
                offset = 0;
                int parsePos = 0;
                while ((cmd = GetFormatSpec(format, ref parsePos)) != FORMAT_END)
                {
                    int count = GetFormatCount(format, ref parsePos);
                    switch (cmd)
                    {
                    case 'a':
                    case 'A':
                    {
                        if (arg >= argv.Length)
                        {
                            MissingArg(interp);
                        }
                        if (count == BINARY_ALL)
                        {
                            count = length - offset;
                        }
                        else
                        {
                            if (count == BINARY_NOCOUNT)
                            {
                                count = 1;
                            }
                            if (count > length - offset)
                            {
                                break;
                            }
                        }
                        size = count;
                        // Trim trailing nulls and spaces, if necessary.
                        if (cmd == 'A')
                        {
                            while (size > 0)
                            {
                                if (src[offset + size - 1] != '\x0000' && src[offset + size - 1] != ' ')
                                {
                                    break;
                                }
                                size--;
                            }
                        }
                        interp.SetVar(argv[arg++], TclByteArray.NewInstance(src, offset, size), 0);
                        offset += count;
                        break;
                    }

                    case 'b':
                    case 'B':
                    {
                        if (arg >= argv.Length)
                        {
                            MissingArg(interp);
                        }
                        if (count == BINARY_ALL)
                        {
                            count = (length - offset) * 8;
                        }
                        else
                        {
                            if (count == BINARY_NOCOUNT)
                            {
                                count = 1;
                            }
                            if (count > (length - offset) * 8)
                            {
                                break;
                            }
                        }
                        StringBuilder s          = new StringBuilder(count);
                        int           thisOffset = offset;
                        if (cmd == 'b')
                        {
                            for (int ix = 0; ix < count; ix++)
                            {
                                if ((ix % 8) != 0)
                                {
                                    value >>= 1;
                                }
                                else
                                {
                                    value = src[thisOffset++];
                                }
                                s.Append((value & 1) != 0 ? '1' : '0');
                            }
                        }
                        else
                        {
                            for (int ix = 0; ix < count; ix++)
                            {
                                if ((ix % 8) != 0)
                                {
                                    value <<= 1;
                                }
                                else
                                {
                                    value = src[thisOffset++];
                                }
                                s.Append((value & 0x80) != 0 ? '1' : '0');
                            }
                        }
                        interp.SetVar(argv[arg++], TclString.NewInstance(s.ToString()), 0);
                        offset += (count + 7) / 8;
                        break;
                    }

                    case 'h':
                    case 'H':
                    {
                        if (arg >= argv.Length)
                        {
                            MissingArg(interp);
                        }
                        if (count == BINARY_ALL)
                        {
                            count = (length - offset) * 2;
                        }
                        else
                        {
                            if (count == BINARY_NOCOUNT)
                            {
                                count = 1;
                            }
                            if (count > (length - offset) * 2)
                            {
                                break;
                            }
                        }
                        StringBuilder s          = new StringBuilder(count);
                        int           thisOffset = offset;
                        if (cmd == 'h')
                        {
                            for (int ix = 0; ix < count; ix++)
                            {
                                if ((ix % 2) != 0)
                                {
                                    value >>= 4;
                                }
                                else
                                {
                                    value = src[thisOffset++];
                                }
                                s.Append(HEXDIGITS[value & 0xf]);
                            }
                        }
                        else
                        {
                            for (int ix = 0; ix < count; ix++)
                            {
                                if ((ix % 2) != 0)
                                {
                                    value <<= 4;
                                }
                                else
                                {
                                    value = src[thisOffset++];
                                }
                                s.Append(HEXDIGITS[value >> 4 & 0xf]);
                            }
                        }
                        interp.SetVar(argv[arg++], TclString.NewInstance(s.ToString()), 0);
                        offset += (count + 1) / 2;
                        break;
                    }

                    case 'c':
                    case 's':
                    case 'S':
                    case 'i':
                    case 'I':
                    case 'f':
                    case 'd':
                    {
                        if (arg >= argv.Length)
                        {
                            MissingArg(interp);
                        }
                        switch (cmd)
                        {
                        case 'c':
                            size = 1;
                            break;

                        case 's':
                        case 'S':
                            size = 2;
                            break;

                        case 'i':
                        case 'I':
                            size = 4;
                            break;

                        case 'f':
                            size = 4;
                            break;

                        case 'd':
                            size = 8;
                            break;
                        }
                        TclObject valueObj;
                        if (count == BINARY_NOCOUNT)
                        {
                            if (length - offset < size)
                            {
                                break;
                            }
                            valueObj = ScanNumber(src, offset, cmd);
                            offset  += size;
                        }
                        else
                        {
                            if (count == BINARY_ALL)
                            {
                                count = (length - offset) / size;
                            }
                            if (length - offset < count * size)
                            {
                                break;
                            }
                            valueObj = TclList.NewInstance();
                            int thisOffset = offset;
                            for (int ix = 0; ix < count; ix++)
                            {
                                TclList.Append(null, valueObj, ScanNumber(src, thisOffset, cmd));
                                thisOffset += size;
                            }
                            offset += count * size;
                        }
                        interp.SetVar(argv[arg++], valueObj, 0);
                        break;
                    }

                    case 'x':
                    {
                        if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        if (count == BINARY_ALL || count > length - offset)
                        {
                            offset = length;
                        }
                        else
                        {
                            offset += count;
                        }
                        break;
                    }

                    case 'X':
                    {
                        if (count == BINARY_NOCOUNT)
                        {
                            count = 1;
                        }
                        if (count == BINARY_ALL || count > offset)
                        {
                            offset = 0;
                        }
                        else
                        {
                            offset -= count;
                        }
                        break;
                    }

                    case '@':
                    {
                        if (count == BINARY_NOCOUNT)
                        {
                            AlephWithoutCount(interp);
                        }
                        if (count == BINARY_ALL || count > length)
                        {
                            offset = length;
                        }
                        else
                        {
                            offset = count;
                        }
                        break;
                    }

                    default:
                    {
                        BadField(interp, cmd);
                    }
                    break;
                    }
                }
                // Set the result to the last position of the cursor.
                interp.SetResult(arg - 4);
            }
            break;
            }
            return(TCL.CompletionCode.RETURN);
        }