plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

fmt.c (4434B)


      1 /* Copyright (c) 2002-2006 Lucent Technologies; see LICENSE */
      2 #include <stdarg.h>
      3 #include <string.h>
      4 
      5 /*
      6  * As of 2020, older systems like RHEL 6 and AIX still do not have C11 atomics.
      7  * On those systems, make the code use volatile int accesses and hope for the best.
      8  * (Most uses of fmtinstall are not actually racing with calls to print that lookup
      9  * formats. The code used volatile here for years without too many problems,
     10  * even though that's technically racy. A mutex is not OK, because we want to
     11  * be able to call print from signal handlers.)
     12  *
     13  * RHEL is using an old GCC (atomics were added in GCC 4.9).
     14  * AIX is using its own IBM compiler (XL C).
     15  */
     16 #if __IBMC__ || !__clang__ && __GNUC__ && (__GNUC__ < 4 || (__GNUC__==4 && __GNUC_MINOR__<9))
     17 #warning not using C11 stdatomic on legacy system
     18 #define _Atomic volatile
     19 #define atomic_load(x) (*(x))
     20 #define atomic_store(x, y) (*(x)=(y))
     21 #define ATOMIC_VAR_INIT(x) (x)
     22 #else
     23 #include <stdatomic.h>
     24 #endif
     25 
     26 #include "plan9.h"
     27 #include "fmt.h"
     28 #include "fmtdef.h"
     29 
     30 enum
     31 {
     32 	Maxfmt = 128
     33 };
     34 
     35 typedef struct Convfmt Convfmt;
     36 struct Convfmt
     37 {
     38 	int	c;
     39 	Fmts	fmt;
     40 };
     41 
     42 static struct
     43 {
     44 	/*
     45 	 * lock updates to fmt by calling __fmtlock, __fmtunlock.
     46 	 * reads can start at nfmt and work backward without
     47 	 * further locking. later fmtinstalls take priority over earlier
     48 	 * ones because of the backwards loop.
     49 	 * once installed, a format is never overwritten.
     50 	 */
     51 	_Atomic int	nfmt;
     52 	Convfmt	fmt[Maxfmt];
     53 } fmtalloc = {
     54 	#ifdef PLAN9PORT
     55 		ATOMIC_VAR_INIT(27),
     56 	#else
     57 		ATOMIC_VAR_INIT(30),
     58 	#endif
     59 	{
     60 		{' ',	__flagfmt},
     61 		{'#',	__flagfmt},
     62 		{'%',	__percentfmt},
     63 		{'\'',	__flagfmt},
     64 		{'+',	__flagfmt},
     65 		{',',	__flagfmt},
     66 		{'-',	__flagfmt},
     67 		{'C',	__runefmt},	/* Plan 9 addition */
     68 		{'E',	__efgfmt},
     69 	#ifndef PLAN9PORT
     70 		{'F',	__efgfmt},	/* ANSI only */
     71 	#endif
     72 		{'G',	__efgfmt},
     73 	#ifndef PLAN9PORT
     74 		{'L',	__flagfmt},	/* ANSI only */
     75 	#endif
     76 		{'S',	__runesfmt},	/* Plan 9 addition */
     77 		{'X',	__ifmt},
     78 		{'b',	__ifmt},		/* Plan 9 addition */
     79 		{'c',	__charfmt},
     80 		{'d',	__ifmt},
     81 		{'e',	__efgfmt},
     82 		{'f',	__efgfmt},
     83 		{'g',	__efgfmt},
     84 		{'h',	__flagfmt},
     85 	#ifndef PLAN9PORT
     86 		{'i',	__ifmt},		/* ANSI only */
     87 	#endif
     88 		{'l',	__flagfmt},
     89 		{'n',	__countfmt},
     90 		{'o',	__ifmt},
     91 		{'p',	__ifmt},
     92 		{'r',	__errfmt},
     93 		{'s',	__strfmt},
     94 	#ifdef PLAN9PORT
     95 		{'u',	__flagfmt},
     96 	#else
     97 		{'u',	__ifmt},
     98 	#endif
     99 		{'x',	__ifmt},
    100 	}
    101 };
    102 
    103 int	(*fmtdoquote)(int);
    104 
    105 /*
    106  * __fmtlock() must be set
    107  */
    108 static int
    109 __fmtinstall(int c, Fmts f)
    110 {
    111 	Convfmt *p;
    112 	int i;
    113 
    114 	if(c<=0 || c>=65536)
    115 		return -1;
    116 	if(!f)
    117 		f = __badfmt;
    118 
    119 	i = atomic_load(&fmtalloc.nfmt);
    120 	if(i == Maxfmt)
    121 		return -1;
    122 	p = &fmtalloc.fmt[i];
    123 	p->c = c;
    124 	p->fmt = f;
    125 	atomic_store(&fmtalloc.nfmt, i+1);
    126 
    127 	return 0;
    128 }
    129 
    130 int
    131 fmtinstall(int c, int (*f)(Fmt*))
    132 {
    133 	int ret;
    134 
    135 	__fmtlock();
    136 	ret = __fmtinstall(c, f);
    137 	__fmtunlock();
    138 	return ret;
    139 }
    140 
    141 static Fmts
    142 fmtfmt(int c)
    143 {
    144 	Convfmt *p, *ep;
    145 
    146 	ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
    147 	for(p=ep; p-- > fmtalloc.fmt; )
    148 		if(p->c == c)
    149 			return p->fmt;
    150 
    151 	return __badfmt;
    152 }
    153 
    154 void*
    155 __fmtdispatch(Fmt *f, void *fmt, int isrunes)
    156 {
    157 	Rune rune, r;
    158 	int i, n;
    159 
    160 	f->flags = 0;
    161 	f->width = f->prec = 0;
    162 
    163 	for(;;){
    164 		if(isrunes){
    165 			r = *(Rune*)fmt;
    166 			fmt = (Rune*)fmt + 1;
    167 		}else{
    168 			fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
    169 			r = rune;
    170 		}
    171 		f->r = r;
    172 		switch(r){
    173 		case '\0':
    174 			return nil;
    175 		case '.':
    176 			f->flags |= FmtWidth|FmtPrec;
    177 			continue;
    178 		case '0':
    179 			if(!(f->flags & FmtWidth)){
    180 				f->flags |= FmtZero;
    181 				continue;
    182 			}
    183 			/* fall through */
    184 		case '1': case '2': case '3': case '4':
    185 		case '5': case '6': case '7': case '8': case '9':
    186 			i = 0;
    187 			while(r >= '0' && r <= '9'){
    188 				i = i * 10 + r - '0';
    189 				if(isrunes){
    190 					r = *(Rune*)fmt;
    191 					fmt = (Rune*)fmt + 1;
    192 				}else{
    193 					r = *(char*)fmt;
    194 					fmt = (char*)fmt + 1;
    195 				}
    196 			}
    197 			if(isrunes)
    198 				fmt = (Rune*)fmt - 1;
    199 			else
    200 				fmt = (char*)fmt - 1;
    201 		numflag:
    202 			if(f->flags & FmtWidth){
    203 				f->flags |= FmtPrec;
    204 				f->prec = i;
    205 			}else{
    206 				f->flags |= FmtWidth;
    207 				f->width = i;
    208 			}
    209 			continue;
    210 		case '*':
    211 			i = va_arg(f->args, int);
    212 			if(i < 0){
    213 				/*
    214 				 * negative precision =>
    215 				 * ignore the precision.
    216 				 */
    217 				if(f->flags & FmtPrec){
    218 					f->flags &= ~FmtPrec;
    219 					f->prec = 0;
    220 					continue;
    221 				}
    222 				i = -i;
    223 				f->flags |= FmtLeft;
    224 			}
    225 			goto numflag;
    226 		}
    227 		n = (*fmtfmt(r))(f);
    228 		if(n < 0)
    229 			return nil;
    230 		if(n == 0)
    231 			return fmt;
    232 	}
    233 }