You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
6.7 KiB
356 lines
6.7 KiB
/* |
|
* Copyright (c) 2010, 2013-2014 Wind River Systems, Inc. |
|
* Copyright (c) 2020 Nordic Semiconductor ASA |
|
* Copyright (c) 2021 BayLibre, SAS |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <stdarg.h> |
|
#include <stdint.h> |
|
#include <string.h> |
|
#include <zephyr/sys/cbprintf.h> |
|
#include <sys/types.h> |
|
#include <zephyr/sys/util.h> |
|
|
|
#ifdef CONFIG_CBPRINTF_FULL_INTEGRAL |
|
typedef intmax_t int_value_type; |
|
typedef uintmax_t uint_value_type; |
|
#define DIGITS_BUFLEN 21 |
|
BUILD_ASSERT(sizeof(uint_value_type) <= 8U, |
|
"DIGITS_BUFLEN may be incorrect"); |
|
#else |
|
typedef int32_t int_value_type; |
|
typedef uint32_t uint_value_type; |
|
#define DIGITS_BUFLEN 10 |
|
#endif |
|
|
|
#define ALPHA(fmt) (((fmt) & 0x60) - '0' - 10 + 1) |
|
|
|
/* Convert value to string, storing characters downwards */ |
|
static inline int convert_value(uint_value_type num, unsigned int base, |
|
unsigned int alpha, char *buftop) |
|
{ |
|
int i = 0; |
|
|
|
do { |
|
unsigned int c = num % base; |
|
if (c >= 10) { |
|
c += alpha; |
|
} |
|
buftop[i--] = c + '0'; |
|
num /= base; |
|
} while (num); |
|
|
|
return -i; |
|
} |
|
|
|
#define OUTC(_c) do { \ |
|
out((int)(_c), ctx); \ |
|
if (IS_ENABLED(CONFIG_CBPRINTF_LIBC_SUBSTS)) { \ |
|
++count; \ |
|
} \ |
|
} while (false) |
|
|
|
#define PAD_ZERO BIT(0) |
|
#define PAD_TAIL BIT(1) |
|
|
|
/* Skip over the argument tag if needed as it is not being used here. */ |
|
#define SKIP_TAG_IF_NEEDED(ap, tagged_ap) \ |
|
do { \ |
|
if (IS_ENABLED(CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS) \ |
|
&& tagged_ap) { \ |
|
(void)va_arg(ap, int); \ |
|
} \ |
|
} while (0) |
|
|
|
/** |
|
* @brief Printk internals |
|
* |
|
* See printk() for description. |
|
* @param fmt Format string |
|
* @param ap Variable parameters |
|
* |
|
* @return printed byte count if CONFIG_CBPRINTF_LIBC_SUBSTS is set |
|
*/ |
|
int z_cbvprintf_impl(cbprintf_cb __out, void *ctx, const char *fmt, |
|
va_list ap, uint32_t flags) |
|
{ |
|
size_t count = 0; |
|
char buf[DIGITS_BUFLEN]; |
|
char *prefix, *data; |
|
int min_width, precision, data_len; |
|
char padding_mode, length_mod, special; |
|
cbprintf_cb_local out = __out; |
|
|
|
const bool tagged_ap = (flags & Z_CBVPRINTF_PROCESS_FLAG_TAGGED_ARGS) |
|
== Z_CBVPRINTF_PROCESS_FLAG_TAGGED_ARGS; |
|
|
|
/* we pre-increment in the loop afterwards */ |
|
fmt--; |
|
|
|
start: |
|
while (*++fmt != '%') { |
|
if (*fmt == '\0') { |
|
return count; |
|
} |
|
OUTC(*fmt); |
|
} |
|
|
|
min_width = -1; |
|
precision = -1; |
|
prefix = ""; |
|
padding_mode = 0; |
|
length_mod = 0; |
|
special = 0; |
|
|
|
for (fmt++ ; ; fmt++) { |
|
switch (*fmt) { |
|
case 0: |
|
return count; |
|
|
|
case '%': |
|
OUTC('%'); |
|
goto start; |
|
|
|
case '-': |
|
padding_mode = PAD_TAIL; |
|
continue; |
|
|
|
case '.': |
|
precision = 0; |
|
padding_mode &= (char)~PAD_ZERO; |
|
continue; |
|
|
|
case '0': |
|
if (min_width < 0 && precision < 0 && !padding_mode) { |
|
padding_mode = PAD_ZERO; |
|
continue; |
|
} |
|
__fallthrough; |
|
|
|
case '1': |
|
case '2': |
|
case '3': |
|
case '4': |
|
case '5': |
|
case '6': |
|
case '7': |
|
case '8': |
|
case '9': |
|
if (precision >= 0) { |
|
precision = 10 * precision + *fmt - '0'; |
|
} else { |
|
if (min_width < 0) { |
|
min_width = 0; |
|
} |
|
min_width = 10 * min_width + *fmt - '0'; |
|
} |
|
continue; |
|
|
|
case '*': |
|
SKIP_TAG_IF_NEEDED(ap, tagged_ap); |
|
|
|
if (precision >= 0) { |
|
precision = va_arg(ap, int); |
|
} else { |
|
min_width = va_arg(ap, int); |
|
if (min_width < 0) { |
|
min_width = -min_width; |
|
padding_mode = PAD_TAIL; |
|
} |
|
} |
|
continue; |
|
|
|
case '+': |
|
case ' ': |
|
case '#': |
|
special = *fmt; |
|
continue; |
|
|
|
case 'h': |
|
case 'l': |
|
case 'z': |
|
if (*fmt == 'h' && length_mod == 'h') { |
|
length_mod = 'H'; |
|
} else if (*fmt == 'l' && length_mod == 'l') { |
|
length_mod = 'L'; |
|
} else if (length_mod == '\0') { |
|
length_mod = *fmt; |
|
} else { |
|
OUTC('%'); |
|
OUTC(*fmt); |
|
goto start; |
|
} |
|
continue; |
|
|
|
case 'd': |
|
case 'i': |
|
case 'u': { |
|
uint_value_type d; |
|
|
|
SKIP_TAG_IF_NEEDED(ap, tagged_ap); |
|
|
|
if (length_mod == 'z') { |
|
if (*fmt == 'u') { |
|
d = va_arg(ap, size_t); |
|
} else { |
|
d = va_arg(ap, ssize_t); |
|
} |
|
} else if (length_mod == 'l') { |
|
if (*fmt == 'u') { |
|
d = va_arg(ap, unsigned long); |
|
} else { |
|
d = va_arg(ap, long); |
|
} |
|
} else if (length_mod == 'L') { |
|
if (*fmt == 'u') { |
|
unsigned long long llu = |
|
va_arg(ap, unsigned long long); |
|
|
|
if (llu != (uint_value_type) llu) { |
|
data = "ERR"; |
|
data_len = 3; |
|
precision = 0; |
|
break; |
|
} |
|
d = (uint_value_type) llu; |
|
} else { |
|
long long lld = va_arg(ap, long long); |
|
|
|
if (lld != (int_value_type) lld) { |
|
data = "ERR"; |
|
data_len = 3; |
|
precision = 0; |
|
break; |
|
} |
|
d = (int_value_type) lld; |
|
} |
|
} else if (*fmt == 'u') { |
|
d = va_arg(ap, unsigned int); |
|
} else { |
|
d = va_arg(ap, int); |
|
} |
|
|
|
if (*fmt != 'u' && (int_value_type)d < 0) { |
|
d = -d; |
|
prefix = "-"; |
|
min_width--; |
|
} else if (special == ' ') { |
|
prefix = " "; |
|
min_width--; |
|
} else if (special == '+') { |
|
prefix = "+"; |
|
min_width--; |
|
} else { |
|
; |
|
} |
|
data_len = convert_value(d, 10, 0, buf + sizeof(buf) - 1); |
|
data = buf + sizeof(buf) - data_len; |
|
break; |
|
} |
|
|
|
case 'p': |
|
case 'x': |
|
case 'X': { |
|
uint_value_type x; |
|
|
|
SKIP_TAG_IF_NEEDED(ap, tagged_ap); |
|
|
|
if (*fmt == 'p') { |
|
x = (uintptr_t)va_arg(ap, void *); |
|
if (x == (uint_value_type)0) { |
|
data = "(nil)"; |
|
data_len = 5; |
|
precision = 0; |
|
break; |
|
} |
|
special = '#'; |
|
} else if (length_mod == 'l') { |
|
x = va_arg(ap, unsigned long); |
|
} else if (length_mod == 'L') { |
|
unsigned long long llx = |
|
va_arg(ap, unsigned long long); |
|
|
|
if (llx != (uint_value_type) llx) { |
|
data = "ERR"; |
|
data_len = 3; |
|
precision = 0; |
|
break; |
|
} |
|
x = (uint_value_type) llx; |
|
} else { |
|
x = va_arg(ap, unsigned int); |
|
} |
|
if (special == '#') { |
|
prefix = (*fmt & 0x20) ? "0x" : "0X"; |
|
min_width -= 2; |
|
} |
|
data_len = convert_value(x, 16, ALPHA(*fmt), |
|
buf + sizeof(buf) - 1); |
|
data = buf + sizeof(buf) - data_len; |
|
break; |
|
} |
|
|
|
case 's': { |
|
SKIP_TAG_IF_NEEDED(ap, tagged_ap); |
|
|
|
data = va_arg(ap, char *); |
|
data_len = strlen(data); |
|
if (precision >= 0 && data_len > precision) { |
|
data_len = precision; |
|
} |
|
precision = 0; |
|
break; |
|
} |
|
|
|
case 'c': { |
|
int c; |
|
|
|
SKIP_TAG_IF_NEEDED(ap, tagged_ap); |
|
|
|
c = va_arg(ap, int); |
|
|
|
buf[0] = c; |
|
data = buf; |
|
data_len = 1; |
|
precision = 0; |
|
break; |
|
} |
|
|
|
default: |
|
OUTC('%'); |
|
OUTC(*fmt); |
|
goto start; |
|
} |
|
|
|
if (precision < 0 && (padding_mode & PAD_ZERO)) { |
|
precision = min_width; |
|
} |
|
min_width -= data_len; |
|
precision -= data_len; |
|
if (precision > 0) { |
|
min_width -= precision; |
|
} |
|
|
|
if (!(padding_mode & PAD_TAIL)) { |
|
while (--min_width >= 0) { |
|
OUTC(' '); |
|
} |
|
} |
|
while (*prefix) { |
|
OUTC(*prefix++); |
|
} |
|
while (--precision >= 0) { |
|
OUTC('0'); |
|
} |
|
while (--data_len >= 0) { |
|
OUTC(*data++); |
|
} |
|
while (--min_width >= 0) { |
|
OUTC(' '); |
|
} |
|
|
|
goto start; |
|
} |
|
}
|
|
|