summaryrefslogtreecommitdiffstats
path: root/lib/sfmt.c
blob: f903cbfb982f603956f51017999172b10db11bee (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include "string.h"
#include "format.h"

char*
sfmt(char *s, size_t limit, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);

    s = svfmt(s, limit, fmt, ap);

    va_end(ap);
    return s;
}

char*
svfmt(char *s, size_t limit, const char *fmt, va_list ap)
{

    size_t fmtlen = strlen(fmt);
    size_t si = 0;
    for(size_t fmti = 0; fmti < fmtlen; fmti++)
    {
        if(si >= limit) break;
        if(fmt[fmti] != '%') {
            s[si++] = fmt[fmti];
            continue;
        }
        // TODO: Format flags
        fmti++;

        bool alt = false;
        bool zpad = false;
        for(;;fmti++){
            if(fmt[fmti] == '#') { alt = true; continue; }
            if(fmt[fmti] == '0') { zpad = true; continue; }
            break;
        }
        int fwidth = 0;
        if(fmt[fmti] >= '1' && fmt[fmti] <= '9')
        {
            for(;fmt[fmti] >= '0' && fmt[fmti] <= '9';fmti++) {
                fwidth *= 10;
                fwidth += fmt[fmti] - '0';
            }
        }
        int precision = 0;
        if(fmt[fmti] == '.')
        {
            fmti++;
            for(;fmt[fmti] >= '0' && fmt[fmti] <= '9';fmti++) {
                precision *= 10;
                precision += fmt[fmti] - '0';
            }
        }

        bool sgn = true;
        bool upper = false;
        int radix = 10;
        switch(fmt[fmti])
        {
            case '%':
                s[si++] = '%';
                break;
            case 'b':
                radix = 2;
            case 'X':
                upper = true;
            case 'x':
                if(radix == 10)
                    radix = 16;
            case 'o':
                if(radix == 10)
                    radix = 8;
            case 'u':
                sgn = false;
            case 'i': {
                if((radix == 8 || radix == 16) && alt) {
                    s[si++] = '0';
                    if(radix == 16) {
                        s[si++] = 'x';
                    }
                    if(radix == 2) {
                        s[si++] = 'b';
                    }
                }
                size_t osi = si;
                size_t nlen = ltostr(&s[si], limit - si, va_arg(ap, long), sgn, radix);
                if(upper) sntoupper(&s[si], nlen);
                si += nlen;

                int lpad = fwidth - (int)nlen;
                if(lpad > 0)
                {
                    if(lpad + osi >= limit) lpad = (limit - osi - 1);
                    memmove(&s[osi + lpad], &s[osi], nlen);
                    memset(&s[osi], zpad ? '0' : ' ', lpad);
                    si += lpad;
                }
                      } break;
            case 's': {
                const char *str = va_arg(ap, char*);
                size_t slen = strlen(str);
                size_t wlen = slen > limit - si ? limit - si : slen;
                for(size_t i = 0; i < wlen; i++)
                    s[si++] = str[i];
                      } break;
        }
    }
    s[si] = 0;
    return s;
}