U0 SPutChar(U8 **_dst, U8 ch, U8 **_buf)
{
    I64 i;
    U8 *dst = *_dst, *buf;

    if (_buf)
    {
        buf = *_buf;
        i = dst - buf;
        if (i >= MSize(buf))
        {
            buf = MAlloc(i << 1 + 1);
            MemCopy(buf, *_buf, i);
            Free(*_buf);
            dst = buf + i;
            *_buf = buf;
        }
    }
    *dst++ = ch;
    *_dst = dst;
}

U0 OutStr(U8 *ptr, U8 **_buf, U8 **_dst, I64 len, I64 flags)
{
    I64 i, j;

    if (!ptr)
        i = 0;
    else
        i = StrLen(ptr);
    if (flags & PRINTF_TRUNCATE && i > len)
        i = len;
    if (flags & PRINTF_LEFT_JUSTIFY)
    {
        for (j = 0; j < i; j++)
            SPutChar(_dst, *ptr++, _buf);
        for (j = 0; j < len - i; j++)
            SPutChar(_dst, CH_SPACE, _buf);
    }
    else
    {
        for (j = 0; j < len - i; j++)
            SPutChar(_dst, CH_SPACE, _buf);
        for (j = len - i; j < len; j++)
            SPutChar(_dst, *ptr++, _buf);
    }
}

U8 *MPrintTime(CDate cdt)
{
    CDateStruct ds;

    Date2Struct(&ds, cdt + local_time_offset);
    return MStrPrint("%02d:%02d:%02d", ds.hour, ds.min, ds.sec);
}

U8 *MPrintDate(CDate cdt)
{
    CDateStruct ds;

    Date2Struct(&ds, cdt + local_time_offset);
    return MStrPrint("%02d/%02d/%02d", ds.mon, ds.day_of_mon, ds.year % 100);
}

U8 *MPrintQ(U8 *ptr, I64 flags)
{
    U8 **_buf, *buf, **_dst, *dst, buf2[8], *ptr2;
    I64  ch;

    buf = MAlloc(STR_LEN);
    _buf = &buf;
    dst = buf;
    _dst = &dst;
    if (ptr)
        while (ch = *ptr++)
        {
            switch (ch)
            {
                case '$':
                    if (flags & PRINTF_DOLLAR)
                    {
                        SPutChar(_dst, '\\', _buf);
                        SPutChar(_dst, 'd',  _buf);
                    }
                    else
                    {
                        SPutChar(_dst, ch, _buf);
                        SPutChar(_dst, ch, _buf);
                    }
                    break;

                case '%':
                    SPutChar(_dst, ch, _buf);
                    if (flags & PRINTF_SLASH)
                        SPutChar(_dst, ch, _buf);
                    break;

                case '\n':
                    SPutChar(_dst, '\\', _buf);
                    SPutChar(_dst, 'n',  _buf);
                    break;

                case '\r':
                    SPutChar(_dst, '\\', _buf);
                    SPutChar(_dst, 'r',  _buf);
                    break;

                case '\t':
                    SPutChar(_dst, '\\', _buf);
                    SPutChar(_dst, 't',  _buf);
                    break;

                case '"':
                case '\\':
                    SPutChar(_dst, '\\', _buf);
                    SPutChar(_dst, ch,   _buf);
                    break;

                default:
                    if (ch >= CH_SPACE && ch != 0x7F)
                        SPutChar(_dst, ch, _buf);
                    else
                    {
                        StrPrint(buf2, "\\x%02X", ch);
                        ptr2 = buf2;
                        while (*ptr2)
                            SPutChar(_dst, *ptr2++, _buf);
                    }
            }
        }
    SPutChar(_dst, 0, _buf);
    return buf;
}

U8 *MPrintq(U8 *ptr, I64 flags)
{
    U8 **_buf, *buf, **_dst, *dst;
    I64  i, j, ch, ch1;

    buf = MAlloc(STR_LEN);
    _buf = &buf;
    dst = buf;
    _dst = &dst;
    if (ptr)
        while (ch =*ptr++)
        {
            ch1 = *ptr;
            switch (ch)
            {
                case '\\':
                    switch (ch1)
                    {
                        start:
                            case '0':
                                SPutChar(_dst, 0,    _buf);
                                break;

                            case '\'':
                                SPutChar(_dst, '\'', _buf);
                                break;

                            case '\`':
                                SPutChar(_dst, '\`', _buf);
                                break;

                            case '"':
                                SPutChar(_dst, '"',  _buf);
                                break;

                            case '\\':
                                SPutChar(_dst, '\\', _buf);
                                break;

                            case 'd':
                                SPutChar(_dst, '$',  _buf);
                                break;

                            case 'n':
                                SPutChar(_dst, '\n', _buf);
                                break;

                            case 'r':
                                SPutChar(_dst, '\r', _buf);
                                break;

                            case 't':
                                SPutChar(_dst, '\t', _buf);
                                break;
                        end:
                            ptr++;
                            break;

                        case 'x':
                        case 'X':
                            i = 0;
                            ptr++;
                            for (j = 0; j < 2; j++)
                            {
                                ch1 = ToUpper(*ptr++);
                                if (Bt(char_bmp_hex_numeric, ch1))
                                {
                                    if (ch1 <= '9')
                                        i = i << 4 + ch1 - '0';
                                    else
                                        i = i << 4 + ch1 - 'A' + 10;
                                }
                                else
                                {
                                    ptr--;
                                    break;
                                }
                            }
                            SPutChar(_dst, i, _buf);
                            break;

                        default:
                            SPutChar(_dst, ch, _buf);
                    }
                    break;

                case '$':
                    SPutChar(_dst, ch, _buf);
                    if (ch1 == '$')
                        ptr++;
                    break;

                case '%':
                    SPutChar(_dst, ch, _buf);
                    if (flags & PRINTF_SLASH && ch1 == '%')
                        ptr++;
                    break;

                default:
                    SPutChar(_dst, ch, _buf);
            }
        }
    SPutChar(_dst, 0, _buf);
    return buf;
}

U8  *sys_pos_pows_lets = " KMGTPEZY",
    *sys_neg_pows_lets = " munpfazy",
    *sys_pos_pows_list = "kilo\0mega\0giga\0tera\0peta\0exa\0zetta\0yotta\0",
    *sys_neg_pows_list = "milli\0micro\0nano\0pico\0femto\0atto\0zepto\0yocto\0";

#define TMP_BUF_LEN     256
#define SLOP            8

U8 *StrPrintJoin(U8 *dst, U8 *format, I64 argc, I64 *argv)
{/*Print("") Format Strings
In float formatting, do not exceed 18-digits
before or after the decimal point
because the numbers before and after
the decimal point are stored
in 64-bits.  Use exponentiated forms
to avoid this.
*/
    I64      i, j, l, ch, k, k0, n, n0,
             len, dec_len, flags, old_flags,
             aux_format_num, comma_count, comma_format_count, cur_arg=0;
    U64      m;
    F64      d, d1;
    CDoc    *doc;
    U8      *ptr, **_buf, *buf, **_dst, tmp_buf[TMP_BUF_LEN], tmp_buf2[TMP_BUF_LEN*2];

    if (!format)
        throw('StrPrint');
    if (dst)
    {
        _buf = NULL;
        buf  = dst;
    }
    else
    {
        buf  = MAlloc(STR_LEN);
        _buf = &buf;
        dst  = buf;
    }
    _dst = &dst;

    while (ch = *format++)
    {
        if (ch == '%')
        {
            flags = 0;
            if (*format == '-')
            {
                flags |= PRINTF_LEFT_JUSTIFY;
                format++;
            }
            if (*format == '0')
            {
                flags |= PRINTF_PAD_ZERO;
                format++;
            }
            len = 0;
            while ('0' <= *format <= '9')
                len = len * 10 + *format++ - '0';
            if (*format == '*')
            {
                format++;
                if (cur_arg >= argc)
                    throw('StrPrint');
                len = argv[cur_arg++];
            }
            dec_len = 0;
            if (*format == '.')
            {
                format++;
                while ('0' <= *format <= '9')
                    dec_len = dec_len * 10 + *format++ - '0';
                if (*format == '*')
                {
                    format++;
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    dec_len = argv[cur_arg++];
                }
                flags |= PRINTF_DECIMAL;
            }

            aux_format_num = 0;
            while (TRUE)
            {
                switch (*format)
                {
                    start:
                        case '$':
                            flags |= PRINTF_DOLLAR;
                            break;

                        case '/':
                            flags |= PRINTF_SLASH;
                            break;

                        case ',':
                            flags |= PRINTF_COMMA;
                            break;

                        case 't':
                            flags |= PRINTF_TRUNCATE;
                            break;

                        case 'l': //harmless
                            break;
                    end:
                        format++;
                        break;

                    case 'h':
                        format++;
                        flags |= PRINTF_AUX_FORMAT_NUM;
                        if (*format == '?')
                        {
                            format++;
                            flags |= PRINTF_QUESTION;
                        }
                        else
                        {
                            if (*format == '*')
                            {
                                format++;
                                if (cur_arg >= argc)
                                    throw('StrPrint');
                                aux_format_num = argv[cur_arg++];
                            }
                            else
                            {
                                if (*format == '-')
                                {
                                    format++;
                                    flags |= PRINTF_NEG_AUX_FORMAT_NUM;
                                }
                                while ('0' <= *format <= '9')
                                    aux_format_num = aux_format_num * 10 + *format++ - '0';
                                if (flags & PRINTF_NEG_AUX_FORMAT_NUM)
                                    aux_format_num = -aux_format_num;
                            }
                        }
                        break;

                    default:
                        goto sp_arg;
                }
            }

            sp_arg:
            k = 0;
            switch (*format++)
            {
                start:
                    case 'F':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        if (flags & PRINTF_DOLLAR)
                        {
                            doc = argv[cur_arg++];
                            old_flags = doc->flags;
                            doc->flags |= DOCF_NO_CURSOR;
                            ptr = DocSave(doc);
                            doc->flags = old_flags;
                        }
                        else
                            ptr = FileRead(argv[cur_arg++]);
                        break;

                    case 'Q':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = MPrintQ(argv[cur_arg++], flags);
                        break;

                    case 'q':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = MPrintq(argv[cur_arg++], flags);
                        break;

                    case 'D':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = MPrintDate(argv[cur_arg++]);
                        break;

                    case 'T':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = MPrintTime(argv[cur_arg++]);
                        break;
                end:
                    OutStr(ptr, _buf, _dst, len, flags);
                    Free(ptr);
                    break;

                start:
                    case 's':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = argv[cur_arg++];
                        break;

                    case 'o': // Bool
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = DefineSub(ToBool(argv[cur_arg++]), "ST_FALSE_TRUE");
                        break;

                    case 'S':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        ptr = Define(argv[cur_arg++]);
                        break;

                    case 'z':
                        if (cur_arg + 1 >= argc)
                            throw('StrPrint');
                        ptr = ListSub(argv[cur_arg], argv[cur_arg + 1]);
                        cur_arg = cur_arg + 2;
                        break;

                    case 'Z':
                        if (cur_arg + 1 >= argc)
                            throw('StrPrint');
                        ptr = DefineSub(argv[cur_arg], argv[cur_arg + 1]);
                        cur_arg = cur_arg + 2;
                        break;
                end:
                    OutStr(ptr, _buf, _dst, len, flags);
                    break;

                start:
                    case 'c':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        tmp_buf[0](I64) = argv[cur_arg++];
                        tmp_buf[8] = 0;
                        break;

                    case 'C':
                        if (cur_arg >= argc)
                            throw('StrPrint');
                        tmp_buf[0](I64) = argv[cur_arg++];
                        tmp_buf[8] = 0;
                        ptr = tmp_buf;
                        while (*ptr)
                        {
                            *ptr = ToUpper(*ptr);
                            ptr++;
                        }
                        break;
                end:
                    if (!(flags & PRINTF_AUX_FORMAT_NUM))
                        aux_format_num = 1;
                    while (aux_format_num-- > 0)
                        OutStr(tmp_buf, _buf, _dst, len, flags);
                    break;

                case 'p':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    StrPrintFunSeg(tmp_buf, argv[cur_arg++], len, flags);
                    OutStr(tmp_buf, _buf, _dst, len, flags);
                    break;

                case 'P':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    StrPrintFunSeg(tmp_buf, argv[cur_arg], len, flags);
                    if (!IsRaw || !_buf)
                    {
                        StrPrint(tmp_buf2, "$LK,\"%s\",A=\"AD:0x%X\"$", tmp_buf, argv[cur_arg]);
                        OutStr(tmp_buf2, _buf, _dst, len, flags);
                    }
                    else
                        OutStr(tmp_buf, _buf, _dst, len, flags);
                    cur_arg++;
                    break;

                case 'd':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    m = argv[cur_arg++];
                    if (m(I64) < 0)
                    {
                        flags |= PRINTF_NEG;
                        m = -m;
                    }
sp_out_dec:
                    if (flags & PRINTF_AUX_FORMAT_NUM)
                    {
                        if (!len)
                            len = 12;
                        d = m;
                        goto sp_out_eng;
                    }
                    if (flags & PRINTF_COMMA)
                    {
                        comma_format_count = comma_count = 3;
                        do
                        {
                            tmp_buf[k++] = ModU64(&m, 10) + '0';
                            if (!m)
                                break;
                            if (!--comma_count)
                            {
                                tmp_buf[k++] = ',';
                                comma_count = 3;
                            }
                        }
                        while (k < TMP_BUF_LEN - SLOP);
sp_out_comma_num:
                        if (flags & PRINTF_NEG)
                            i = 1;
                        else
                            i = 0;
                        if (len < 0)
                            len = 0;
                        if (flags & PRINTF_TRUNCATE && k + i > len)
                            k = len - i;
                        if (k < 0)
                            k = 0;
                        if (flags & PRINTF_PAD_ZERO)
                        {
                            if (flags & PRINTF_NEG)
                                SPutChar(_dst, '-', _buf);
                            comma_count = (len - k - i + comma_format_count - comma_count + 1) % (comma_format_count + 1) + 1;
                            for (; i < len - k; i++)
                            {
                                if (!--comma_count)
                                {
                                    SPutChar(_dst, ',', _buf);
                                    comma_count = comma_format_count;
                                    if (++i >= len - k)
                                        break;
                                }
                                SPutChar(_dst, '0', _buf);
                            }
                        }
                        else
                        {
                            for (; i < len - k; i++)
                                SPutChar(_dst, CH_SPACE, _buf);
                            if (flags & PRINTF_NEG)
                                SPutChar(_dst, '-', _buf);
                        }
                    }
                    else
                    {
                        do
                        {
                            tmp_buf[k++] = ModU64(&m, 10) + '0';
                            if (!m)
                                break;
                        }
                        while (k < TMP_BUF_LEN - SLOP);
sp_out_num:
                        if (flags & PRINTF_NEG)
                            i = 1;
                        else
                            i = 0;
                        if (len < 0)
                            len = 0;
                        if (flags & PRINTF_TRUNCATE && k + i > len)
                            k = len - i;
                        if (k < 0)
                            k = 0;
                        if (flags & PRINTF_PAD_ZERO)
                        {
                            if (flags & PRINTF_NEG)
                                SPutChar(_dst, '-', _buf);
                            for (; i < len - k; i++)
                                SPutChar(_dst, '0', _buf);
                        }
                        else
                        {
                            for (; i < len - k; i++)
                                SPutChar(_dst, CH_SPACE, _buf);
                            if (flags & PRINTF_NEG)
                                SPutChar(_dst, '-', _buf);
                        }
                    }
                    for (i = k - 1; i >= 0; i--)
                        SPutChar(_dst, tmp_buf[i], _buf);
                    break;

                case 'u':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    m = argv[cur_arg++];
                    goto sp_out_dec;

                case 'f':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    d = argv[cur_arg++](F64);
                    if (d < 0)
                    {
                        flags |= PRINTF_NEG;
                        d = -d;
                    }

                    if (d == inf)
                    {
sp_out_inf:
                        if (flags & PRINTF_NEG)
                            i = 1;
                        else
                            i = 0;
                        k = 1;
                        if (len < 0)
                            len = 0;
                        if (flags & PRINTF_TRUNCATE && k + i > len)
                            k = len - i;
                        if (k < 0)
                            k = 0;
                        for (; i < len - k; i++)
                            SPutChar(_dst, CH_SPACE, _buf);
                        if (flags & PRINTF_NEG)
                            SPutChar(_dst, '-', _buf);
                        for (i = 0; i < k; i++)
                            SPutChar(_dst, 'inf', _buf);
                        break;
                    }

sp_out_f:
                    if (dec_len < 0)
                        dec_len = 0;
                    n = Log10(d);
                    if (i = dec_len)
                    {
                        if (flags & PRINTF_COMMA)
                            i = i - i / 4;
                        if (n + i > 17)
                        {
                            n += i - 17;
                            d *= Pow10I64(i - n);
                        }
                        else
                        {
                            n = 0;
                            d *= Pow10I64(i);
                        }
                        i = dec_len;
                    }
                    else if (n > 17)
                    {
                        n -= 17;
                        d *= Pow10I64(-n);
                    }
                    else
                        n = 0;

                    m = Round(d);
                    if (flags & PRINTF_COMMA)
                    {
                        comma_count = i & 3;
                        while (i-- && k < TMP_BUF_LEN - SLOP)
                        {
                            if (i > 2 && !comma_count--)
                            {
                                tmp_buf[k++] = ',';
                                comma_count = 2;
                                if (!--i)
                                    break;
                            }
                            if (n)
                            {
                                n--;
                                tmp_buf[k++] = '0';
                            }
                            else
                                tmp_buf[k++] = ModU64(&m, 10) + '0';
                            if (!i)
                                break;
                        }
                    }
                    else
                    {
                        while (i-- && k < TMP_BUF_LEN - SLOP)
                        {
                            if (n)
                            {
                                n--;
                                tmp_buf[k++] = '0';
                            }
                            else
                                tmp_buf[k++] = ModU64(&m, 10) + '0';
                        }
                    }
                    if (dec_len)
                        tmp_buf[k++] = '.';
                    if (flags & PRINTF_COMMA)
                    {
                        comma_count = 3;
                        do
                        {
                            if (n)
                            {
                                n--;
                                tmp_buf[k++] = '0';
                            }
                            else
                                tmp_buf[k++] = ModU64(&m, 10) + '0';
                            if (!m)
                                break;
                            if (!--comma_count)
                            {
                                tmp_buf[k++] = ',';
                                comma_count = 3;
                            }
                        }
                        while (k < TMP_BUF_LEN - SLOP);

                    }
                    else
                    {
                        do
                        {
                            if (n)
                            {
                                n--;
                                tmp_buf[k++] = '0';
                            }
                            else
                                tmp_buf[k++] = ModU64(&m, 10) + '0';
                            if (!m)
                                break;
                        }
                        while (k < TMP_BUF_LEN - SLOP);

                    }
                    goto sp_out_num;

                case 'e':
                    if (!len)
                        len = 12;
                    flags |= PRINTF_TRUNCATE;

                    if (cur_arg >= argc)
                        throw('StrPrint');
                    d = argv[cur_arg++](F64);
                    if (d < 0)
                    {
                        flags |= PRINTF_NEG;
                        d = -d;
                    }
                    if (d == inf)
                        goto sp_out_inf;

                    if (d)
                        n = Floor(Log10(d));
                    else
                        n = 0;
sp_out_e:
                    d /= Pow10I64(n);

                    k0 = k;
                    for (l = 0; l < 2; l++)
                    {
                        n0 = n;
                        if (n < 0)
                        {
                            n = -n;
                            flags |= PRINTF_NEG_E;
                        }
                        else
                            flags &= ~PRINTF_NEG_E;

                        i = 3;

                        do tmp_buf[k++] = ModU64(&n, 10) + '0';
                        while (n && i--);

                        if (flags & PRINTF_NEG_E)
                            tmp_buf[k++] = '-';
                        tmp_buf[k++] = 'e';
                        dec_len = len - k - 2;
                        if (flags & PRINTF_NEG)
                            dec_len--;

                        if (d)
                        {
                            d1 = d + Pow10I64(-dec_len) / 2;
                            if (d1 < 1.0)
                            {
                                d *= 10;
                                n = n0 - 1;
                                k = k0;
                            }
                            else if (d1 >= 10)
                            {
                                d /= 10;
                                n = n0 + 1;
                                k = k0;
                            }
                            else
                                break;
                        }
                        else
                            break;
                    }

                    goto sp_out_f;

                case 'g':
                    if (!len)
                        len = 12;
                    flags |= PRINTF_TRUNCATE;
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    d = argv[cur_arg++](F64);
                    if (d < 0)
                    {
                        flags |= PRINTF_NEG;
                        d = -d;
                    }
                    if (d == inf)
                        goto sp_out_inf;
                    if (d)
                        n = Floor(Log10(d));
                    else
                        n = 0;
                    if (n >= len - 1 - dec_len || n < -(dec_len - 1))
                        goto sp_out_e;
                    else
                        goto sp_out_f;

                case 'n':
                    if (!len)
                        len = 12;
                    flags |= PRINTF_TRUNCATE;
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    d = argv[cur_arg++](F64);
                    if (d < 0)
                    {
                        flags |= PRINTF_NEG;
                        d = -d;
                    }
sp_out_eng: //Engineering notation
                    if (d == inf)
                        goto sp_out_inf;
                    if (d)
                        n = FloorI64(Floor(Log10(d)), 3);
                    else
                        n = 0;
                    d /= Pow10I64(n);

                    if (n < 0)
                    {
                        n = -n;
                        flags |= PRINTF_NEG_E;
                    }
                    if (flags & PRINTF_AUX_FORMAT_NUM && -24 <= n <= 24)
                    {
                        if (flags & PRINTF_QUESTION)
                        {
                            if (flags & PRINTF_NEG_E)
                                i = -n / 3;
                            else
                                i = n / 3;
                            j = 0;
                        }
                        else
                        {
                            if (flags & PRINTF_NEG_E)
                                j = -n - aux_format_num;
                            else
                                j = n - aux_format_num;
                            d *= Pow10I64(j);
                            i = aux_format_num / 3;
                        }
                        if (i < 0)
                            tmp_buf[k++] = sys_neg_pows_lets[-i];
                        else if (i > 0)
                            tmp_buf[k++] = sys_pos_pows_lets[i];
                        else if (len != 0)
                            tmp_buf[k++] = CH_SPACE;
                        if (!(flags & PRINTF_DECIMAL))
                        {
                            dec_len = len - k - 2;
                            if (flags & PRINTF_NEG)
                                dec_len--;
                            if (j > 0)
                            {
                                if (flags & PRINTF_COMMA)
                                    dec_len -= 4 * j / 3;
                                else
                                    dec_len -= j;
                            }
                            d1 = d + Pow10I64(-dec_len + 1) / 2;
                            if (d1 >= 10)
                            {
                                dec_len--;
                                if (d1 >= 100)
                                    dec_len--;
                            }
                        }
                    }
                    else
                    {
                        i = 3;
                        do tmp_buf[k++] = ModU64(&n, 10) + '0';
                        while (n && i--);
                        if (flags & PRINTF_NEG_E)
                            tmp_buf[k++] = '-';
                        tmp_buf[k++] = 'e';
                        if (!dec_len)
                        {
                            dec_len = len - k - 2;
                            if (flags & PRINTF_NEG)
                                dec_len--;
                            d1 = d + Pow10I64(-dec_len + 1) / 2;
                            if (d1 >= 10)
                            {
                                dec_len--;
                                if (d1 >= 100)
                                    dec_len--;
                            }
                        }
                    }
                    if (flags & PRINTF_COMMA)
                    {
                        if (len && dec_len > 0 && !(dec_len & 3))
                            tmp_buf[k++] = ',';
                        dec_len -= dec_len / 4;
                    }
                    goto sp_out_f;

                case 'X':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    m = argv[cur_arg++];
                    if (flags & PRINTF_COMMA)
                    {
                        comma_format_count = comma_count = 4;
                        do
                        {
                            tmp_buf[k] = m & 15 + '0';
                            if (tmp_buf[k] > '9')
                                tmp_buf[k] += 'A' - 0x3A;
                            k++;
                            m >>= 4;
                            if (!m)
                                break;
                            if (!--comma_count)
                            {
                                tmp_buf[k++] = ',';
                                comma_count = 4;
                            }
                        }
                        while (k < TMP_BUF_LEN - SLOP);

                        goto sp_out_comma_num;
                    }
                    else
                    {
                        do
                        {
                            tmp_buf[k] = m & 15 + '0';
                            if (tmp_buf[k] > '9')
                                tmp_buf[k] += 'A' - 0x3A;
                            k++;
                            m >>= 4;
                        }
                        while (m && k < TMP_BUF_LEN - SLOP);

                        goto sp_out_num;
                    }

                case 'x':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    m = argv[cur_arg++];
                    if (flags & PRINTF_COMMA)
                    {
                        comma_format_count = comma_count = 4;
                        do
                        {
                            tmp_buf[k] = m & 15 + '0';
                            if (tmp_buf[k] > '9')
                                tmp_buf[k] += 'a' - 0x3A;
                            k++;
                            m >>= 4;
                            if (!m)
                                break;
                            if (!--comma_count)
                            {
                                tmp_buf[k++] = ',';
                                comma_count = 4;
                            }
                        }
                        while (k < TMP_BUF_LEN - SLOP);

                        goto sp_out_comma_num;
                    }
                    else
                    {
                        do
                        {
                            tmp_buf[k] = m & 15 + '0';
                            if (tmp_buf[k] > '9')
                                tmp_buf[k] += 'a' - 0x3A;
                            k++;
                            m >>= 4;
                        }
                        while (m && k < TMP_BUF_LEN - SLOP);

                        goto sp_out_num;
                    }

                case 'b':
                case 'B':
                    if (cur_arg >= argc)
                        throw('StrPrint');
                    m = argv[cur_arg++];
                    if (flags & PRINTF_COMMA)
                    {
                        comma_format_count = comma_count = 4;
                        do
                        {
                            tmp_buf[k++] = m & 1 + '0';
                            m >>= 1;
                            if (!m)
                                break;
                            if (!--comma_count)
                            {
                                tmp_buf[k++] = ',';
                                comma_count = 4;
                            }
                        }
                        while (k < TMP_BUF_LEN - SLOP);

                        goto sp_out_comma_num;
                    }
                    else
                    {
                        do
                        {
                            tmp_buf[k++] = m & 1 + '0';
                            m >>= 1;
                        }
                        while (m && k < TMP_BUF_LEN - SLOP);

                        goto sp_out_num;
                    }

                case '%':
                    SPutChar(_dst, '%', _buf);
                    break;
            }
        }
        else
            SPutChar(_dst, ch, _buf);
    }
    SPutChar(_dst, 0, _buf);
    return buf;
}

U8 *StrPrint(U8 *dst, U8 *format, ...)
{//See StrPrintJoin().
    return StrPrintJoin(dst, format, argc, argv);
}

U8 *CatPrint(U8 *_dst, U8 *format, ...)
{//StrCat().  See StrPrintJoin().
    U8 *dst = _dst;

    while (*dst)
        dst++;
    StrPrintJoin(dst, format, argc, argv);

    return _dst;
}

U0 Print(U8 *format, ...)
{//Print("") Format Strings.  See StrPrintJoin().
//Don't use this.  See Print() shortcut.
    U8 *buf = StrPrintJoin(NULL, format, argc, argv);

    PutS(buf);//Don't use PutS().  See Print() shortcut.
    Free(buf);
}

U8 *MStrPrint(U8 *format, ...)
{//MAlloc StrPrint.  See StrPrintJoin().
    U8 *res, *buf = StrPrintJoin(NULL, format, argc, argv);

    res = StrNew(buf);
    Free(buf);

    return res;
}

U0 PrintErr(U8 *format, ...)
{//Print "Err:" and message in blinking red.
    U8 *buf = StrPrintJoin(NULL, format, argc, argv);

    GetOutOfDollar;
    "%,p . %,p . %,p . %,p " ST_ERR_ST "%s", Caller, Caller(2), Caller(3), Caller(4), buf;
    Free(buf);
}

U0 PrintWarn(U8 *format, ...)
{//Print "Warn:" and message in blinking red.
    U8 *buf = StrPrintJoin(NULL, format, argc, argv);

    GetOutOfDollar;
    "%,p . %,p . %,p . %,p " ST_WARN_ST "%s", Caller, Caller(2), Caller(3), Caller(4), buf;
    Free(buf);
}