mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 02:40:08 +00:00
libports: freestanding 'format' string library
This little library implements rudimentary format-string support. It is useful for porting 3rd-party code that ought not depend on a full libc. Issue #2064
This commit is contained in:
parent
50ee8dfaf8
commit
9ef0f1b6cb
60
repos/libports/include/format/console.h
Normal file
60
repos/libports/include/format/console.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* \brief Simple console for debug output
|
||||
* \author Norman Feske
|
||||
* \date 2006-04-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__FORMAT__CONSOLE_H_
|
||||
#define _INCLUDE__FORMAT__CONSOLE_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
namespace Format { class Console; }
|
||||
|
||||
|
||||
class Format::Console
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~Console() {}
|
||||
|
||||
/**
|
||||
* Print format string
|
||||
*/
|
||||
void printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
|
||||
void vprintf(const char *format, va_list) __attribute__((format(printf, 2, 0)));
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Back end used for the output of a single character
|
||||
*/
|
||||
virtual void _out_char(char c) = 0;
|
||||
|
||||
/**
|
||||
* Back end for the output of a null-terminated string
|
||||
*
|
||||
* The default implementation uses _out_char. This method may
|
||||
* be overridden by the backend for improving efficiency.
|
||||
*
|
||||
* This method is virtual to enable the use an optimized
|
||||
* string-output operation on some target platforms, e.g.,
|
||||
* a kernel debugger that offers a string-output syscall. The
|
||||
* default implementation calls '_out_char' for each character.
|
||||
*/
|
||||
virtual void _out_string(const char *str);
|
||||
|
||||
private:
|
||||
|
||||
template <typename T> void _out_unsigned(T value, unsigned base = 10, int pad = 0);
|
||||
template <typename T> void _out_signed(T value, unsigned base = 10);
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__FORMAT__CONSOLE_H_ */
|
99
repos/libports/include/format/snprintf.h
Normal file
99
repos/libports/include/format/snprintf.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* \brief Facility to write format string into character buffer
|
||||
* \author Norman Feske
|
||||
* \date 2006-07-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__FORMAT__SNPRINTF_H_
|
||||
#define _INCLUDE__FORMAT__SNPRINTF_H_
|
||||
|
||||
#include <format/console.h>
|
||||
|
||||
namespace Format {
|
||||
|
||||
class String_console;
|
||||
|
||||
using size_t = __SIZE_TYPE__;
|
||||
|
||||
/**
|
||||
* Print format string into character buffer
|
||||
*
|
||||
* \param dst destination buffer
|
||||
* \param dst_len size of 'dst' in bytes
|
||||
* \param format format string followed by the list of arguments
|
||||
*
|
||||
* \return number of characters written to destination buffer
|
||||
*/
|
||||
inline size_t snprintf(char *dst, size_t dst_size, const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
}
|
||||
|
||||
|
||||
class Format::String_console : public Console
|
||||
{
|
||||
private:
|
||||
|
||||
char *_dst;
|
||||
size_t _dst_len;
|
||||
size_t _w_offset { 0 };
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
String_console &operator = (String_console const &);
|
||||
String_console(String_console const &);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param dst destination character buffer
|
||||
* \param dst_len size of 'dst'
|
||||
*/
|
||||
String_console(char *dst, size_t dst_len)
|
||||
: _dst(dst), _dst_len(dst_len)
|
||||
{ _dst[0] = 0; }
|
||||
|
||||
/**
|
||||
* Return number of characters in destination buffer
|
||||
*/
|
||||
size_t len() { return _w_offset; }
|
||||
|
||||
|
||||
/***********************
|
||||
** Console interface **
|
||||
***********************/
|
||||
|
||||
void _out_char(char c) override
|
||||
{
|
||||
/* ensure to leave space for null-termination */
|
||||
if (_w_offset + 2 > _dst_len)
|
||||
return;
|
||||
|
||||
_dst[_w_offset++] = c;
|
||||
_dst[_w_offset] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline Format::size_t Format::snprintf(char *dst, size_t dst_len, const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
|
||||
String_console sc(dst, dst_len);
|
||||
sc.vprintf(format, list);
|
||||
|
||||
va_end(list);
|
||||
return sc.len();
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__FORMAT__SNPRINTF_H_ */
|
6
repos/libports/lib/mk/format.mk
Normal file
6
repos/libports/lib/mk/format.mk
Normal file
@ -0,0 +1,6 @@
|
||||
SRC_CC += console.cc
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/lib/format
|
||||
|
||||
# for reusing output.h
|
||||
REP_INC_DIR += src/include/base/internal
|
17
repos/libports/recipes/api/format/content.mk
Normal file
17
repos/libports/recipes/api/format/content.mk
Normal file
@ -0,0 +1,17 @@
|
||||
MIRROR_FROM_REP_DIR := include/format src/lib/format lib/mk/format.mk
|
||||
|
||||
content: $(MIRROR_FROM_REP_DIR)
|
||||
|
||||
$(MIRROR_FROM_REP_DIR):
|
||||
$(mirror_from_rep_dir)
|
||||
|
||||
MIRROR_FROM_BASE_DIR := src/include/base/internal/output.h
|
||||
|
||||
content: $(MIRROR_FROM_BASE_DIR)
|
||||
|
||||
$(MIRROR_FROM_BASE_DIR):
|
||||
mkdir -p $(dir $@)
|
||||
cp -r $(addprefix $(GENODE_DIR)/repos/base/,$@) $(dir $@)
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
1
repos/libports/recipes/api/format/hash
Normal file
1
repos/libports/recipes/api/format/hash
Normal file
@ -0,0 +1 @@
|
||||
2023-03-06 bcd738392c9ae1d3f10b17e0b412051d07e650fb
|
256
repos/libports/src/lib/format/console.cc
Normal file
256
repos/libports/src/lib/format/console.cc
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* \brief Output of format strings
|
||||
* \author Norman Feske
|
||||
* \date 2006-04-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* format library interface */
|
||||
#include <format/console.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/stdint.h>
|
||||
#include <output.h> /* base/internal/output.h */
|
||||
|
||||
using namespace Format;
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
/**
|
||||
* Format string command representation
|
||||
*/
|
||||
class Format_command
|
||||
{
|
||||
public:
|
||||
|
||||
enum Type { INT, UINT, STRING, CHAR, PTR, PERCENT, INVALID };
|
||||
enum Length { DEFAULT, LONG, SIZE_T, LONG_LONG };
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Read decimal value from string
|
||||
*/
|
||||
int decode_decimal(const char *str, int *consumed)
|
||||
{
|
||||
int res = 0;
|
||||
while (1) {
|
||||
char c = str[*consumed];
|
||||
|
||||
if (!c || c < '0' || c > '0' + 9)
|
||||
return res;
|
||||
|
||||
res = (res * 10) + c - '0';
|
||||
(*consumed)++;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Type type = INVALID; /* format argument type */
|
||||
Length length = DEFAULT; /* format argument length */
|
||||
int padding = 0; /* min number of characters to print */
|
||||
int base = 10; /* base of numeric arguments */
|
||||
bool zeropad = false; /* pad with zero instead of space */
|
||||
bool uppercase = false; /* use upper case for hex numbers */
|
||||
int consumed = 0; /* nb of consumed format string chars */
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param format begin of command in format string
|
||||
*/
|
||||
explicit Format_command(const char *format)
|
||||
{
|
||||
/* check for command begin and eat the character */
|
||||
if (format[consumed] != '%') return;
|
||||
if (!format[++consumed]) return;
|
||||
|
||||
/* heading zero indicates zero-padding */
|
||||
zeropad = (format[consumed] == '0');
|
||||
|
||||
/* read decimal padding value */
|
||||
padding = decode_decimal(format, &consumed);
|
||||
if (!format[consumed]) return;
|
||||
|
||||
/* decode length */
|
||||
switch (format[consumed]) {
|
||||
|
||||
case 'l':
|
||||
{
|
||||
/* long long ints are marked by a subsequenting 'l' character */
|
||||
bool is_long_long = (format[consumed + 1] == 'l');
|
||||
|
||||
length = is_long_long ? LONG_LONG : LONG;
|
||||
consumed += is_long_long ? 2 : 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'z':
|
||||
|
||||
length = SIZE_T;
|
||||
consumed++;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
|
||||
length = LONG;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (!format[consumed]) return;
|
||||
|
||||
/* decode type */
|
||||
switch (format[consumed]) {
|
||||
|
||||
case 'd':
|
||||
case 'i': type = INT; base = 10; break;
|
||||
case 'o': type = UINT; base = 8; break;
|
||||
case 'u': type = UINT; base = 10; break;
|
||||
case 'x': type = UINT; base = 16; break;
|
||||
case 'X': type = UINT; base = 16; uppercase = 1; break;
|
||||
case 'p': type = PTR; base = 16; break;
|
||||
case 'c': type = CHAR; break;
|
||||
case 's': type = STRING; break;
|
||||
case '%': type = PERCENT; break;
|
||||
|
||||
case 0: return;
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* eat type character */
|
||||
consumed++;
|
||||
}
|
||||
|
||||
int numeric()
|
||||
{
|
||||
return (type == INT || type == UINT || type == PTR);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Console::_out_string(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
_out_string("<NULL>");
|
||||
else
|
||||
while (*str) _out_char(*str++);
|
||||
}
|
||||
|
||||
|
||||
void Console::printf(const char *format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
vprintf(format, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
|
||||
void Console::vprintf(const char *format, va_list list)
|
||||
{
|
||||
|
||||
while (*format) {
|
||||
|
||||
/* eat and output plain characters */
|
||||
if (*format != '%') {
|
||||
_out_char(*format++);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* parse format argument descriptor */
|
||||
Format_command cmd(format);
|
||||
|
||||
/* read numeric argument from va_list */
|
||||
long long numeric_arg = 0;
|
||||
if (cmd.numeric()) {
|
||||
switch (cmd.length) {
|
||||
|
||||
case Format_command::LONG_LONG:
|
||||
|
||||
numeric_arg = va_arg(list, long long);
|
||||
break;
|
||||
|
||||
case Format_command::LONG:
|
||||
|
||||
numeric_arg = (cmd.type == Format_command::UINT) ?
|
||||
(long long)va_arg(list, unsigned long) : va_arg(list, long);
|
||||
break;
|
||||
|
||||
case Format_command::SIZE_T:
|
||||
|
||||
numeric_arg = va_arg(list, size_t);
|
||||
break;
|
||||
|
||||
case Format_command::DEFAULT:
|
||||
|
||||
numeric_arg = (cmd.type == Format_command::UINT) ?
|
||||
(long long)va_arg(list, unsigned int) : va_arg(list, int);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* call type-specific output routines */
|
||||
switch (cmd.type) {
|
||||
|
||||
case Format_command::INT:
|
||||
|
||||
if (cmd.length == Format_command::LONG_LONG)
|
||||
out_signed<long long>(numeric_arg, cmd.base,
|
||||
[&] (char c) { _out_char(c); });
|
||||
else
|
||||
out_signed<long>((long)numeric_arg, cmd.base,
|
||||
[&] (char c) { _out_char(c); });
|
||||
break;
|
||||
|
||||
case Format_command::UINT:
|
||||
|
||||
if (cmd.length == Format_command::LONG_LONG) {
|
||||
out_unsigned<unsigned long long>(numeric_arg, cmd.base, cmd.padding,
|
||||
[&] (char c) { _out_char(c); });
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
case Format_command::PTR:
|
||||
|
||||
out_unsigned<unsigned long>((long)numeric_arg, cmd.base, cmd.padding,
|
||||
[&] (char c) { _out_char(c); });
|
||||
break;
|
||||
|
||||
case Format_command::CHAR:
|
||||
|
||||
_out_char((char)va_arg(list, int));
|
||||
break;
|
||||
|
||||
case Format_command::STRING:
|
||||
|
||||
_out_string(va_arg(list, const char *));
|
||||
break;
|
||||
|
||||
case Format_command::PERCENT:
|
||||
|
||||
_out_char('%');
|
||||
break;
|
||||
|
||||
case Format_command::INVALID:
|
||||
|
||||
_out_string("<warning: unsupported format string argument>");
|
||||
/* consume the argument of the unsupported command */
|
||||
va_arg(list, long);
|
||||
break;
|
||||
}
|
||||
|
||||
/* proceed with format string after command */
|
||||
format += cmd.consumed;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user