2022-05-26 22:13:02 +00:00
|
|
|
/**
|
2022-06-14 14:59:08 +00:00
|
|
|
* The code was taken from Marco Paland's printf.
|
|
|
|
*
|
2022-05-26 22:13:02 +00:00
|
|
|
* Copyright (c) 2014-2019 Marco Paland <info@paland.com>
|
2022-06-25 20:28:28 +00:00
|
|
|
* Copyright (c) 2021-2022 Alex Kotov
|
2022-05-26 22:13:02 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <kernaux/assert.h>
|
|
|
|
#include <kernaux/printf_fmt.h>
|
|
|
|
|
2022-06-07 05:35:14 +00:00
|
|
|
#include "libc.h"
|
|
|
|
|
2022-05-26 22:13:02 +00:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
static unsigned int _atoi(const char** str);
|
|
|
|
|
|
|
|
struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create()
|
|
|
|
{
|
|
|
|
struct KernAux_PrintfFmt_Spec spec;
|
|
|
|
KernAux_PrintfFmt_Spec_init(&spec);
|
|
|
|
return spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KernAux_PrintfFmt_Spec_init(struct KernAux_PrintfFmt_Spec *const spec)
|
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
2022-05-26 22:13:02 +00:00
|
|
|
|
|
|
|
spec->flags = 0u;
|
|
|
|
spec->width = 0u;
|
|
|
|
spec->precision = 0u;
|
2022-05-26 22:54:01 +00:00
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_NONE;
|
|
|
|
spec->base = 0;
|
2022-05-28 09:08:48 +00:00
|
|
|
|
|
|
|
spec->set_width = false;
|
|
|
|
spec->set_precision = false;
|
2022-05-26 22:13:02 +00:00
|
|
|
}
|
|
|
|
|
2022-05-28 09:16:38 +00:00
|
|
|
const char *KernAux_PrintfFmt_Spec_parse(struct KernAux_PrintfFmt_Spec *spec, const char *format)
|
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
|
|
|
KERNAUX_ASSERT(format);
|
2022-05-28 09:16:38 +00:00
|
|
|
|
|
|
|
KernAux_PrintfFmt_Spec_parse_flags(spec, &format);
|
|
|
|
KernAux_PrintfFmt_Spec_parse_width(spec, &format);
|
|
|
|
KernAux_PrintfFmt_Spec_parse_precision(spec, &format);
|
|
|
|
KernAux_PrintfFmt_Spec_parse_length(spec, &format);
|
|
|
|
KernAux_PrintfFmt_Spec_parse_type(spec, &format);
|
|
|
|
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
2022-05-28 08:56:48 +00:00
|
|
|
void KernAux_PrintfFmt_Spec_parse_flags(struct KernAux_PrintfFmt_Spec *const spec, const char **const format)
|
2022-05-26 22:13:02 +00:00
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
|
|
|
KERNAUX_ASSERT(format);
|
|
|
|
KERNAUX_ASSERT(*format);
|
2022-05-26 22:13:02 +00:00
|
|
|
|
|
|
|
bool running = true;
|
|
|
|
do {
|
|
|
|
switch (**format) {
|
|
|
|
case '0': spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD; ++(*format); break;
|
|
|
|
case '-': spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LEFT; ++(*format); break;
|
|
|
|
case '+': spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_PLUS; ++(*format); break;
|
|
|
|
case ' ': spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_SPACE; ++(*format); break;
|
|
|
|
case '#': spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_HASH; ++(*format); break;
|
|
|
|
default: running = false; break;
|
|
|
|
}
|
|
|
|
} while (running);
|
|
|
|
}
|
|
|
|
|
2022-05-28 09:08:48 +00:00
|
|
|
void KernAux_PrintfFmt_Spec_parse_width(struct KernAux_PrintfFmt_Spec *const spec, const char **const format)
|
2022-05-26 22:13:02 +00:00
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
|
|
|
KERNAUX_ASSERT(format);
|
|
|
|
KERNAUX_ASSERT(*format);
|
2022-05-26 22:13:02 +00:00
|
|
|
|
|
|
|
if (isdigit(**format)) {
|
|
|
|
spec->width = _atoi(format);
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_width = false;
|
2022-05-26 23:50:01 +00:00
|
|
|
} else if (**format == '*') {
|
|
|
|
++(*format);
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_width = true;
|
2022-05-26 22:13:02 +00:00
|
|
|
} else {
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_width = false;
|
2022-05-26 22:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-28 09:08:48 +00:00
|
|
|
void KernAux_PrintfFmt_Spec_parse_precision(struct KernAux_PrintfFmt_Spec *const spec, const char **const format)
|
2022-05-26 22:13:02 +00:00
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
|
|
|
KERNAUX_ASSERT(format);
|
|
|
|
KERNAUX_ASSERT(*format);
|
2022-05-26 22:13:02 +00:00
|
|
|
|
|
|
|
if (**format == '.') {
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_PRECISION;
|
|
|
|
++(*format);
|
|
|
|
if (isdigit(**format)) {
|
|
|
|
spec->precision = _atoi(format);
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_precision = false;
|
2022-05-26 23:50:01 +00:00
|
|
|
} else if (**format == '*') {
|
|
|
|
++(*format);
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_precision = true;
|
2022-05-26 22:13:02 +00:00
|
|
|
} else {
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_precision = false;
|
2022-05-26 22:13:02 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-05-28 09:08:48 +00:00
|
|
|
spec->set_precision = false;
|
2022-05-26 22:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-28 08:56:48 +00:00
|
|
|
void KernAux_PrintfFmt_Spec_parse_length(struct KernAux_PrintfFmt_Spec *const spec, const char **const format)
|
2022-05-26 22:13:02 +00:00
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
|
|
|
KERNAUX_ASSERT(format);
|
|
|
|
KERNAUX_ASSERT(*format);
|
2022-05-26 22:13:02 +00:00
|
|
|
|
|
|
|
switch (**format) {
|
|
|
|
case 'l':
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG;
|
|
|
|
++(*format);
|
|
|
|
if (**format == 'l') {
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG;
|
|
|
|
++(*format);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_SHORT;
|
|
|
|
++(*format);
|
|
|
|
if (**format == 'h') {
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_CHAR;
|
|
|
|
++(*format);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
spec->flags |= (sizeof(ptrdiff_t) == sizeof(long) ? KERNAUX_PRINTF_FMT_FLAGS_LONG : KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG);
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
case 'j':
|
|
|
|
spec->flags |= (sizeof(intmax_t) == sizeof(long) ? KERNAUX_PRINTF_FMT_FLAGS_LONG : KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG);
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
case 'z':
|
|
|
|
spec->flags |= (sizeof(size_t) == sizeof(long) ? KERNAUX_PRINTF_FMT_FLAGS_LONG : KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG);
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-28 08:56:48 +00:00
|
|
|
void KernAux_PrintfFmt_Spec_parse_type(struct KernAux_PrintfFmt_Spec *const spec, const char **const format)
|
2022-05-26 22:54:01 +00:00
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
|
|
|
KERNAUX_ASSERT(format);
|
|
|
|
KERNAUX_ASSERT(*format);
|
2022-05-26 22:54:01 +00:00
|
|
|
|
|
|
|
switch (**format) {
|
|
|
|
case 'd':
|
|
|
|
case 'i':
|
|
|
|
case 'u':
|
|
|
|
case 'x':
|
|
|
|
case 'X':
|
|
|
|
case 'o':
|
|
|
|
case 'b':
|
|
|
|
// set the base
|
|
|
|
if (**format == 'x' || **format == 'X') {
|
|
|
|
spec->base = 16u;
|
|
|
|
} else if (**format == 'o') {
|
|
|
|
spec->base = 8u;
|
|
|
|
} else if (**format == 'b') {
|
|
|
|
spec->base = 2u;
|
|
|
|
} else {
|
|
|
|
spec->base = 10u;
|
|
|
|
spec->flags &= ~KERNAUX_PRINTF_FMT_FLAGS_HASH; // no hash for dec format
|
|
|
|
}
|
|
|
|
// uppercase
|
|
|
|
if (**format == 'X') {
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// no plus or space flag for u, x, X, o, b
|
|
|
|
if ((**format != 'i') && (**format != 'd')) {
|
|
|
|
spec->flags &= ~(KERNAUX_PRINTF_FMT_FLAGS_PLUS | KERNAUX_PRINTF_FMT_FLAGS_SPACE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore '0' flag when precision is given
|
|
|
|
if (spec->flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) {
|
|
|
|
spec->flags &= ~KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert the integer
|
|
|
|
if ((**format == 'i') || (**format == 'd')) {
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_INT;
|
|
|
|
} else {
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_UINT;
|
|
|
|
}
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef ENABLE_FLOAT
|
|
|
|
case 'f':
|
|
|
|
case 'F':
|
|
|
|
if (**format == 'F') spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE;
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_FLOAT;
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'e':
|
|
|
|
case 'E':
|
|
|
|
case 'g':
|
|
|
|
case 'G':
|
|
|
|
if ((**format == 'g')||(**format == 'G')) spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_ADAPT_EXP;
|
|
|
|
if ((**format == 'E')||(**format == 'G')) spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE;
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_EXP;
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
#endif // ENABLE_FLOAT
|
|
|
|
|
|
|
|
case 'c':
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_CHAR;
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_STR;
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
spec->width = sizeof(void*) * 2u;
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD | KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE;
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_PTR;
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '%':
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_PERCENT;
|
|
|
|
++(*format);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
spec->type = KERNAUX_PRINTF_FMT_TYPE_NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-28 08:56:48 +00:00
|
|
|
void KernAux_PrintfFmt_Spec_set_width(struct KernAux_PrintfFmt_Spec *const spec, const int width)
|
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
2022-05-28 08:56:48 +00:00
|
|
|
|
|
|
|
if (width < 0) {
|
|
|
|
spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LEFT; // reverse padding
|
|
|
|
spec->width = (unsigned int)-width;
|
|
|
|
} else {
|
|
|
|
spec->width = (unsigned int)width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void KernAux_PrintfFmt_Spec_set_precision(struct KernAux_PrintfFmt_Spec *const spec, const int precision)
|
|
|
|
{
|
2022-06-13 16:28:00 +00:00
|
|
|
KERNAUX_ASSERT(spec);
|
2022-05-28 08:56:48 +00:00
|
|
|
|
|
|
|
spec->precision = precision > 0 ? (unsigned int)precision : 0u;
|
|
|
|
}
|
|
|
|
|
2022-05-26 22:13:02 +00:00
|
|
|
// internal ASCII string to unsigned int conversion
|
|
|
|
unsigned int _atoi(const char** str)
|
|
|
|
{
|
|
|
|
const int result = atoi(*str);
|
|
|
|
while (isdigit(**str)) (*str)++;
|
|
|
|
return result;
|
|
|
|
}
|