mirror of
https://git.planet-casio.com/Vhex-Kernel-Core/fxlibc.git
synced 2024-12-28 04:23:38 +01:00
stdio: move the printf implementation from gint
This commit is contained in:
parent
66463bfe17
commit
f52e0923bc
30 changed files with 1754 additions and 593 deletions
22
3rdparty/grisu2b_59_56/LICENSE
vendored
Normal file
22
3rdparty/grisu2b_59_56/LICENSE
vendored
Normal 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
14
3rdparty/grisu2b_59_56/README
vendored
Normal 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
58
3rdparty/grisu2b_59_56/diy_fp.h
vendored
Normal 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
110
3rdparty/grisu2b_59_56/double.h
vendored
Normal 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
27
3rdparty/grisu2b_59_56/grisu2.h
vendored
Normal 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
95
3rdparty/grisu2b_59_56/grisu2b_59_56.c
vendored
Normal 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
32
3rdparty/grisu2b_59_56/k_comp.h
vendored
Normal 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
27
3rdparty/grisu2b_59_56/powers.h
vendored
Normal 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"
|
36
3rdparty/grisu2b_59_56/powers_ten_round64.h
vendored
Normal file
36
3rdparty/grisu2b_59_56/powers_ten_round64.h
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -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()
|
||||
|
|
|
@ -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
226
include/fxlibc/printf.h
Normal 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__ */
|
|
@ -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__*/
|
||||
|
|
|
@ -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
18
src/libc/stdio/fprintf.c
Normal 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;
|
||||
}
|
|
@ -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__*/
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
295
src/libc/stdio/printf/format_fp.c
Normal file
295
src/libc/stdio/printf/format_fp.c
Normal 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);
|
||||
}
|
302
src/libc/stdio/printf/format_usual.c
Normal file
302
src/libc/stdio/printf/format_usual.c
Normal 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);
|
||||
}
|
292
src/libc/stdio/printf/print.c
Normal file
292
src/libc/stdio/printf/print.c
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
12
src/libc/stdio/vfprintf.c
Normal 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
12
src/libc/stdio/vprintf.c
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue