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. */
7static 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
59static 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
73char *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
171char *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
201static 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
276int 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 unsigned int str(void)
Definition: lib.h:366
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
static uint32_t divmod64(uint64_t *dividend, uint32_t divisor)
Definition: div.h:11
#define LF_TO_CRLF
Definition: libc.h:48
#define strlen(s)
Definition: libc.h:18
#define va_arg(v, l)
Definition: stdarg.h:12
__builtin_va_list va_list
Definition: stdarg.h:9
__SIZE_TYPE__ size_t
Definition: stddef.h:9
__UINT64_TYPE__ uint64_t
Definition: stdint.h:17
__UINT8_TYPE__ uint8_t
Definition: stdint.h:14
size_t strnlen(const char *str, size_t max)
Definition: string.c:13
#define PUT(c)
Definition: vsnprintf.c:53
#define PLUS
Definition: vsnprintf.c:43
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
char * fmt_string(char *str, char *end, const char *val, int width, int precision, unsigned int flags)
Definition: vsnprintf.c:171
#define LEFT
Definition: vsnprintf.c:42
#define SIGNED
Definition: vsnprintf.c:49
#define SPACE
Definition: vsnprintf.c:44
static int fmt_int(const char **fmt)
Definition: vsnprintf.c:59
int vsnprintf_internal(char *buf, size_t size, const char *fmt, va_list args, unsigned int caller_flags)
Definition: vsnprintf.c:276
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 UPPER
Definition: vsnprintf.c:48
#define ZERO
Definition: vsnprintf.c:46
#define ALTERNATE
Definition: vsnprintf.c:45
static int isdigit(int c)
Definition: vsnprintf.c:7