fiss

Friedel's Initialization and Service Supervision
Log | Files | Refs | LICENSE

fmt.c (4751B)


      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 "fmt.h"
     27 #include "fmtdef.h"
     28 #include "plan9.h"
     29 
     30 enum {
     31 	Maxfmt = 128
     32 };
     33 
     34 typedef struct Convfmt Convfmt;
     35 struct Convfmt {
     36 	int  c;
     37 	Fmts fmt;
     38 };
     39 
     40 static struct
     41 {
     42 	/*
     43 	 * lock updates to fmt by calling __fmtlock, __fmtunlock.
     44 	 * reads can start at nfmt and work backward without
     45 	 * further locking. later fmtinstalls take priority over earlier
     46 	 * ones because of the backwards loop.
     47 	 * once installed, a format is never overwritten.
     48 	 */
     49 	_Atomic int nfmt;
     50 	Convfmt     fmt[Maxfmt];
     51 } fmtalloc = {
     52 #ifdef PLAN9PORT
     53 	ATOMIC_VAR_INIT(27),
     54 #else
     55 	ATOMIC_VAR_INIT(30),
     56 #endif
     57 	{
     58 	    { ' ', __flagfmt },
     59 	    { '#', __flagfmt },
     60 	    { '%', __percentfmt },
     61 	    { '\'', __flagfmt },
     62 	    { '+', __flagfmt },
     63 	    { ',', __flagfmt },
     64 	    { '-', __flagfmt },
     65 	    { 'C', __runefmt }, /* Plan 9 addition */
     66 	    { 'E', __efgfmt },
     67 #ifndef PLAN9PORT
     68 	    { 'F', __efgfmt }, /* ANSI only */
     69 #endif
     70 	    { 'G', __efgfmt },
     71 #ifndef PLAN9PORT
     72 	    { 'L', __flagfmt }, /* ANSI only */
     73 #endif
     74 	    { 'S', __runesfmt }, /* Plan 9 addition */
     75 	    { 'X', __ifmt },
     76 	    { 'b', __ifmt }, /* Plan 9 addition */
     77 	    { 'c', __charfmt },
     78 	    { 'd', __ifmt },
     79 	    { 'e', __efgfmt },
     80 	    { 'f', __efgfmt },
     81 	    { 'g', __efgfmt },
     82 	    { 'h', __flagfmt },
     83 #ifndef PLAN9PORT
     84 	    { 'i', __ifmt }, /* ANSI only */
     85 #endif
     86 	    { 'l', __flagfmt },
     87 	    { 'n', __countfmt },
     88 	    { 'o', __ifmt },
     89 	    { 'p', __ifmt },
     90 	    { 'r', __errfmt },
     91 	    { 's', __strfmt },
     92 #ifdef PLAN9PORT
     93 	    { 'u', __flagfmt },
     94 #else
     95 	    { 'u', __ifmt },
     96 #endif
     97 	    { 'x', __ifmt },
     98 	}
     99 };
    100 
    101 int (*fmtdoquote)(int);
    102 
    103 /*
    104  * __fmtlock() must be set
    105  */
    106 static int
    107 __fmtinstall(int c, Fmts f) {
    108 	Convfmt* p;
    109 	int      i;
    110 
    111 	if (c <= 0 || c >= 65536)
    112 		return -1;
    113 	if (!f)
    114 		f = __badfmt;
    115 
    116 	i = atomic_load(&fmtalloc.nfmt);
    117 	if (i == Maxfmt)
    118 		return -1;
    119 	p      = &fmtalloc.fmt[i];
    120 	p->c   = c;
    121 	p->fmt = f;
    122 	atomic_store(&fmtalloc.nfmt, i + 1);
    123 
    124 	return 0;
    125 }
    126 
    127 int fmtinstall(int c, int (*f)(Fmt*)) {
    128 	int ret;
    129 
    130 	__fmtlock();
    131 	ret = __fmtinstall(c, f);
    132 	__fmtunlock();
    133 	return ret;
    134 }
    135 
    136 static Fmts
    137 fmtfmt(int c) {
    138 	Convfmt *p, *ep;
    139 
    140 	ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
    141 	for (p = ep; p-- > fmtalloc.fmt;)
    142 		if (p->c == c)
    143 			return p->fmt;
    144 
    145 	return __badfmt;
    146 }
    147 
    148 void* __fmtdispatch(Fmt* f, void* fmt, int isrunes) {
    149 	Rune rune, r;
    150 	int  i, n;
    151 
    152 	f->flags = 0;
    153 	f->width = f->prec = 0;
    154 
    155 	for (;;) {
    156 		if (isrunes) {
    157 			r   = *(Rune*) fmt;
    158 			fmt = (Rune*) fmt + 1;
    159 		} else {
    160 			fmt = (char*) fmt + chartorune(&rune, (char*) fmt);
    161 			r   = rune;
    162 		}
    163 		f->r = r;
    164 		switch (r) {
    165 			case '\0':
    166 				return nil;
    167 			case '.':
    168 				f->flags |= FmtWidth | FmtPrec;
    169 				continue;
    170 			case '0':
    171 				if (!(f->flags & FmtWidth)) {
    172 					f->flags |= FmtZero;
    173 					continue;
    174 				}
    175 				/* fall through */
    176 			case '1':
    177 			case '2':
    178 			case '3':
    179 			case '4':
    180 			case '5':
    181 			case '6':
    182 			case '7':
    183 			case '8':
    184 			case '9':
    185 				i = 0;
    186 				while (r >= '0' && r <= '9') {
    187 					i = i * 10 + r - '0';
    188 					if (isrunes) {
    189 						r   = *(Rune*) fmt;
    190 						fmt = (Rune*) fmt + 1;
    191 					} else {
    192 						r   = *(char*) fmt;
    193 						fmt = (char*) fmt + 1;
    194 					}
    195 				}
    196 				if (isrunes)
    197 					fmt = (Rune*) fmt - 1;
    198 				else
    199 					fmt = (char*) fmt - 1;
    200 			numflag:
    201 				if (f->flags & FmtWidth) {
    202 					f->flags |= FmtPrec;
    203 					f->prec = i;
    204 				} else {
    205 					f->flags |= FmtWidth;
    206 					f->width = i;
    207 				}
    208 				continue;
    209 			case '*':
    210 				i = va_arg(f->args, int);
    211 				if (i < 0) {
    212 					/*
    213 					 * negative precision =>
    214 					 * ignore the precision.
    215 					 */
    216 					if (f->flags & FmtPrec) {
    217 						f->flags &= ~FmtPrec;
    218 						f->prec = 0;
    219 						continue;
    220 					}
    221 					i = -i;
    222 					f->flags |= FmtLeft;
    223 				}
    224 				goto numflag;
    225 		}
    226 		n = (*fmtfmt(r))(f);
    227 		if (n < 0)
    228 			return nil;
    229 		if (n == 0)
    230 			return fmt;
    231 	}
    232 }