Separate out platform stuff

This commit is contained in:
bumbread 2022-06-20 17:28:03 +11:00
parent 4c09cdcca4
commit 88ef3bf81f
28 changed files with 334 additions and 1418 deletions

View File

@ -31,7 +31,7 @@ mkdir bin\%PLATFORM%
del ciabatta.lib 2> nul del ciabatta.lib 2> nul
for /R src\%PLATFORM% %%F in (*.c) do ( for /R src\%PLATFORM% %%F in (*.c) do (
echo %%F echo %%F
clang -c -o bin\%PLATFORM%\%%~nF.obj %%F %CIABATTA_OPTIONS% clang -Isrc/win -c -o bin\%PLATFORM%\%%~nF.obj %%F %CIABATTA_OPTIONS%
) )
for /R src\code %%F in (*.c) do ( for /R src\code %%F in (*.c) do (
echo %%F echo %%F

View File

@ -1,26 +0,0 @@
#pragma once
#if defined(_MSC_VER) && !defined(__clang__)
#define _compiler_msvc
#endif
#if defined(__GNUC__) && !defined(__clang__)
#define _compiler_gcc
#endif
#if defined(__clang__)
#define _compiler_clang
#endif
#if defined(__CUIKC__)
#define _compiler_cuik
#endif
#if !(defined(_compiler_msvc) \
|| defined(_compiler_gcc) \
|| defined(_compiler_cuik) \
|| defined(_compiler_clang))
#error "Unsupported Compiler"
#endif

View File

@ -1,43 +0,0 @@
#pragma once
#include <stddef.h>
#if defined(_WIN32)
#define _os_win
#endif
#if defined(__linux__) && !defined(__ANDROID__)
#define _os_linux
#endif
#if !(defined(_os_win) \
|| defined(_os_linux))
#error "Unsupported OS"
#endif
// OS-dependent IO Functions
// TODO: see if we need this or will it be easier for linux to just pass
// the mode string.
typedef struct _OS_ModeFlags {
int base_mode;
int binary;
int update;
int exclusive;
} _OS_ModeFlags;
typedef struct FILE FILE;
void _os_print_stack_trace();
int _os_del_file(char const *filename);
int _os_mov_file(char const *old, char const *new);
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();
_Noreturn void _os_exit(int code);

View File

@ -1,55 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
// Microsoft uses this to refer to the secure functions so we'll allow it
#ifdef __STDC_WANT_SECURE_LIB__
#define __STDC_WANT_LIB_EXT1__ 1
#endif
// Compiler Identification
#if defined(_MSC_VER) && !defined(__clang__)
#define _compiler_msvc
#endif
#if defined(__GNUC__) && !defined(__clang__)
#define _compiler_gnu
#endif
#if defined(__clang__)
#define _compiler_clang
#endif
#if defined(__CUIKC__)
#define _compiler_cuik
#endif
#if !(defined(_compiler_msvc) \
|| defined(_compiler_gnu) \
|| defined(_compiler_cuik) \
|| defined(_compiler_clang))
#error "Unsupported Compiler"
#endif
// OS Identification
#if defined(_WIN32)
#define _os_win
#endif
#if defined(__linux__) && !defined(__ANDROID__)
#define _os_linux
#endif
#if !(defined(_os_win) \
|| defined(_os_linux))
#error "Unsupported OS"
#endif
#ifdef __STDC_WANT_LIB_EXT1__
typedef int errno_t;
typedef size_t rsize_t;
#endif

View File

@ -1,3 +1,4 @@
#pragma once #pragma once
#if !defined(__func__) #if !defined(__func__)

View File

@ -1,21 +0,0 @@
#include <assert.h>
#include <stdio.h> // printf
#include <stdlib.h> // abort
#include <_os.h> // stack trace
void _Noreturn _assert(
char const *cond,
char const *func,
char const *file,
int line
) {
printf("Assertion failed: %s\n", cond);
printf(" Function: %s\n", func);
printf(" File: %s\n", file);
printf(" Line: %d\n", line);
printf("Trace:\n");
_os_print_stack_trace();
abort();
}

View File

@ -8,8 +8,7 @@
#define asuint64(x) ((union {double f; uint64_t i;}){x}).i #define asuint64(x) ((union {double f; uint64_t i;}){x}).i
#define asdouble(x) ((union {double f; uint64_t i;}){x}).f #define asdouble(x) ((union {double f; uint64_t i;}){x}).f
#include <_compiler.h> #if defined(__GNUC__) || defined(__clang__)
#if defined(_compiler_clang) || defined(_compiler_gcc)
#define just_do_it(t) __attribute__((unused)) volatile t #define just_do_it(t) __attribute__((unused)) volatile t
#else #else
#define just_do_it(t) volatile t #define just_do_it(t) volatile t

View File

@ -1,46 +0,0 @@
#include <_os.h>
#include <signal.h>
void _signal_default_handler(int sig)
{
}
void _signal_ignore_handler(int sig)
{
// ignore :kekw:
}
static void (*(handlers[]))(int) = {
[SIGINT] = _signal_ignore_handler,
[SIGILL] = _signal_ignore_handler,
[SIGFPE] = _signal_ignore_handler,
[SIGSEGV] = _signal_ignore_handler,
[SIGTERM] = _signal_ignore_handler,
[SIGABRT] = _signal_ignore_handler,
[SIGBREAK] = _signal_ignore_handler,
[SIGALIGN] = _signal_ignore_handler,
[SIGSTEP] = _signal_ignore_handler,
};
void (*signal(int sig, void (*func)(int)))(int)
{
if(_SIG_MIN <= sig && sig <= _SIG_MAX) {
handlers[sig] = func;
return func;
}
return SIG_ERR;
}
int raise(int sig)
{
if(_SIG_MIN <= sig && sig <= _SIG_MAX) {
handlers[sig](sig);
if(sig == SIGFPE || sig == SIGILL || sig == SIGSEGV) {
_os_exit(-69420);
}
return 1;
}
return 0;
}

View File

@ -1,65 +0,0 @@
#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;
}

View File

@ -1,723 +0,0 @@
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <string.h>
#include <math.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;
}

View File

@ -1,67 +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>
// 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);
}

View File

@ -1,40 +0,0 @@
#include <stdlib.h>
#include <signal.h>
#include <_os.h>
typedef void (*ExitRoutine)(void);
// The implementation shall support the registration
// of at least 32 functions.
static ExitRoutine _exit_routines[64];
static int _exit_routine_count;
_Noreturn void abort(void) {
raise(SIGABRT);
_os_exit(-69);
}
int atexit(void (*func)(void)) {
if (_exit_routine_count >= COUNTOF(_exit_routines)) {
return 0;
}
_exit_routines[_exit_routine_count++] = func;
return 1;
}
_Noreturn void exit(int status) {
// doing them in reverse seems nicer
for (int i = _exit_routine_count; i--;) {
_exit_routines[i]();
}
_os_exit(status);
}
_Noreturn void _Exit(int status) {
// doesn't run atexit routines
_os_exit(status);
}

View File

@ -1,13 +1,12 @@
#pragma once #pragma once
#include <_os.h>
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
#include <processenv.h>
#include <shellapi.h>
#include <DbgHelp.h>
struct _os_heap { void _setup_timer(void);
HANDLE handle; void _setup_eh();
}; void _setup_heap();
void _os_timing_init(void);

21
src/win/win_assert.c Normal file
View File

@ -0,0 +1,21 @@
#include <assert.h>
#include <win.h>
//#include <stdio.h> // printf
#include <stdlib.h> // abort
void _Noreturn _assert(
char const *cond,
char const *func,
char const *file,
int line
) {
// printf("Assertion failed: %s\n", cond);
// printf(" Function: %s\n", func);
// printf(" File: %s\n", file);
// printf(" Line: %d\n", line);
// printf("Trace:\n");
// _os_print_stack_trace();
abort();
}

View File

@ -1,96 +0,0 @@
#include <_os.h>
#include <locale.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "win.h"
extern int main(int argc, char** argv);
// Some shell32.lib related crap
DECLSPEC_IMPORT LPWSTR GetCommandLineW();
DECLSPEC_IMPORT LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs);
int _wcsicmp(wchar_t const* s1, wchar_t const* s2) {
int diff;
do {
diff = *s1 - *s2;
} while(diff != 0 && *s1 != 0 && *s2 != 0);
return diff;
}
static size_t count_wide_chars(const wchar_t* str) {
size_t len = 0;
while (str[len] != 0) len++;
return len;
}
static bool convert_wide_chars_to_ansi(char* out, const wchar_t* str, size_t len) {
for (size_t i = 0; i < len; i++) {
wchar_t ch = *str++;
if (ch < 0 || 0x7F >= ch) {
*out++ = 0;
return false;
}
*out++ = ch;
}
*out++ = 0;
return true;
}
void mainCRTStartup() {
_os_timing_init();
HANDLE heap = GetProcessHeap();
if (heap == NULL) {
ExitProcess(-42069);
}
int arg_count;
LPWSTR* args_wide = CommandLineToArgvW(GetCommandLineW(), &arg_count);
if (!args_wide) {
ExitProcess(-69420);
}
char** args = HeapAlloc(heap, 0, arg_count * sizeof(char*));
if (arg_count == 0) {
arg_count = 1;
args[0] = "";
}
// Convert wide chars into ANSI
for (int i = 0; i < arg_count; i++) {
size_t wide_len = count_wide_chars(args_wide[i]) + 1;
args[i] = HeapAlloc(heap, 0, wide_len);
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,
};
_heap_setup(&heap_data);
srand(0);
setlocale(LC_ALL, "C");
_os_init_eh();
int exit_code = main(arg_count, args);
// we call exit because we want atexit routines run
exit(exit_code);
}
// This symbol is required to be present if we're using floating-point
// numbers
int _fltused=0;

View File

@ -1,35 +0,0 @@
#include <stdlib.h>
#include "win.h"
char *getenv(const char *name) {
// The string pointed to shall not be modified by the program, but may be
// overwritten by a subsequent call to the getenv function
static size_t env_string_cap;
static char* env_string;
DWORD env_length = GetEnvironmentVariable(name, NULL, 0);
if (env_length == 0) {
return 0;
}
// Upscale the internal string
if (env_length > env_string_cap) {
char* newstr = realloc(env_string, env_length);
if (newstr == NULL) {
free(env_string);
return 0;
}
env_string = newstr;
env_string_cap = env_length;
}
GetEnvironmentVariable(name, env_string, env_length);
return env_string;
}
_Noreturn void _os_exit(int code) {
ExitProcess(code);
}

147
src/win/win_environment.c Normal file
View File

@ -0,0 +1,147 @@
#include <locale.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <win.h>
// Windows symbols because windows
int _fltused=0;
extern int main(int argc, char** argv);
// Exit routines
#define ATEXIT_FUNC_COUNT 64
static void (*atexit_funcs[ATEXIT_FUNC_COUNT])(void);
static int atexit_func_count;
int _wcsicmp(wchar_t const* s1, wchar_t const* s2) {
int diff;
do {
diff = *s1 - *s2;
} while(diff != 0 && *s1 != 0 && *s2 != 0);
return diff;
}
static size_t count_wide_chars(const wchar_t* str) {
size_t len = 0;
while (str[len] != 0) len++;
return len;
}
static bool wchar_to_ansi(char* out, const wchar_t* str, size_t len) {
for (size_t i = 0; i < len; i++) {
wchar_t ch = *str++;
if (ch < 0 || ch > 0x7F) {
*out++ = 0;
return false;
}
*out++ = ch;
}
*out++ = 0;
return true;
}
static char **get_command_args(int *argc_ptr) {
int argc;
char** args;
LPCWSTR command_line = GetCommandLineW();
LPWSTR* args_wide = CommandLineToArgvW(command_line, &argc);
if (!args_wide || argc <= 0) {
ExitProcess(-69420);
}
args = calloc(argc, sizeof(char*));
// Convert wide chars into ANSI
for (int i = 0; i < argc; i++) {
size_t arg_len = count_wide_chars(args_wide[i]);
args[i] = malloc(arg_len+1);
wchar_to_ansi(args[i], args_wide[i], arg_len+1);
}
*argc_ptr = argc;
return args;
}
void mainCRTStartup() {
// Initialize standard pipe handles
stdout = (FILE*) GetStdHandle(STD_OUTPUT_HANDLE);
stderr = (FILE*) GetStdHandle(STD_ERROR_HANDLE);
stdin = (FILE*) GetStdHandle(STD_INPUT_HANDLE);
// Set-up some platform stuff
_setup_heap();
_setup_eh();
_setup_timer();
// Set-up CRT stuff
srand(0);
setlocale(LC_ALL, "C");
// Parse command-line arguments
int argc;
char **args = get_command_args(&argc);
int exit_code = main(argc, args);
// we call exit because we want atexit routines run
exit(exit_code);
}
_Noreturn void abort(void) {
raise(SIGABRT);
ExitProcess(-69);
}
int atexit(void (*func)(void)) {
if (atexit_func_count >= ATEXIT_FUNC_COUNT) {
return 0;
}
atexit_funcs[atexit_func_count++] = func;
return 1;
}
_Noreturn void exit(int status) {
for (int i = atexit_func_count; i--;) {
atexit_funcs[i]();
}
ExitProcess(status);
}
_Noreturn void _Exit(int status) {
ExitProcess(status);
}
char *getenv(const char *name) {
// The string pointed to shall not be modified by the program, but may be
// overwritten by a subsequent call to the getenv function
static size_t env_string_cap;
static char* env_string;
DWORD env_length = GetEnvironmentVariable(name, NULL, 0);
if (env_length == 0) {
return 0;
}
// Upscale the internal string
if (env_length > env_string_cap) {
char* newstr = realloc(env_string, env_length);
if (newstr == NULL) {
free(env_string);
return 0;
}
env_string = newstr;
env_string_cap = env_length;
}
GetEnvironmentVariable(name, env_string, env_length);
return env_string;
}

View File

@ -1,13 +1,9 @@
#include <stdlib.h> #include <win.h>
#include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <limits.h>
#include "win.h" static HANDLE heap_handle;
// TODO: lock the heap before allocation (?)
HANDLE _heap;
static bool is_power_of_two(size_t s) { static bool is_power_of_two(size_t s) {
return (s & (s-1)) == 0; return (s & (s-1)) == 0;
@ -17,8 +13,11 @@ static intptr_t align_forward(intptr_t p, size_t a) {
return (p+a-1)&~(a-1); return (p+a-1)&~(a-1);
} }
void _heap_setup(_os_heap *heap) { void _setup_heap() {
_heap = heap->handle; heap_handle = GetProcessHeap();
if (heap_handle == NULL) {
ExitProcess(-42069);
}
} }
void *aligned_alloc(size_t alignment, size_t size) { void *aligned_alloc(size_t alignment, size_t size) {
@ -39,7 +38,7 @@ void *aligned_alloc(size_t alignment, size_t size) {
if(alignment > 8) { if(alignment > 8) {
min_req_size += alignment; min_req_size += alignment;
} }
void *block_start = HeapAlloc(_heap, 0, min_req_size); void *block_start = HeapAlloc(heap_handle, 0, min_req_size);
intptr_t block_start_i = (intptr_t)block_start; intptr_t block_start_i = (intptr_t)block_start;
intptr_t aligned_block_start_i = align_forward(block_start_i, alignment); intptr_t aligned_block_start_i = align_forward(block_start_i, alignment);
void *aligned_block_start = (void *)aligned_block_start_i; void *aligned_block_start = (void *)aligned_block_start_i;
@ -53,7 +52,7 @@ void *calloc(size_t nmemb, size_t size) {
if(nmemb > SIZE_MAX/size) { if(nmemb > SIZE_MAX/size) {
return NULL; return NULL;
} }
void *block_start = HeapAlloc(_heap, HEAP_ZERO_MEMORY, size*nmemb); void *block_start = HeapAlloc(heap_handle, HEAP_ZERO_MEMORY, size*nmemb);
return block_start; return block_start;
} }
@ -61,7 +60,7 @@ void free(void *ptr) {
if(ptr == NULL) { if(ptr == NULL) {
return; return;
} }
HeapFree(_heap, 0, ptr); HeapFree(heap_handle, 0, ptr);
} }
void *malloc(size_t size) { void *malloc(size_t size) {
@ -72,11 +71,12 @@ void *realloc(void *ptr, size_t size) {
if (ptr == NULL) { if (ptr == NULL) {
if (size == 0) return NULL; if (size == 0) return NULL;
return HeapAlloc(_heap, 0, size); return HeapAlloc(heap_handle, 0, size);
} else if (size == 0) { } else if (size == 0) {
HeapFree(_heap, 0, ptr); HeapFree(heap_handle, 0, ptr);
return NULL; return NULL;
} else { } else {
return HeapReAlloc(_heap, 0, ptr, size); return HeapReAlloc(heap_handle, 0, ptr, size);
} }
} }

View File

@ -1,134 +0,0 @@
#include "win.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// It's just mapped directly to HANDLE
struct FILE {
int unused;
};
int _os_del_file(char const *filename) {
int ok = DeleteFileA(filename);
return ok != 0;
}
int _os_mov_file(char const *old, char const *new) {
int ok = MoveFileA(old, new);
return ok != 0;
}
char *_os_tmpname(char *buffer) {
static UINT uniq = 0;
DWORD path_len = GetTempPathA(L_tmpnam, buffer);
if(path_len == 0) return NULL;
UINT ok = GetTempFileNameA(buffer, "", uniq, buffer);
if(ok == 0) return NULL;
return buffer;
}
FILE *_os_fopen(char const *restrict name, _OS_ModeFlags flags) {
DWORD desaddr = 0;
DWORD share = 0;
DWORD disp = 0;
switch(flags.base_mode) {
case 'r': {
desaddr = GENERIC_READ;
if(!flags.update) {
share = FILE_SHARE_READ;
}
disp = OPEN_EXISTING;
if(flags.update) {
disp = OPEN_ALWAYS;
}
} break;
case 'w': {
desaddr = GENERIC_WRITE;
disp = CREATE_ALWAYS;
} break;
case 'a': {
desaddr = GENERIC_WRITE;
} break;
}
if(flags.exclusive) {
disp = CREATE_NEW;
}
HANDLE fileHandle = CreateFileA(
name,
desaddr,
share,
NULL,
disp,
FILE_ATTRIBUTE_NORMAL,
NULL
);
FILE *file = (FILE *)fileHandle;
if(fileHandle == INVALID_HANDLE_VALUE) {
file = NULL;
}
return file;
}
int _os_fclose(FILE *file) {
HANDLE fileHandle = (HANDLE)file;
BOOL ok = CloseHandle(fileHandle);
return ok != 0;
}
void _os_file_write(void* ctx, size_t n, const char str[]) {
DWORD written = 0;
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)));
if (cmd_line == NULL) {
goto error;
}
memcpy(cmd_line, L"cmd.exe ", sizeof(L"cmd.exe "));
MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, cmd_line + sizeof("cmd.exe ") - 1, wchars_required);
STARTUPINFOW si = {
.cb = sizeof(STARTUPINFOW),
.dwFlags = STARTF_USESTDHANDLES,
.hStdInput = GetStdHandle(STD_INPUT_HANDLE),
.hStdError = GetStdHandle(STD_ERROR_HANDLE),
.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
};
PROCESS_INFORMATION pi = {};
if (!CreateProcessW(NULL, cmd_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
goto error;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exit_code;
if (!GetExitCodeProcess(pi.hProcess, &exit_code)) {
goto error;
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(cmd_line);
return exit_code;
error:
free(cmd_line);
return -1;
}

View File

@ -1,10 +1,7 @@
#include <_os.h> #include <win.h>
#define WIN32_LEAN_AND_MEAN
#include<Windows.h>
#include<DbgHelp.h>
#include <signal.h>
#include <signal.h> #include <signal.h>
#include <stddef.h> #include <stddef.h>
#include <inttypes.h> #include <inttypes.h>
@ -54,32 +51,45 @@ static LONG _win32_handler(EXCEPTION_POINTERS *ExceptionInfo) {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
void _os_print_stack_trace() {
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
void *stack[128]; void _setup_eh() {
USHORT frames = CaptureStackBackTrace(2, 128, stack, NULL);
SYMBOL_INFO* symbol = calloc(sizeof(SYMBOL_INFO)+256, 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for(size_t i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)stack[i], 0, symbol);
if(strcmp(symbol->Name, "BaseThreadInitThunk") == 0) break;
if(strcmp(symbol->Name, "mainCRTStartup") == 0) break;
printf(" %u: 0x%"PRIx64" (%s)\n",
(int)(frames-i-1),
symbol->Address,
symbol->Name
);
}
free(symbol);
}
void _os_init_eh() {
void *res = AddVectoredExceptionHandler(1, &_win32_handler); void *res = AddVectoredExceptionHandler(1, &_win32_handler);
if(res == NULL) { if(res == NULL) {
ExitProcess(-69420); ExitProcess(-69420);
} }
} }
void _signal_default_handler(int sig){}
void _signal_ignore_handler(int sig){}
static void (*(handlers[]))(int) = {
[SIGINT] = _signal_ignore_handler,
[SIGILL] = _signal_ignore_handler,
[SIGFPE] = _signal_ignore_handler,
[SIGSEGV] = _signal_ignore_handler,
[SIGTERM] = _signal_ignore_handler,
[SIGABRT] = _signal_ignore_handler,
[SIGBREAK] = _signal_ignore_handler,
[SIGALIGN] = _signal_ignore_handler,
[SIGSTEP] = _signal_ignore_handler,
};
void (*signal(int sig, void (*func)(int)))(int) {
if(_SIG_MIN <= sig && sig <= _SIG_MAX) {
handlers[sig] = func;
return func;
}
return SIG_ERR;
}
int raise(int sig)
{
if(_SIG_MIN <= sig && sig <= _SIG_MAX) {
handlers[sig](sig);
if(sig == SIGFPE || sig == SIGILL || sig == SIGSEGV) {
ExitProcess(-69420);
}
return 1;
}
return 0;
}

26
src/win/win_stack_trace.c Normal file
View File

@ -0,0 +1,26 @@
#include <win.h>
#include <stdlib.h>
void _os_print_stack_trace() {
HANDLE process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
void *stack[128];
USHORT frames = CaptureStackBackTrace(2, 128, stack, NULL);
SYMBOL_INFO* symbol = calloc(sizeof(SYMBOL_INFO)+256, 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for(size_t i = 0; i < frames; i++) {
SymFromAddr(process, (DWORD64)stack[i], 0, symbol);
if(strcmp(symbol->Name, "BaseThreadInitThunk") == 0) break;
if(strcmp(symbol->Name, "mainCRTStartup") == 0) break;
// printf(" %u: 0x%"PRIx64" (%s)\n",
// (int)(frames-i-1),
// symbol->Address,
// symbol->Name
// );
}
free(symbol);
}

58
src/win/win_stdio.c Normal file
View File

@ -0,0 +1,58 @@
#include <win.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// It's just mapped directly to HANDLE
struct FILE {
int unused;
};
FILE *stdout;
FILE *stdin;
FILE *stderr;
int system(const char* string) {
int wchars_required = MultiByteToWideChar(65001, 0, string, -1, NULL, 0);
wchar_t* cmd_line = malloc(sizeof(L"cmd.exe ") + (wchars_required * sizeof(wchar_t)));
if (cmd_line == NULL) {
goto error;
}
memcpy(cmd_line, L"cmd.exe ", sizeof(L"cmd.exe "));
MultiByteToWideChar(65001, 0, string, -1, cmd_line + sizeof("cmd.exe ") - 1, wchars_required);
STARTUPINFOW si = {
.cb = sizeof(STARTUPINFOW),
.dwFlags = STARTF_USESTDHANDLES,
.hStdInput = GetStdHandle(STD_INPUT_HANDLE),
.hStdError = GetStdHandle(STD_ERROR_HANDLE),
.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
};
PROCESS_INFORMATION pi = {};
if (!CreateProcessW(NULL, cmd_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
goto error;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exit_code;
if (!GetExitCodeProcess(pi.hProcess, &exit_code)) {
goto error;
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(cmd_line);
return exit_code;
error:
free(cmd_line);
return -1;
}

View File

@ -5,8 +5,8 @@
// Based on these posts: // Based on these posts:
// https://preshing.com/20120305/implementing-a-recursive-mutex/ // https://preshing.com/20120305/implementing-a-recursive-mutex/
// https://preshing.com/20120226/roll-your-own-lightweight-mutex/ // https://preshing.com/20120226/roll-your-own-lightweight-mutex/
#include "win.h" #include <win.h>
#include "threads.h" #include <threads.h>
void mtx_destroy(mtx_t *mtx) { void mtx_destroy(mtx_t *mtx) {
CloseHandle(mtx->semaphore); CloseHandle(mtx->semaphore);

View File

@ -1,11 +1,20 @@
#include <time.h> #include <time.h>
#include <stdint.h> #include <stdint.h>
#include "win.h" #include <win.h>
// Store the time since we started running the process // Store the time since we started running the process
static uint64_t timer_freq; static uint64_t timer_freq;
static uint64_t timer_start; static uint64_t timer_start;
void _setup_timer(void) {
LARGE_INTEGER freq, start;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
timer_start = start.QuadPart;
timer_freq = freq.QuadPart;
}
int timespec_get(struct timespec *ts, int base) { int timespec_get(struct timespec *ts, int base) {
if (base != TIME_UTC) return 0; if (base != TIME_UTC) return 0;
@ -42,14 +51,3 @@ clock_t clock(void) {
return scaled_millis; return scaled_millis;
} }
void _os_timing_init(void) {
LARGE_INTEGER freq, start;
if (QueryPerformanceFrequency(&freq) && QueryPerformanceCounter(&start)) {
timer_start = start.QuadPart;
timer_freq = freq.QuadPart;
} else {
// failure...
timer_start = timer_freq = UINT64_MAX;
}
}

8
test/test_os.c Normal file
View File

@ -0,0 +1,8 @@
int main(int argc, char **argv) {
for(int i = 0; i != argc; ++i) {
char *arg = argv[i];
arg = arg;
}
return 0;
}