root/src/lib9/fmt/fmt.c

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

DEFINITIONS

This source file includes following definitions.
  1. __fmtinstall
  2. fmtinstall
  3. fmtfmt
  4. __fmtdispatch

/*
 * 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"

enum
{
        Maxfmt = 64
};

typedef struct Convfmt Convfmt;
struct Convfmt
{
        int     c;
        volatile        Fmts    fmt;    /* for spin lock in fmtfmt; avoids race due to write order */
};

static struct
{
        /* lock by calling __fmtlock, __fmtunlock */
        int     nfmt;
        Convfmt fmt[Maxfmt];
} fmtalloc;

static Convfmt knownfmt[] = {
        ' ',    __flagfmt,
        '#',    __flagfmt,
        '%',    __percentfmt,
        '\'',   __flagfmt,
        '+',    __flagfmt,
        ',',    __flagfmt,
        '-',    __flagfmt,
        'C',    __runefmt,      /* Plan 9 addition */
        'E',    __efgfmt,
#ifndef PLAN9PORT
        'F',    __efgfmt,       /* ANSI only */
#endif
        'G',    __efgfmt,
#ifndef PLAN9PORT
        'L',    __flagfmt,      /* ANSI only */
#endif
        'S',    __runesfmt,     /* Plan 9 addition */
        'X',    __ifmt,
        'b',    __ifmt,         /* Plan 9 addition */
        'c',    __charfmt,
        'd',    __ifmt,
        'e',    __efgfmt,
        'f',    __efgfmt,
        'g',    __efgfmt,
        'h',    __flagfmt,
#ifndef PLAN9PORT
        'i',    __ifmt,         /* ANSI only */
#endif
        'l',    __flagfmt,
        'n',    __countfmt,
        'o',    __ifmt,
        'p',    __ifmt,
        'r',    __errfmt,
        's',    __strfmt,
#ifdef PLAN9PORT
        'u',    __flagfmt,
#else
        'u',    __ifmt,
#endif
        'x',    __ifmt,
        0,      nil,
};


int     (*fmtdoquote)(int);

/*
 * __fmtlock() must be set
 */
static int
__fmtinstall(int c, Fmts f)
{
        Convfmt *p, *ep;

        if(c<=0 || c>=65536)
                return -1;
        if(!f)
                f = __badfmt;

        ep = &fmtalloc.fmt[fmtalloc.nfmt];
        for(p=fmtalloc.fmt; p<ep; p++)
                if(p->c == c)
                        break;

        if(p == &fmtalloc.fmt[Maxfmt])
                return -1;

        p->fmt = f;
        if(p == ep){    /* installing a new format character */
                fmtalloc.nfmt++;
                p->c = c;
        }

        return 0;
}

int
fmtinstall(int c, int (*f)(Fmt*))
{
        int ret;

        __fmtlock();
        ret = __fmtinstall(c, f);
        __fmtunlock();
        return ret;
}

static Fmts
fmtfmt(int c)
{
        Convfmt *p, *ep;

        ep = &fmtalloc.fmt[fmtalloc.nfmt];
        for(p=fmtalloc.fmt; p<ep; p++)
                if(p->c == c){
                        while(p->fmt == nil)    /* loop until value is updated */
                                ;
                        return p->fmt;
                }

        /* is this a predefined format char? */
        __fmtlock();
        for(p=knownfmt; p->c; p++)
                if(p->c == c){
                        __fmtinstall(p->c, p->fmt);
                        __fmtunlock();
                        return p->fmt;
                }
        __fmtunlock();

        return __badfmt;
}

void*
__fmtdispatch(Fmt *f, void *fmt, int isrunes)
{
        Rune rune, r;
        int i, n;

        f->flags = 0;
        f->width = f->prec = 0;

        for(;;){
                if(isrunes){
                        r = *(Rune*)fmt;
                        fmt = (Rune*)fmt + 1;
                }else{
                        fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
                        r = rune;
                }
                f->r = r;
                switch(r){
                case '\0':
                        return nil;
                case '.':
                        f->flags |= FmtWidth|FmtPrec;
                        continue;
                case '0':
                        if(!(f->flags & FmtWidth)){
                                f->flags |= FmtZero;
                                continue;
                        }
                        /* fall through */
                case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                        i = 0;
                        while(r >= '0' && r <= '9'){
                                i = i * 10 + (int)r - '0';
                                if(isrunes){
                                        r = *(Rune*)fmt;
                                        fmt = (Rune*)fmt + 1;
                                }else{
                                        r = (Rune)*(char*)fmt;
                                        fmt = (char*)fmt + 1;
                                }
                        }
                        if(isrunes)
                                fmt = (Rune*)fmt - 1;
                        else
                                fmt = (char*)fmt - 1;
                numflag:
                        if(f->flags & FmtWidth){
                                f->flags |= FmtPrec;
                                f->prec = i;
                        }else{
                                f->flags |= FmtWidth;
                                f->width = i;
                        }
                        continue;
                case '*':
                        i = va_arg(f->args, int);
                        if(i < 0){
                                /*
                                 * negative precision =>
                                 * ignore the precision.
                                 */
                                if(f->flags & FmtPrec){
                                        f->flags &= ~(ulong)FmtPrec;
                                        f->prec = 0;
                                        continue;
                                }
                                i = -i;
                                f->flags |= FmtLeft;
                        }
                        goto numflag;
                }
                n = (*fmtfmt((int)r))(f);
                if(n < 0)
                        return nil;
                if(n == 0)
                        return fmt;
        }
}

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