mirror of https://github.com/flysand7/ciabatta.git
prinf formats
This commit is contained in:
parent
f533aa779c
commit
f80957583b
2
bake.cmd
2
bake.cmd
|
@ -51,5 +51,5 @@ del build\*.obj
|
||||||
|
|
||||||
:skip_crt_compilation
|
:skip_crt_compilation
|
||||||
echo Compiling test..
|
echo Compiling test..
|
||||||
clang -fno-builtin test\test_math.c ciabatta.lib -std=c11 -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS%
|
clang -fno-builtin test\test_printf.c ciabatta.lib -std=c11 -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS%
|
||||||
::cl test\test_math.c /Iinc -D_CRT_SECURE_NO_WARNINGS /Z7 /link ciabatta.lib kernel32.lib user32.lib shell32.lib -nostdlib -nodefaultlibs
|
::cl test\test_math.c /Iinc -D_CRT_SECURE_NO_WARNINGS /Z7 /link ciabatta.lib kernel32.lib user32.lib shell32.lib -nostdlib -nodefaultlibs
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <fenv.h>
|
||||||
|
|
||||||
|
#define rln2 1.4426950408889634073599246810018921374266459541529859341354494069
|
||||||
|
|
|
@ -86,6 +86,11 @@ void _os_file_write(void* ctx, size_t n, const char str[]) {
|
||||||
WriteFile((HANDLE) ctx, str, n, &written, NULL);
|
WriteFile((HANDLE) ctx, str, n, &written, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _os_file_write_char(void* ctx, char ch) {
|
||||||
|
DWORD written = 0;
|
||||||
|
WriteFile((HANDLE) ctx, &ch, 1, &written, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
int system(const char* string) {
|
int system(const char* string) {
|
||||||
int wchars_required = MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, NULL, 0);
|
int wchars_required = MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, NULL, 0);
|
||||||
wchar_t* cmd_line = malloc(sizeof(L"cmd.exe ") + (wchars_required * sizeof(wchar_t)));
|
wchar_t* cmd_line = malloc(sizeof(L"cmd.exe ") + (wchars_required * sizeof(wchar_t)));
|
||||||
|
|
220
code/printf.h
220
code/printf.h
|
@ -1,220 +0,0 @@
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
//TODO: verify printf("%d", 0). From the code it looked like it would print
|
|
||||||
// an empty string.
|
|
||||||
|
|
||||||
// NOTE: this file doesn't exist in a vacuum, it's a template for generating
|
|
||||||
// the formatted print, you should define FMT_CHAR_TYPE before including it
|
|
||||||
inline static int FMT_FUNC_NAME (void *ctx, OutputFunc out, const FMT_CHAR_TYPE *fmt, va_list args) {
|
|
||||||
size_t full_length = 0;
|
|
||||||
|
|
||||||
while (*fmt) {
|
|
||||||
// skip any normal non percent text
|
|
||||||
const FMT_CHAR_TYPE *start = fmt;
|
|
||||||
while (*fmt && *fmt != '%') fmt++;
|
|
||||||
|
|
||||||
// print all those non percent text
|
|
||||||
out(ctx, fmt - start, start);
|
|
||||||
full_length += (fmt - start);
|
|
||||||
|
|
||||||
// null terminated
|
|
||||||
if (*fmt == '\0') break;
|
|
||||||
fmt += 1;
|
|
||||||
|
|
||||||
if (*fmt == '%') {
|
|
||||||
out(ctx, 1, "%");
|
|
||||||
fmt++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int precision = 0;
|
|
||||||
if (*fmt == '.') {
|
|
||||||
// custom precision
|
|
||||||
fmt++;
|
|
||||||
|
|
||||||
if (isdigit(*fmt)) {
|
|
||||||
// just a small atoi
|
|
||||||
// TODO: handle overflow, just in case(?)
|
|
||||||
while (isdigit(*fmt)) {
|
|
||||||
precision *= 10u;
|
|
||||||
precision += (*fmt - '0');
|
|
||||||
|
|
||||||
fmt++;
|
|
||||||
}
|
|
||||||
} else if (*fmt == '*') {
|
|
||||||
int p = va_arg(args, int);
|
|
||||||
|
|
||||||
precision = p >= 0 ? ((unsigned int)p) : 0;
|
|
||||||
fmt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// integer length specifiers
|
|
||||||
enum {
|
|
||||||
CHAR,
|
|
||||||
SHORT,
|
|
||||||
INT,
|
|
||||||
LONG,
|
|
||||||
LLONG
|
|
||||||
} int_length = INT;
|
|
||||||
|
|
||||||
if (*fmt == 'l') {
|
|
||||||
fmt++;
|
|
||||||
|
|
||||||
if (*fmt == 'l') {
|
|
||||||
int_length = LLONG;
|
|
||||||
fmt++;
|
|
||||||
} else {
|
|
||||||
int_length = LONG;
|
|
||||||
}
|
|
||||||
} else if (*fmt == 'h') {
|
|
||||||
fmt++;
|
|
||||||
|
|
||||||
if (*fmt == 'h') {
|
|
||||||
int_length = CHAR;
|
|
||||||
fmt++;
|
|
||||||
} else {
|
|
||||||
int_length = SHORT;
|
|
||||||
}
|
|
||||||
} else if (*fmt == 'z') {
|
|
||||||
fmt++;
|
|
||||||
|
|
||||||
int_length = _Generic((size_t)0,
|
|
||||||
unsigned char: CHAR,
|
|
||||||
unsigned short: SHORT,
|
|
||||||
unsigned int: INT,
|
|
||||||
unsigned long: LONG,
|
|
||||||
unsigned long long: LLONG);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CHAR_TYPE ch = *fmt++;
|
|
||||||
const char* characters = "0123456789abcdef";
|
|
||||||
if (ch == 'X') characters = "0123456789ABCDEF";
|
|
||||||
|
|
||||||
switch (ch) {
|
|
||||||
case 'c': {
|
|
||||||
const char chr = va_arg(args, int);
|
|
||||||
out(ctx, 1, &chr);
|
|
||||||
full_length ++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'f':
|
|
||||||
case 'L': {
|
|
||||||
double d = va_arg(args, double);
|
|
||||||
|
|
||||||
if(signbit(d)) { // TODO: negative zero
|
|
||||||
out(ctx, 1, "-");
|
|
||||||
d = -d;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isinf(d)) {
|
|
||||||
out(ctx, sizeof"inf"-1, "inf");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(isnan(d)) {
|
|
||||||
out(ctx, sizeof"nan"-1, "nan");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t w = (uint64_t)d;
|
|
||||||
d -= w;
|
|
||||||
FMT_CHAR_TYPE buffer[20];
|
|
||||||
size_t len = sizeof(buffer);
|
|
||||||
do {
|
|
||||||
buffer[--len] = characters[w % 10];
|
|
||||||
w /= 10;
|
|
||||||
} while(w != 0);
|
|
||||||
out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len);
|
|
||||||
|
|
||||||
char dot = '.';
|
|
||||||
out(ctx, 1, &dot);
|
|
||||||
|
|
||||||
for(int i = 0; i != 6; ++i) {
|
|
||||||
d *= 10;
|
|
||||||
int dv = (int)d;
|
|
||||||
d -= dv;
|
|
||||||
char digit = characters[dv];
|
|
||||||
out(ctx, 1, &digit);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case 's': {
|
|
||||||
const FMT_CHAR_TYPE *str = va_arg(args, FMT_CHAR_TYPE*);
|
|
||||||
size_t len = FMT_STRLEN_S(str, precision ? precision : SIZE_MAX);
|
|
||||||
|
|
||||||
out(ctx, len, str);
|
|
||||||
full_length += len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'b':
|
|
||||||
case 'o':
|
|
||||||
case 'i':
|
|
||||||
case 'u':
|
|
||||||
case 'd':
|
|
||||||
case 'x':
|
|
||||||
case 'X': {
|
|
||||||
int base = 10;
|
|
||||||
switch (ch) {
|
|
||||||
case 'X': case 'x': base = 16; break;
|
|
||||||
case 'o': base = 8; break;
|
|
||||||
case 'b': base = 2; break;
|
|
||||||
default: base = 10; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintmax_t i;
|
|
||||||
if (ch == 'd' || ch == 'i') {
|
|
||||||
intmax_t num = 0;
|
|
||||||
switch (int_length) {
|
|
||||||
case CHAR: num = (char) va_arg(args, int); break;
|
|
||||||
case SHORT: num = (short) va_arg(args, int); break;
|
|
||||||
case INT: num = va_arg(args, int); break;
|
|
||||||
case LONG: num = va_arg(args, long); break;
|
|
||||||
case LLONG: num = va_arg(args, long long); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num < 0) {
|
|
||||||
out(ctx, 1, "-");
|
|
||||||
i = -num;
|
|
||||||
full_length += 1;
|
|
||||||
} else {
|
|
||||||
i = num;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (int_length) {
|
|
||||||
case CHAR: i = (char) va_arg(args, int); break;
|
|
||||||
case SHORT: i = (short) va_arg(args, int); break;
|
|
||||||
case INT: i = va_arg(args, int); break;
|
|
||||||
case LONG: i = va_arg(args, long); break;
|
|
||||||
case LLONG: i = va_arg(args, long long); break;
|
|
||||||
default: i = 0; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
out(ctx, 1, "0");
|
|
||||||
full_length += 1;
|
|
||||||
} else {
|
|
||||||
FMT_CHAR_TYPE buffer[20];
|
|
||||||
size_t len = sizeof(buffer);
|
|
||||||
|
|
||||||
// we build the number in reverse
|
|
||||||
while (i != 0) {
|
|
||||||
buffer[--len] = characters[i % base];
|
|
||||||
i /= base;
|
|
||||||
}
|
|
||||||
|
|
||||||
out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len);
|
|
||||||
full_length += (sizeof(buffer) - len);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return full_length;
|
|
||||||
}
|
|
||||||
|
|
139
code/stdio.c
139
code/stdio.c
|
@ -1,139 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include <_os.h>
|
|
||||||
|
|
||||||
#define __STDC_WANT_LIB_EXT1__ 1
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int remove(const char *filename)
|
|
||||||
{
|
|
||||||
return _os_del_file(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rename(const char *old, const char *new)
|
|
||||||
{
|
|
||||||
return _os_mov_file(old, new);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *tmpnam(char *s)
|
|
||||||
{
|
|
||||||
static char static_buffer[L_tmpnam];
|
|
||||||
char *buffer = s;
|
|
||||||
if(s == NULL) buffer = static_buffer;
|
|
||||||
return _os_tmpname(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *fopen(const char *restrict filename, const char *restrict mode)
|
|
||||||
{
|
|
||||||
// Basically defined UB here by introducing missing modes
|
|
||||||
// It is simpler to implement that way I think.
|
|
||||||
int base_mode = mode[0];
|
|
||||||
int binary = 0;
|
|
||||||
int exclusive = 0;
|
|
||||||
int update = 0;
|
|
||||||
for(; *mode != 0; ++mode) {
|
|
||||||
if(*mode == 'x') exclusive = 1;
|
|
||||||
if(*mode == 'b') binary = 1;
|
|
||||||
if(*mode == '+') update = 1;
|
|
||||||
}
|
|
||||||
if(base_mode == 'r' && exclusive) return NULL;
|
|
||||||
if(base_mode == 'a' && exclusive) return NULL;
|
|
||||||
|
|
||||||
_OS_ModeFlags mode_flags = {
|
|
||||||
.base_mode = base_mode,
|
|
||||||
.binary = binary,
|
|
||||||
.update = update,
|
|
||||||
.exclusive = exclusive,
|
|
||||||
};
|
|
||||||
|
|
||||||
return _os_fopen(filename, mode_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fclose(FILE *stream)
|
|
||||||
{
|
|
||||||
return _os_fclose(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:kekw:
|
|
||||||
FILE *freopen(
|
|
||||||
const char *restrict filename,
|
|
||||||
const char *restrict mode,
|
|
||||||
FILE *restrict stream)
|
|
||||||
{
|
|
||||||
fclose(stream);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void(*OutputFunc)(void* ctx, size_t n, const char str[]);
|
|
||||||
|
|
||||||
///////////////////////////////////////////////
|
|
||||||
// Instantiate formatted print functions
|
|
||||||
///////////////////////////////////////////////
|
|
||||||
// TODO: instantiate wide char variants of print
|
|
||||||
#define FMT_FUNC_NAME fmt_print_char
|
|
||||||
#define FMT_CHAR_TYPE char
|
|
||||||
#define FMT_STRLEN_S(s, maxsize) strnlen_s(s, maxsize)
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
size_t used, capacity;
|
|
||||||
char* string;
|
|
||||||
} StrPrintCtx;
|
|
||||||
|
|
||||||
FILE *stdout, *stderr, *stdin;
|
|
||||||
|
|
||||||
#define CALL_PRINTF(fmt_func, ctx, out, fmt) \
|
|
||||||
va_list args; \
|
|
||||||
va_start(args, fmt); \
|
|
||||||
int result = fmt_func(ctx, out, fmt, args); \
|
|
||||||
va_end(args)
|
|
||||||
|
|
||||||
static void string_write(void *ctx, size_t n, const char *restrict str) {
|
|
||||||
StrPrintCtx *c = ctx;
|
|
||||||
memcpy(c->string + c->used, str, n);
|
|
||||||
c->used += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fprintf(FILE *file, const char *restrict fmt, ...) {
|
|
||||||
CALL_PRINTF(fmt_print_char, file, _os_file_write, fmt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int printf(const char *restrict fmt, ...) {
|
|
||||||
CALL_PRINTF(fmt_print_char, stdout, _os_file_write, fmt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) {
|
|
||||||
StrPrintCtx ctx = { 0, n, s };
|
|
||||||
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sprintf(char *restrict s, const char *restrict fmt, ...) {
|
|
||||||
StrPrintCtx ctx = { 0, SIZE_MAX, s };
|
|
||||||
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vfprintf(FILE *file, const char *restrict fmt, va_list args) {
|
|
||||||
return fmt_print_char(file, _os_file_write, fmt, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
int vprintf(const char *restrict fmt, va_list args) {
|
|
||||||
return fmt_print_char(stdout, _os_file_write, fmt, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list args) {
|
|
||||||
StrPrintCtx ctx = { 0, n, s };
|
|
||||||
return fmt_print_char(&ctx, string_write, fmt, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
int vsprintf(char *restrict s, const char *restrict fmt, va_list args) {
|
|
||||||
StrPrintCtx ctx = { 0, SIZE_MAX, s };
|
|
||||||
return fmt_print_char(&ctx, string_write, fmt, args);
|
|
||||||
}
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <_os.h>
|
||||||
|
|
||||||
|
FILE *stdout;
|
||||||
|
FILE *stderr;
|
||||||
|
FILE *stdin;
|
||||||
|
|
||||||
|
int remove(const char *filename)
|
||||||
|
{
|
||||||
|
return _os_del_file(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rename(const char *old, const char *new)
|
||||||
|
{
|
||||||
|
return _os_mov_file(old, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tmpnam(char *s) {
|
||||||
|
static char static_buffer[L_tmpnam];
|
||||||
|
char *buffer = s;
|
||||||
|
if(s == NULL) buffer = static_buffer;
|
||||||
|
return _os_tmpname(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fopen(const char *restrict filename, const char *restrict mode)
|
||||||
|
{
|
||||||
|
// Basically defined UB here by introducing missing modes
|
||||||
|
// It is simpler to implement that way I think.
|
||||||
|
int base_mode = mode[0];
|
||||||
|
int binary = 0;
|
||||||
|
int exclusive = 0;
|
||||||
|
int update = 0;
|
||||||
|
for(; *mode != 0; ++mode) {
|
||||||
|
if(*mode == 'x') exclusive = 1;
|
||||||
|
if(*mode == 'b') binary = 1;
|
||||||
|
if(*mode == '+') update = 1;
|
||||||
|
}
|
||||||
|
if(base_mode == 'r' && exclusive) return NULL;
|
||||||
|
if(base_mode == 'a' && exclusive) return NULL;
|
||||||
|
|
||||||
|
_OS_ModeFlags mode_flags = {
|
||||||
|
.base_mode = base_mode,
|
||||||
|
.binary = binary,
|
||||||
|
.update = update,
|
||||||
|
.exclusive = exclusive,
|
||||||
|
};
|
||||||
|
|
||||||
|
return _os_fopen(filename, mode_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fclose(FILE *stream)
|
||||||
|
{
|
||||||
|
return _os_fclose(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:kekw:
|
||||||
|
FILE *freopen(
|
||||||
|
const char *restrict filename,
|
||||||
|
const char *restrict mode,
|
||||||
|
FILE *restrict stream)
|
||||||
|
{
|
||||||
|
fclose(stream);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,718 @@
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// This stuff is kinda related to what's going on in this file, so I left it
|
||||||
|
// in a rather ugly manner here.
|
||||||
|
#if !defined(once)
|
||||||
|
#define once
|
||||||
|
|
||||||
|
#define countof(arr) (sizeof(arr)/sizeof(arr[0]))
|
||||||
|
|
||||||
|
#define CALL_PRINTF(fmt_func, ctx, out, fmt) \
|
||||||
|
va_list args; \
|
||||||
|
va_start(args, fmt); \
|
||||||
|
int result = fmt_func(ctx, out, fmt, args); \
|
||||||
|
va_end(args)
|
||||||
|
|
||||||
|
enum ArgLength {
|
||||||
|
LEN_HH,
|
||||||
|
LEN_H,
|
||||||
|
LEN_I,
|
||||||
|
LEN_L,
|
||||||
|
LEN_LL,
|
||||||
|
LEN_J,
|
||||||
|
LEN_Z,
|
||||||
|
LEN_T,
|
||||||
|
} typedef ArgLength;
|
||||||
|
|
||||||
|
enum ArgConv {
|
||||||
|
CONV_INT,
|
||||||
|
CONV_FLT,
|
||||||
|
CONV_EXP,
|
||||||
|
CONV_SCI,
|
||||||
|
CONV_CHAR,
|
||||||
|
CONV_STR,
|
||||||
|
CONV_PTR,
|
||||||
|
CONV_N,
|
||||||
|
CONV_PERCENT,
|
||||||
|
} typedef ArgConv;
|
||||||
|
|
||||||
|
struct Format typedef Format;
|
||||||
|
struct Format {
|
||||||
|
bool flag_left;
|
||||||
|
bool flag_sign;
|
||||||
|
bool flag_space;
|
||||||
|
bool flag_form;
|
||||||
|
bool flag_zero;
|
||||||
|
bool flag_upper;
|
||||||
|
bool flag_unsigned;
|
||||||
|
int base;
|
||||||
|
int width;
|
||||||
|
int prec;
|
||||||
|
ArgLength arg_len;
|
||||||
|
ArgConv arg_conv;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static uint64_t get_int_arg(Format *fmt, va_list args, bool *signp) {
|
||||||
|
bool sign = 0;
|
||||||
|
uint64_t num = 0;
|
||||||
|
|
||||||
|
// Read the integer from the args and split it into tuple of (sign, digits)
|
||||||
|
// If we've got HH or H we need to parse as int, and do the necessary conv
|
||||||
|
// to unsigned if needed.
|
||||||
|
if(fmt->arg_len < LEN_I) {
|
||||||
|
int ch = va_arg(args, int);
|
||||||
|
if(ch < 0) {
|
||||||
|
if(fmt->flag_unsigned) {
|
||||||
|
num = ch + (fmt->arg_len == LEN_HH? UCHAR_MAX : USHRT_MAX);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
num = -ch;
|
||||||
|
sign = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise we see whether the number is signed or unsigned. For unsigned
|
||||||
|
// numbers we just parse according to the length, for signed we do that
|
||||||
|
// plus handle the sign
|
||||||
|
else if(fmt->flag_unsigned) {
|
||||||
|
switch(fmt->arg_len) {
|
||||||
|
case LEN_I: num = va_arg(args, unsigned int); break;
|
||||||
|
case LEN_L: num = va_arg(args, unsigned long); break;
|
||||||
|
case LEN_LL: num = va_arg(args, unsigned long long); break;
|
||||||
|
case LEN_J: num = va_arg(args, uintmax_t); break;
|
||||||
|
case LEN_Z: num = va_arg(args, size_t); break;
|
||||||
|
case LEN_T: num = va_arg(args, size_t); break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: signed size_t
|
||||||
|
int64_t i = 0;
|
||||||
|
switch(fmt->arg_len) {
|
||||||
|
case LEN_I: i = va_arg(args, int); break;
|
||||||
|
case LEN_L: i = va_arg(args, long); break;
|
||||||
|
case LEN_LL: i = va_arg(args, long long); break;
|
||||||
|
case LEN_J: i = va_arg(args, uintmax_t); break;
|
||||||
|
case LEN_Z: i = va_arg(args, int64_t); break;
|
||||||
|
case LEN_T: i = va_arg(args, ptrdiff_t); break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
if(i < 0) {
|
||||||
|
num = -i;
|
||||||
|
sign = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
num = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*signp = sign;
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Generic printf function that is used to print to stdout, strings and files.
|
||||||
|
// The basic idea is to make generic printf that uses output function as
|
||||||
|
// parameter and that function is distinct for printing to files, strings etc.
|
||||||
|
// Furthermore, this generic printf is abbstracted over character type, and
|
||||||
|
// depending on the macro fchar it could print in char's or wchar_t's.
|
||||||
|
|
||||||
|
typedef void (suffix(out_func_t))(void* ctx, fchar ch);
|
||||||
|
|
||||||
|
inline static int suffix(fmt_atoi)(fchar const *str, int *value) {
|
||||||
|
int i = 0;
|
||||||
|
int val = 0;
|
||||||
|
while('0' <= str[i] && str[i] <= '9') {
|
||||||
|
int digit = str[i] - '0';
|
||||||
|
if(val > (INT_MAX - digit) / 10) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
val = 10*val + digit;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
*value = val;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int suffix(fmt_parse)(
|
||||||
|
Format *format,
|
||||||
|
fchar const *str,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
*format = (Format){0};
|
||||||
|
// Parse flags
|
||||||
|
int i = 0;
|
||||||
|
for(;;) {
|
||||||
|
if (str[i] == '-') format->flag_left = true;
|
||||||
|
else if(str[i] == '+') format->flag_sign = true;
|
||||||
|
else if(str[i] == ' ') format->flag_space = true;
|
||||||
|
else if(str[i] == '#') format->flag_form = true;
|
||||||
|
else if(str[i] == '0') format->flag_zero = true;
|
||||||
|
else break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
// Optional width specifier
|
||||||
|
int width = 0;
|
||||||
|
if(str[i] == '*') {
|
||||||
|
++i;
|
||||||
|
width = va_arg(args, int);
|
||||||
|
if(width < 0) {
|
||||||
|
width = -width;
|
||||||
|
format->flag_zero = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int width_len = suffix(fmt_atoi)(str+i, &width);
|
||||||
|
if(width_len < 0) return -1;
|
||||||
|
i += width_len;
|
||||||
|
}
|
||||||
|
// Optional precision specifier
|
||||||
|
int prec = 0;
|
||||||
|
if(str[i] == '.') {
|
||||||
|
++i;
|
||||||
|
if(str[i] == '*') {
|
||||||
|
++i;
|
||||||
|
prec = va_arg(args, int);
|
||||||
|
if(prec < 0) {
|
||||||
|
prec = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int prec_len = suffix(fmt_atoi)(str+i, &prec);
|
||||||
|
if(prec_len < 0) return -1;
|
||||||
|
i += prec_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
format->width = width;
|
||||||
|
format->prec = prec;
|
||||||
|
// Optional length modifier
|
||||||
|
format->arg_len = LEN_I;
|
||||||
|
if(str[i] == 'h') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_H;
|
||||||
|
if(str[i] == 'h') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_HH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(str[i] == 'l') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_L;
|
||||||
|
if(str[i] == 'l') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_LL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(str[i] == 'j') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_J;
|
||||||
|
}
|
||||||
|
else if(str[i] == 'z') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_Z;
|
||||||
|
}
|
||||||
|
else if(str[i] == 't') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_T;
|
||||||
|
}
|
||||||
|
else if(str[i] == 'L') {
|
||||||
|
++i;
|
||||||
|
format->arg_len = LEN_L;
|
||||||
|
}
|
||||||
|
// Conversion specifier
|
||||||
|
switch(str[i]) {
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
format->arg_conv = CONV_INT;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
format->arg_conv = CONV_INT;
|
||||||
|
format->flag_unsigned = true;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
format->arg_conv = CONV_INT;
|
||||||
|
format->flag_unsigned = true;
|
||||||
|
format->base = 8;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
format->arg_conv = CONV_INT;
|
||||||
|
format->flag_unsigned = true;
|
||||||
|
format->base = 16;
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
format->arg_conv = CONV_INT;
|
||||||
|
format->flag_unsigned = true;
|
||||||
|
format->flag_upper = true;
|
||||||
|
format->base = 16;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
format->arg_conv = CONV_FLT;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
format->arg_conv = CONV_FLT;
|
||||||
|
format->flag_upper = true;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
format->arg_conv = CONV_EXP;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
format->arg_conv = CONV_EXP;
|
||||||
|
format->flag_upper = true;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
format->arg_conv = CONV_SCI;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
format->arg_conv = CONV_SCI;
|
||||||
|
format->flag_upper = true;
|
||||||
|
format->base = 10;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
format->arg_conv = CONV_FLT;
|
||||||
|
format->base = 16;
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
format->arg_conv = CONV_FLT;
|
||||||
|
format->flag_upper = true;
|
||||||
|
format->base = 16;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
format->arg_conv = CONV_CHAR;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
format->arg_conv = CONV_STR;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
format->arg_conv = CONV_PTR;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
format->arg_conv = CONV_N;
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
format->arg_conv = CONV_PERCENT;
|
||||||
|
break;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
// Ignoring flags etc
|
||||||
|
if(format->flag_sign && format->flag_zero) {
|
||||||
|
format->flag_zero = 0;
|
||||||
|
}
|
||||||
|
if(format->flag_sign && format->flag_space) {
|
||||||
|
format->flag_space = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(format->prec != 0) {
|
||||||
|
format->flag_zero = 0;
|
||||||
|
}
|
||||||
|
else if(format->flag_zero) {
|
||||||
|
// HACK: NOTE: I hate printf formats
|
||||||
|
format->prec = format->width;
|
||||||
|
format->width = 0;
|
||||||
|
}
|
||||||
|
if(format->arg_conv == CONV_FLT && format->prec == 0) {
|
||||||
|
format->prec = 6;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int suffix(fmt_fprint_int)(
|
||||||
|
void *ctx,
|
||||||
|
suffix(out_func_t) *out,
|
||||||
|
Format *fmt,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
int w = 0;
|
||||||
|
|
||||||
|
bool sign;
|
||||||
|
uint64_t num = get_int_arg(fmt, args, &sign);
|
||||||
|
|
||||||
|
// Find the actual length the number takes up
|
||||||
|
int prefix_len = 0;
|
||||||
|
int digits_num = 0;
|
||||||
|
if(fmt->flag_space || fmt->flag_sign || sign) {
|
||||||
|
prefix_len = 1;
|
||||||
|
}
|
||||||
|
if(num != 0 && fmt->flag_form) {
|
||||||
|
if(fmt->base == 16) prefix_len += 2; // 0x
|
||||||
|
if(fmt->base == 8) digits_num += 1; // 0
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint64_t digits = num;
|
||||||
|
do {
|
||||||
|
digits_num += 1;
|
||||||
|
digits /= fmt->base;
|
||||||
|
} while(digits != 0);
|
||||||
|
}
|
||||||
|
int s_len = prefix_len + digits_num; // length of significant chars
|
||||||
|
int d_len = s_len; // length of all displayed chars
|
||||||
|
int zpad = 0; // pre-pad of zeroes from precision
|
||||||
|
if(digits_num < fmt->prec) {
|
||||||
|
zpad = fmt->prec - digits_num;
|
||||||
|
d_len = s_len + zpad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if padding is necessary and extract info about padding
|
||||||
|
bool left_pad = false;
|
||||||
|
bool right_pad = true;
|
||||||
|
char pad_ch = ' ';
|
||||||
|
int pad_len = 0;
|
||||||
|
if(!sign && fmt->flag_space) { // make space part of pad
|
||||||
|
d_len += 1;
|
||||||
|
pad_len = 1;
|
||||||
|
}
|
||||||
|
if(d_len < fmt->width) {
|
||||||
|
pad_len = fmt->width - d_len;
|
||||||
|
}
|
||||||
|
if(fmt->flag_left) {
|
||||||
|
left_pad = true;
|
||||||
|
right_pad = false;
|
||||||
|
}
|
||||||
|
if(fmt->flag_zero) {
|
||||||
|
pad_ch = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill up an array of significant digits
|
||||||
|
char digits_arr[33] = {0};
|
||||||
|
char *digits = digits_arr + countof(digits_arr);
|
||||||
|
{
|
||||||
|
uint64_t temp = num;
|
||||||
|
do {
|
||||||
|
int digit = temp % fmt->base;
|
||||||
|
char ch = 0;
|
||||||
|
if(digit < 10) ch = digit + '0';
|
||||||
|
else if(fmt->flag_upper) ch = digit - 10 + 'A';
|
||||||
|
else ch = digit - 10 + 'a';
|
||||||
|
*--digits = ch;
|
||||||
|
temp /= fmt->base;
|
||||||
|
} while(temp != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start displaying number
|
||||||
|
// Left-pad if necessary
|
||||||
|
if(left_pad) while(pad_len--) {
|
||||||
|
out(ctx, pad_ch);
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print sign
|
||||||
|
if(sign) { out(ctx, '-'); ++w; }
|
||||||
|
else if(fmt->flag_sign) { out(ctx, '+'); ++w; }
|
||||||
|
else if(fmt->flag_space) { out(ctx, ' '); ++w; }
|
||||||
|
|
||||||
|
// Print 0x, 0X
|
||||||
|
if(num != 0 && fmt->base == 16 && fmt->flag_form) {
|
||||||
|
out(ctx, '0');
|
||||||
|
if(fmt->flag_upper) out(ctx, 'X');
|
||||||
|
else out(ctx, 'x');
|
||||||
|
w += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print zpad
|
||||||
|
while(zpad--) {
|
||||||
|
out(ctx, '0');
|
||||||
|
++ w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print digit string
|
||||||
|
while(*digits != 0) {
|
||||||
|
out(ctx, *digits);
|
||||||
|
++digits;
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right-pad if necessary
|
||||||
|
if(right_pad) while(pad_len--) {
|
||||||
|
out(ctx, pad_ch);
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int suffix(fmt_fprint_ptr)(
|
||||||
|
void *ctx,
|
||||||
|
suffix(out_func_t) *out,
|
||||||
|
Format *fmt,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
int w = 0;
|
||||||
|
uintptr_t num = va_arg(args, uintptr_t);
|
||||||
|
|
||||||
|
int s_len = 2 + 16; // length of significant chars
|
||||||
|
int d_len = s_len; // length of all displayed chars
|
||||||
|
int zpad = 0; // pre-pad of zeroes from precision
|
||||||
|
if(16 < fmt->prec) {
|
||||||
|
zpad = fmt->prec - 16;
|
||||||
|
d_len = s_len + zpad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if padding is necessary and extract info about padding
|
||||||
|
bool left_pad = true;
|
||||||
|
bool right_pad = false;
|
||||||
|
char pad_ch = ' ';
|
||||||
|
int pad_len = 0;
|
||||||
|
if(d_len < fmt->width) {
|
||||||
|
pad_len = fmt->width - d_len;
|
||||||
|
}
|
||||||
|
if(fmt->flag_left) {
|
||||||
|
left_pad = false;
|
||||||
|
right_pad = true;
|
||||||
|
}
|
||||||
|
if(fmt->flag_zero) {
|
||||||
|
pad_ch = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill up an array of significant digits
|
||||||
|
char digits_arr[33] = {0};
|
||||||
|
char *digits = digits_arr + countof(digits_arr);
|
||||||
|
{
|
||||||
|
uint64_t temp = num;
|
||||||
|
do {
|
||||||
|
int digit = temp % 16;
|
||||||
|
char ch = 0;
|
||||||
|
if(digit < 10) ch = digit + '0';
|
||||||
|
else if(fmt->flag_upper) ch = digit - 10 + 'A';
|
||||||
|
else ch = digit - 10 + 'a';
|
||||||
|
*--digits = ch;
|
||||||
|
temp /= 16;
|
||||||
|
} while(s_len-- != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start displaying number
|
||||||
|
// Left-pad if necessary
|
||||||
|
if(left_pad) while(pad_len--) {
|
||||||
|
out(ctx, pad_ch);
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
out(ctx, '0');
|
||||||
|
out(ctx, 'x');
|
||||||
|
w += 2;
|
||||||
|
|
||||||
|
// Print zpad
|
||||||
|
while(zpad--) {
|
||||||
|
out(ctx, '0');
|
||||||
|
++ w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print digit string
|
||||||
|
while(*digits != 0) {
|
||||||
|
out(ctx, *digits);
|
||||||
|
++digits;
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right-pad if necessary
|
||||||
|
if(right_pad) while(pad_len--) {
|
||||||
|
out(ctx, pad_ch);
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int suffix(fmt_fprint_char)(
|
||||||
|
void *ctx,
|
||||||
|
suffix(out_func_t) *out,
|
||||||
|
Format *fmt,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
int w = 0;
|
||||||
|
bool sign;
|
||||||
|
uint64_t num = get_int_arg(fmt, args, &sign);
|
||||||
|
|
||||||
|
// Calculate padding
|
||||||
|
bool left_pad = true;
|
||||||
|
bool right_pad = false;
|
||||||
|
uint64_t pad = 0;
|
||||||
|
if(1 < fmt->width) {
|
||||||
|
pad = fmt->width - 1;
|
||||||
|
}
|
||||||
|
if(fmt->flag_left) {
|
||||||
|
left_pad = false;
|
||||||
|
right_pad = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print left pad
|
||||||
|
if(left_pad) while(pad--) {
|
||||||
|
out(ctx, ' ');
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
out(ctx, num);
|
||||||
|
++w;
|
||||||
|
|
||||||
|
// Print right pad
|
||||||
|
if(right_pad) while(pad--) {
|
||||||
|
out(ctx, ' ');
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int suffix(fmt_just_fucking_print_float_someone_improve_this_later)(
|
||||||
|
void *ctx,
|
||||||
|
suffix(out_func_t) *out,
|
||||||
|
Format *fmt,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
int w = 0;
|
||||||
|
double f = va_arg(args, double);
|
||||||
|
uint64_t i = (uint64_t)f;
|
||||||
|
|
||||||
|
char digits_arr[33] = {0};
|
||||||
|
char *digits = digits_arr + countof(digits_arr);
|
||||||
|
{
|
||||||
|
uint64_t t = i;
|
||||||
|
do {
|
||||||
|
char ch = (t % 10) + '0';
|
||||||
|
*--digits = ch;
|
||||||
|
t /= 10;
|
||||||
|
} while(t-- != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(*digits != 0) {
|
||||||
|
out(ctx, *digits);
|
||||||
|
++digits;
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
f -= i;
|
||||||
|
uint64_t nprec = fmt->prec;
|
||||||
|
if(nprec != 0) {
|
||||||
|
out(ctx, '.');
|
||||||
|
w++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i != fmt->prec; ++i) {
|
||||||
|
f *= 10;
|
||||||
|
uint64_t digit = (uint64_t)f;
|
||||||
|
out(ctx, digit + '0');
|
||||||
|
++w;
|
||||||
|
f -= digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline static int suffix(fmt_fprint_str)(
|
||||||
|
void *ctx,
|
||||||
|
suffix(out_func_t) *out,
|
||||||
|
Format *fmt,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
int w = 0;
|
||||||
|
char *str = va_arg(args, char *);
|
||||||
|
uint64_t len = strlen(str);
|
||||||
|
|
||||||
|
// Calculate padding
|
||||||
|
bool left_pad = true;
|
||||||
|
bool right_pad = false;
|
||||||
|
uint64_t pad = 0;
|
||||||
|
if(len < fmt->width) {
|
||||||
|
pad = fmt->width - len;
|
||||||
|
}
|
||||||
|
if(fmt->flag_left) {
|
||||||
|
left_pad = false;
|
||||||
|
right_pad = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print left pad
|
||||||
|
if(left_pad) while(pad--) {
|
||||||
|
out(ctx, ' ');
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(*str) {
|
||||||
|
out(ctx, *str++);
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print right pad
|
||||||
|
if(right_pad) while(pad--) {
|
||||||
|
out(ctx, ' ');
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int suffix(fmt_print)(
|
||||||
|
void *ctx,
|
||||||
|
suffix(out_func_t) *out,
|
||||||
|
const fchar *fmt,
|
||||||
|
va_list args
|
||||||
|
) {
|
||||||
|
int w = 0; // N chars output
|
||||||
|
int i = 0; // N chars read from fmt
|
||||||
|
while(fmt[i] != 0) {
|
||||||
|
if(fmt[i] == '%') {
|
||||||
|
++i;
|
||||||
|
Format format;
|
||||||
|
int fmt_len = suffix(fmt_parse)(&format, fmt+i, args);
|
||||||
|
if(fmt_len < 0) return -1;
|
||||||
|
i += fmt_len;
|
||||||
|
int written;
|
||||||
|
switch(format.arg_conv) {
|
||||||
|
case CONV_INT: {
|
||||||
|
written = suffix(fmt_fprint_int)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_FLT: {
|
||||||
|
written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_EXP: {
|
||||||
|
written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_SCI: {
|
||||||
|
written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_CHAR: {
|
||||||
|
written = suffix(fmt_fprint_char)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_STR: {
|
||||||
|
written = suffix(fmt_fprint_str)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_PTR: {
|
||||||
|
written = suffix(fmt_fprint_ptr)(ctx, out, &format, args);
|
||||||
|
} break;
|
||||||
|
case CONV_N: {
|
||||||
|
int *n = va_arg(args, int *);
|
||||||
|
*n = w;
|
||||||
|
written = 0;
|
||||||
|
} break;
|
||||||
|
case CONV_PERCENT: {
|
||||||
|
out(ctx, '%');
|
||||||
|
written = 1;
|
||||||
|
} break;
|
||||||
|
if(written < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
w += written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Print non-formatted characters as god intended
|
||||||
|
else while(fmt[i] != '%' && fmt[i] != 0) {
|
||||||
|
out(ctx, fmt[i]);
|
||||||
|
++i;
|
||||||
|
++w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <_os.h>
|
||||||
|
|
||||||
|
#define __STDC_WANT_LIB_EXT1__ 1
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// Instantiate generic printf function for byte strings
|
||||||
|
// TODO: instantiate wide char variants of print
|
||||||
|
#define suffix(name) name ## _char
|
||||||
|
#define fchar char
|
||||||
|
#define fstrlen(s, maxsize) strnlen_s(s, maxsize)
|
||||||
|
#include "fmt_print.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t used, capacity;
|
||||||
|
char* string;
|
||||||
|
} StrPrintCtx;
|
||||||
|
|
||||||
|
static void string_write(void *ctx, char ch) {
|
||||||
|
StrPrintCtx *c = ctx;
|
||||||
|
c->string[c->used++] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fprintf(FILE *file, const char *restrict fmt, ...) {
|
||||||
|
CALL_PRINTF(fmt_print_char, file, _os_file_write_char, fmt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int printf(const char *restrict fmt, ...) {
|
||||||
|
CALL_PRINTF(fmt_print_char, stdout, _os_file_write_char, fmt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) {
|
||||||
|
StrPrintCtx ctx = { 0, n, s };
|
||||||
|
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sprintf(char *restrict s, const char *restrict fmt, ...) {
|
||||||
|
StrPrintCtx ctx = { 0, SIZE_MAX, s };
|
||||||
|
CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vfprintf(FILE *file, const char *restrict fmt, va_list args) {
|
||||||
|
return fmt_print_char(file, _os_file_write_char, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vprintf(const char *restrict fmt, va_list args) {
|
||||||
|
return fmt_print_char(stdout, _os_file_write_char, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list args) {
|
||||||
|
StrPrintCtx ctx = { 0, n, s };
|
||||||
|
return fmt_print_char(&ctx, string_write, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vsprintf(char *restrict s, const char *restrict fmt, va_list args) {
|
||||||
|
StrPrintCtx ctx = { 0, SIZE_MAX, s };
|
||||||
|
return fmt_print_char(&ctx, string_write, fmt, args);
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ char *_os_tmpname(char *buffer);
|
||||||
FILE *_os_fopen(char const *restrict name, _OS_ModeFlags flags);
|
FILE *_os_fopen(char const *restrict name, _OS_ModeFlags flags);
|
||||||
int _os_fclose(FILE *file);
|
int _os_fclose(FILE *file);
|
||||||
void _os_file_write(void* ctx, size_t n, const char str[]);
|
void _os_file_write(void* ctx, size_t n, const char str[]);
|
||||||
|
void _os_file_write_char(void* ctx, char ch);
|
||||||
|
|
||||||
void _os_init_eh();
|
void _os_init_eh();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("%f\n", 3.1415);
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue