root/src/lib9/fmt/dofmt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dofmt
  2. __fmtflush
  3. __fmtpad
  4. __rfmtpad
  5. __fmtcpy
  6. __fmtrcpy
  7. __charfmt
  8. __runefmt
  9. fmtstrcpy
  10. __strfmt
  11. fmtrunestrcpy
  12. __runesfmt
  13. __percentfmt
  14. __ifmt
  15. __countfmt
  16. __flagfmt
  17. __badfmt

/*
 * The authors of this software are Rob Pike and Ken Thompson,
 * with contributions from Mike Burrows and Sean Dorward.
 *
 *     Copyright (c) 2002-2006 by Lucent Technologies.
 *     Portions Copyright (c) 2004 Google Inc.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES
 * NOR GOOGLE INC MAKE ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING
 * THE MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */

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

/* format the output into f->to and return the number of characters fmted  */
int
dofmt(Fmt *f, char *fmt)
{
        Rune rune, *rt, *rs;
        Rune r;
        char *t, *s;
        int n, nfmt;

        nfmt = f->nfmt;
        for(;;){
                if(f->runes){
                        rt = (Rune*)f->to;
                        rs = (Rune*)f->stop;
                        while((r = (Rune)*(uchar*)fmt) && r != '%'){
                                if(r < Runeself)
                                        fmt++;
                                else{
                                        fmt += chartorune(&rune, fmt);
                                        r = rune;
                                }
                                FMTRCHAR(f, rt, rs, r);
                        }
                        fmt++;
                        f->nfmt += (int)(rt - (Rune *)f->to);
                        f->to = rt;
                        if(!r)
                                return f->nfmt - nfmt;
                        f->stop = rs;
                }else{
                        t = (char*)f->to;
                        s = (char*)f->stop;
                        while((r = (Rune)*(uchar*)fmt) && r != '%'){
                                if(r < Runeself){
                                        FMTCHAR(f, t, s, r);
                                        fmt++;
                                }else{
                                        n = chartorune(&rune, fmt);
                                        if(t + n > s){
                                                t = (char*)__fmtflush(f, t, n);
                                                if(t != nil)
                                                        s = (char*)f->stop;
                                                else
                                                        return -1;
                                        }
                                        while(n--)
                                                *t++ = *fmt++;
                                }
                        }
                        fmt++;
                        f->nfmt += (int)(t - (char *)f->to);
                        f->to = t;
                        if(!r)
                                return f->nfmt - nfmt;
                        f->stop = s;
                }

                fmt = (char*)__fmtdispatch(f, fmt, 0);
                if(fmt == nil)
                        return -1;
        }
}

void *
__fmtflush(Fmt *f, void *t, int len)
{
        if(f->runes)
                f->nfmt += (int)((Rune*)t - (Rune*)f->to);
        else
                f->nfmt += (int)((char*)t - (char *)f->to);
        f->to = t;
        if(f->flush == 0 || (*f->flush)(f) == 0 || (char*)f->to + len > (char*)f->stop){
                f->stop = f->to;
                return nil;
        }
        return f->to;
}

/*
 * put a formatted block of memory sz bytes long of n runes into the output buffer,
 * left/right justified in a field of at least f->width characters (if FmtWidth is set)
 */
int
__fmtpad(Fmt *f, int n)
{
        char *t, *s;
        int i;

        t = (char*)f->to;
        s = (char*)f->stop;
        for(i = 0; i < n; i++)
                FMTCHAR(f, t, s, ' ');
        f->nfmt += (int)(t - (char *)f->to);
        f->to = t;
        return 0;
}

int
__rfmtpad(Fmt *f, int n)
{
        Rune *t, *s;
        int i;

        t = (Rune*)f->to;
        s = (Rune*)f->stop;
        for(i = 0; i < n; i++)
                FMTRCHAR(f, t, s, ' ');
        f->nfmt += (int)(t - (Rune *)f->to);
        f->to = t;
        return 0;
}

int
__fmtcpy(Fmt *f, const void *vm, int n, int sz)
{
        Rune *rt, *rs, r;
        char *t, *s, *m, *me;
        ulong fl;
        int nc, w;

        m = (char*)vm;
        me = m + sz;
        fl = f->flags;
        w = 0;
        if(fl & FmtWidth)
                w = f->width;
        if((fl & FmtPrec) && n > f->prec)
                n = f->prec;
        if(f->runes){
                if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
                        return -1;
                rt = (Rune*)f->to;
                rs = (Rune*)f->stop;
                for(nc = n; nc > 0; nc--){
                        r = *(uchar*)m;
                        if(r < Runeself)
                                m++;
                        else if((me - m) >= UTFmax || fullrune(m, (int)(me-m)))
                                m += chartorune(&r, m);
                        else
                                break;
                        FMTRCHAR(f, rt, rs, r);
                }
                f->nfmt += (int)(rt - (Rune *)f->to);
                f->to = rt;
                if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
                        return -1;
        }else{
                if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
                        return -1;
                t = (char*)f->to;
                s = (char*)f->stop;
                for(nc = n; nc > 0; nc--){
                        r = *(uchar*)m;
                        if(r < Runeself)
                                m++;
                        else if((me - m) >= UTFmax || fullrune(m, (int)(me-m)))
                                m += chartorune(&r, m);
                        else
                                break;
                        FMTRUNE(f, t, s, r);
                }
                f->nfmt += (int)(t - (char *)f->to);
                f->to = t;
                if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
                        return -1;
        }
        return 0;
}

int
__fmtrcpy(Fmt *f, const void *vm, int n)
{
        Rune r, *m, *me, *rt, *rs;
        char *t, *s;
        ulong fl;
        int w;

        m = (Rune*)vm;
        fl = f->flags;
        w = 0;
        if(fl & FmtWidth)
                w = f->width;
        if((fl & FmtPrec) && n > f->prec)
                n = f->prec;
        if(f->runes){
                if(!(fl & FmtLeft) && __rfmtpad(f, w - n) < 0)
                        return -1;
                rt = (Rune*)f->to;
                rs = (Rune*)f->stop;
                for(me = m + n; m < me; m++)
                        FMTRCHAR(f, rt, rs, *m);
                f->nfmt += (int)(rt - (Rune *)f->to);
                f->to = rt;
                if(fl & FmtLeft && __rfmtpad(f, w - n) < 0)
                        return -1;
        }else{
                if(!(fl & FmtLeft) && __fmtpad(f, w - n) < 0)
                        return -1;
                t = (char*)f->to;
                s = (char*)f->stop;
                for(me = m + n; m < me; m++){
                        r = *m;
                        FMTRUNE(f, t, s, r);
                }
                f->nfmt += (int)(t - (char *)f->to);
                f->to = t;
                if(fl & FmtLeft && __fmtpad(f, w - n) < 0)
                        return -1;
        }
        return 0;
}

/* fmt out one character */
int
__charfmt(Fmt *f)
{
        char x[1];

        x[0] = (char)va_arg(f->args, int);
        f->prec = 1;
        return __fmtcpy(f, (const char*)x, 1, 1);
}

/* fmt out one rune */
int
__runefmt(Fmt *f)
{
        Rune x[1];

        x[0] = (Rune)va_arg(f->args, int);
        return __fmtrcpy(f, (const void*)x, 1);
}

/* public helper routine: fmt out a null terminated string already in hand */
int
fmtstrcpy(Fmt *f, char *s)
{
        int i, j;

        if(!s)
                return __fmtcpy(f, "<nil>", 5, 5);
        /* if precision is specified, make sure we don't wander off the end */
        if(f->flags & FmtPrec){
#ifdef PLAN9PORT
                Rune r;
                i = 0;
                for(j=0; j<f->prec && s[i]; j++)
                        i += chartorune(&r, s+i);
#else
                /* ANSI requires precision in bytes, not Runes */
                for(i=0; i<f->prec; i++)
                        if(s[i] == 0)
                                break;
                j = utfnlen(s, i);      /* won't print partial at end */
#endif
                return __fmtcpy(f, s, j, i);
        }
        return __fmtcpy(f, s, utflen(s), (int)strlen(s));
}

/* fmt out a null terminated utf string */
int
__strfmt(Fmt *f)
{
        char *s;

        s = va_arg(f->args, char *);
        return fmtstrcpy(f, s);
}

/* public helper routine: fmt out a null terminated rune string already in hand */
int
fmtrunestrcpy(Fmt *f, Rune *s)
{
        Rune *e;
        int n, p;

        if(!s)
                return __fmtcpy(f, "<nil>", 5, 5);
        /* if precision is specified, make sure we don't wander off the end */
        if(f->flags & FmtPrec){
                p = f->prec;
                for(n = 0; n < p; n++)
                        if(s[n] == 0)
                                break;
        }else{
                for(e = s; *e; e++)
                        ;
                n = (int)(e - s);
        }
        return __fmtrcpy(f, s, n);
}

/* fmt out a null terminated rune string */
int
__runesfmt(Fmt *f)
{
        Rune *s;

        s = va_arg(f->args, Rune *);
        return fmtrunestrcpy(f, s);
}

/* fmt a % */
int
__percentfmt(Fmt *f)
{
        Rune x[1];

        x[0] = f->r;
        f->prec = 1;
        return __fmtrcpy(f, (const void*)x, 1);
}

/* fmt an integer */
int
__ifmt(Fmt *f)
{
        char buf[140], *p, *conv;
        /* 140: for 64 bits of binary + 3-byte sep every 4 digits */
        uvlong vu;
        ulong fl, u;
        int neg, base, i, n, w, isv;
        int ndig, len, excess, bytelen;
        char *grouping;
        char *thousands;

        neg = 0;
        fl = f->flags;
        isv = 0;
        vu = 0;
        u = 0;
#ifndef PLAN9PORT
        /*
         * Unsigned verbs for ANSI C
         */
        switch(f->r){
        case 'o':
        case 'p':
        case 'u':
        case 'x':
        case 'X':
                fl |= FmtUnsigned;
                fl &= ~(FmtSign|FmtSpace);
                break;
        }
#endif
        if(f->r == 'p'){
                u = (uintptr)va_arg(f->args, void*);
                f->r = 'x';
                fl |= FmtUnsigned;
        }else if(fl & FmtVLong){
                isv = 1;
                if(fl & FmtUnsigned)
                        vu = va_arg(f->args, uvlong);
                else
                        vu = (uvlong)va_arg(f->args, vlong);
        }else if(fl & FmtLong){
                if(fl & FmtUnsigned)
                        u = va_arg(f->args, ulong);
                else
                        u = (ulong)va_arg(f->args, long);
        }else if(fl & FmtByte){
                if(fl & FmtUnsigned)
                        u = (uchar)va_arg(f->args, int);
                else
                        u = (ulong)(char)va_arg(f->args, int);
        }else if(fl & FmtShort){
                if(fl & FmtUnsigned)
                        u = (ushort)va_arg(f->args, int);
                else
                        u = (ulong)(short)va_arg(f->args, int);
        }else{
                if(fl & FmtUnsigned)
                        u = va_arg(f->args, uint);
                else
                        u = (ulong)va_arg(f->args, int);
        }
        conv = "0123456789abcdef";
        grouping = "\4";        /* for hex, octal etc. (undefined by spec but nice) */
        thousands = f->thousands;
        switch(f->r){
        case 'd':
        case 'i':
        case 'u':
                base = 10;
                grouping = f->grouping;
                break;
        case 'X':
                conv = "0123456789ABCDEF";
                /* fall through */
        case 'x':
                base = 16;
                thousands = ":";
                break;
        case 'b':
                base = 2;
                thousands = ":";
                break;
        case 'o':
                base = 8;
                break;
        default:
                return -1;
        }
        if(!(fl & FmtUnsigned)){
                if(isv && (vlong)vu < 0){
                        vu = (uvlong)-(vlong)vu;
                        neg = 1;
                }else if(!isv && (long)u < 0){
                        u = (ulong)-(long)u;
                        neg = 1;
                }
        }
        p = buf + sizeof buf - 1;
        n = 0;  /* in runes */
        excess = 0;     /* number of bytes > number runes */
        ndig = 0;
        len = utflen(thousands);
        bytelen = (int)strlen(thousands);
        if(isv){
                while(vu){
                        i = (int)(vu % (uvlong)base);
                        vu /= (uvlong)base;
                        if((fl & FmtComma) && n % 4 == 3){
                                *p-- = ',';
                                n++;
                        }
                        if((fl & FmtApost) && __needsep(&ndig, &grouping)){
                                n += len;
                                excess += bytelen - len;
                                p -= bytelen;
                                memmove(p+1, thousands, (size_t)bytelen);
                        }
                        *p-- = conv[i];
                        n++;
                }
        }else{
                while(u){
                        i = (int)(u % (ulong)base);
                        u /= (ulong)base;
                        if((fl & FmtComma) && n % 4 == 3){
                                *p-- = ',';
                                n++;
                        }
                        if((fl & FmtApost) && __needsep(&ndig, &grouping)){
                                n += len;
                                excess += bytelen - len;
                                p -= bytelen;
                                memmove(p+1, thousands, (size_t)bytelen);
                        }
                        *p-- = conv[i];
                        n++;
                }
        }
        if(n == 0){
                /*
                 * "The result of converting a zero value with
                 * a precision of zero is no characters."  - ANSI
                 *
                 * "For o conversion, # increases the precision, if and only if
                 * necessary, to force the first digit of the result to be a zero
                 * (if the value and precision are both 0, a single 0 is printed)." - ANSI
                 */
                if(!(fl & FmtPrec) || f->prec != 0 || (f->r == 'o' && (fl & FmtSharp))){
                        *p-- = '0';
                        n = 1;
                        if(fl & FmtApost)
                                __needsep(&ndig, &grouping);
                }

                /*
                 * Zero values don't get 0x.
                 */
                if(f->r == 'x' || f->r == 'X')
                        fl &= ~(ulong)FmtSharp;
        }
        for(w = f->prec; n < w && p > buf+3; n++){
                if((fl & FmtApost) && __needsep(&ndig, &grouping)){
                        n += len;
                        excess += bytelen - len;
                        p -= bytelen;
                        memmove(p+1, thousands, (size_t)bytelen);
                }
                *p-- = '0';
        }
        if(neg || (fl & (FmtSign|FmtSpace)))
                n++;
        if(fl & FmtSharp){
                if(base == 16)
                        n += 2;
                else if(base == 8){
                        if(p[1] == '0')
                                fl &= ~(ulong)FmtSharp;
                        else
                                n++;
                }
        }
        if((fl & FmtZero) && !(fl & (FmtLeft|FmtPrec))){
                w = 0;
                if(fl & FmtWidth)
                        w = f->width;
                for(; n < w && p > buf+3; n++){
                        if((fl & FmtApost) && __needsep(&ndig, &grouping)){
                                n += len;
                                excess += bytelen - len;
                                p -= bytelen;
                                memmove(p+1, thousands, (size_t)bytelen);
                        }
                        *p-- = '0';
                }
                f->flags &= ~(ulong)FmtWidth;
        }
        if(fl & FmtSharp){
                if(base == 16)
                        *p-- = (char)f->r;
                if(base == 16 || base == 8)
                        *p-- = '0';
        }
        if(neg)
                *p-- = '-';
        else if(fl & FmtSign)
                *p-- = '+';
        else if(fl & FmtSpace)
                *p-- = ' ';
        f->flags &= ~(ulong)FmtPrec;
        return __fmtcpy(f, p + 1, n, n + excess);
}

int
__countfmt(Fmt *f)
{
        void *p;
        ulong fl;

        fl = f->flags;
        p = va_arg(f->args, void*);
        if(fl & FmtVLong){
                *(vlong*)p = f->nfmt;
        }else if(fl & FmtLong){
                *(long*)p = f->nfmt;
        }else if(fl & FmtByte){
                *(char*)p = (char)f->nfmt;
        }else if(fl & FmtShort){
                *(short*)p = (short)f->nfmt;
        }else{
                *(int*)p = f->nfmt;
        }
        return 0;
}

int
__flagfmt(Fmt *f)
{
        switch(f->r){
        case ',':
                f->flags |= FmtComma;
                break;
        case '-':
                f->flags |= FmtLeft;
                break;
        case '+':
                f->flags |= FmtSign;
                break;
        case '#':
                f->flags |= FmtSharp;
                break;
        case '\'':
                f->flags |= FmtApost;
                break;
        case ' ':
                f->flags |= FmtSpace;
                break;
        case 'u':
                f->flags |= FmtUnsigned;
                break;
        case 'h':
                if(f->flags & FmtShort)
                        f->flags |= FmtByte;
                f->flags |= FmtShort;
                break;
        case 'L':
                f->flags |= FmtLDouble;
                break;
        case 'l':
                if(f->flags & FmtLong)
                        f->flags |= FmtVLong;
                f->flags |= FmtLong;
                break;
        }
        return 1;
}

/* default error format */
int
__badfmt(Fmt *f)
{
        char x[2+UTFmax];
        int n;

        x[0] = '%';
        n = 1 + runetochar(x+1, &f->r);
        x[n++] = '%';
        f->prec = n;
        __fmtcpy(f, (const void*)x, n, n);
        return 0;
}

/* [<][>][^][v][top][bottom][index][help] */