I64 Str2I64(U8 *st, I64 radix=10, U8 **_end_ptr=NULL)
{//String to I64. Similar to strtoul().
//Allows radix change with "0x20" "0b1010" "0d123" "0o18".
    //Be careful of Str2I64("0b101", 16)-->0xB101.
    Bool neg = FALSE;
    I64  ch, res = 0;

    if (!st || !(2 <= radix <= 36))
    {
        if (_end_ptr)
            *_end_ptr = st;
        return 0;
    }
    while (Bt(char_bmp_white_space, *st))
        st++;
    while (TRUE)
        switch (*st)
        {
            case '-':
                st++;
                neg = !neg;
                break;

            case '+':
                st++;
                break;

            case '0':
                st++;
                ch = ToUpper(*st);
                if (ch >= 'B' && (radix <= 10 || ch > 'A' + radix - 11))
                    switch (ch)
                    {
                        case 'B': radix = 2;  st++; break;
                        case 'D': radix = 10; st++; break;
                        case 'X': radix = 16; st++; break;
                    }
            default:
                goto ai_cont;
        }
ai_cont:
    while (ch = ToUpper(*st++))
    {
        if (radix > 10)
        {
            if ('0' <= ch <= '9')
                res = res * radix + ch - '0';
            else if ('A' <= ch <= 'A' + radix - 11)
                res = res * radix + ch - 'A' + 10;
            else
                break;
        }
        else if ('0' <= ch <= '0' + radix - 1)
            res = res * radix + ch - '0';
        else
            break;
    }
    if (_end_ptr)
        *_end_ptr = st - 1;
    if (neg)
        return -res;
    else
        return res;
}

F64 Str2F64(U8 *src, U8 **_end_ptr=NULL)
{/*String to F64.
Does not allow more than 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, k, ch;
    F64  d;
    Bool neg = FALSE, neg_e = FALSE;

    ch = *src++;
    while (Bt(char_bmp_white_space, ch))
        ch = *src++;
    if (ch == '-')
    {
        neg = TRUE;
        ch = *src++;
    }
    if (!StrNCompare(src - 1, "inf", 3))
    {
        d=inf;
        src += 3;
        goto a2f_end;
    }
    if (*src == 'inf')
    {
        d = inf;
        src++;
        goto a2f_end;
    }
    i = 0;
    while (TRUE)
    {
        if (Bt(char_bmp_dec_numeric, ch))
            i = i * 10 + ch - '0';
        else
        {
            if (ch == '.' || ch == 'e' || ch == 'E')
                break;
            d = i;
            goto a2f_end;
        }
        ch = *src++;
    }
    if (ch == '.')
        ch = *src++;
    k = 0;
    while (TRUE)
    {
        if (Bt(char_bmp_dec_numeric, ch))
        {
            i = i * 10 + ch - '0';
            k++;
        }
        else
        {
            if (ch == 'e' || ch == 'E')
                break;
            d = i * Pow10I64(-k);
            goto a2f_end;
        }
        ch = *src++;
    }
    ch = *src++;
    if (ch == '-')
    {
        neg_e = TRUE;
        ch = *src++;
    }
    j = 0;
    while (TRUE)
    {
        if (Bt(char_bmp_dec_numeric, ch))
            j = j * 10 + ch - '0';
        else
        {
            if (neg_e)
                d = i * Pow10I64(-j - k);
            else
                d = i * Pow10I64(j - k);
            goto a2f_end;
        }
        ch = *src++;
    }
a2f_end:
    if (_end_ptr)
        *_end_ptr = src - 1;
    if (neg)
        return -d;
    else
        return d;
}

CDate Str2Date(U8 *_src)
{/*"*+nnnn", "*-nnnn", "mm/dd", "mm/dd/yy"
It also supports some functions
SM() start of mon
EM() end of mon
SY() start of year
EY() end of year
Full expressions are not implimented
but you can do stuff like SM(*-7)+3
and it will return the 3rd day after
the start of mon for seven days before
today.
*/
    CDate        res = 0;
    CDateStruct  ds, ds_now;
    U8          *src = MStrUtil(_src, SUF_REM_SPACES | SUF_TO_UPPER), *v = StrNew(src), *ptr = src;
    Bool         start_mon = FALSE, end_mon = FALSE, start_year = FALSE, end_year = FALSE;

    MemSet(&ds, 0, sizeof(CDateStruct));
    if (!StrNCompare(ptr, "SM(", 3))
    {
        ptr += 3;
        start_mon = TRUE;
    }
    else if (!StrNCompare(ptr, "EM(", 3))
    {
        ptr += 3;
        end_mon = TRUE;
    }
    else if (!StrNCompare(ptr, "SY(", 3))
    {
        ptr += 3;
        start_year = TRUE;
    }
    else if (!StrNCompare(ptr, "EY(", 3))
    {
        ptr += 3;
        end_year = TRUE;
    }
    if (*ptr == '*')
    {
        ptr++;
        if (*ptr == '+' || *ptr == '-')
            res.date = Str2I64(ptr,, &ptr);
        res += Now + local_time_offset;
    }
    else
    {
        StrFirstRemove(ptr, "/", v); //Put mon into v
        ds.mon = Str2I64(v);
        if (StrOcc(ptr, '/'))
        {
            StrFirstRemove(ptr, "/", v); //Put day into v leaving year in pointer
            ds.day_of_mon = Str2I64(v);
            ds.year       = Str2I64(ptr,, &ptr);
            if (ds.year < 100) //if not 4 digit year
                ds.year += 2000;
        }
        else
        {
            ds.day_of_mon = Str2I64(ptr,, &ptr);
            Date2Struct(&ds_now, Now + local_time_offset);
            ds.year = ds_now.year;
        }
        res = Struct2Date(&ds);
    }
    if (*ptr == ')')
        ptr++;

    if (start_mon)
        res.date = FirstDayOfMon(res.date);
    else if (end_mon)
        res.date = LastDayOfMon(res.date);
    else if (start_year)
        res.date = FirstDayOfYear(res.date);
    else if (end_year)
        res.date = LastDayOfYear(res.date);

    if (*ptr == '+' || *ptr == '-')
        res.date += Str2I64(ptr);
    Free(src);
    Free(v);

    return res - local_time_offset;
}

U8 *StrScan(U8 *src, U8 *format, ...)
{/*Opposite of sprintf().  Pass pointers to data to be scanned-in.
For "%s", pass pointer to pointer (be careful because address
of array is the same as array--create pointer to array
and take address.
*/
    U8  *buf, *ptr, **pptr;
    Bool left_justify = FALSE;
    I64  ch, cur_arg = 0, i, len, *i_ptr, dec_len;
    F64 *d_ptr;

    if (!format)
        throw('Scan');
    while (ch = *format++)
    {
        if (ch == '%')
        {
            if (*format == '%')
            {
                src++;
                format++;
            }
            else
            {
                if (*format == '-')
                {
                    left_justify = TRUE;
                    format++;
                }
                else
                    left_justify = FALSE;
                len = 0;
                while ('0' <= *format <= '9')
                    len = len * 10 + (*format++ - '0');
                if (*format == '*')
                {
                    format++;
                    if (cur_arg >= argc)
                        throw('Scan');
                    len = argv[cur_arg++];
                }
                ch = *format++;
                if (ch && !len)
                {
                    ptr = src;
                    while (*ptr && *ptr != *format)
                        ptr++;
                    len = ptr - src;
                }
                else
                {
                    if (ch == '.')
                    {
                        dec_len = 0;
                        while ('0' <= *format <= '9')
                            dec_len = dec_len * 10 + (*format++ - '0');
                        if (*format == '*')
                        {
                            format++;
                            if (cur_arg >= argc)
                                throw('Scan');
                            dec_len = argv[cur_arg++];
                        }
                        ch = *format++;
                    }
                }
                buf = MAlloc(len + 1);
                for (i = 0; i < len; i++)
                    buf[i] = *src++;
                buf[i] = 0;
                switch (ch)
                {
                    case 's':
                        if (cur_arg >= argc)
                            throw('Scan');
                        pptr = argv[cur_arg++];
                        StrCopy(*pptr, buf);
                        break;

                    case 'c':
                        if (cur_arg >= argc)
                            throw('Scan');
                        ptr = argv[cur_arg++];
                        *ptr = *buf;
                        break;

                    case 'C':
                        if (cur_arg >= argc)
                            throw('Scan');
                        ptr = argv[cur_arg++];
                        *ptr = ToUpper(*buf);
                        break;

                    case 'z':
                        if (cur_arg + 1 >= argc)
                            throw('Scan');
                        i_ptr = argv[cur_arg++];
                        *i_ptr = ListMatch(buf, argv[cur_arg++]);
                        break;

                    case 'Z':
                        if (cur_arg + 1 >= argc)
                            throw('Scan');
                        i_ptr = argv[cur_arg++];
                        *i_ptr = DefineMatch(buf, argv[cur_arg++]);
                        break;

                    case 'd':
                        if (cur_arg >= argc)
                            throw('Scan');
                        i_ptr = argv[cur_arg++];
                        *i_ptr = Str2I64(buf);
                        break;

                    case 'X':
                        if (cur_arg >= argc)
                            throw('Scan');
                        i_ptr = argv[cur_arg++];
                        *i_ptr = Str2I64(buf, 16);
                        break;

                    case 'b':
                        if (cur_arg >= argc)
                            throw('Scan');
                        i_ptr = argv[cur_arg++];
                        *i_ptr = Str2I64(buf, 2);
                        break;

                    case 'e':
                    case 'f':
                    case 'g':
                    case 'n':
                        if (cur_arg >= argc)
                            throw('Scan');
                        d_ptr = argv[cur_arg++];
                        *d_ptr = Str2F64(buf);
                        break;

                    case 'D':
                        if (cur_arg >= argc)
                            throw('Scan');
                        i_ptr = argv[cur_arg++];
                        *i_ptr = Str2Date(buf);
                        break;
                }
                Free(buf);
            }
        }
        else
            src++;
    }

    return src;
}