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