mirror of https://github.com/flysand7/ciabatta.git
Added printf variants
This commit is contained in:
parent
04555ac1db
commit
e964f2375d
|
@ -4,6 +4,7 @@ setlocal enabledelayedexpansion
|
|||
set PLATFORM=win32
|
||||
set CIABATTA_OPTIONS=-Iinc -g -gcodeview -nodefaultlibs -D_CRT_SECURE_NO_WARNINGS
|
||||
|
||||
del ciabatta.lib
|
||||
for /R code %%F in (*.c) do (
|
||||
echo %%F
|
||||
clang -c -o build\%%~nF.obj %%F %CIABATTA_OPTIONS%
|
||||
|
@ -15,3 +16,5 @@ for /R platform\%PLATFORM% %%F in (*.c) do (
|
|||
llvm-ar rc ciabatta.lib build\*.obj
|
||||
|
||||
clang test\test.c ciabatta.lib -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS%
|
||||
del build\*.obj
|
||||
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
// 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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
FMT_CHAR_TYPE ch = *fmt++;
|
||||
switch (ch) {
|
||||
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;
|
||||
}
|
||||
|
||||
const char* characters = "0123456789abcdef";
|
||||
if (ch == 'X') characters = "0123456789ABCDEF";
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// Even if the user doesn't enable LIB_EXT1 we still have it existing
|
||||
size_t strnlen_s(const char *s, size_t maxsize);
|
||||
|
||||
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"
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// Platform dependent
|
||||
///////////////////////////////////////////////
|
||||
#if defined(_os_win)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
// It's just mapped directly to HANDLE
|
||||
struct FILE {
|
||||
int unused;
|
||||
};
|
||||
|
||||
static void file_write(void* ctx, size_t n, const char str[]) {
|
||||
DWORD written = 0;
|
||||
WriteFile((HANDLE) ctx, str, n, &written, NULL);
|
||||
}
|
||||
#else
|
||||
#error "TODO: Implement this"
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////
|
||||
// Platform indenpendent
|
||||
///////////////////////////////////////////////
|
||||
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, file_write, fmt);
|
||||
return result;
|
||||
}
|
||||
|
||||
int printf(const char *restrict fmt, ...) {
|
||||
CALL_PRINTF(fmt_print_char, stdout, 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, file_write, fmt, args);
|
||||
}
|
||||
|
||||
int vprintf(const char *restrict fmt, va_list args) {
|
||||
return fmt_print_char(stdout, 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);
|
||||
}
|
|
@ -49,6 +49,8 @@ size_t strlen(const char *s) {
|
|||
|
||||
// __STDC_WANT_LIB_EXT1__
|
||||
size_t strnlen_s(const char *s, size_t maxsize) {
|
||||
if (s == NULL) return 0;
|
||||
|
||||
size_t i = 0;
|
||||
while (s[i] && i < maxsize) {
|
||||
i++;
|
||||
|
|
|
@ -27,10 +27,7 @@ typedef int64_t fpos_t;
|
|||
|
||||
#define TMP_MAX INT_MAX
|
||||
|
||||
// TODO: complete these, they don't need to be macros btw, could be external globals
|
||||
#define stderr ((FILE*)NULL)
|
||||
#define stdin ((FILE*)NULL)
|
||||
#define stdout ((FILE*)NULL)
|
||||
extern FILE *stdout, *stderr, *stdin;
|
||||
|
||||
int remove(const char *filename);
|
||||
int rename(const char *oldname, const char *newname);
|
||||
|
|
10
inc/wchar.h
10
inc/wchar.h
|
@ -26,8 +26,7 @@ int wscanf(const wchar_t * restrict format, ...);
|
|||
wint_t fgetwc(FILE *stream);
|
||||
wchar_t *fgetws(wchar_t * restrict s, int n, FILE * restrict stream);
|
||||
wint_t fputwc(wchar_t c, FILE *stream);
|
||||
int fputws(const wchar_t * restrict s,
|
||||
FILE * restrict stream);
|
||||
int fputws(const wchar_t * restrict s, FILE * restrict stream);
|
||||
int fwide(FILE *stream, int mode);
|
||||
wint_t getwc(FILE *stream);
|
||||
wint_t getwchar(void);
|
||||
|
@ -93,12 +92,9 @@ size_t wcsrtombs(char * restrict dst, const wchar_t ** restrict src, size_t len,
|
|||
errno_t wmemmove_s(wchar_t *s1, rsize_t s1max, const wchar_t *s2, rsize_t n);
|
||||
errno_t wcscat_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2);
|
||||
errno_t wcsncat_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n);
|
||||
wchar_t *wcstok_s(wchar_t * restrict s1, rsize_t * restrict s1max,
|
||||
const wchar_t * restrict s2, wchar_t ** restrict ptr);
|
||||
wchar_t *wcstok_s(wchar_t * restrict s1, rsize_t * restrict s1max, const wchar_t * restrict s2, wchar_t ** restrict ptr);
|
||||
size_t wcsnlen_s(const wchar_t *s, size_t maxsize);
|
||||
errno_t wcrtomb_s(size_t * restrict retval,
|
||||
char * restrict s, rsize_t smax,
|
||||
wchar_t wc, mbstate_t * restrict ps);
|
||||
errno_t wcrtomb_s(size_t * restrict retval, char * restrict s, rsize_t smax, wchar_t wc, mbstate_t * restrict ps);
|
||||
errno_t mbsrtowcs_s(size_t * restrict retval, wchar_t * restrict dst, rsize_t dstmax, const char ** restrict src, rsize_t len, mbstate_t * restrict ps);
|
||||
errno_t wcsrtombs_s(size_t * restrict retval, char * restrict dst, rsize_t dstmax, const wchar_t ** restrict src, rsize_t len, mbstate_t * restrict ps);
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "win32.h"
|
||||
|
||||
|
@ -58,6 +59,12 @@ void mainCRTStartup() {
|
|||
convert_wide_chars_to_ansi(args[i], args_wide[i], wide_len);
|
||||
}
|
||||
|
||||
// Initialize terminal
|
||||
stdout = (FILE*) GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
stderr = (FILE*) GetStdHandle(STD_ERROR_HANDLE);
|
||||
stdin = (FILE*) GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
// Initialize heap
|
||||
_os_heap heap_data = {
|
||||
.handle = heap,
|
||||
};
|
||||
|
|
10
test/test.c
10
test/test.c
|
@ -1,10 +1,14 @@
|
|||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
/*for (int i = 0; i < argv; i++) {
|
||||
printf("[%d] = %s\n", argv[i]);
|
||||
}*/
|
||||
for (int i = 0; i < argc; i++) {
|
||||
printf("[%d] = %s\n", i, argv[i]);
|
||||
}
|
||||
|
||||
printf("%d\n", 583875381);
|
||||
printf("%.*s", (int)3, "Hello");
|
||||
|
||||
for (char c = 'a'; c != 'z'; ++c) {
|
||||
assert(isupper(toupper(c)));
|
||||
|
|
Loading…
Reference in New Issue