opc source code
This commit is contained in:
595
third_party/open62541/deps/mp_printf.c
vendored
Normal file
595
third_party/open62541/deps/mp_printf.c
vendored
Normal file
@@ -0,0 +1,595 @@
|
||||
/**
|
||||
* @author (c) Julius Pfrommer
|
||||
* 2023, Fraunhofer IOSB, Germany
|
||||
* @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
|
||||
* 2021-2023, Haifa, Palestine/Israel
|
||||
* @author (c) Marco Paland (info@paland.com)
|
||||
* 2014-2019, PALANDesign Hannover, Germany
|
||||
*
|
||||
* @note Others have made smaller contributions to this file: see the
|
||||
* contributors page at https://github.com/eyalroz/printf/graphs/contributors
|
||||
* or ask one of the authors. The original code for exponential specifiers was
|
||||
* contributed by Martijn Jasperse <m.jasperse@gmail.com>.
|
||||
*
|
||||
* @brief Small stand-alone implementation of the printf family of functions
|
||||
* (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems with
|
||||
* limited resources.
|
||||
*
|
||||
* @note the implementations are thread-safe; re-entrant; use no functions from
|
||||
* the standard library; and do not dynamically allocate any memory.
|
||||
*
|
||||
* @license The MIT License (MIT)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "mp_printf.h"
|
||||
#include "dtoa.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// 'ntoa' conversion buffer size, this must be big enough to hold one converted
|
||||
// numeric number including padded zeros (dynamically created on stack)
|
||||
#define PRINTF_INTEGER_BUFFER_SIZE 32
|
||||
|
||||
// size of the fixed (on-stack) buffer for printing individual decimal numbers.
|
||||
// this must be big enough to hold one converted floating-point value including
|
||||
// padded zeros.
|
||||
#define PRINTF_DECIMAL_BUFFER_SIZE 32
|
||||
|
||||
// Default precision for the floating point conversion specifiers (the C
|
||||
// standard sets this at 6)
|
||||
#define PRINTF_DEFAULT_FLOAT_PRECISION 6
|
||||
|
||||
// internal flag definitions
|
||||
#define FLAGS_ZEROPAD (1U << 0U)
|
||||
#define FLAGS_LEFT (1U << 1U)
|
||||
#define FLAGS_PLUS (1U << 2U)
|
||||
#define FLAGS_SPACE (1U << 3U)
|
||||
#define FLAGS_HASH (1U << 4U)
|
||||
#define FLAGS_UPPERCASE (1U << 5U)
|
||||
#define FLAGS_CHAR (1U << 6U)
|
||||
#define FLAGS_SHORT (1U << 7U)
|
||||
// Only used with PRINTF_SUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS
|
||||
#define FLAGS_LONG (1U << 9U)
|
||||
#define FLAGS_LONG_LONG (1U << 10U)
|
||||
#define FLAGS_PRECISION (1U << 11U)
|
||||
#define FLAGS_ADAPT_EXP (1U << 12U)
|
||||
#define FLAGS_POINTER (1U << 13U)
|
||||
// Note: Similar, but not identical, effect as FLAGS_HASH
|
||||
#define FLAGS_SIGNED (1U << 14U)
|
||||
|
||||
#define BASE_BINARY 2
|
||||
#define BASE_OCTAL 8
|
||||
#define BASE_DECIMAL 10
|
||||
#define BASE_HEX 16
|
||||
|
||||
typedef unsigned int printf_flags_t;
|
||||
typedef uint8_t numeric_base_t;
|
||||
typedef unsigned long long printf_unsigned_value_t;
|
||||
typedef long long printf_signed_value_t;
|
||||
|
||||
// Note in particular the behavior here on LONG_MIN or LLONG_MIN; it is valid
|
||||
// and well-defined, but if you're not careful you can easily trigger undefined
|
||||
// behavior with -LONG_MIN or -LLONG_MIN
|
||||
#define ABS_FOR_PRINTING(_x) \
|
||||
((printf_unsigned_value_t)((_x) > 0 ? (_x) : -((printf_signed_value_t)_x)))
|
||||
|
||||
// internal secure strlen @return The length of the string (excluding the
|
||||
// terminating 0) limited by 'maxsize' @note strlen uses size_t, but wes only
|
||||
// use this function with size_t variables - hence the signature.
|
||||
static size_t
|
||||
strnlen_s_(const char *str, size_t maxsize) {
|
||||
for(size_t i = 0; i < maxsize; i++) {
|
||||
if(!str[i])
|
||||
return i;
|
||||
}
|
||||
return maxsize;
|
||||
}
|
||||
|
||||
// internal test if char is a digit (0-9)
|
||||
// @return true if char is a digit
|
||||
static bool is_digit_(char ch) { return (ch >= '0') && (ch <= '9'); }
|
||||
|
||||
// internal ASCII string to size_t conversion
|
||||
static size_t
|
||||
atou_(const char **str) {
|
||||
size_t i = 0U;
|
||||
while(is_digit_(**str)) {
|
||||
i = i * 10U + (size_t)(*((*str)++) - '0');
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// Output buffer
|
||||
typedef struct {
|
||||
char *buffer;
|
||||
size_t pos;
|
||||
size_t max_chars;
|
||||
} output_t;
|
||||
|
||||
static void
|
||||
putchar_(output_t *out, char c) {
|
||||
size_t write_pos = out->pos++;
|
||||
// We're _always_ increasing pos, so as to count how may characters
|
||||
// _would_ have been written if not for the max_chars limitation
|
||||
if(write_pos >= out->max_chars)
|
||||
return;
|
||||
// it must be the case that out->buffer != NULL , due to the constraint
|
||||
// on output_t ; and note we're relying on write_pos being non-negative.
|
||||
out->buffer[write_pos] = c;
|
||||
}
|
||||
|
||||
static void
|
||||
out_(output_t *out, const char *buf, size_t len) {
|
||||
if(out->pos < out->max_chars) {
|
||||
size_t write_len = len;
|
||||
if(out->pos + len > out->max_chars)
|
||||
write_len = out->max_chars - out->pos;
|
||||
for(size_t i = 0; i < write_len; i++)
|
||||
out->buffer[out->pos + i] = buf[i];
|
||||
}
|
||||
out->pos += len; // Always increase pos by len
|
||||
}
|
||||
|
||||
// output the specified string in reverse, taking care of any zero-padding
|
||||
static void
|
||||
out_rev_(output_t *output, const char *buf, size_t len, size_t width,
|
||||
printf_flags_t flags) {
|
||||
const size_t start_pos = output->pos;
|
||||
|
||||
// pad spaces up to given width
|
||||
if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
||||
for(size_t i = len; i < width; i++) {
|
||||
putchar_(output, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
// reverse string
|
||||
while(len) {
|
||||
putchar_(output, buf[--len]);
|
||||
}
|
||||
|
||||
// append pad spaces up to given width
|
||||
if(flags & FLAGS_LEFT) {
|
||||
while(output->pos - start_pos < width) {
|
||||
putchar_(output, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked by print_integer after the actual number has been printed, performing
|
||||
// necessary work on the number's prefix (as the number is initially printed in
|
||||
// reverse order)
|
||||
static void
|
||||
print_integer_finalization(output_t *output, char *buf, size_t len, bool negative,
|
||||
numeric_base_t base, size_t precision, size_t width,
|
||||
printf_flags_t flags) {
|
||||
size_t unpadded_len = len;
|
||||
|
||||
// pad with leading zeros
|
||||
if(!(flags & FLAGS_LEFT)) {
|
||||
if(width && (flags & FLAGS_ZEROPAD) &&
|
||||
(negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
|
||||
width--;
|
||||
}
|
||||
while((flags & FLAGS_ZEROPAD) && (len < width) &&
|
||||
(len < PRINTF_INTEGER_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
while((len < precision) && (len < PRINTF_INTEGER_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
|
||||
if(base == BASE_OCTAL && (len > unpadded_len)) {
|
||||
// Since we've written some zeros, we've satisfied the alternative format
|
||||
// leading space requirement
|
||||
flags &= ~FLAGS_HASH;
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if(flags & (FLAGS_HASH | FLAGS_POINTER)) {
|
||||
if(!(flags & FLAGS_PRECISION) && len && ((len == precision) || (len == width))) {
|
||||
// Let's take back some padding digits to fit in what will eventually be
|
||||
// the format-specific prefix
|
||||
if(unpadded_len < len) {
|
||||
len--; // This should suffice for BASE_OCTAL
|
||||
}
|
||||
if(len && (base == BASE_HEX || base == BASE_BINARY) && (unpadded_len < len)) {
|
||||
len--; // ... and an extra one for 0x or 0b
|
||||
}
|
||||
}
|
||||
if((base == BASE_HEX) && !(flags & FLAGS_UPPERCASE) &&
|
||||
(len < PRINTF_INTEGER_BUFFER_SIZE)) {
|
||||
buf[len++] = 'x';
|
||||
} else if((base == BASE_HEX) && (flags & FLAGS_UPPERCASE) &&
|
||||
(len < PRINTF_INTEGER_BUFFER_SIZE)) {
|
||||
buf[len++] = 'X';
|
||||
} else if((base == BASE_BINARY) && (len < PRINTF_INTEGER_BUFFER_SIZE)) {
|
||||
buf[len++] = 'b';
|
||||
}
|
||||
if(len < PRINTF_INTEGER_BUFFER_SIZE) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if(len < PRINTF_INTEGER_BUFFER_SIZE) {
|
||||
if(negative) {
|
||||
buf[len++] = '-';
|
||||
} else if(flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
} else if(flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
out_rev_(output, buf, len, width, flags);
|
||||
}
|
||||
|
||||
// An internal itoa-like function
|
||||
static void
|
||||
print_integer(output_t *output, printf_unsigned_value_t value, bool negative,
|
||||
numeric_base_t base, size_t precision, size_t width, printf_flags_t flags) {
|
||||
char buf[PRINTF_INTEGER_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
if(!value) {
|
||||
if(!(flags & FLAGS_PRECISION)) {
|
||||
buf[len++] = '0';
|
||||
flags &= ~FLAGS_HASH;
|
||||
// We drop this flag this since either the alternative and regular modes
|
||||
// of the specifier don't differ on 0 values, or (in the case of octal)
|
||||
// we've already provided the special handling for this mode.
|
||||
} else if(base == BASE_HEX) {
|
||||
flags &= ~FLAGS_HASH;
|
||||
// We drop this flag this since either the alternative and regular modes
|
||||
// of the specifier don't differ on 0 values
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] =
|
||||
(char)(digit < 10 ? '0' + digit
|
||||
: (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10);
|
||||
value /= base;
|
||||
} while(value && (len < PRINTF_INTEGER_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
print_integer_finalization(output, buf, len, negative, base, precision, width, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
print_floating_point(output_t *output, double value, size_t precision,
|
||||
size_t width, printf_flags_t flags) {
|
||||
if((flags & FLAGS_PLUS) && value > 0.0)
|
||||
putchar_(output, '+');
|
||||
|
||||
// set default precision, if not set explicitly
|
||||
//if(!(flags & FLAGS_PRECISION) || precision > PRINTF_DECIMAL_BUFFER_SIZE - 5)
|
||||
// precision = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
|
||||
char buf[PRINTF_DECIMAL_BUFFER_SIZE];
|
||||
unsigned len = dtoa(value, buf); // Fill the buffer (TODO: Consider precision)
|
||||
out_(output, buf, len); // Print the buffer
|
||||
}
|
||||
|
||||
// Advances the format pointer past the flags, and returns the parsed flags
|
||||
// due to the characters passed
|
||||
static printf_flags_t
|
||||
parse_flags(const char **format) {
|
||||
printf_flags_t flags = 0U;
|
||||
do {
|
||||
switch(**format) {
|
||||
case '0': flags |= FLAGS_ZEROPAD; break;
|
||||
case '-': flags |= FLAGS_LEFT; break;
|
||||
case '+': flags |= FLAGS_PLUS; break;
|
||||
case ' ': flags |= FLAGS_SPACE; break;
|
||||
case '#': flags |= FLAGS_HASH; break;
|
||||
default: return flags;
|
||||
}
|
||||
(*format)++;
|
||||
} while(true);
|
||||
}
|
||||
|
||||
#define ADVANCE_IN_FORMAT_STRING(cptr_) \
|
||||
do { \
|
||||
(cptr_)++; \
|
||||
if(!*(cptr_)) \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
static void
|
||||
format_string_loop(output_t *output, const char *format, va_list args) {
|
||||
while(*format) {
|
||||
if(*format != '%') {
|
||||
// A regular content character
|
||||
putchar_(output, *format);
|
||||
format++;
|
||||
continue;
|
||||
}
|
||||
// We're parsing a format specifier: %[flags][width][.precision][length]
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
|
||||
printf_flags_t flags = parse_flags(&format);
|
||||
|
||||
// evaluate width field
|
||||
size_t width = 0U;
|
||||
if(is_digit_(*format)) {
|
||||
width = atou_(&format);
|
||||
} else if(*format == '*') {
|
||||
const int w = va_arg(args, int);
|
||||
if(w < 0) {
|
||||
flags |= FLAGS_LEFT; // reverse padding
|
||||
width = (size_t)-w;
|
||||
} else {
|
||||
width = (size_t)w;
|
||||
}
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
}
|
||||
|
||||
// evaluate precision field
|
||||
size_t precision = 0U;
|
||||
if(*format == '.') {
|
||||
flags |= FLAGS_PRECISION;
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
if(is_digit_(*format)) {
|
||||
precision = atou_(&format);
|
||||
} else if(*format == '*') {
|
||||
const int precision_ = va_arg(args, int);
|
||||
precision = precision_ > 0 ? (size_t)precision_ : 0U;
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate length field
|
||||
switch(*format) {
|
||||
case 'l':
|
||||
flags |= FLAGS_LONG;
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
if(*format == 'l') {
|
||||
flags |= FLAGS_LONG_LONG;
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
flags |= FLAGS_SHORT;
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
if(*format == 'h') {
|
||||
flags |= FLAGS_CHAR;
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
flags |=
|
||||
(sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
break;
|
||||
case 'j':
|
||||
flags |=
|
||||
(sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
break;
|
||||
case 'z':
|
||||
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
ADVANCE_IN_FORMAT_STRING(format);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
switch(*format) {
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'b': {
|
||||
if(*format == 'd' || *format == 'i') {
|
||||
flags |= FLAGS_SIGNED;
|
||||
}
|
||||
|
||||
numeric_base_t base;
|
||||
if(*format == 'x' || *format == 'X') {
|
||||
base = BASE_HEX;
|
||||
} else if(*format == 'o') {
|
||||
base = BASE_OCTAL;
|
||||
} else if(*format == 'b') {
|
||||
base = BASE_BINARY;
|
||||
} else {
|
||||
base = BASE_DECIMAL;
|
||||
flags &=
|
||||
~FLAGS_HASH; // decimal integers have no alternative presentation
|
||||
}
|
||||
|
||||
if(*format == 'X') {
|
||||
flags |= FLAGS_UPPERCASE;
|
||||
}
|
||||
|
||||
format++;
|
||||
// ignore '0' flag when precision is given
|
||||
if(flags & FLAGS_PRECISION) {
|
||||
flags &= ~FLAGS_ZEROPAD;
|
||||
}
|
||||
|
||||
if(flags & FLAGS_SIGNED) {
|
||||
// A signed specifier: d, i or possibly I + bit size if enabled
|
||||
if(flags & FLAGS_LONG_LONG) {
|
||||
const long long value = va_arg(args, long long);
|
||||
print_integer(output, ABS_FOR_PRINTING(value), value < 0, base,
|
||||
precision, width, flags);
|
||||
} else if(flags & FLAGS_LONG) {
|
||||
const long value = va_arg(args, long);
|
||||
print_integer(output, ABS_FOR_PRINTING(value), value < 0, base,
|
||||
precision, width, flags);
|
||||
} else {
|
||||
// We never try to interpret the argument as something
|
||||
// potentially-smaller than int, due to integer promotion rules:
|
||||
// Even if the user passed a short int, short unsigned etc. -
|
||||
// these will come in after promotion, as int's (or unsigned for
|
||||
// the case of short unsigned when it has the same size as int)
|
||||
const int value =
|
||||
(flags & FLAGS_CHAR) ? (signed char)va_arg(args, int)
|
||||
: (flags & FLAGS_SHORT) ? (short int)va_arg(args, int)
|
||||
: va_arg(args, int);
|
||||
print_integer(output, ABS_FOR_PRINTING(value), value < 0, base,
|
||||
precision, width, flags);
|
||||
}
|
||||
} else {
|
||||
// An unsigned specifier: u, x, X, o, b
|
||||
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
|
||||
|
||||
if(flags & FLAGS_LONG_LONG) {
|
||||
print_integer(output, (printf_unsigned_value_t)
|
||||
va_arg(args, unsigned long long),
|
||||
false, base, precision, width, flags);
|
||||
} else if(flags & FLAGS_LONG) {
|
||||
print_integer(output, (printf_unsigned_value_t)
|
||||
va_arg(args, unsigned long),
|
||||
false, base, precision, width, flags);
|
||||
} else {
|
||||
const unsigned int value = (flags & FLAGS_CHAR)
|
||||
? (unsigned char)va_arg(args, unsigned int)
|
||||
: (flags & FLAGS_SHORT)
|
||||
? (unsigned short int)va_arg(args, unsigned int)
|
||||
: va_arg(args, unsigned int);
|
||||
print_integer(output, (printf_unsigned_value_t)value, false, base,
|
||||
precision, width, flags);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'f':
|
||||
case 'F':
|
||||
if(*format == 'F')
|
||||
flags |= FLAGS_UPPERCASE;
|
||||
print_floating_point(output, (double)va_arg(args, double),
|
||||
precision, width, flags);
|
||||
format++;
|
||||
break;
|
||||
|
||||
case 'c': {
|
||||
size_t l = 1U;
|
||||
// pre padding
|
||||
if(!(flags & FLAGS_LEFT)) {
|
||||
while(l++ < width) {
|
||||
putchar_(output, ' ');
|
||||
}
|
||||
}
|
||||
// char output
|
||||
putchar_(output, (char)va_arg(args, int));
|
||||
// post padding
|
||||
if(flags & FLAGS_LEFT) {
|
||||
while(l++ < width) {
|
||||
putchar_(output, ' ');
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 's': {
|
||||
const char *p = va_arg(args, char *);
|
||||
if(p == NULL) {
|
||||
out_rev_(output, ")llun(", 6, width, flags);
|
||||
} else {
|
||||
// string length
|
||||
size_t l = strnlen_s_(p, precision ? precision : INT32_MAX);
|
||||
if(flags & FLAGS_PRECISION) {
|
||||
l = (l < precision ? l : precision);
|
||||
}
|
||||
|
||||
// pre padding
|
||||
if(!(flags & FLAGS_LEFT)) {
|
||||
for(size_t i = 0; l + i < width; i++) {
|
||||
putchar_(output, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
// string output
|
||||
out_(output, p, l);
|
||||
|
||||
// post padding
|
||||
if(flags & FLAGS_LEFT) {
|
||||
for(size_t i = 0; l + i < width; i++) {
|
||||
putchar_(output, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p': {
|
||||
width =
|
||||
sizeof(void *) * 2U + 2; // 2 hex chars per byte + the "0x" prefix
|
||||
flags |= FLAGS_ZEROPAD | FLAGS_POINTER;
|
||||
uintptr_t value = (uintptr_t)va_arg(args, void *);
|
||||
(value == (uintptr_t)NULL)
|
||||
? out_rev_(output, ")lin(", 5, width, flags)
|
||||
: print_integer(output, (printf_unsigned_value_t)value, false,
|
||||
BASE_HEX, precision, width, flags);
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '%':
|
||||
putchar_(output, '%');
|
||||
format++;
|
||||
break;
|
||||
|
||||
default:
|
||||
putchar_(output, *format);
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mp_vsnprintf(char *s, size_t n, const char *format, va_list arg) {
|
||||
// Check that the inputs are sane
|
||||
if(!s || n < 1)
|
||||
return -1;
|
||||
|
||||
// Format the string
|
||||
output_t out = {s, 0, n};
|
||||
format_string_loop(&out, format, arg);
|
||||
|
||||
// Write the string-terminating '\0' character
|
||||
size_t null_char_pos = out.pos < out.max_chars ? out.pos : out.max_chars - 1;
|
||||
out.buffer[null_char_pos] = '\0';
|
||||
|
||||
// Return written chars without terminating \0
|
||||
return (int)out.pos;
|
||||
}
|
||||
|
||||
int
|
||||
mp_snprintf(char *s, size_t n, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const int ret = mp_vsnprintf(s, n, format, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user