Add first structure and start work on format specifier parser
This commit is contained in:
commit
af27e79678
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
build/*
|
||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
*.user
|
||||||
|
.cache/*
|
||||||
|
.vscode/*
|
8
CMakeLists.txt
Normal file
8
CMakeLists.txt
Normal 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)
|
||||||
|
|
||||||
|
|
30
include/simple-printf/simple-printf.h
Normal file
30
include/simple-printf/simple-printf.h
Normal 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
134
src/simple-printf.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user