stdio: move the printf implementation from gint

This commit is contained in:
Lephenixnoir 2021-06-07 18:57:11 +02:00
parent 66463bfe17
commit f52e0923bc
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
30 changed files with 1754 additions and 593 deletions

22
3rdparty/grisu2b_59_56/LICENSE vendored Normal file
View file

@ -0,0 +1,22 @@
Copyright (c) 2009 Florian Loitsch
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.

14
3rdparty/grisu2b_59_56/README vendored Normal file
View file

@ -0,0 +1,14 @@
Grisu is an algorithm by Florian Loitsch to print the decimal representation of
floating-point numbers.
The original code from which this folder is extracted can be found at
<https://drive.google.com/open?id=0BwvYOx00EwKmejFIMjRORTFLcTA>.
Only a minimal variation, the Grisu2 rounding algorithm with α=-59 and γ=-56,
is present here. The code has been adapted to fit the limited runtime as well
as the use of OpenLibm.
See the original paper at <https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf>
and Florian Loitsch's home page at <https://florian.loitsch.com/publications>.
The files are licensed under the permissive conditions of the attached LICENSE.

58
3rdparty/grisu2b_59_56/diy_fp.h vendored Normal file
View file

@ -0,0 +1,58 @@
/*
Copyright (c) 2009 Florian Loitsch
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.
*/
#pragma once
#include <stdint.h>
typedef struct diy_fp_t {
uint64_t f;
int e;
} diy_fp_t;
static diy_fp_t minus(diy_fp_t x, diy_fp_t y) {
diy_fp_t r = {.f = x.f - y.f, .e = x.e};
return r;
}
/*
static diy_fp_t minus(diy_fp_t x, diy_fp_t y) {
assert(x.e == y.e);
assert(x.f >= y.f);
diy_fp_t r = {.f = x.f - y.f, .e = x.e};
return r;
}
*/
static diy_fp_t multiply(diy_fp_t x, diy_fp_t y) {
uint64_t a,b,c,d,ac,bc,ad,bd,tmp,h;
diy_fp_t r; uint64_t M32 = 0xFFFFFFFF;
a = x.f >> 32; b = x.f & M32;
c = y.f >> 32; d = y.f & M32;
ac = a*c; bc = b*c; ad = a*d; bd = b*d;
tmp = (bd>>32) + (ad&M32) + (bc&M32);
tmp += 1U << 31; /// mult_round
r.f = ac+(ad>>32)+(bc>>32)+(tmp >>32);
r.e = x.e + y.e + 64;
return r;
}

110
3rdparty/grisu2b_59_56/double.h vendored Normal file
View file

@ -0,0 +1,110 @@
/*
Copyright (c) 2009 Florian Loitsch
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.
*/
#pragma once
#include "diy_fp.h"
#include "powers.h"
#include <stdbool.h>
typedef union {
double d;
uint64_t n;
} converter_t;
static uint64_t double_to_uint64(double d) { converter_t tmp; tmp.d = d; return tmp.n; }
static double uint64_to_double(uint64_t d64) { converter_t tmp; tmp.n = d64; return tmp.d; }
#define DP_SIGNIFICAND_SIZE 52
#define DP_EXPONENT_BIAS (0x3FF + DP_SIGNIFICAND_SIZE)
#define DP_MIN_EXPONENT (-DP_EXPONENT_BIAS)
#define DP_EXPONENT_MASK 0x7FF0000000000000
#define DP_SIGNIFICAND_MASK 0x000FFFFFFFFFFFFF
#define DP_HIDDEN_BIT 0x0010000000000000
static diy_fp_t normalize_diy_fp(diy_fp_t in) {
diy_fp_t res = in;
/* Normalize now */
/* the original number could have been a denormal. */
while (! (res.f & DP_HIDDEN_BIT))
{
res.f <<= 1;
res.e--;
}
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 1);
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 1);
return res;
}
static diy_fp_t double2diy_fp(double d) {
uint64_t d64 = double_to_uint64(d);
int biased_e = (d64 & DP_EXPONENT_MASK) >> DP_SIGNIFICAND_SIZE;
uint64_t significand = (d64 & DP_SIGNIFICAND_MASK);
diy_fp_t res;
if (biased_e != 0)
{
res.f = significand + DP_HIDDEN_BIT;
res.e = biased_e - DP_EXPONENT_BIAS;
} else {
res.f = significand;
res.e = DP_MIN_EXPONENT + 1;
}
return res;
}
static diy_fp_t normalize_boundary(diy_fp_t in) {
diy_fp_t res = in;
/* Normalize now */
/* the original number could have been a denormal. */
while (! (res.f & (DP_HIDDEN_BIT << 1)))
{
res.f <<= 1;
res.e--;
}
/* do the final shifts in one go. Don't forget the hidden bit (the '-1') */
res.f <<= (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
res.e = res.e - (DIY_SIGNIFICAND_SIZE - DP_SIGNIFICAND_SIZE - 2);
return res;
}
static void normalized_boundaries(double d, diy_fp_t* out_m_minus, diy_fp_t* out_m_plus) {
diy_fp_t v = double2diy_fp(d);
diy_fp_t pl, mi;
bool significand_is_zero = v.f == DP_HIDDEN_BIT;
pl.f = (v.f << 1) + 1; pl.e = v.e - 1;
pl = normalize_boundary(pl);
if (significand_is_zero)
{
mi.f = (v.f << 2) - 1;
mi.e = v.e - 2;
} else {
mi.f = (v.f << 1) - 1;
mi.e = v.e - 1;
}
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*out_m_plus = pl;
*out_m_minus = mi;
}

27
3rdparty/grisu2b_59_56/grisu2.h vendored Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright (c) 2009 Florian Loitsch
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.
*/
#pragma once
void grisu2(double v, char* buffer, int* length, int* K);

95
3rdparty/grisu2b_59_56/grisu2b_59_56.c vendored Normal file
View file

@ -0,0 +1,95 @@
/*
Copyright (c) 2009 Florian Loitsch
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.
*/
#include "diy_fp.h"
#include "k_comp.h"
#include "double.h"
#include "powers.h"
#include <stdbool.h>
#include "grisu2.h"
#include <stdint.h>
#define TEN9 1000000000
void grisu_round(char* buffer, int len,
uint64_t delta, uint64_t rest,
uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w &&
delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest+ten_kappa - wp_w))
{
buffer[len-1]--; rest += ten_kappa;
}
}
void digit_gen(diy_fp_t W, diy_fp_t Mp, diy_fp_t delta,
char* buffer, int* len, int* K) {
uint32_t div; int d,kappa; diy_fp_t one, wp_w;
wp_w = minus(Mp, W);
one.f = ((uint64_t) 1) << -Mp.e; one.e = Mp.e;
uint32_t p1 = Mp.f >> -one.e; /// Mp_cut
uint64_t p2 = Mp.f & (one.f - 1);
*len = 0; kappa = 10; div = TEN9;
while (kappa > 0) {
d = p1 / div;
if (d || *len) buffer[(*len)++] = '0' + d; /// Mp_inv1
p1 %= div; kappa--;
uint64_t tmp = (((uint64_t)p1)<<-one.e)+p2;
if (tmp <= delta.f) { /// Mp_delta
*K += kappa;
grisu_round(buffer, *len, delta.f, tmp, ((uint64_t)div) << -one.e, wp_w.f);
return;
}
div /= 10;
}
uint64_t unit = 1;
while (1) {
p2 *= 10; delta.f *= 10; unit *= 10;
d = p2 >> -one.e;
if (d || *len) buffer[(*len)++] = '0' + d;
p2 &= one.f - 1; kappa--;
if (p2 < delta.f) {
*K += kappa;
grisu_round(buffer, *len, delta.f, p2, one.f, wp_w.f*unit);
return;
}
}
}
void grisu2(double v, char* buffer, int* length, int* K) {
diy_fp_t w_m, w_p;
int q = 64, alpha = -59, gamma = -56; int pos;
normalized_boundaries(v, &w_m, &w_p);
diy_fp_t w = normalize_diy_fp(double2diy_fp(v));
int mk = k_comp(w_p.e + q, alpha, gamma);
diy_fp_t c_mk = cached_power(mk);
diy_fp_t W = multiply(w, c_mk);
diy_fp_t Wp = multiply(w_p, c_mk);
diy_fp_t Wm = multiply(w_m, c_mk);
Wm.f++; Wp.f--;
diy_fp_t delta = minus(Wp, Wm);
*K = -mk;
digit_gen(W, Wp, delta, buffer, length, K);
}

32
3rdparty/grisu2b_59_56/k_comp.h vendored Normal file
View file

@ -0,0 +1,32 @@
/*
Copyright (c) 2009 Florian Loitsch
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.
*/
#pragma once
#include <openlibm.h>
#define D_1_LOG2_10 0.30102999566398114 // 1 / lg(10)
static int k_comp(int e, int alpha, int gamma) {
return ceil((alpha-e+63) * D_1_LOG2_10);
}

27
3rdparty/grisu2b_59_56/powers.h vendored Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright (c) 2009 Florian Loitsch
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.
*/
#pragma once
#include "diy_fp.h"
#include "powers_ten_round64.h"

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.15)
project(FxLibc VERSION 1.0.0 LANGUAGES C ASM)
set(CMAKE_INSTALL_MESSAGE LAZY)
# Options
# * -DFXLIBC_TARGET=<vhex-sh, vhex-x86, casiowin-fx, casiowin-cg, gint>
@ -79,6 +81,8 @@ endif()
# Building
set(SOURCES
# 3rdparty
3rdparty/grisu2b_59_56/grisu2b_59_56.c
# assert
src/libc/assert/assert.c
# ctype
@ -110,19 +114,21 @@ set(SOURCES
src/libc/signal/signal.c
src/libc/signal/raise.c
# stdio
src/libc/stdio/vsnprintf.c
src/libc/stdio/sprintf.c
src/libc/stdio/dprintf.c
src/libc/stdio/snprintf.c
src/libc/stdio/puts.c
src/libc/stdio/vsprintf.c
src/libc/stdio/putc.c
src/libc/stdio/internal/printf_actions.c
src/libc/stdio/internal/printf_options.c
src/libc/stdio/internal/printf_common.c
src/libc/stdio/internal/printf.h
src/libc/stdio/vdprintf.c
src/libc/stdio/fprintf.c
src/libc/stdio/printf.c
src/libc/stdio/printf/format_fp.c
src/libc/stdio/printf/format_usual.c
src/libc/stdio/printf/print.c
src/libc/stdio/putc.c
src/libc/stdio/puts.c
src/libc/stdio/snprintf.c
src/libc/stdio/sprintf.c
src/libc/stdio/vdprintf.c
src/libc/stdio/vfprintf.c
src/libc/stdio/vprintf.c
src/libc/stdio/vsnprintf.c
src/libc/stdio/vsprintf.c
# stdlib
src/libc/stdlib/abort.c
src/libc/stdlib/abs.c
@ -179,6 +185,10 @@ set(SOURCES
src/libc/string/strtok.c
src/libc/string/strxfrm.c)
# Silence extended warnings on Grisu2b code
set_source_files_properties(3rdparty/grisu2b_59_56/grisu2b_59_56.c PROPERTIES
COMPILE_OPTIONS "-Wno-all;-Wno-extra")
if(vhex-generic IN_LIST TARGET_FOLDERS)
# TODO
endif()

View file

@ -108,6 +108,14 @@ This work is licensed under a CC0 1.0 Universal License. To view a copy of this
license, visit: https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt
Or see the LICENSE file.
FxLibc also includes third-party code that is distributed under its own
license. Currently, this includes:
* A stripped-down version of the [Grisu2b floating-point representation
algorithm](https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
with α=-59 and γ=-56, by Florian Loitsch. See `src/3rdparty/grisu2b_59_56/README`
for details, and [the original code here](https://drive.google.com/open?id=0BwvYOx00EwKmejFIMjRORTFLcTA).
---
### Special thanks to

226
include/fxlibc/printf.h Normal file
View file

@ -0,0 +1,226 @@
#ifndef __FXLIBC_PRINTF_H__
#define __FXLIBC_PRINTF_H__
/* This headers covers fxlibc-specific extensions to the *printf API. */
#include <bits/types/FILE.h>
#include <stdint.h>
#include <stdarg.h>
/*
** Output specification; only one of the targets may be non-trivial.
** -> str != NULL
** -> fp != NULL
** -> fd >= 0
** The size field can be set independently.
*/
struct __printf_output {
/* Final output, after buffering */
char * restrict str;
FILE *fp;
int fd;
/* Size of the final output */
size_t size;
/* The following members are set by __printf; do not write to them. */
/* Current output buffer, output position, and buffer limit */
char *buffer, *ptr, *limit;
/* Number of characters written so far */
size_t count;
};
/* Generic formatted printing. */
int __printf(
struct __printf_output * restrict __out,
char const * restrict __format,
va_list *__args);
/*
** Enable floating-point formatters %e, %E, %f, %F, %g, and %G. The formats are
** disabled by default because the representation algorithm has tables of
** powers of 10 and quite a bit of code, resulting in 10-15 kiB of additional
** size in every binary.
**
** Calling this functions pulls object files with floating-point representation
** code from the fxlibc library and registers formatters for all 6
** floating-point formats.
*/
void __printf_enable_fp(void);
/* Format extension API. */
/* Standard format. */
struct __printf_format {
/* Minimal length of formatted string (padding can be added). */
uint16_t length;
/* How much significant characters of data, meaning varies. */
int16_t precision;
/*
** Size specifier for integers (%o, %x, %i, %d, %u), may be one of:
** (0) char (8-bit)
** (1) short (16-bit)
** (2) int (32-bit)
** (3) long (32-bit)
** (4) long long (64-bit)
*/
uint8_t size;
/* (#) Alternative form: base prefixes, decimal point. */
uint8_t alternative :1;
/* ( ) Add a blank sign before nonnegative numbers. */
uint8_t blank_sign :1;
/* (+) Always add a sign before a number (overrides ' '). */
uint8_t force_sign :1;
/*
** Alignment options, from lowest priority to highest priority:
** NUL By default, align right
** (0) Left-pad numerical values with zeros
** (-) Align left by adding space on the right, dropping zeros
*/
uint8_t alignment;
/* Format specifier. */
char spec;
};
/*
** Type of format functions.
** -> __spec is the specifier letter (eg. "d" in "%d")
** -> __opts are the length, precision, sign, alignment, etc. options
** -> __args is a pointer to the variable list of arguments to read from
*/
typedef void __printf_formatter_t(
struct __printf_output * restrict __out,
struct __printf_format * restrict __opts,
va_list * restrict __args
);
/*
** Register a new format.
**
** The formatter designated by the specified lowercase or uppercase letter
** (eg 'p' or 'P') is registered. This functions allows overriding default
** formatters, but this is very much discouraged. Letters with special meaning
** in the standard cannot be changed. A formatted can be removed of disabled by
** registering NULL.
**
** Here are used characters in the C standard:
**
** a: Hexadecimal floating-point A: Hexadecimal floating-point
** b: _ B: _
** c: Character C: Deprecated synonym for lc
** d: Decimal integer D: _
** e: Exponent floating-point E: Exponent floating-point
** f: Floating-point F: Floating-point
** g: General floating-point G: General: floating-point
** h: short or char size H: _
** i: Integer I: Locale-aware digits
** j: intmax_t size J: _
** k: _ K: _
** l: long or long long size L: long double size
** m: Error message from errno M: _
** n: Number of characters written N: _
** o: Octal integer O: _
** p: Pointer P: _
** q: Nonstandard synonym for ll Q: _
** r: _ R: _
** s: String S: Deprecated synonym for ls
** t: ptrdiff_t size T: _
** u: Unsigned decimal integer U: _
** v: _ V: _
** w: _ W: _
** x: Hexadecimal integer X: Hexadecimal integer
** y: _ Y: _
** z: size_t size Z: Old synonym for z
*/
void __printf_register(int __spec, __printf_formatter_t *__format);
/* Functions for formatters to output characters. */
/* Flush the buffer. (Don't call this directly.) */
void __printf_flush(struct __printf_output *__out);
/* Output a single character in the buffer (and possibly flush it). */
static inline void __printf_out(struct __printf_output *__out,
int __c)
{
if(__out->ptr >= __out->limit) __printf_flush(__out);
*(__out->ptr++) = __c;
}
/* Output the same character __n times. */
static inline void __printf_outn(struct __printf_output *__out,
int __c, int __n)
{
while(__n-- > 0) __printf_out(__out, __c);
}
/* Output a string. */
static inline void __printf_outstr(struct __printf_output *__out,
char const *__str, int __n)
{
for(int i = 0; i < __n; i++) __printf_out(__out, __str[i]);
}
/* Helper functions for formatters. */
/*
** Format geometry helper. The structure of a format is as follows:
**
** sign v |< zeros >| |< content >|
** _ _ _ _ _ _ _ _ _ + 0 x 0 0 0 0 0 0 8 a c 7 . 3 c _ _ _ _ _ _ _ _ _ _
** |< left_spaces >| ^^^ prefix |< right_spaces >|
**
** The sign character is absent if sign=0, the prefix is specified by length
** and is also absent if prefix=0.
*/
struct __printf_geometry
{
uint16_t left_spaces; /* Spaces before content */
uint8_t sign; /* Sign character (NUL, ' ', '+' or '-') */
uint8_t prefix; /* Base prefix ('0', '0x', etc) length */
uint16_t zeros; /* For integer displays, number of zeros */
uint16_t content; /* Content length in bytes */
uint16_t right_spaces; /* Spaces after content */
/* Style of display:
_PRINTF_GENERIC Sign ignored, 0-padding ignored
_PRINTF_INTEGER .precision causes 0-padding
_PRINTF_NUMERIC No effect */
enum { _PRINTF_GENERIC = 0, _PRINTF_INTEGER, _PRINTF_NUMERIC } style;
};
/*
** Calculate the geometry of a format.
**
** The caller provides as input __opt (as it received in the formatter
** function), and the following attributes of __geometry:
**
** - prefix: the length of the desired prefix (if unused, 0)
** - content: the natural content length for the provided data
** - sign: the sign of the input ('+' or '-'); for _PRINTF_GENERIC, 0
** - style, which affects the meaning of options
**
** This function outputs:
** - sign: will be changed to ' ' or NUL (0) depending on options
** - All fields of __geometry that are not part of the input
**
** The algorithm for laying out the format is as follows.
** 1. For numerical and integer formats, turn a "+" sign into " " if
** __opt->blank_sign is set, "+" if __opt->force_sign is set, NUL otherwise.
** 2. Compute the total amount of padding needed to reach __opt->length.
** 3. In integer style, if a precision is specified and more than content
** length plus sign and prefix, turn some padding into zeros.
** 4. If numerical and integer styles, if __opt->alignment == '0' turn all the
** padding into zeros.
** 5. Turn remaining padding into spaces at the left (if opt->alignment == NUL)
** or right (if opt->alignment == '-').
*/
void __printf_compute_geometry(
struct __printf_format *__opt,
struct __printf_geometry *__geometry);
#endif /* __FXLIBC_PRINTF_H__ */

View file

@ -5,6 +5,7 @@
#include <stdarg.h>
#include <bits/types/FILE.h>
/* Standard input, output and error streams. */
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
@ -13,17 +14,71 @@ extern FILE *stderr;
#define stdout stdout
#define stderr stderr
/* *printf() familly - formatted output conversion. */
extern int printf(const char *restrict format, ...);
extern int dprintf(int fd, const char *restrict format, ...);
extern int sprintf(char *restrict str, const char *restrict format, ...);
extern int snprintf(char *restrict str, size_t size, const char *restrict format, ...);
extern int vdprintf(int fd, const char *restrict format, va_list ap);
extern int vsprintf(char *restrict str, const char *restrict format, va_list ap);
extern int vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list ap);
/*
** Formatted input/output functions.
**
** These functions implement most of printf(3)'s features, including:
** - Signed and unsigned integer formats (%d, %i, %o, %u, %x, %X)
** - Character, string and pointer formats (%c, %s, %p)
** - Character count and strerror() shorthand formats (%n, %m)
** - Format options (0, #, -, (space), length, precision)
** - Parameter length (hh, h, l, ll, z)
** - Limiting the size of the output and still returning the whole length
** - If __printf_enable_fp() from <fxlibc/printf.h> is called: floating-point
** formats (%e, %E, %f, %F, %g, %G) (disabled by default to save space)
**
** They do not support:
** - Hexadecimal floating-point (%a, %A)
** - Some size modifiers: long double (L), intmax_t (j), ptrdiff_t (t), and the
** nonstandard synonyms q (ll) and Z (z)
** - Dynamic length field (*)
** - Thousands separators (') and locale-aware digits (I)
** - Nonstandard synonyms %C (%lc) and %S (%ls)
**
** There are extensions, namely to allow for custom conversions to be added.
** One custom conversion can be enabled with __printf_enable_fixed() from
** <fxlibc/printf.h>: a decimal fixed-point format %k which is like %d but
** with a decimal point. See <fxlibc/printf.h> for details.
*/
/* Formatted print to file. */
extern int fprintf(FILE * restrict __fp, char const * restrict __format, ...);
/* Formatted print to stdout. */
extern int printf(char const * restrict __format, ...);
/* Formatted print to string (with limited size). */
extern int snprintf(char * restrict __str, size_t __size,
char const * restrict __format, ...);
/* Formatted print to string (with unlimited size!). */
extern int sprintf(char * restrict __str, char const * restrict __format, ...);
/* Formatted print to file (variable argument list). */
extern int vfprintf(FILE * restrict __fp, char const * restrict __format,
va_list __args);
/* Formatted print to stdout (variable argument list). */
extern int vprintf(char const * restrict __format, va_list __args);
/* Formatted print to string (limited size, variable argument list). */
extern int vsnprintf(char * restrict __str, size_t __size,
char const * restrict __format, va_list __args);
/* Formatted print to string (unlimited size!, variable argument list). */
extern int vsprintf(char * restrict __str, char const * restrict __format,
va_list __args);
/* putx() - display char / string */
extern int putchar(int c);
extern int puts(const char *s);
/* Extensions. */
/* Formatted print to file descriptor. */
extern int dprintf(int __fd, char const * restrict __format, ...);
/* Formatted print to file descriptor (variable argument list). */
extern int vdprintf(int __fd, char const * restrict __format, va_list __args);
#endif /*__STDIO_H__*/

View file

@ -1,16 +1,20 @@
#include <stdio.h>
#include <fxlibc/printf.h>
/*
** The function dprintf() is the same as fprintf() except that it outputs to a
** file descriptor, fd, instead of to a stdio stream.
*/
int dprintf(int fd, const char *restrict format, ...)
int dprintf(int fd, char const * restrict fmt, ...)
{
va_list ap;
int ret;
if(fd < 0) return -1;
va_start(ap, format);
ret = vdprintf(fd, format, ap);
va_end(ap);
return (ret);
struct __printf_output out = {
.fd = fd,
.size = 65536,
};
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

18
src/libc/stdio/fprintf.c Normal file
View file

@ -0,0 +1,18 @@
#include <stdio.h>
#include <fxlibc/printf.h>
int fprintf(FILE * restrict fp, char const * restrict fmt, ...)
{
struct __printf_output out = {
.fp = fp,
.size = 65536,
};
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

View file

@ -1,58 +0,0 @@
#ifndef _SRC_STDIO_INTERNAL_PRINTF_H__
#define _SRC_STDIO_INTERNAL_PRINTF_H__
#include <stddef.h>
#include <stdint.h>
//---
// Internal printf() part
//---
#define PRINTF_INTERNAL_BUFFER_SIZE 32
// internal structure used by any printf function familly
struct printf_opt
{
// Internal buffer to avoid syscall flux
char buffer[PRINTF_INTERNAL_BUFFER_SIZE];
size_t buffer_cursor;
// Common part
int counter;
va_list ap;
// *dprintf part
int fd;
// *sprintf part
char *str;
size_t str_size;
// For string / fd common support
void (*disp_char)(struct printf_opt *opt, char n);
void (*disp_fflush)(struct printf_opt *opt);
// Printf-options
struct {
uint8_t diez : 1;
uint8_t zero : 1;
uint8_t minus : 1;
uint8_t space : 1;
uint8_t plus : 1;
uint8_t const : 3;
} flags;
int width;
int precision;
int uppercase;
int lenght;
// Internal format management.
char sign;
char base[2];
char format[32];
int digits;
};
// Internal symbols used to define all actions possibility
extern void (*action[26])(struct printf_opt *opt, char n);
#endif /*_SRC_STDIO_INTERNAL_PRINTF_H__*/

View file

@ -1,208 +0,0 @@
#include <stdio.h>
// internal depency
#include "printf.h"
// Define all actions
static void action_str(struct printf_opt *op, char n);
static void action_ptr(struct printf_opt *op, char n);
static void action_int(struct printf_opt *op, char n);
static void action_uint(struct printf_opt *op, char n);
static void action_char(struct printf_opt *op, char n);
// Define all actions which can be used
void (*action[26])(struct printf_opt *opt, char n) = {
NULL, NULL, action_char, action_int,
NULL, NULL, NULL, NULL,
action_int, NULL, NULL, NULL,
NULL, NULL, action_uint, action_ptr,
NULL, NULL, action_str, NULL,
action_uint, NULL, NULL, action_uint,
NULL, NULL,
};
//---
// Disp part
//---
static void base_to_str(struct printf_opt *opt, uint32_t num, int base,
int digits)
{
char *hexa = "0123456789abcdef";
if (opt->uppercase == 1)
hexa = "0123456789ABCDEF";
opt->digits = 0;
while (num != 0 || opt->digits < digits) {
opt->format[opt->digits++] = hexa[num % base];
num = num / base;
}
}
static void disp_format(struct printf_opt *opt)
{
// display pre symbols
if (opt->sign != '\0')
(*opt->disp_char)(opt, opt->sign);
if (opt->base[0] != '\0')
(*opt->disp_char)(opt, opt->base[0]);
if (opt->base[1] != '\0')
(*opt->disp_char)(opt, opt->base[1]);
// padding
if (opt->flags.minus == 1 && opt->width > opt->digits) {
int total = opt->digits + (opt->sign != '\0') +
(opt->base[0] != '\0') + (opt->base[1] != '\0');
total = opt->width - total;
while (--total >= 0)
(*opt->disp_char)(opt,
(opt->flags.zero == 1) ? '0' : ' ');
}
// Display number
int saved_digits = opt->digits;
while (--opt->digits >= 0)
(*opt->disp_char)(opt, opt->format[opt->digits]);
// padding
if (opt->flags.minus == 0 && opt->width > saved_digits) {
int total = saved_digits + (opt->sign != '\0') +
(opt->base[0] != '\0') + (opt->base[1] != '\0');
total = opt->width - total;
while (--total >= 0)
(*opt->disp_char)(opt, ' ');
}
}
//---
// Args part
//---
static uint32_t get_arg_i(struct printf_opt *opt)
{
switch (opt->lenght) {
case 0:
return ((signed char)va_arg(opt->ap, int));
case 1:
return ((short int)va_arg(opt->ap, int));
case 2:
return (va_arg(opt->ap, long int));
case 3:
return (va_arg(opt->ap, long long int));
case 4:
return (va_arg(opt->ap, intmax_t));
case 5:
return (va_arg(opt->ap, size_t));
case 6:
return (va_arg(opt->ap, ptrdiff_t));
}
return (va_arg(opt->ap, int));
}
static uint32_t get_arg_u(struct printf_opt *opt)
{
switch (opt->lenght) {
case 0:
return ((unsigned char)va_arg(opt->ap, int));
case 1:
return ((unsigned short int)va_arg(opt->ap, int));
case 2:
return (va_arg(opt->ap, unsigned long int));
case 3:
return (va_arg(opt->ap, unsigned long long int));
case 4:
return (va_arg(opt->ap, intmax_t));
case 5:
return (va_arg(opt->ap, size_t));
case 6:
return (va_arg(opt->ap, ptrdiff_t));
}
return (va_arg(opt->ap, unsigned int));
}
//---
// Actions part.
//---
static void action_str(struct printf_opt *opt, char n)
{
const char *str;
(void)n;
str = va_arg(opt->ap, const char *);
while (*str != '\0')
(*opt->disp_char)(opt, *(str++));
}
static void action_char(struct printf_opt *opt, char n)
{
n = (char)va_arg(opt->ap, int);
(*opt->disp_char)(opt, n);
}
static void action_ptr(struct printf_opt *opt, char n)
{
(void)n;
opt->sign = '@';
opt->base[0] = '0';
opt->base[1] = 'x';
base_to_str(opt, (uintptr_t)va_arg(opt->ap, void*), 16, 8);
disp_format(opt);
}
static void action_int(struct printf_opt *opt, char n)
{
int64_t num;
// Get data and check negative value
// FIXME: max negative value can not be reversed
(void)n;
num = get_arg_i(opt);
if (num < 0) {
opt->sign = '-';
num = -num;
} else if (opt->flags.space == 1 || opt->flags.plus == 1) {
opt->sign = (opt->flags.plus == 1) ? '+' : ' ';
}
// Generate / display number
base_to_str(opt, num, 10, 1);
disp_format(opt);
}
static void action_uint(struct printf_opt *opt, char n)
{
uint32_t num;
int base;
// Get appropriate base
switch (n) {
case 'o':
base = 8;
break;
case 'x':
base = 16;
break;
default:
base = 10;
break;
}
// Display extra symbols if needed
if (opt->flags.diez == 1) {
if (n == 'o') {
opt->base[0] = '0';
} else if (n == 'x') {
opt->base[0] = '0';
opt->base[1] = (opt->uppercase == 1) ? 'X' : 'x';
}
}
// Get number
num = get_arg_u(opt);
// Generate / display number
base_to_str(opt, num, base, 1);
disp_format(opt);
}

View file

@ -1,48 +0,0 @@
#include <stdio.h>
// internal depency
#include "printf.h"
//TODO: precision handling
int printf_common(struct printf_opt *opt, const char *restrict format)
{
extern int printf_get_options(struct printf_opt *opt,
const char *restrict format);
int saved_p;
char tmp;
int p;
p = -1;
opt->counter = 0;
opt->buffer_cursor = 0;
while (format[++p] != '\0') {
// Check printable char
if (format[p] != '%' || format[p + 1] == '%') {
tmp = format[p];
if (format[p] == '%')
p = p + 1;
(*opt->disp_char)(opt,tmp);
continue;
}
// Get options
saved_p = p;
p = p + printf_get_options(opt, &format[p + 1]);
// Check arg validity
if (((format[p + 1] >= 'a' && format[p + 1] <= 'z') ||
(format[p + 1] >= 'A' && format[p + 1] <= 'Z')) &&
action[(format[p + 1] | 0x20) - 'a'] != NULL) {
tmp = (format[p + 1] | 0x20) - 'a';
(*action[(int)tmp]) (opt,tmp);
p = p + 1;
continue;
}
// Default, print the %
(*opt->disp_char)(opt, '%');
p = saved_p;
}
(*opt->disp_fflush)(opt);
return (opt->counter);
}

View file

@ -1,127 +0,0 @@
#include <stdio.h>
// internal depency
#include "printf.h"
static int get_flags(struct printf_opt *opt, const char *restrict format)
{
int i;
i = -1;
opt->flags.diez = 0;
opt->flags.zero = 0;
opt->flags.minus = 0;
opt->flags.space = 0;
opt->flags.plus = 0;
while (format[++i] != '\0') {
switch (format[i]) {
case '#':
opt->flags.diez = 1;
break;
case '0':
opt->flags.zero = 1;
break;
case '-':
opt->flags.minus = 1;
break;
case ' ':
opt->flags.space = 1;
break;
case '+':
opt->flags.plus = 1;
break;
default:
return (i);
}
}
return (i);
}
static int get_width(struct printf_opt *opt, const char *restrict format)
{
// Check dynamic width
if (format[0] == '*') {
opt->width = va_arg(opt->ap, int);
return (1);
}
// Check error
int i = -1;
opt->width = 0;
if (format[0] == '0')
return (0);
// Get static width
while (format[++i] >= '0' && format[i] <= '9')
opt->width = (opt->width * 10) + (format[i] - '0');
return (i);
}
static int get_precision(struct printf_opt *opt, const char *restrict format)
{
// Check if precision is specified
if (format[0] != '.')
return (0);
// Check dynamic precision
if (format[1] == '*') {
opt->precision = va_arg(opt->ap, int);
return (2);
}
// Get static precision
int i = 0;
opt->precision = 0;
while (format[++i] >= '0' && format[i] <= '9')
opt->precision = (opt->precision * 10) + (format[i] - '0');
// Check default precision
if (i == 0)
opt->precision = 1;
return (i);
}
static int get_lenght(struct printf_opt *opt, const char *restrict format)
{
opt->lenght = -1;
switch (format[0]) {
case 'h':
opt->lenght = (format[1] == 'h') ? 1 : 0;
break;
case 'l':
opt->lenght = (format[1] == 'l') ? 3 : 2;
break;
case 'j':
opt->lenght = 4;
break;
case 'z':
opt->lenght = 5;
break;
case 't':
opt->lenght = 6;
break;
default:
return (0);
}
return ((opt->lenght == 1 || opt->lenght == 3) ? 2 : 1);
}
int printf_get_options(struct printf_opt *opt, const char *restrict format)
{
int i;
// Wipe internal format infos
opt->sign = '\0';
opt->base[0] = '\0';
opt->base[1] = '\0';
// Get generals opetions
i = get_flags(opt, &format[0]);
i += get_width(opt, &format[i]);
i += get_precision(opt, &format[i]);
i += get_lenght(opt, &format[i]);
// Check upper case actions
opt->uppercase = (format[i] == 'X') ? 1 : 0;
return (i);
}

View file

@ -1,19 +1,18 @@
#include <stdio.h>
#include <unistd.h>
#include <fxlibc/printf.h>
/*
** printf() write the output under the control of a format string that specifies
** how subsequent arguments (or arguments accessed via the variable-length
** argument facilities of stdarg(3)) are converted for output then write to
** the STDOUT.
*/
int printf(const char *restrict format, ...)
int printf(char const * restrict fmt, ...)
{
va_list ap;
int ret;
struct __printf_output out = {
.fp = stdout,
.size = 65536,
};
va_start(ap, format);
ret = vdprintf(STDOUT_FILENO, format, ap);
va_end(ap);
return (ret);
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

View file

@ -0,0 +1,295 @@
#include <stddef.h>
#include <fxlibc/printf.h>
#include "../../../../3rdparty/grisu2b_59_56/grisu2.h"
#define min(x, y) ({ \
__auto_type _x = (x); \
__auto_type _y = (y); \
(_x < _y) ? (_x) : (_y); \
})
//---
// String generation for doubles
//---
/* The grisu2() function does not have a size limit so we generate into a
buffer large enough to hold the result. */
static char digit_buffer[50];
/* Round a number at the specified place (which can be out of bounds). An extra
byte before the buffer should be secured to leave room for a new digit in
case a carry reaches there. Returns the new start-of-buffer.
@buffer Buffer with generated digits, updated if needed
@length Number of digits generated in the buffer, updated if needed
@e Location of decimal dot relative to integer in buffer
@place Decimal to place to round to */
static void round_str(char **buffer_ptr, int *length, int e, int place)
{
char *buffer = *buffer_ptr;
/* Interpret place as relative to buffer indices */
place += *length + e - 1;
/* Specified place is out-of-bounds */
if(place < 0 || place >= *length - 1) return;
/* Next digit is 0..4 so rounding has no effect */
if(buffer[place + 1] < '5') return;
/* Propagate carries if needed */
while(place >= -1)
{
buffer[place]++;
if(buffer[place] <= '9') break;
buffer[place] = '0';
place--;
}
/* Add one digit if needed */
if(place >= 0) return;
(*buffer_ptr)--;
(*length)++;
}
/* Remove zeros at the end of the digits, reducing [length] accordingly. */
static int remove_zeros(char *buffer, int length, int unremovable)
{
int removed = 0;
while(length > unremovable && buffer[length - 1] == '0')
{
buffer[length - 1] = 0;
length--;
removed++;
}
return removed;
}
/* Handles infinities and NaNs. */
static int special_notation(struct __printf_output *out, double v, int upper)
{
if(__builtin_isinf(v) && v < 0)
{
__printf_outstr(out, upper ? "-INF" : "-inf", 4);
return 1;
}
if(__builtin_isinf(v))
{
__printf_outstr(out, upper ? "INF" : "inf", 3);
return 1;
}
if(__builtin_isnan(v))
{
__printf_outstr(out, upper ? "NAN" : "nan", 3);
return 1;
}
return 0;
}
/* Prints decimal explicitly for %f and %g. */
static void direct_notation(
struct __printf_output *out,
struct __printf_format *opt,
struct __printf_geometry g,
char *digits, int length, int e)
{
/* Number of characters for decimal part, including dot */
int dec_chars = opt->precision + (opt->precision > 0);
/* See case discussion below */
g.content = (length + e >= 0) ? length + e + dec_chars : 1 + dec_chars;
__printf_compute_geometry(opt, &g);
__printf_outn(out, ' ', g.left_spaces);
if(g.sign) __printf_out(out, g.sign);
__printf_outn(out, '0', g.zeros);
int pre = opt->precision;
if(e >= 0) /* xxxxxx00[.00] */
{
/* Decimal dot is after digits; rounding never occurs */
__printf_outstr(out, digits, length);
__printf_outn(out, '0', e);
if(pre > 0)
{
__printf_out(out, '.');
__printf_outn(out, '0', pre);
}
}
else if(length + e > 0) /* xxxy(.xx), xx.xy(xx), xx.xxxx[00] */
{
/* Decimal dot is within the digits; we might have rounded */
__printf_outstr(out, digits, length + e);
if(pre > 0)
{
__printf_out(out, '.');
__printf_outstr(out, digits + length + e, min(-e,pre));
__printf_outn(out, '0', pre + e);
}
}
else if(length + e <= 0) /* 0.00(00xxxx), 0.00xy(xx), 0.00xxxx00 */
{
/* Decimal dot is before the digits; we might have rounded */
__printf_out(out, '0');
if(pre > 0)
{
__printf_out(out, '.');
__printf_outn(out, '0', min(-e - length, pre));
__printf_outstr(out, digits, min(length,pre+length+e));
__printf_outn(out, '0', pre + e);
}
}
__printf_outn(out, ' ', g.right_spaces);
}
/* Prints exponent notation for %e and %g. */
static void exponent_notation(
struct __printf_output *out,
struct __printf_format *opt,
struct __printf_geometry g,
char *digits, int length, int e, int uppercase)
{
int true_e = e + length - 1;
/* Number of characters for decimal part and for exponent */
int dec_chars = opt->precision + (opt->precision > 0);
int exp_chars = 4 + (true_e >= 100 || true_e <= -100);
g.content = 1 + dec_chars + exp_chars;
__printf_compute_geometry(opt, &g);
__printf_outn(out, ' ', g.left_spaces);
if(g.sign) __printf_out(out, g.sign);
__printf_outn(out, '0', g.zeros);
/* Digits */
__printf_out(out, digits[0]);
if(opt->precision > 0)
{
__printf_out(out, '.');
__printf_outstr(out, digits+1, min(length-1, opt->precision));
__printf_outn(out, '0', opt->precision - (length - 1));
}
/* Exponent */
__printf_out(out, uppercase ? 'E' : 'e');
__printf_out(out, true_e >= 0 ? '+' : '-');
if(true_e < 0) true_e = -true_e;
if(true_e >= 100)
{
__printf_out(out, true_e / 100 + '0');
true_e %= 100;
}
__printf_out(out, true_e / 10 + '0');
__printf_out(out, true_e % 10 + '0');
__printf_outn(out, ' ', g.right_spaces);
}
//---
// Formatters for kprint
//---
static void __printf_format_eEfFgG(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
double v = va_arg(*args, double);
digit_buffer[0] = '0';
char *digits = digit_buffer + 1;
int length = 0, e = 0;
int is_e = (opt->spec | 0x20) == 'e';
int is_f = (opt->spec | 0x20) == 'f';
int is_g = (opt->spec | 0x20) == 'g';
/* In %e and %f, default to 6 decimals. In %g, default to 6 significant
digits, and force to at least 1 */
if(opt->precision < 0) opt->precision = 6;
if(opt->precision == 0 && is_g) opt->precision = 1;
if(special_notation(out, v, (opt->spec & 0x20) == 0)) return;
/* fabs(v) = int(digits) * 10^e */
if(v == 0.0) digits[length++] = '0';
else grisu2(v, digits, &length, &e);
digits[length] = 0;
/* In %f and %e, .precision is the number of decimal places; in %g, it
is the number of significant digits. Determine the number of decimal
places for the rounding (which is one more than the final number if
a carry creates a new significant digit on the left) */
int round_place = opt->precision;
if(is_e) round_place -= (length + e - 1);
if(is_g) round_place -= (length + e);
/* Round off to the specified number of decimal places. digits and
length may extend one place left because of carries */
round_str(&digits, &length, e, round_place);
struct __printf_geometry g = {
.sign = (v < 0) ? '-' : '+',
.prefix = 0,
.style = _PRINTF_NUMERIC,
};
if(is_f) {
return direct_notation(out, opt, g, digits, length, e);
}
if(is_e) {
return exponent_notation(out, opt, g, digits, length, e,
opt->spec == 'E');
}
int true_e = length + e - 1;
int extreme = (true_e < -4) || (true_e >= opt->precision);
/* %g is left. Remove decimal zeros at the end of the digit string,
starting from the last-shown digit. Keep all figits before the
point, the amount of which depends on the mode */
int removed = remove_zeros(digits, min(length, opt->precision),
extreme ? 1 : true_e + 1);
opt->precision -= removed;
if(extreme)
{
/* Don't print more significant digits than we have digits
(elimination of trailing zeros) */
opt->precision = min(opt->precision, length);
/* There is one leading digit and this many decimal places */
opt->precision--;
return exponent_notation(out, opt, g, digits, length, e,
opt->spec == 'G');
}
else
{
/* Number of digits before the point */
int leading_digits = true_e + 1;
/* Eliminate trailing zeros after the point */
opt->precision = min(opt->precision, length);
/* Remove leading digits from decimal place count */
opt->precision -= leading_digits;
return direct_notation(out, opt, g, digits, length, e);
}
}
void __printf_enable_fp(void)
{
__printf_register('e', __printf_format_eEfFgG);
__printf_register('E', __printf_format_eEfFgG);
__printf_register('f', __printf_format_eEfFgG);
__printf_register('F', __printf_format_eEfFgG);
__printf_register('g', __printf_format_eEfFgG);
__printf_register('G', __printf_format_eEfFgG);
}

View file

@ -0,0 +1,302 @@
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <fxlibc/printf.h>
//---
// Digit generation
//---
/* digits_10(): Generate digits in base 10
Fills up the provided digit string from least significant to most
significant digit, not adding zeros except if argument is zero. Returns the
number of digits. No NUL terminator is added. */
static int digits_10(char *str, uint64_t n)
{
int digits = 0;
while(n || !digits)
{
/* TODO: Use fast division in __printf's digits_10() */
str[digits++] = (n % 10) + '0';
n /= 10;
}
return digits;
}
/* digits_16(): Generate digits in base 16 */
static int digits_16(char *str, int uppercase, uint64_t n)
{
char *hex = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
int digits = 0;
while(n || !digits)
{
str[digits++] = hex[n & 0xf];
n >>= 4;
}
return digits;
}
/* digits_8(): Generate digits in base 8 */
static int digits_8(char *str, uint64_t n)
{
int digits = 0;
while(n || !digits)
{
str[digits++] = (n & 0x7) + '0';
n >>= 3;
}
return digits;
}
//---
// Loading helpers
//---
static int64_t load_i(int size, va_list *args)
{
/* All smaller types are promoted to int with sign extension, so we
don't need to care about them. */
if(size == 3) return va_arg(*args, long);
if(size == 4) return va_arg(*args, long long);
return va_arg(*args, int);
}
static uint64_t load_u(int size, va_list *args)
{
/* Again, no need to care about small types */
if(size == 3) return va_arg(*args, unsigned long);
if(size == 4) return va_arg(*args, unsigned long long);
return va_arg(*args, unsigned int);
}
//---
// Formatter functions
//---
/* Character formatter (%c)
(-) Move spaces to the right
{len} Specifies numbers of (whitespace-padded) characters to print */
void __printf_format_c(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
int c = va_arg(*args, int);
struct __printf_geometry g = {
.prefix = 0, .sign = 0, .content = 1,
};
__printf_compute_geometry(opt, &g);
__printf_outn(out, ' ', g.left_spaces);
__printf_out(out, c);
__printf_outn(out, ' ', g.right_spaces);
}
/* String formatter (%s)
(-) Move spaces to the right
{len} Minimal numbers of characters to output
{pre} Maximal numbers of bytes to read from argument */
void __printf_format_s(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
char const *s = va_arg(*args, char const *);
/* Compute content length, which is the smallest of two sizes: the
length set as precision and the actual length of the string */
size_t len = 0;
uint32_t precision = opt->precision ? opt->precision : (-1);
while(s[len] && len < precision) len++;
struct __printf_geometry g = {
.prefix = 0, .sign = 0, .content = len,
};
__printf_compute_geometry(opt, &g);
__printf_outn(out, ' ', g.left_spaces);
for(size_t i = 0; i < len; i++) __printf_out(out, s[i]);
__printf_outn(out, ' ', g.right_spaces);
}
/*Integer formatter (%d, %i)
(0) Pad with zeros, rather than spaces, on the left
(-) Move spaces to the right (overrides '0', extends {pre})
( ) Force a blank sign before nonnegative numbers
(+) Force a sign before every number (overrides ' ')
{len} Minimal number of characters to print
{pre} Forces a minimal number of digits, creating 0s (overrides '0') */
void __printf_format_di(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
int64_t n = load_i(opt->size, args);
/* Compute the sign and the absolute value */
struct __printf_geometry g = {
.sign = (n < 0) ? '-' : '+',
.prefix = 0,
.style = _PRINTF_INTEGER,
};
if(n < 0) n = -n;
/* Get the digit string */
char digits[32];
int pure, total;
pure = digits_10(digits, n);
if(opt->precision == 0 && !n) pure = 0;
total = (pure > opt->precision ? pure : opt->precision);
g.content = total;
__printf_compute_geometry(opt, &g);
/* Print the result */
__printf_outn(out, ' ', g.left_spaces);
if(g.sign) __printf_out(out, g.sign);
__printf_outn(out, '0', g.zeros);
__printf_outn(out, '0', total - pure);
for(int i = pure - 1; i >= 0; i--) __printf_out(out, digits[i]);
__printf_outn(out, ' ', g.right_spaces);
}
/* Unsigned integer formatter in various bases (%u, %o, %x)
(#) Enable base prefixes ("0" in octal, "0x" in hexadecimal)
(0) Pad with zeros, rather than spaces, on the left
(-) Move spaces to the right (overrides '0', extends {pre})
{len} Minimal number of characters to print
{pre} Forces a minimal number of digits, creating 0s (overrides '0') */
void __printf_format_ouxX(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
uint64_t n = load_u(opt->size, args);
char digits[48];
int pure = 0, total;
int specl = opt->spec | 0x20;
if(specl == 'u') pure = digits_10(digits, n);
if(specl == 'o') pure = digits_8(digits, n);
if(specl == 'x') pure = digits_16(digits, opt->spec == 'X', n);
if(opt->precision == 0 && !n) pure = 0;
total = (pure > opt->precision ? pure : opt->precision);
/* Prefix length */
size_t prefix = 0;
if(opt->alternative) prefix = (specl != 'u') + (specl == 'x');
struct __printf_geometry g = {
.sign = 0, .prefix = prefix, .content = total,
.style = _PRINTF_INTEGER,
};
__printf_compute_geometry(opt, &g);
/* Output */
__printf_outn(out, ' ', g.left_spaces);
if(opt->alternative)
{
if(specl != 'u') __printf_out(out, '0');
if(specl == 'x') __printf_out(out, opt->spec);
}
__printf_outn(out, '0', g.zeros);
__printf_outn(out, '0', total - pure);
for(int i = pure - 1; i >= 0; i--) __printf_out(out, digits[i]);
__printf_outn(out, ' ', g.right_spaces);
}
/* Pointer formatter (%p) */
void __printf_format_p(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
(void)opt;
void *p = va_arg(*args, void *);
char digits[] = "00000000";
digits_16(digits, 0, (uint32_t)p);
__printf_out(out, '0');
__printf_out(out, 'x');
for(int i = 7; i >= 0; i--) __printf_out(out, digits[i]);
}
/* errno message formatter (%m) */
void __printf_format_m(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
(void)opt;
(void)args;
char const *message = strerror(errno);
__printf_outstr(out, message, strlen(message));
}
/* Number of characters written so far (%n) */
void __printf_format_n(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
void *p = va_arg(*args, void *);
if(opt->size == 0) *(char *)p = out->count;
if(opt->size == 1) *(short *)p = out->count;
if(opt->size == 2) *(int *)p = out->count;
if(opt->size == 3) *(long *)p = out->count;
if(opt->size == 4) *(long long *)p = out->count;
}
/* Fixed-point decimal formatter (extension: %k)
(0) Pad with zeros, rather than spaces, on the left
(-) Move spaces to the right (overrides '0')
( ) Force a blank sign before nonnegative numbers
(+) Force a sign before every number (overrides ' ')
{len} Minimal number of characters to print
{pre} Number of digits after the decimal dot */
void __printf_format_k(
struct __printf_output * restrict out,
struct __printf_format * restrict opt,
va_list * restrict args)
{
int64_t n = load_i(opt->size, args);
/* Compute the sign and the absolute value */
struct __printf_geometry g = {
.sign = (n < 0) ? '-' : '+',
.prefix = 0,
.style = _PRINTF_NUMERIC,
};
if(n < 0) n = -n;
/* Get the digit string */
char digits[32];
g.content = digits_10(digits, n) + 1;
__printf_compute_geometry(opt, &g);
/* Print the result */
__printf_outn(out, ' ', g.left_spaces);
if(g.sign) __printf_out(out, g.sign);
__printf_outn(out, '0', g.zeros);
for(int i = g.content - 2; i >= 0; i--)
{
if(i == opt->precision - 1) __printf_out(out, '.');
__printf_out(out, digits[i]);
}
__printf_outn(out, ' ', g.right_spaces);
}

View file

@ -0,0 +1,292 @@
#include <stdio.h>
#include <ctype.h>
#include <fxlibc/printf.h>
/* Internal buffer, used when no buffer is specified for output */
#define _PRINTF_BUFSIZE 64
static char __printf_buffer[_PRINTF_BUFSIZE];
/* Notation for a letter that cannot be used as a specifier */
#define _PRINTF_USED ((__printf_formatter_t *)1)
/* Default formatter functions */
__printf_formatter_t __printf_format_c;
__printf_formatter_t __printf_format_di;
__printf_formatter_t __printf_format_m;
__printf_formatter_t __printf_format_n;
__printf_formatter_t __printf_format_ouxX;
__printf_formatter_t __printf_format_p;
__printf_formatter_t __printf_format_s;
static __printf_formatter_t *__printf_formatters[52] = {
/* Uppercase letters */
NULL, /* A: Hexadecimal floating-point */
NULL,
_PRINTF_USED, /* C: Deprecated synonym for lc */
NULL,
NULL, /* E: Exponent floating-point */
NULL, /* F: Floating-point */
NULL, /* G: General floating-point */
NULL,
_PRINTF_USED, /* I: Locale-aware digits */
NULL,
NULL,
_PRINTF_USED, /* L: long double size */
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
_PRINTF_USED, /* S: Deprecated synonym for ls */
NULL,
NULL,
NULL,
NULL,
__printf_format_ouxX, /* X: Hexadecimal integer */
NULL,
_PRINTF_USED, /* Z: Old synonym for z */
/* Lowercase letters */
NULL, /* a: Hexadecimal floating-point */
NULL,
__printf_format_c, /* c: Character */
__printf_format_di, /* d: Decimal integer */
NULL, /* e: Exponent floating-point */
NULL, /* f: Floating-point */
NULL, /* g: General floating-point */
_PRINTF_USED, /* h: short or char size */
__printf_format_di, /* i: Integer */
_PRINTF_USED, /* j: intmax_t size */
NULL, /* k: _ */
_PRINTF_USED, /* l: long or long long size */
__printf_format_m, /* m: Error message from errno */
__printf_format_n, /* n: Number of characters written */
__printf_format_ouxX, /* o: Octal integer */
__printf_format_p, /* p: Pointer */
_PRINTF_USED, /* q: Nonstandard synonym for ll */
NULL, /* r: _ */
__printf_format_s, /* s: String */
_PRINTF_USED, /* t: ptrdiff_t size */
__printf_format_ouxX, /* u: Unsigned decimal integer */
NULL, /* v: _ */
NULL, /* w: _ */
__printf_format_ouxX, /* x: Hexadecimal integer */
NULL, /* y: _ */
_PRINTF_USED, /* z: size_t size */
};
void __printf_register(int spec, __printf_formatter_t *format)
{
if(isupper(spec))
spec = spec - 'A';
else if(islower(spec))
spec = spec - 'a' + 26;
else return;
if(__printf_formatters[spec] == _PRINTF_USED || format == _PRINTF_USED)
return;
__printf_formatters[spec] = format;
}
void __printf_flush(struct __printf_output *out)
{
/* Update the number of flushed characters */
out->count += out->ptr - out->buffer;
/* String: write a NUL at the end even if overriding a character */
if(out->str && out->str != __printf_buffer)
{
char *nul = out->ptr;
if(out->limit - 1 < nul) nul = out->limit - 1;
*nul = 0x00;
}
/* File pointer: output with fwrite */
else if(out->fp)
{
// fwrite(out->buffer, out->ptr - out->buffer, 1, out->fp);
}
/* File pointer: output with write */
else if(out->fd)
{
// write(fd, out->buffer, out->ptr - out->buffer);
}
/* Switch to the internal buffer (when writing to a string that string
is now full, and when writing to files we've always been there) */
out->buffer = __printf_buffer;
out->limit = __printf_buffer + _PRINTF_BUFSIZE;
out->ptr = out->buffer;
}
/* Parse format strings. */
static struct __printf_format parse_fmt(char const * restrict *options_ptr)
{
/* No options enabled by default, set the size to int */
struct __printf_format opt = { .size = 2, .precision = -1 };
/* This function acts as a deterministic finite automaton */
enum {
basic, /* Reading option characters */
length, /* Reading length digits */
precision, /* Reading precision digits */
} state = basic;
char const *options = *options_ptr;
for(int c; (c = *options); options++)
{
int c_low = c | 0x20;
if(c_low >= 'a' && c_low <= 'z' && c != 'h' && c != 'l' &&
c != 'L') break;
if(c == '.')
{
state = precision;
opt.precision = 0;
continue;
}
else if(state == length && c >= '0' && c <= '9')
{
opt.length = opt.length * 10 + (c - '0');
continue;
}
else if(state == precision && c >= '0' && c <= '9')
{
opt.precision = opt.precision * 10 + (c - '0');
continue;
}
/* Remain in basic state mode */
state = basic;
if(c == '#') opt.alternative = 1;
if(c == ' ') opt.blank_sign = 1;
if(c == '+') opt.force_sign = 1;
/* Alignment options, including priority */
if((c == '-' || c == '0') && opt.alignment != '0')
{
opt.alignment = c;
}
/* Data size */
if(c == 'h') opt.size--;
if(c == 'l') opt.size++;
if(c == 'z') opt.size = 3;
if(c == 'L') {}
if(c >= '1' && c <= '9') state = length, options--;
}
*options_ptr = options;
return opt;
}
/* The formatted printer. */
int __printf(
struct __printf_output * restrict out,
char const * restrict format,
va_list *args)
{
/* Set up the buffer for the output */
if(out->fp || out->fd)
{
out->buffer = __printf_buffer;
out->limit = __printf_buffer + _PRINTF_BUFSIZE;
}
else
{
out->buffer = out->str;
out->limit = out->str + out->size;
}
out->ptr = out->buffer;
out->count = 0;
int c;
struct __printf_format opt;
while((c = *format++))
{
if(c != '%')
{
__printf_out(out, c);
continue;
}
if(!(c = *format)) break;
/* '%' character */
if(c == '%')
{
__printf_out(out, '%');
format++;
continue;
}
opt = parse_fmt(&format);
opt.spec = *format++;
int id;
if(isupper(opt.spec))
id = opt.spec - 'A';
else if(islower(opt.spec))
id = opt.spec - 'a' + 26;
else continue;
__printf_formatter_t *f = __printf_formatters[id];
if(f == NULL || f == _PRINTF_USED) continue;
f(out, &opt, args);
}
__printf_flush(out);
return (int)out->count;
}
void __printf_compute_geometry(
struct __printf_format *opt,
struct __printf_geometry *g)
{
int integral = (g->style == _PRINTF_INTEGER);
int numerical = (g->style == _PRINTF_NUMERIC) || integral;
int padding;
/* Sign character (no discussion required for negative values) */
if(numerical && g->sign == '+')
{
g->sign = 0;
if(opt->blank_sign) g->sign = ' ';
if(opt->force_sign) g->sign = '+';
}
g->zeros = 0;
padding = opt->length - (g->sign != 0) - g->prefix
- (g->content > opt->precision ? g->content : opt->precision);
if(padding < 0) padding = 0;
/* In integral modes, precision forces zeros */
if(integral && opt->precision >= 0)
{
if(opt->alignment == '0') opt->alignment = 0;
int zeros = opt->precision - g->content;
if(zeros > 0) g->zeros = zeros;
}
if(opt->alignment == '0')
{
/* Zeros are only allowed in numerical modes */
if(numerical) g->zeros = padding;
else g->left_spaces = padding;
}
else if(opt->alignment == '-')
{
g->right_spaces = padding;
}
else
{
g->left_spaces = padding;
}
}

View file

@ -1,17 +1,19 @@
#include <stdio.h>
#include <fxlibc/printf.h>
/*
** sprintf(), snprintf(), vsprintf() and vsnprintf() write the output under the
** control of a format string that specifies how subsequent arguments (or
** arguments accessed via the variable-length argument facilities of stdarg(3))
** are converted for output then write to the character string str.
*/
int snprintf(char *restrict str, size_t size, const char *restrict format, ...)
int snprintf(char * restrict str, size_t size, char const * restrict fmt, ...)
{
va_list ap;
/* This is valid even if str=NULL. */
struct __printf_output out = {
.str = str,
.size = size,
};
va_start(ap, format);
size = vsnprintf(str, size, format, ap);
va_end(ap);
return (size);
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

View file

@ -1,18 +1,19 @@
#include <stdio.h>
#include <fxlibc/printf.h>
/*
** sprintf(), snprintf(), vsprintf() and vsnprintf() write the output under the
** control of a format string that specifies how subsequent arguments (or
** arguments accessed via the variable-length argument facilities of stdarg(3))
** are converted for output then write to the character string str.
*/
int sprintf(char *restrict str, const char *restrict format, ...)
int sprintf(char * restrict str, char const * restrict fmt, ...)
{
va_list ap;
int size;
/* This is valid even if str=NULL. */
struct __printf_output out = {
.str = str,
.size = 65536,
};
va_start(ap, format);
size = vsnprintf(str, 65535, format, ap);
va_end(ap);
return (size);
va_list args;
va_start(args, fmt);
int count = __printf(&out, fmt, &args);
va_end(args);
return count;
}

View file

@ -1,45 +1,14 @@
#include <stdio.h>
#include <unistd.h>
#include <fxlibc/printf.h>
// internal depency
#include "internal/printf.h"
// FIXME:
// if the writte syscall do not return the same
// number of bytes that requested, stop the function !
static void disp_fflush(struct printf_opt *opt)
int vdprintf(int fd, char const * restrict fmt, va_list args)
{
if (opt->buffer_cursor != 0) {
opt->counter += write(opt->fd, opt->buffer, opt->buffer_cursor);
opt->buffer_cursor = 0;
}
}
static void disp_char(struct printf_opt *opt, char n)
{
// Check if we should force flush the internal buffer
if (opt->buffer_cursor >= PRINTF_INTERNAL_BUFFER_SIZE)
disp_fflush(opt);
// Save char
opt->buffer[opt->buffer_cursor++] = n;
}
/*
** The functions vdprintf() are equivalent to the dprintf() except that they
** are called with a va_list instead of a variable number of arguments. These
** functions do not call the va_end macro. Because they invoke the va_arg macro,
** the value of ap is undefined after the call.
*/
int vdprintf(int fd, const char *restrict format, va_list ap)
{
extern int printf_common(struct printf_opt *opt,
const char *restrict format);
struct printf_opt opt;
opt.fd = fd;
opt.disp_char = &disp_char;
opt.disp_fflush = &disp_fflush;
va_copy(opt.ap, ap);
return (printf_common(&opt, format));
if(fd < 0) return -1;
struct __printf_output out = {
.fd = fd,
.size = 65536,
};
return __printf(&out, fmt, &args);
}

12
src/libc/stdio/vfprintf.c Normal file
View file

@ -0,0 +1,12 @@
#include <stdio.h>
#include <fxlibc/printf.h>
int vfprintf(FILE * restrict fp, char const * restrict fmt, va_list args)
{
struct __printf_output out = {
.fp = fp,
.size = 65536,
};
return __printf(&out, fmt, &args);
}

12
src/libc/stdio/vprintf.c Normal file
View file

@ -0,0 +1,12 @@
#include <stdio.h>
#include <fxlibc/printf.h>
int vprintf(char const * restrict fmt, va_list args)
{
struct __printf_output out = {
.fp = stdout,
.size = 65536,
};
return __printf(&out, fmt, &args);
}

View file

@ -1,39 +1,14 @@
#include <stdio.h>
#include <fxlibc/printf.h>
// internal depency
#include "internal/printf.h"
static void disp_char(struct printf_opt *opt, char n)
int vsnprintf(char * restrict str, size_t size, char const * restrict fmt,
va_list args)
{
// Check write possibility
if (opt->buffer_cursor < opt->str_size - 1) {
opt->str[opt->buffer_cursor] = n;
opt->buffer_cursor = opt->buffer_cursor + 1;
}
}
/* This is valid even if str=NULL. */
struct __printf_output out = {
.str = str,
.size = size,
};
static void disp_fflush(struct printf_opt *opt)
{
opt->str[opt->buffer_cursor] = '\0';
}
/*
** The functions vsnprintf() are equivalent to the snprintf() except that they
** are called with a va_list instead of a variable number of arguments. These
** functions do not call the va_end macro. Because they invoke the va_arg macro,
** the value of ap is undefined after the call.
*/
int vsnprintf(char *restrict str, size_t size, const char *restrict format,
va_list ap)
{
extern int printf_common(struct printf_opt *opt,
const char *restrict format);
struct printf_opt opt;
opt.str = str;
opt.str_size = size;
opt.disp_char = &disp_char;
opt.disp_fflush = &disp_fflush;
va_copy(opt.ap, ap);
return (printf_common(&opt, format) + 1);
return __printf(&out, fmt, &args);
}

View file

@ -1,12 +1,13 @@
#include <stdio.h>
#include <fxlibc/printf.h>
/*
** The functions vsprintf() are equivalent to the sprintf() except that they
** are called with a va_list instead of a variable number of arguments. These
** functions do not call the va_end macro. Because they invoke the va_arg macro,
** the value of ap is undefined after the call.
*/
int vsprintf(char *restrict str, const char *restrict format, va_list ap)
int vsprintf(char * restrict str, char const * restrict fmt, va_list args)
{
return (vsnprintf(str, 65535, format, ap));
/* This is valid even if str=NULL. */
struct __printf_output out = {
.str = str,
.size = 65536,
};
return __printf(&out, fmt, &args);
}