Change tiny printf implementation

This commit is contained in:
topjohnwu 2024-10-02 16:34:23 -07:00
parent c032a12c97
commit 795cab5ab6
7 changed files with 1505 additions and 1057 deletions

View File

@ -22,7 +22,7 @@ LOCAL_SRC_FILES := \
misc.c \
stdio.c \
syscalls.c \
tinystdio/tinystdio.c \
printf/printf.c \
bionic/dirent.cpp \
bionic/strerror.cpp \
bionic/syscall-$(TARGET_ARCH).S \

24
fmt.c
View File

@ -1,28 +1,16 @@
#include <stdlib.h>
#include "stdio_impl.h"
#include "tinystdio/tinystdio.h"
#include "printf/printf.h"
// tfp_vfprintf implementation
// tiny_vfprintf implementation
struct file_putp {
FILE *fp;
int len;
};
static void file_putc(void *data, char ch) {
struct file_putp *putp = data;
if (fputc(ch, putp->fp) >= 0) {
++putp->len;
}
static void fct_putchar(char ch, void *p) {
fputc(ch, (FILE *) p);
}
int tfp_vfprintf(FILE *stream, const char *format, va_list arg) {
struct file_putp data;
data.fp = stream;
data.len = 0;
tfp_format(&data, &file_putc, format, arg);
return data.len;
int tiny_vfprintf(FILE *stream, const char *format, va_list arg) {
return vfctprintf(fct_putchar, stream, format, arg);
}
// {s,f}printf family wrappers

1394
printf/printf.c Normal file

File diff suppressed because it is too large Load Diff

99
printf/printf.h Normal file
View File

@ -0,0 +1,99 @@
/**
* @author (c) Eyal Rozenberg <eyalroz1@gmx.com>
* 2021-2023, Haifa, Palestine/Israel
* @author (c) Marco Paland (info@paland.com)
* 2014-2019, PALANDesign Hannover, Germany
*
* @note Others have made smaller contributions to this file: see the
* contributors page at https://github.com/eyalroz/printf/graphs/contributors
* or ask one of the authors.
*
* @brief Small stand-alone implementation of the printf family of functions
* (`(v)printf`, `(v)s(n)printf` etc., geared towards use on embedded systems
* with a very limited resources.
*
* @note the implementations are thread-safe; re-entrant; use no functions from
* the standard library; and do not dynamically allocate any memory.
*
* @license The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef PRINTF_H_
#define PRINTF_H_
#ifdef __cplusplus
# include <cstdarg>
# include <cstddef>
extern "C" {
#else
# include <stdarg.h>
# include <stddef.h>
#endif
#ifdef __GNUC__
# if ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4)
# define ATTR_PRINTF(one_based_format_index, first_arg) \
__attribute__((format(gnu_printf, (one_based_format_index), (first_arg))))
# else
# define ATTR_PRINTF(one_based_format_index, first_arg) \
__attribute__((format(printf, (one_based_format_index), (first_arg))))
# endif
# define ATTR_VPRINTF(one_based_format_index) \
ATTR_PRINTF((one_based_format_index), 0)
#else
# define ATTR_PRINTF(one_based_format_index, first_arg)
# define ATTR_VPRINTF(one_based_format_index)
#endif
// If you want to include this implementation file directly rather than
// link against it, this will let you control the functions' visibility,
// e.g. make them static so as not to clash with other objects also
// using them.
#ifndef PRINTF_VISIBILITY
#define PRINTF_VISIBILITY
#endif
/**
* printf/vprintf with user-specified output function
*
* An alternative to @ref printf_, in which the output function is specified
* dynamically (rather than @ref putchar_ being used)
*
* @param out An output function which takes one character and a type-erased
* additional parameters
* @param extra_arg The type-erased argument to pass to the output function @p
* out with each call
* @param format A string specifying the format of the output, with %-marked
* specifiers of how to interpret additional arguments.
* @param arg Additional arguments to the function, one for each specifier in
* @p format
* @return The number of characters for which the output f unction was invoked,
* not counting the terminating null character
*
*/
PRINTF_VISIBILITY
int vfctprintf(void (*out)(char c, void* extra_arg), void* extra_arg, const char* format, va_list arg) ATTR_VPRINTF(3);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // PRINTF_H_

5
printf/printf_config.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 0
#define PRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS 0
#define PRINTF_CHECK_FOR_NUL_IN_FORMAT_SPECIFIER 0

View File

@ -1,834 +0,0 @@
/*
File: tinyprintf.c
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
#include "tinystdio.h"
/*
* Configuration
*/
/* Enable long int support */
#define PRINTF_LONG_SUPPORT
/* Enable long long int support (implies long int support) */
#define PRINTF_LONG_LONG_SUPPORT
/* Enable %z (size_t) support */
#define PRINTF_SIZE_T_SUPPORT
/*
* Configuration adjustments
*/
#ifdef PRINTF_SIZE_T_SUPPORT
#include <sys/types.h>
#endif
#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
/* __SIZEOF_<type>__ defined at least by gcc */
#ifdef __SIZEOF_POINTER__
# define SIZEOF_POINTER __SIZEOF_POINTER__
#endif
#ifdef __SIZEOF_LONG_LONG__
# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__
#endif
#ifdef __SIZEOF_LONG__
# define SIZEOF_LONG __SIZEOF_LONG__
#endif
#ifdef __SIZEOF_INT__
# define SIZEOF_INT __SIZEOF_INT__
#endif
#ifdef __GNUC__
# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline))
#else
# define _TFP_GCC_NO_INLINE_
#endif
/*
* Implementation
*/
struct param {
bool lz; /**< Leading zeros */
bool alt; /**< alternate form */
bool uc; /**< Upper case (for base16 only) */
bool align_left; /**< 0 == align right (default), 1 == align left */
int width; /**< field width */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
char prec; /**< Floating point precision */
};
#ifdef PRINTF_LONG_LONG_SUPPORT
static void _TFP_GCC_NO_INLINE_ ulli2a(
unsigned long long int num, struct param *p)
{
int n = 0;
unsigned long long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void lli2a(long long int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ulli2a(num, p);
}
#endif
#ifdef PRINTF_LONG_SUPPORT
static void uli2a(unsigned long int num, struct param *p)
{
int n = 0;
unsigned long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void li2a(long num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
uli2a(num, p);
}
#endif
static void ui2a(unsigned int num, struct param *p)
{
int n = 0;
unsigned int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void i2a(int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ui2a(num, p);
}
static int a2d(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else
return -1;
}
static char a2u(char ch, const char **src, int base, int *nump)
{
const char *p = *src;
int num = 0;
int digit;
while ((digit = a2d(ch)) >= 0) {
if (digit > base)
break;
num = num * base + digit;
ch = *p++;
}
*src = p;
*nump = num;
return ch;
}
static void putchw(void *putp, putcf putf, struct param *p)
{
char ch;
int n = p->width;
char *bf = p->bf;
/* Number of filling characters */
while (*bf++ && n > 0)
n--;
if (p->sign)
n--;
if (p->alt && p->base == 16)
n -= 2;
else if (p->alt && p->base == 8)
n--;
/* Fill with space to align to the right, before alternate or sign */
if (!p->lz && !p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
/* print sign */
if (p->sign)
putf(putp, p->sign);
/* Alternate */
if (p->alt && p->base == 16) {
putf(putp, '0');
putf(putp, (p->uc ? 'X' : 'x'));
} else if (p->alt && p->base == 8) {
putf(putp, '0');
}
/* Fill with zeros, after alternate or sign */
if (p->lz) {
while (n-- > 0)
putf(putp, '0');
}
/* Put actual buffer */
bf = p->bf;
while ((ch = *bf++))
putf(putp, ch);
/* Fill with space to align to the left, after string */
if (!p->lz && p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
}
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
{
struct param p;
double fval;
int temp_buffer[16];
int fpart;
int fiter;
int ffactor;
int sign;
#ifdef PRINTF_LONG_SUPPORT
char bf[23]; /* long = 64b on some architectures */
#else
char bf[12]; /* int = 32b on some architectures */
#endif
char ch;
p.bf = bf;
while ((ch = *(fmt++))) {
if (ch != '%') {
putf(putp, ch);
} else {
#ifdef PRINTF_LONG_SUPPORT
char lng = 0; /* 1 for long, 2 for long long */
#endif
/* Init parameter struct */
p.lz = 0;
p.alt = 0;
p.width = 0;
p.align_left = 0;
p.sign = 0;
p.prec = TINY_PRINTF_FP_PRECISION;
/* Flags */
while ((ch = *(fmt++))) {
switch (ch) {
case '-':
p.align_left = 1;
continue;
case '0':
p.lz = 1;
continue;
case '#':
p.alt = 1;
continue;
case '+':
p.sign = 1;
continue;
default:
break;
}
break;
}
/* Width */
if (ch >= '0' && ch <= '9') {
ch = a2u(ch, &fmt, 10, &(p.width));
}
/* We accept 'x.y' format but don't support it completely:
* we ignore the 'y' digit => this ignores 0-fill
* size and makes it == width (ie. 'x') */
if (ch == '.') {
//p.lz = 1; /* zero-padding */
/* ignore actual 0-fill size: */
ch = *(fmt++);
if (ch >= '0' && ch <= '9')
p.prec = ch - '0';
do
{
ch = *(fmt++);
} while (ch >= '0' && ch <= '9');
}
#ifdef PRINTF_SIZE_T_SUPPORT
# ifdef PRINTF_LONG_SUPPORT
if (ch == 'z') {
ch = *(fmt++);
if (sizeof(size_t) == sizeof(unsigned long int))
lng = 1;
# ifdef PRINTF_LONG_LONG_SUPPORT
else if (sizeof(size_t) == sizeof(unsigned long long int))
lng = 2;
# endif
} else
# endif
#endif
#ifdef PRINTF_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 1;
#ifdef PRINTF_LONG_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 2;
}
#endif
}
#endif
switch (ch) {
case 0:
goto abort;
case 'u':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'd':
case 'i':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
lli2a(va_arg(va, long long int), &p);
else
#endif
if (1 == lng)
li2a(va_arg(va, long int), &p);
else
#endif
i2a(va_arg(va, int), &p);
putchw(putp, putf, &p);
break;
#ifdef SIZEOF_POINTER
case 'p':
p.alt = 1;
# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT
lng = 0;
# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG
lng = 1;
# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG
lng = 2;
# endif
#endif
case 'x':
case 'X':
p.base = 16;
p.uc = (ch == 'X')?1:0;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'o':
p.base = 8;
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'c':
putf(putp, (char)(va_arg(va, int)));
break;
case 's':
p.bf = va_arg(va, char *);
putchw(putp, putf, &p);
p.bf = bf;
break;
case '%':
putf(putp, ch);
break;
case 'f':
case 'F':
fval = va_arg(va, double);
sign = 0;
if (fval < 0)
{
sign = 1;
p.width--;
fval = - fval;
}
else if (p.sign) {
sign = 2;
p.width--;
}
fpart = (int)fval;
fiter = 0;
while (fpart != 0)
{
temp_buffer[fiter++] = fpart % 10;
fpart = fpart / 10;
}
fiter--;
if (fiter == -1)
p.width--;
/* Leading zeros */
if (p.lz) {
if (sign == 1)
putf(putp, '-');
else if (sign == 2)
putf(putp, '+');
while (p.width-- > p.prec + fiter + 2)
{
putf(putp, '0');
}
}
else
{
while (p.width-- > p.prec + fiter + 2)
{
putf(putp, ' ');
}
if (sign == 1)
putf(putp, '-');
else if (sign == 2)
putf(putp, '+');
}
if (fiter == -1)
putf(putp, '0');
while (fiter > -1)
{
putf(putp, '0' + (temp_buffer[fiter--]));
}
putf(putp, '.');
ffactor = 1;
while (p.prec-- > 0)
{
ffactor *= 10;
fpart = (int)((fval - (int)fval)*ffactor);
if (fpart == 0)
putf(putp, '0');
}
fiter = 0;
while (fpart != 0)
{
temp_buffer[fiter++] = fpart % 10;
fpart = fpart / 10;
}
fiter--;
while (fiter > -1)
{
putf(putp, '0' + (temp_buffer[fiter--]));
}
break;
default:
break;
}
}
}
abort:;
}
#if TINYPRINTF_DEFINE_TFP_PRINTF
static putcf stdout_putf;
static void *stdout_putp;
void init_printf(void *putp, putcf putf)
{
stdout_putf = putf;
stdout_putp = putp;
}
void tfp_printf(char *fmt, ...)
{
va_list va;
va_start(va, fmt);
tfp_format(stdout_putp, stdout_putf, fmt, va);
va_end(va);
}
#endif
#if TINYPRINTF_DEFINE_TFP_SPRINTF
struct _vsnprintf_putcf_data
{
size_t dest_capacity;
char *dest;
size_t num_chars;
};
static void _vsnprintf_putcf(void *p, char c)
{
struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p;
if (data->num_chars < data->dest_capacity)
data->dest[data->num_chars] = c;
data->num_chars ++;
}
int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
struct _vsnprintf_putcf_data data;
data.dest = str;
data.dest_capacity = size ? size - 1 : 0;
data.num_chars = 0;
tfp_format(&data, _vsnprintf_putcf, format, ap);
if (data.num_chars < data.dest_capacity)
data.dest[data.num_chars] = '\0';
else if (size)
data.dest[data.dest_capacity] = '\0';
return data.num_chars;
}
int tfp_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsnprintf(str, size, format, ap);
va_end(ap);
return retval;
}
struct _vsprintf_putcf_data
{
char *dest;
size_t num_chars;
};
static void _vsprintf_putcf(void *p, char c)
{
struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p;
data->dest[data->num_chars++] = c;
}
int tfp_vsprintf(char *str, const char *format, va_list ap)
{
struct _vsprintf_putcf_data data;
data.dest = str;
data.num_chars = 0;
tfp_format(&data, _vsprintf_putcf, format, ap);
data.dest[data.num_chars] = '\0';
return data.num_chars;
}
int tfp_sprintf(char *str, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsprintf(str, format, ap);
va_end(ap);
return retval;
}
#endif
int tfp_vsscanf(const char* str, const char* format, va_list ap)
{
int value, tmp;
float fvalue;
double Fvalue;
int count = 0;
int pos;
char neg, fmt_code;
for (count = 0; *format != 0 && *str != 0; format++, str++)
{
while (*format == ' ' && *format != 0) format++;
if (*format == 0)
break;
while (*str == ' ' && *str != 0) str++;
if (*str == 0)
break;
if (*format == '%')
{
format++;
if (*format == 'n')
{
if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
{
fmt_code = 'x';
str += 2;
}
else
if (str[0] == 'b')
{
fmt_code = 'b';
str++;
}
else
fmt_code = 'd';
}
else
fmt_code = *format;
switch (fmt_code)
{
case 'x':
case 'X':
for (value = 0, pos = 0; *str != 0; str++, pos++)
{
if ('0' <= *str && *str <= '9')
tmp = *str - '0';
else
if ('a' <= *str && *str <= 'f')
tmp = *str - 'a' + 10;
else
if ('A' <= *str && *str <= 'F')
tmp = *str - 'A' + 10;
else
break;
value *= 16;
value += tmp;
}
if (pos == 0)
return count;
*(va_arg(ap, int*)) = value;
count++;
break;
case 'b':
for (value = 0, pos = 0; *str != 0; str++, pos++)
{
if (*str != '0' && *str != '1')
break;
value *= 2;
value += *str - '0';
}
if (pos == 0)
return count;
*(va_arg(ap, int*)) = value;
count++;
break;
case 'd':
if (*str == '-')
{
neg = 1;
str++;
}
else
neg = 0;
for (value = 0, pos = 0; *str != 0; str++, pos++)
{
if ('0' <= *str && *str <= '9')
value = value*10 + (int)(*str - '0');
else
break;
}
if (pos == 0)
return count;
*(va_arg(ap, int*)) = neg ? -value : value;
count++;
break;
case 'f':
if (*str == '-')
{
neg = 1;
str++;
}
else
neg = 0;
int point_flag = 0;
int exp = 0;
for (fvalue = 0, pos = 0; *str != 0 ; str++, pos++)
{
if (*str == '.')
{
point_flag = 1;
str++;
}
if ('0' <= *str && *str <= '9')
fvalue = fvalue*10 + (int)(*str - '0');
else
break;
if (point_flag == 1)
exp++;
}
if (pos == 0)
return count;
for (pos = 0; pos < exp; pos++)
fvalue = fvalue/10.0;
*(va_arg(ap, float*)) = neg ? -fvalue : fvalue;
count++;
break;
case 'F':
if (*str == '-')
{
neg = 1;
str++;
}
else
neg = 0;
int Fpoint_flag = 0;
int Fexp = 0;
for (Fvalue = 0, pos = 0; *str != 0 ; str++, pos++)
{
if (*str == '.')
{
Fpoint_flag = 1;
str++;
}
if ('0' <= *str && *str <= '9')
Fvalue = Fvalue*10 + (int)(*str - '0');
else
break;
if (Fpoint_flag == 1)
Fexp++;
}
if (pos == 0)
return count;
for (pos = 0; pos < Fexp; pos++)
Fvalue = Fvalue/10.0;
*(va_arg(ap, double*)) = neg ? -Fvalue : Fvalue;
count++;
break;
case 'c':
*(va_arg(ap, char*)) = *str;
count++;
break;
case 's':
pos = 0;
char* tab = va_arg(ap, char*);
while (*str != ' ' && *str != 0)
*(tab++) = *str++;
*tab = 0;
count++;
break;
default:
return count;
}
}
else
{
if (*format != *str)
break;
}
}
return count;
}

View File

@ -1,204 +0,0 @@
/*
File: tinyprintf.h
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'.
They provide a simple and small (+400 loc) printf functionality to
be used in embedded systems.
I've found them so useful in debugging that I do not bother with a
debugger at all.
They are distributed in source form, so to use them, just compile them
into your project.
Two printf variants are provided: printf and the 'sprintf' family of
functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf').
The formats supported by this implementation are:
'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'.
Zero padding and field width are also supported.
If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then
the long specifier is also supported. Note that this will pull in some
long math routines (pun intended!) and thus make your executable
noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the
long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t
specifier.
The memory footprint of course depends on the target CPU, compiler and
compiler options, but a rough guesstimate (based on a H8S target) is about
1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space.
Not too bad. Your mileage may vary. By hacking the source code you can
get rid of some hundred bytes, I'm sure, but personally I feel the balance of
functionality and flexibility versus code size is close to optimal for
many embedded systems.
To use the printf, you need to supply your own character output function,
something like :
void putc ( void* p, char c)
{
while (!SERIAL_PORT_EMPTY) ;
SERIAL_PORT_TX_REGISTER = c;
}
Before you can call printf, you need to initialize it to use your
character output function with something like:
init_printf(NULL,putc);
Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc',
the NULL (or any pointer) you pass into the 'init_printf' will eventually be
passed to your 'putc' routine. This allows you to pass some storage space (or
anything really) to the character output function, if necessary.
This is not often needed but it was implemented like that because it made
implementing the sprintf function so neat (look at the source code).
The code is re-entrant, except for the 'init_printf' function, so it is safe
to call it from interrupts too, although this may result in mixed output.
If you rely on re-entrancy, take care that your 'putc' function is re-entrant!
The printf and sprintf functions are actually macros that translate to
'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set
(default). Setting it to 0 makes it possible to use them along with
'stdio.h' printf's in a single source file. When
'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are
not function-like macros, so if you have variables or struct members
with these names, things will explode in your face. Without variadic
macros this is the best we can do to wrap these function. If it is a
problem, just give up the macros and use the functions directly, or
rename them.
It is also possible to avoid defining tfp_printf and/or tfp_sprintf by
clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or
'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to
export only tfp_format, which is at the core of all the other
functions.
For further details see source code.
regs Kusti, 23.10.2004
31.01.2015
Update from Cebotari Vladislav
cebotari.vladislav@gmail.com
- Added floating point support with different precision in x.y format
also with leading zeros possibility (like standard printf function).
Floating point printf is tested on tiva launchpad (tm4c123gh6pm TI mcu)
- Also vsscanf for floats and double %f - float, %F - double
*/
#ifndef __TFP_PRINTF__
#define __TFP_PRINTF__
#include <stdarg.h>
/* Global configuration */
/* Set this to 0 if you do not want to provide tfp_printf */
#ifndef TINYPRINTF_DEFINE_TFP_PRINTF
# define TINYPRINTF_DEFINE_TFP_PRINTF 0
#endif
/* Set this to 0 if you do not want to provide
tfp_sprintf/snprintf/vsprintf/vsnprintf */
#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF
# define TINYPRINTF_DEFINE_TFP_SPRINTF 0
#endif
/* Set this to 0 if you do not want tfp_printf and
tfp_{vsn,sn,vs,s}printf to be also available as
printf/{vsn,sn,vs,s}printf */
#ifndef TINYPRINTF_OVERRIDE_LIBC
# define TINYPRINTF_OVERRIDE_LIBC 0
#endif
# define TINY_PRINTF_FP_PRECISION 6
/* Optional external types dependencies */
#if TINYPRINTF_DEFINE_TFP_SPRINTF
# include <sys/types.h> /* size_t */
#endif
/* Declarations */
#ifdef __GNUC__
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \
__attribute__((format (printf, fmt_idx, arg1_idx)))
#else
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*putcf) (void *, char);
/*
'tfp_format' really is the central function for all tinyprintf. For
each output character after formatting, the 'putf' callback is
called with 2 args:
- an arbitrary void* 'putp' param defined by the user and
passed unmodified from 'tfp_format',
- the character.
The 'tfp_printf' and 'tfp_sprintf' functions simply define their own
callback and pass to it the right 'putp' it is expecting.
*/
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va);
# if TINYPRINTF_OVERRIDE_LIBC
# define tfp_vsscanf vsscanf
# endif
int tfp_vsscanf(const char* str, const char* format, va_list va);
#if TINYPRINTF_DEFINE_TFP_SPRINTF
# if TINYPRINTF_OVERRIDE_LIBC
# define tfp_vsnprintf vsnprintf
# define tfp_snprintf snprintf
# define tfp_vsprintf vsprintf
# define tfp_sprintf sprintf
# endif
int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(3, 4);
int tfp_vsprintf(char *str, const char *fmt, va_list ap);
int tfp_sprintf(char *str, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(2, 3);
#endif
#if TINYPRINTF_DEFINE_TFP_PRINTF
# if TINYPRINTF_OVERRIDE_LIBC
# define tfp_printf printf
# endif
void init_printf(void *putp, putcf putf);
void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
#endif
#ifdef __cplusplus
}
#endif
#endif