1 // sprintf() and related functions.
3 // This software is copyright (c) 2007 Scott Wood <scott@buserror.net>.
5 // This software is provided 'as-is', without any express or implied warranty.
6 // In no event will the authors or contributors be held liable for any damages
7 // arising from the use of this software.
9 // Permission is hereby granted to everyone, free of charge, to use, copy,
10 // modify, prepare derivative works of, publish, distribute, perform,
11 // sublicense, and/or sell copies of the Software, provided that the above
12 // copyright notice and disclaimer of warranty be included in all copies or
13 // substantial portions of this software.
26 group_thousands = 0x0020, // FIXME -- unimplemented
28 long_long_arg = 0x0080,
30 short_short_arg = 0x0200,
36 has_precision = 0x8000,
39 static void printf_string(char *buf, size_t &opos, size_t limit,
40 const char *src, size_t len)
43 size_t olen = opos + len <= limit ? len : limit - opos;
44 memcpy(buf + opos, src, olen);
50 static void printf_fill(char *buf, size_t &opos, size_t limit,
54 size_t olen = opos + len <= limit ? len : limit - opos;
55 memset(buf + opos, ch, olen);
61 static void printf_num(char *obuf, size_t &opos, size_t limit,
62 int64_t value, long radix, int fieldwidth,
63 int precision, int flags)
67 int letter = (flags & capital_hex) ? 'A' - 10 : 'a' - 10;
70 if (flags & num_signed)
71 uval = value < 0 ? -value : value;
75 // An explicit precision of 0 suppresses all output if the value
76 // is zero. Otherwise, the output size is not limited by precision
79 if (uval != 0 || !(flags & has_precision) || precision != 0) do {
80 int ch = uval % radix;
85 buf[pos] = ch + letter;
93 // length which counts against fieldwidth but not precision
96 if (flags & num_signed) {
98 printf_fill(obuf, opos, limit, '-', 1);
100 } else if (flags & always_sign) {
101 printf_fill(obuf, opos, limit, '+', 1);
103 } else if (flags & leave_blank) {
104 printf_fill(obuf, opos, limit, ' ', 1);
109 if ((flags & alt_form) && value != 0) {
110 if (radix == 8 && (!(flags & has_precision) || precision <= len)) {
111 flags |= has_precision;
116 printf_string(obuf, opos, limit, "0x", 2);
121 if ((flags & has_precision) && len < precision) {
130 if (!(flags & neg_field) && len < fieldwidth) {
131 char padchar = (flags & zero_pad) ? '0' : ' ';
132 printf_fill(obuf, opos, limit, padchar, fieldwidth - len);
136 if (precision != 0) {
137 printf_fill(obuf, opos, limit, '0', precision);
141 printf_string(obuf, opos, limit, buf + pos + 1, 64 - pos);
143 if ((flags & neg_field) && len < fieldwidth)
144 printf_fill(obuf, opos, limit, ' ', fieldwidth - len);
147 extern "C" size_t vsnprintf(char *buf, size_t size,
148 const char *str, va_list args)
150 size_t opos = 0; // position in the output string
151 unsigned int flags = 0;
157 for (size_t pos = 0; str[pos]; pos++) switch (state) {
159 if (str[pos] == '%') {
169 buf[opos] = str[pos];
174 case 1: // A percent has been seen; read in format characters
181 if (!(flags & has_precision)) {
189 if (flags & has_precision)
194 fieldwidth += str[pos++] - '0';
195 } while (str[pos] >= '0' && str[pos] <= '9');
201 if (fieldwidth || (flags & has_precision))
204 fieldwidth = va_arg(args, int);
208 flags |= has_precision;
210 if (str[pos + 1] == '*') {
212 precision = va_arg(args, int);
213 } else while (str[pos + 1] >= '0' && str[pos + 1] <= '9') {
215 precision += str[++pos] - '0';
225 flags |= leave_blank;
229 flags |= always_sign;
233 flags |= group_thousands;
237 if (flags & long_arg)
238 flags |= long_long_arg;
245 if (flags & long_arg)
246 flags |= short_short_arg;
257 flags |= ptrdiff_arg;
260 // Note that %z and other such "new" format characters are
261 // basically useless because some GCC coder actually went out
262 // of their way to make the compiler reject C99 format
263 // strings in C++ code, with no way of overriding it that I
264 // can find (the source code comments suggest the checking is
265 // only when using -pedantic, but I wasn't using -pedantic).
267 // Thus, we have the choice of either avoiding %z and friends
268 // (and possibly needing to insert hackish casts to silence
269 // the compiler's warnings if different architectures define
270 // types like size_t in different ways), or not using the
271 // format warnings at all.
273 // To mitigate this, 32-bit architectures should define
274 // pointer-sized special types as "long" rather than "int",
275 // so that %lx/%ld can always be used with them. Fixed-size
276 // 32-bit types should be declared as "int" rather than
277 // "long" for the same reason.
287 if ((flags & intmax_arg) || (flags & long_long_arg))
288 arg = va_arg(args, long long);
289 else if (flags & size_t_arg)
290 arg = va_arg(args, ssize_t);
291 else if (flags & ptrdiff_arg)
292 arg = va_arg(args, ptrdiff_t);
293 else if (flags & long_arg)
294 arg = va_arg(args, long);
295 else if (flags & short_short_arg)
296 arg = (signed char)va_arg(args, int);
297 else if (flags & short_arg)
298 arg = (short)va_arg(args, int);
300 arg = va_arg(args, int);
303 printf_num(buf, opos, size - 1, arg, 10,
304 fieldwidth, precision, flags);
310 flags |= capital_hex;
324 if ((flags & intmax_arg) || (flags & long_long_arg))
325 arg = va_arg(args, unsigned long long);
326 else if (flags & size_t_arg)
327 arg = va_arg(args, size_t);
328 else if (flags & ptrdiff_arg)
329 arg = va_arg(args, intptr_t);
330 else if (flags & long_arg)
331 arg = va_arg(args, unsigned long);
332 else if (flags & short_short_arg)
333 arg = (unsigned char)va_arg(args, unsigned int);
334 else if (flags & short_arg)
335 arg = (unsigned short)va_arg(args, unsigned int);
336 else if (flags & short_short_arg)
337 arg = (signed char)va_arg(args, int);
338 else if (flags & short_arg)
339 arg = (short)va_arg(args, int);
341 arg = va_arg(args, unsigned int);
343 printf_num(buf, opos, size - 1, arg, radix,
344 fieldwidth, precision, flags);
351 buf[opos] = va_arg(args, int);
358 const char *arg = va_arg(args, const char *);
363 size_t len = strlen(arg);
364 printf_string(buf, opos, size - 1, arg, len);
370 const void *arg = va_arg(args, const void *);
372 printf_num(buf, opos, size - 1, (unsigned long)arg, 16,
373 fieldwidth, precision, flags);
380 if ((flags & intmax_arg) || (flags & long_long_arg))
381 *va_arg(args, unsigned long long *) = opos;
382 else if (flags & size_t_arg)
383 *va_arg(args, ssize_t *) = opos;
384 else if (flags & ptrdiff_arg)
385 *va_arg(args, ptrdiff_t *) = opos;
386 else if (flags & long_arg)
387 *va_arg(args, long *) = opos;
388 else if (flags & short_short_arg)
389 *va_arg(args, signed char *) = opos;
390 else if (flags & short_arg)
391 *va_arg(args, short *) = opos;
393 *va_arg(args, int *) = opos;
399 default_case: // label for goto
402 buf[opos] = str[pos];
418 extern "C" size_t snprintf(char *buf, size_t size, const char *str, ...)
422 int ret = vsnprintf(buf, size, str, args);
427 extern "C" size_t sprintf(char *buf, const char *str, ...)
431 int ret = vsnprintf(buf, ULONG_MAX, str, args);