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
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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 wchars_required = MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, NULL, 0);
|
||||
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);
|
||||
int _os_fclose(FILE *file);
|
||||
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();
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
printf("%f\n", 3.1415);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue