diff options
Diffstat (limited to 'src/lib/sprintf.C')
-rw-r--r-- | src/lib/sprintf.C | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/src/lib/sprintf.C b/src/lib/sprintf.C new file mode 100644 index 000000000..9443c705e --- /dev/null +++ b/src/lib/sprintf.C @@ -0,0 +1,538 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/sprintf.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2013 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ +#include <util/sprintf.H> +#include <builtins.h> +#include <string.h> +#include <ctype.h> + +namespace Util +{ + +struct format_options +{ + enum + { + ALT_FORM = 0x1, + ZERO_PAD = 0x2, + LEFT_ALIGN = 0x4, + EMPTY_SIGN = 0x8, + PLUS_SIGN = 0x10 + }; + + int flags; + + size_t field_width; + size_t precision; + + enum lengths + { + LEN_INT, + LEN_CHAR, + LEN_SHORT, + LEN_LONG, + LEN_LONGLONG, + LEN_SIZET, + LEN_PTRDIFF, + }; + + lengths length; + + bool upper; + + enum types + { + TYPE_PERCENT, + TYPE_SIGNED_DECIMAL, + TYPE_DECIMAL, + TYPE_OCTAL, + TYPE_HEX, + TYPE_BINARY, + TYPE_CHAR, + TYPE_STRING, + TYPE_PTR, + }; + + types type; +}; + +void parse_format_options(format_options& opt, const char*& fmt) +{ + opt = format_options(); + ++fmt; // increment past first %. + + if (('\0' == *fmt) || ('%' == *fmt)) + { + opt.type = opt.TYPE_PERCENT; + return; + } + + // Look for 'flag' type options. + bool should_continue = true; + while(should_continue) + { + switch(*fmt) + { + case '#': + opt.flags |= opt.ALT_FORM; + ++fmt; + break; + + case '0': + opt.flags |= opt.ZERO_PAD; + ++fmt; + break; + + case '-': + opt.flags |= opt.LEFT_ALIGN; + ++fmt; + break; + + case ' ': + opt.flags |= opt.EMPTY_SIGN; + ++fmt; + break; + + case '+': + opt.flags |= opt.PLUS_SIGN; + ++fmt; + break; + + default: + should_continue = false; + break; + } + } + + // Look for field width options. + should_continue = true; + while (should_continue) + { + if(isdigit(*fmt)) + { + opt.field_width *= 10; + opt.field_width += (*fmt) - '0'; + ++fmt; + } + else + { + should_continue = false; + } + } + + // Look for precision options. + if ('.' == *fmt) + { + ++fmt; + should_continue = true; + while (should_continue) + { + if(isdigit(*fmt)) + { + opt.precision *= 10; + opt.precision += (*fmt) - '0'; + ++fmt; + } + else + { + should_continue = false; + } + } + } + + // Look for length modifiers. + should_continue = true; + while (should_continue) + { + switch (*fmt) + { + case 'h': + if ((opt.length == opt.LEN_SHORT) || + (opt.length == opt.LEN_CHAR)) + { + opt.length = opt.LEN_CHAR; + } + else + { + opt.length = opt.LEN_SHORT; + } + ++fmt; + break; + + case 'l': + if ((opt.length == opt.LEN_LONG) || + (opt.length == opt.LEN_LONGLONG)) + { + opt.length = opt.LEN_LONGLONG; + } + else + { + opt.length = opt.LEN_LONG; + } + ++fmt; + break; + + case 'z': + opt.length = opt.LEN_SIZET; + ++fmt; + break; + + case 't': + opt.length = opt.LEN_PTRDIFF; + ++fmt; + break; + + default: + should_continue = false; + break; + } + } + + // Look for the type specifier + switch (*fmt) + { + case 'd': + case 'i': + opt.type = opt.TYPE_SIGNED_DECIMAL; + break; + + case 'o': + opt.type = opt.TYPE_OCTAL; + break; + + case 'u': + opt.type = opt.TYPE_DECIMAL; + break; + + case 'x': + opt.type = opt.TYPE_HEX; + break; + + case 'X': + opt.type = opt.TYPE_HEX; + opt.upper = true; + break; + + case 'b': + opt.type = opt.TYPE_BINARY; + break; + + case 'B': + opt.type = opt.TYPE_BINARY; + opt.upper = true; + break; + + case 'c': + opt.type = opt.TYPE_CHAR; + break; + + case 's': + opt.type = opt.TYPE_STRING; + break; + + case 'p': + opt.type = opt.TYPE_PTR; + break; + + default: + opt.type = opt.TYPE_PERCENT; + } + if ('\0' != *fmt) + { + ++fmt; + } + +} + +size_t display_pre_header(ConsoleBufferInterface& func, + const format_options& f, size_t size) +{ + size_t count = 0; + if (!(f.flags & format_options::LEFT_ALIGN) && + f.field_width) + { + for (size_t i = size; i < f.field_width; i++) + { + func(' '); + ++count; + } + } + return count; +} + +size_t display_post_header(ConsoleBufferInterface& func, + const format_options& f, size_t size) +{ + size_t count = 0; + if ((f.flags & format_options::LEFT_ALIGN) && + f.field_width) + { + for (size_t i = size; i < f.field_width; i++) + { + func(' '); + ++count; + } + } + return count; +} + +size_t display_string(ConsoleBufferInterface& func, + const format_options& f, const char* string) +{ + size_t count = 0; + size_t len = strlen(string); + + count += display_pre_header(func, f, len); + for(size_t i = 0; i < len; i++) + { + func(string[i]); + ++count; + } + count += display_post_header(func, f, len); + + return count; +} + +size_t display_number(ConsoleBufferInterface& func, + const format_options& f, uint64_t number) +{ + static const char* digits = "0123456789abcdef"; + size_t count = 0; + + char output[64]; + size_t len = 0; + + // Determine sign of number. + char sign = '\0'; + if (f.type == format_options::TYPE_SIGNED_DECIMAL) + { + if (0 > (int64_t) number) + { + sign = '-'; + number = -1 * (int64_t) number; + } + else if (f.flags & format_options::PLUS_SIGN) + { + sign = '+'; + } + else if (f.flags & format_options::EMPTY_SIGN) + { + sign = ' '; + } + } + + // Determine base. + uint64_t base = 0; + switch (f.type) + { + case format_options::TYPE_BINARY: + base = 2; + break; + + case format_options::TYPE_OCTAL: + base = 8; + break; + + case format_options::TYPE_HEX: + case format_options::TYPE_PTR: + base = 16; + break; + + case format_options::TYPE_SIGNED_DECIMAL: + case format_options::TYPE_DECIMAL: + default: + base = 10; + break; + } + + // Determine alt-form header state. + size_t special_len = 0; + char special[2]; + + if ((f.flags & format_options::ALT_FORM) || + (f.type == format_options::TYPE_PTR)) + { + switch (f.type) + { + case format_options::TYPE_BINARY: + special[0] = '0'; + special[1] = 'b'; + special_len = 2; + break; + + case format_options::TYPE_HEX: + case format_options::TYPE_PTR: + special[0] = '0'; + special[1] = 'x'; + special_len = 2; + break; + + case format_options::TYPE_OCTAL: + special[0] = '0'; + special_len = 1; + break; + + default: + break; + } + } + + // Convert number to ascii. + while(number) + { + output[len++] = digits[number % base]; + number /= base; + } + if (len == 0) + { + output[len++] = digits[0]; + } + + // Fix up zero pad. + while(len < f.precision) + { + output[len++] = digits[0]; + } + + // Output pre-header. + size_t total_len = len + (sign == '\0' ? 0 : 1) + special_len; + if (f.flags & format_options::ZERO_PAD) + { + while (total_len < f.field_width) + { + output[len++] = digits[0]; + ++total_len; + } + } + else + { + count += display_pre_header(func, f, total_len); + } + + // Output sign, special. + if (sign != '\0') + { + func(sign); + ++count; + } + if (special_len) + { + for (size_t i = 0; i < special_len; i++) + { + func(f.upper ? toupper(special[i]) : special[i]); + ++count; + } + } + + // Output number. + for (size_t i = 0; i < len; i++) + { + func(f.upper ? toupper(output[len - (i+1)]) : output[len - (i+1)]); + ++count; + } + + // Output post-header. + if (!(f.flags & format_options::ZERO_PAD)) + { + count += display_post_header(func, f, total_len); + } + + return count; +} + +size_t vasprintf(ConsoleBufferInterface& func, const char* fmt, va_list& args) +{ + int count = 0; + + while(*fmt) + { + if ('%' == *fmt) + { + format_options f; + parse_format_options(f, fmt); + + switch(f.type) + { + case format_options::TYPE_PERCENT: + func('%'); + ++count; + break; + + case format_options::TYPE_PTR: + display_number(func, f, (uint64_t) va_arg(args, void*)); + break; + + case format_options::TYPE_CHAR: + count += display_pre_header(func, f, 1); + func(va_arg(args,int)); + ++count; + count += display_post_header(func, f, 1); + break; + + case format_options::TYPE_STRING: + count += display_string(func, f, va_arg(args,const char*)); + break; + + // All the number cases. + default: + { + uint64_t number = 0; + switch (f.length) + { + case format_options::LEN_INT: + case format_options::LEN_CHAR: + case format_options::LEN_SHORT: + // All of these types are automatically + // promoted to 'unsigned int' when passed + // through va_arg lists. + number = (uint64_t) va_arg(args, unsigned int); + break; + + case format_options::LEN_LONG: + case format_options::LEN_LONGLONG: + // Hostboot doesn't use 'long long' but FSP + // code tends to use %llx for uint64_t. + number = (uint64_t) va_arg(args, unsigned long); + break; + + case format_options::LEN_SIZET: + number = (uint64_t) va_arg(args, size_t); + break; + + case format_options::LEN_PTRDIFF: + number = (uint64_t) va_arg(args, ptrdiff_t); + break; + } + count += display_number(func, f, number); + } + } + } + else // Stand-alone character. + { + func(*fmt++); + ++count; + } + } + return count; +} + +}; |