9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

fmt.c (3575B)


      1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
      2 #include <stdarg.h>
      3 #include <string.h>
      4 #include "plan9.h"
      5 #include "fmt.h"
      6 #include "fmtdef.h"
      7 
      8 enum
      9 {
     10 	Maxfmt = 64
     11 };
     12 
     13 typedef struct Convfmt Convfmt;
     14 struct Convfmt
     15 {
     16 	int	c;
     17 	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
     18 };
     19 
     20 static struct
     21 {
     22 	/* lock by calling __fmtlock, __fmtunlock */
     23 	int	nfmt;
     24 	Convfmt	fmt[Maxfmt];
     25 } fmtalloc;
     26 
     27 static Convfmt knownfmt[] = {
     28 	' ',	__flagfmt,
     29 	'#',	__flagfmt,
     30 	'%',	__percentfmt,
     31 	'\'',	__flagfmt,
     32 	'+',	__flagfmt,
     33 	',',	__flagfmt,
     34 	'-',	__flagfmt,
     35 	'C',	__runefmt,	/* Plan 9 addition */
     36 	'E',	__efgfmt,
     37 #ifndef PLAN9PORT
     38 	'F',	__efgfmt,	/* ANSI only */
     39 #endif
     40 	'G',	__efgfmt,
     41 #ifndef PLAN9PORT
     42 	'L',	__flagfmt,	/* ANSI only */
     43 #endif
     44 	'S',	__runesfmt,	/* Plan 9 addition */
     45 	'X',	__ifmt,
     46 	'b',	__ifmt,		/* Plan 9 addition */
     47 	'c',	__charfmt,
     48 	'd',	__ifmt,
     49 	'e',	__efgfmt,
     50 	'f',	__efgfmt,
     51 	'g',	__efgfmt,
     52 	'h',	__flagfmt,
     53 #ifndef PLAN9PORT
     54 	'i',	__ifmt,		/* ANSI only */
     55 #endif
     56 	'l',	__flagfmt,
     57 	'n',	__countfmt,
     58 	'o',	__ifmt,
     59 	'p',	__ifmt,
     60 	'r',	__errfmt,
     61 	's',	__strfmt,
     62 #ifdef PLAN9PORT
     63 	'u',	__flagfmt,
     64 #else
     65 	'u',	__ifmt,
     66 #endif
     67 	'x',	__ifmt,
     68 	0,	nil,
     69 };
     70 
     71 
     72 int	(*fmtdoquote)(int);
     73 
     74 /*
     75  * __fmtlock() must be set
     76  */
     77 static int
     78 __fmtinstall(int c, Fmts f)
     79 {
     80 	Convfmt *p, *ep;
     81 
     82 	if(c<=0 || c>=65536)
     83 		return -1;
     84 	if(!f)
     85 		f = __badfmt;
     86 
     87 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
     88 	for(p=fmtalloc.fmt; p<ep; p++)
     89 		if(p->c == c)
     90 			break;
     91 
     92 	if(p == &fmtalloc.fmt[Maxfmt])
     93 		return -1;
     94 
     95 	p->fmt = f;
     96 	if(p == ep){	/* installing a new format character */
     97 		fmtalloc.nfmt++;
     98 		p->c = c;
     99 	}
    100 
    101 	return 0;
    102 }
    103 
    104 int
    105 fmtinstall(int c, int (*f)(Fmt*))
    106 {
    107 	int ret;
    108 
    109 	__fmtlock();
    110 	ret = __fmtinstall(c, f);
    111 	__fmtunlock();
    112 	return ret;
    113 }
    114 
    115 static Fmts
    116 fmtfmt(int c)
    117 {
    118 	Convfmt *p, *ep;
    119 
    120 	ep = &fmtalloc.fmt[fmtalloc.nfmt];
    121 	for(p=fmtalloc.fmt; p<ep; p++)
    122 		if(p->c == c){
    123 			while(p->fmt == nil)	/* loop until value is updated */
    124 				;
    125 			return p->fmt;
    126 		}
    127 
    128 	/* is this a predefined format char? */
    129 	__fmtlock();
    130 	for(p=knownfmt; p->c; p++)
    131 		if(p->c == c){
    132 			__fmtinstall(p->c, p->fmt);
    133 			__fmtunlock();
    134 			return p->fmt;
    135 		}
    136 	__fmtunlock();
    137 
    138 	return __badfmt;
    139 }
    140 
    141 void*
    142 __fmtdispatch(Fmt *f, void *fmt, int isrunes)
    143 {
    144 	Rune rune, r;
    145 	int i, n;
    146 
    147 	f->flags = 0;
    148 	f->width = f->prec = 0;
    149 
    150 	for(;;){
    151 		if(isrunes){
    152 			r = *(Rune*)fmt;
    153 			fmt = (Rune*)fmt + 1;
    154 		}else{
    155 			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
    156 			r = rune;
    157 		}
    158 		f->r = r;
    159 		switch(r){
    160 		case '\0':
    161 			return nil;
    162 		case '.':
    163 			f->flags |= FmtWidth|FmtPrec;
    164 			continue;
    165 		case '0':
    166 			if(!(f->flags & FmtWidth)){
    167 				f->flags |= FmtZero;
    168 				continue;
    169 			}
    170 			/* fall through */
    171 		case '1': case '2': case '3': case '4':
    172 		case '5': case '6': case '7': case '8': case '9':
    173 			i = 0;
    174 			while(r >= '0' && r <= '9'){
    175 				i = i * 10 + r - '0';
    176 				if(isrunes){
    177 					r = *(Rune*)fmt;
    178 					fmt = (Rune*)fmt + 1;
    179 				}else{
    180 					r = *(char*)fmt;
    181 					fmt = (char*)fmt + 1;
    182 				}
    183 			}
    184 			if(isrunes)
    185 				fmt = (Rune*)fmt - 1;
    186 			else
    187 				fmt = (char*)fmt - 1;
    188 		numflag:
    189 			if(f->flags & FmtWidth){
    190 				f->flags |= FmtPrec;
    191 				f->prec = i;
    192 			}else{
    193 				f->flags |= FmtWidth;
    194 				f->width = i;
    195 			}
    196 			continue;
    197 		case '*':
    198 			i = va_arg(f->args, int);
    199 			if(i < 0){
    200 				/*
    201 				 * negative precision =>
    202 				 * ignore the precision.
    203 				 */
    204 				if(f->flags & FmtPrec){
    205 					f->flags &= ~FmtPrec;
    206 					f->prec = 0;
    207 					continue;
    208 				}
    209 				i = -i;
    210 				f->flags |= FmtLeft;
    211 			}
    212 			goto numflag;
    213 		}
    214 		n = (*fmtfmt(r))(f);
    215 		if(n < 0)
    216 			return nil;
    217 		if(n == 0)
    218 			return fmt;
    219 	}
    220 }