Add first structure and start work on format specifier parser

This commit is contained in:
Mario Hüttel 2023-05-29 11:40:13 +02:00
commit af27e79678
4 changed files with 178 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
build/*
*.o
*.d
*.user
.cache/*
.vscode/*

8
CMakeLists.txt Normal file
View File

@ -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)

View File

@ -0,0 +1,30 @@
#ifndef _SIMPLE_PRINTF_H_
#define _SIMPLE_PRINTF_H_
#include <stddef.h>
#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_ */

134
src/simple-printf.c Normal file
View File

@ -0,0 +1,134 @@
#include <simple-printf/simple-printf.h>
#include <stddef.h>
#include <stdbool.h>
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;
}