Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include        <u.h>
#include        <libc.h>

enum
{
        SIZE    = 1024,
        IDIGIT  = 40,
        MAXCONV = 40,
        FDIGIT  = 30,
        FDEFLT  = 6,
        NONE    = -1000,
        MAXFMT  = 512,

        FPLUS   = 1<<0,
        FMINUS  = 1<<1,
        FSHARP  = 1<<2,
        FLONG   = 1<<3,
        FSHORT  = 1<<4,
        FUNSIGN = 1<<5,
        FVLONG  = 1<<6,
};

int     printcol;

static  int     convcount;
static  char    fmtindex[MAXFMT];

static  int     noconv(va_list*, Fconv*);
static  int     flags(va_list*, Fconv*);

static  int     cconv(va_list*, Fconv*);
static  int     rconv(va_list*, Fconv*);
static  int     sconv(va_list*, Fconv*);
static  int     percent(va_list*, Fconv*);
static  int     column(va_list*, Fconv*);

int     numbconv(va_list*, Fconv*);

static
int     (*fmtconv[MAXCONV])(va_list*, Fconv*) =
{
        noconv
};

static
void
initfmt(void)
{
        int cc;

        cc = 0;
        fmtconv[cc] = noconv;
        cc++;

        fmtconv[cc] = flags;
        fmtindex['+'] = cc;
        fmtindex['-'] = cc;
        fmtindex['#'] = cc;
        fmtindex['h'] = cc;
        fmtindex['l'] = cc;
        fmtindex['u'] = cc;
        cc++;

        fmtconv[cc] = numbconv;
        fmtindex['d'] = cc;
        fmtindex['o'] = cc;
        fmtindex['x'] = cc;
        fmtindex['X'] = cc;
        cc++;

        fmtconv[cc] = cconv;
        fmtindex['c'] = cc;
        fmtindex['C'] = cc;
        cc++;

        fmtconv[cc] = rconv;
        fmtindex['r'] = cc;
        cc++;

        fmtconv[cc] = sconv;
        fmtindex['s'] = cc;
        fmtindex['S'] = cc;
        cc++;

        fmtconv[cc] = percent;
        fmtindex['%'] = cc;
        cc++;

        fmtconv[cc] = column;
        fmtindex['|'] = cc;
        cc++;

        convcount = cc;
}

int
fmtinstall(int c, int (*f)(va_list*, Fconv*))
{

        if(convcount == 0)
                initfmt();
        if(c < 0 || c >= MAXFMT)
                return -1;
        if(convcount >= MAXCONV)
                return -1;
        fmtconv[convcount] = f;
        fmtindex[c] = convcount;
        convcount++;
        return 0;
}

char*
doprint(char *s, char *es, char *fmt, va_list argp)
{
        int n, c;
        Rune rune;
        Fconv local;

        if(s >= es)
                return s;
        local.out = s;
        local.eout = es-UTFmax-1;

loop:
        c = *fmt & 0xff;
        if(c >= Runeself) {
                n = chartorune(&rune, fmt);
                fmt += n;
                c = rune;
        } else
                fmt++;
        switch(c) {
        case 0:
                *local.out = 0;
                return local.out;
        
        default:
                printcol++;
                goto common;

        case '\n':
                printcol = 0;
                goto common;

        case '\t':
                printcol = (printcol+8) & ~7;
                goto common;

        common:
                if(local.out < local.eout)
                        if(c >= Runeself) {
                                rune = c;
                                n = runetochar(local.out, &rune);
                                local.out += n;
                        } else
                                *local.out++ = c;
                goto loop;

        case '%':
                break;
        }
        local.f1 = NONE;
        local.f2 = NONE;
        local.f3 = 0;

        /*
         * read one of the following
         *      1. number, => f1, f2 in order.
         *      2. '*' same as number (from args)
         *      3. '.' ignored (separates numbers)
         *      4. flag => f3
         *      5. verb and terminate
         */
l0:
        c = *fmt & 0xff;
        if(c >= Runeself) {
                n = chartorune(&rune, fmt);
                fmt += n;
                c = rune;
        } else
                fmt++;

l1:
        if(c == 0) {
                fmt--;
                goto loop;
        }
        if(c == '.') {
                if(local.f1 == NONE)
                        local.f1 = 0;
                local.f2 = 0;
                goto l0;
        }
        if((c >= '1' && c <= '9') ||
           (c == '0' && local.f1 != NONE)) {    /* '0' is a digit for f2 */
                n = 0;
                while(c >= '0' && c <= '9') {
                        n = n*10 + c-'0';
                        c = *fmt++;
                }
                if(local.f1 == NONE)
                        local.f1 = n;
                else
                        local.f2 = n;
                goto l1;
        }
        if(c == '*') {
                n = va_arg(argp, int);
                if(local.f1 == NONE)
                        local.f1 = n;
                else
                        local.f2 = n;
                goto l0;
        }
        n = 0;
        if(c >= 0 && c < MAXFMT)
                n = fmtindex[c];
        local.chr = c;
        n = (*fmtconv[n])(&argp, &local);
        if(n < 0) {
                local.f3 |= -n;
                goto l0;
        }
        goto loop;
}

int
numbconv(va_list *arg, Fconv *fp)
{
        char s[IDIGIT];
        int i, f, n, b, ucase;
        short h;
        long v;
        vlong vl;

        SET(v);
        SET(vl);

        ucase = 0;
        b = fp->chr;
        switch(fp->chr) {
        case 'u':
                fp->f3 |= FUNSIGN;
        case 'd':
                b = 10;
                break;

        case 'o':
                b = 8;
                break;

        case 'X':
                ucase = 1;
        case 'x':
                b = 16;
                break;
        }

        f = 0;
        switch(fp->f3 & (FVLONG|FLONG|FSHORT|FUNSIGN)) {
        case FVLONG|FLONG:
                vl = va_arg(*arg, vlong);
                break;

        case FUNSIGN|FVLONG|FLONG:
                vl = va_arg(*arg, uvlong);
                break;

        case FLONG:
                v = va_arg(*arg, long);
                break;

        case FUNSIGN|FLONG:
                v = va_arg(*arg, ulong);
                break;

        case FSHORT:
                h = va_arg(*arg, int);
                v = h;
                break;

        case FUNSIGN|FSHORT:
                h = va_arg(*arg, int);
                v = (ushort)h;
                break;

        default:
                v = va_arg(*arg, int);
                break;

        case FUNSIGN:
                v = va_arg(*arg, unsigned);
                break;
        }
        if(fp->f3 & FVLONG) {
                if(!(fp->f3 & FUNSIGN) && vl < 0) {
                        vl = -vl;
                        f = 1;
                }
        } else {
                if(!(fp->f3 & FUNSIGN) && v < 0) {
                        v = -v;
                        f = 1;
                }
        }
        s[IDIGIT-1] = 0;
        for(i = IDIGIT-2;; i--) {
                if(fp->f3 & FVLONG)
                        n = (uvlong)vl % b;
                else
                        n = (ulong)v % b;
                n += '0';
                if(n > '9') {
                        n += 'a' - ('9'+1);
                        if(ucase)
                                n += 'A'-'a';
                }
                s[i] = n;
                if(i < 2)
                        break;
                if(fp->f3 & FVLONG)
                        vl = (uvlong)vl / b;
                else
                        v = (ulong)v / b;
                if(fp->f2 != NONE && i >= IDIGIT-fp->f2)
                        continue;
                if(fp->f3 & FVLONG) {
                        if(vl <= 0)
                                break;
                        continue;
                }
                if(v <= 0)
                        break;
        }

        if(fp->f3 & FSHARP) {
                if(b == 8 && s[i] != '0')
                        s[--i] = '0';
                if(b == 16) {
                        if(ucase)
                                s[--i] = 'X';
                        else
                                s[--i] = 'x';
                        s[--i] = '0';
                }
        }
        if(f)
                s[--i] = '-';
        fp->f2 = NONE;
        strconv(s+i, fp);
        return 0;
}

void
Strconv(Rune *s, Fconv *fp)
{
        int n, c, i;
        Rune rune;

        if(fp->f3 & FMINUS)
                fp->f1 = -fp->f1;
        n = 0;
        if(fp->f1 != NONE && fp->f1 >= 0) {
                for(; s[n]; n++)
                        ;
                while(n < fp->f1) {
                        if(fp->out < fp->eout)
                                *fp->out++ = ' ';
                        printcol++;
                        n++;
                }
        }
        for(;;) {
                c = *s++;
                if(c == 0)
                        break;
                n++;
                if(fp->f2 == NONE || fp->f2 > 0) {
                        if(fp->out < fp->eout)
                                if(c >= Runeself) {
                                        rune = c;
                                        i = runetochar(fp->out, &rune);
                                        fp->out += i;
                                } else
                                        *fp->out++ = c;
                        if(fp->f2 != NONE)
                                fp->f2--;
                        switch(c) {
                        default:
                                printcol++;
                                break;
                        case '\n':
                                printcol = 0;
                                break;
                        case '\t':
                                printcol = (printcol+8) & ~7;
                                break;
                        }
                }
        }
        if(fp->f1 != NONE && fp->f1 < 0) {
                fp->f1 = -fp->f1;
                while(n < fp->f1) {
                        if(fp->out < fp->eout)
                                *fp->out++ = ' ';
                        printcol++;
                        n++;
                }
        }
}

void
strconv(char *s, Fconv *fp)
{
        int n, c, i;
        Rune rune;

        if(fp->f3 & FMINUS)
                fp->f1 = -fp->f1;
        n = 0;
        if(fp->f1 != NONE && fp->f1 >= 0) {
                n = utflen(s);
                while(n < fp->f1) {
                        if(fp->out < fp->eout)
                                *fp->out++ = ' ';
                        printcol++;
                        n++;
                }
        }
        for(;;) {
                c = *s & 0xff;
                if(c >= Runeself) {
                        i = chartorune(&rune, s);
                        s += i;
                        c = rune;
                } else
                        s++;
                if(c == 0)
                        break;
                n++;
                if(fp->f2 == NONE || fp->f2 > 0) {
                        if(fp->out < fp->eout)
                                if(c >= Runeself) {
                                        rune = c;
                                        i = runetochar(fp->out, &rune);
                                        fp->out += i;
                                } else
                                        *fp->out++ = c;
                        if(fp->f2 != NONE)
                                fp->f2--;
                        switch(c) {
                        default:
                                printcol++;
                                break;
                        case '\n':
                                printcol = 0;
                                break;
                        case '\t':
                                printcol = (printcol+8) & ~7;
                                break;
                        }
                }
        }
        if(fp->f1 != NONE && fp->f1 < 0) {
                fp->f1 = -fp->f1;
                while(n < fp->f1) {
                        if(fp->out < fp->eout)
                                *fp->out++ = ' ';
                        printcol++;
                        n++;
                }
        }
}

static
int
noconv(va_list *arg, Fconv *fp)
{
        int n;
        char s[10];

        if(convcount == 0) {
                initfmt();
                n = 0;
                if(fp->chr >= 0 && fp->chr < MAXFMT)
                        n = fmtindex[fp->chr];
                return (*fmtconv[n])(arg, fp);
        }
        s[0] = '*';
        s[1] = fp->chr;
        s[2] = '*';
        s[3] = 0;
        fp->f1 = 0;
        fp->f2 = NONE;
        fp->f3 = 0;
        strconv(s, fp);
        return 0;
}

static
int
rconv(va_list*, Fconv *fp)
{
        char s[ERRLEN];

        s[0] = 0;
        errstr(s);
        fp->f2 = NONE;
        strconv(s, fp);
        return 0;
}

static
int
cconv(va_list *arg, Fconv *fp)
{
        char s[10];
        Rune rune;

        rune = va_arg(*arg, int);
        if(fp->chr == 'c')
                rune &= 0xff;
        s[runetochar(s, &rune)] = 0;

        fp->f2 = NONE;
        strconv(s, fp);
        return 0;
}

static
int
sconv(va_list *arg, Fconv *fp)
{
        char *s;
        Rune *r;

        if(fp->chr == 's') {
                s = va_arg(*arg, char*);
                if(s == 0)
                        s = "<null>";
                strconv(s, fp);
        } else {
                r = va_arg(*arg, Rune*);
                if(r == 0)
                        r = L"<null>";
                Strconv(r, fp);
        }
        return 0;
}

static
int
percent(va_list*, Fconv *fp)
{

        if(fp->out < fp->eout)
                *fp->out++ = '%';
        printcol++;
        return 0;
}

static
int
column(va_list *arg, Fconv *fp)
{
        int col, pc;

        col = va_arg(*arg, int);
        while(fp->out < fp->eout && printcol < col) {
                pc = (printcol+8) & ~7;
                if(pc <= col) {
                        *fp->out++ = '\t';
                        printcol = pc;
                } else {
                        *fp->out++ = ' ';
                        printcol++;
                }
        }
        return 0;
}

static
int
flags(va_list*, Fconv *fp)
{
        int f;

        f = 0;
        switch(fp->chr) {
        case '+':
                f = FPLUS;
                break;

        case '-':
                f = FMINUS;
                break;

        case '#':
                f = FSHARP;
                break;

        case 'h':
                f = FSHORT;
                break;

        case 'l':
                f = FLONG;
                if(fp->f3 & FLONG)
                        f = FVLONG;
                break;

        case 'u':
                f = FUNSIGN;
                break;
        }
        return -f;
}