From af27e796783176c0a87613493e39e708338f009c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20H=C3=BCttel?= Date: Mon, 29 May 2023 11:40:13 +0200 Subject: [PATCH] Add first structure and start work on format specifier parser --- .gitignore | 6 ++ CMakeLists.txt | 8 ++ include/simple-printf/simple-printf.h | 30 ++++++ src/simple-printf.c | 134 ++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/simple-printf/simple-printf.h create mode 100644 src/simple-printf.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bcda4cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/* +*.o +*.d +*.user +.cache/* +.vscode/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5ec34b5 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.0) +project(simple-printf) + +aux_source_directory("src" SOURCES) +add_library(${PROJECT_NAME} STATIC ${SOURCES}) +target_include_directories(${PROJECT_NAME} PUBLIC include) + + diff --git a/include/simple-printf/simple-printf.h b/include/simple-printf/simple-printf.h new file mode 100644 index 0000000..4a0714d --- /dev/null +++ b/include/simple-printf/simple-printf.h @@ -0,0 +1,30 @@ +#ifndef _SIMPLE_PRINTF_H_ +#define _SIMPLE_PRINTF_H_ + +#include + +#ifndef SIMPLE_PRINTF_ATTR_FORMAT +# if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3))) +# define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) __attribute__((__format__(__printf__, fmt, args))) +# else +# define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) +# endif +#else +# define SIMPLE_PRINTF_ATTR_FORMAT(fmt, args) +#endif + +/** + * @brief Simple implementation of snprintf + * + * Supports the following format specifiers: + * %d, %i, %s, %u, %z, %c, %x, %X + * @param[out] dest Buffer to write result to + * @param dest_size Size of @p dest in bytes + * @param[in] fmt Format string + * @return Number of written bytes. Not including the null terminator. + * @return negative in case of an error + * @note The resulting output in @p dest will always be null terminated. + */ +int simple_snprintf(char *dest, size_t dest_size, const char *fmt, ...) SIMPLE_PRINTF_ATTR_FORMAT(3, 4); + +#endif /* _SIMPLE_PRINTF_H_ */ \ No newline at end of file diff --git a/src/simple-printf.c b/src/simple-printf.c new file mode 100644 index 0000000..98e2990 --- /dev/null +++ b/src/simple-printf.c @@ -0,0 +1,134 @@ +#include +#include +#include + +enum format_specifier_type { + FMT_INVALID = 0, + FMT_INTEGER, + FMT_UNSIGNED, + FMT_STRING, + FMT_HEXUINT_CAPITALIZED, + FMT_HEXUNIT, + FMT_CHAR, + FMT_SIZE, + FMT_PERCENT, +}; + +struct format_specifier { + enum format_specifier_type specifier; + size_t length; /**< @brief lenght of formatted output.*/ + bool zero_pad; + size_t length_of_provided_specifier; +}; + +static void parse_format_specifier(const char *start, struct format_specifier *result) +{ + size_t fmt_len = 1ull; + bool keep_parsing = true; + bool is_in_number = false; + const char *number; + size_t num_len = 0; + + result->specifier = FMT_INVALID; + result->zero_pad = 0; + result->length_of_provided_specifier = 0; + result->length = 0; + if (!start) + return; + + /* Check if specifier starts with % */ + if (*start != '%') + return; + + start++; + while (keep_parsing) { + /* Parse the format specifier*/ + switch (*start) { + case '0': + if (fmt_len == 1) { + result->zero_pad = 1; + break; + } + /* Fallthru */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* Number */ + if (!is_in_number) { + /* Start of number */ + is_in_number = true; + number = start; + num_len = 0; + } else { + /* Continue parsing number */ + num_len++; + } + break; + case 'c': + result->specifier = FMT_CHAR; + keep_parsing = false; + break; + case 'z': + result->specifier = FMT_SIZE; + keep_parsing = false; + break; + case 'x': + result->specifier = FMT_HEXUNIT; + keep_parsing = false; + break; + case 'X': + result->specifier = FMT_HEXUINT_CAPITALIZED; + keep_parsing = false; + break; + case 's': + result->specifier = FMT_STRING; + keep_parsing = false; + break; + case 'u': + result->specifier = FMT_UNSIGNED; + keep_parsing = false; + break; + case 'i': /* Falltrhu */ + case 'd': + result->specifier = FMT_INTEGER; + keep_parsing = false; + break; + case '%': + if (fmt_len == 1) { + result->specifier = FMT_PERCENT; + } + keep_parsing = false; + default: + /* Unrecognized stuff. Abort */ + keep_parsing = false; + break; + } + + if (keep_parsing == false) { + /* End reached. Check the number */ + + } + + start++; + fmt_len++; + } +} + +int simple_snprintf(char *dest, size_t dest_size, const char *fmt, ...) +{ + int ret = -1; + + if (!dest || !dest_size || !fmt) { + /* Parameter error */ + return -1000; + } + + return ret; +} +