URI:
       tfmt.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfmt.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 }