Xen Test Framework
vsnprintf.c
Go to the documentation of this file.
1 #include <arch/div.h>
2 #include <xtf/libc.h>
3 #include <xtf/compiler.h>
4 
5 #ifndef isdigit
6 /* Avoid pulling in all of ctypes just for this. */
7 static int isdigit(int c)
8 {
9  return c >= '0' && c <= '9';
10 }
11 #endif
12 
13 /*
14  * The subset of formatting supported:
15  *
16  * From the C11 specification:
17  * A conversion specification is the '%' character followed by:
18  * - Zero or more flags: ('-', '+', ' ', '#', '0')
19  * - '-' Left justified
20  * - '+' Explicit sign
21  * - ' ' Space instead of sign
22  * - '#' Alternative representation
23  * - '0' Zero-pad from the left
24  * - Optional minimum width field ('*' or integer)
25  * - Optional precision ('.' followed by '*' or integer)
26  * - Optional length modifier:
27  * - 'hh' - char
28  * - 'h' - short
29  * - 'l' - long
30  * - 'll' - long long
31  * - 'z' - size_t
32  * - Mandatory coversion specifier:
33  * - signed integer ('d', 'i')
34  * - unsigned integer ('o', 'u', 'x', 'X' uppercase)
35  * - unsigned char ('c')
36  * - array of char ('s')
37  * - pointer to void ('p')
38  * - literal '%'
39  */
40 
41 /* Flags */
42 #define LEFT (1u << 0)
43 #define PLUS (1u << 1)
44 #define SPACE (1u << 2)
45 #define ALTERNATE (1u << 3)
46 #define ZERO (1u << 4)
47 /* Conversions */
48 #define UPPER (1u << 5)
49 #define SIGNED (1u << 6)
50 /* Careful not to overlap with vsnprintf_internal() flags. */
51 
52 /* Shorthand for ensuring str moves forwards, but not overruning the buffer. */
53 #define PUT(c) \
54  ({ if ( str < end ) \
55  *str = (c); \
56  ++str; \
57  })
58 
59 static int fmt_int(const char **fmt)
60 {
61  int res = 0;
62 
63  while( isdigit(**fmt) )
64  {
65  res *= 10;
66  res += **fmt - '0';
67  ++*fmt;
68  }
69 
70  return res;
71 }
72 
73 char *fmt_number(char *str, char *end, long long val, unsigned int base,
74  int width, int precision, unsigned int flags)
75 {
76  static const char lower[] = "0123456789abcdef";
77  static const char upper[] = "0123456789ABCDEF";
78  const char *digits = (flags & UPPER) ? upper : lower;
79 
80  char tmp[24], prefix = '\0';
81  int i = 0;
82  uint64_t uval = val;
83 
84  /* Sanity check base. */
85  if ( !(base == 8 || base == 10 || base == 16) )
86  return str;
87 
88  /* Override zeropad if we are aligning left or have a specific precsion. */
89  if ( (flags & LEFT) || (precision != -1) )
90  flags &= ~ZERO;
91 
92  /* Signed values only can get explicit svign treatment. */
93  if ( flags & SIGNED )
94  {
95  if ( val < 0 )
96  {
97  prefix = '-';
98  uval = -val;
99  width--;
100  }
101  else if ( flags & PLUS )
102  {
103  prefix = '+';
104  width--;
105  }
106  else if ( flags & SPACE )
107  {
108  prefix = ' ';
109  width--;
110  }
111  }
112 
113  /* Alternate representation applies to oct/hex only. */
114  if ( flags & ALTERNATE )
115  switch ( base )
116  {
117  case 16: width -= 2; break;
118  case 8: width -= 1; break;
119  }
120 
121  /* Make sure we at least have a single '0'. */
122  if ( val == 0 )
123  tmp[i++] = '0';
124  else
125  /* tmp contains the number formatted backwards. */
126  while ( uval )
127  tmp[i++] = digits[divmod64(&uval, base)];
128 
129  /* Expand precision if the number is too long. */
130  if ( i > precision )
131  precision = i;
132  width -= precision;
133 
134  /* If we are doing nothing special, pad with ' ' on the LHS. */
135  if ( (flags & (LEFT|ZERO)) == 0 )
136  while ( width-- > 0 )
137  PUT(' ');
138 
139  /* Optional sign character for signed numbers. */
140  if ( prefix )
141  PUT(prefix);
142 
143  /* Optional leading '0x' or '0'. */
144  if ( flags & ALTERNATE )
145  switch ( base )
146  {
147  case 16: PUT('0'); PUT('x'); break;
148  case 8: PUT('0'); break;
149  }
150 
151  /* Zero pad at the start of the number. */
152  if ( flags & ZERO )
153  while ( width-- > 0 )
154  PUT('0');
155 
156  /* If we have too fewer digits than precision, zero pad some more. */
157  while ( i < precision-- )
158  PUT('0');
159 
160  /* Copy the number from tmp. */
161  while ( i-- )
162  PUT(tmp[i]);
163 
164  /* Pad any remaining width on the RHS. */
165  while (width-- > 0)
166  PUT(' ');
167 
168  return str;
169 }
170 
171 char *fmt_string(char *str, char *end, const char *val,
172  int width, int precision, unsigned int flags)
173 {
174  int len, i;
175 
176  if ( !val )
177  val = "(NULL)";
178 
179  if ( precision < 0 )
180  len = strlen(val);
181  else
182  len = strnlen(val, precision);
183 
184  if ( !(flags & LEFT) )
185  while ( len < width-- )
186  PUT(' ');
187 
188  for ( i = 0; i < len; ++i )
189  {
190  if ( (flags & LF_TO_CRLF) && val[i] == '\n' )
191  PUT('\r');
192  PUT(val[i]);
193  }
194 
195  while ( len < width-- )
196  PUT(' ');
197 
198  return str;
199 }
200 
201 static char *pointer(
202  char *str, char *end, const char **fmt_ptr, const void *arg,
203  int width, int precision, unsigned int flags)
204 {
205  const char *fmt = *fmt_ptr;
206 
207  switch ( fmt[1] )
208  {
209  case 'h': /* Raw buffer as hex string. */
210  {
211  const uint8_t *hex_buffer = arg;
212  char sep = ' ';
213 
214  /* Consumed 'h' from the format string. */
215  ++*fmt_ptr;
216 
217  /* Bound user count from %* to between 0 and 128 bytes. */
218  if ( width <= 0 )
219  return str;
220  if ( width > 128 )
221  width = 128;
222 
223  /*
224  * Peek ahead in the format string to see if a recognised separator
225  * modifier is present.
226  */
227  switch ( fmt[2] )
228  {
229  case 'C': /* Colons. */
230  ++*fmt_ptr;
231  sep = ':';
232  break;
233 
234  case 'D': /* Dashes. */
235  ++*fmt_ptr;
236  sep = '-';
237  break;
238 
239  case 'N': /* No separator. */
240  ++*fmt_ptr;
241  sep = 0;
242  break;
243  }
244 
245  for ( int i = 0; ; )
246  {
247  /* Each byte: 2 chars, 0-padded, base 16, no hex prefix. */
248  str = fmt_number(str, end, hex_buffer[i], 16, 2, -1, ZERO);
249 
250  if ( ++i == width )
251  return str;
252 
253  if ( sep )
254  PUT(sep);
255  }
256  }
257  break;
258 
259  default:
260  if ( arch_fmt_pointer(&str, end, fmt_ptr,
261  arg, width, precision, flags) )
262  return str;
263  }
264 
265  /* Fall back to plain 32/64bit hex integer. */
266  if ( width == -1 )
267  {
268  width = 2 * sizeof(void *);
269  flags |= ZERO;
270  }
271 
272  return fmt_number(str, end, (unsigned long)arg, 16,
273  width, precision, flags);
274 }
275 
276 int vsnprintf_internal(char *buf, size_t size, const char *fmt, va_list args,
277  unsigned int caller_flags)
278 {
279  char *str = buf, *end = buf + size;
280 
281  for ( ; *fmt != '\0'; ++fmt )
282  {
283  const char *spec_start = fmt; /* For rewinding on error. */
284 
285  unsigned long long num;
286  unsigned int base, flags = caller_flags;
287  int width = -1, precision = -1;
288  char c, length_mod = 'i';
289 
290  /* Put regular characters into the destination. */
291  if ( *fmt != '%' )
292  {
293  c = *fmt;
294  goto put_char;
295  }
296 
297  next_flag: /* Process any flags. */
298  ++fmt;
299  switch ( *fmt )
300  {
301  case '-': flags |= LEFT; goto next_flag;
302  case '+': flags |= PLUS; goto next_flag;
303  case ' ': flags |= SPACE; goto next_flag;
304  case '#': flags |= ALTERNATE; goto next_flag;
305  case '0': flags |= ZERO; goto next_flag;
306  }
307 
308  /* Process the width field. */
309  if ( *fmt == '*' )
310  {
311  ++fmt;
312  width = va_arg(args, int);
313  if ( width < 0 )
314  {
315  flags |= LEFT;
316  width = -width;
317  }
318  }
319  else if ( isdigit(*fmt) )
320  {
321  width = fmt_int(&fmt);
322  if ( width < 0 )
323  {
324  flags |= LEFT;
325  width = -width;
326  }
327  }
328 
329  /* Process the precision field. */
330  if ( *fmt == '.' )
331  {
332  ++fmt;
333  if ( *fmt == '*' )
334  {
335  ++fmt;
336  precision = va_arg(args, int);
337  }
338  else if ( isdigit(*fmt) )
339  precision = fmt_int(&fmt);
340 
341  /* Negative precision is meaningless */
342  if ( precision < 0 )
343  precision = -1;
344  }
345 
346  /* Process the length modifier. */
347  switch ( *fmt )
348  {
349  case 'h': length_mod = 'h'; ++fmt; break;
350  case 'l': length_mod = 'l'; ++fmt; break;
351  case 'z': length_mod = 'z'; ++fmt; break;
352  }
353  /* Might be two... */
354  switch ( *fmt )
355  {
356  case 'h': length_mod = 'H'; ++fmt; break;
357  case 'l': length_mod = 'L'; ++fmt; break;
358  }
359 
360  /* Process the conversion modifier. */
361  switch ( *fmt )
362  {
363  case '%': /* Literal '%'. */
364  PUT('%');
365  continue;
366 
367  case 'c': /* Unsigned char. */
368  c = va_arg(args, int);
369 
370  if ( !(flags & LEFT) )
371  while ( --width > 0 )
372  PUT(' ');
373 
374  put_char:
375  if ( (flags & LF_TO_CRLF) && c == '\n' )
376  PUT('\r');
377  PUT(c);
378 
379  while ( --width > 0 )
380  PUT(' ');
381 
382  continue;
383 
384  case 's': /* String. */
385  str = fmt_string(str, end, va_arg(args, const char *),
386  width, precision, flags);
387  continue;
388 
389  case 'p': /* Pointer. */
390  str = pointer(str, end, &fmt, va_arg(args, const void *),
391  width, precision, flags);
392  continue;
393 
394  default: /* Something unrecognised - print the specifier literally. */
395  PUT('%');
396  fmt = spec_start;
397  continue;
398 
399  /* From here on down, all the numbers. */
400 
401  case 'o': /* Octal. */
402  base = 8;
403  break;
404 
405  case 'd': case 'i': /* Signed decimal. */
406  flags |= SIGNED;
407  /* fallthrough */
408  case 'u': /* Unsigned decimal. */
409  base = 10;
410  break;
411 
412  case 'X': /* Uppercase hex. */
413  flags |= UPPER;
414  /* fallthrough */
415  case 'x': /* Lowercase hex. */
416  base = 16;
417  break;
418  }
419 
420  /* Pull the arg and cast correctly. */
421  switch ( length_mod )
422  {
423  case 'H':
424  if ( flags & SIGNED )
425  num = (signed char)va_arg(args, int);
426  else
427  num = (unsigned char)va_arg(args, int);
428  break;
429 
430  case 'h':
431  if ( flags & SIGNED )
432  num = (signed short)va_arg(args, int);
433  else
434  num = (unsigned short)va_arg(args, int);
435  break;
436 
437  case 'i':
438  if ( flags & SIGNED )
439  num = (signed int)va_arg(args, int);
440  else
441  num = (unsigned int)va_arg(args, int);
442  break;
443 
444  case 'z':
445  num = (size_t)va_arg(args, size_t);
446  break;
447 
448  case 'l':
449  if ( flags & SIGNED )
450  num = (signed long)va_arg(args, long);
451  else
452  num = (unsigned long)va_arg(args, unsigned long);
453  break;
454 
455  case 'L':
456  if ( flags & SIGNED )
457  num = (signed long long)va_arg(args, long long);
458  else
459  num = (unsigned long long)va_arg(args, unsigned long long);
460  break;
461 
462  default: /* Really shouldn't happen, but rewind just in case. */
463  PUT('%');
464  fmt = spec_start;
465  continue;
466  }
467 
468  str = fmt_number(str, end, num, base, width, precision, flags);
469  }
470 
471  /* NUL terminate the buffer, if there is room (but don't count '\0'). */
472  if ( str < end )
473  *str = '\0';
474  /*
475  * Or trucate at the final character if an overrun occurred and buf is not
476  * 0 length.
477  */
478  else if ( size > 0 )
479  end[-1] = '\0';
480 
481  return str - buf;
482 }
483 
484 /*
485  * Local variables:
486  * mode: C
487  * c-file-style: "BSD"
488  * c-basic-offset: 4
489  * tab-width: 4
490  * indent-tabs-mode: nil
491  * End:
492  */
static int fmt_int(const char **fmt)
Definition: vsnprintf.c:59
#define PLUS
Definition: vsnprintf.c:43
#define strlen(s)
Definition: libc.h:18
uint16_t size
Definition: memory.h:24
bool arch_fmt_pointer(char **str_ptr, char *end, const char **fmt_ptr, const void *arg, int width, int precision, unsigned int flags)
Definition: decode.c:91
#define UPPER
Definition: vsnprintf.c:48
int vsnprintf_internal(char *buf, size_t size, const char *fmt, va_list args, unsigned int caller_flags)
Definition: vsnprintf.c:276
#define PUT(c)
Definition: vsnprintf.c:53
#define SPACE
Definition: vsnprintf.c:44
#define LF_TO_CRLF
Definition: libc.h:48
#define ZERO
Definition: vsnprintf.c:46
char * fmt_string(char *str, char *end, const char *val, int width, int precision, unsigned int flags)
Definition: vsnprintf.c:171
static uint32_t divmod64(uint64_t *dividend, uint32_t divisor)
Definition: div.h:11
#define ALTERNATE
Definition: vsnprintf.c:45
size_t strnlen(const char *str, size_t max)
Definition: string.c:13
__UINT64_TYPE__ uint64_t
Definition: stdint.h:17
#define SIGNED
Definition: vsnprintf.c:49
static unsigned int str(void)
Definition: lib.h:352
__SIZE_TYPE__ size_t
Definition: stddef.h:9
__builtin_va_list va_list
Definition: stdarg.h:9
char * fmt_number(char *str, char *end, long long val, unsigned int base, int width, int precision, unsigned int flags)
Definition: vsnprintf.c:73
#define LEFT
Definition: vsnprintf.c:42
#define va_arg(v, l)
Definition: stdarg.h:12
static int isdigit(int c)
Definition: vsnprintf.c:7
__UINT8_TYPE__ uint8_t
Definition: stdint.h:14
static char * pointer(char *str, char *end, const char **fmt_ptr, const void *arg, int width, int precision, unsigned int flags)
Definition: vsnprintf.c:201