Coverage Report

Created: 2025-08-03 06:53

/src/plan9port/src/lib9/fmt/fmt.c
Line
Count
Source (jump to first uncovered line)
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
0
{
111
0
  Convfmt *p;
112
0
  int i;
113
114
0
  if(c<=0 || c>=65536)
115
0
    return -1;
116
0
  if(!f)
117
0
    f = __badfmt;
118
119
0
  i = atomic_load(&fmtalloc.nfmt);
120
0
  if(i == Maxfmt)
121
0
    return -1;
122
0
  p = &fmtalloc.fmt[i];
123
0
  p->c = c;
124
0
  p->fmt = f;
125
0
  atomic_store(&fmtalloc.nfmt, i+1);
126
127
0
  return 0;
128
0
}
129
130
int
131
fmtinstall(int c, int (*f)(Fmt*))
132
0
{
133
0
  int ret;
134
135
0
  __fmtlock();
136
0
  ret = __fmtinstall(c, f);
137
0
  __fmtunlock();
138
0
  return ret;
139
0
}
140
141
static Fmts
142
fmtfmt(int c)
143
20.9k
{
144
20.9k
  Convfmt *p, *ep;
145
146
20.9k
  ep = &fmtalloc.fmt[atomic_load(&fmtalloc.nfmt)];
147
142k
  for(p=ep; p-- > fmtalloc.fmt; )
148
142k
    if(p->c == c)
149
20.9k
      return p->fmt;
150
151
0
  return __badfmt;
152
20.9k
}
153
154
void*
155
__fmtdispatch(Fmt *f, void *fmt, int isrunes)
156
20.9k
{
157
20.9k
  Rune rune, r;
158
20.9k
  int i, n;
159
160
20.9k
  f->flags = 0;
161
20.9k
  f->width = f->prec = 0;
162
163
25.8k
  for(;;){
164
25.8k
    if(isrunes){
165
0
      r = *(Rune*)fmt;
166
0
      fmt = (Rune*)fmt + 1;
167
25.8k
    }else{
168
25.8k
      fmt = (char*)fmt + chartorune(&rune, (char*)fmt);
169
25.8k
      r = rune;
170
25.8k
    }
171
25.8k
    f->r = r;
172
25.8k
    switch(r){
173
0
    case '\0':
174
0
      return nil;
175
2.46k
    case '.':
176
2.46k
      f->flags |= FmtWidth|FmtPrec;
177
2.46k
      continue;
178
0
    case '0':
179
0
      if(!(f->flags & FmtWidth)){
180
0
        f->flags |= FmtZero;
181
0
        continue;
182
0
      }
183
      /* fall through */
184
2.46k
    case '1': case '2': case '3': case '4':
185
2.46k
    case '5': case '6': case '7': case '8': case '9':
186
2.46k
      i = 0;
187
4.92k
      while(r >= '0' && r <= '9'){
188
2.46k
        i = i * 10 + r - '0';
189
2.46k
        if(isrunes){
190
0
          r = *(Rune*)fmt;
191
0
          fmt = (Rune*)fmt + 1;
192
2.46k
        }else{
193
2.46k
          r = *(char*)fmt;
194
2.46k
          fmt = (char*)fmt + 1;
195
2.46k
        }
196
2.46k
      }
197
2.46k
      if(isrunes)
198
0
        fmt = (Rune*)fmt - 1;
199
2.46k
      else
200
2.46k
        fmt = (char*)fmt - 1;
201
2.46k
    numflag:
202
2.46k
      if(f->flags & FmtWidth){
203
2.46k
        f->flags |= FmtPrec;
204
2.46k
        f->prec = i;
205
2.46k
      }else{
206
0
        f->flags |= FmtWidth;
207
0
        f->width = i;
208
0
      }
209
2.46k
      continue;
210
0
    case '*':
211
0
      i = va_arg(f->args, int);
212
0
      if(i < 0){
213
        /*
214
         * negative precision =>
215
         * ignore the precision.
216
         */
217
0
        if(f->flags & FmtPrec){
218
0
          f->flags &= ~FmtPrec;
219
0
          f->prec = 0;
220
0
          continue;
221
0
        }
222
0
        i = -i;
223
0
        f->flags |= FmtLeft;
224
0
      }
225
0
      goto numflag;
226
25.8k
    }
227
20.9k
    n = (*fmtfmt(r))(f);
228
20.9k
    if(n < 0)
229
0
      return nil;
230
20.9k
    if(n == 0)
231
20.9k
      return fmt;
232
20.9k
  }
233
20.9k
}